summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kLdr
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kLdr')
-rw-r--r--src/lib/kStuff/kLdr/Doxyfile1252
-rw-r--r--src/lib/kStuff/kLdr/Makefile.kmk224
-rw-r--r--src/lib/kStuff/kLdr/kLdr-os2.c66
-rw-r--r--src/lib/kStuff/kLdr/kLdr-os2.def115
-rw-r--r--src/lib/kStuff/kLdr/kLdr-win.c77
-rw-r--r--src/lib/kStuff/kLdr/kLdr-win.def113
-rw-r--r--src/lib/kStuff/kLdr/kLdr.c145
-rw-r--r--src/lib/kStuff/kLdr/kLdrA-os2.asm66
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyld.c1509
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyldFind.c1086
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyldMod.c1300
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyldOS.c133
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyldSem.c198
-rw-r--r--src/lib/kStuff/kLdr/kLdrExeStub-os2.asm72
-rw-r--r--src/lib/kStuff/kLdr/kLdrExeStub-os2.c59
-rw-r--r--src/lib/kStuff/kLdr/kLdrExeStub-os2A.asm41
-rw-r--r--src/lib/kStuff/kLdr/kLdrExeStub-win.c62
-rw-r--r--src/lib/kStuff/kLdr/kLdrHlp.h9
-rw-r--r--src/lib/kStuff/kLdr/kLdrInternal.h463
-rw-r--r--src/lib/kStuff/kLdr/kLdrMod.c914
-rw-r--r--src/lib/kStuff/kLdr/kLdrModLX.c2701
-rw-r--r--src/lib/kStuff/kLdr/kLdrModMachO.c3729
-rw-r--r--src/lib/kStuff/kLdr/kLdrModNative.c1206
-rw-r--r--src/lib/kStuff/kLdr/kLdrModPE.c2044
-rw-r--r--src/lib/kStuff/kLdr/testcase/Makefile.kmk305
-rw-r--r--src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86bin0 -> 3072 bytes
-rw-r--r--src/lib/kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86bin0 -> 2800 bytes
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-0-a.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-0-b.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-0-c.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-0-d.c8
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-0-driver.c502
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-0.c13
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-1-a.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-1-b.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-1-c.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-1-d.c8
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-1.c15
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-2-a.c8
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-2-b.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-2-c.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-2-d.c10
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-2.c16
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-3-driver.c216
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-3-ext.c39
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-3-imp-os2.def34
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-3-imp-win.def34
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst-3.c78
-rw-r--r--src/lib/kStuff/kLdr/testcase/tst.h57
-rw-r--r--src/lib/kStuff/kLdr/testcase/tstDllMain.c192
-rw-r--r--src/lib/kStuff/kLdr/testcase/tstDllMainStub-os2.asm40
-rw-r--r--src/lib/kStuff/kLdr/testcase/tstDllMainStub.c76
-rw-r--r--src/lib/kStuff/kLdr/testcase/tstExeMainStub-os2.asm40
-rw-r--r--src/lib/kStuff/kLdr/testcase/tstExeMainStub.c93
-rw-r--r--src/lib/kStuff/kLdr/tg/KLDRSTATE.gifbin0 -> 14294 bytes
-rw-r--r--src/lib/kStuff/kLdr/tg/KLDRSTATE.txvstc529
-rw-r--r--src/lib/kStuff/kLdr/tg/default.txvpck8
-rw-r--r--src/lib/kStuff/kLdr/tg/kLdr.tpr23
-rw-r--r--src/lib/kStuff/kLdr/tg/kLdr.tws2
-rw-r--r--src/lib/kStuff/kLdr/tstkLdrHeap.c223
-rw-r--r--src/lib/kStuff/kLdr/tstkLdrMod.c629
61 files changed, 20862 insertions, 0 deletions
diff --git a/src/lib/kStuff/kLdr/Doxyfile b/src/lib/kStuff/kLdr/Doxyfile
new file mode 100644
index 0000000..d54c1f5
--- /dev/null
+++ b/src/lib/kStuff/kLdr/Doxyfile
@@ -0,0 +1,1252 @@
+# Doxyfile 1.5.0
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = kLdr
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = YES
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = tst*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = tg
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = NO
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/src/lib/kStuff/kLdr/Makefile.kmk b/src/lib/kStuff/kLdr/Makefile.kmk
new file mode 100644
index 0000000..fc8455b
--- /dev/null
+++ b/src/lib/kStuff/kLdr/Makefile.kmk
@@ -0,0 +1,224 @@
+# $Id: Makefile.kmk 29 2009-07-01 20:30:29Z bird $
+## @file
+# kLdr - The Dynamic Loader, sub-makefile.
+#
+
+#
+# Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+
+DEPTH ?= ..
+SUB_DEPTH = ..
+include $(PATH_KBUILD)/subheader.kmk
+
+#todo: include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# Template for testcases.
+#
+TEMPLATE_TST = Testcase template
+ifeq ($(BUILD_TARGET),win)
+ ifeq ($(BUILD_TARGET_ARCH), x86)
+ TEMPLATE_TST_TOOL = VCC70
+ TEMPLATE_TST_CFLAGS = -W3 -Zi -Zl -MD
+ TEMPLATE_TST_LIBS = $(PATH_TOOL_VCC70_LIB)/msvcrt.lib
+ else
+ TEMPLATE_TST_TOOL = VCC80AMD64
+ TEMPLATE_TST_CFLAGS = -W3 -Zi -Zl -MD
+ TEMPLATE_TST_LIBS = $(PATH_TOOL_VCC80AMD64_LIB)/msvcrt.lib
+ endif
+ TEMPLATE_TST_CFLAGS.release = -O2
+ TEMPLATE_TST_ASFLAGS = -f win
+ TEMPLATE_TST_DEFS = __WIN__
+ TEMPLATE_TST_SDKS = WINPSDK W2K3DDK
+
+else
+ ifeq ($(BUILD_TARGET),os2)
+ TEMPLATE_TST_TOOL = GCC3OMF
+ TEMPLATE_TST_ASFLAGS = -f obj
+ TEMPLATE_TST_LIBS = os2 gcc end
+ else ifeq ($(BUILD_TARGET),darwin)
+ TEMPLATE_TST_TOOL = GCC4MACHO
+ TEMPLATE_TST_ASFLAGS = -f macho
+ TEMPLATE_TST_LIBS = #os2 gcc end
+ else
+ TEMPLATE_TST_TOOL = GCC3
+ TEMPLATE_TST_ASFLAGS = -f elf
+ TEMPLATE_TST_LIBS = gcc
+ endif
+ TEMPLATE_TST_CFLAGS = -Wall -pedantic -g -std=gnu99
+ TEMPLATE_TST_CFLAGS.release = -O2
+ TEMPLATE_TST_LDFLAGS =
+endif
+TEMPLATE_TST_INCS := $(PATH_SUB_CURRENT) $(PATH_SUB_ROOT)/include
+
+
+#
+# The kLdr DLL.
+#
+DLLS += kLdr
+kLdr_ASTOOL = NASM
+ifeq ($(BUILD_TARGET),win)
+ ifeq ($(BUILD_TARGET_ARCH),x86)
+ kLdr_TOOL = VCC70
+ kLdr_CFLAGS = -W3 -Zl -ML
+ kLdr_LDFLAGS = -Entry:DllMain@12 -Debug
+ kLdr_LIBS = \
+ $(PATH_TOOL_VCC70_LIB)/LIBC.lib \
+ $(PATH_SDK_W2K3DDKX86_LIB)/ntdll.lib
+ else
+ kLdr_TOOL = VCC80AMD64
+ kLdr_ASTOOL = YASM
+ kLdr_CFLAGS = -W3 -Zl -MT
+ kLdr_LDFLAGS = -Entry:DllMain -Debug
+ kLdr_LIBS = \
+ $(PATH_TOOL_VCC80AMD64_LIB)/LIBCMT.lib \
+ $(PATH_SDK_W2K3DDKAMD64_LIB)/ntdll.lib
+ endif
+ kLdr_ASFLAGS = -f win
+ kLdr_DEFS = __WIN__
+ kLdr_SDKS.x86 = WIN32SDK W2K3DDKX86
+ kLdr_SDKS.amd64 = WIN64SDK W2K3DDKAMD64
+else
+ ifeq ($(BUILD_TARGET),os2)
+ kLdr_TOOL = GCC3OMF
+ kLdr_ASFLAGS = -f obj
+ kLdr_LIBS = os2 gcc end
+ else ifeq ($(BUILD_TARGET),darwin)
+ kLdr_TOOL = GCC4MACHO
+ kLdr_ASFLAGS = -f macho
+ kLdr_LIBS = #os2 gcc end
+ else
+ kLdr_TOOL = GCC3
+ kLdr_ASFLAGS = -f elf
+ kLdr_LIBS = gcc
+ endif
+ kLdr_CFLAGS = -Wall -pedantic
+ kLdr_LDFLAGS = -nostdlib
+endif
+kLdr_INCS := $(PATH_SUB_CURRENT) $(PATH_SUB_ROOT)/include
+kLdr_SOURCES = \
+ kLdr.c \
+ kLdrDyld.c \
+ kLdrDyldFind.c \
+ kLdrDyldMod.c \
+ kLdrDyldOS.c \
+ kLdrDyLdSem.c \
+ kLdrMod.c \
+ kLdrModLX.c \
+ kLdrModMachO.c \
+ kLdrModNative.c \
+ kLdrModPE.c
+kLdr_SOURCES.os2 = \
+ kLdr-os2.def \
+ kLdr-os2.c \
+ kLdrA-os2.asm
+kLdr_SOURCES.win = \
+ kLdr-win.def \
+ kLdr-win.c
+kLdr_LIBS += \
+ $(PATH_LIB)/kRdrStatic$(SUFF_LIB) \
+ $(PATH_LIB)/kCpuStatic$(SUFF_LIB) \
+ $(PATH_LIB)/kHlpBareStatic$(SUFF_LIB) \
+ $(PATH_LIB)/kErrStatic$(SUFF_LIB)
+
+#
+# A static edition of kLdr.
+#
+LIBRARIES += kLdrStatic
+kLdrStatic_TEMPLATE = kStuffLIB
+kLdrStatic_SDKS.win = WINPSDK W2K3DDK
+kLdrStatic_INCS := $(PATH_SUB_CURRENT) $(PATH_SUB_ROOT)/include
+kLdrStatic_DEFS.darwin = __DARWIN__
+kLdrStatic_DEFS.os2 = __OS2__
+kLdrStatic_DEFS.win = __WIN__
+kLdrStatic_SOURCES = $(kLdr_SOURCES)
+
+#
+# The OS/2 stub program.
+#
+PROGRAMS.os2 = kLdrExeStub-os2
+kLdrExeStub-os2_TOOL = GCC3OMF
+kLdrExeStub-os2_ASTOOL = NASM
+kLdrExeStub-os2_ASFLAGS = -f obj
+#kLdrExeStub-os2_LDFLAGS = -nostdlib
+kLdrExeStub-os2_LDFLAGS = -nostdlib -Zstack 64
+kLdrExeStub-os2_LIBS = $(TARGET_kLdr)
+#kLdrExeStub-os2_SOURCES = kLdrExeStub-os2.asm
+kLdrExeStub-os2_SOURCES = kLdrExeStub-os2A.asm kLdrExeStub-os2.c
+
+#
+# The Windows stub program.
+#
+PROGRAMS.win = kLdrExeStub-win
+kLdrExeStub-win_TOOL.win.x86 = VCC70
+kLdrExeStub-win_TOOL.win.amd64 = VCC80AMD64
+kLdrExeStub-win_SDKS.x86 = WIN32SDK
+kLdrExeStub-win_SDKS.amd64 = WIN64SDK
+kLdrExeStub-win_INCS := $(PATH_SUB_CURRENT) $(PATH_SUB_ROOT)/include
+kLdrExeStub-win_DEFS = __WIN__
+kLdrExeStub-win_CFLAGS = -W3 -Zl
+kLdrExeStub-win_CFLAGS.debug = -Zi
+kLdrExeStub-win_LDFLAGS = -Entry:WindowsMain -SubSystem:Console -FIXED:NO
+kLdrExeStub-win_LIBS = $(TARGET_kLdr:.dll=.lib)
+kLdrExeStub-win_SOURCES = kLdrExeStub-win.c
+
+
+##
+## The (stub) utility.
+##
+#PROGRAMS = kLdrUtil
+
+
+#
+# Heap testcase.
+#
+#PROGRAMS += tstkLdrHeap
+tstkLdrHeap_TEMPLATE = TST
+tstkLdrHeap_SOURCES = \
+ tstkLdrHeap.c \
+ kHlp.c \
+ kHlpHeap.c \
+ kHlpMem.c \
+ kHlpPath.c \
+ kHlpSem.c \
+ kHlpStr.c \
+
+#
+# Heap testcase.
+#
+PROGRAMS += tstkLdrMod
+tstkLdrMod_TEMPLATE = TST
+tstkLdrMod_SOURCES = \
+ tstkLdrMod.c
+ifeq ($(BUILD_TARGET),win)
+tstkLdrMod_LIBS = $(TARGET_kLdr:.dll=.lib)
+else
+tstkLdrMod_LIBS = $(TARGET_kLdr)
+endif
+
+
+# Generate rules.
+include $(PATH_KBUILD)/subfooter.kmk
+
diff --git a/src/lib/kStuff/kLdr/kLdr-os2.c b/src/lib/kStuff/kLdr/kLdr-os2.c
new file mode 100644
index 0000000..62835ac
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdr-os2.c
@@ -0,0 +1,66 @@
+/* $Id: kLdr-os2.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, OS/2 Specifics.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#define INCL_BASE
+#include <os2.h>
+
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+
+/**
+ * The DLL main function.
+ *
+ * @returns TRUE / FALSE.
+ * @param hmod The dll handle.
+ * @param fFlags Flags.
+ */
+ULONG _System _DLL_InitTerm(HMODULE hmod, ULONG fFlags)
+{
+ switch (fFlags)
+ {
+ case 0:
+ {
+ int rc = kldrInit();
+ return rc == 0;
+ }
+
+ case 1:
+ kldrTerm();
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdr-os2.def b/src/lib/kStuff/kLdr/kLdr-os2.def
new file mode 100644
index 0000000..e9895f7
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdr-os2.def
@@ -0,0 +1,115 @@
+; $Id: kLdr-os2.def 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - The Dynamic Loader, OS/2 Linker Definition File.
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+LIBRARY kLdr INITINSTANCE TERMINSTANCE
+DATA MULTIPLE
+EXPORTS
+ ; The file reader API
+ _kRdrAddProvider
+ _kRdrOpen
+ _kRdrClose
+ _kRdrRead
+ _kRdrAllMap
+ _kRdrAllUnmap
+ _kRdrSize
+ _kRdrTell
+ _kRdrName
+ _kRdrPageSize
+ _kRdrMap
+ _kRdrRefresh
+ _kRdrProtect
+ _kRdrUnmap
+ _kRdrDone
+
+ ; The module interpreter API
+ _kLdrModOpen
+ _kLdrModOpenFromRdr
+ _kLdrModOpenNative
+ _kLdrModOpenNativeByHandle
+ _kLdrModClose
+ _kLdrModQuerySymbol
+ _kLdrModEnumSymbols
+ _kLdrModGetImport
+ _kLdrModNumberOfImports
+ _kLdrModCanExecuteOn
+ _kLdrModGetStackInfo
+ _kLdrModQueryMainEntrypoint
+ _kLdrModEnumDbgInfo
+ _kLdrModHasDbgInfo
+ _kLdrModMap
+ _kLdrModUnmap
+ _kLdrModAllocTLS
+ _kLdrModFreeTLS
+ _kLdrModReload
+ _kLdrModFixupMapping
+ _kLdrModCallInit
+ _kLdrModCallTerm
+ _kLdrModCallThread
+ _kLdrModSize
+ _kLdrModGetBits
+ _kLdrModRelocateBits
+
+ ; Process Bootstrapping
+ _kLdrDyldLoadExe
+
+ ; Dynamic loading
+ _kLdrDyldLoad
+ _kLdrDyldUnload
+ _kLdrDyldFindByName
+ _kLdrDyldFindByAddress
+ _kLdrDyldGetName
+ _kLdrDyldGetFilename
+ _kLdrDyldQuerySymbol
+
+
+ ; OS/2 API wrappers:
+; kLdrLoadModule
+; kLdrFreeModule
+; kLdrQueryModuleHandle
+; kLdrQueryModuleName
+; kLdrQueryProcAddr
+; kLdrQueryProcType
+; kLdrQueryModFromEIP
+; kLdrReplaceModule
+; kLdrGetResource
+; kLdrFreeResource
+; kLdrQueryResourceSize
+
+ ; dlfcn API wrappers:
+; _kLdrDlOpen
+; _kLdrDlClose
+; _kLdrDlError
+; _kLdrDlSym
+; _kLdrDlFunc
+
+ ; Error APIs:
+ _kErrStr
+
+
diff --git a/src/lib/kStuff/kLdr/kLdr-win.c b/src/lib/kStuff/kLdr/kLdr-win.c
new file mode 100644
index 0000000..1fe7e59
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdr-win.c
@@ -0,0 +1,77 @@
+/* $Id: kLdr-win.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, Windows Specifics.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <Windows.h>
+
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+
+/**
+ * The DLL main function.
+ *
+ * @returns TRUE / FALSE.
+ * @param hDllHandle The dll handle.
+ * @param dwReason The reason we're being called.
+ * @param lpReserved Reserved.
+ */
+BOOL __stdcall DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ int rc = kldrInit();
+ return rc == 0;
+ }
+
+ case DLL_PROCESS_DETACH:
+ kldrTerm();
+ return TRUE;
+
+ case DLL_THREAD_ATTACH:
+ {
+ //int rc = kLdrDyldThreadAttach();
+ //return rc == 0;
+ return TRUE;
+ }
+
+ case DLL_THREAD_DETACH:
+ //kLdrDyldThreadDetach();
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdr-win.def b/src/lib/kStuff/kLdr/kLdr-win.def
new file mode 100644
index 0000000..fc36e59
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdr-win.def
@@ -0,0 +1,113 @@
+; $Id: kLdr-win.def 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - The Dynamic Loader, Windows Linker Definition File.
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+LIBRARY kLdr
+EXPORTS
+ ; The file reader API
+ kRdrAddProvider
+ kRdrOpen
+ kRdrClose
+ kRdrRead
+ kRdrAllMap
+ kRdrAllUnmap
+ kRdrSize
+ kRdrTell
+ kRdrName
+ kRdrPageSize
+ kRdrMap
+ kRdrRefresh
+ kRdrProtect
+ kRdrUnmap
+ kRdrDone
+
+ ; The module interpreter API
+ kLdrModOpen
+ kLdrModOpenFromRdr
+ kLdrModOpenNative
+ kLdrModOpenNativeByHandle
+ kLdrModClose
+ kLdrModQuerySymbol
+ kLdrModEnumSymbols
+ kLdrModGetImport
+ kLdrModNumberOfImports
+ kLdrModCanExecuteOn
+ kLdrModGetStackInfo
+ kLdrModQueryMainEntrypoint
+ kLdrModEnumDbgInfo
+ kLdrModHasDbgInfo
+ kLdrModMap
+ kLdrModUnmap
+ kLdrModAllocTLS
+ kLdrModFreeTLS
+ kLdrModReload
+ kLdrModFixupMapping
+ kLdrModCallInit
+ kLdrModCallTerm
+ kLdrModCallThread
+ kLdrModSize
+ kLdrModGetBits
+ kLdrModRelocateBits
+
+ ; Process Bootstrapping
+ kLdrDyldLoadExe
+
+ ; Dynamic loading
+ kLdrDyldLoad
+ kLdrDyldUnload
+ kLdrDyldFindByName
+ kLdrDyldFindByAddress
+ kLdrDyldGetName
+ kLdrDyldGetFilename
+ kLdrDyldQuerySymbol
+
+
+ ; OS/2 API wrappers:
+; kLdrLoadModule
+; kLdrFreeModule
+; kLdrQueryModuleHandle
+; kLdrQueryModuleName
+; kLdrQueryProcAddr
+; kLdrQueryProcType
+; kLdrQueryModFromEIP
+; kLdrReplaceModule
+; kLdrGetResource
+; kLdrFreeResource
+; kLdrQueryResourceSize
+
+ ; dlfcn API wrappers:
+; _kLdrDlOpen
+; _kLdrDlClose
+; _kLdrDlError
+; _kLdrDlSym
+; _kLdrDlFunc
+
+ ; Error APIs:
+ kErrName
+
diff --git a/src/lib/kStuff/kLdr/kLdr.c b/src/lib/kStuff/kLdr/kLdr.c
new file mode 100644
index 0000000..38f4cfa
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdr.c
@@ -0,0 +1,145 @@
+/* $Id: kLdr.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/** @mainpage kLdr - The Dynamic Loader
+ *
+ * The purpose of kLdr is to provide a generic interface for querying
+ * information about and loading executable image modules.
+ *
+ * kLdr defines the term executable image to include all kinds of files that contains
+ * binary code that can be executed on a CPU - linker objects (OBJs/Os), shared
+ * objects (SOs), dynamic link libraries (DLLs), executables (EXEs), and all kinds
+ * of kernel modules / device drivers (SYSs).
+ *
+ * kLdr provides two types of services:
+ * -# Inspect or/and load individual modules (kLdrMod).
+ * -# Work as a dynamic loader - construct and maintain an address space (kLdrDy).
+ *
+ * The kLdrMod API works on KLDRMOD structures where all the internals are exposed, while
+ * the kLdrDy API works opque KLDRDY structures. KLDRDY are in reality simple wrappers
+ * around KLDRMOD with some extra linking and attributes.
+ *
+ */
+
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Flag indicating whether we've initialized the loader or not.
+ *
+ * 0 if not initialized.
+ * -1 if we're initializing or terminating.
+ * 1 if we've successfully initialized it.
+ * -2 if initialization failed.
+ */
+static int volatile g_fInitialized;
+
+
+
+/**
+ * Initializes the loader.
+ * @returns 0 on success, non-zero OS status code on failure.
+ */
+int kldrInit(void)
+{
+ int rc;
+
+ /* check we're already good. */
+ if (g_fInitialized == 1)
+ return 0;
+
+ /* a tiny serialization effort. */
+ for (;;)
+ {
+ if (g_fInitialized == 1)
+ return 0;
+ if (g_fInitialized == -2)
+ return -1;
+ /** @todo atomic test and set if we care. */
+ if (g_fInitialized == 0)
+ {
+ g_fInitialized = -1;
+ break;
+ }
+ kHlpSleep(1);
+ }
+
+ /*
+ * Do the initialization.
+ */
+ rc = kHlpHeapInit();
+ if (!rc)
+ {
+ rc = kLdrDyldSemInit();
+ if (!rc)
+ {
+ rc = kldrDyldInit();
+ if (!rc)
+ {
+ g_fInitialized = 1;
+ return 0;
+ }
+ kLdrDyldSemTerm();
+ }
+ kHlpHeapTerm();
+ }
+ g_fInitialized = -2;
+ return rc;
+}
+
+
+/**
+ * Terminates the loader.
+ */
+void kldrTerm(void)
+{
+ /* can't terminate unless it's initialized. */
+ if (g_fInitialized != 1)
+ return;
+ g_fInitialized = -1;
+
+ /*
+ * Do the termination.
+ */
+ kLdrDyldSemTerm();
+ kHlpHeapTerm();
+
+ /* done */
+ g_fInitialized = 0;
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrA-os2.asm b/src/lib/kStuff/kLdr/kLdrA-os2.asm
new file mode 100644
index 0000000..cc9784a
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrA-os2.asm
@@ -0,0 +1,66 @@
+; $Id: kLdrA-os2.asm 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - The Dynamic Loader, OS/2 Assembly Helpers.
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+segment TEXT32 public align=16 CLASS=CODE use32
+
+;
+; _DLL_InitTerm
+;
+..start:
+extern _DLL_InitTerm
+ jmp _DLL_InitTerm
+
+
+;
+; kLdrLoadExe wrapper which loads the bootstrap stack.
+;
+global _kLdrDyldLoadExe
+_kLdrDyldLoadExe:
+ push ebp
+ mov ebp, esp
+
+ ; switch stack.
+; extern _abStack
+; lea esp, [_abStack + 8192 - 4]
+ push dword [ebp + 8 + 20]
+ push dword [ebp + 8 + 16]
+ push dword [ebp + 8 + 12]
+ push dword [ebp + 8 + 8]
+
+ ; call worker on the new stack.
+ extern _kldrDyldLoadExe
+ call _kldrDyldLoadExe
+
+ ; we shouldn't return!
+we_re_not_supposed_to_get_here:
+ int3
+ int3
+ jmp short we_re_not_supposed_to_get_here
+
diff --git a/src/lib/kStuff/kLdr/kLdrDyld.c b/src/lib/kStuff/kLdr/kLdrDyld.c
new file mode 100644
index 0000000..9ff3dd8
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyld.c
@@ -0,0 +1,1509 @@
+/* $Id: kLdrDyld.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRDYLD_STRICT
+ * Define KLDRDYLD_STRICT to enabled strict checks in kLdrDyld. */
+#define KLDRDYLD_STRICT 1
+
+/** @def KLDRDYLD_ASSERT
+ * Assert that an expression is true when KLDRDYLD_STRICT is defined.
+ */
+#ifdef KLDRDYLD_STRICT
+# define KLDRDYLD_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRDYLD_ASSERT(expr) do {} while (0)
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Pointer to the executable module.
+ * (This is exported, so no prefix.) */
+PKLDRDYLDMOD kLdrDyldExe = NULL;
+/** Pointer to the head module (the executable).
+ * (This is exported, so no prefix.) */
+PKLDRDYLDMOD kLdrDyldHead = NULL;
+/** Pointer to the tail module.
+ * (This is exported, so no prefix.) */
+PKLDRDYLDMOD kLdrDyldTail = NULL;
+/** Pointer to the head module of the initialization list.
+ * The outermost load call will pop elements from this list in LIFO order (i.e.
+ * from the tail). The list is only used during non-recursive initialization
+ * and may therefore share the pNext/pPrev members with the termination list
+ * since we don't push a module onto the termination list untill it has been
+ * successfully initialized. */
+PKLDRDYLDMOD g_pkLdrDyldInitHead;
+/** Pointer to the tail module of the initalization list.*/
+PKLDRDYLDMOD g_pkLdrDyldInitTail;
+/** Pointer to the head module of the termination order list.
+ * This is a LIFO just like the the init list. */
+PKLDRDYLDMOD g_pkLdrDyldTermHead;
+/** Pointer to the tail module of the termination order list. */
+PKLDRDYLDMOD g_pkLdrDyldTermTail;
+/** Pointer to the head module of the bind order list.
+ * The modules in this list makes up the global namespace used when binding symbol unix fashion. */
+PKLDRDYLDMOD g_pkLdrDyldBindHead;
+/** Pointer to the tail module of the bind order list. */
+PKLDRDYLDMOD g_pkLdrDyldBindTail;
+
+/** Flag indicating bootstrap time.
+ * When set the error behaviour changes. Any kind of serious failure
+ * is fatal and will terminate the process. */
+int g_fBootstrapping;
+/** The global error buffer. */
+char g_szkLdrDyldError[1024];
+
+/** The default flags. */
+KU32 kLdrDyldFlags = 0;
+/** The default search method. */
+KLDRDYLDSEARCH kLdrDyldSearch = KLDRDYLD_SEARCH_HOST;
+
+
+/** @name The main stack.
+ * @{ */
+/** Indicates that the other MainStack globals have been filled in. */
+unsigned g_fkLdrDyldDoneMainStack = 0;
+/** Whether the stack was allocated seperatly or was part of the executable. */
+unsigned g_fkLdrDyldMainStackAllocated = 0;
+/** Pointer to the main stack object. */
+void *g_pvkLdrDyldMainStack = NULL;
+/** The size of the main stack object. */
+KSIZE g_cbkLdrDyldMainStack = 0;
+/** @} */
+
+
+/** The load stack.
+ * This contains frames with modules affected by active loads.
+ *
+ * Each kLdrDyldLoad and kLdrDyldLoadExe call will create a new stack frame containing
+ * all the modules involved in the operation. The modules will be ordered in recursive
+ * init order within the frame.
+ */
+static PPKLDRDYLDMOD g_papStackMods;
+/** The number of used entries in the g_papStackMods array. */
+static KU32 g_cStackMods;
+/** The number of entries allocated for the g_papStackMods array. */
+static KU32 g_cStackModsAllocated;
+/** Number of active load calls. */
+static KU32 g_cActiveLoadCalls;
+/** Number of active unload calls. */
+static KU32 g_cActiveUnloadCalls;
+/** Total number of load calls. */
+static KU32 g_cTotalLoadCalls;
+/** Total mumber of unload calls. */
+static KU32 g_cTotalUnloadCalls;
+/** Boolean flag indicating that GC is active. */
+static KU32 g_fActiveGC;
+
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+/** @name API worker routines.
+ * @internal
+ * @{ */
+void kldrDyldDoLoadExeStackSwitch(PKLDRDYLDMOD pExe, void *pvStack, KSIZE cbStack);
+static int kldrDyldDoLoad(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, KSIZE cchErr);
+static int kldrDyldDoLoad2(PKLDRDYLDMOD pLoadedMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags);
+static int kldrDyldDoLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags);
+static int kldrDyldDoUnload(PKLDRDYLDMOD pMod);
+static int kldrDyldDoFindByName(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PPKLDRDYLDMOD ppMod);
+static int kldrDyldDoFindByAddress(KUPTR Address, PPKLDRDYLDMOD ppMod, KU32 *piSegment, KUPTR *poffSegment);
+static int kldrDyldDoGetName(PKLDRDYLDMOD pMod, char *pszName, KSIZE cchName);
+static int kldrDyldDoGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename);
+static int kldrDyldDoQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName, KUPTR *pValue, KU32 *pfKind);
+/** @} */
+
+/** @name Misc load/unload workers
+ * @internal
+ * @{
+ */
+static void kldrDyldDoModuleTerminationAndGarabageCollection(void);
+/** @} */
+
+/** @name The load stack.
+ * @internal
+ * @{ */
+static KU32 kldrDyldStackNewFrame(PKLDRDYLDMOD pMod);
+static int kldrDyldStackAddModule(PKLDRDYLDMOD pMod);
+static int kldrDyldStackFrameCompleted(void);
+static void kldrDyldStackCleanupOne(PKLDRDYLDMOD pMod, int rc);
+static void kldrDyldStackDropFrame(KU32 iLoad1st, KU32 iLoadEnd, int rc);
+/** @} */
+
+static int kldrDyldCopyError(int rc, char *pszErr, KSIZE cchErr);
+
+
+
+/**
+ * Initialize the dynamic loader.
+ */
+int kldrDyldInit(void)
+{
+ kLdrDyldHead = kLdrDyldTail = NULL;
+ g_pkLdrDyldTermHead = g_pkLdrDyldTermTail = NULL;
+ g_pkLdrDyldBindHead = g_pkLdrDyldBindTail = NULL;
+ kLdrDyldFlags = 0;
+ g_szkLdrDyldError[0] = '\0';
+
+ g_fkLdrDyldDoneMainStack = 0;
+ g_fkLdrDyldMainStackAllocated = 0;
+ g_pvkLdrDyldMainStack = NULL;
+ g_cbkLdrDyldMainStack = 0;
+
+ return kldrDyldFindInit();
+}
+
+
+/**
+ * Terminate the dynamic loader.
+ */
+void kldrDyldTerm(void)
+{
+
+}
+
+
+/**
+ * Bootstrap an executable.
+ *
+ * This is called from the executable stub to replace the stub and run the
+ * executable specified in the argument package.
+ *
+ * Since this is boostrap time there isn't anything to return to. So, instead
+ * the process will be terminated upon failure.
+ *
+ * We also have to keep in mind that this function is called on a small, small,
+ * stack and therefore any kind of large stack objects or deep recursions must
+ * be avoided. Since loading the executable will involve more or less all
+ * operations in the loader, this restriction really applies everywhere.
+ *
+ * @param pArgs Pointer to the argument package residing in the executable stub.
+ * @param pvOS OS specific argument.
+ */
+#ifndef __OS2__ /* kLdrDyldLoadExe is implemented in assembly on OS/2. */
+void kLdrDyldLoadExe(PCKLDREXEARGS pArgs, void *pvOS)
+#else
+void kldrDyldLoadExe(PCKLDREXEARGS pArgs, void *pvOS)
+#endif
+{
+ void *pvStack;
+ KSIZE cbStack;
+ PKLDRDYLDMOD pExe;
+ int rc;
+
+ /*
+ * Indicate that we're boostrapping and ensure that initialization was successful.
+ */
+ g_fBootstrapping = 1;
+ rc = kldrInit();
+ if (rc)
+ kldrDyldFailure(rc, "Init failure, rc=%d", rc);
+
+ /*
+ * Validate the argument package.
+ */
+ if (pArgs->fFlags & ~( KLDRYDLD_LOAD_FLAGS_GLOBAL_SYMBOLS
+ | KLDRYDLD_LOAD_FLAGS_DEEP_SYMBOLS
+ | KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT
+ | KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE))
+ kldrDyldFailure(KERR_INVALID_PARAMETER, "Bad fFlags=%#x", pArgs->fFlags);
+ if ( pArgs->enmSearch <= KLDRDYLD_SEARCH_INVALID
+ || pArgs->enmSearch >= KLDRDYLD_SEARCH_END)
+ kldrDyldFailure(KERR_INVALID_PARAMETER, "Bad enmSearch=%d", pArgs->enmSearch);
+
+ /*
+ * Set defaults.
+ */
+ kLdrDyldFlags |= (pArgs->fFlags & KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT);
+ kLdrDyldSearch = pArgs->enmSearch;
+
+ /** @todo make sense of this default prefix/suffix stuff. */
+ if (pArgs->szDefPrefix[0] != '\0')
+ kHlpMemCopy(kLdrDyldDefPrefix, pArgs->szDefPrefix, K_MIN(sizeof(pArgs->szDefPrefix), sizeof(kLdrDyldDefPrefix)));
+ if (pArgs->szDefSuffix[0] != '\0')
+ kHlpMemCopy(kLdrDyldDefSuffix, pArgs->szDefSuffix, K_MIN(sizeof(pArgs->szDefSuffix), sizeof(kLdrDyldDefSuffix)));
+
+ /** @todo append that path to the one for the specified search method. */
+ /** @todo create a function for doing this, an exposed api preferably. */
+ /* append path */
+ cbStack = sizeof(kLdrDyldLibraryPath) - kHlpStrLen(kLdrDyldLibraryPath); /* borrow cbStack for a itty bit. */
+ kHlpMemCopy(kLdrDyldLibraryPath, pArgs->szLibPath, K_MIN(sizeof(pArgs->szLibPath), cbStack));
+ kLdrDyldLibraryPath[sizeof(kLdrDyldLibraryPath) - 1] = '\0';
+
+ /*
+ * Make sure we own the loader semaphore (necessary for init).
+ */
+ rc = kLdrDyldSemRequest();
+ if (rc)
+ kldrDyldFailure(rc, "Sem req. failure, rc=%d", rc);
+
+ /*
+ * Open and map the executable module before we join paths with kLdrDyldLoad().
+ */
+ rc = kldrDyldFindNewModule(pArgs->szExecutable, NULL, NULL, pArgs->enmSearch,
+ pArgs->fFlags | KLDRDYLD_LOAD_FLAGS_EXECUTABLE, &pExe);
+ if (rc)
+ kldrDyldFailure(rc, "Can't find/open the executable '%s', rc=%d", pArgs->szExecutable, rc);
+ rc = kldrDyldModMap(pExe);
+ if (rc)
+ kldrDyldFailure(rc, "Failed to map the executable '%s', rc=%d", pExe->pMod->pszFilename, rc);
+
+ kLdrDyldExe = pExe;
+
+ /*
+ * Query the stack and go to OS specific code to
+ * setup and switch stack. The OS specific code will call us
+ * back at kldrDyldDoLoadExe.
+ */
+ rc = kldrDyldModGetMainStack(pExe, &pvStack, &cbStack);
+ if (rc)
+ kldrDyldFailure(rc, "Failed to map the executable '%s', rc=%d", pExe->pMod->pszFilename, rc);
+ kldrDyldDoLoadExeStackSwitch(pExe, pvStack, cbStack);
+ kldrDyldFailure(-1, "Failed to setup the stack for '%s'.", pExe->pMod->pszFilename);
+}
+
+
+/**
+ * Loads a module into the current process.
+ *
+ * @returns 0 on success, non-zero native OS status code or kLdr status code on failure.
+ * @param pszDll The name of the dll to open.
+ * @param pszPrefix Prefix to use when searching.
+ * @param pszSuffix Suffix to use when searching.
+ * @param enmSearch Method to use when locating the module and any modules it may depend on.
+ * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines.
+ * @param phMod Where to store the handle to the loaded module.
+ * @param pszErr Where to store extended error information. (optional)
+ * @param cchErr The size of the buffer pointed to by pszErr.
+ */
+int kLdrDyldLoad(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PHKLDRMOD phMod, char *pszErr, KSIZE cchErr)
+{
+ int rc;
+
+ /* validate arguments and initialize return values. */
+ if (pszErr && cchErr)
+ *pszErr = '\0';
+ *phMod = NIL_HKLDRMOD;
+ K_VALIDATE_STRING(pszDll);
+ K_VALIDATE_OPTIONAL_STRING(pszPrefix);
+ K_VALIDATE_OPTIONAL_STRING(pszSuffix);
+ K_VALIDATE_ENUM(enmSearch, KLDRDYLD_SEARCH);
+ K_VALIDATE_OPTIONAL_BUFFER(pszErr, cchErr);
+
+ /* get the semaphore and do the job. */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ PKLDRDYLDMOD pMod = NULL;
+ g_cTotalLoadCalls++;
+ g_cActiveLoadCalls++;
+ rc = kldrDyldDoLoad(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod, pszErr, cchErr);
+ g_cActiveLoadCalls--;
+ kldrDyldDoModuleTerminationAndGarabageCollection();
+ kLdrDyldSemRelease();
+ *phMod = pMod ? pMod->hMod : NIL_HKLDRMOD;
+ }
+ return rc;
+}
+
+
+/**
+ * Unloads a module loaded by kLdrDyldLoad.
+ *
+ * @returns 0 on success, non-zero native OS status code or kLdr status code on failure.
+ * @param hMod Module handle.
+ */
+int kLdrDyldUnload(HKLDRMOD hMod)
+{
+ int rc;
+
+ /* validate */
+ KLDRDYLD_VALIDATE_HKLDRMOD(hMod);
+
+ /* get sem & do work */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ g_cTotalUnloadCalls++;
+ g_cActiveUnloadCalls++;
+ rc = kldrDyldDoUnload(hMod);
+ g_cActiveUnloadCalls--;
+ kldrDyldDoModuleTerminationAndGarabageCollection();
+ kLdrDyldSemRelease();
+ }
+ return rc;
+}
+
+
+/**
+ * Finds a module by name or filename.
+ *
+ * This call does not increase any reference counters and must not be
+ * paired with kLdrDyldUnload() like kLdrDyldLoad().
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND or some I/O error on failure.
+ * @param pszDll The name of the dll to look for.
+ * @param pszPrefix Prefix than can be used when searching.
+ * @param pszSuffix Suffix than can be used when searching.
+ * @param enmSearch Method to use when locating the module.
+ * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines.
+ * @param phMod Where to store the handle of the module on success.
+ */
+int kLdrDyldFindByName(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PHKLDRMOD phMod)
+{
+ int rc;
+
+ /* validate & initialize */
+ *phMod = NIL_HKLDRMOD;
+ K_VALIDATE_STRING(pszDll);
+
+ /* get sem & do work */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ PKLDRDYLDMOD pMod = NULL;
+ rc = kldrDyldDoFindByName(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod);
+ kLdrDyldSemRelease();
+ *phMod = pMod ? pMod->hMod : NIL_HKLDRMOD;
+ }
+ return rc;
+}
+
+
+/**
+ * Finds a module by address.
+ *
+ * This call does not increase any reference counters and must not be
+ * paired with kLdrDyldUnload() like kLdrDyldLoad().
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND on failure.
+ * @param Address The address believed to be within some module.
+ * @param phMod Where to store the module handle on success.
+ * @param piSegment Where to store the segment number. (optional)
+ * @param poffSegment Where to store the offset into the segment. (optional)
+ */
+int kLdrDyldFindByAddress(KUPTR Address, PHKLDRMOD phMod, KU32 *piSegment, KUPTR *poffSegment)
+{
+ int rc;
+
+ /* validate & initialize */
+ *phMod = NIL_HKLDRMOD;
+ if (piSegment)
+ *piSegment = ~(KU32)0;
+ if (poffSegment)
+ *poffSegment = ~(KUPTR)0;
+
+ /* get sem & do work */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ PKLDRDYLDMOD pMod = NULL;
+ rc = kldrDyldDoFindByAddress(Address, &pMod, piSegment, poffSegment);
+ kLdrDyldSemRelease();
+ *phMod = pMod ? pMod->hMod : NIL_HKLDRMOD;
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the module name.
+ *
+ * @returns 0 on success and pszName filled with the name.
+ * @returns KERR_INVALID_HANDLE or KERR_BUFFER_OVERFLOW on failure.
+ * @param hMod The module handle.
+ * @param pszName Where to put the name.
+ * @param cchName The size of the name buffer.
+ * @see kLdrDyldGetFilename
+ */
+int kLdrDyldGetName(HKLDRMOD hMod, char *pszName, KSIZE cchName)
+{
+ int rc;
+
+ /* validate */
+ if (pszName && cchName)
+ *pszName = '\0';
+ KLDRDYLD_VALIDATE_HKLDRMOD(hMod);
+ K_VALIDATE_BUFFER(pszName, cchName);
+
+ /* get sem & do work */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ rc = kldrDyldDoGetName(hMod, pszName, cchName);
+ kLdrDyldSemRelease();
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the module filename.
+ *
+ * @returns 0 on success and pszFilename filled with the name.
+ * @returns KERR_INVALID_HANDLE or KERR_BUFFER_OVERFLOW on failure.
+ * @param hMod The module handle.
+ * @param pszFilename Where to put the filename.
+ * @param cchFilename The size of the filename buffer.
+ * @see kLdrDyldGetName
+ */
+int kLdrDyldGetFilename(HKLDRMOD hMod, char *pszFilename, KSIZE cchFilename)
+{
+ int rc;
+
+ /* validate & initialize */
+ if (pszFilename && cchFilename);
+ *pszFilename = '\0';
+ KLDRDYLD_VALIDATE_HKLDRMOD(hMod);
+ K_VALIDATE_BUFFER(pszFilename, cchFilename);
+
+ /* get sem & do work */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ rc = kldrDyldDoGetFilename(hMod, pszFilename, cchFilename);
+ kLdrDyldSemRelease();
+ }
+ return rc;
+}
+
+
+/**
+ * Queries the value and type of a symbol.
+ *
+ * @returns 0 on success and pValue and pfKind set.
+ * @returns KERR_INVALID_HANDLE or KLDR_ERR_SYMBOL_NOT_FOUND on failure.
+ * @param hMod The module handle.
+ * @param uSymbolOrdinal The symbol ordinal. This is ignored if pszSymbolName is non-zero.
+ * @param pszSymbolName The symbol name.
+ * @param pszSymbolVersion The symbol version. Optional.
+ * @param pValue Where to put the symbol value. Optional if pfKind is non-zero.
+ * @param pfKind Where to put the symbol kind flags. Optional if pValue is non-zero.
+ */
+int kLdrDyldQuerySymbol(HKLDRMOD hMod, KU32 uSymbolOrdinal, const char *pszSymbolName,
+ const char *pszSymbolVersion, KUPTR *pValue, KU32 *pfKind)
+{
+ int rc;
+
+ /* validate & initialize */
+ if (pfKind)
+ *pfKind = 0;
+ if (pValue)
+ *pValue = 0;
+ if (!pfKind && !pValue)
+ return KERR_INVALID_PARAMETER;
+ KLDRDYLD_VALIDATE_HKLDRMOD(hMod);
+ K_VALIDATE_OPTIONAL_STRING(pszSymbolName);
+
+ /* get sem & do work */
+ rc = kLdrDyldSemRequest();
+ if (!rc)
+ {
+ rc = kldrDyldDoQuerySymbol(hMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind);
+ kLdrDyldSemRelease();
+ }
+ return rc;
+}
+
+
+/**
+ * Worker kLdrDoLoadExe().
+ * Used after we've switch to the final process stack.
+ *
+ * @param pExe The executable module.
+ * @internal
+ */
+void kldrDyldDoLoadExe(PKLDRDYLDMOD pExe)
+{
+ int rc;
+
+ /*
+ * Load the executable module with its prerequisites and initialize them.
+ */
+ g_cActiveLoadCalls++;
+ rc = kldrDyldDoLoad2(pExe, NULL, NULL, kLdrDyldSearch, kLdrDyldFlags | KLDRDYLD_LOAD_FLAGS_EXECUTABLE);
+ if (rc)
+ kldrDyldFailure(rc, "load 2 failed for '%s', rc=%d", pExe->pMod->pszFilename);
+ g_cActiveLoadCalls--;
+ kldrDyldDoModuleTerminationAndGarabageCollection();
+
+ /*
+ * Invoke the executable entry point.
+ */
+ kldrDyldModStartExe(pExe);
+ kldrDyldFailure(-1, "failed to invoke main!");
+}
+
+
+/**
+ * Worker for kLdrDyldLoad() and helper for kLdrDyldLoadExe().
+ * @internal
+ */
+static int kldrDyldDoLoad(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, KSIZE cchErr)
+{
+ int rc;
+
+ /*
+ * Try find the module among the ones that's already loaded.
+ */
+ rc = kldrDyldFindExistingModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, ppMod);
+ if (!rc)
+ {
+ switch ((*ppMod)->enmState)
+ {
+ /*
+ * Prerequisites are ok, so nothing to do really.
+ */
+ case KLDRSTATE_GOOD:
+ case KLDRSTATE_INITIALIZING:
+ return kldrDyldModDynamicLoad(*ppMod);
+
+ /*
+ * The module can't be loaded because it failed to initialize.
+ */
+ case KLDRSTATE_INITIALIZATION_FAILED:
+ return KLDR_ERR_MODULE_INIT_FAILED_ALREADY;
+
+ /*
+ * Prerequisites needs loading / reattaching and the module
+ * (may depending on fFlags) needs to be initialized.
+ */
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ break;
+
+ /*
+ * Prerequisites needs to be loaded again
+ */
+ case KLDRSTATE_PENDING_TERMINATION:
+ break;
+
+ /*
+ * The module has been terminated so it need to be reloaded, have it's
+ * prereqs loaded, fixed up and initialized before we can use it again.
+ */
+ case KLDRSTATE_PENDING_GC:
+ rc = kldrDyldModReload(*ppMod);
+ if (rc)
+ return kldrDyldCopyError(rc, pszErr, cchErr);
+ break;
+
+ /*
+ * Forget it, we don't know how to deal with re-initialization here.
+ */
+ case KLDRSTATE_TERMINATING:
+ KLDRDYLD_ASSERT(!"KLDR_ERR_MODULE_TERMINATING");
+ return KLDR_ERR_MODULE_TERMINATING;
+
+ /*
+ * Invalid state.
+ */
+ default:
+ KLDRDYLD_ASSERT(!"invalid state");
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * We'll have to load it from file.
+ */
+ rc = kldrDyldFindNewModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, ppMod);
+ if (rc)
+ return kldrDyldCopyError(rc, pszErr, cchErr);
+ rc = kldrDyldModMap(*ppMod);
+ }
+
+ /*
+ * Join cause with kLdrDyldLoadExe.
+ */
+ if (!rc)
+ rc = kldrDyldDoLoad2(*ppMod, pszPrefix, pszSuffix, enmSearch, fFlags);
+ else
+ kldrDyldStackCleanupOne(*ppMod, rc);
+
+ /*
+ * Copy any error or warning to the error buffer.
+ */
+ return kldrDyldCopyError(rc, pszErr, cchErr);
+}
+
+
+/**
+ * 2nd half of kLdrDyldLoad() and kLdrDyldLoadExe().
+ *
+ * @internal
+ */
+static int kldrDyldDoLoad2(PKLDRDYLDMOD pLoadedMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags)
+{
+ /*
+ * Load prerequisites.
+ */
+ KU32 i;
+ KU32 iLoad1st = kldrDyldStackNewFrame(pLoadedMod);
+ int rc = kldrDyldDoLoadPrerequisites(pLoadedMod, pszPrefix, pszSuffix, enmSearch, fFlags);
+ KU32 iLoadEnd = kldrDyldStackFrameCompleted();
+ if (rc)
+ {
+ kldrDyldModAddRef(pLoadedMod);
+ kldrDyldStackCleanupOne(pLoadedMod, rc); /* in case it didn't get pushed onto the stack. */
+ kldrDyldModDeref(pLoadedMod);
+ }
+
+ /*
+ * Apply fixups.
+ */
+ for (i = iLoad1st; !rc && i < iLoadEnd; i++)
+ {
+ PKLDRDYLDMOD pMod = g_papStackMods[i];
+ if ( pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES
+ || pMod->enmState == KLDRSTATE_RELOADED_LOADED_PREREQUISITES)
+ rc = kldrDyldModFixup(pMod);
+ }
+
+ /*
+ * Advance fixed up module onto initialization.
+ */
+ for (i = iLoad1st; !rc && i < iLoadEnd; i++)
+ {
+ PKLDRDYLDMOD pMod = g_papStackMods[i];
+ if ( pMod->enmState == KLDRSTATE_FIXED_UP
+ || pMod->enmState == KLDRSTATE_RELOADED_FIXED_UP)
+ pMod->enmState = KLDRSTATE_PENDING_INITIALIZATION;
+ KLDRDYLD_ASSERT( pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION
+ || pMod->enmState == KLDRSTATE_GOOD);
+ }
+
+ /*
+ * Call the initializers if we're loading in recursive mode or
+ * if we're the outermost load call.
+ */
+ if (fFlags & KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT)
+ {
+ for (i = iLoad1st; !rc && i < iLoadEnd; i++)
+ {
+ PKLDRDYLDMOD pMod = g_papStackMods[i];
+ if (pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION)
+ rc = kldrDyldModCallInit(pMod);
+ else if (pMod->enmState == KLDRSTATE_INITIALIZATION_FAILED)
+ rc = KLDR_ERR_PREREQUISITE_MODULE_INIT_FAILED_ALREADY;
+ else
+ KLDRDYLD_ASSERT(g_papStackMods[i]->enmState == KLDRSTATE_GOOD);
+ }
+#ifdef KLDRDYLD_STRICT
+ for (i = iLoad1st; !rc && i < iLoadEnd; i++)
+ KLDRDYLD_ASSERT(g_papStackMods[i]->enmState == KLDRSTATE_GOOD);
+#endif
+ }
+ else if (g_cActiveLoadCalls <= 1)
+ {
+ while (!rc && g_pkLdrDyldInitHead)
+ {
+ PKLDRDYLDMOD pMod = g_pkLdrDyldInitHead;
+ g_pkLdrDyldInitHead = pMod->InitTerm.pNext;
+ if (pMod->InitTerm.pNext)
+ pMod->InitTerm.pNext->InitTerm.pPrev = NULL;
+ else
+ g_pkLdrDyldInitTail = NULL;
+ pMod->fInitList = 0;
+ rc = kldrDyldModCallInit(pMod);
+ }
+ }
+
+ /*
+ * Complete the load by incrementing the dynamic load count of the
+ * requested module (return handle is already set).
+ */
+ if (!rc)
+ {
+ if (fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE)
+ {
+ pLoadedMod->cDepRefs++; /* just make it stick. */
+ pLoadedMod->cRefs++;
+ }
+ else
+ rc = kldrDyldModDynamicLoad(pLoadedMod);
+ }
+
+ kldrDyldStackDropFrame(iLoad1st, iLoadEnd, rc);
+ return rc;
+}
+
+
+/**
+ * kldrDyldDoLoad() helper which will load prerequisites and
+ * build the initialization array / list.
+ *
+ * @returns 0 on success, non-zero error code on failure.
+ * @param pMod The module to start at.
+ * @param pszPrefix Prefix to use when searching.
+ * @param pszSuffix Suffix to use when searching.
+ * @param enmSearch Method to use when locating the module and any modules it may depend on.
+ * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines.
+ */
+static int kldrDyldDoLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags)
+{
+ static struct
+ {
+ /** The module. */
+ PKLDRDYLDMOD pMod;
+ /** The number of prerequisite modules left to process.
+ * This starts at ~0U to inidicate that we need to load/check prerequisistes. */
+ unsigned cLeft;
+ } s_aEntries[64];
+ unsigned cEntries;
+ int rc = 0;
+
+ /* Prerequisites are always global and they just aren't executables. */
+ fFlags &= ~(KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE | KLDRDYLD_LOAD_FLAGS_EXECUTABLE);
+
+ /* push the first entry. */
+ s_aEntries[0].pMod = pMod;
+ s_aEntries[0].cLeft = ~0U;
+ cEntries = 1;
+
+ /*
+ * The recursion loop.
+ */
+ while (!rc && cEntries > 0)
+ {
+ const unsigned i = cEntries - 1;
+ pMod = s_aEntries[i].pMod;
+ if (s_aEntries[i].cLeft == ~0U)
+ {
+ /*
+ * Load prerequisite modules.
+ */
+ switch (pMod->enmState)
+ {
+ /*
+ * Load immediate prerequisite modules and push the ones needing
+ * attention onto the stack.
+ */
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_RELOADED:
+ case KLDRSTATE_PENDING_TERMINATION:
+ rc = kldrDyldModLoadPrerequisites(pMod, pszPrefix, pszSuffix, enmSearch, fFlags);
+ KLDRDYLD_ASSERT( pMod->enmState == KLDRSTATE_GOOD
+ || pMod->enmState == KLDRSTATE_RELOADED_LOADED_PREREQUISITES
+ || pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES
+ || rc);
+ if (!rc)
+ s_aEntries[i].cLeft = pMod->cPrereqs;
+ break;
+
+ /*
+ * Check its prerequisite modules the first time around.
+ */
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ if (pMod->fAlreadySeen)
+ break;
+ pMod->fAlreadySeen = 1;
+ s_aEntries[i].cLeft = pMod->cPrereqs;
+ break;
+
+ /*
+ * These are ok.
+ */
+ case KLDRSTATE_LOADED_PREREQUISITES:
+ case KLDRSTATE_RELOADED_LOADED_PREREQUISITES:
+ case KLDRSTATE_INITIALIZING:
+ case KLDRSTATE_GOOD:
+ s_aEntries[i].cLeft = 0;
+ break;
+
+ /*
+ * All other stats are invalid.
+ */
+ default:
+ KLDRDYLD_ASSERT(!"invalid state");
+ break;
+ }
+ }
+ else if (s_aEntries[i].cLeft > 0)
+ {
+ /*
+ * Recurse down into the next prereq.
+ */
+ KLDRDYLD_ASSERT(s_aEntries[i].cLeft <= pMod->cPrereqs);
+ if (cEntries < sizeof(s_aEntries) / sizeof(s_aEntries[0]))
+ {
+ s_aEntries[cEntries].cLeft = ~(KU32)0;
+ s_aEntries[cEntries].pMod = pMod->papPrereqs[pMod->cPrereqs - s_aEntries[i].cLeft];
+ s_aEntries[i].cLeft--;
+ cEntries++;
+ }
+ else
+ rc = KLDR_ERR_PREREQUISITE_RECURSED_TOO_DEEPLY;
+ }
+ else
+ {
+ /*
+ * We're done with this module, record it for init/cleanup.
+ */
+ cEntries--;
+ if (pMod->enmState != KLDRSTATE_GOOD)
+ {
+ kldrDyldStackAddModule(pMod);
+ if ( !(fFlags & KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT)
+ && !pMod->fInitList)
+ {
+ pMod->fInitList = 1;
+ pMod->InitTerm.pNext = NULL;
+ pMod->InitTerm.pPrev = g_pkLdrDyldInitTail;
+ if (g_pkLdrDyldInitTail)
+ g_pkLdrDyldInitTail->InitTerm.pNext = pMod;
+ else
+ g_pkLdrDyldInitHead = pMod;
+ g_pkLdrDyldInitTail = pMod;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Gets prerequisite module.
+ *
+ * This will try load the requested module if necessary, returning it in the MAPPED state.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND or I/O error on failure.
+ * @param pszDll The name of the dll to look for.
+ * @param pszPrefix Prefix than can be used when searching.
+ * @param pszSuffix Suffix than can be used when searching.
+ * @param enmSearch Method to use when locating the module.
+ * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines.
+ * @param pDep The depentant module.
+ * @param ppMod Where to put the module we get.
+ */
+int kldrDyldGetPrerequisite(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PKLDRDYLDMOD pDep, PPKLDRDYLDMOD ppMod)
+{
+ int rc;
+ PKLDRDYLDMOD pMod;
+
+ *ppMod = NULL;
+
+ /*
+ * Try find the module among the ones that's already loaded.
+ *
+ * This is very similar to the kldrDyldDoLoad code, except it has to deal with
+ * a couple of additional states and occurs only during prerequisite loading
+ * and the action taken is a little bit different.
+ */
+ rc = kldrDyldFindExistingModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod);
+ if (!rc)
+ {
+ switch (pMod->enmState)
+ {
+ /*
+ * These are good.
+ */
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_RELOADED:
+ case KLDRSTATE_LOADED_PREREQUISITES:
+ case KLDRSTATE_RELOADED_LOADED_PREREQUISITES:
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ case KLDRSTATE_INITIALIZING:
+ case KLDRSTATE_GOOD:
+ case KLDRSTATE_PENDING_TERMINATION:
+ break;
+
+ /*
+ * The module has been terminated so it need to be reloaded, have it's
+ * prereqs loaded, fixed up and initialized before we can use it again.
+ */
+ case KLDRSTATE_PENDING_GC:
+ rc = kldrDyldModReload(pMod);
+ break;
+
+ /*
+ * The module can't be loaded because it failed to initialize already.
+ */
+ case KLDRSTATE_INITIALIZATION_FAILED:
+ rc = KLDR_ERR_PREREQUISITE_MODULE_INIT_FAILED;
+ break;
+
+ /*
+ * Forget it, no idea how to deal with re-initialization.
+ */
+ case KLDRSTATE_TERMINATING:
+ return KLDR_ERR_PREREQUISITE_MODULE_TERMINATING;
+
+ /*
+ * Invalid state.
+ */
+ default:
+ KLDRDYLD_ASSERT(!"invalid state");
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * We'll have to load it from file.
+ */
+ rc = kldrDyldFindNewModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod);
+ if (!rc)
+ rc = kldrDyldModMap(pMod);
+ }
+
+ /*
+ * On success add dependency.
+ */
+ if (!rc)
+ {
+ kldrDyldModAddDep(pMod, pDep);
+ *ppMod = pMod;
+ }
+ return rc;
+}
+
+
+/**
+ * Starts a new load stack frame.
+ *
+ * @returns Where the new stack frame starts.
+ * @param pLoadMod The module being loaded (only used for asserting).
+ */
+static KU32 kldrDyldStackNewFrame(PKLDRDYLDMOD pLoadMod)
+{
+ /*
+ * Clear the fAlreadySeen flags.
+ */
+ PKLDRDYLDMOD pMod = kLdrDyldHead;
+ while (pMod)
+ {
+ pMod->fAlreadySeen = 0;
+
+#ifdef KLDRDYLD_ASSERT
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_RELOADED:
+ /* only the just loaded module can be in this state. */
+ KLDRDYLD_ASSERT(pMod == pLoadMod);
+ break;
+
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ case KLDRSTATE_INITIALIZING:
+ case KLDRSTATE_PENDING_TERMINATION:
+ case KLDRSTATE_PENDING_GC:
+ case KLDRSTATE_TERMINATING:
+ case KLDRSTATE_INITIALIZATION_FAILED:
+ case KLDRSTATE_PENDING_DESTROY:
+ /* requires recursion. */
+ KLDRDYLD_ASSERT(g_cActiveLoadCalls + g_cActiveLoadCalls + g_fActiveGC > 1);
+ break;
+
+ case KLDRSTATE_GOOD:
+ /* requires nothing. */
+ break;
+
+ default:
+ KLDRDYLD_ASSERT(!"Invalid state");
+ break;
+ }
+#endif
+
+ /* next */
+ pMod = pMod->Load.pNext;
+ }
+ return g_cStackMods;
+}
+
+
+/**
+ * Records the module.
+ *
+ * @return 0 on success, KERR_NO_MEMORY if we can't expand the table.
+ * @param pMod The module to record.
+ */
+static int kldrDyldStackAddModule(PKLDRDYLDMOD pMod)
+{
+ /*
+ * Grow the stack if necessary.
+ */
+ if (g_cStackMods + 1 > g_cStackModsAllocated)
+ {
+ KU32 cNew = g_cStackModsAllocated ? g_cStackModsAllocated * 2 : 128;
+ void *pvOld = g_papStackMods;
+ void *pvNew = kHlpAlloc(cNew * sizeof(g_papStackMods[0]));
+ if (!pvNew)
+ return KERR_NO_MEMORY;
+ kHlpMemCopy(pvNew, pvOld, g_cStackMods * sizeof(g_papStackMods[0]));
+ g_papStackMods = (PPKLDRDYLDMOD)pvNew;
+ kHlpFree(pvOld);
+ }
+
+ /*
+ * Add a reference and push the module onto the stack.
+ */
+ kldrDyldModAddRef(pMod);
+ g_papStackMods[g_cStackMods++] = pMod;
+ return 0;
+}
+
+
+/**
+ * The frame has been completed.
+ *
+ * @returns Where the frame ends.
+ */
+static int kldrDyldStackFrameCompleted(void)
+{
+ return g_cStackMods;
+}
+
+
+/**
+ * Worker routine for kldrDyldStackDropFrame() and kldrDyldDoLoad().
+ *
+ * @param pMod The module to perform cleanups on.
+ * @param rc Used for state verification.
+ */
+static void kldrDyldStackCleanupOne(PKLDRDYLDMOD pMod, int rc)
+{
+ switch (pMod->enmState)
+ {
+ /*
+ * Just push it along to the PENDING_DESTROY state.
+ */
+ case KLDRSTATE_MAPPED:
+ KLDRDYLD_ASSERT(rc);
+ kldrDyldModUnmap(pMod);
+ KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY);
+ break;
+
+ /*
+ * Move back to PENDING_GC.
+ */
+ case KLDRSTATE_RELOADED:
+ KLDRDYLD_ASSERT(rc);
+ pMod->enmState = KLDRSTATE_PENDING_GC;
+ break;
+
+ /*
+ * Unload prerequisites and unmap the modules.
+ */
+ case KLDRSTATE_LOADED_PREREQUISITES:
+ case KLDRSTATE_FIXED_UP:
+ KLDRDYLD_ASSERT(rc);
+ kldrDyldModUnloadPrerequisites(pMod);
+ KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY);
+ kldrDyldModUnmap(pMod);
+ KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY);
+ break;
+
+ /*
+ * Unload prerequisites and push it back to PENDING_GC.
+ */
+ case KLDRSTATE_RELOADED_LOADED_PREREQUISITES:
+ case KLDRSTATE_RELOADED_FIXED_UP:
+ kldrDyldModUnloadPrerequisites(pMod);
+ KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_GC);
+ break;
+
+ /*
+ * Nothing to do, just asserting sanity.
+ */
+ case KLDRSTATE_INITIALIZING:
+ /* Implies there is another load going on. */
+ KLDRDYLD_ASSERT(g_cActiveLoadCalls > 1);
+ break;
+ case KLDRSTATE_TERMINATING:
+ /* GC in progress. */
+ KLDRDYLD_ASSERT(g_fActiveGC);
+ break;
+ case KLDRSTATE_PENDING_TERMINATION:
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ case KLDRSTATE_PENDING_GC:
+ case KLDRSTATE_PENDING_DESTROY:
+ KLDRDYLD_ASSERT(rc);
+ break;
+ case KLDRSTATE_GOOD:
+ break;
+
+ /*
+ * Bad states.
+ */
+ default:
+ KLDRDYLD_ASSERT(!"drop frame bad state (a)");
+ break;
+ }
+}
+
+
+/**
+ * Done with the stack frame, dereference all the modules in it.
+ *
+ * @param iLoad1st The start of the stack frame.
+ * @param iLoadEnd The end of the stack frame.
+ * @param rc Used for state verification.
+ */
+static void kldrDyldStackDropFrame(KU32 iLoad1st, KU32 iLoadEnd, int rc)
+{
+ KU32 i;
+ KLDRDYLD_ASSERT(iLoad1st <= g_cStackMods);
+ KLDRDYLD_ASSERT(iLoadEnd == g_cStackMods);
+
+ /*
+ * First pass: Do all the cleanups we can, but don't destroy anything just yet.
+ */
+ i = iLoadEnd;
+ while (i-- > iLoad1st)
+ {
+ PKLDRDYLDMOD pMod = g_papStackMods[i];
+ kldrDyldStackCleanupOne(pMod, rc);
+ }
+
+ /*
+ * Second pass: Release the references so modules pending destruction
+ * can be completely removed.
+ */
+ for (i = iLoad1st; i < iLoadEnd ; i++)
+ {
+ PKLDRDYLDMOD pMod = g_papStackMods[i];
+
+ /*
+ * Revalidate the module state.
+ */
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_INITIALIZING:
+ case KLDRSTATE_TERMINATING:
+ case KLDRSTATE_PENDING_TERMINATION:
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ case KLDRSTATE_PENDING_GC:
+ case KLDRSTATE_PENDING_DESTROY:
+ case KLDRSTATE_GOOD:
+ break;
+ default:
+ KLDRDYLD_ASSERT(!"drop frame bad state (b)");
+ break;
+ }
+
+ /*
+ * Release it.
+ */
+ kldrDyldModDeref(pMod);
+ }
+
+ /*
+ * Drop the stack frame.
+ */
+ g_cStackMods = iLoad1st;
+}
+
+
+/**
+ * Do garbage collection.
+ *
+ * This isn't doing anything unless it's called from the last
+ * load or unload call.
+ */
+static void kldrDyldDoModuleTerminationAndGarabageCollection(void)
+{
+ PKLDRDYLDMOD pMod;
+
+ /*
+ * We don't do anything until we're got rid of all recursive calls.
+ * This will ensure that we get the most optimal termination order and
+ * that we don't unload anything too early.
+ */
+ if (g_cActiveLoadCalls || g_cActiveUnloadCalls || g_fActiveGC)
+ return;
+ g_fActiveGC = 1;
+
+ do
+ {
+ /*
+ * 1. Release prerequisites for any left over modules.
+ */
+ for (pMod = kLdrDyldHead; pMod; pMod = pMod->Load.pNext)
+ {
+ kldrDyldModAddRef(pMod);
+
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_GOOD:
+ case KLDRSTATE_PENDING_GC:
+ case KLDRSTATE_PENDING_TERMINATION:
+ break;
+
+ case KLDRSTATE_INITIALIZATION_FAILED: /* just in case */
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ kldrDyldModUnloadPrerequisites(pMod);
+ break;
+
+ default:
+ KLDRDYLD_ASSERT(!"invalid GC state (a)");
+ break;
+ }
+
+ kldrDyldModDeref(pMod);
+ }
+
+ /*
+ * 2. Do init calls until we encounter somebody calling load/unload.
+ */
+ for (pMod = g_pkLdrDyldTermHead; pMod; pMod = pMod->InitTerm.pNext)
+ {
+ int fRestart = 0;
+ kldrDyldModAddRef(pMod);
+
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_GOOD:
+ case KLDRSTATE_PENDING_GC:
+ break;
+
+ case KLDRSTATE_PENDING_TERMINATION:
+ {
+ const KU32 cTotalLoadCalls = g_cTotalLoadCalls;
+ const KU32 cTotalUnloadCalls = g_cTotalUnloadCalls;
+ kldrDyldModCallTerm(pMod);
+ fRestart = cTotalLoadCalls != g_cTotalLoadCalls
+ || cTotalUnloadCalls != g_cTotalUnloadCalls;
+ break;
+ }
+
+ default:
+ KLDRDYLD_ASSERT(!"invalid GC state (b)");
+ break;
+ }
+
+ kldrDyldModDeref(pMod);
+ if (fRestart)
+ break;
+ }
+ } while (pMod);
+
+ /*
+ * Unmap and destroy modules pending for GC.
+ */
+ pMod = kLdrDyldHead;
+ while (pMod)
+ {
+ PKLDRDYLDMOD pNext = pMod->Load.pNext;
+ kldrDyldModAddRef(pMod);
+
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_INITIALIZATION_FAILED:
+ case KLDRSTATE_PENDING_GC:
+ KLDRDYLD_ASSERT(!pMod->cDepRefs);
+ KLDRDYLD_ASSERT(!pMod->cDynRefs);
+ pMod->enmState = KLDRSTATE_GC;
+ kldrDyldModUnmap(pMod);
+ KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY);
+ kldrDyldModDestroy(pMod);
+ KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_DESTROYED);
+ break;
+
+ case KLDRSTATE_GOOD:
+ break;
+ default:
+ KLDRDYLD_ASSERT(!"invalid GC state (c)");
+ break;
+ }
+
+ kldrDyldModDeref(pMod);
+
+ /* next */
+ pMod = pNext;
+ }
+
+ g_fActiveGC = 0;
+}
+
+
+/**
+ * Worker for kLdrDyldUnload().
+ * @internal
+ */
+static int kldrDyldDoUnload(PKLDRDYLDMOD pMod)
+{
+ return kldrDyldModDynamicUnload(pMod);
+}
+
+
+/**
+ * Worker for kLdrDyldFindByName().
+ * @internal
+ */
+static int kldrDyldDoFindByName(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PPKLDRDYLDMOD ppMod)
+{
+ return kldrDyldFindExistingModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, ppMod);
+}
+
+
+/**
+ * Worker for kLdrDyldFindByAddress().
+ * @internal
+ */
+static int kldrDyldDoFindByAddress(KUPTR Address, PPKLDRDYLDMOD ppMod, KU32 *piSegment, KUPTR *poffSegment)
+{
+ /* Scan the segments of each module in the load list. */
+ PKLDRDYLDMOD pMod = kLdrDyldHead;
+ while (pMod)
+ {
+ KU32 iSeg;
+ for (iSeg = 0; iSeg < pMod->pMod->cSegments; iSeg++)
+ {
+ KLDRADDR off = (KLDRADDR)Address - pMod->pMod->aSegments[iSeg].MapAddress;
+ if (off < pMod->pMod->aSegments[iSeg].cb)
+ {
+ *ppMod = pMod->hMod;
+ if (piSegment)
+ *piSegment = iSeg;
+ if (poffSegment)
+ *poffSegment = (KUPTR)off;
+ return 0;
+ }
+ }
+
+ /* next */
+ pMod = pMod->Load.pNext;
+ }
+
+ return KLDR_ERR_MODULE_NOT_FOUND;
+}
+
+
+/**
+ * Worker for kLdrDyldGetName().
+ * @internal
+ */
+static int kldrDyldDoGetName(PKLDRDYLDMOD pMod, char *pszName, KSIZE cchName)
+{
+ return kldrDyldModGetName(pMod, pszName, cchName);
+}
+
+
+/**
+ * Worker for kLdrDyldGetFilename().
+ * @internal
+ */
+static int kldrDyldDoGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename)
+{
+ return kldrDyldModGetFilename(pMod, pszFilename, cchFilename);
+}
+
+
+/**
+ * Worker for kLdrDyldQuerySymbol().
+ * @internal
+ */
+static int kldrDyldDoQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName, KUPTR *pValue, KU32 *pfKind)
+{
+ return kldrDyldModQuerySymbol(pMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind);
+}
+
+
+/**
+ * Panic / failure
+ *
+ * @returns rc if we're in a position where we can return.
+ * @param rc Return code.
+ * @param pszFormat Message string. Limited fprintf like formatted.
+ * @param ... Message string arguments.
+ */
+int kldrDyldFailure(int rc, const char *pszFilename, ...)
+{
+ /** @todo print it. */
+ if (g_fBootstrapping);
+ kHlpExit(1);
+ return rc;
+}
+
+
+/**
+ * Copies the error string to the user buffer.
+ *
+ * @returns rc.
+ * @param rc The status code.
+ * @param pszErr Where to copy the error string to.
+ * @param cchErr The size of the destination buffer.
+ */
+static int kldrDyldCopyError(int rc, char *pszErr, KSIZE cchErr)
+{
+ KSIZE cchToCopy;
+
+ /* if no error string, format the rc into a string. */
+ if (!g_szkLdrDyldError[0] && rc)
+ kHlpInt2Ascii(g_szkLdrDyldError, sizeof(g_szkLdrDyldError), rc, 10);
+
+ /* copy it if we got something. */
+ if (cchErr && pszErr && g_szkLdrDyldError[0])
+ {
+ cchToCopy = kHlpStrLen(g_szkLdrDyldError);
+ if (cchToCopy >= cchErr)
+ cchToCopy = cchErr - 1;
+ kHlpMemCopy(pszErr, g_szkLdrDyldError, cchToCopy);
+ pszErr[cchToCopy] = '\0';
+ }
+
+ return rc;
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrDyldFind.c b/src/lib/kStuff/kLdr/kLdrDyldFind.c
new file mode 100644
index 0000000..9d6562e
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyldFind.c
@@ -0,0 +1,1086 @@
+/* $Id: kLdrDyldFind.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, File Searching Methods.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+#if K_OS == K_OS_LINUX
+# include <k/kHlpSys.h>
+
+#elif K_OS == K_OS_OS2
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+# ifndef LIBPATHSTRICT
+# define LIBPATHSTRICT 3
+# endif
+ extern APIRET APIENTRY DosQueryHeaderInfo(HMODULE hmod, ULONG ulIndex, PVOID pvBuffer, ULONG cbBuffer, ULONG ulSubFunction);
+# define QHINF_EXEINFO 1 /* NE exeinfo. */
+# define QHINF_READRSRCTBL 2 /* Reads from the resource table. */
+# define QHINF_READFILE 3 /* Reads from the executable file. */
+# define QHINF_LIBPATHLENGTH 4 /* Gets the libpath length. */
+# define QHINF_LIBPATH 5 /* Gets the entire libpath. */
+# define QHINF_FIXENTRY 6 /* NE only */
+# define QHINF_STE 7 /* NE only */
+# define QHINF_MAPSEL 8 /* NE only */
+
+#elif K_OS == K_OS_WINDOWS
+# undef IMAGE_DOS_SIGNATURE
+# undef IMAGE_NT_SIGNATURE
+# include <Windows.h>
+
+#endif
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRDYLDFIND_STRICT
+ * Define KLDRDYLDFIND_STRICT to enabled strict checks in kLdrDyldFind. */
+#define KLDRDYLDFIND_STRICT 1
+
+/** @def KLDRDYLDFIND_ASSERT
+ * Assert that an expression is true when KLDRDYLDFIND_STRICT is defined.
+ */
+#ifdef KLDRDYLDFIND_STRICT
+# define KLDRDYLDFIND_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRDYLDFIND_ASSERT(expr) do {} while (0)
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Search arguments.
+ * This avoids a bunch of unnecessary string lengths and calculations.
+ */
+typedef struct KLDRDYLDFINDARGS
+{
+ const char *pszName;
+ KSIZE cchName;
+
+ const char *pszPrefix;
+ KSIZE cchPrefix;
+
+ const char *pszSuffix;
+ KSIZE cchSuffix;
+
+ KSIZE cchMaxLength;
+
+ KLDRDYLDSEARCH enmSearch;
+ KU32 fFlags;
+ PPKRDR ppRdr;
+} KLDRDYLDFINDARGS, *PKLDRDYLDFINDARGS;
+
+typedef const KLDRDYLDFINDARGS *PCKLDRDYLDFINDARGS;
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** @name The kLdr search method parameters.
+ * @{ */
+/** The kLdr EXE search path.
+ * During EXE searching the it's initialized with the values of the KLDR_PATH and
+ * the PATH env.vars. Both ';' and ':' can be used as separators.
+ */
+char kLdrDyldExePath[8192];
+/** The kLdr DLL search path.
+ * During initialization the KLDR_LIBRARY_PATH env.var. and the path in the
+ * executable stub is appended. Both ';' and ':' can be used as separators.
+ */
+char kLdrDyldLibraryPath[8192];
+/** The kLdr application directory.
+ * This is initialized when the executable is 'loaded' or by a kLdr user.
+ */
+char kLdrDyldAppDir[260];
+/** The default kLdr DLL prefix.
+ * This is initialized with the KLDR_DEF_PREFIX env.var. + the prefix in the executable stub.
+ */
+char kLdrDyldDefPrefix[16];
+/** The default kLdr DLL suffix.
+ * This is initialized with the KLDR_DEF_SUFFIX env.var. + the prefix in the executable stub.
+ */
+char kLdrDyldDefSuffix[16];
+/** @} */
+
+
+/** @name The OS/2 search method parameters.
+ * @{
+ */
+/** The OS/2 LIBPATH.
+ * This is queried from the os2krnl on OS/2, while on other systems initialized using
+ * the KLDR_OS2_LIBPATH env.var.
+ */
+char kLdrDyldOS2Libpath[2048];
+/** The OS/2 LIBPATHSTRICT ("T" or '\0').
+ * This is queried from the os2krnl on OS/2, while on other systems initialized using
+ * the KLDR_OS2_LIBPATHSTRICT env.var.
+ */
+char kLdrDyldOS2LibpathStrict[8];
+/** The OS/2 BEGINLIBPATH.
+ * This is queried from the os2krnl on OS/2, while on other systems initialized using
+ * the KLDR_OS2_BEGINLIBPATH env.var.
+ */
+char kLdrDyldOS2BeginLibpath[2048];
+/** The OS/2 ENDLIBPATH.
+ * This is queried from the os2krnl on OS/2, while on other systems initialized using
+ * the KLDR_OS2_ENDLIBPATH env.var.
+ */
+char kLdrDyldOS2EndLibpath[2048];
+/** @} */
+
+
+/** @name The Windows search method parameters.
+ * @{ */
+/** The Windows application directory.
+ * This is initialized when the executable is 'loaded' or by a kLdr user.
+ */
+char kLdrDyldWindowsAppDir[260];
+/** The Windows system directory.
+ * This is queried from the Win32/64 subsystem on Windows, while on other systems
+ * initialized using the KLDR_WINDOWS_SYSTEM_DIR env.var.
+ */
+char kLdrDyldWindowsSystemDir[260];
+/** The Windows directory.
+ * This is queried from the Win32/64 subsystem on Windows, while on other systems
+ * initialized using the KLDR_WINDOWS_DIR env.var.
+ */
+char kLdrDyldWindowsDir[260];
+/** The Windows path.
+ * This is queried from the PATH env.var. on Windows, while on other systems
+ * initialized using the KLDR_WINDOWS_PATH env.var. and falling back on
+ * the PATH env.var. if it wasn't found.
+ */
+char kLdrDyldWindowsPath[8192];
+/** @} */
+
+
+/** @name The Common Unix search method parameters.
+ * @{
+ */
+/** The Common Unix library path.
+ * Initialized from the env.var. KLDR_UNIX_LIBRARY_PATH or LD_LIBRARY_PATH or the
+ * former wasn't found.
+ */
+char kLdrDyldUnixLibraryPath[8192];
+/** The Common Unix system library path. */
+char kLdrDyldUnixSystemLibraryPath[1024] = "/lib;/usr/lib";
+/** @} */
+
+/** @todo Deal with DT_RUNPATH and DT_RPATH. */
+/** @todo ld.so.cache? */
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static int kldrDyldFindDoDllSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKRDR ppRdr);
+static int kldrDyldFindDoExeSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKRDR ppRdr);
+static int kldrDyldFindTryOpen(const char *pszFilename, PPKRDR ppRdr);
+static int kldrDyldFindTryOpenPath(const char *pchPath, KSIZE cchPath, PCKLDRDYLDFINDARGS pArgs);
+static int kldrDyldFindEnumeratePath(const char *pszSearchPath, PCKLDRDYLDFINDARGS pArgs);
+static int kldrDyldFindGetDefaults(KLDRDYLDSEARCH *penmSearch, const char **pszPrefix,
+ const char **pszSuffix, const char *pszName, KU32 fFlags);
+
+
+/**
+ * Initializes the find paths.
+ *
+ * @returns 0 on success, non-zero on failure.
+ */
+int kldrDyldFindInit(void)
+{
+ KSIZE cch;
+ int rc;
+ char szTmp[sizeof(kLdrDyldDefSuffix)];
+
+ /*
+ * The kLdr search parameters.
+ */
+ rc = kHlpGetEnv("KLDR_LIBRARY_PATH", kLdrDyldLibraryPath, sizeof(kLdrDyldLibraryPath));
+ rc = kHlpGetEnv("KLDR_DEF_PREFIX", szTmp, sizeof(szTmp));
+ if (!rc)
+ kHlpMemCopy(kLdrDyldDefPrefix, szTmp, sizeof(szTmp));
+ rc = kHlpGetEnv("KLDR_DEF_SUFFIX", szTmp, sizeof(szTmp));
+ if (!rc)
+ kHlpMemCopy(kLdrDyldDefSuffix, szTmp, sizeof(szTmp));
+
+ /*
+ * The OS/2 search parameters.
+ */
+#if K_OS == K_OS_OS2
+ rc = DosQueryHeaderInfo(NULLHANDLE, 0, kLdrDyldOS2Libpath, sizeof(kLdrDyldOS2Libpath), QHINF_LIBPATH);
+ if (rc)
+ return rc;
+ rc = DosQueryExtLIBPATH((PSZ)kLdrDyldOS2LibpathStrict, LIBPATHSTRICT);
+ if (rc)
+ kLdrDyldOS2LibpathStrict[0] = '\0';
+ rc = DosQueryExtLIBPATH((PSZ)kLdrDyldOS2BeginLibpath, BEGIN_LIBPATH);
+ if (rc)
+ kLdrDyldOS2BeginLibpath[0] = '\0';
+ rc = DosQueryExtLIBPATH((PSZ)kLdrDyldOS2EndLibpath, END_LIBPATH);
+ if (rc)
+ kLdrDyldOS2EndLibpath[0] = '\0';
+
+#else
+ kHlpGetEnv("KLDR_OS2_LIBPATH", kLdrDyldOS2Libpath, sizeof(kLdrDyldOS2Libpath));
+ kHlpGetEnv("KLDR_OS2_LIBPATHSTRICT", kLdrDyldOS2LibpathStrict, sizeof(kLdrDyldOS2LibpathStrict));
+ if ( kLdrDyldOS2LibpathStrict[0] == 'T'
+ || kLdrDyldOS2LibpathStrict[0] == 't')
+ kLdrDyldOS2LibpathStrict[0] = 'T';
+ else
+ kLdrDyldOS2LibpathStrict[0] = '\0';
+ kLdrDyldOS2LibpathStrict[1] = '\0';
+ kHlpGetEnv("KLDR_OS2_BEGINLIBPATH", kLdrDyldOS2BeginLibpath, sizeof(kLdrDyldOS2BeginLibpath));
+ kHlpGetEnv("KLDR_OS2_ENDLIBPATH", kLdrDyldOS2EndLibpath, sizeof(kLdrDyldOS2EndLibpath));
+#endif
+
+ /*
+ * The windows search parameters.
+ */
+#if K_OS == K_OS_WINDOWS
+ cch = GetSystemDirectory(kLdrDyldWindowsSystemDir, sizeof(kLdrDyldWindowsSystemDir));
+ if (cch >= sizeof(kLdrDyldWindowsSystemDir))
+ return (rc = GetLastError()) ? rc : -1;
+ cch = GetWindowsDirectory(kLdrDyldWindowsDir, sizeof(kLdrDyldWindowsDir));
+ if (cch >= sizeof(kLdrDyldWindowsDir))
+ return (rc = GetLastError()) ? rc : -1;
+ kHlpGetEnv("PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath));
+#else
+ kHlpGetEnv("KLDR_WINDOWS_SYSTEM_DIR", kLdrDyldWindowsSystemDir, sizeof(kLdrDyldWindowsSystemDir));
+ kHlpGetEnv("KLDR_WINDOWS_DIR", kLdrDyldWindowsDir, sizeof(kLdrDyldWindowsDir));
+ rc = kHlpGetEnv("KLDR_WINDOWS_PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath));
+ if (rc)
+ kHlpGetEnv("PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath));
+#endif
+
+ /*
+ * The Unix search parameters.
+ */
+ rc = kHlpGetEnv("KLDR_UNIX_LIBRARY_PATH", kLdrDyldUnixLibraryPath, sizeof(kLdrDyldUnixLibraryPath));
+ if (rc)
+ kHlpGetEnv("LD_LIBRARY_PATH", kLdrDyldUnixLibraryPath, sizeof(kLdrDyldUnixLibraryPath));
+
+ (void)cch;
+ return 0;
+}
+
+
+/**
+ * Lazily initialize the two application directory paths.
+ */
+static void kldrDyldFindLazyInitAppDir(void)
+{
+ if (!kLdrDyldAppDir[0])
+ {
+#if K_OS == K_OS_DARWIN
+ /** @todo implement this! */
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+
+#elif K_OS == K_OS_LINUX
+ KSSIZE cch = kHlpSys_readlink("/proc/self/exe", kLdrDyldAppDir, sizeof(kLdrDyldAppDir) - 1);
+ if (cch > 0)
+ {
+ kLdrDyldAppDir[cch] = '\0';
+ *kHlpGetFilename(kLdrDyldAppDir) = '\0';
+ kHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ }
+ else
+ {
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+ }
+
+#elif K_OS == K_OS_OS2
+ PPIB pPib;
+ PTIB pTib;
+ APIRET rc;
+
+ DosGetInfoBlocks(&pTib, &pPib);
+ rc = DosQueryModuleName(pPib->pib_hmte, sizeof(kLdrDyldAppDir), kLdrDyldAppDir);
+ if (!rc)
+ {
+ *kHlpGetFilename(kLdrDyldAppDir) = '\0';
+ kHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ }
+ else
+ {
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+ }
+
+#elif K_OS == K_OS_WINDOWS
+ DWORD dwSize = GetModuleFileName(NULL /* the executable */, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ if (dwSize > 0)
+ {
+ *kHlpGetFilename(kLdrDyldAppDir) = '\0';
+ kHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ }
+ else
+ {
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+ }
+
+#else
+# error "Port me"
+#endif
+ }
+}
+
+
+/**
+ * Locates and opens a module using the specified search method.
+ *
+ * @returns 0 and *ppMod on success, non-zero OS specific error on failure.
+ *
+ * @param pszName Partial or complete name, it's specific to the search method to determin which.
+ * @param pszPrefix Prefix than can be used when searching.
+ * @param pszSuffix Suffix than can be used when searching.
+ * @param enmSearch The file search method to apply.
+ * @param fFlags Search flags.
+ * @param ppMod Where to store the file provider instance on success.
+ */
+int kldrDyldFindNewModule(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod)
+{
+ int rc;
+ PKRDR pRdr = NULL;
+
+ *ppMod = NULL;
+
+ /*
+ * If this isn't just a filename, we the caller has specified a file
+ * that should be opened directly and not a module name to be searched for.
+ */
+ if (!kHlpIsFilenameOnly(pszName))
+ rc = kldrDyldFindTryOpen(pszName, &pRdr);
+ else if (!(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE))
+ rc = kldrDyldFindDoDllSearch(pszName, pszPrefix, pszSuffix, enmSearch, fFlags, &pRdr);
+ else
+ rc = kldrDyldFindDoExeSearch(pszName, pszPrefix, pszSuffix, enmSearch, fFlags, &pRdr);
+ if (!rc)
+ {
+#ifdef KLDRDYLDFIND_STRICT
+ /* Sanity check of kldrDyldFindExistingModule. */
+ if (fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)
+ {
+ const char *pszFilename = kRdrName(pRdr);
+ const KSIZE cchFilename = kHlpStrLen(pszFilename);
+ PKLDRDYLDMOD pCur;
+ for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext)
+ KLDRDYLDFIND_ASSERT( pCur->pMod->cchFilename != cchFilename
+ || kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename));
+ }
+#endif
+
+ /*
+ * Check for matching non-global modules that should be promoted.
+ */
+ if (!(fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE))
+ {
+ const char *pszFilename = kRdrName(pRdr);
+ const KSIZE cchFilename = kHlpStrLen(pszFilename);
+ PKLDRDYLDMOD pCur;
+ for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext)
+ {
+ if ( !pCur->fGlobalOrSpecific
+ && pCur->pMod->cchFilename == cchFilename
+ && !kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename))
+ {
+ kRdrClose(pRdr);
+ kldrDyldModMarkGlobal(pCur);
+ *ppMod = pCur;
+ return 0;
+ }
+ KLDRDYLDFIND_ASSERT( pCur->pMod->cchFilename != cchFilename
+ || kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename));
+ }
+ }
+
+ /*
+ * Create a new module.
+ */
+ rc = kldrDyldModCreate(pRdr, fFlags, ppMod);
+ if (rc)
+ kRdrClose(pRdr);
+ }
+ return rc;
+}
+
+
+/**
+ * Searches for a DLL file using the specified method.
+ *
+ * @returns 0 on success and *ppMod pointing to the new module.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND if the specified file couldn't be opened.
+ * @returns non-zero kLdr or OS specific status code on other failures.
+ * @param pszName The name.
+ * @param pszPrefix The prefix, optional.
+ * @param pszSuffix The suffix, optional.
+ * @param enmSearch The search method.
+ * @param fFlags The load/search flags.
+ * @param ppRdr Where to store the pointer to the file provider instance on success.
+ */
+static int kldrDyldFindDoDllSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKRDR ppRdr)
+{
+ int rc;
+ KLDRDYLDFINDARGS Args;
+
+ /*
+ * Initialize the argument structure and resolve defaults.
+ */
+ Args.enmSearch = enmSearch;
+ Args.pszPrefix = pszPrefix;
+ Args.pszSuffix = pszSuffix;
+ rc = kldrDyldFindGetDefaults(&Args.enmSearch, &Args.pszPrefix, &Args.pszSuffix, pszName, fFlags);
+ if (rc)
+ return rc;
+ Args.pszName = pszName;
+ Args.cchName = kHlpStrLen(pszName);
+ Args.cchPrefix = Args.pszPrefix ? kHlpStrLen(Args.pszPrefix) : 0;
+ Args.cchSuffix = Args.pszSuffix ? kHlpStrLen(Args.pszSuffix) : 0;
+ Args.cchMaxLength = Args.cchName + Args.cchSuffix + Args.cchPrefix;
+ Args.fFlags = fFlags;
+ Args.ppRdr = ppRdr;
+
+ /*
+ * Apply the specified search method.
+ */
+/** @todo get rid of the strlen() on the various paths here! */
+ switch (Args.enmSearch)
+ {
+ case KLDRDYLD_SEARCH_KLDR:
+ {
+ kldrDyldFindLazyInitAppDir();
+ if (kLdrDyldAppDir[0] != '\0')
+ {
+ rc = kldrDyldFindTryOpenPath(kLdrDyldAppDir, kHlpStrLen(kLdrDyldAppDir), &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ }
+ rc = kldrDyldFindTryOpenPath(".", 1, &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ rc = kldrDyldFindEnumeratePath(kLdrDyldLibraryPath, &Args);
+ break;
+ }
+
+ case KLDRDYLD_SEARCH_OS2:
+ {
+ rc = kldrDyldFindEnumeratePath(kLdrDyldOS2BeginLibpath, &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ rc = kldrDyldFindEnumeratePath(kLdrDyldOS2Libpath, &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ rc = kldrDyldFindEnumeratePath(kLdrDyldOS2EndLibpath, &Args);
+ break;
+ }
+
+ case KLDRDYLD_SEARCH_WINDOWS:
+ case KLDRDYLD_SEARCH_WINDOWS_ALTERED:
+ {
+ kldrDyldFindLazyInitAppDir();
+ rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsAppDir, kHlpStrLen(kLdrDyldWindowsAppDir), &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ if (Args.enmSearch == KLDRDYLD_SEARCH_WINDOWS_ALTERED)
+ {
+ rc = kldrDyldFindTryOpenPath(".", 1, &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ }
+ rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsSystemDir, kHlpStrLen(kLdrDyldWindowsSystemDir), &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsDir, kHlpStrLen(kLdrDyldWindowsDir), &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ if (Args.enmSearch == KLDRDYLD_SEARCH_WINDOWS)
+ {
+ rc = kldrDyldFindTryOpenPath(".", 1, &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ }
+ rc = kldrDyldFindEnumeratePath(kLdrDyldWindowsPath, &Args);
+ break;
+ }
+
+ case KLDRDYLD_SEARCH_UNIX_COMMON:
+ {
+ rc = kldrDyldFindEnumeratePath(kLdrDyldUnixLibraryPath, &Args);
+ if (rc == KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ rc = kldrDyldFindEnumeratePath(kLdrDyldUnixSystemLibraryPath, &Args);
+ break;
+ }
+
+ default: kHlpAssert(!"internal error"); return -1;
+ }
+ return rc;
+}
+
+
+/**
+ * Searches for an EXE file using the specified method.
+ *
+ * @returns 0 on success and *ppMod pointing to the new module.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND if the specified file couldn't be opened.
+ * @returns non-zero kLdr or OS specific status code on other failures.
+ * @param pszName The name.
+ * @param pszPrefix The prefix, optional.
+ * @param pszSuffix The suffix, optional.
+ * @param enmSearch The search method.
+ * @param fFlags The load/search flags.
+ * @param ppRdr Where to store the pointer to the file provider instance on success.
+ */
+static int kldrDyldFindDoExeSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKRDR ppRdr)
+{
+ int rc;
+ KLDRDYLDFINDARGS Args;
+
+ /*
+ * Initialize the argument structure and resolve defaults.
+ */
+ Args.enmSearch = enmSearch;
+ Args.pszPrefix = pszPrefix;
+ Args.pszSuffix = pszSuffix;
+ rc = kldrDyldFindGetDefaults(&Args.enmSearch, &Args.pszPrefix, &Args.pszSuffix, pszName, fFlags);
+ if (rc)
+ return rc;
+ Args.pszName = pszName;
+ Args.cchName = kHlpStrLen(pszName);
+ Args.cchPrefix = Args.pszPrefix ? kHlpStrLen(Args.pszPrefix) : 0;
+ Args.cchSuffix = Args.pszSuffix ? kHlpStrLen(Args.pszSuffix) : 0;
+ Args.cchMaxLength = Args.cchName + Args.cchSuffix + Args.cchPrefix;
+ Args.fFlags = fFlags;
+ Args.ppRdr = ppRdr;
+
+ /*
+ * If we're bootstrapping a process, we'll start by looking in the
+ * application directory and the check out the path.
+ */
+ if (g_fBootstrapping)
+ {
+ kldrDyldFindLazyInitAppDir();
+ if (kLdrDyldAppDir[0] != '\0')
+ {
+ rc = kldrDyldFindTryOpenPath(kLdrDyldAppDir, kHlpStrLen(kLdrDyldAppDir), &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ return rc;
+ }
+ }
+
+ /*
+ * Search the EXE search path. Initialize it the first time around.
+ */
+ if (!kLdrDyldExePath[0])
+ {
+ KSIZE cch;
+ kHlpGetEnv("KLDR_EXE_PATH", kLdrDyldExePath, sizeof(kLdrDyldExePath) - 10);
+ cch = kHlpStrLen(kLdrDyldExePath);
+ kLdrDyldExePath[cch++] = ';';
+ kHlpGetEnv("PATH", &kLdrDyldExePath[cch], sizeof(kLdrDyldExePath) - cch);
+ }
+ return kldrDyldFindEnumeratePath(kLdrDyldExePath, &Args);
+}
+
+
+/**
+ * Try open the specfied file.
+ *
+ * @returns 0 on success and *ppMod pointing to the new module.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND if the specified file couldn't be opened.
+ * @returns non-zero kLdr or OS specific status code on other failures.
+ * @param pszFilename The filename.
+ * @param ppRdr Where to store the pointer to the new module.
+ */
+static int kldrDyldFindTryOpen(const char *pszFilename, PPKRDR ppRdr)
+{
+ int rc;
+
+ /*
+ * Try open the file.
+ */
+ rc = kRdrOpen(ppRdr, pszFilename);
+ if (!rc)
+ return 0;
+ /** @todo deal with return codes properly. */
+ if (rc >= KERR_BASE && rc <= KERR_END)
+ return rc;
+
+ return KLDR_ERR_MODULE_NOT_FOUND;
+}
+
+
+/**
+ * Composes a filename from the specified directory path,
+ * prefix (optional), name and suffix (optional, will try with and without).
+ *
+ * @param pchPath The directory path - this doesn't have to be null terminated.
+ * @param cchPath The length of the path.
+ * @param pArgs The search argument structure.
+ *
+ * @returns See kldrDyldFindTryOpen
+ */
+static int kldrDyldFindTryOpenPath(const char *pchPath, KSIZE cchPath, PCKLDRDYLDFINDARGS pArgs)
+{
+ static char s_szFilename[1024];
+ char *psz;
+ int rc;
+
+ /*
+ * Ignore any attempts at opening empty paths.
+ * This can happen when a *Dir globals is empty.
+ */
+ if (!cchPath)
+ return KLDR_ERR_MODULE_NOT_FOUND; /* ignore */
+
+ /*
+ * Limit check first.
+ */
+ if (cchPath + 1 + pArgs->cchMaxLength >= sizeof(s_szFilename))
+ {
+ KLDRDYLDFIND_ASSERT(!"too long");
+ return KLDR_ERR_MODULE_NOT_FOUND; /* ignore */
+ }
+
+ /*
+ * The directory path.
+ */
+ kHlpMemCopy(s_szFilename, pchPath, cchPath);
+ psz = &s_szFilename[cchPath];
+ if (psz[-1] != '/'
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
+ && psz[-1] != '\\'
+ && psz[-1] != ':'
+#endif
+ )
+ *psz++ = '/';
+
+ /*
+ * The name.
+ */
+ if (pArgs->cchPrefix)
+ {
+ kHlpMemCopy(psz, pArgs->pszPrefix, pArgs->cchPrefix);
+ psz += pArgs->cchPrefix;
+ }
+ kHlpMemCopy(psz, pArgs->pszName, pArgs->cchName);
+ psz += pArgs->cchName;
+ if (pArgs->cchSuffix)
+ {
+ kHlpMemCopy(psz, pArgs->pszSuffix, pArgs->cchSuffix);
+ psz += pArgs->cchSuffix;
+ }
+ *psz = '\0';
+
+
+ /*
+ * Try open it.
+ */
+ rc = kldrDyldFindTryOpen(s_szFilename, pArgs->ppRdr);
+ /* If we're opening an executable, try again without the suffix.*/
+ if ( rc
+ && pArgs->cchSuffix
+ && (pArgs->fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE))
+ {
+ psz -= pArgs->cchSuffix;
+ *psz = '\0';
+ rc = kldrDyldFindTryOpen(s_szFilename, pArgs->ppRdr);
+ }
+ return rc;
+}
+
+
+/**
+ * Enumerates the specfied path.
+ *
+ * @returns Any return code from the kldrDyldFindTryOpenPath() which isn't KLDR_ERR_MODULE_NOT_FOUND.
+ * @returns KLDR_ERR_MODULE_NOT_FOUND if the end of the search path was reached.
+ * @param pszSearchPath The search path to enumeare.
+ * @param pArgs The search argument structure.
+ */
+static int kldrDyldFindEnumeratePath(const char *pszSearchPath, PCKLDRDYLDFINDARGS pArgs)
+{
+ const char *psz = pszSearchPath;
+ for (;;)
+ {
+ const char *pszEnd;
+ KSIZE cchPath;
+
+ /*
+ * Trim.
+ */
+ while (*psz == ';' || *psz == ':')
+ psz++;
+ if (*psz == '\0')
+ return KLDR_ERR_MODULE_NOT_FOUND;
+
+ /*
+ * Find the end.
+ */
+ pszEnd = psz + 1;
+ while ( *pszEnd != '\0'
+ && *pszEnd != ';'
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
+ && ( *pszEnd != ':'
+ || ( pszEnd - psz == 1
+ && ( (*psz >= 'A' && *psz <= 'Z')
+ || (*psz >= 'a' && *psz <= 'z')
+ )
+ )
+ )
+#else
+ && *pszEnd != ':'
+#endif
+ )
+ pszEnd++;
+
+ /*
+ * If not empty path, try open the module using it.
+ */
+ cchPath = pszEnd - psz;
+ if (cchPath > 0)
+ {
+ int rc;
+ rc = kldrDyldFindTryOpenPath(psz, cchPath, pArgs);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ return rc;
+ }
+
+ /* next */
+ psz = pszEnd;
+ }
+}
+
+
+/**
+ * Resolve default search method, prefix and suffix.
+ *
+ * @returns 0 on success, KERR_INVALID_PARAMETER on failure.
+ * @param penmSearch The search method. In/Out.
+ * @param ppszPrefix The prefix. In/Out.
+ * @param ppszSuffix The suffix. In/Out.
+ * @param pszName The name. In.
+ * @param fFlags The load/search flags.
+ */
+static int kldrDyldFindGetDefaults(KLDRDYLDSEARCH *penmSearch, const char **ppszPrefix, const char **ppszSuffix,
+ const char *pszName, KU32 fFlags)
+{
+ unsigned fCaseSensitive;
+
+ /*
+ * Fixup search method alias.
+ */
+ if (*penmSearch == KLDRDYLD_SEARCH_HOST)
+#if K_OS == K_OS_DARWIN
+ /** @todo *penmSearch = KLDRDYLD_SEARCH_DARWIN; */
+ *penmSearch = KLDRDYLD_SEARCH_UNIX_COMMON;
+#elif K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ *penmSearch = KLDRDYLD_SEARCH_UNIX_COMMON;
+#elif K_OS == K_OS_OS2
+ *penmSearch = KLDRDYLD_SEARCH_OS2;
+#elif K_OS == K_OS_WINDOWS
+ *penmSearch = KLDRDYLD_SEARCH_WINDOWS;
+#else
+# error "Port me"
+#endif
+
+ /*
+ * Apply search method specific prefix/suffix.
+ */
+ switch (*penmSearch)
+ {
+ case KLDRDYLD_SEARCH_KLDR:
+ if (!*ppszPrefix && kLdrDyldDefPrefix[0])
+ *ppszPrefix = kLdrDyldDefPrefix;
+ if (!*ppszSuffix && kLdrDyldDefSuffix[0])
+ *ppszSuffix = kLdrDyldDefSuffix;
+ fCaseSensitive = 1;
+ break;
+
+ case KLDRDYLD_SEARCH_OS2:
+ if (!*ppszSuffix)
+ *ppszSuffix = !(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE) ? ".dll" : ".exe";
+ fCaseSensitive = 0;
+ break;
+
+ case KLDRDYLD_SEARCH_WINDOWS:
+ case KLDRDYLD_SEARCH_WINDOWS_ALTERED:
+ if (!*ppszSuffix)
+ *ppszSuffix = !(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE) ? ".dll" : ".exe";
+ fCaseSensitive = 0;
+ break;
+
+ case KLDRDYLD_SEARCH_UNIX_COMMON:
+ fCaseSensitive = 1;
+ break;
+
+ default:
+ KLDRDYLDFIND_ASSERT(!"invalid search method");
+ return KERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Drop the suffix if it's already included in the name.
+ */
+ if (*ppszSuffix)
+ {
+ const KSIZE cchName = kHlpStrLen(pszName);
+ const KSIZE cchSuffix = kHlpStrLen(*ppszSuffix);
+ if ( cchName > cchSuffix
+ && ( fCaseSensitive
+ ? !kHlpMemComp(pszName + cchName - cchSuffix, *ppszSuffix, cchSuffix)
+ : !kHlpMemICompAscii(pszName + cchName - cchSuffix, *ppszSuffix, cchSuffix))
+ )
+ *ppszSuffix = NULL;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Locates an already open module using the specified search method.
+ *
+ * @returns 0 and *ppMod on success, non-zero OS specific error on failure.
+ *
+ * @param pszName Partial or complete name, it's specific to the search method to determin which.
+ * @param pszPrefix Prefix than can be used when searching.
+ * @param pszSuffix Suffix than can be used when searching.
+ * @param enmSearch The file search method to apply.
+ * @param fFlags Search flags.
+ * @param ppMod Where to store the file provider instance on success.
+ */
+int kldrDyldFindExistingModule(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod)
+{
+
+ int rc;
+ unsigned fOS2LibpathStrict;
+ *ppMod = NULL;
+
+ /*
+ * Don't bother if no modules are loaded yet.
+ */
+ if (!kLdrDyldHead)
+ return KLDR_ERR_MODULE_NOT_FOUND;
+
+ /*
+ * Defaults.
+ */
+ rc = kldrDyldFindGetDefaults(&enmSearch, &pszPrefix, &pszSuffix, pszName, fFlags);
+ if (rc)
+ return rc;
+
+ /*
+ * If this isn't just a filename, the caller has specified a file
+ * that should be opened directly and not a module name to be searched for.
+ *
+ * In order to do the right thing we'll have to open the file and get the
+ * correct filename for it.
+ *
+ * The OS/2 libpath strict method require us to find the correct DLL first.
+ */
+ fOS2LibpathStrict = 0;
+ if ( !kHlpIsFilenameOnly(pszName)
+ || (fOS2LibpathStrict = ( enmSearch == KLDRDYLD_SEARCH_OS2
+ && kLdrDyldOS2LibpathStrict[0] == 'T')
+ )
+ )
+ {
+ PKRDR pRdr;
+ if (fOS2LibpathStrict)
+ rc = kldrDyldFindDoDllSearch(pszName, pszPrefix, pszSuffix, enmSearch, fFlags, &pRdr);
+ else
+ rc = kldrDyldFindTryOpen(pszName, &pRdr);
+ if (!rc)
+ {
+ /* do a filename based search. */
+ const char *pszFilename = kRdrName(pRdr);
+ const KSIZE cchFilename = kHlpStrLen(pszFilename);
+ PKLDRDYLDMOD pCur;
+ rc = KLDR_ERR_MODULE_NOT_FOUND;
+ for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext)
+ {
+ if ( pCur->pMod->cchFilename == cchFilename
+ && !kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename))
+ {
+ *ppMod = pCur;
+ rc = 0;
+ break;
+ }
+ }
+ kRdrClose(pRdr);
+ }
+ }
+ else
+ {
+ const KSIZE cchName = kHlpStrLen(pszName);
+ const KSIZE cchPrefix = pszPrefix ? kHlpStrLen(pszPrefix) : 0;
+ const KSIZE cchSuffix = pszSuffix ? kHlpStrLen(pszSuffix) : 0;
+ const char *pszNameSuffix = kHlpGetSuff(pszName);
+ PKLDRDYLDMOD pCur = kLdrDyldHead;
+
+ /*
+ * Some of the methods are case insensitive (ASCII), others are case sensitive.
+ * To avoid having todo indirect calls to the compare functions here, we split
+ * ways even if it means a lot of duplicate code.
+ */
+ if ( enmSearch == KLDRDYLD_SEARCH_OS2
+ || enmSearch == KLDRDYLD_SEARCH_WINDOWS
+ || enmSearch == KLDRDYLD_SEARCH_WINDOWS_ALTERED)
+ {
+ const unsigned fNameHasSuffix = pszNameSuffix
+ && kHlpStrLen(pszNameSuffix) == cchSuffix
+ && !kHlpMemICompAscii(pszNameSuffix, pszName + cchName - cchSuffix, cchSuffix);
+ for (; pCur; pCur = pCur->Load.pNext)
+ {
+ /* match global / specific */
+ if ( !pCur->fGlobalOrSpecific
+ && !(fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE))
+ continue;
+
+ /* match name */
+ if ( pCur->pMod->cchName == cchName
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix, pszName, cchName))
+ break;
+ }
+ if (cchSuffix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchName, pszSuffix, cchSuffix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if ( fNameHasSuffix
+ && pCur->pMod->cchName == cchName - cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszName, cchName - cchSuffix))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix + cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix, pszName, cchName)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix + cchName, pszSuffix, cchSuffix))
+ break;
+ if ( fNameHasSuffix
+ && pCur->pMod->cchName == cchName + cchPrefix - cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix, pszName, cchName - cchSuffix))
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ const unsigned fNameHasSuffix = pszNameSuffix
+ && kHlpStrLen(pszNameSuffix) == cchSuffix
+ && kHlpMemComp(pszNameSuffix, pszName + cchName - cchSuffix, cchSuffix);
+ for (; pCur; pCur = pCur->Load.pNext)
+ {
+ /* match global / specific */
+ if ( !pCur->fGlobalOrSpecific
+ && !(fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE))
+ continue;
+
+ /* match name */
+ if ( pCur->pMod->cchName == cchName
+ && !kHlpMemComp(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix
+ && !kHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName))
+ break;
+ }
+ if (cchSuffix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName + cchName, pszSuffix, cchSuffix)
+ && !kHlpMemComp(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if ( fNameHasSuffix
+ && pCur->pMod->cchName == cchName - cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName, pszName, cchName - cchSuffix))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix + cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix + cchName, pszSuffix, cchSuffix))
+ break;
+ if ( pCur->pMod->cchName == cchName + cchPrefix - cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName - cchSuffix))
+ break;
+ }
+ }
+ }
+ }
+
+ /* search result. */
+ if (pCur)
+ {
+ *ppMod = pCur;
+ rc = 0;
+ }
+ else
+ rc = KLDR_ERR_MODULE_NOT_FOUND;
+ }
+
+ return rc;
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrDyldMod.c b/src/lib/kStuff/kLdr/kLdrDyldMod.c
new file mode 100644
index 0000000..b25f6fc
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyldMod.c
@@ -0,0 +1,1300 @@
+/* $Id: kLdrDyldMod.c 81 2016-08-18 22:10:38Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, Dyld module methods.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRDYLDMOD_STRICT
+ * Define KLDRDYLDMOD_STRICT to enabled strict checks in kLdrDyld. */
+#define KLDRDYLDMOD_STRICT 1
+
+/** @def KLDRDYLDMOD_ASSERT
+ * Assert that an expression is true when KLDRDYLD_STRICT is defined.
+ */
+#ifdef KLDRDYLDMOD_STRICT
+# define KLDRDYLDMOD_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRDYLDMOD_ASSERT(expr) do {} while (0)
+#endif
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static void kldrDyldModUnlink(PKLDRDYLDMOD pMod);
+
+
+
+/**
+ * Creates a module from the specified file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to the new instance.
+ * On failure a non-zero kLdr status code is returned.
+ * @param pRdr The file provider instance.
+ * @param fFlags Load/search flags.
+ * @param ppMod Where to put the pointer to the new module on success.
+ */
+int kldrDyldModCreate(PKRDR pRdr, KU32 fFlags, PPKLDRDYLDMOD ppMod)
+{
+ PKLDRDYLDMOD pMod;
+ PKLDRMOD pRawMod;
+ int rc;
+
+ *ppMod = NULL;
+
+/** @todo deal with fFlags (exec/dll) */
+/** @todo Check up the cpu architecture. */
+
+ /*
+ * Try open an module interpreter.
+ */
+ rc = kLdrModOpenFromRdr(pRdr, 0 /*fFlags*/, KCPUARCH_UNKNOWN, &pRawMod);
+ if (rc)
+ return kldrDyldFailure(rc, "%s: %rc", kRdrName(pRdr), rc);
+
+ /*
+ * Match the module aginst the load flags.
+ */
+ switch (pRawMod->enmType)
+ {
+ case KLDRTYPE_EXECUTABLE_FIXED:
+ case KLDRTYPE_EXECUTABLE_RELOCATABLE:
+ case KLDRTYPE_EXECUTABLE_PIC:
+ if (!(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE))
+ {
+ kLdrModClose(pRawMod);
+ return KLDR_ERR_NOT_EXE;
+ }
+ break;
+
+ case KLDRTYPE_OBJECT: /* We can handle these as DLLs. */
+ case KLDRTYPE_SHARED_LIBRARY_FIXED:
+ case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
+ case KLDRTYPE_SHARED_LIBRARY_PIC:
+ case KLDRTYPE_FORWARDER_DLL:
+ if (fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE)
+ {
+ kLdrModClose(pRawMod);
+ return KLDR_ERR_NOT_DLL;
+ }
+ break;
+
+ default:
+ KLDRDYLDMOD_ASSERT(!"Bad enmType!");
+ case KLDRTYPE_CORE:
+ return fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE ? KLDR_ERR_NOT_EXE : KLDR_ERR_NOT_DLL;
+ }
+
+ /*
+ * Allocate a new dyld module.
+ */
+ pMod = (PKLDRDYLDMOD)kHlpAlloc(sizeof(*pMod));
+ if (pMod)
+ {
+ pMod->enmState = KLDRSTATE_OPEN;
+ pMod->pMod = pRawMod;
+ pMod->hMod = pMod;
+ pMod->cDepRefs = pMod->cDynRefs = pMod->cRefs = 0;
+ switch (pRawMod->enmType)
+ {
+ case KLDRTYPE_EXECUTABLE_FIXED:
+ case KLDRTYPE_EXECUTABLE_RELOCATABLE:
+ case KLDRTYPE_EXECUTABLE_PIC:
+ pMod->fExecutable = 1;
+ break;
+ default:
+ pMod->fExecutable = 0;
+ break;
+ }
+ pMod->fGlobalOrSpecific = 0;
+ pMod->fBindable = 0;
+ pMod->fInitList = 0;
+ pMod->fAlreadySeen = 0;
+ pMod->fMapped = 0;
+ pMod->fAllocatedTLS = 0;
+ pMod->f25Reserved = 0;
+ pMod->InitTerm.pNext = NULL;
+ pMod->InitTerm.pPrev = NULL;
+ pMod->Bind.pNext = NULL;
+ pMod->Bind.pPrev = NULL;
+ pMod->cPrereqs = 0;
+ pMod->papPrereqs = NULL;
+ pMod->u32MagicHead = KLDRDYMOD_MAGIC;
+ pMod->u32MagicTail = KLDRDYMOD_MAGIC;
+
+ /* it. */
+ pMod->Load.pNext = NULL;
+ pMod->Load.pPrev = kLdrDyldTail;
+ if (kLdrDyldTail)
+ kLdrDyldTail->Load.pNext = pMod;
+ else
+ kLdrDyldHead = pMod;
+ kLdrDyldTail = pMod;
+
+ /* deal with the remaining flags. */
+ if (fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)
+ kldrDyldModMarkSpecific(pMod);
+ else
+ kldrDyldModMarkGlobal(pMod);
+
+ if (fFlags & KLDRYDLD_LOAD_FLAGS_GLOBAL_SYMBOLS)
+ kldrDyldModSetBindable(pMod, 0 /* not deep binable */);
+ else
+ kldrDyldModClearBindable(pMod);
+
+ /*
+ * We're good.
+ */
+ *ppMod = pMod;
+ rc = 0;
+ }
+ else
+ {
+ kLdrModClose(pRawMod);
+ rc = KERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+
+/**
+ * Creates a module for a native module.
+ *
+ * @returns 0 on success and *ppMod pointing to the new instance.
+ * On failure a non-zero kLdr status code is returned.
+ * @param hNativeModule The native handle.
+ * @param ppMod Where to put the pointer to the new module on success.
+ * @remark This function ain't finalized yet.
+ */
+int kldrDyldModCreateNative(KUPTR hNativeModule)
+{
+#if 0
+ /*
+ * Check if this module is already loaded by the native OS loader.
+ */
+ rc = kld
+ {
+#if K_OS == K_OS_OS2
+ HMODULE hmod = NULLHANDLE;
+ APIRET rc = DosQueryModuleHandle(kRdrName(pRdr), &hmod);
+ if (!rc)
+
+#elif K_OS == K_OS_WINDOWS
+ HMODULE hmod = NULL;
+ if (GetModuleHandle(kRdrName(pRdr))
+
+#else
+# error "Port me"
+#endif
+ }
+#endif
+ return -1;
+}
+
+
+/**
+ * Destroys a module pending destruction.
+ *
+ * @param pMod The module in question.
+ */
+void kldrDyldModDestroy(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ /*
+ * Validate the state.
+ */
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_PENDING_DESTROY:
+ case KLDRSTATE_GC:
+ break;
+ default:
+ KLDRDYLDMOD_ASSERT(!"Invalid state");
+ break;
+ }
+ KLDRDYLDMOD_ASSERT(!pMod->fInitList);
+ KLDRDYLDMOD_ASSERT(!pMod->cDynRefs);
+ KLDRDYLDMOD_ASSERT(!pMod->cDepRefs);
+
+ /*
+ * Ensure that the module is unmapped.
+ */
+ if (pMod->fAllocatedTLS)
+ {
+ kLdrModFreeTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ pMod->fAllocatedTLS = 0;
+ }
+ if (pMod->fMapped)
+ {
+ rc = kLdrModUnmap(pMod->pMod); KLDRDYLDMOD_ASSERT(!rc);
+ pMod->fMapped = 0;
+ }
+
+ /*
+ * Ensure it's unlinked from all chains.
+ */
+ if (pMod->enmState < KLDRSTATE_PENDING_DESTROY)
+ kldrDyldModUnlink(pMod);
+
+ /*
+ * Free everything associated with the module.
+ */
+ /* the prerequisite array. */
+ if (pMod->papPrereqs)
+ {
+ KU32 i = pMod->cPrereqs;
+ while (i-- > 0)
+ {
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs[i] == NULL);
+ pMod->papPrereqs[i] = NULL;
+ }
+
+ kHlpFree(pMod->papPrereqs);
+ pMod->papPrereqs = NULL;
+ pMod->cPrereqs = 0;
+ }
+
+ /* the module interpreter. */
+ if (pMod->pMod)
+ {
+ rc = kLdrModClose(pMod->pMod); KLDRDYLDMOD_ASSERT(!rc);
+ pMod->pMod = NULL;
+ }
+
+
+ /*
+ * Finally, change the module state and free the module if
+ * there are not more references to it. If somebody is still
+ * referencing it, postpone the freeing to Deref.
+ */
+ pMod->enmState = KLDRSTATE_DESTROYED;
+ if (!pMod->cRefs)
+ {
+ pMod->u32MagicHead = 1;
+ pMod->u32MagicTail = 2;
+ kHlpFree(pMod);
+ }
+}
+
+
+/**
+ * Unlinks the module from any list it might be in.
+ * It is assumed that the module is at least linked into the load list.
+ *
+ * @param pMod The moduel.
+ */
+static void kldrDyldModUnlink(PKLDRDYLDMOD pMod)
+{
+ /* load list */
+ if (pMod->Load.pNext)
+ pMod->Load.pNext->Load.pPrev = pMod->Load.pPrev;
+ else
+ kLdrDyldTail = pMod->Load.pPrev;
+ if (pMod->Load.pPrev)
+ pMod->Load.pPrev->Load.pNext = pMod->Load.pNext;
+ else
+ kLdrDyldHead = pMod->Load.pNext;
+
+ /* bind list */
+ if (pMod->fBindable)
+ kldrDyldModClearBindable(pMod);
+
+ /* init term */
+ if (pMod->fInitList)
+ {
+ KLDRDYLDMOD_ASSERT(pMod->enmState < KLDRSTATE_INITIALIZATION_FAILED);
+ pMod->fInitList = 0;
+ if (pMod->InitTerm.pNext)
+ pMod->InitTerm.pNext->InitTerm.pPrev = pMod->InitTerm.pPrev;
+ else
+ g_pkLdrDyldInitTail = pMod->InitTerm.pPrev;
+ if (pMod->InitTerm.pPrev)
+ pMod->InitTerm.pPrev->InitTerm.pNext = pMod->InitTerm.pNext;
+ else
+ g_pkLdrDyldInitHead = pMod->InitTerm.pNext;
+ }
+ else if (pMod->enmState > KLDRSTATE_INITIALIZATION_FAILED)
+ {
+ KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_GOOD);
+ if (pMod->InitTerm.pNext)
+ pMod->InitTerm.pNext->InitTerm.pPrev = pMod->InitTerm.pPrev;
+ else
+ g_pkLdrDyldTermTail = pMod->InitTerm.pPrev;
+ if (pMod->InitTerm.pPrev)
+ pMod->InitTerm.pPrev->InitTerm.pNext = pMod->InitTerm.pNext;
+ else
+ g_pkLdrDyldTermHead = pMod->InitTerm.pNext;
+ }
+ pMod->InitTerm.pNext = NULL;
+ pMod->InitTerm.pPrev = NULL;
+}
+
+
+/**
+ * Marks a module as bindable, i.e. it'll be considered when
+ * resolving names the unix way.
+ *
+ * @param pMod The module.
+ * @param fDeep When set the module will be inserted at the head of the
+ * module list used to resolve symbols. This means that the
+ * symbols in this module will be prefered of all the other
+ * modules.
+ */
+void kldrDyldModSetBindable(PKLDRDYLDMOD pMod, unsigned fDeep)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_OPEN && pMod->enmState < KLDRSTATE_PENDING_GC);
+ if (!pMod->fBindable)
+ {
+ pMod->fBindable = 1;
+ if (!fDeep)
+ {
+ pMod->Bind.pNext = NULL;
+ pMod->Bind.pPrev = g_pkLdrDyldBindTail;
+ if (g_pkLdrDyldBindTail)
+ g_pkLdrDyldBindTail->Bind.pNext = pMod;
+ else
+ g_pkLdrDyldBindHead = pMod;
+ g_pkLdrDyldBindTail = pMod;
+ }
+ else
+ {
+ pMod->Bind.pPrev = NULL;
+ pMod->Bind.pNext = g_pkLdrDyldBindHead;
+ if (g_pkLdrDyldBindHead)
+ g_pkLdrDyldBindHead->Bind.pPrev = pMod;
+ else
+ g_pkLdrDyldBindTail = pMod;
+ g_pkLdrDyldBindHead = pMod;
+ }
+ }
+}
+
+
+/**
+ * Marks a module as not bindable, i.e. it will not be considered when
+ * resolving names the unix way.
+ *
+ * @param pMod The module.
+ */
+void kldrDyldModClearBindable(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_OPEN && pMod->enmState < KLDRSTATE_PENDING_DESTROY);
+ if (pMod->fBindable)
+ {
+ pMod->fBindable = 0;
+ if (pMod->Bind.pPrev)
+ pMod->Bind.pPrev->Bind.pNext = pMod->Bind.pNext;
+ else
+ g_pkLdrDyldBindHead = pMod->Bind.pNext;
+ if (pMod->Bind.pNext)
+ pMod->Bind.pNext->Bind.pPrev = pMod->Bind.pPrev;
+ else
+ g_pkLdrDyldBindTail = pMod->Bind.pPrev;
+ pMod->Bind.pNext = NULL;
+ pMod->Bind.pPrev = NULL;
+ }
+}
+
+
+/**
+ * Marks the module as global instead of being specific.
+ *
+ * A global module can be a matching result when the request
+ * doesn't specify a path. A specific module will not match
+ * unless the path also matches.
+ *
+ * @param pMod The module.
+ */
+void kldrDyldModMarkGlobal(PKLDRDYLDMOD pMod)
+{
+ pMod->fGlobalOrSpecific = 1;
+}
+
+
+/**
+ * Marks the module as specific instead of global.
+ *
+ * See kldrDyldModMarkGlobal for an explanation of the two terms.
+ *
+ * @param pMod The module.
+ */
+void kldrDyldModMarkSpecific(PKLDRDYLDMOD pMod)
+{
+ pMod->fGlobalOrSpecific = 0;
+}
+
+
+/**
+ * Adds a reference to the module making sure it won't be freed just yet.
+ *
+ * @param pMod The module.
+ */
+void kldrDyldModAddRef(PKLDRDYLDMOD pMod)
+{
+ pMod->cRefs++;
+}
+
+
+/**
+ * Dereference a module.
+ *
+ * @param pMod
+ */
+void kldrDyldModDeref(PKLDRDYLDMOD pMod)
+{
+ /* validate input */
+ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0);
+ KLDRDYLDMOD_ASSERT(pMod->cRefs >= pMod->cDepRefs + pMod->cDynRefs);
+ KLDRDYLDMOD_ASSERT(pMod->enmState > KLDRSTATE_INVALID && pMod->enmState <= KLDRSTATE_END);
+
+ /* decrement. */
+ if (pMod->cRefs > 0)
+ pMod->cRefs--;
+
+ /* execute delayed freeing. */
+ if ( pMod->enmState == KLDRSTATE_DESTROYED
+ && !pMod->cRefs)
+ {
+ pMod->u32MagicHead = 1;
+ pMod->u32MagicTail = 2;
+ kHlpFree(pMod);
+ }
+}
+
+
+/**
+ * Increment the count of modules depending on this module.
+ *
+ * @param pMod The module.
+ * @param pDep The module which depends on us.
+ */
+void kldrDyldModAddDep(PKLDRDYLDMOD pMod, PKLDRDYLDMOD pDep)
+{
+ (void)pDep;
+
+ /* validate state */
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_RELOADED:
+ case KLDRSTATE_LOADED_PREREQUISITES:
+ case KLDRSTATE_RELOADED_LOADED_PREREQUISITES:
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ case KLDRSTATE_INITIALIZING:
+ case KLDRSTATE_GOOD:
+ break;
+ default:
+ KLDRDYLDMOD_ASSERT(!"invalid state");
+ break;
+
+ }
+ KLDRDYLDMOD_ASSERT(pMod->enmState > KLDRSTATE_INVALID && pMod->enmState <= KLDRSTATE_END);
+ pMod->cRefs++;
+ pMod->cDepRefs++;
+}
+
+
+/**
+ * Drop a dependency.
+ *
+ * @param pMod The module.
+ * @param pDep The module which depends on us.
+ */
+void kldrDyldModRemoveDep(PKLDRDYLDMOD pMod, PKLDRDYLDMOD pDep)
+{
+ KLDRDYLDMOD_ASSERT(pMod->cDepRefs > 0);
+ if (pMod->cDepRefs == 0)
+ return;
+ KLDRDYLDMOD_ASSERT(pMod->cDepRefs <= pMod->cRefs);
+ KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_MAPPED && pMod->enmState <= KLDRSTATE_PENDING_DESTROY);
+
+ pMod->cRefs--;
+ pMod->cDepRefs--;
+ if ( pMod->cDepRefs > 0
+ || pMod->cDynRefs > 0)
+ return;
+
+ /*
+ * The module should be unloaded.
+ */
+ kldrDyldModUnloadPrerequisites(pMod);
+}
+
+
+/**
+ * Increment the dynamic load count.
+ *
+ * @returns 0
+ * @param pMod The module.
+ */
+int kldrDyldModDynamicLoad(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT( pMod->enmState == KLDRSTATE_GOOD
+ || pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION
+ || pMod->enmState == KLDRSTATE_INITIALIZING);
+ pMod->cRefs++;
+ pMod->cDynRefs++;
+ return 0;
+}
+
+
+/**
+ * Decrement the dynamic load count of the module and unload the module
+ * if the total reference count reaches zero.
+ *
+ * This may cause a cascade of unloading to occure. See kldrDyldModUnloadPrerequisites().
+ *
+ * @returns status code.
+ * @retval 0 on success.
+ * @retval KLDR_ERR_NOT_LOADED_DYNAMICALLY if the module wasn't loaded dynamically.
+ * @param pMod The module to unload.
+ */
+int kldrDyldModDynamicUnload(PKLDRDYLDMOD pMod)
+{
+ if (pMod->cDynRefs == 0)
+ return KLDR_ERR_NOT_LOADED_DYNAMICALLY;
+ KLDRDYLDMOD_ASSERT(pMod->cDynRefs <= pMod->cRefs);
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD);
+
+ pMod->cRefs--;
+ pMod->cDynRefs--;
+ if ( pMod->cDynRefs > 0
+ || pMod->cDepRefs > 0)
+ return 0;
+
+ /*
+ * The module should be unloaded.
+ */
+ kldrDyldModUnloadPrerequisites(pMod);
+ return 0;
+}
+
+
+/**
+ * Worker for kldrDyldModUnloadPrerequisites.
+ *
+ * @returns The number of modules that now can be unloaded.
+ * @param pMod The module in question.
+ */
+static KU32 kldrDyldModUnloadPrerequisitesOne(PKLDRDYLDMOD pMod)
+{
+ PKLDRDYLDMOD pMod2;
+ KU32 cToUnload = 0;
+ KU32 i;
+
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs || !pMod->cPrereqs);
+
+ /*
+ * Release the one in this module.
+ */
+ for (i = 0; i < pMod->cPrereqs; i++)
+ {
+ pMod2 = pMod->papPrereqs[i];
+ if (pMod2)
+ {
+ pMod->papPrereqs[i] = NULL;
+
+ /* do the derefering ourselves or we'll end up in a recursive loop here. */
+ KLDRDYLDMOD_ASSERT(pMod2->cDepRefs > 0);
+ KLDRDYLDMOD_ASSERT(pMod2->cRefs >= pMod2->cDepRefs);
+ pMod2->cDepRefs--;
+ pMod2->cRefs--;
+ cToUnload += !pMod2->cDepRefs && !pMod2->cDynRefs;
+ }
+ }
+
+ /*
+ * Change the state
+ */
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_LOADED_PREREQUISITES:
+ case KLDRSTATE_FIXED_UP:
+ pMod->enmState = KLDRSTATE_PENDING_DESTROY;
+ kldrDyldModUnlink(pMod);
+ break;
+
+ case KLDRSTATE_PENDING_INITIALIZATION:
+ pMod->enmState = KLDRSTATE_PENDING_GC;
+ break;
+
+ case KLDRSTATE_RELOADED_FIXED_UP:
+ case KLDRSTATE_RELOADED_LOADED_PREREQUISITES:
+ case KLDRSTATE_GOOD:
+ pMod->enmState = KLDRSTATE_PENDING_TERMINATION;
+ break;
+
+ case KLDRSTATE_INITIALIZATION_FAILED:
+ break;
+
+ default:
+ KLDRDYLDMOD_ASSERT(!"invalid state");
+ break;
+ }
+
+ return cToUnload;
+}
+
+
+/**
+ * This is the heart of the unload code.
+ *
+ * It will recursivly (using the load list) initiate module unloading
+ * of all affected modules.
+ *
+ * This function will cause a state transition to PENDING_DESTROY, PENDING_GC
+ * or PENDING_TERMINATION depending on the module state. There is one exception
+ * to this, and that's INITIALIZATION_FAILED, where the state will not be changed.
+ *
+ * @param pMod The module which prerequisites should be unloaded.
+ */
+void kldrDyldModUnloadPrerequisites(PKLDRDYLDMOD pMod)
+{
+ KU32 cToUnload;
+
+ /* sanity */
+#ifdef KLDRDYLD_STRICT
+ {
+ PKLDRDYLDMOD pMod2;
+ for (pMod2 = kLdrDyldHead; pMod2; pMod2 = pMod2->Load.pNext)
+ KLDRDYLDMOD_ASSERT(pMod2->enmState != KLDRSTATE_GOOD || pMod2->cRefs);
+ }
+#endif
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs);
+
+ /*
+ * Unload prereqs of the module we're called on first.
+ */
+ cToUnload = kldrDyldModUnloadPrerequisitesOne(pMod);
+
+ /*
+ * Iterate the load list in a cyclic manner until there are no more
+ * modules that can be pushed on into unloading.
+ */
+ while (cToUnload)
+ {
+ cToUnload = 0;
+ for (pMod = kLdrDyldHead; pMod; pMod = pMod->Load.pNext)
+ {
+ if ( pMod->cDepRefs
+ || pMod->cDynRefs
+ || pMod->enmState >= KLDRSTATE_PENDING_TERMINATION
+ || pMod->enmState < KLDRSTATE_LOADED_PREREQUISITES)
+ continue;
+ cToUnload += kldrDyldModUnloadPrerequisitesOne(pMod);
+ }
+ }
+}
+
+
+/**
+ * Loads the prerequisite modules this module depends on.
+ *
+ * To find each of the prerequisite modules this method calls
+ * kldrDyldGetPrerequisite() and it will make sure the modules
+ * are added to the load stack frame.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * The state is changed to LOADED_PREREQUISITES or RELOADED_LOADED_PREREQUISITES.
+ * @param pMod The module.
+ * @param pszPrefix Prefix to use when searching.
+ * @param pszSuffix Suffix to use when searching.
+ * @param enmSearch Method to use when locating the module and any modules it may depend on.
+ * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines.
+ */
+int kldrDyldModLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags)
+{
+ KI32 cPrereqs;
+ KU32 i;
+ int rc = 0;
+
+ /* sanity */
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_RELOADED:
+ break;
+ default:
+ KLDRDYLDMOD_ASSERT(!"invalid state");
+ return -1;
+ }
+
+ /*
+ * Query number of prerequiste modules and allocate the array.
+ */
+ cPrereqs = kLdrModNumberOfImports(pMod->pMod, NULL);
+ kHlpAssert(cPrereqs >= 0);
+ if (pMod->cPrereqs != cPrereqs)
+ {
+ KLDRDYLDMOD_ASSERT(!pMod->papPrereqs);
+ pMod->papPrereqs = (PPKLDRDYLDMOD)kHlpAllocZ(sizeof(pMod->papPrereqs[0]) * cPrereqs);
+ if (!pMod->papPrereqs)
+ return KERR_NO_MEMORY;
+ pMod->cPrereqs = cPrereqs;
+ }
+ else
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs || !pMod->cPrereqs);
+
+ /*
+ * Iterate the prerequisites and load them.
+ */
+ for (i = 0; i < pMod->cPrereqs; i++)
+ {
+ static char s_szPrereq[260];
+ PKLDRDYLDMOD pPrereqMod;
+
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs[i] == NULL);
+ rc = kLdrModGetImport(pMod->pMod, NULL, i, s_szPrereq, sizeof(s_szPrereq));
+ if (rc)
+ break;
+ rc = kldrDyldGetPrerequisite(s_szPrereq, pszPrefix, pszSuffix, enmSearch, fFlags, pMod, &pPrereqMod);
+ if (rc)
+ break;
+ pMod->papPrereqs[i] = pPrereqMod;
+ }
+
+ /* change the state regardless of what happend. */
+ if (pMod->enmState == KLDRSTATE_MAPPED)
+ pMod->enmState = KLDRSTATE_LOADED_PREREQUISITES;
+ else
+ pMod->enmState = KLDRSTATE_RELOADED_LOADED_PREREQUISITES;
+ return rc;
+}
+
+
+/**
+ * Maps an open module.
+ *
+ * On success the module will be in the MAPPED state.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * @param pMod The module which needs to be unmapped and set pending for destruction.
+ */
+int kldrDyldModMap(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ /* sanity */
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_OPEN);
+ KLDRDYLDMOD_ASSERT(!pMod->fMapped);
+ if (pMod->fMapped)
+ return 0;
+
+ /* do the job. */
+ rc = kLdrModMap(pMod->pMod);
+ if (!rc)
+ {
+ rc = kLdrModAllocTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ if (!rc)
+ {
+ /** @todo TLS */
+ pMod->fMapped = 1;
+ pMod->enmState = KLDRSTATE_MAPPED;
+ }
+ else
+ kLdrModUnmap(pMod->pMod);
+ }
+ return rc;
+}
+
+
+/**
+ * Unmaps the module, unlinks it from everywhere marks it PENDING_DESTROY.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * @param pMod The module which needs to be unmapped and set pending for destruction.
+ */
+int kldrDyldModUnmap(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ /* sanity */
+ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0);
+ KLDRDYLDMOD_ASSERT(pMod->fMapped);
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_GC:
+ case KLDRSTATE_PENDING_DESTROY:
+ break;
+ default:
+ KLDRDYLDMOD_ASSERT(!"invalid state");
+ return -1;
+ }
+
+ /* do the job. */
+ if (pMod->fAllocatedTLS)
+ {
+ kLdrModFreeTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ pMod->fAllocatedTLS = 0;
+ }
+ rc = kLdrModUnmap(pMod->pMod);
+ if (!rc)
+ {
+ pMod->fMapped = 0;
+ if (pMod->enmState < KLDRSTATE_PENDING_DESTROY)
+ {
+ pMod->enmState = KLDRSTATE_PENDING_DESTROY;
+ kldrDyldModUnlink(pMod);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Reloads the module.
+ *
+ * Reloading means that all modified pages are restored to their original
+ * state. Whether this includes the code segments depends on whether the fixups
+ * depend on the addend in the place they are fixing up - so it's format specific.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * @param pMod The module which needs to be unmapped and set pending for destruction.
+ */
+int kldrDyldModReload(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ /* sanity */
+ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0);
+ KLDRDYLDMOD_ASSERT(pMod->fMapped);
+
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_GC:
+ case KLDRSTATE_PENDING_DESTROY:
+ break;
+ default:
+ KLDRDYLDMOD_ASSERT(!"invalid state");
+ return -1;
+ }
+
+ /* Free TLS before reloading. */
+ if (pMod->fAllocatedTLS)
+ {
+ kLdrModFreeTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ pMod->fAllocatedTLS = 0;
+ }
+
+ /* Let the module interpreter do the reloading of the mapping. */
+ rc = kLdrModReload(pMod->pMod);
+ if (!rc)
+ {
+ rc = kLdrModAllocTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ if (!rc)
+ {
+ pMod->fAllocatedTLS = 1;
+ pMod->enmState = KLDRSTATE_RELOADED;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * @copydoc FNKLDRMODGETIMPORT
+ * pvUser points to the KLDRDYLDMOD.
+ */
+static int kldrDyldModFixupGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
+{
+ static int s_cRecursiveCalls = 0;
+ PKLDRDYLDMOD pDyldMod = (PKLDRDYLDMOD)pvUser;
+ int rc;
+
+ /* guard against too deep forwarder recursion. */
+ if (s_cRecursiveCalls >= 5)
+ return KLDR_ERR_TOO_LONG_FORWARDER_CHAIN;
+ s_cRecursiveCalls++;
+
+ if (iImport != NIL_KLDRMOD_IMPORT)
+ {
+ /* specific import module search. */
+ PKLDRDYLDMOD pPrereqMod;
+
+ KLDRDYLDMOD_ASSERT(iImport < pDyldMod->cPrereqs);
+ pPrereqMod = pDyldMod->papPrereqs[iImport];
+
+ KLDRDYLDMOD_ASSERT(pPrereqMod);
+ KLDRDYLDMOD_ASSERT(pPrereqMod->u32MagicHead == KLDRDYMOD_MAGIC);
+ KLDRDYLDMOD_ASSERT(pPrereqMod->u32MagicTail == KLDRDYMOD_MAGIC);
+ KLDRDYLDMOD_ASSERT(pPrereqMod->enmState < KLDRSTATE_TERMINATING);
+
+ rc = kLdrModQuerySymbol(pPrereqMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP,
+ iSymbol, pchSymbol, cchSymbol, pszVersion,
+ kldrDyldModFixupGetImportCallback, pPrereqMod, puValue, pfKind);
+ if (rc)
+ {
+ if (pchSymbol)
+ kldrDyldFailure(rc, "%s[%d]->%s.%.*s%s", pDyldMod->pMod->pszName, iImport,
+ pPrereqMod->pMod->pszName, cchSymbol, pchSymbol, pszVersion ? pszVersion : "");
+ else
+ kldrDyldFailure(rc, "%s[%d]->%s.%d%s", pDyldMod->pMod->pszName, iImport,
+ pPrereqMod->pMod->pszName, iSymbol, pszVersion ? pszVersion : "");
+ }
+ }
+ else
+ {
+ /* bind list search. */
+ unsigned fFound = 0;
+ PKLDRDYLDMOD pBindMod = g_pkLdrDyldBindHead;
+ rc = 0;
+ while (pBindMod)
+ {
+ KU32 fKind;
+ KLDRADDR uValue;
+ rc = kLdrModQuerySymbol(pBindMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP,
+ iSymbol, pchSymbol, cchSymbol, pszVersion,
+ kldrDyldModFixupGetImportCallback, pBindMod, &uValue, &fKind);
+ if ( !rc
+ && ( !fFound
+ || !(fKind & KLDRSYMKIND_WEAK)
+ )
+ )
+ {
+ *pfKind = fKind;
+ *puValue = uValue;
+ fFound = 1;
+ if (!(fKind & KLDRSYMKIND_WEAK))
+ break;
+ }
+
+ /* next */
+ pBindMod = pBindMod->Bind.pNext;
+ }
+ rc = fFound ? 0 : KLDR_ERR_SYMBOL_NOT_FOUND;
+ if (!fFound)
+ {
+ if (pchSymbol)
+ kldrDyldFailure(rc, "%s->%.*s%s", pDyldMod->pMod->pszName, cchSymbol, pchSymbol, pszVersion ? pszVersion : "");
+ else
+ kldrDyldFailure(rc, "%s->%d%s", pDyldMod->pMod->pszName, iSymbol, pszVersion ? pszVersion : "");
+ }
+ }
+
+ s_cRecursiveCalls--;
+ return rc;
+}
+
+
+/**
+ * Applies fixups to a module which prerequisistes has been
+ * successfully loaded.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * @param pMod The module which needs to be unmapped and set pending for destruction.
+ */
+int kldrDyldModFixup(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ /* sanity */
+ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0);
+ KLDRDYLDMOD_ASSERT( pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES
+ || pMod->enmState == KLDRSTATE_RELOADED_LOADED_PREREQUISITES);
+
+ /* do the job */
+ rc = kLdrModFixupMapping(pMod->pMod, kldrDyldModFixupGetImportCallback, pMod);/** @todo fixme. */
+ if (!rc)
+ pMod->enmState = KLDRSTATE_FIXED_UP;
+ return rc;
+}
+
+
+/**
+ * Calls the module initialization entry point if any.
+ *
+ * This is considered to be a module specific thing and leave if
+ * to the module interpreter. They will have to deal with different
+ * module init practices between platforms should there be any.
+ *
+ * @returns 0 and state changed to GOOD on success.
+ * Non-zero OS or kLdr status code and status changed to INITIALIZATION_FAILED on failure.
+ * @param pMod The module that should be initialized.
+ */
+int kldrDyldModCallInit(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION);
+ KLDRDYLDMOD_ASSERT(!pMod->fInitList);
+
+ pMod->enmState = KLDRSTATE_INITIALIZING;
+ rc = kLdrModCallInit(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod);
+ if (!rc)
+ {
+ pMod->enmState = KLDRSTATE_GOOD;
+ /* push it onto the termination list.*/
+ pMod->InitTerm.pPrev = NULL;
+ pMod->InitTerm.pNext = g_pkLdrDyldTermHead;
+ if (g_pkLdrDyldTermHead)
+ g_pkLdrDyldTermHead->InitTerm.pPrev = pMod;
+ else
+ g_pkLdrDyldTermTail = pMod;
+ g_pkLdrDyldTermHead = pMod;
+ }
+ else
+ pMod->enmState = KLDRSTATE_INITIALIZATION_FAILED;
+
+ return rc;
+}
+
+
+/**
+ * Calls the module termination entry point if any.
+ *
+ * This'll change the module status to PENDING_GC.
+ *
+ * @param pMod The module that should be initialized.
+ */
+void kldrDyldModCallTerm(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_TERMINATION);
+
+ pMod->enmState = KLDRSTATE_TERMINATING;
+ kLdrModCallTerm(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod);
+ pMod->enmState = KLDRSTATE_PENDING_GC;
+ /* unlinking on destruction. */
+}
+
+
+/**
+ * Calls the thread attach entry point if any.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pMod The module.
+ */
+int kldrDyldModAttachThread(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD);
+
+ return kLdrModCallThread(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod, 1 /* attach */);
+}
+
+
+/**
+ * Calls the thread detach entry point if any.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pMod The module.
+ */
+void kldrDyldModDetachThread(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD);
+
+ kLdrModCallThread(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod, 0 /* detach */);
+}
+
+
+/**
+ * Gets the main stack, allocate it if necessary.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param ppvStack Where to store the address of the stack (lowest address).
+ * @param pcbStack Where to store the size of the stack.
+ */
+int kldrDyldModGetMainStack(PKLDRDYLDMOD pMod, void **ppvStack, KSIZE *pcbStack)
+{
+ int rc = 0;
+ KLDRSTACKINFO StackInfo;
+ KLDRDYLDMOD_ASSERT(pMod->fExecutable);
+
+ /*
+ * Since we might have to allocate the stack ourselves, and there will only
+ * ever be one main stack, we'll be keeping the main stack info in globals.
+ */
+ if (!g_fkLdrDyldDoneMainStack)
+ {
+ rc = kLdrModGetStackInfo(pMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP, &StackInfo);
+ if (!rc)
+ {
+ /* check if there is a stack size override/default. */
+ KSIZE cbDefOverride;
+ if (kHlpGetEnvUZ("KLDR_MAIN_STACK_SIZE", &cbDefOverride))
+ cbDefOverride = 0;
+
+
+ /* needs allocating? */
+ if ( StackInfo.LinkAddress == NIL_KLDRADDR
+ || StackInfo.cbStack < cbDefOverride)
+ {
+ KSIZE cbStack = (KSIZE)K_MAX(StackInfo.cbStack, cbDefOverride);
+
+ g_pvkLdrDyldMainStack = kldrDyldOSAllocStack(cbStack);
+ if (g_pvkLdrDyldMainStack)
+ {
+ g_cbkLdrDyldMainStack = cbStack;
+ g_fkLdrDyldMainStackAllocated = 1;
+ }
+ else
+ rc = KLDR_ERR_MAIN_STACK_ALLOC_FAILED;
+ }
+ else
+ {
+ KLDRDYLDMOD_ASSERT(StackInfo.Address != NIL_KLDRADDR);
+ KLDRDYLDMOD_ASSERT(StackInfo.cbStack > 0);
+
+ g_fkLdrDyldMainStackAllocated = 0;
+ g_pvkLdrDyldMainStack = (void *)(KUPTR)StackInfo.Address;
+ KLDRDYLDMOD_ASSERT((KUPTR)g_pvkLdrDyldMainStack == StackInfo.Address);
+
+ g_cbkLdrDyldMainStack = (KSIZE)StackInfo.cbStack;
+ KLDRDYLDMOD_ASSERT(StackInfo.cbStack == g_cbkLdrDyldMainStack);
+ }
+ }
+ if (!rc)
+ g_fkLdrDyldDoneMainStack = 1;
+ }
+
+ if (!rc)
+ {
+ if (ppvStack)
+ *ppvStack = g_pvkLdrDyldMainStack;
+ if (pcbStack)
+ *pcbStack = g_cbkLdrDyldMainStack;
+ }
+
+ return rc;
+}
+
+
+/**
+ * This starts the executable module.
+ *
+ * @returns non-zero OS or kLdr status code on failure.
+ * (won't return on success.)
+ * @param pMod The executable module.
+ */
+int kldrDyldModStartExe(PKLDRDYLDMOD pMod)
+{
+ int rc;
+ KLDRADDR MainEPAddress;
+ void *pvStack;
+ KSIZE cbStack;
+ KLDRDYLDMOD_ASSERT(pMod->fExecutable);
+
+ rc = kLdrModQueryMainEntrypoint(pMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP, &MainEPAddress);
+ if (rc)
+ return rc;
+ rc = kldrDyldModGetMainStack(pMod, &pvStack, &cbStack);
+ if (rc)
+ return rc;
+ return kldrDyldOSStartExe((KUPTR)MainEPAddress, pvStack, cbStack);
+}
+
+
+/**
+ * Gets the module name.
+ *
+ * @returns 0 on success, KERR_BUFFER_OVERFLOW on failure.
+ * @param pMod The module.
+ * @param pszName Where to store the name.
+ * @param cchName The size of the name buffer.
+ */
+int kldrDyldModGetName(PKLDRDYLDMOD pMod, char *pszName, KSIZE cchName)
+{
+ KSIZE cch = K_MIN(cchName, pMod->pMod->cchName + 1);
+ if (cch)
+ {
+ kHlpMemCopy(pszName, pMod->pMod->pszName, cch - 1);
+ pszName[cch - 1] = '\0';
+ }
+ return cchName <= pMod->pMod->cchName ? KERR_BUFFER_OVERFLOW : 0;
+}
+
+
+/**
+ * Gets the module filename.
+ *
+ * @returns 0 on success, KERR_BUFFER_OVERFLOW on failure.
+ * @param pMod The module.
+ * @param pszFilename Where to store the filename.
+ * @param cchFilename The size of the filename buffer.
+ */
+int kldrDyldModGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename)
+{
+ KSIZE cch = K_MIN(cchFilename, pMod->pMod->cchFilename + 1);
+ if (cch)
+ {
+ kHlpMemCopy(pszFilename, pMod->pMod->pszFilename, cch - 1);
+ pszFilename[cch - 1] = '\0';
+ }
+ return cchFilename <= pMod->pMod->cchFilename ? KERR_BUFFER_OVERFLOW : 0;
+}
+
+
+/**
+ * Gets the address/value of a symbol in the specified module.
+ *
+ * @returns 0 on success, KLDR_ERR_SYMBOL_NOT_FOUND on failure.
+ * @param pMod The module.
+ * @param uSymbolOrdinal The symbol ordinal 0. This is ignored if the name is non-zero.
+ * @param pszSymbolName The symbol name. Can be NULL.
+ * @param puValue Where to store the value. optional.
+ * @param pfKind Where to store the symbol kind. optional.
+ */
+int kldrDyldModQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName,
+ KUPTR *puValue, KU32 *pfKind)
+{
+ int rc;
+ KLDRADDR uValue = 0;
+ KU32 fKind = 0;
+
+ rc = kLdrModQuerySymbol(pMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP,
+ uSymbolOrdinal, pszSymbolName, kHlpStrLen(pszSymbolName), NULL,
+ kldrDyldModFixupGetImportCallback, pMod,
+ &uValue, &fKind);
+ if (!rc)
+ {
+ if (puValue)
+ {
+ *puValue = (KUPTR)uValue;
+ KLDRDYLDMOD_ASSERT(*puValue == uValue);
+ }
+ if (pfKind)
+ *pfKind = fKind;
+ }
+
+ return rc;
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrDyldOS.c b/src/lib/kStuff/kLdr/kLdrDyldOS.c
new file mode 100644
index 0000000..ed47561
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyldOS.c
@@ -0,0 +1,133 @@
+/* $Id: kLdrDyldOS.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, OS specific operations.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+#if K_OS == K_OS_OS2
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+
+#elif K_OS == K_OS_WINDOWS
+# undef IMAGE_DOS_SIGNATURE
+# undef IMAGE_NT_SIGNATURE
+# include <Windows.h>
+
+#else
+# include <k/kHlpAlloc.h>
+
+#endif
+
+
+/**
+ * Allocates a stack.
+ *
+ * @returns Pointer to the stack. NULL on allocation failure (assumes out of memory).
+ * @param cb The size of the stack. This shall be page aligned.
+ * If 0, a OS specific default stack size will be employed.
+ */
+void *kldrDyldOSAllocStack(KSIZE cb)
+{
+#if K_OS == K_OS_OS2
+ APIRET rc;
+ PVOID pv;
+
+ if (!cb)
+ cb = 1 * 1024*1024; /* 1MB */
+
+ rc = DosAllocMem(&pv, cb, OBJ_TILE | PAG_COMMIT | PAG_WRITE | PAG_READ);
+ if (rc == NO_ERROR)
+ return pv;
+ return NULL;
+
+#elif K_OS == K_OS_WINDOWS
+
+ if (!cb)
+ cb = 1 *1024*1024; /* 1MB */
+
+ return VirtualAlloc(NULL, cb, MEM_COMMIT, PAGE_READWRITE);
+
+#else
+ void *pv;
+
+ if (!cb)
+ cb = 1 * 1024*1024; /* 1MB */
+
+ if (!kHlpPageAlloc(&pv, cb, KPROT_READWRITE, K_FALSE))
+ return pv;
+ return NULL;
+#endif
+}
+
+
+/**
+ * Invokes the main executable entry point with whatever
+ * parameters specific to the host OS and/or module format.
+ *
+ * @returns
+ * @param uMainEPAddress The address of the main entry point.
+ * @param pvStack Pointer to the stack object.
+ * @param cbStack The size of the stack object.
+ */
+int kldrDyldOSStartExe(KUPTR uMainEPAddress, void *pvStack, KSIZE cbStack)
+{
+#if K_OS == K_OS_WINDOWS
+ /*
+ * Invoke the entrypoint on the current stack for now.
+ * Deal with other formats and stack switching another day.
+ */
+ int rc;
+ int (*pfnEP)(void);
+ pfnEP = (int (*)(void))uMainEPAddress;
+
+ rc = pfnEP();
+
+ TerminateProcess(GetCurrentProcess(), rc);
+ kHlpAssert(!"TerminateProcess failed");
+ for (;;)
+ TerminateProcess(GetCurrentProcess(), rc);
+#endif
+
+ return -1;
+}
+
+
+void kldrDyldDoLoadExeStackSwitch(PKLDRDYLDMOD pExe, void *pvStack, KSIZE cbStack)
+{
+ /*kHlpAssert(!"not implemented");*/
+
+ /** @todo implement this properly! */
+ kldrDyldDoLoadExe(pExe);
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrDyldSem.c b/src/lib/kStuff/kLdr/kLdrDyldSem.c
new file mode 100644
index 0000000..9ffa497
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyldSem.c
@@ -0,0 +1,198 @@
+/* $Id: kLdrDyldSem.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, Semaphore Helper Functions.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kDefs.h>
+#include <k/kHlpSem.h>
+#include <k/kHlpAssert.h>
+
+#if K_OS == K_OS_DARWIN
+# include <mach/mach.h>
+# undef mach_task_self /* don't use the macro (if we're using bare helpers ) */
+
+#elif K_OS == K_OS_OS2
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+
+#elif K_OS == K_OS_WINDOWS
+# include <Windows.h>
+
+#else
+# error "port me"
+#endif
+
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+#if K_OS == K_OS_DARWIN
+/** The loader sempahore. */
+static semaphore_t g_Semaphore = MACH_PORT_NULL;
+
+#elif K_OS == K_OS_OS2
+/** The loader sempahore. */
+static HMTX g_hmtx;
+
+#elif K_OS == K_OS_WINDOWS
+/** The loader sempahore. */
+static CRITICAL_SECTION g_CritSect;
+
+#else
+# error "port me"
+#endif
+
+
+/**
+ * Initializes the loader semaphore.
+ *
+ * @returns 0 on success, non-zero OS status code on failure.
+ */
+int kLdrDyldSemInit(void)
+{
+#if K_OS == K_OS_DARWIN
+ kern_return_t krc;
+
+ krc = semaphore_create(mach_task_self(), &g_Semaphore, SYNC_POLICY_FIFO, 0);
+ if (krc != KERN_SUCCESS)
+ return krc;
+
+#elif K_OS == K_OS_OS2
+ APIRET rc;
+ g_hmtx = NULLHANDLE;
+ rc = DosCreateMutexSem(NULL, &g_hmtx, 0, FALSE);
+ if (rc)
+ return rc;
+
+#elif K_OS == K_OS_WINDOWS
+ InitializeCriticalSection(&g_CritSect);
+
+#else
+# error "port me"
+#endif
+ return 0;
+}
+
+
+/**
+ * Terminates the loader semaphore.
+ */
+void kLdrDyldSemTerm(void)
+{
+#if K_OS == K_OS_DARWIN
+ kern_return_t krc;
+ semaphore_t Semaphore = g_Semaphore;
+ g_Semaphore = MACH_PORT_NULL;
+ krc = semaphore_destroy(mach_task_self(), Semaphore);
+ kHlpAssert(krc == KERN_SUCCESS); (void)krc;
+
+#elif K_OS == K_OS_OS2
+ HMTX hmtx = g_hmtx;
+ g_hmtx = NULLHANDLE;
+ DosCloseMutexSem(hmtx);
+
+#elif K_OS == K_OS_WINDOWS
+ DeleteCriticalSection(&g_CritSect);
+
+#else
+# error "port me"
+#endif
+}
+
+
+/**
+ * Requests the loader sempahore ownership.
+ * This can be done recursivly.
+ *
+ * @returns 0 on success, non-zero OS status code on failure.
+ */
+int kLdrDyldSemRequest(void)
+{
+#if K_OS == K_OS_DARWIN
+ /* not sure about this... */
+ kern_return_t krc;
+ do krc = semaphore_wait(g_Semaphore);
+ while (krc == KERN_ABORTED);
+ if (krc == KERN_SUCCESS)
+ return 0;
+ return krc;
+
+#elif K_OS == K_OS_OS2
+ APIRET rc = DosRequestMutexSem(g_hmtx, 5000);
+ if (rc == ERROR_TIMEOUT || rc == ERROR_SEM_TIMEOUT || rc == ERROR_INTERRUPT)
+ {
+ unsigned i = 0;
+ do
+ {
+ /** @todo check for deadlocks etc. */
+ rc = DosRequestMutexSem(g_hmtx, 1000);
+ } while ( ( rc == ERROR_TIMEOUT
+ || rc == ERROR_SEM_TIMEOUT
+ || rc == ERROR_INTERRUPT)
+ && i++ < 120);
+ }
+ return rc;
+
+#elif K_OS == K_OS_WINDOWS
+ EnterCriticalSection(&g_CritSect);
+ return 0;
+
+#else
+# error "port me"
+#endif
+}
+
+
+/**
+ * Releases the loader semaphore ownership.
+ * The caller is responsible for making sure it's the semaphore owner!
+ */
+void kLdrDyldSemRelease(void)
+{
+#if K_OS == K_OS_DARWIN
+ /* not too sure about this... */
+ kern_return_t krc = semaphore_signal(g_Semaphore);
+ kHlpAssert(krc == KERN_SUCCESS); (void)krc;
+
+#elif K_OS == K_OS_OS2
+ APIRET rc = DosReleaseMutexSem(g_hmtx);
+ kHlpAssert(!rc); (void)rc;
+
+#elif K_OS == K_OS_WINDOWS
+ LeaveCriticalSection(&g_CritSect);
+
+#else
+# error "port me"
+#endif
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrExeStub-os2.asm b/src/lib/kStuff/kLdr/kLdrExeStub-os2.asm
new file mode 100644
index 0000000..ad897e3
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrExeStub-os2.asm
@@ -0,0 +1,72 @@
+; $Id: kLdrExeStub-os2.asm 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - OS/2 Loader Stub.
+;
+; This file contains a 64kb code/data/stack segment which is used to kick off
+; the loader dll that loads the process.
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+struc KLDRARGS
+ .fFlags resd 1
+ .enmSearch resd 1
+ .szExecutable resb 260
+ .szDefPrefix resb 16
+ .szDefSuffix resb 16
+ .szLibPath resb (4096 - (4 + 4 + 16 + 16 + 260))
+endstruc
+
+extern _kLdrDyldLoadExe
+
+
+segment DATA32 stack CLASS=DATA align=16 use32
+..start:
+ push args
+ jmp _kLdrDyldLoadExe
+
+;
+; Argument structure.
+;
+align 4
+args:
+istruc KLDRARGS
+ at KLDRARGS.fFlags, dd 0
+ at KLDRARGS.enmSearch, dd 2 ;KLDRDYLD_SEARCH_HOST
+ at KLDRARGS.szDefPrefix, db ''
+ at KLDRARGS.szDefSuffix, db '.dll'
+; at KLDRARGS.szExecutable, db 'tst-0.exe'
+ at KLDRARGS.szLibPath, db ''
+iend
+
+segment STACK32 stack CLASS=STACK align=16 use32
+; pad up to 64KB.
+resb 60*1024
+
+global WEAK$ZERO
+WEAK$ZERO EQU 0
+group DGROUP, DATA32 STACK32
+
diff --git a/src/lib/kStuff/kLdr/kLdrExeStub-os2.c b/src/lib/kStuff/kLdr/kLdrExeStub-os2.c
new file mode 100644
index 0000000..17a4b1c
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrExeStub-os2.c
@@ -0,0 +1,59 @@
+/* $Id: kLdrExeStub-os2.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - OS/2 C Loader Stub.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include <os2.h>
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The stub arguments. */
+static const KLDREXEARGS g_Args =
+{
+ /* .fFlags = */ KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT,
+ /* .enmSearch = */ KLDRDYLD_SEARCH_OS2,
+ /* .szExecutable = */ "tst-0", /* just while testing */
+ /* .szDefPrefix = */ "",
+ /* .szDefSuffix = */ ".dll",
+ /* .szLibPath = */ ""
+};
+
+/**
+ * OS/2 'main'.
+ */
+int _System OS2Main(HMODULE hmod, ULONG ulReserved, PCH pszzEnv, PCH pszzCmdLine)
+{
+ return kLdrDyldLoadExe(&g_Args, &hmod);
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrExeStub-os2A.asm b/src/lib/kStuff/kLdr/kLdrExeStub-os2A.asm
new file mode 100644
index 0000000..b3d692c
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrExeStub-os2A.asm
@@ -0,0 +1,41 @@
+; $Id: kLdrExeStub-os2A.asm 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - OS/2 Loader Stub, entry point thingy...
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+
+segment TEXT32 public CLASS=CODE align=16 use32
+extern OS2Main
+..start:
+ jmp OS2Main
+
+segment DATA32 stack CLASS=DATA align=16 use32
+
+global WEAK$ZERO
+WEAK$ZERO EQU 0
+
diff --git a/src/lib/kStuff/kLdr/kLdrExeStub-win.c b/src/lib/kStuff/kLdr/kLdrExeStub-win.c
new file mode 100644
index 0000000..55920e5
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrExeStub-win.c
@@ -0,0 +1,62 @@
+/* $Id: kLdrExeStub-win.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Windows Loader Stub.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include <Windows.h>
+#include "kLdrInternal.h"
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The stub arguments. */
+static const KLDREXEARGS g_Args =
+{
+ /* .fFlags = */ KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT,
+ /* .enmSearch = */ KLDRDYLD_SEARCH_WINDOWS,
+ /* .szExecutable = */ "tst-0", /* just while testing */
+ /* .szDefPrefix = */ "",
+ /* .szDefSuffix = */ "",
+ /* .szLibPath = */ ""
+};
+
+/**
+ * Windows 'main'.
+ */
+int WindowsMain(void)
+{
+ kLdrDyldLoadExe(&g_Args, NULL);
+ /* won't happen */
+ return 0;
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrHlp.h b/src/lib/kStuff/kLdr/kLdrHlp.h
new file mode 100644
index 0000000..ab54f10
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrHlp.h
@@ -0,0 +1,9 @@
+
+int kldrHlpGetEnv(const char *pszVar, char *pszVal, KSIZE cchVal);
+int kldrHlpGetEnvUZ(const char *pszVar, KSIZE *pcb);
+
+void kldrHlpExit(int rc);
+void kldrHlpSleep(unsigned cMillies);
+
+char *kldrHlpInt2Ascii(char *psz, KSIZE cch, long lVal, unsigned iBase);
+
diff --git a/src/lib/kStuff/kLdr/kLdrInternal.h b/src/lib/kStuff/kLdr/kLdrInternal.h
new file mode 100644
index 0000000..c670a41
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrInternal.h
@@ -0,0 +1,463 @@
+/* $Id: kLdrInternal.h 117 2020-03-15 15:23:36Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, internal header.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ___kLdrInternal_h___
+#define ___kLdrInternal_h___
+
+#include <k/kHlp.h>
+#include <k/kRdr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(__X86__) && !defined(__AMD64__)
+# if defined(__i386__) || defined(_M_IX86)
+# define __X86__
+# elif defined(__x86_64__) || defined(_M_X64) || defined(__AMD64__) || defined(_M_AMD64)
+# define __AMD64__
+# else
+# error "can't figure out the target arch."
+# endif
+#endif
+
+/* ignore definitions in winnt.h */
+#undef IMAGE_DOS_SIGNATURE
+#undef IMAGE_NT_SIGNATURE
+
+/** @name Signatures we know
+ * @{ */
+/** ELF signature ("\x7fELF"). */
+#define IMAGE_ELF_SIGNATURE K_LE2H_U32(0x7f | ('E' << 8) | ((KU32)'L' << 16) | ((KU32)'F' << 24))
+/** PE signature ("PE\0\0"). */
+#define IMAGE_NT_SIGNATURE K_LE2H_U32('P' | ('E' << 8))
+/** LX signature ("LX") */
+#define IMAGE_LX_SIGNATURE K_LE2H_U16('L' | ('X' << 8))
+/** LE signature ("LE") */
+#define IMAGE_LE_SIGNATURE K_LE2H_U16('L' | ('E' << 8))
+/** NE signature ("NE") */
+#define IMAGE_NE_SIGNATURE K_LE2H_U16('N' | ('E' << 8))
+/** MZ signature ("MZ"). */
+#define IMAGE_DOS_SIGNATURE K_LE2H_U16('M' | ('Z' << 8))
+/** The FAT signature (universal binaries). */
+#define IMAGE_FAT_SIGNATURE KU32_C(0xcafebabe)
+/** The FAT signature (universal binaries), other endian. */
+#define IMAGE_FAT_SIGNATURE_OE KU32_C(0xbebafeca)
+/** The 32-bit Mach-O signature. */
+#define IMAGE_MACHO32_SIGNATURE KU32_C(0xfeedface)
+/** The 32-bit Mach-O signature, other endian. */
+#define IMAGE_MACHO32_SIGNATURE_OE KU32_C(0xcefaedfe)
+/** The 64-bit Mach-O signature. */
+#define IMAGE_MACHO64_SIGNATURE KU32_C(0xfeedfacf)
+/** The 64-bit Mach-O signature, other endian. */
+#define IMAGE_MACHO64_SIGNATURE_OE KU32_C(0xfefaedfe)
+/** @} */
+
+/** @defgroup grp_kLdrInternal Internals
+ * @internal
+ * @{
+ */
+
+KI32 kldrModLXDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved);
+KI32 kldrModPEDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved);
+
+/**
+ * The state of a dynamic loader module.
+ * @image html KLDRSTATE.gif "The state diagram"
+ */
+typedef enum KLDRSTATE
+{
+ /** The usual invalid 0 enum. */
+ KLDRSTATE_INVALID = 0,
+
+ /** The module has just been opened and linked into the load list.
+ *
+ * Prev state: -
+ * Next state: MAPPED, PENDING_DESTROY
+ */
+ KLDRSTATE_OPEN,
+
+ /** The module segments has been mapped into the process memory.
+ *
+ * Prev state: OPEN
+ * Next state: LOADED_PREREQUISITES, PENDING_DESTROY
+ */
+ KLDRSTATE_MAPPED,
+ /** The module has been reloaded and needs to be fixed up again.
+ * This can occure when the loader is called recursivly.
+ *
+ * The reason RELOADED modules must go back to the PENDING_GC state is
+ * because we want to guard against uninit order issues, and therefore
+ * doesn't unmap modules untill all pending termintation callbacks has
+ * been executed.
+ *
+ * Prev state: PENDING_GC
+ * Next state: RELOADED_LOADED_PREREQUISITES, PENDING_GC
+ */
+ KLDRSTATE_RELOADED,
+
+ /** The immediate prerequisites have been loaded.
+ *
+ * Prev state: MAPPED
+ * Next state: FIXED_UP, PENDING_DESTROY
+ */
+ KLDRSTATE_LOADED_PREREQUISITES,
+ /** The immediate prerequisites have been loaded for a reloaded module.
+ *
+ * Prev state: RELOADED
+ * Next state: RELOADED_FIXED_UP, PENDING_GC
+ */
+ KLDRSTATE_RELOADED_LOADED_PREREQUISITES,
+
+ /** Fixups has been applied.
+ *
+ * Prev state: LOADED_PREREQUISITES
+ * Next state: PENDING_INITIALIZATION, PENDING_DESTROY
+ */
+ KLDRSTATE_FIXED_UP,
+ /** Fixups has been applied.
+ *
+ * Prev state: RELOADED_LOADED_PREREQUISITES
+ * Next state: PENDING_INITIALIZATION, PENDING_GC
+ */
+ KLDRSTATE_RELOADED_FIXED_UP,
+
+ /** Pending initialization.
+ * While the module is in this state the loader is in reentrant mode.
+ *
+ * Prev state: FIXED_UP, RELOADED_FIXED_UP
+ * Next state: INITIALIZATION, PENDING_GC
+ */
+ KLDRSTATE_PENDING_INITIALIZATION,
+
+ /** Initializing.
+ * While the module is in this state the loader is in reentrant mode.
+ *
+ * Prev state: PENDING_INITIALIZATION
+ * Next state: GOOD, PENDING_GC
+ */
+ KLDRSTATE_INITIALIZING,
+
+ /** Initialization failed.
+ *
+ * This is somewhat similar to PENDING_GC except that, a module
+ * in this state cannot be reloaded untill we've done GC. This ensures
+ * that a init failure during recursive loading is propagated up.
+ *
+ * While the module is in this state the loader is in reentrant mode.
+ *
+ * Prev state: INITIALIZING
+ * Next state: GC
+ */
+ KLDRSTATE_INITIALIZATION_FAILED,
+
+ /** The module has been successfully loaded and initialized.
+ * While the module is in this state the loader can be in reentrant
+ * or 'unused' mode.
+ *
+ * Prev state: INITIALIZING
+ * Next state: PENDING_TERMINATION
+ */
+ KLDRSTATE_GOOD,
+
+ /** Pending termination, reference count is 0.
+ * While the module is in this state the loader is in reentrant mode.
+ * Prerequisite modules are dropped when a module enters this state.
+ *
+ * Prev state: GOOD
+ * Next state: TERMINATING, GOOD
+ */
+ KLDRSTATE_PENDING_TERMINATION,
+
+ /** Terminating, reference count is still 0.
+ * While the module is in this state the loader is in reentrant mode.
+ *
+ * Prev state: PENDING_TERMINATION
+ * Next state: PENDING_GC
+ */
+ KLDRSTATE_TERMINATING,
+
+ /** Pending garbage collection.
+ * Prerequisite modules are dropped when a module enters this state (if not done already).
+ *
+ * Prev state: TERMINATING, PENDING_INITIALIZATION, INITIALIZATION_FAILED
+ * Next state: GC, RELOADED
+ */
+ KLDRSTATE_PENDING_GC,
+
+ /** Being garbage collected.
+ *
+ * Prev state: PENDING_GC, INITIALIZATION_FAILED
+ * Next state: PENDING_DESTROY, DESTROYED
+ */
+ KLDRSTATE_GC,
+
+ /** The module has be unlinked, but there are still stack references to it.
+ *
+ * Prev state: GC, FIXED_UP, LOADED_PREREQUISITES, MAPPED, OPEN
+ * Next state: DESTROYED
+ */
+ KLDRSTATE_PENDING_DESTROY,
+
+ /** The module has been destroyed but not freed yet.
+ *
+ * This happens when a module ends up being destroyed when cRefs > 0. The
+ * module structure will be freed when cRefs reaches 0.
+ *
+ * Prev state: GC, PENDING_DESTROY
+ */
+ KLDRSTATE_DESTROYED,
+
+ /** The end of valid states (exclusive) */
+ KLDRSTATE_END = KLDRSTATE_DESTROYED,
+ /** The usual 32-bit blowup. */
+ KLDRSTATE_32BIT_HACK = 0x7fffffff
+} KLDRSTATE;
+
+
+/**
+ * Dynamic loader module.
+ */
+typedef struct KLDRDYLDMOD
+{
+ /** Magic number. */
+ KU32 u32MagicHead;
+ /** The module state. */
+ KLDRSTATE enmState;
+ /** The module. */
+ PKLDRMOD pMod;
+ /** The module handle. */
+ HKLDRMOD hMod;
+ /** The total number of references. */
+ KU32 cRefs;
+ /** The number of dependency references. */
+ KU32 cDepRefs;
+ /** The number of dynamic load references. */
+ KU32 cDynRefs;
+ /** Set if this is the executable module.
+ * When clear, the module is a shared object or relocatable object. */
+ KU32 fExecutable : 1;
+ /** Global DLL (set) or specific DLL (clear). */
+ KU32 fGlobalOrSpecific : 1;
+ /** Whether the module contains bindable symbols in the global unix namespace. */
+ KU32 fBindable : 1;
+ /** Set if linked into the global init list. */
+ KU32 fInitList : 1;
+ /** Already loaded or checked prerequisites.
+ * This flag is used when loading prerequisites, when set it means that
+ * this module is already seen and shouldn't be processed again. */
+ KU32 fAlreadySeen : 1;
+ /** Set if the module is currently mapped.
+ * This is used to avoid unnecessary calls to kLdrModUnmap during cleanup. */
+ KU32 fMapped : 1;
+ /** Set if TLS allocation has been done. (part of the mapping). */
+ KU32 fAllocatedTLS : 1;
+ /** Reserved for future use. */
+ KU32 f25Reserved : 25;
+ /** The load list linkage. */
+ struct
+ {
+ /** The next module in the list. */
+ struct KLDRDYLDMOD *pNext;
+ /** The prev module in the list. */
+ struct KLDRDYLDMOD *pPrev;
+ } Load;
+ /** The initialization and termination list linkage.
+ * If non-recursive initialization is used, the module will be pushed on
+ * the initialization list.
+ * A module will be linked into the termination list upon a successful
+ * return from module initialization. */
+ struct
+ {
+ /** The next module in the list. */
+ struct KLDRDYLDMOD *pNext;
+ /** The prev module in the list. */
+ struct KLDRDYLDMOD *pPrev;
+ } InitTerm;
+ /** The bind order list linkage.
+ * The module is not in this list when fBindable is clear. */
+ struct
+ {
+ /** The next module in the list. */
+ struct KLDRDYLDMOD *pNext;
+ /** The prev module in the list. */
+ struct KLDRDYLDMOD *pPrev;
+ } Bind;
+
+ /** The number of prerequisite modules in the prereq array. */
+ KU32 cPrereqs;
+ /** Pointer to an array of prerequisite module pointers.
+ * This array is only filled when in the states starting with
+ * KLDRSTATE_LOADED_PREREQUISITES thru KLDRSTATE_GOOD.
+ */
+ struct KLDRDYLDMOD **papPrereqs;
+
+ /** Magic number. */
+ KU32 u32MagicTail;
+} KLDRDYLDMOD, *PKLDRDYLDMOD, **PPKLDRDYLDMOD;
+
+/** KLDRDYLDMOD magic value. (Fuyumi Soryo) */
+#define KLDRDYMOD_MAGIC 0x19590106
+
+/** Return / crash validation of a module handle argument. */
+#define KLDRDYLD_VALIDATE_HKLDRMOD(hMod) \
+ do { \
+ if ( (hMod) == NIL_HKLDRMOD \
+ || (hMod)->u32MagicHead != KLDRDYMOD_MAGIC \
+ || (hMod)->u32MagicTail != KLDRDYMOD_MAGIC) \
+ { \
+ return KERR_INVALID_HANDLE; \
+ } \
+ } while (0)
+
+
+int kldrInit(void);
+void kldrTerm(void);
+
+int kldrDyldInit(void);
+void kldrDyldTerm(void);
+
+void kldrDyldDoLoadExe(PKLDRDYLDMOD pExe);
+int kldrDyldFailure(int rc, const char *pszFormat, ...);
+
+int kldrDyldOSStartExe(KUPTR uMainEntrypoint, void *pvStack, KSIZE cbStack);
+void *kldrDyldOSAllocStack(KSIZE cb);
+
+int kldrDyldFindInit(void);
+int kldrDyldFindNewModule(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod);
+int kldrDyldFindExistingModule(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod);
+
+int kldrDyldGetPrerequisite(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch,
+ unsigned fFlags, PKLDRDYLDMOD pDep, PPKLDRDYLDMOD ppMod);
+
+
+int kldrDyldModCreate(PKRDR pRdr, KU32 fFlags, PPKLDRDYLDMOD ppMod);
+void kldrDyldModDestroy(PKLDRDYLDMOD pMod);
+void kldrDyldModAddRef(PKLDRDYLDMOD pMod);
+void kldrDyldModDeref(PKLDRDYLDMOD pMod);
+void kldrDyldModAddDep(PKLDRDYLDMOD pMod, PKLDRDYLDMOD pDep);
+void kldrDyldModRemoveDep(PKLDRDYLDMOD pMod, PKLDRDYLDMOD pDep);
+int kldrDyldModDynamicLoad(PKLDRDYLDMOD pMod);
+int kldrDyldModDynamicUnload(PKLDRDYLDMOD pMod);
+void kldrDyldModMarkGlobal(PKLDRDYLDMOD pMod);
+void kldrDyldModMarkSpecific(PKLDRDYLDMOD pMod);
+void kldrDyldModSetBindable(PKLDRDYLDMOD pMod, unsigned fDeep);
+void kldrDyldModClearBindable(PKLDRDYLDMOD pMod);
+int kldrDyldModMap(PKLDRDYLDMOD pMod);
+int kldrDyldModUnmap(PKLDRDYLDMOD pMod);
+int kldrDyldModLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags);
+int kldrDyldModCheckPrerequisites(PKLDRDYLDMOD pMod);
+void kldrDyldModUnloadPrerequisites(PKLDRDYLDMOD pMod);
+int kldrDyldModFixup(PKLDRDYLDMOD pMod);
+int kldrDyldModCallInit(PKLDRDYLDMOD pMod);
+void kldrDyldModCallTerm(PKLDRDYLDMOD pMod);
+int kldrDyldModReload(PKLDRDYLDMOD pMod);
+int kldrDyldModAttachThread(PKLDRDYLDMOD pMod);
+void kldrDyldModDetachThread(PKLDRDYLDMOD pMod);
+int kldrDyldModGetMainStack(PKLDRDYLDMOD pMod, void **ppvStack, KSIZE *pcbStack);
+int kldrDyldModStartExe(PKLDRDYLDMOD pMod);
+
+int kldrDyldModGetName(PKLDRDYLDMOD pMod, char *pszName, KSIZE cchName);
+int kldrDyldModGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename);
+int kldrDyldModQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName, KUPTR *puValue, KU32 *pfKind);
+
+
+/** Pointer to the head module (the executable).
+ * (This is exported, so no prefix.) */
+extern PKLDRDYLDMOD kLdrDyldHead;
+/** Pointer to the tail module.
+ * (This is exported, so no prefix.) */
+extern PKLDRDYLDMOD kLdrDyldTail;
+/** Pointer to the head module of the initialization list.
+ * The outermost load call will pop elements from this list in LIFO order (i.e.
+ * from the tail). The list is only used during non-recursive initialization
+ * and may therefore share the pNext/pPrev members with the termination list
+ * since we don't push a module onto the termination list untill it has been
+ * successfully initialized. */
+extern PKLDRDYLDMOD g_pkLdrDyldInitHead;
+/** Pointer to the tail module of the initalization list. */
+extern PKLDRDYLDMOD g_pkLdrDyldInitTail;
+/** Pointer to the head module of the termination order list. */
+extern PKLDRDYLDMOD g_pkLdrDyldTermHead;
+/** Pointer to the tail module of the termination order list. */
+extern PKLDRDYLDMOD g_pkLdrDyldTermTail;
+/** Pointer to the head module of the bind order list.
+ * The modules in this list makes up the global namespace used when binding symbol unix fashion. */
+extern PKLDRDYLDMOD g_pkLdrDyldBindHead;
+/** Pointer to the tail module of the bind order list. */
+extern PKLDRDYLDMOD g_pkLdrDyldBindTail;
+
+/** Indicates that the other MainStack globals have been filled in. */
+extern unsigned g_fkLdrDyldDoneMainStack;
+/** Whether the stack was allocated seperatly or was part of the executable. */
+extern unsigned g_fkLdrDyldMainStackAllocated;
+/** Pointer to the main stack object. */
+extern void *g_pvkLdrDyldMainStack;
+/** The size of the main stack object. */
+extern KSIZE g_cbkLdrDyldMainStack;
+
+/** The global error buffer. */
+extern char g_szkLdrDyldError[1024];
+
+extern char kLdrDyldExePath[8192];
+extern char kLdrDyldLibraryPath[8192];
+extern char kLdrDyldDefPrefix[16];
+extern char kLdrDyldDefSuffix[16];
+
+extern int g_fBootstrapping;
+
+
+/** @name The Loader semaphore
+ * @{ */
+int kLdrDyldSemInit(void);
+void kLdrDyldSemTerm(void);
+int kLdrDyldSemRequest(void);
+void kLdrDyldSemRelease(void);
+/** @} */
+
+
+/** @name Module interpreter method tables
+ * @{ */
+extern KLDRMODOPS g_kLdrModLXOps;
+extern KLDRMODOPS g_kLdrModMachOOps;
+extern KLDRMODOPS g_kLdrModNativeOps;
+extern KLDRMODOPS g_kLdrModPEOps;
+/** @} */
+
+
+/** @} */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/kStuff/kLdr/kLdrMod.c b/src/lib/kStuff/kLdr/kLdrMod.c
new file mode 100644
index 0000000..5c11260
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrMod.c
@@ -0,0 +1,914 @@
+/* $Id: kLdrMod.c 81 2016-08-18 22:10:38Z bird $ */
+/** @file
+ * kLdr - The Module Interpreter.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+#include <k/kCpu.h>
+#include <k/kLdrFmts/mz.h>
+#if 1 /* testing headers */
+# include <k/kLdrFmts/pe.h>
+# include <k/kLdrFmts/lx.h>
+# include <k/kLdrFmts/mach-o.h>
+#endif
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRMOD_STRICT
+ * Define KLDRMOD_STRICT to enabled strict checks in KLDRMOD. */
+#define KLDRMOD_STRICT 1
+
+/** @def KLDRMOD_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMOD_STRICT
+# define KLDRMOD_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRMOD_ASSERT(expr) do {} while (0)
+#endif
+
+/** Return / crash validation of a module argument. */
+#define KLDRMOD_VALIDATE_EX(pMod, rc) \
+ do { \
+ if ( (pMod)->u32Magic != KLDRMOD_MAGIC \
+ || (pMod)->pOps == NULL \
+ )\
+ { \
+ return (rc); \
+ } \
+ } while (0)
+
+/** Return / crash validation of a module argument. */
+#define KLDRMOD_VALIDATE(pMod) \
+ KLDRMOD_VALIDATE_EX(pMod, KERR_INVALID_PARAMETER)
+
+/** Return / crash validation of a module argument. */
+#define KLDRMOD_VALIDATE_VOID(pMod) \
+ do { \
+ if ( (pMod)->u32Magic != KLDRMOD_MAGIC \
+ || (pMod)->pOps == NULL \
+ )\
+ { \
+ return; \
+ } \
+ } while (0)
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The list of module interpreters. */
+static PCKLDRMODOPS g_pModInterpreterHead = NULL;
+
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+
+
+
+/**
+ * Open a executable image by file name.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ * On failure, a non-zero OS specific error code is returned.
+ * @param pszFilename The filename to open.
+ * @param fFlags Flags, MBZ.
+ * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means
+ * anything goes, but with a preference for the current
+ * host architecture.
+ * @param ppMod Where to store the module handle.
+ */
+int kLdrModOpen(const char *pszFilename, KU32 fFlags, KCPUARCH enmCpuArch, PPKLDRMOD ppMod)
+{
+ /*
+ * Open the file using a bit provider.
+ */
+ PKRDR pRdr;
+ int rc = kRdrOpen(&pRdr, pszFilename);
+ if (!rc)
+ {
+ rc = kLdrModOpenFromRdr(pRdr, fFlags, enmCpuArch, ppMod);
+ if (!rc)
+ return 0;
+ kRdrClose(pRdr);
+ }
+ return rc;
+}
+
+
+/**
+ * Select image from the FAT according to the enmCpuArch and fFlag.
+ *
+ * @returns 0 on success and *poffHdr set to the image header.
+ * On failure, a non-zero error code is returned.
+ *
+ * @param pRdr The file provider instance to use.
+ * @param fFlags Flags, MBZ.
+ * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means
+ * anything goes, but with a preference for the current
+ * host architecture.
+ * @param u32Magic The FAT magic.
+ * @param poffHdr Where to store the offset of the selected image.
+ */
+static int kldrModOpenFromRdrSelectImageFromFAT(PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KU32 u32Magic, KLDRFOFF *poffHdr)
+{
+ int rcRet = KLDR_ERR_CPU_ARCH_MISMATCH;
+ KLDRFOFF off = *poffHdr + sizeof(KU32);
+ KLDRFOFF offEndFAT;
+ KBOOL fCpuArchWhatever;
+ KU32 cArchs;
+ KU32 iArch;
+ int rc;
+ K_NOREF(fFlags);
+
+ /* Read fat_header_t::nfat_arch. */
+ rc = kRdrRead(pRdr, &cArchs, sizeof(cArchs), off);
+ if (rc)
+ return rc;
+ off += sizeof(KU32);
+ if (u32Magic == IMAGE_FAT_SIGNATURE_OE)
+ cArchs = K_E2E_U32(cArchs);
+ if (cArchs == 0)
+ return KLDR_ERR_FAT_INVALID;
+
+ /* Deal with KCPUARCH_UNKNOWN. */
+ fCpuArchWhatever = enmCpuArch == KCPUARCH_UNKNOWN;
+ if (fCpuArchWhatever)
+ {
+ KCPU enmCpuIgnored;
+ kCpuGetArchAndCpu(&enmCpuArch, &enmCpuIgnored);
+ }
+
+ /*
+ * Iterate the architecture list.
+ */
+ offEndFAT = off + cArchs * sizeof(fat_arch_t);
+ for (iArch = 0; iArch < cArchs; iArch++)
+ {
+ KCPUARCH enmEntryArch;
+ fat_arch_t Arch;
+ rc = kRdrRead(pRdr, &Arch, sizeof(Arch), off);
+ if (rc)
+ return rc;
+ off += sizeof(Arch);
+
+ if (u32Magic == IMAGE_FAT_SIGNATURE_OE)
+ {
+ Arch.cputype = K_E2E_U32(Arch.cputype);
+ Arch.cpusubtype = K_E2E_U32(Arch.cpusubtype);
+ Arch.offset = K_E2E_U32(Arch.offset);
+ Arch.size = K_E2E_U32(Arch.size);
+ Arch.align = K_E2E_U32(Arch.align);
+ }
+
+ /* Simple validation. */
+ if ( (KLDRFOFF)Arch.offset < offEndFAT
+ || (KLDRFOFF)Arch.offset >= kRdrSize(pRdr)
+ || Arch.align >= 32
+ || Arch.offset & ((KU32_C(1) << Arch.align) - KU32_C(1)))
+ return KLDR_ERR_FAT_INVALID;
+
+ /* deal with the cputype and cpusubtype. (See similar code in kLdrModMachO.c.) */
+ switch (Arch.cputype)
+ {
+ case CPU_TYPE_X86:
+ enmEntryArch = KCPUARCH_X86_32;
+ switch (Arch.cpusubtype)
+ {
+ case CPU_SUBTYPE_I386_ALL:
+ /*case CPU_SUBTYPE_386: ^^ ;*/
+ case CPU_SUBTYPE_486:
+ case CPU_SUBTYPE_486SX:
+ /*case CPU_SUBTYPE_586: vv */
+ case CPU_SUBTYPE_PENT:
+ case CPU_SUBTYPE_PENTPRO:
+ case CPU_SUBTYPE_PENTII_M3:
+ case CPU_SUBTYPE_PENTII_M5:
+ case CPU_SUBTYPE_CELERON:
+ case CPU_SUBTYPE_CELERON_MOBILE:
+ case CPU_SUBTYPE_PENTIUM_3:
+ case CPU_SUBTYPE_PENTIUM_3_M:
+ case CPU_SUBTYPE_PENTIUM_3_XEON:
+ case CPU_SUBTYPE_PENTIUM_M:
+ case CPU_SUBTYPE_PENTIUM_4:
+ case CPU_SUBTYPE_PENTIUM_4_M:
+ case CPU_SUBTYPE_XEON:
+ case CPU_SUBTYPE_XEON_MP:
+ break;
+ default:
+ return KLDR_ERR_FAT_UNSUPPORTED_CPU_SUBTYPE;
+ }
+ break;
+
+ case CPU_TYPE_X86_64:
+ enmEntryArch = KCPUARCH_AMD64;
+ switch (Arch.cpusubtype & ~CPU_SUBTYPE_MASK)
+ {
+ case CPU_SUBTYPE_X86_64_ALL:
+ break;
+ default:
+ return KLDR_ERR_FAT_UNSUPPORTED_CPU_SUBTYPE;
+ }
+ break;
+
+ default:
+ enmEntryArch = KCPUARCH_UNKNOWN;
+ break;
+ }
+
+ /*
+ * Finally the actual image selecting.
+ *
+ * Return immediately on a perfect match. Otherwise continue looking,
+ * if we're none too picky, remember the first image in case we don't
+ * get lucky.
+ */
+ if (enmEntryArch == enmCpuArch)
+ {
+ *poffHdr = Arch.offset;
+ return 0;
+ }
+
+ if ( fCpuArchWhatever
+ && rcRet == KLDR_ERR_CPU_ARCH_MISMATCH)
+ {
+ *poffHdr = Arch.offset;
+ rcRet = 0;
+ }
+ }
+
+ return rcRet;
+}
+
+
+/**
+ * Open a executable image from a file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ * On failure, a non-zero OS specific error code is returned.
+ * @param pRdr The file provider instance to use.
+ * On success, the ownership of the instance is taken by the
+ * module and the caller must not ever touch it again.
+ * (The instance is not closed on failure, the call has to do that.)
+ * @param fFlags Flags, MBZ.
+ * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means
+ * anything goes, but with a preference for the current
+ * host architecture.
+ * @param ppMod Where to store the module handle.
+ */
+int kLdrModOpenFromRdr(PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, PPKLDRMOD ppMod)
+{
+ union
+ {
+ KU32 u32;
+ KU16 u16;
+ KU16 au16[2];
+ KU8 au8[4];
+ } u;
+ KLDRFOFF offHdr = 0;
+ int rc;
+
+ kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER);
+
+ for (;;)
+ {
+ /*
+ * Try figure out what kind of image this is.
+ * Always read the 'new header' if we encounter MZ.
+ */
+ rc = kRdrRead(pRdr, &u, sizeof(u), offHdr);
+ if (rc)
+ return rc;
+ if ( u.u16 == IMAGE_DOS_SIGNATURE
+ && kRdrSize(pRdr) > (KFOFF)sizeof(IMAGE_DOS_HEADER))
+ {
+ rc = kRdrRead(pRdr, &u, sizeof(u.u32), K_OFFSETOF(IMAGE_DOS_HEADER, e_lfanew));
+ if (rc)
+ return rc;
+ if ((KLDRFOFF)u.u32 < kRdrSize(pRdr))
+ {
+ offHdr = u.u32;
+ rc = kRdrRead(pRdr, &u, sizeof(u.u32), offHdr);
+ if (rc)
+ return rc;
+ }
+ else
+ u.u16 = IMAGE_DOS_SIGNATURE;
+ }
+
+ /*
+ * Handle FAT images too here (one only).
+ */
+ if ( ( u.u32 == IMAGE_FAT_SIGNATURE
+ || u.u32 == IMAGE_FAT_SIGNATURE_OE)
+ && offHdr == 0)
+ {
+ rc = kldrModOpenFromRdrSelectImageFromFAT(pRdr, fFlags, enmCpuArch, u.u32, &offHdr);
+ if (rc)
+ return rc;
+ if (offHdr)
+ continue;
+ }
+ break;
+ }
+
+
+ /*
+ * Use the magic to select the appropriate image interpreter head on.
+ */
+ if (u.u16 == IMAGE_DOS_SIGNATURE)
+ rc = KLDR_ERR_MZ_NOT_SUPPORTED;
+ else if (u.u16 == IMAGE_NE_SIGNATURE)
+ rc = KLDR_ERR_NE_NOT_SUPPORTED;
+ else if (u.u16 == IMAGE_LX_SIGNATURE)
+ rc = g_kLdrModLXOps.pfnCreate(&g_kLdrModLXOps, pRdr, fFlags, enmCpuArch, offHdr, ppMod);
+ else if (u.u16 == IMAGE_LE_SIGNATURE)
+ rc = KLDR_ERR_LE_NOT_SUPPORTED;
+ else if (u.u32 == IMAGE_NT_SIGNATURE)
+ rc = g_kLdrModPEOps.pfnCreate(&g_kLdrModPEOps, pRdr, fFlags, enmCpuArch, offHdr, ppMod);
+ else if ( u.u32 == IMAGE_MACHO32_SIGNATURE
+ || u.u32 == IMAGE_MACHO32_SIGNATURE_OE
+ || u.u32 == IMAGE_MACHO64_SIGNATURE
+ || u.u32 == IMAGE_MACHO64_SIGNATURE_OE)
+ rc = g_kLdrModMachOOps.pfnCreate(&g_kLdrModMachOOps, pRdr, fFlags, enmCpuArch, offHdr, ppMod);
+ else if (u.u32 == IMAGE_ELF_SIGNATURE)
+ rc = KLDR_ERR_ELF_NOT_SUPPORTED;
+ else
+ rc = KLDR_ERR_UNKNOWN_FORMAT;
+
+ /*
+ * If no head on hit, let each interpreter have a go.
+ */
+ if (rc)
+ {
+ PCKLDRMODOPS pOps;
+ for (pOps = g_pModInterpreterHead; pOps; pOps = pOps->pNext)
+ {
+ int rc2 = pOps->pfnCreate(pOps, pRdr, fFlags, enmCpuArch, offHdr, ppMod);
+ if (!rc2)
+ return rc;
+ }
+ *ppMod = NULL;
+ }
+ return rc;
+}
+
+
+/**
+ * Closes an open module.
+ *
+ * The caller is responsible for calling kLdrModUnmap() and kLdrFreeTLS()
+ * before closing the module.
+ *
+ * @returns 0 on success, non-zero on failure. The module instance state
+ * is unknown on failure, it's best not to touch it.
+ * @param pMod The module.
+ */
+int kLdrModClose(PKLDRMOD pMod)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnDestroy(pMod);
+}
+
+
+/**
+ * Queries a symbol by name or ordinal number.
+ *
+ * @returns 0 and *puValue and *pfKind on success.
+ * KLDR_ERR_SYMBOL_NOT_FOUND is returned if the symbol wasn't found.
+ * Other failures could stem from bad executable format failures,
+ * read failure in case pvBits isn't specified and no mapping should be used.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param BaseAddress The module base address to use when calculating the symbol value.
+ * There are two special values that can be used:
+ * KLDRMOD_BASEADDRESS_LINK and KLDRMOD_BASEADDRESS_MAP.
+ * @param iSymbol The symbol ordinal. (optional)
+ * @param pchSymbol The symbol name. (optional)
+ * Important, this doesn't have to be a null-terminated string.
+ * @param cchSymbol The length of the symbol name.
+ * @param pszVersion The symbol version. NULL if not versioned.
+ * @param pfnGetForwarder The callback to use when resolving a forwarder symbol. This is optional
+ * and if not specified KLDR_ERR_FORWARDER is returned instead.
+ * @param pvUser The user argument for the pfnGetForwarder callback.
+ * @param puValue Where to store the symbol value. (optional)
+ * @param pfKind On input one of the KLDRSYMKIND_REQ_* #defines.
+ * On output the symbol kind. (optional)
+ */
+int kLdrModQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+ KLDRMOD_VALIDATE(pMod);
+ if (!puValue && !pfKind)
+ return KERR_INVALID_PARAMETER;
+ if (puValue)
+ *puValue = 0;
+ if (pfKind)
+ K_VALIDATE_FLAGS(*pfKind, KLDRSYMKIND_REQ_SEGMENTED);
+ return pMod->pOps->pfnQuerySymbol(pMod, pvBits, BaseAddress, iSymbol, pchSymbol, cchSymbol, pszVersion,
+ pfnGetForwarder, pvUser, puValue, pfKind);
+}
+
+
+/**
+ * Enumerate the symbols in the module.
+ *
+ * @returns 0 on success and non-zero a status code on failure.
+ * @param pMod The module which symbols should be enumerated.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param BaseAddress The module base address to use when calculating the symbol values.
+ * There are two special values that could be can:
+ * KLDRMOD_BASEADDRESS_LINK and KLDRMOD_BASEADDRESS_MAP.
+ * @param fFlags The enumeration flags. A combination of the KLDRMOD_ENUM_SYMS_FLAGS_* \#defines.
+ * @param pfnCallback The enumeration callback function.
+ * @param pvUser The user argument to the callback function.
+ */
+int kLdrModEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 fFlags,
+ PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ KLDRMOD_VALIDATE(pMod);
+ K_VALIDATE_FLAGS(fFlags, KLDRMOD_ENUM_SYMS_FLAGS_ALL);
+ return pMod->pOps->pfnEnumSymbols(pMod, pvBits, BaseAddress, fFlags, pfnCallback, pvUser);
+}
+
+
+/**
+ * Get the name of an import module by ordinal number.
+ *
+ * @returns 0 and name in pszName on success.
+ * On buffer overruns KERR_BUFFER_OVERFLOW will be returned.
+ * On other failures and appropriate error code is returned.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits().
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param iImport The import module ordinal number.
+ * @param pszName Where to store the name.
+ * @param cchName The size of the name buffer.
+ */
+int kLdrModGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnGetImport(pMod, pvBits, iImport, pszName, cchName);
+}
+
+
+/**
+ * Get the number of import modules.
+ *
+ * @returns The number of import modules. -1 if something really bad happens.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits().
+ * This can be used by some module interpreters to reduce memory consumption.
+ */
+KI32 kLdrModNumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnNumberOfImports(pMod, pvBits);
+}
+
+
+/**
+ * Checks if this module can be executed by the specified arch+cpu.
+ *
+ * @returns 0 if it can, KCPU_ERR_ARCH_CPU_NOT_COMPATIBLE if it can't.
+ * Other failures may occur and cause other return values.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits().
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param enmArch The CPU architecture.
+ * @param enmCpu The CPU series/model.
+ */
+int kLdrModCanExecuteOn(PKLDRMOD pMod, const void *pvBits, KCPUARCH enmArch, KCPU enmCpu)
+{
+ KLDRMOD_VALIDATE(pMod);
+ if (pMod->pOps->pfnCanExecuteOn)
+ return pMod->pOps->pfnCanExecuteOn(pMod, pvBits, enmArch, enmCpu);
+ return kCpuCompare(pMod->enmArch, pMod->enmCpu, enmArch, enmCpu);
+}
+
+
+/**
+ * Gets the image stack info.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pMod
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param BaseAddress The module base address to use when calculating the stack address.
+ * There are two special values that can be used:
+ * KLDRMOD_BASEADDRESS_LINK and KLDRMOD_BASEADDRESS_MAP.
+ * @param pStackInfo The stack information.
+ */
+int kLdrModGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnGetStackInfo(pMod, pvBits, BaseAddress, pStackInfo);
+}
+
+
+/**
+ * Queries the main entrypoint of the module.
+ *
+ * Only executable are supposed to have an main entrypoint, though some object and DLL
+ * formats will also allow this.
+ *
+ * @returns 0 and *pMainEPAddress on success. Non-zero status code on failure.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param BaseAddress The module base address to use when calculating the entrypoint address.
+ * There are two special values that can be used:
+ * KLDRMOD_BASEADDRESS_LINK and KLDRMOD_BASEADDRESS_MAP.
+ * @param pMainEPAddress Where to store the entry point address.
+ */
+int kLdrModQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+ KLDRMOD_VALIDATE(pMod);
+ *pMainEPAddress = 0;
+ return pMod->pOps->pfnQueryMainEntrypoint(pMod, pvBits, BaseAddress, pMainEPAddress);
+}
+
+
+/**
+ * Queries the image UUID, if the image has one.
+ *
+ * @returns 0 and *pvUuid. Non-zero status code on failure.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param pvUuid Where to store the UUID.
+ * @param cbUuid Size of the UUID buffer, must be at least 16 bytes.
+ */
+int kLdrModQueryImageUuid(PKLDRMOD pMod, const void *pvBits, void *pvUuid, KSIZE cbUuid)
+{
+ KLDRMOD_VALIDATE(pMod);
+ if (cbUuid < 16)
+ return KERR_INVALID_SIZE;
+ if (pMod->pOps->pfnQueryImageUuid)
+ return pMod->pOps->pfnQueryImageUuid(pMod, pvBits, pvUuid, cbUuid);
+ return KLDR_ERR_NO_IMAGE_UUID;
+}
+
+
+/**
+ * Queries info about a resource.
+ *
+ * If there are multiple resources matching the criteria, the best or
+ * first match will be return.
+ *
+ *
+ * @returns 0 on success.
+ * @returns Whatever non-zero status returned by pfnCallback (enumeration was stopped).
+ * @returns non-zero kLdr or native status code on failure.
+ *
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param BaseAddress The module base address to use when calculating the resource addresses.
+ * There are two special values that can be used:
+ * KLDRMOD_BASEADDRESS_LINK and KLDRMOD_BASEADDRESS_MAP.
+ * @param idType The resource type id to match if not NIL_KLDRMOD_RSRC_TYPE_ID.
+ * @param pszType The resource type name to match if no NULL.
+ * @param idName The resource name id to match if not NIL_KLDRMOD_RSRC_NAME_ID.
+ * @param pszName The resource name to match if not NULL.
+ * @param idLang The language id to match.
+ * @param pfnCallback The callback function.
+ * @param pvUser The user argument for the callback.
+ */
+int kLdrModQueryResource(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 idType, const char *pszType,
+ KU32 idName, const char *pszName, KU32 idLang, PKLDRADDR pAddrRsrc, KSIZE *pcbRsrc)
+{
+ KLDRMOD_VALIDATE(pMod);
+ if (!pAddrRsrc && !pcbRsrc)
+ return KERR_INVALID_PARAMETER;
+ if (pAddrRsrc)
+ *pAddrRsrc = NIL_KLDRADDR;
+ if (pcbRsrc)
+ *pcbRsrc = 0;
+ return pMod->pOps->pfnQueryResource(pMod, pvBits, BaseAddress, idType, pszType, idName, pszName, idLang, pAddrRsrc, pcbRsrc);
+}
+
+
+/**
+ * Enumerates the resources matching the specfied criteria.
+ *
+ *
+ * @returns 0 on success.
+ * @returns Whatever non-zero status returned by pfnCallback (enumeration was stopped).
+ * @returns non-zero kLdr or native status code on failure.
+ *
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits() currently located at BaseAddress.
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param BaseAddress The module base address to use when calculating the resource addresses.
+ * There are two special values that can be used:
+ * KLDRMOD_BASEADDRESS_LINK and KLDRMOD_BASEADDRESS_MAP.
+ * @param idType The resource type id to match if not NIL_KLDRMOD_RSRC_TYPE_ID.
+ * @param pszType The resource type name to match if no NULL.
+ * @param idName The resource name id to match if not NIL_KLDRMOD_RSRC_NAME_ID.
+ * @param pszName The resource name to match if not NULL.
+ * @param idLang The language id to match.
+ * @param pfnCallback The callback function.
+ * @param pvUser The user argument for the callback.
+ */
+int kLdrModEnumResources(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 idType, const char *pszType,
+ KU32 idName, const char *pszName, KU32 idLang, PFNKLDRENUMRSRC pfnCallback, void *pvUser)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnEnumResources(pMod, pvBits, BaseAddress, idType, pszType, idName, pszName, idLang, pfnCallback, pvUser);
+}
+
+
+/**
+ * Enumerate the debug info formats contained in the executable image.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure, or non-zero callback status.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits().
+ * This can be used by some module interpreters to reduce memory consumption.
+ * @param pfnCallback The callback function.
+ * @param pvUser The user argument.
+ * @see pg_kDbg for the debug info reader.
+ */
+int kLdrModEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnEnumDbgInfo(pMod, pvBits, pfnCallback, pvUser);
+}
+
+
+/**
+ * Checks if the module has debug info embedded or otherwise associated with it.
+ *
+ * @returns 0 if it has debug info, KLDR_ERR_NO_DEBUG_INFO if no debug info,
+ * and non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param pvBits Optional pointer to bits returned by kLdrModGetBits().
+ * This can be used by some module interpreters to reduce memory consumption.
+ */
+int kLdrModHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnHasDbgInfo(pMod, pvBits);
+}
+
+
+/**
+ * May free up some resources held by the module.
+ *
+ * @todo define exactly what it possible to do after this call.
+ *
+ * @returns 0 on success, KLDR_ERR_* on failure.
+ * @param pMod The module.
+ */
+int kLdrModMostlyDone(PKLDRMOD pMod)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnMostlyDone(pMod);
+}
+
+
+/**
+ * Maps the module into the memory of the caller.
+ *
+ * On success the actual addresses for the segments can be found in MapAddress
+ * member of each segment in the segment array.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pMod The module to be mapped.
+ * @remark kLdr only supports one mapping at a time of a module.
+ */
+int kLdrModMap(PKLDRMOD pMod)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnMap(pMod);
+}
+
+
+/**
+ * Unmaps a module previously mapped by kLdrModMap().
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pMod The module to unmap.
+ */
+int kLdrModUnmap(PKLDRMOD pMod)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnUnmap(pMod);
+}
+
+
+/**
+ * Reloads all dirty pages in a module previously mapped by kLdrModMap().
+ *
+ * The module interpreter may omit code pages if it can safely apply code
+ * fixups again in a subsequent kLdrModFixupMapping() call.
+ *
+ * The caller is responsible for freeing TLS before calling this function.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ */
+int kLdrModReload(PKLDRMOD pMod)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnReload(pMod);
+}
+
+
+/**
+ * Fixup the mapping made by kLdrModMap().
+ *
+ * The caller is only responsible for not calling this function more than
+ * once without doing kLDrModReload() inbetween.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param pfnGetImport The callback for resolving external (imported) symbols.
+ * @param pvUser The callback user argument.
+ */
+int kLdrModFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnFixupMapping(pMod, pfnGetImport, pvUser);
+}
+
+
+/**
+ * Get the size of the mapped module.
+ *
+ * @returns The size of the mapped module (in bytes).
+ * @param pMod The module.
+ */
+KLDRADDR kLdrModSize(PKLDRMOD pMod)
+{
+ KLDRMOD_VALIDATE_EX(pMod, 0);
+ return pMod->pOps->pfnSize(pMod);
+}
+
+
+/**
+ * Gets the module bits.
+ *
+ * The module interpreter will fill a mapping allocated by the caller with the
+ * module bits reallocated to the specified address.
+ *
+ * @returns 0 on succes, non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param pvBits Where to put the bits.
+ * @param BaseAddress The base address that should correspond to the first byte in pvBits
+ * upon return.
+ * @param pfnGetImport The callback ufor resolving external (imported) symbols.
+ * @param pvUser The callback user argument.
+ */
+int kLdrModGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnGetBits(pMod, pvBits, BaseAddress, pfnGetImport, pvUser);
+}
+
+
+/**
+ * Relocates the module bits previously obtained by kLdrModGetBits().
+ *
+ * @returns 0 on succes, non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param pvBits Where to put the bits.
+ * @param NewBaseAddress The new base address.
+ * @param OldBaseAddress The old base address (i.e. the one specified to kLdrModGetBits() or as
+ * NewBaseAddressto the previous kLdrModRelocateBits() call).
+ * @param pfnGetImport The callback ufor resolving external (imported) symbols.
+ * @param pvUser The callback user argument.
+ */
+int kLdrModRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnRelocateBits(pMod, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
+}
+
+
+/**
+ * Allocates Thread Local Storage for module mapped by kLdrModMap().
+ *
+ * Calling kLdrModAllocTLS() more than once without calling kLdrModFreeTLS()
+ * between each invocation is not supported.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param pvMapping The external mapping address or RTLDRMOD_INT_MAP.
+ */
+int kLdrModAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnAllocTLS(pMod, pvMapping);
+}
+
+
+/**
+ * Frees Thread Local Storage previously allocated by kLdrModAllocTLS().
+ *
+ * The caller is responsible for only calling kLdrModFreeTLS() once
+ * after calling kLdrModAllocTLS().
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param pvMapping The external mapping address or RTLDRMOD_INT_MAP.
+ */
+void kLdrModFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ KLDRMOD_VALIDATE_VOID(pMod);
+ pMod->pOps->pfnFreeTLS(pMod, pvMapping);
+}
+
+
+
+
+/**
+ * Call the module initializiation function of a mapped module (if any).
+ *
+ * @returns 0 on success or no init function, non-zero on init function failure or invalid pMod.
+ * @param pMod The module.
+ * @param pvMapping The external mapping address or RTLDRMOD_INT_MAP.
+ * @param uHandle The module handle to use if any of the init functions requires the module handle.
+ */
+int kLdrModCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnCallInit(pMod, pvMapping, uHandle);
+}
+
+
+/**
+ * Call the module termination function of a mapped module (if any).
+ *
+ * @returns 0 on success or no term function, non-zero on invalid pMod.
+ * @param pMod The module.
+ * @param pvMapping The external mapping address or RTLDRMOD_INT_MAP.
+ * @param uHandle The module handle to use if any of the term functions requires the module handle.
+ *
+ * @remark Termination function failure will be ignored by the module interpreter.
+ */
+int kLdrModCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ KLDRMOD_VALIDATE(pMod);
+ return pMod->pOps->pfnCallTerm(pMod, pvMapping, uHandle);
+}
+
+
+/**
+ * Call the thread attach or detach function of a mapped module (if any).
+ *
+ * Any per-thread TLS initialization/termination will have to be done at this time too.
+ *
+ * @returns 0 on success or no attach/detach function, non-zero on attach failure or invalid pMod.
+ * @param pMod The module.
+ * @param pvMapping The external mapping address or RTLDRMOD_INT_MAP.
+ * @param uHandle The module handle to use if any of the thread attach/detach functions
+ * requires the module handle.
+ *
+ * @remark Detach function failure will be ignored by the module interpreter.
+ */
+int kLdrModCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+ KLDRMOD_VALIDATE(pMod);
+ K_VALIDATE_FLAGS(fAttachingOrDetaching, 1);
+ return pMod->pOps->pfnCallThread(pMod, pvMapping, uHandle, fAttachingOrDetaching);
+}
+
diff --git a/src/lib/kStuff/kLdr/kLdrModLX.c b/src/lib/kStuff/kLdr/kLdrModLX.c
new file mode 100644
index 0000000..073b129
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrModLX.c
@@ -0,0 +1,2701 @@
+/* $Id: kLdrModLX.c 117 2020-03-15 15:23:36Z bird $ */
+/** @file
+ * kLdr - The Module Interpreter for the Linear eXecutable (LX) Format.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+#include <k/kLdrFmts/lx.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRMODLX_STRICT
+ * Define KLDRMODLX_STRICT to enabled strict checks in KLDRMODLX. */
+#define KLDRMODLX_STRICT 1
+
+/** @def KLDRMODLX_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMODLX_STRICT
+# define KLDRMODLX_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRMODLX_ASSERT(expr) do {} while (0)
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Instance data for the LX module interpreter.
+ */
+typedef struct KLDRMODLX
+{
+ /** Pointer to the module. (Follows the section table.) */
+ PKLDRMOD pMod;
+ /** Pointer to the user mapping. */
+ const void *pvMapping;
+ /** The size of the mapped LX image. */
+ KSIZE cbMapped;
+ /** Reserved flags. */
+ KU32 f32Reserved;
+
+ /** The offset of the LX header. */
+ KLDRFOFF offHdr;
+ /** Copy of the LX header. */
+ struct e32_exe Hdr;
+
+ /** Pointer to the loader section.
+ * Allocated together with this strcture. */
+ const KU8 *pbLoaderSection;
+ /** Pointer to the last byte in the loader section. */
+ const KU8 *pbLoaderSectionLast;
+ /** Pointer to the object table in the loader section. */
+ const struct o32_obj *paObjs;
+ /** Pointer to the object page map table in the loader section. */
+ const struct o32_map *paPageMappings;
+ /** Pointer to the resource table in the loader section. */
+ const struct rsrc32 *paRsrcs;
+ /** Pointer to the resident name table in the loader section. */
+ const KU8 *pbResNameTab;
+ /** Pointer to the entry table in the loader section. */
+ const KU8 *pbEntryTab;
+
+ /** Pointer to the non-resident name table. */
+ KU8 *pbNonResNameTab;
+ /** Pointer to the last byte in the non-resident name table. */
+ const KU8 *pbNonResNameTabLast;
+
+ /** Pointer to the fixup section. */
+ KU8 *pbFixupSection;
+ /** Pointer to the last byte in the fixup section. */
+ const KU8 *pbFixupSectionLast;
+ /** Pointer to the fixup page table within pvFixupSection. */
+ const KU32 *paoffPageFixups;
+ /** Pointer to the fixup record table within pvFixupSection. */
+ const KU8 *pbFixupRecs;
+ /** Pointer to the import module name table within pvFixupSection. */
+ const KU8 *pbImportMods;
+ /** Pointer to the import module name table within pvFixupSection. */
+ const KU8 *pbImportProcs;
+} KLDRMODLX, *PKLDRMODLX;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static int kldrModLXHasDbgInfo(PKLDRMOD pMod, const void *pvBits);
+static int kldrModLXRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int kldrModLXDoCreate(PKRDR pRdr, KLDRFOFF offNewHdr, PKLDRMODLX *ppModLX);
+static const KU8 *kldrModLXDoNameTableLookupByOrdinal(const KU8 *pbNameTable, KSSIZE cbNameTable, KU32 iOrdinal);
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pchSymbol, KSIZE cchSymbol, KU32 *piSymbol);
+static const KU8 *kldrModLXDoNameTableLookupByName(const KU8 *pbNameTable, KSSIZE cbNameTable,
+ const char *pchSymbol, KSIZE cchSymbol);
+static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits);
+static int kldrModLXDoIterDataUnpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc);
+static int kldrModLXDoIterData2Unpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc);
+static void kLdrModLXMemCopyW(KU8 *pbDst, const KU8 *pbSrc, int cb);
+static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect);
+static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, void *pvMapping, unsigned uOp, KUPTR uHandle);
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind);
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX);
+static int kldrModLXDoReloc(KU8 *pbPage, int off, KLDRADDR PageAddress, const struct r32_rlc *prlc,
+ int iSelector, KLDRADDR uValue, KU32 fKind);
+
+
+/**
+ * Create a loader module instance interpreting the executable image found
+ * in the specified file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ * On failure, a non-zero OS specific error code is returned.
+ * @param pOps Pointer to the registered method table.
+ * @param pRdr The file provider instance to use.
+ * @param fFlags Flags, MBZ.
+ * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means
+ * anything goes, but with a preference for the current
+ * host architecture.
+ * @param offNewHdr The offset of the new header in MZ files. -1 if not found.
+ * @param ppMod Where to store the module instance pointer.
+ */
+static int kldrModLXCreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod)
+{
+ PKLDRMODLX pModLX;
+ int rc;
+ K_NOREF(fFlags);
+
+ /*
+ * Create the instance data and do a minimal header validation.
+ */
+ rc = kldrModLXDoCreate(pRdr, offNewHdr, &pModLX);
+ if (!rc)
+ {
+ /*
+ * Match up against the requested CPU architecture.
+ */
+ if ( enmCpuArch == KCPUARCH_UNKNOWN
+ || pModLX->pMod->enmArch == enmCpuArch)
+ {
+ pModLX->pMod->pOps = pOps;
+ pModLX->pMod->u32Magic = KLDRMOD_MAGIC;
+ *ppMod = pModLX->pMod;
+ return 0;
+ }
+ rc = KLDR_ERR_CPU_ARCH_MISMATCH;
+ }
+ kHlpFree(pModLX);
+ return rc;
+}
+
+
+/**
+ * Separate function for reading creating the LX module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModLXDoCreate(PKRDR pRdr, KLDRFOFF offNewHdr, PKLDRMODLX *ppModLX)
+{
+ struct e32_exe Hdr;
+ PKLDRMODLX pModLX;
+ PKLDRMOD pMod;
+ KSIZE cb;
+ KSIZE cchFilename;
+ KSIZE offLdrStuff;
+ KU32 off, offEnd;
+ KU32 i;
+ int rc;
+ int fCanOptimizeMapping;
+ KU32 NextRVA;
+ *ppModLX = NULL;
+
+ /*
+ * Read the signature and file header.
+ */
+ rc = kRdrRead(pRdr, &Hdr, sizeof(Hdr), offNewHdr > 0 ? offNewHdr : 0);
+ if (rc)
+ return rc;
+ if ( Hdr.e32_magic[0] != E32MAGIC1
+ || Hdr.e32_magic[1] != E32MAGIC2)
+ return KLDR_ERR_UNKNOWN_FORMAT;
+
+ /* We're not interested in anything but x86 images. */
+ if ( Hdr.e32_level != E32LEVEL
+ || Hdr.e32_border != E32LEBO
+ || Hdr.e32_worder != E32LEWO
+ || Hdr.e32_cpu < E32CPU286
+ || Hdr.e32_cpu > E32CPU486
+ || Hdr.e32_pagesize != OBJPAGELEN
+ )
+ return KLDR_ERR_LX_BAD_HEADER;
+
+ /* Some rough sanity checks. */
+ offEnd = kRdrSize(pRdr) >= (KLDRFOFF)~(KU32)16 ? ~(KU32)16 : (KU32)kRdrSize(pRdr);
+ if ( Hdr.e32_itermap > offEnd
+ || Hdr.e32_datapage > offEnd
+ || Hdr.e32_nrestab > offEnd
+ || Hdr.e32_nrestab + Hdr.e32_cbnrestab > offEnd
+ || Hdr.e32_ldrsize > offEnd - offNewHdr - sizeof(Hdr)
+ || Hdr.e32_fixupsize > offEnd - offNewHdr - sizeof(Hdr)
+ || Hdr.e32_fixupsize + Hdr.e32_ldrsize > offEnd - offNewHdr - sizeof(Hdr))
+ return KLDR_ERR_LX_BAD_HEADER;
+
+ /* Verify the loader section. */
+ offEnd = Hdr.e32_objtab + Hdr.e32_ldrsize;
+ if (Hdr.e32_objtab < sizeof(Hdr))
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+ off = Hdr.e32_objtab + sizeof(struct o32_obj) * Hdr.e32_objcnt;
+ if (off > offEnd)
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_objmap
+ && (Hdr.e32_objmap < off || Hdr.e32_objmap > offEnd))
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_rsrccnt
+ && ( Hdr.e32_rsrctab < off
+ || Hdr.e32_rsrctab > offEnd
+ || Hdr.e32_rsrctab + sizeof(struct rsrc32) * Hdr.e32_rsrccnt > offEnd))
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_restab
+ && (Hdr.e32_restab < off || Hdr.e32_restab > offEnd - 2))
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_enttab
+ && (Hdr.e32_enttab < off || Hdr.e32_enttab >= offEnd))
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_dircnt
+ && (Hdr.e32_dirtab < off || Hdr.e32_dirtab > offEnd - 2))
+ return KLDR_ERR_LX_BAD_LOADER_SECTION;
+
+ /* Verify the fixup section. */
+ off = offEnd;
+ offEnd = off + Hdr.e32_fixupsize;
+ if ( Hdr.e32_fpagetab
+ && (Hdr.e32_fpagetab < off || Hdr.e32_fpagetab > offEnd))
+ {
+ /*
+ * wlink mixes the fixup section and the loader section.
+ */
+ off = Hdr.e32_fpagetab;
+ offEnd = off + Hdr.e32_fixupsize;
+ Hdr.e32_ldrsize = off - Hdr.e32_objtab;
+ }
+ if ( Hdr.e32_frectab
+ && (Hdr.e32_frectab < off || Hdr.e32_frectab > offEnd))
+ return KLDR_ERR_LX_BAD_FIXUP_SECTION;
+ if ( Hdr.e32_impmod
+ && (Hdr.e32_impmod < off || Hdr.e32_impmod > offEnd || Hdr.e32_impmod + Hdr.e32_impmodcnt > offEnd))
+ return KLDR_ERR_LX_BAD_FIXUP_SECTION;
+ if ( Hdr.e32_impproc
+ && (Hdr.e32_impproc < off || Hdr.e32_impproc > offEnd))
+ return KLDR_ERR_LX_BAD_FIXUP_SECTION;
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ cchFilename = kHlpStrLen(kRdrName(pRdr));
+ cb = K_ALIGN_Z(sizeof(KLDRMODLX), 8)
+ + K_ALIGN_Z(K_OFFSETOF(KLDRMOD, aSegments[Hdr.e32_objcnt + 1]), 8)
+ + K_ALIGN_Z(cchFilename + 1, 8);
+ offLdrStuff = cb;
+ cb += Hdr.e32_ldrsize + 2; /* +2 for two extra zeros. */
+ pModLX = (PKLDRMODLX)kHlpAlloc(cb);
+ if (!pModLX)
+ return KERR_NO_MEMORY;
+ *ppModLX = pModLX;
+
+ /* KLDRMOD */
+ pMod = (PKLDRMOD)((KU8 *)pModLX + K_ALIGN_Z(sizeof(KLDRMODLX), 8));
+ pMod->pvData = pModLX;
+ pMod->pRdr = pRdr;
+ pMod->pOps = NULL; /* set upon success. */
+ pMod->cSegments = Hdr.e32_objcnt;
+ pMod->cchFilename = (KU32)cchFilename;
+ pMod->pszFilename = (char *)K_ALIGN_P(&pMod->aSegments[pMod->cSegments], 8);
+ kHlpMemCopy((char *)pMod->pszFilename, kRdrName(pRdr), cchFilename + 1);
+ pMod->pszName = NULL; /* finalized further down */
+ pMod->cchName = 0;
+ pMod->fFlags = 0;
+ switch (Hdr.e32_cpu)
+ {
+ case E32CPU286:
+ pMod->enmCpu = KCPU_I80286;
+ pMod->enmArch = KCPUARCH_X86_16;
+ break;
+ case E32CPU386:
+ pMod->enmCpu = KCPU_I386;
+ pMod->enmArch = KCPUARCH_X86_32;
+ break;
+ case E32CPU486:
+ pMod->enmCpu = KCPU_I486;
+ pMod->enmArch = KCPUARCH_X86_32;
+ break;
+ }
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+ pMod->enmFmt = KLDRFMT_LX;
+ switch (Hdr.e32_mflags & E32MODMASK)
+ {
+ case E32MODEXE:
+ pMod->enmType = !(Hdr.e32_mflags & E32NOINTFIX)
+ ? KLDRTYPE_EXECUTABLE_RELOCATABLE
+ : KLDRTYPE_EXECUTABLE_FIXED;
+ break;
+
+ case E32MODDLL:
+ case E32PROTDLL:
+ case E32MODPROTDLL:
+ pMod->enmType = !(Hdr.e32_mflags & E32SYSDLL)
+ ? KLDRTYPE_SHARED_LIBRARY_RELOCATABLE
+ : KLDRTYPE_SHARED_LIBRARY_FIXED;
+ break;
+
+ case E32MODPDEV:
+ case E32MODVDEV:
+ pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
+ break;
+ }
+ pMod->u32Magic = 0; /* set upon success. */
+
+ /* KLDRMODLX */
+ pModLX->pMod = pMod;
+ pModLX->pvMapping = 0;
+ pModLX->cbMapped = 0;
+ pModLX->f32Reserved = 0;
+
+ pModLX->offHdr = offNewHdr >= 0 ? offNewHdr : 0;
+ kHlpMemCopy(&pModLX->Hdr, &Hdr, sizeof(Hdr));
+
+ pModLX->pbLoaderSection = (KU8 *)pModLX + offLdrStuff;
+ pModLX->pbLoaderSectionLast = pModLX->pbLoaderSection + pModLX->Hdr.e32_ldrsize - 1;
+ pModLX->paObjs = NULL;
+ pModLX->paPageMappings = NULL;
+ pModLX->paRsrcs = NULL;
+ pModLX->pbResNameTab = NULL;
+ pModLX->pbEntryTab = NULL;
+
+ pModLX->pbNonResNameTab = NULL;
+ pModLX->pbNonResNameTabLast = NULL;
+
+ pModLX->pbFixupSection = NULL;
+ pModLX->pbFixupSectionLast = NULL;
+ pModLX->paoffPageFixups = NULL;
+ pModLX->pbFixupRecs = NULL;
+ pModLX->pbImportMods = NULL;
+ pModLX->pbImportProcs = NULL;
+
+ /*
+ * Read the loader data.
+ */
+ rc = kRdrRead(pRdr, (void *)pModLX->pbLoaderSection, pModLX->Hdr.e32_ldrsize, pModLX->Hdr.e32_objtab + pModLX->offHdr);
+ if (rc)
+ return rc;
+ ((KU8 *)pModLX->pbLoaderSectionLast)[1] = 0;
+ ((KU8 *)pModLX->pbLoaderSectionLast)[2] = 0;
+ if (pModLX->Hdr.e32_objcnt)
+ pModLX->paObjs = (const struct o32_obj *)pModLX->pbLoaderSection;
+ if (pModLX->Hdr.e32_objmap)
+ pModLX->paPageMappings = (const struct o32_map *)(pModLX->pbLoaderSection + pModLX->Hdr.e32_objmap - pModLX->Hdr.e32_objtab);
+ if (pModLX->Hdr.e32_rsrccnt)
+ pModLX->paRsrcs = (const struct rsrc32 *)(pModLX->pbLoaderSection + pModLX->Hdr.e32_rsrctab - pModLX->Hdr.e32_objtab);
+ if (pModLX->Hdr.e32_restab)
+ pModLX->pbResNameTab = pModLX->pbLoaderSection + pModLX->Hdr.e32_restab - pModLX->Hdr.e32_objtab;
+ if (pModLX->Hdr.e32_enttab)
+ pModLX->pbEntryTab = pModLX->pbLoaderSection + pModLX->Hdr.e32_enttab - pModLX->Hdr.e32_objtab;
+
+ /*
+ * Get the soname from the resident name table.
+ * Very convenient that it's the 0 ordinal, because then we get a
+ * free string terminator.
+ * (The table entry consists of a pascal string followed by a 16-bit ordinal.)
+ */
+ if (pModLX->pbResNameTab)
+ pMod->pszName = (const char *)kldrModLXDoNameTableLookupByOrdinal(pModLX->pbResNameTab,
+ pModLX->pbLoaderSectionLast - pModLX->pbResNameTab + 1,
+ 0);
+ if (!pMod->pszName)
+ return KLDR_ERR_LX_NO_SONAME;
+ pMod->cchName = *(const KU8 *)pMod->pszName++;
+ if (pMod->cchName != kHlpStrLen(pMod->pszName))
+ return KLDR_ERR_LX_BAD_SONAME;
+
+ /*
+ * Quick validation of the object table.
+ */
+ cb = 0;
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ if (pModLX->paObjs[i].o32_base & (OBJPAGELEN - 1))
+ return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+ if (pModLX->paObjs[i].o32_base + pModLX->paObjs[i].o32_size <= pModLX->paObjs[i].o32_base)
+ return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+ if (pModLX->paObjs[i].o32_mapsize > (pModLX->paObjs[i].o32_size + (OBJPAGELEN - 1)))
+ return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+ if ( pModLX->paObjs[i].o32_mapsize
+ && ( (KU8 *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap] > pModLX->pbLoaderSectionLast
+ || (KU8 *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap + pModLX->paObjs[i].o32_mapsize]
+ > pModLX->pbLoaderSectionLast))
+ return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+ if (i > 0 && !(pModLX->paObjs[i].o32_flags & OBJRSRC))
+ {
+ if (pModLX->paObjs[i].o32_base <= pModLX->paObjs[i - 1].o32_base)
+ return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+ if (pModLX->paObjs[i].o32_base < pModLX->paObjs[i - 1].o32_base + pModLX->paObjs[i - 1].o32_mapsize)
+ return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+ }
+ }
+
+ /*
+ * Check if we can optimize the mapping by using a different
+ * object alignment. The linker typically uses 64KB alignment,
+ * we can easily get away with page alignment in most cases.
+ */
+ fCanOptimizeMapping = !(Hdr.e32_mflags & (E32NOINTFIX | E32SYSDLL));
+ NextRVA = 0;
+
+ /*
+ * Setup the KLDRMOD segment array.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ /* unused */
+ pMod->aSegments[i].pvUser = NULL;
+ pMod->aSegments[i].MapAddress = 0;
+ pMod->aSegments[i].pchName = NULL;
+ pMod->aSegments[i].cchName = 0;
+ pMod->aSegments[i].offFile = -1;
+ pMod->aSegments[i].cbFile = -1;
+ pMod->aSegments[i].SelFlat = 0;
+ pMod->aSegments[i].Sel16bit = 0;
+
+ /* flags */
+ pMod->aSegments[i].fFlags = 0;
+ if (pModLX->paObjs[i].o32_flags & OBJBIGDEF)
+ pMod->aSegments[i].fFlags = KLDRSEG_FLAG_16BIT;
+ if (pModLX->paObjs[i].o32_flags & OBJALIAS16)
+ pMod->aSegments[i].fFlags = KLDRSEG_FLAG_OS2_ALIAS16;
+ if (pModLX->paObjs[i].o32_flags & OBJCONFORM)
+ pMod->aSegments[i].fFlags = KLDRSEG_FLAG_OS2_CONFORM;
+ if (pModLX->paObjs[i].o32_flags & OBJIOPL)
+ pMod->aSegments[i].fFlags = KLDRSEG_FLAG_OS2_IOPL;
+
+ /* size and addresses */
+ pMod->aSegments[i].Alignment = OBJPAGELEN;
+ pMod->aSegments[i].cb = pModLX->paObjs[i].o32_size;
+ pMod->aSegments[i].LinkAddress = pModLX->paObjs[i].o32_base;
+ pMod->aSegments[i].RVA = NextRVA;
+ if ( fCanOptimizeMapping
+ || i + 1 >= pMod->cSegments
+ || (pModLX->paObjs[i].o32_flags & OBJRSRC)
+ || (pModLX->paObjs[i + 1].o32_flags & OBJRSRC))
+ pMod->aSegments[i].cbMapped = K_ALIGN_Z(pModLX->paObjs[i].o32_size, OBJPAGELEN);
+ else
+ pMod->aSegments[i].cbMapped = pModLX->paObjs[i + 1].o32_base - pModLX->paObjs[i].o32_base;
+ NextRVA += (KU32)pMod->aSegments[i].cbMapped;
+
+ /* protection */
+ switch ( pModLX->paObjs[i].o32_flags
+ & (OBJSHARED | OBJREAD | OBJWRITE | OBJEXEC))
+ {
+ case 0:
+ case OBJSHARED:
+ pMod->aSegments[i].enmProt = KPROT_NOACCESS;
+ break;
+ case OBJREAD:
+ case OBJREAD | OBJSHARED:
+ pMod->aSegments[i].enmProt = KPROT_READONLY;
+ break;
+ case OBJWRITE:
+ case OBJWRITE | OBJREAD:
+ pMod->aSegments[i].enmProt = KPROT_WRITECOPY;
+ break;
+ case OBJWRITE | OBJSHARED:
+ case OBJWRITE | OBJSHARED | OBJREAD:
+ pMod->aSegments[i].enmProt = KPROT_READWRITE;
+ break;
+ case OBJEXEC:
+ case OBJEXEC | OBJSHARED:
+ pMod->aSegments[i].enmProt = KPROT_EXECUTE;
+ break;
+ case OBJEXEC | OBJREAD:
+ case OBJEXEC | OBJREAD | OBJSHARED:
+ pMod->aSegments[i].enmProt = KPROT_EXECUTE_READ;
+ break;
+ case OBJEXEC | OBJWRITE:
+ case OBJEXEC | OBJWRITE | OBJREAD:
+ pMod->aSegments[i].enmProt = KPROT_EXECUTE_WRITECOPY;
+ break;
+ case OBJEXEC | OBJWRITE | OBJSHARED:
+ case OBJEXEC | OBJWRITE | OBJSHARED | OBJREAD:
+ pMod->aSegments[i].enmProt = KPROT_EXECUTE_READWRITE;
+ break;
+ }
+ if ((pModLX->paObjs[i].o32_flags & (OBJREAD | OBJWRITE | OBJEXEC | OBJRSRC)) == OBJRSRC)
+ pMod->aSegments[i].enmProt = KPROT_READONLY;
+ /*pMod->aSegments[i].f16bit = !(pModLX->paObjs[i].o32_flags & OBJBIGDEF)
+ pMod->aSegments[i].fIOPL = !(pModLX->paObjs[i].o32_flags & OBJIOPL)
+ pMod->aSegments[i].fConforming = !(pModLX->paObjs[i].o32_flags & OBJCONFORM) */
+ }
+
+ /* set the mapping size */
+ pModLX->cbMapped = NextRVA;
+
+ /*
+ * We're done.
+ */
+ *ppModLX = pModLX;
+ return 0;
+}
+
+
+/** @copydoc KLDRMODOPS::pfnDestroy */
+static int kldrModLXDestroy(PKLDRMOD pMod)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ int rc = 0;
+ KLDRMODLX_ASSERT(!pModLX->pvMapping);
+
+ if (pMod->pRdr)
+ {
+ rc = kRdrClose(pMod->pRdr);
+ pMod->pRdr = NULL;
+ }
+ if (pModLX->pbNonResNameTab)
+ {
+ kHlpFree(pModLX->pbNonResNameTab);
+ pModLX->pbNonResNameTab = NULL;
+ }
+ if (pModLX->pbFixupSection)
+ {
+ kHlpFree(pModLX->pbFixupSection);
+ pModLX->pbFixupSection = NULL;
+ }
+ pMod->u32Magic = 0;
+ pMod->pOps = NULL;
+ kHlpFree(pModLX);
+ return rc;
+}
+
+
+/**
+ * Resolved base address aliases.
+ *
+ * @param pModLX The interpreter module instance
+ * @param pBaseAddress The base address, IN & OUT.
+ */
+static void kldrModLXResolveBaseAddress(PKLDRMODLX pModLX, PKLDRADDR pBaseAddress)
+{
+ if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP)
+ *pBaseAddress = pModLX->pMod->aSegments[0].MapAddress;
+ else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK)
+ *pBaseAddress = pModLX->pMod->aSegments[0].LinkAddress;
+}
+
+
+/** @copydoc kLdrModQuerySymbol */
+static int kldrModLXQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ KU32 iOrdinal;
+ int rc;
+ const struct b32_bundle *pBundle;
+ K_NOREF(pvBits);
+ K_NOREF(pszVersion);
+
+ /*
+ * Give up at once if there is no entry table.
+ */
+ if (!pModLX->Hdr.e32_enttab)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ /*
+ * Translate the symbol name into an ordinal.
+ */
+ if (pchSymbol)
+ {
+ rc = kldrModLXDoNameLookup(pModLX, pchSymbol, cchSymbol, &iSymbol);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Iterate the entry table.
+ * (The entry table is made up of bundles of similar exports.)
+ */
+ iOrdinal = 1;
+ pBundle = (const struct b32_bundle *)pModLX->pbEntryTab;
+ while (pBundle->b32_cnt && iOrdinal <= iSymbol)
+ {
+ static const KSIZE s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+ /*
+ * Check for a hit first.
+ */
+ iOrdinal += pBundle->b32_cnt;
+ if (iSymbol < iOrdinal)
+ {
+ KU32 offObject;
+ const struct e32_entry *pEntry = (const struct e32_entry *)((KUPTR)(pBundle + 1)
+ + (iSymbol - (iOrdinal - pBundle->b32_cnt))
+ * s_cbEntry[pBundle->b32_type]);
+
+ /*
+ * Calculate the return address.
+ */
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+ switch (pBundle->b32_type)
+ {
+ /* empty bundles are place holders unused ordinal ranges. */
+ case EMPTY:
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ /* e32_flags + a 16-bit offset. */
+ case ENTRY16:
+ offObject = pEntry->e32_variant.e32_offset.offset16;
+ if (pfKind)
+ *pfKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_NO_TYPE;
+ break;
+
+ /* e32_flags + a 16-bit offset + a 16-bit callgate selector. */
+ case GATE16:
+ offObject = pEntry->e32_variant.e32_callgate.offset;
+ if (pfKind)
+ *pfKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_CODE;
+ break;
+
+ /* e32_flags + a 32-bit offset. */
+ case ENTRY32:
+ offObject = pEntry->e32_variant.e32_offset.offset32;
+ if (pfKind)
+ *pfKind = KLDRSYMKIND_32BIT;
+ break;
+
+ /* e32_flags + 16-bit import module ordinal + a 32-bit procname or ordinal. */
+ case ENTRYFWD:
+ return kldrModLXDoForwarderQuery(pModLX, pEntry, pfnGetForwarder, pvUser, puValue, pfKind);
+
+ default:
+ /* anyone actually using TYPEINFO will end up here. */
+ KLDRMODLX_ASSERT(!"Bad bundle type");
+ return KLDR_ERR_LX_BAD_BUNDLE;
+ }
+
+ /*
+ * Validate the object number and calc the return address.
+ */
+ if ( pBundle->b32_obj <= 0
+ || pBundle->b32_obj > pMod->cSegments)
+ return KLDR_ERR_LX_BAD_BUNDLE;
+ if (puValue)
+ *puValue = BaseAddress
+ + offObject
+ + pMod->aSegments[pBundle->b32_obj - 1].RVA;
+ return 0;
+ }
+
+ /*
+ * Skip the bundle.
+ */
+ if (pBundle->b32_type > ENTRYFWD)
+ {
+ KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+ return KLDR_ERR_LX_BAD_BUNDLE;
+ }
+ if (pBundle->b32_type == 0)
+ pBundle = (const struct b32_bundle *)((const KU8 *)pBundle + 2);
+ else
+ pBundle = (const struct b32_bundle *)((const KU8 *)(pBundle + 1) + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+ }
+
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * Do name lookup.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModLX The module to lookup the symbol in.
+ * @param pchSymbol The symbol to lookup.
+ * @param cchSymbol The symbol name length.
+ * @param piSymbol Where to store the symbol ordinal.
+ */
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pchSymbol, KSIZE cchSymbol, KU32 *piSymbol)
+{
+
+ /*
+ * First do a hash table lookup.
+ */
+ /** @todo hash name table for speed. */
+
+ /*
+ * Search the name tables.
+ */
+ const KU8 *pbName = kldrModLXDoNameTableLookupByName(pModLX->pbResNameTab,
+ pModLX->pbLoaderSectionLast - pModLX->pbResNameTab + 1,
+ pchSymbol, cchSymbol);
+ if (!pbName)
+ {
+ if (!pModLX->pbNonResNameTab)
+ {
+ /* lazy load it */
+ /** @todo non-resident name table. */
+ }
+ if (pModLX->pbNonResNameTab)
+ pbName = kldrModLXDoNameTableLookupByName(pModLX->pbResNameTab,
+ pModLX->pbNonResNameTabLast - pModLX->pbResNameTab + 1,
+ pchSymbol, cchSymbol);
+ }
+ if (!pbName)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ *piSymbol = *(const KU16 *)(pbName + 1 + *pbName);
+ return 0;
+}
+
+
+#if 0
+/**
+ * Hash a symbol using the algorithm from sdbm.
+ *
+ * The following was is the documenation of the orignal sdbm functions:
+ *
+ * This algorithm was created for sdbm (a public-domain reimplementation of
+ * ndbm) database library. it was found to do well in scrambling bits,
+ * causing better distribution of the keys and fewer splits. it also happens
+ * to be a good general hashing function with good distribution. the actual
+ * function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
+ * is the faster version used in gawk. [there is even a faster, duff-device
+ * version] the magic constant 65599 was picked out of thin air while
+ * experimenting with different constants, and turns out to be a prime.
+ * this is one of the algorithms used in berkeley db (see sleepycat) and
+ * elsewhere.
+ */
+static KU32 kldrModLXDoHash(const char *pchSymbol, KU8 cchSymbol)
+{
+ KU32 hash = 0;
+ int ch;
+
+ while ( cchSymbol-- > 0
+ && (ch = *(unsigned const char *)pchSymbol++))
+ hash = ch + (hash << 6) + (hash << 16) - hash;
+
+ return hash;
+}
+#endif
+
+
+/**
+ * Lookup a name table entry by name.
+ *
+ * @returns Pointer to the name table entry if found.
+ * @returns NULL if not found.
+ * @param pbNameTable Pointer to the name table that should be searched.
+ * @param cbNameTable The size of the name table.
+ * @param pchSymbol The name of the symbol we're looking for.
+ * @param cchSymbol The length of the symbol name.
+ */
+static const KU8 *kldrModLXDoNameTableLookupByName(const KU8 *pbNameTable, KSSIZE cbNameTable,
+ const char *pchSymbol, KSIZE cchSymbol)
+{
+ /*
+ * Determin the namelength up front so we can skip anything which doesn't matches the length.
+ */
+ KU8 cbSymbol8Bit = (KU8)cchSymbol;
+ if (cbSymbol8Bit != cchSymbol)
+ return NULL; /* too long. */
+
+ /*
+ * Walk the name table.
+ */
+ while (*pbNameTable != 0 && cbNameTable > 0)
+ {
+ const KU8 cbName = *pbNameTable;
+
+ cbNameTable -= cbName + 1 + 2;
+ if (cbNameTable < 0)
+ break;
+
+ if ( cbName == cbSymbol8Bit
+ && !kHlpMemComp(pbNameTable + 1, pchSymbol, cbName))
+ return pbNameTable;
+
+ /* next entry */
+ pbNameTable += cbName + 1 + 2;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Deal with a forwarder entry.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModLX The PE module interpreter instance.
+ * @param pEntry The forwarder entry.
+ * @param pfnGetForwarder The callback for resolving forwarder symbols. (optional)
+ * @param pvUser The user argument for the callback.
+ * @param puValue Where to put the value. (optional)
+ * @param pfKind Where to put the symbol kind. (optional)
+ */
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+ int rc;
+ KU32 iSymbol;
+ const char *pchSymbol;
+ KU8 cchSymbol;
+
+ if (!pfnGetForwarder)
+ return KLDR_ERR_FORWARDER_SYMBOL;
+
+ /*
+ * Validate the entry import module ordinal.
+ */
+ if ( !pEntry->e32_variant.e32_fwd.modord
+ || pEntry->e32_variant.e32_fwd.modord > pModLX->Hdr.e32_impmodcnt)
+ return KLDR_ERR_LX_BAD_FORWARDER;
+
+ /*
+ * Figure out the parameters.
+ */
+ if (pEntry->e32_flags & FWD_ORDINAL)
+ {
+ iSymbol = pEntry->e32_variant.e32_fwd.value;
+ pchSymbol = NULL; /* no symbol name. */
+ cchSymbol = 0;
+ }
+ else
+ {
+ const KU8 *pbName;
+
+ /* load the fixup section if necessary. */
+ if (!pModLX->pbImportProcs)
+ {
+ rc = kldrModLXDoLoadFixupSection(pModLX);
+ if (rc)
+ return rc;
+ }
+
+ /* Make name pointer. */
+ pbName = pModLX->pbImportProcs + pEntry->e32_variant.e32_fwd.value;
+ if ( pbName >= pModLX->pbFixupSectionLast
+ || pbName < pModLX->pbFixupSection
+ || !*pbName)
+ return KLDR_ERR_LX_BAD_FORWARDER;
+
+
+ /* check for '#' name. */
+ if (pbName[1] == '#')
+ {
+ KU8 cbLeft = *pbName;
+ const KU8 *pb = pbName + 1;
+ unsigned uBase;
+
+ /* base detection */
+ uBase = 10;
+ if ( cbLeft > 1
+ && pb[1] == '0'
+ && (pb[2] == 'x' || pb[2] == 'X'))
+ {
+ uBase = 16;
+ pb += 2;
+ cbLeft -= 2;
+ }
+
+ /* ascii to integer */
+ iSymbol = 0;
+ while (cbLeft-- > 0)
+ {
+ /* convert char to digit. */
+ unsigned uDigit = *pb++;
+ if (uDigit >= '0' && uDigit <= '9')
+ uDigit -= '0';
+ else if (uDigit >= 'a' && uDigit <= 'z')
+ uDigit -= 'a' + 10;
+ else if (uDigit >= 'A' && uDigit <= 'Z')
+ uDigit -= 'A' + 10;
+ else if (!uDigit)
+ break;
+ else
+ return KLDR_ERR_LX_BAD_FORWARDER;
+ if (uDigit >= uBase)
+ return KLDR_ERR_LX_BAD_FORWARDER;
+
+ /* insert the digit */
+ iSymbol *= uBase;
+ iSymbol += uDigit;
+ }
+ if (!iSymbol)
+ return KLDR_ERR_LX_BAD_FORWARDER;
+
+ pchSymbol = NULL; /* no symbol name. */
+ cchSymbol = 0;
+ }
+ else
+ {
+ pchSymbol = (char *)pbName + 1;
+ cchSymbol = *pbName;
+ iSymbol = NIL_KLDRMOD_SYM_ORDINAL;
+ }
+ }
+
+ /*
+ * Resolve the forwarder.
+ */
+ rc = pfnGetForwarder(pModLX->pMod, pEntry->e32_variant.e32_fwd.modord - 1, iSymbol, pchSymbol, cchSymbol, NULL, puValue, pfKind, pvUser);
+ if (!rc && pfKind)
+ *pfKind |= KLDRSYMKIND_FORWARDER;
+ return rc;
+}
+
+
+/**
+ * Loads the fixup section from the executable image.
+ *
+ * The fixup section isn't loaded until it's accessed. It's also freed by kLdrModDone().
+ *
+ * @returns 0 on success, non-zero kLdr or native status code on failure.
+ * @param pModLX The PE module interpreter instance.
+ */
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX)
+{
+ int rc;
+ KU32 off;
+ void *pv;
+
+ pv = kHlpAlloc(pModLX->Hdr.e32_fixupsize);
+ if (!pv)
+ return KERR_NO_MEMORY;
+
+ off = pModLX->Hdr.e32_objtab + pModLX->Hdr.e32_ldrsize;
+ rc = kRdrRead(pModLX->pMod->pRdr, pv, pModLX->Hdr.e32_fixupsize,
+ off + pModLX->offHdr);
+ if (!rc)
+ {
+ pModLX->pbFixupSection = pv;
+ pModLX->pbFixupSectionLast = pModLX->pbFixupSection + pModLX->Hdr.e32_fixupsize;
+ KLDRMODLX_ASSERT(!pModLX->paoffPageFixups);
+ if (pModLX->Hdr.e32_fpagetab)
+ pModLX->paoffPageFixups = (const KU32 *)(pModLX->pbFixupSection + pModLX->Hdr.e32_fpagetab - off);
+ KLDRMODLX_ASSERT(!pModLX->pbFixupRecs);
+ if (pModLX->Hdr.e32_frectab)
+ pModLX->pbFixupRecs = pModLX->pbFixupSection + pModLX->Hdr.e32_frectab - off;
+ KLDRMODLX_ASSERT(!pModLX->pbImportMods);
+ if (pModLX->Hdr.e32_impmod)
+ pModLX->pbImportMods = pModLX->pbFixupSection + pModLX->Hdr.e32_impmod - off;
+ KLDRMODLX_ASSERT(!pModLX->pbImportProcs);
+ if (pModLX->Hdr.e32_impproc)
+ pModLX->pbImportProcs = pModLX->pbFixupSection + pModLX->Hdr.e32_impproc - off;
+ }
+ else
+ kHlpFree(pv);
+ return rc;
+}
+
+
+/** @copydoc kLdrModEnumSymbols */
+static int kldrModLXEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ const struct b32_bundle *pBundle;
+ KU32 iOrdinal;
+ int rc = 0;
+ K_NOREF(pvBits);
+ K_NOREF(fFlags);
+
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+
+ /*
+ * Enumerate the entry table.
+ * (The entry table is made up of bundles of similar exports.)
+ */
+ iOrdinal = 1;
+ pBundle = (const struct b32_bundle *)pModLX->pbEntryTab;
+ while (pBundle->b32_cnt && iOrdinal)
+ {
+ static const KSIZE s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+ /*
+ * Enum the entries in the bundle.
+ */
+ if (pBundle->b32_type != EMPTY)
+ {
+ const struct e32_entry *pEntry;
+ KSIZE cbEntry;
+ KLDRADDR BundleRVA;
+ unsigned cLeft;
+
+
+ /* Validate the bundle. */
+ switch (pBundle->b32_type)
+ {
+ case ENTRY16:
+ case GATE16:
+ case ENTRY32:
+ if ( pBundle->b32_obj <= 0
+ || pBundle->b32_obj > pMod->cSegments)
+ return KLDR_ERR_LX_BAD_BUNDLE;
+ BundleRVA = pMod->aSegments[pBundle->b32_obj - 1].RVA;
+ break;
+
+ case ENTRYFWD:
+ BundleRVA = 0;
+ break;
+
+ default:
+ /* anyone actually using TYPEINFO will end up here. */
+ KLDRMODLX_ASSERT(!"Bad bundle type");
+ return KLDR_ERR_LX_BAD_BUNDLE;
+ }
+
+ /* iterate the bundle entries. */
+ cbEntry = s_cbEntry[pBundle->b32_type];
+ pEntry = (const struct e32_entry *)(pBundle + 1);
+ cLeft = pBundle->b32_cnt;
+ while (cLeft-- > 0)
+ {
+ KLDRADDR uValue;
+ KU32 fKind;
+ int fFoundName;
+ const KU8 *pbName;
+
+ /*
+ * Calc the symbol value and kind.
+ */
+ switch (pBundle->b32_type)
+ {
+ /* e32_flags + a 16-bit offset. */
+ case ENTRY16:
+ uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_offset.offset16;
+ fKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_NO_TYPE;
+ break;
+
+ /* e32_flags + a 16-bit offset + a 16-bit callgate selector. */
+ case GATE16:
+ uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_callgate.offset;
+ fKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_CODE;
+ break;
+
+ /* e32_flags + a 32-bit offset. */
+ case ENTRY32:
+ uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_offset.offset32;
+ fKind = KLDRSYMKIND_32BIT;
+ break;
+
+ /* e32_flags + 16-bit import module ordinal + a 32-bit procname or ordinal. */
+ case ENTRYFWD:
+ uValue = 0; /** @todo implement enumeration of forwarders properly. */
+ fKind = KLDRSYMKIND_FORWARDER;
+ break;
+
+ default: /* shut up gcc. */
+ uValue = 0;
+ fKind = KLDRSYMKIND_NO_BIT | KLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ /*
+ * Any symbol names?
+ */
+ fFoundName = 0;
+
+ /* resident name table. */
+ pbName = pModLX->pbResNameTab;
+ if (pbName)
+ {
+ do
+ {
+ pbName = kldrModLXDoNameTableLookupByOrdinal(pbName, pModLX->pbLoaderSectionLast - pbName + 1, iOrdinal);
+ if (!pbName)
+ break;
+ fFoundName = 1;
+ rc = pfnCallback(pMod, iOrdinal, (const char *)pbName + 1, *pbName, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+
+ /* skip to the next entry */
+ pbName += 1 + *pbName + 2;
+ } while (pbName < pModLX->pbLoaderSectionLast);
+ }
+
+ /* resident name table. */
+ pbName = pModLX->pbNonResNameTab;
+ /** @todo lazy load the non-resident name table. */
+ if (pbName)
+ {
+ do
+ {
+ pbName = kldrModLXDoNameTableLookupByOrdinal(pbName, pModLX->pbNonResNameTabLast - pbName + 1, iOrdinal);
+ if (!pbName)
+ break;
+ fFoundName = 1;
+ rc = pfnCallback(pMod, iOrdinal, (const char *)pbName + 1, *pbName, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+
+ /* skip to the next entry */
+ pbName += 1 + *pbName + 2;
+ } while (pbName < pModLX->pbLoaderSectionLast);
+ }
+
+ /*
+ * If no names, call once with the ordinal only.
+ */
+ if (!fFoundName)
+ {
+ rc = pfnCallback(pMod, iOrdinal, NULL, 0, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+
+ /* next */
+ iOrdinal++;
+ pEntry = (const struct e32_entry *)((KUPTR)pEntry + cbEntry);
+ }
+ }
+
+ /*
+ * The next bundle.
+ */
+ if (pBundle->b32_type > ENTRYFWD)
+ {
+ KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+ return KLDR_ERR_LX_BAD_BUNDLE;
+ }
+ if (pBundle->b32_type == 0)
+ pBundle = (const struct b32_bundle *)((const KU8 *)pBundle + 2);
+ else
+ pBundle = (const struct b32_bundle *)((const KU8 *)(pBundle + 1) + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Lookup a name table entry by ordinal.
+ *
+ * @returns Pointer to the name table entry if found.
+ * @returns NULL if not found.
+ * @param pbNameTable Pointer to the name table that should be searched.
+ * @param cbNameTable The size of the name table.
+ * @param iOrdinal The ordinal to search for.
+ */
+static const KU8 *kldrModLXDoNameTableLookupByOrdinal(const KU8 *pbNameTable, KSSIZE cbNameTable, KU32 iOrdinal)
+{
+ while (*pbNameTable != 0 && cbNameTable > 0)
+ {
+ const KU8 cbName = *pbNameTable;
+ KU32 iName;
+
+ cbNameTable -= cbName + 1 + 2;
+ if (cbNameTable < 0)
+ break;
+
+ iName = *(pbNameTable + cbName + 1)
+ | ((unsigned)*(pbNameTable + cbName + 2) << 8);
+ if (iName == iOrdinal)
+ return pbNameTable;
+
+ /* next entry */
+ pbNameTable += cbName + 1 + 2;
+ }
+
+ return NULL;
+}
+
+
+/** @copydoc kLdrModGetImport */
+static int kldrModLXGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ const KU8 *pb;
+ int rc;
+ K_NOREF(pvBits);
+
+ /*
+ * Validate
+ */
+ if (iImport >= pModLX->Hdr.e32_impmodcnt)
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /*
+ * Lazy loading the fixup section.
+ */
+ if (!pModLX->pbImportMods)
+ {
+ rc = kldrModLXDoLoadFixupSection(pModLX);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Iterate the module import table until we reach the requested import ordinal.
+ */
+ pb = pModLX->pbImportMods;
+ while (iImport-- > 0)
+ pb += *pb + 1;
+
+ /*
+ * Copy out the result.
+ */
+ if (*pb < cchName)
+ {
+ kHlpMemCopy(pszName, pb + 1, *pb);
+ pszName[*pb] = '\0';
+ rc = 0;
+ }
+ else
+ {
+ kHlpMemCopy(pszName, pb + 1, cchName);
+ if (cchName)
+ pszName[cchName - 1] = '\0';
+ rc = KERR_BUFFER_OVERFLOW;
+ }
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModNumberOfImports */
+static KI32 kldrModLXNumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ K_NOREF(pvBits);
+ return pModLX->Hdr.e32_impmodcnt;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModLXGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ const KU32 i = pModLX->Hdr.e32_stackobj;
+ K_NOREF(pvBits);
+
+ if ( i
+ && i <= pMod->cSegments
+ && pModLX->Hdr.e32_esp <= pMod->aSegments[i - 1].LinkAddress + pMod->aSegments[i - 1].cb
+ && pModLX->Hdr.e32_stacksize
+ && pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize >= pMod->aSegments[i - 1].LinkAddress)
+ {
+
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+ pStackInfo->LinkAddress = pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize;
+ pStackInfo->Address = BaseAddress
+ + pMod->aSegments[i - 1].RVA
+ + pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize - pMod->aSegments[i - 1].LinkAddress;
+ }
+ else
+ {
+ pStackInfo->Address = NIL_KLDRADDR;
+ pStackInfo->LinkAddress = NIL_KLDRADDR;
+ }
+ pStackInfo->cbStack = pModLX->Hdr.e32_stacksize;
+ pStackInfo->cbStackThread = 0;
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModLXQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ K_NOREF(pvBits);
+
+ /*
+ * Convert the address from the header.
+ */
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+ *pMainEPAddress = pModLX->Hdr.e32_startobj
+ && pModLX->Hdr.e32_startobj <= pMod->cSegments
+ && pModLX->Hdr.e32_eip < pMod->aSegments[pModLX->Hdr.e32_startobj - 1].cb
+ ? BaseAddress + pMod->aSegments[pModLX->Hdr.e32_startobj - 1].RVA + pModLX->Hdr.e32_eip
+ : NIL_KLDRADDR;
+ return 0;
+}
+
+
+/** @copydoc kLdrModEnumDbgInfo */
+static int kldrModLXEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+ /*PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;*/
+ K_NOREF(pfnCallback);
+ K_NOREF(pvUser);
+
+ /*
+ * Quit immediately if no debug info.
+ */
+ if (kldrModLXHasDbgInfo(pMod, pvBits))
+ return 0;
+#if 0
+ /*
+ * Read the debug info and look for familiar magics and structures.
+ */
+ /** @todo */
+#endif
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModLXHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ K_NOREF(pvBits);
+
+ /*
+ * Don't curretnly bother with linkers which doesn't advertise it in the header.
+ */
+ if ( !pModLX->Hdr.e32_debuginfo
+ || !pModLX->Hdr.e32_debuglen)
+ return KLDR_ERR_NO_DEBUG_INFO;
+ return 0;
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModLXMap(PKLDRMOD pMod)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ unsigned fFixed;
+ void *pvBase;
+ int rc;
+
+ /*
+ * Already mapped?
+ */
+ if (pModLX->pvMapping)
+ return KLDR_ERR_ALREADY_MAPPED;
+
+ /*
+ * Allocate memory for it.
+ */
+ /* fixed image? */
+ fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
+ || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
+ if (!fFixed)
+ pvBase = NULL;
+ else
+ {
+ pvBase = (void *)(KUPTR)pMod->aSegments[0].LinkAddress;
+ if ((KUPTR)pvBase != pMod->aSegments[0].LinkAddress)
+ return KLDR_ERR_ADDRESS_OVERFLOW;
+ }
+ rc = kHlpPageAlloc(&pvBase, pModLX->cbMapped, KPROT_EXECUTE_READWRITE, fFixed);
+ if (rc)
+ return rc;
+
+ /*
+ * Load the bits, apply page protection, and update the segment table.
+ */
+ rc = kldrModLXDoLoadBits(pModLX, pvBase);
+ if (!rc)
+ rc = kldrModLXDoProtect(pModLX, pvBase, 0 /* protect */);
+ if (!rc)
+ {
+ KU32 i;
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ if (pMod->aSegments[i].RVA != NIL_KLDRADDR)
+ pMod->aSegments[i].MapAddress = (KUPTR)pvBase + (KUPTR)pMod->aSegments[i].RVA;
+ }
+ pModLX->pvMapping = pvBase;
+ }
+ else
+ kHlpPageFree(pvBase, pModLX->cbMapped);
+ return rc;
+}
+
+
+/**
+ * Loads the LX pages into the specified memory mapping.
+ *
+ * @returns 0 on success.
+ * @returns non-zero kLdr or OS status code on failure.
+ *
+ * @param pModLX The LX module interpreter instance.
+ * @param pvBits Where to load the bits.
+ */
+static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits)
+{
+ const PKRDR pRdr = pModLX->pMod->pRdr;
+ KU8 *pbTmpPage = NULL;
+ int rc = 0;
+ KU32 i;
+
+ /*
+ * Iterate the segments.
+ */
+ for (i = 0; i < pModLX->Hdr.e32_objcnt; i++)
+ {
+ const struct o32_obj * const pObj = &pModLX->paObjs[i];
+ const KU32 cPages = (KU32)(pModLX->pMod->aSegments[i].cbMapped / OBJPAGELEN);
+ KU32 iPage;
+ KU8 *pbPage = (KU8 *)pvBits + (KUPTR)pModLX->pMod->aSegments[i].RVA;
+
+ /*
+ * Iterate the page map pages.
+ */
+ for (iPage = 0; !rc && iPage < pObj->o32_mapsize; iPage++, pbPage += OBJPAGELEN)
+ {
+ const struct o32_map *pMap = &pModLX->paPageMappings[iPage + pObj->o32_pagemap - 1];
+ switch (pMap->o32_pageflags)
+ {
+ case VALID:
+ if (pMap->o32_pagesize == OBJPAGELEN)
+ rc = kRdrRead(pRdr, pbPage, OBJPAGELEN,
+ pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+ else if (pMap->o32_pagesize < OBJPAGELEN)
+ {
+ rc = kRdrRead(pRdr, pbPage, pMap->o32_pagesize,
+ pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+ kHlpMemSet(pbPage + pMap->o32_pagesize, 0, OBJPAGELEN - pMap->o32_pagesize);
+ }
+ else
+ rc = KLDR_ERR_LX_BAD_PAGE_MAP;
+ break;
+
+ case ITERDATA:
+ case ITERDATA2:
+ /* make sure we've got a temp page .*/
+ if (!pbTmpPage)
+ {
+ pbTmpPage = kHlpAlloc(OBJPAGELEN + 256);
+ if (!pbTmpPage)
+ break;
+ }
+ /* validate the size. */
+ if (pMap->o32_pagesize > OBJPAGELEN + 252)
+ {
+ rc = KLDR_ERR_LX_BAD_PAGE_MAP;
+ break;
+ }
+
+ /* read it and ensure 4 extra zero bytes. */
+ rc = kRdrRead(pRdr, pbTmpPage, pMap->o32_pagesize,
+ pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+ if (rc)
+ break;
+ kHlpMemSet(pbTmpPage + pMap->o32_pagesize, 0, 4);
+
+ /* unpack it into the image page. */
+ if (pMap->o32_pageflags == ITERDATA2)
+ rc = kldrModLXDoIterData2Unpacking(pbPage, pbTmpPage, pMap->o32_pagesize);
+ else
+ rc = kldrModLXDoIterDataUnpacking(pbPage, pbTmpPage, pMap->o32_pagesize);
+ break;
+
+ case INVALID: /* we're probably not dealing correctly with INVALID pages... */
+ case ZEROED:
+ kHlpMemSet(pbPage, 0, OBJPAGELEN);
+ break;
+
+ case RANGE:
+ KLDRMODLX_ASSERT(!"RANGE");
+ /* Falls through. */
+ default:
+ rc = KLDR_ERR_LX_BAD_PAGE_MAP;
+ break;
+ }
+ }
+ if (rc)
+ break;
+
+ /*
+ * Zero the remaining pages.
+ */
+ if (iPage < cPages)
+ kHlpMemSet(pbPage, 0, (cPages - iPage) * OBJPAGELEN);
+ }
+
+ if (pbTmpPage)
+ kHlpFree(pbTmpPage);
+ return rc;
+}
+
+
+/**
+ * Unpacks iterdata (aka EXEPACK).
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pbDst Where to put the uncompressed data. (Assumes OBJPAGELEN size.)
+ * @param pbSrc The compressed source data.
+ * @param cbSrc The file size of the compressed data. The source buffer
+ * contains 4 additional zero bytes.
+ */
+static int kldrModLXDoIterDataUnpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc)
+{
+ const struct LX_Iter *pIter = (const struct LX_Iter *)pbSrc;
+ int cbDst = OBJPAGELEN;
+
+ /* Validate size of data. */
+ if (cbSrc >= (int)OBJPAGELEN - 2)
+ return KLDR_ERR_LX_BAD_ITERDATA;
+
+ /*
+ * Expand the page.
+ */
+ while (cbSrc > 0 && pIter->LX_nIter)
+ {
+ if (pIter->LX_nBytes == 1)
+ {
+ /*
+ * Special case - one databyte.
+ */
+ cbDst -= pIter->LX_nIter;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA;
+
+ cbSrc -= 4 + 1;
+ if (cbSrc < -4)
+ return KLDR_ERR_LX_BAD_ITERDATA;
+
+ kHlpMemSet(pbDst, pIter->LX_Iterdata, pIter->LX_nIter);
+ pbDst += pIter->LX_nIter;
+ pIter++;
+ }
+ else
+ {
+ /*
+ * General.
+ */
+ int i;
+
+ cbDst -= pIter->LX_nIter * pIter->LX_nBytes;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA;
+
+ cbSrc -= 4 + pIter->LX_nBytes;
+ if (cbSrc < -4)
+ return KLDR_ERR_LX_BAD_ITERDATA;
+
+ for (i = pIter->LX_nIter; i > 0; i--, pbDst += pIter->LX_nBytes)
+ kHlpMemCopy(pbDst, &pIter->LX_Iterdata, pIter->LX_nBytes);
+ pIter = (struct LX_Iter *)((char*)pIter + 4 + pIter->LX_nBytes);
+ }
+ }
+
+ /*
+ * Zero remainder of the page.
+ */
+ if (cbDst > 0)
+ kHlpMemSet(pbDst, 0, cbDst);
+
+ return 0;
+}
+
+
+/**
+ * Unpacks iterdata (aka EXEPACK).
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pbDst Where to put the uncompressed data. (Assumes OBJPAGELEN size.)
+ * @param pbSrc The compressed source data.
+ * @param cbSrc The file size of the compressed data. The source buffer
+ * contains 4 additional zero bytes.
+ */
+static int kldrModLXDoIterData2Unpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc)
+{
+ int cbDst = OBJPAGELEN;
+
+ while (cbSrc > 0)
+ {
+ /*
+ * Bit 0 and 1 is the encoding type.
+ */
+ switch (*pbSrc & 0x03)
+ {
+ /*
+ *
+ * 0 1 2 3 4 5 6 7
+ * type | |
+ * ----------------
+ * cb <cb bytes of data>
+ *
+ * Bits 2-7 is, if not zero, the length of an uncompressed run
+ * starting at the following byte.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+ * type | | | | | |
+ * ---------------- ---------------------- -----------------------
+ * zero cb char to multiply
+ *
+ * If the bits are zero, the following two bytes describes a 1 byte interation
+ * run. First byte is count, second is the byte to copy. A count of zero is
+ * means end of data, and we simply stops. In that case the rest of the data
+ * should be zero.
+ */
+ case 0:
+ {
+ if (*pbSrc)
+ {
+ const int cb = *pbSrc >> 2;
+ cbDst -= cb;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbSrc -= cb + 1;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kHlpMemCopy(pbDst, ++pbSrc, cb);
+ pbDst += cb;
+ pbSrc += cb;
+ }
+ else if (cbSrc < 2)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ else
+ {
+ const int cb = pbSrc[1];
+ if (!cb)
+ goto l_endloop;
+ cbDst -= cb;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbSrc -= 3;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kHlpMemSet(pbDst, pbSrc[2], cb);
+ pbDst += cb;
+ pbSrc += 3;
+ }
+ break;
+ }
+
+
+ /*
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * type | | | | | |
+ * ---- ------- -------------------------
+ * cb1 cb2 - 3 offset <cb1 bytes of data>
+ *
+ * Two bytes layed out as described above, followed by cb1 bytes of data to be copied.
+ * The cb2(+3) and offset describes an amount of data to be copied from the expanded
+ * data relative to the current position. The data copied as you would expect it to be.
+ */
+ case 1:
+ {
+ cbSrc -= 2;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ else
+ {
+ const unsigned off = ((unsigned)pbSrc[1] << 1) | (*pbSrc >> 7);
+ const int cb1 = (*pbSrc >> 2) & 3;
+ const int cb2 = ((*pbSrc >> 4) & 7) + 3;
+
+ pbSrc += 2;
+ cbSrc -= cb1;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbDst -= cb1;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kHlpMemCopy(pbDst, pbSrc, cb1);
+ pbDst += cb1;
+ pbSrc += cb1;
+
+ if (off > OBJPAGELEN - (unsigned)cbDst)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbDst -= cb2;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kHlpMemMove(pbDst, pbDst - off, cb2);
+ pbDst += cb2;
+ }
+ break;
+ }
+
+
+ /*
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * type | | | |
+ * ---- ----------------------------------
+ * cb-3 offset
+ *
+ * Two bytes layed out as described above.
+ * The cb(+3) and offset describes an amount of data to be copied from the expanded
+ * data relative to the current position.
+ *
+ * If offset == 1 the data is not copied as expected, but in the memcpyw manner.
+ */
+ case 2:
+ {
+ cbSrc -= 2;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ else
+ {
+ const unsigned off = ((unsigned)pbSrc[1] << 4) | (*pbSrc >> 4);
+ const int cb = ((*pbSrc >> 2) & 3) + 3;
+
+ pbSrc += 2;
+ if (off > OBJPAGELEN - (unsigned)cbDst)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbDst -= cb;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kLdrModLXMemCopyW(pbDst, pbDst - off, cb);
+ pbDst += cb;
+ }
+ break;
+ }
+
+
+ /*
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+ * type | | | | | |
+ * ---------- ---------------- ----------------------------------
+ * cb1 cb2 offset <cb1 bytes of data>
+ *
+ * Three bytes layed out as described above, followed by cb1 bytes of data to be copied.
+ * The cb2 and offset describes an amount of data to be copied from the expanded
+ * data relative to the current position.
+ *
+ * If offset == 1 the data is not copied as expected, but in the memcpyw manner.
+ */
+ case 3:
+ {
+ cbSrc -= 3;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ else
+ {
+ const int cb1 = (*pbSrc >> 2) & 0xf;
+ const int cb2 = ((pbSrc[1] & 0xf) << 2) | (*pbSrc >> 6);
+ const unsigned off = ((unsigned)pbSrc[2] << 4) | (pbSrc[1] >> 4);
+
+ pbSrc += 3;
+ cbSrc -= cb1;
+ if (cbSrc < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbDst -= cb1;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kHlpMemCopy(pbDst, pbSrc, cb1);
+ pbDst += cb1;
+ pbSrc += cb1;
+
+ if (off > OBJPAGELEN - (unsigned)cbDst)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ cbDst -= cb2;
+ if (cbDst < 0)
+ return KLDR_ERR_LX_BAD_ITERDATA2;
+ kLdrModLXMemCopyW(pbDst, pbDst - off, cb2);
+ pbDst += cb2;
+ }
+ break;
+ }
+ } /* type switch. */
+ } /* unpack loop */
+
+l_endloop:
+
+
+ /*
+ * Zero remainder of the page.
+ */
+ if (cbDst > 0)
+ kHlpMemSet(pbDst, 0, cbDst);
+
+ return 0;
+}
+
+
+/**
+ * Special memcpy employed by the iterdata2 algorithm.
+ *
+ * Emulate a 16-bit memcpy (copying 16-bit at a time) and the effects this
+ * has if src is very close to the destination.
+ *
+ * @param pbDst Destination pointer.
+ * @param pbSrc Source pointer. Will always be <= pbDst.
+ * @param cb Amount of data to be copied.
+ * @remark This assumes that unaligned word and dword access is fine.
+ */
+static void kLdrModLXMemCopyW(KU8 *pbDst, const KU8 *pbSrc, int cb)
+{
+ switch (pbDst - pbSrc)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ /* 16-bit copy (unaligned) */
+ if (cb & 1)
+ *pbDst++ = *pbSrc++;
+ for (cb >>= 1; cb > 0; cb--, pbDst += 2, pbSrc += 2)
+ *(KU16 *)pbDst = *(const KU16 *)pbSrc;
+ break;
+
+ default:
+ /* 32-bit copy (unaligned) */
+ if (cb & 1)
+ *pbDst++ = *pbSrc++;
+ if (cb & 2)
+ {
+ *(KU16 *)pbDst = *(const KU16 *)pbSrc;
+ pbDst += 2;
+ pbSrc += 2;
+ }
+ for (cb >>= 2; cb > 0; cb--, pbDst += 4, pbSrc += 4)
+ *(KU32 *)pbDst = *(const KU32 *)pbSrc;
+ break;
+ }
+}
+
+
+/**
+ * Unprotects or protects the specified image mapping.
+ *
+ * @returns 0 on success.
+ * @returns non-zero kLdr or OS status code on failure.
+ *
+ * @param pModLX The LX module interpreter instance.
+ * @param pvBits The mapping to protect.
+ * @param UnprotectOrProtect If 1 unprotect (i.e. make all writable), otherwise
+ * protect according to the object table.
+ */
+static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect)
+{
+ KU32 i;
+ PKLDRMOD pMod = pModLX->pMod;
+
+ /*
+ * Change object protection.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ int rc;
+ void *pv;
+ KPROT enmProt;
+
+ /* calc new protection. */
+ enmProt = pMod->aSegments[i].enmProt;
+ if (fUnprotectOrProtect)
+ {
+ switch (enmProt)
+ {
+ case KPROT_NOACCESS:
+ case KPROT_READONLY:
+ case KPROT_READWRITE:
+ case KPROT_WRITECOPY:
+ enmProt = KPROT_READWRITE;
+ break;
+ case KPROT_EXECUTE:
+ case KPROT_EXECUTE_READ:
+ case KPROT_EXECUTE_READWRITE:
+ case KPROT_EXECUTE_WRITECOPY:
+ enmProt = KPROT_EXECUTE_READWRITE;
+ break;
+ default:
+ KLDRMODLX_ASSERT(!"bad enmProt");
+ return -1;
+ }
+ }
+ else
+ {
+ /* copy on write -> normal write. */
+ if (enmProt == KPROT_EXECUTE_WRITECOPY)
+ enmProt = KPROT_EXECUTE_READWRITE;
+ else if (enmProt == KPROT_WRITECOPY)
+ enmProt = KPROT_READWRITE;
+ }
+
+
+ /* calc the address and set page protection. */
+ pv = (KU8 *)pvBits + pMod->aSegments[i].RVA;
+
+ rc = kHlpPageProtect(pv, pMod->aSegments[i].cbMapped, enmProt);
+ if (rc)
+ break;
+
+ /** @todo the gap page should be marked NOACCESS! */
+ }
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModLXUnmap(PKLDRMOD pMod)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ KU32 i;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (!pModLX->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Free the mapping and update the segments.
+ */
+ rc = kHlpPageFree((void *)pModLX->pvMapping, pModLX->cbMapped);
+ KLDRMODLX_ASSERT(!rc);
+ pModLX->pvMapping = NULL;
+
+ for (i = 0; i < pMod->cSegments; i++)
+ pMod->aSegments[i].MapAddress = 0;
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModLXAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+
+ /* no tls, just do the error checking. */
+ if ( pvMapping == KLDRMOD_INT_MAP
+ && pModLX->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ return 0;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModLXFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ /* no tls. */
+ K_NOREF(pMod);
+ K_NOREF(pvMapping);
+
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModLXReload(PKLDRMOD pMod)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ int rc, rc2;
+
+ /*
+ * Mapped?
+ */
+ if (!pModLX->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Before doing anything we'll have to make all pages writable.
+ */
+ rc = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 1 /* unprotect */);
+ if (rc)
+ return rc;
+
+ /*
+ * Load the bits again.
+ */
+ rc = kldrModLXDoLoadBits(pModLX, (void *)pModLX->pvMapping);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
+ if (!rc && rc2)
+ rc = rc2;
+ return rc;
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModLXFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ int rc, rc2;
+
+ /*
+ * Mapped?
+ */
+ if (!pModLX->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Before doing anything we'll have to make all pages writable.
+ */
+ rc = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 1 /* unprotect */);
+ if (rc)
+ return rc;
+
+ /*
+ * Apply fixups and resolve imports.
+ */
+ rc = kldrModLXRelocateBits(pMod, (void *)pModLX->pvMapping, (KUPTR)pModLX->pvMapping,
+ pMod->aSegments[0].LinkAddress, pfnGetImport, pvUser);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
+ if (!rc && rc2)
+ rc = rc2;
+ return rc;
+}
+
+
+/** @copydoc kLdrModCallInit */
+static int kldrModLXCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModLX->pvMapping;
+ if (!pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ }
+
+ /*
+ * Do TLS callbacks first and then call the init/term function if it's a DLL.
+ */
+ if ((pModLX->Hdr.e32_mflags & E32MODMASK) == E32MODDLL)
+ rc = kldrModLXDoCallDLL(pModLX, pvMapping, 0 /* attach */, uHandle);
+ else
+ rc = 0;
+ return rc;
+}
+
+
+/**
+ * Call the DLL entrypoint.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MODULE_INIT_FAILED or KLDR_ERR_THREAD_ATTACH_FAILED on failure.
+ * @param pModLX The LX module interpreter instance.
+ * @param pvMapping The module mapping to use (resolved).
+ * @param uOp The operation (DLL_*).
+ * @param uHandle The module handle to present.
+ */
+static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, void *pvMapping, unsigned uOp, KUPTR uHandle)
+{
+ int rc;
+
+ /*
+ * If no entrypoint there isn't anything to be done.
+ */
+ if ( !pModLX->Hdr.e32_startobj
+ || pModLX->Hdr.e32_startobj > pModLX->Hdr.e32_objcnt)
+ return 0;
+
+ /*
+ * Invoke the entrypoint and convert the boolean result to a kLdr status code.
+ */
+ rc = kldrModLXDoCall((KUPTR)pvMapping
+ + (KUPTR)pModLX->pMod->aSegments[pModLX->Hdr.e32_startobj - 1].RVA
+ + pModLX->Hdr.e32_eip,
+ uHandle, uOp, NULL);
+ if (rc)
+ rc = 0;
+ else if (uOp == 0 /* attach */)
+ rc = KLDR_ERR_MODULE_INIT_FAILED;
+ else /* detach: ignore failures */
+ rc = 0;
+ return rc;
+}
+
+
+/**
+ * Do a 3 parameter callback.
+ *
+ * @returns 32-bit callback return.
+ * @param uEntrypoint The address of the function to be called.
+ * @param uHandle The first argument, the module handle.
+ * @param uOp The second argumnet, the reason we're calling.
+ * @param pvReserved The third argument, reserved argument. (figure this one out)
+ */
+KI32 kldrModLXDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved)
+{
+#if defined(__X86__) || defined(__i386__) || defined(_M_IX86)
+ KI32 rc;
+/** @todo try/except */
+
+ /*
+ * Paranoia.
+ */
+# ifdef __GNUC__
+ __asm__ __volatile__(
+ "pushl %2\n\t"
+ "pushl %1\n\t"
+ "pushl %0\n\t"
+ "lea 12(%%esp), %2\n\t"
+ "call *%3\n\t"
+ "movl %2, %%esp\n\t"
+ : "=a" (rc)
+ : "d" (uOp),
+ "S" (0),
+ "c" (uEntrypoint),
+ "0" (uHandle));
+# elif defined(_MSC_VER)
+ __asm {
+ mov eax, [uHandle]
+ mov edx, [uOp]
+ mov ecx, 0
+ mov ebx, [uEntrypoint]
+ push edi
+ mov edi, esp
+ push ecx
+ push edx
+ push eax
+ call ebx
+ mov esp, edi
+ pop edi
+ mov [rc], eax
+ }
+# else
+# error "port me!"
+# endif
+ K_NOREF(pvReserved);
+ return rc;
+
+#else
+ K_NOREF(uEntrypoint);
+ K_NOREF(uHandle);
+ K_NOREF(uOp);
+ K_NOREF(pvReserved);
+ return KCPU_ERR_ARCH_CPU_NOT_COMPATIBLE;
+#endif
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModLXCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModLX->pvMapping;
+ if (!pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ }
+
+ /*
+ * Do the call.
+ */
+ if ((pModLX->Hdr.e32_mflags & E32MODMASK) == E32MODDLL)
+ kldrModLXDoCallDLL(pModLX, pvMapping, 1 /* detach */, uHandle);
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModLXCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+ /* no thread attach/detach callout. */
+ K_NOREF(pMod);
+ K_NOREF(pvMapping);
+ K_NOREF(uHandle);
+ K_NOREF(fAttachingOrDetaching);
+ return 0;
+}
+
+
+/** @copydoc kLdrModSize */
+static KLDRADDR kldrModLXSize(PKLDRMOD pMod)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ return pModLX->cbMapped;
+}
+
+
+/** @copydoc kLdrModGetBits */
+static int kldrModLXGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ int rc;
+
+ /*
+ * Load the image bits.
+ */
+ rc = kldrModLXDoLoadBits(pModLX, pvBits);
+ if (rc)
+ return rc;
+
+ /*
+ * Perform relocations.
+ */
+ return kldrModLXRelocateBits(pMod, pvBits, BaseAddress, pMod->aSegments[0].LinkAddress, pfnGetImport, pvUser);
+
+}
+
+
+/** @copydoc kLdrModRelocateBits */
+static int kldrModLXRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+ KU32 iSeg;
+ int rc;
+
+ /*
+ * Do we need to to *anything*?
+ */
+ if ( NewBaseAddress == OldBaseAddress
+ && NewBaseAddress == pModLX->paObjs[0].o32_base
+ && !pModLX->Hdr.e32_impmodcnt)
+ return 0;
+
+ /*
+ * Load the fixup section.
+ */
+ if (!pModLX->pbFixupSection)
+ {
+ rc = kldrModLXDoLoadFixupSection(pModLX);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Iterate the segments.
+ */
+ for (iSeg = 0; iSeg < pModLX->Hdr.e32_objcnt; iSeg++)
+ {
+ const struct o32_obj * const pObj = &pModLX->paObjs[iSeg];
+ KLDRADDR PageAddress = NewBaseAddress + pModLX->pMod->aSegments[iSeg].RVA;
+ KU32 iPage;
+ KU8 *pbPage = (KU8 *)pvBits + (KUPTR)pModLX->pMod->aSegments[iSeg].RVA;
+
+ /*
+ * Iterate the page map pages.
+ */
+ for (iPage = 0, rc = 0; !rc && iPage < pObj->o32_mapsize; iPage++, pbPage += OBJPAGELEN, PageAddress += OBJPAGELEN)
+ {
+ const KU8 * const pbFixupRecEnd = pModLX->pbFixupRecs + pModLX->paoffPageFixups[iPage + pObj->o32_pagemap];
+ const KU8 *pb = pModLX->pbFixupRecs + pModLX->paoffPageFixups[iPage + pObj->o32_pagemap - 1];
+ KLDRADDR uValue = NIL_KLDRADDR;
+ KU32 fKind = 0;
+ int iSelector;
+
+ /* sanity */
+ if (pbFixupRecEnd < pb)
+ return KLDR_ERR_BAD_FIXUP;
+ if (pbFixupRecEnd - 1 > pModLX->pbFixupSectionLast)
+ return KLDR_ERR_BAD_FIXUP;
+ if (pb < pModLX->pbFixupSection)
+ return KLDR_ERR_BAD_FIXUP;
+
+ /*
+ * Iterate the fixup record.
+ */
+ while (pb < pbFixupRecEnd)
+ {
+ union _rel
+ {
+ const KU8 * pb;
+ const struct r32_rlc *prlc;
+ } u;
+
+ u.pb = pb;
+ pb += 3 + (u.prlc->nr_stype & NRCHAIN ? 0 : 1); /* place pch at the 4th member. */
+
+ /*
+ * Figure out the target.
+ */
+ switch (u.prlc->nr_flags & NRRTYP)
+ {
+ /*
+ * Internal fixup.
+ */
+ case NRRINT:
+ {
+ KU16 iTrgObject;
+ KU32 offTrgObject;
+
+ /* the object */
+ if (u.prlc->nr_flags & NR16OBJMOD)
+ {
+ iTrgObject = *(const KU16 *)pb;
+ pb += 2;
+ }
+ else
+ iTrgObject = *pb++;
+ iTrgObject--;
+ if (iTrgObject >= pModLX->Hdr.e32_objcnt)
+ return KLDR_ERR_BAD_FIXUP;
+
+ /* the target */
+ if ((u.prlc->nr_stype & NRSRCMASK) != NRSSEG)
+ {
+ if (u.prlc->nr_flags & NR32BITOFF)
+ {
+ offTrgObject = *(const KU32 *)pb;
+ pb += 4;
+ }
+ else
+ {
+ offTrgObject = *(const KU16 *)pb;
+ pb += 2;
+ }
+
+ /* calculate the symbol info. */
+ uValue = offTrgObject + NewBaseAddress + pMod->aSegments[iTrgObject].RVA;
+ }
+ else
+ uValue = NewBaseAddress + pMod->aSegments[iTrgObject].RVA;
+ if ( (u.prlc->nr_stype & NRALIAS)
+ || (pMod->aSegments[iTrgObject].fFlags & KLDRSEG_FLAG_16BIT))
+ iSelector = pMod->aSegments[iTrgObject].Sel16bit;
+ else
+ iSelector = pMod->aSegments[iTrgObject].SelFlat;
+ fKind = 0;
+ break;
+ }
+
+ /*
+ * Import by symbol ordinal.
+ */
+ case NRRORD:
+ {
+ KU16 iModule;
+ KU32 iSymbol;
+
+ /* the module ordinal */
+ if (u.prlc->nr_flags & NR16OBJMOD)
+ {
+ iModule = *(const KU16 *)pb;
+ pb += 2;
+ }
+ else
+ iModule = *pb++;
+ iModule--;
+ if (iModule >= pModLX->Hdr.e32_impmodcnt)
+ return KLDR_ERR_BAD_FIXUP;
+#if 1
+ if (u.prlc->nr_flags & NRICHAIN)
+ return KLDR_ERR_BAD_FIXUP;
+#endif
+
+ /* . */
+ if (u.prlc->nr_flags & NR32BITOFF)
+ {
+ iSymbol = *(const KU32 *)pb;
+ pb += 4;
+ }
+ else if (!(u.prlc->nr_flags & NR8BITORD))
+ {
+ iSymbol = *(const KU16 *)pb;
+ pb += 2;
+ }
+ else
+ iSymbol = *pb++;
+
+ /* resolve it. */
+ rc = pfnGetImport(pMod, iModule, iSymbol, NULL, 0, NULL, &uValue, &fKind, pvUser);
+ if (rc)
+ return rc;
+ iSelector = -1;
+ break;
+ }
+
+ /*
+ * Import by symbol name.
+ */
+ case NRRNAM:
+ {
+ KU32 iModule;
+ KU16 offSymbol;
+ const KU8 *pbSymbol;
+
+ /* the module ordinal */
+ if (u.prlc->nr_flags & NR16OBJMOD)
+ {
+ iModule = *(const KU16 *)pb;
+ pb += 2;
+ }
+ else
+ iModule = *pb++;
+ iModule--;
+ if (iModule >= pModLX->Hdr.e32_impmodcnt)
+ return KLDR_ERR_BAD_FIXUP;
+#if 1
+ if (u.prlc->nr_flags & NRICHAIN)
+ return KLDR_ERR_BAD_FIXUP;
+#endif
+
+ /* . */
+ if (u.prlc->nr_flags & NR32BITOFF)
+ {
+ offSymbol = *(const KU32 *)pb;
+ pb += 4;
+ }
+ else if (!(u.prlc->nr_flags & NR8BITORD))
+ {
+ offSymbol = *(const KU16 *)pb;
+ pb += 2;
+ }
+ else
+ offSymbol = *pb++;
+ pbSymbol = pModLX->pbImportProcs + offSymbol;
+ if ( pbSymbol < pModLX->pbImportProcs
+ || pbSymbol > pModLX->pbFixupSectionLast)
+ return KLDR_ERR_BAD_FIXUP;
+
+ /* resolve it. */
+ rc = pfnGetImport(pMod, iModule, NIL_KLDRMOD_SYM_ORDINAL, (const char *)pbSymbol + 1, *pbSymbol, NULL,
+ &uValue, &fKind, pvUser);
+ if (rc)
+ return rc;
+ iSelector = -1;
+ break;
+ }
+
+ case NRRENT:
+ KLDRMODLX_ASSERT(!"NRRENT");
+ /* Falls through. */
+ default:
+ iSelector = -1;
+ break;
+ }
+
+ /* addend */
+ if (u.prlc->nr_flags & NRADD)
+ {
+ if (u.prlc->nr_flags & NR32BITADD)
+ {
+ uValue += *(const KU32 *)pb;
+ pb += 4;
+ }
+ else
+ {
+ uValue += *(const KU16 *)pb;
+ pb += 2;
+ }
+ }
+
+
+ /*
+ * Deal with the 'source' (i.e. the place that should be modified - very logical).
+ */
+ if (!(u.prlc->nr_stype & NRCHAIN))
+ {
+ int off = u.prlc->r32_soff;
+
+ /* common / simple */
+ if ( (u.prlc->nr_stype & NRSRCMASK) == NROFF32
+ && off >= 0
+ && off <= (int)OBJPAGELEN - 4)
+ *(KU32 *)&pbPage[off] = (KU32)uValue;
+ else if ( (u.prlc->nr_stype & NRSRCMASK) == NRSOFF32
+ && off >= 0
+ && off <= (int)OBJPAGELEN - 4)
+ *(KU32 *)&pbPage[off] = (KU32)(uValue - (PageAddress + off + 4));
+ else
+ {
+ /* generic */
+ rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (rc)
+ return rc;
+ }
+ }
+ else if (!(u.prlc->nr_flags & NRICHAIN))
+ {
+ const KI16 *poffSrc = (const KI16 *)pb;
+ KU8 c = u.pb[2];
+
+ /* common / simple */
+ if ((u.prlc->nr_stype & NRSRCMASK) == NROFF32)
+ {
+ while (c-- > 0)
+ {
+ int off = *poffSrc++;
+ if (off >= 0 && off <= (int)OBJPAGELEN - 4)
+ *(KU32 *)&pbPage[off] = (KU32)uValue;
+ else
+ {
+ rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (rc)
+ return rc;
+ }
+ }
+ }
+ else if ((u.prlc->nr_stype & NRSRCMASK) == NRSOFF32)
+ {
+ while (c-- > 0)
+ {
+ int off = *poffSrc++;
+ if (off >= 0 && off <= (int)OBJPAGELEN - 4)
+ *(KU32 *)&pbPage[off] = (KU32)(uValue - (PageAddress + off + 4));
+ else
+ {
+ rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (rc)
+ return rc;
+ }
+ }
+ }
+ else
+ {
+ while (c-- > 0)
+ {
+ rc = kldrModLXDoReloc(pbPage, *poffSrc++, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (rc)
+ return rc;
+ }
+ }
+ pb = (const KU8 *)poffSrc;
+ }
+ else
+ {
+ /* This is a pain because it will require virgin pages on a relocation. */
+ KLDRMODLX_ASSERT(!"NRICHAIN");
+ return KLDR_ERR_LX_NRICHAIN_NOT_SUPPORTED;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Applies the relocation to one 'source' in a page.
+ *
+ * This takes care of the more esotic case while the common cases
+ * are dealt with seperately.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pbPage The page in which to apply the fixup.
+ * @param off Page relative offset of where to apply the offset.
+ * @param uValue The target value.
+ * @param fKind The target kind.
+ */
+static int kldrModLXDoReloc(KU8 *pbPage, int off, KLDRADDR PageAddress, const struct r32_rlc *prlc,
+ int iSelector, KLDRADDR uValue, KU32 fKind)
+{
+#pragma pack(1) /* just to be sure */
+ union
+ {
+ KU8 ab[6];
+ KU32 off32;
+ KU16 off16;
+ KU8 off8;
+ struct
+ {
+ KU16 off;
+ KU16 Sel;
+ } Far16;
+ struct
+ {
+ KU32 off;
+ KU16 Sel;
+ } Far32;
+ } uData;
+#pragma pack()
+ const KU8 *pbSrc;
+ KU8 *pbDst;
+ KU8 cb;
+
+ K_NOREF(fKind);
+
+ /*
+ * Compose the fixup data.
+ */
+ switch (prlc->nr_stype & NRSRCMASK)
+ {
+ case NRSBYT:
+ uData.off8 = (KU8)uValue;
+ cb = 1;
+ break;
+ case NRSSEG:
+ if (iSelector == -1)
+ {
+ /* fixme */
+ }
+ uData.off16 = iSelector;
+ cb = 2;
+ break;
+ case NRSPTR:
+ if (iSelector == -1)
+ {
+ /* fixme */
+ }
+ uData.Far16.off = (KU16)uValue;
+ uData.Far16.Sel = iSelector;
+ cb = 4;
+ break;
+ case NRSOFF:
+ uData.off16 = (KU16)uValue;
+ cb = 2;
+ break;
+ case NRPTR48:
+ if (iSelector == -1)
+ {
+ /* fixme */
+ }
+ uData.Far32.off = (KU32)uValue;
+ uData.Far32.Sel = iSelector;
+ cb = 6;
+ break;
+ case NROFF32:
+ uData.off32 = (KU32)uValue;
+ cb = 4;
+ break;
+ case NRSOFF32:
+ uData.off32 = (KU32)(uValue - (PageAddress + off + 4));
+ cb = 4;
+ break;
+ default:
+ return KLDR_ERR_LX_BAD_FIXUP_SECTION; /** @todo fix error, add more checks! */
+ }
+
+ /*
+ * Apply it. This is sloooow...
+ */
+ pbSrc = &uData.ab[0];
+ pbDst = pbPage + off;
+ while (cb-- > 0)
+ {
+ if (off > (int)OBJPAGELEN)
+ break;
+ if (off >= 0)
+ *pbDst = *pbSrc;
+ pbSrc++;
+ pbDst++;
+ }
+
+ return 0;
+}
+
+
+/**
+ * The LX module interpreter method table.
+ */
+KLDRMODOPS g_kLdrModLXOps =
+{
+ "LX",
+ NULL,
+ kldrModLXCreate,
+ kldrModLXDestroy,
+ kldrModLXQuerySymbol,
+ kldrModLXEnumSymbols,
+ kldrModLXGetImport,
+ kldrModLXNumberOfImports,
+ NULL /* can execute one is optional */,
+ kldrModLXGetStackInfo,
+ kldrModLXQueryMainEntrypoint,
+ NULL /* pfnQueryImageUuid */,
+ NULL /* fixme */,
+ NULL /* fixme */,
+ kldrModLXEnumDbgInfo,
+ kldrModLXHasDbgInfo,
+ kldrModLXMap,
+ kldrModLXUnmap,
+ kldrModLXAllocTLS,
+ kldrModLXFreeTLS,
+ kldrModLXReload,
+ kldrModLXFixupMapping,
+ kldrModLXCallInit,
+ kldrModLXCallTerm,
+ kldrModLXCallThread,
+ kldrModLXSize,
+ kldrModLXGetBits,
+ kldrModLXRelocateBits,
+ NULL /* fixme: pfnMostlyDone */,
+ 42 /* the end */
+};
+
diff --git a/src/lib/kStuff/kLdr/kLdrModMachO.c b/src/lib/kStuff/kLdr/kLdrModMachO.c
new file mode 100644
index 0000000..9b97ec2
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrModMachO.c
@@ -0,0 +1,3729 @@
+/* $Id: kLdrModMachO.c 112 2018-07-04 09:37:39Z bird $ */
+/** @file
+ * kLdr - The Module Interpreter for the MACH-O format.
+ */
+
+/*
+ * Copyright (c) 2006-2013 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+#include <k/kLdrFmts/mach-o.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRMODMACHO_STRICT
+ * Define KLDRMODMACHO_STRICT to enabled strict checks in KLDRMODMACHO. */
+#define KLDRMODMACHO_STRICT 1
+
+/** @def KLDRMODMACHO_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMODMACHO_STRICT
+# define KLDRMODMACHO_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRMODMACHO_ASSERT(expr) do {} while (0)
+#endif
+
+/** @def KLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef KLDRMODMACHO_STRICT2
+# define KLDRMODMACHO_CHECK_RETURN(expr, rc) kHlpAssertReturn(expr, rc)
+#else
+# define KLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (!(expr)) { return (rc); } } while (0)
+#endif
+
+/** @def KLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef KLDRMODMACHO_STRICT2
+# define KLDRMODMACHO_FAILED_RETURN(rc) kHlpAssertFailedReturn(rc)
+#else
+# define KLDRMODMACHO_FAILED_RETURN(rc) return (rc)
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Mach-O section details.
+ */
+typedef struct KLDRMODMACHOSECT
+{
+ /** The size of the section (in bytes). */
+ KLDRSIZE cb;
+ /** The link address of this section. */
+ KLDRADDR LinkAddress;
+ /** The RVA of this section. */
+ KLDRADDR RVA;
+ /** The file offset of this section.
+ * This is -1 if the section doesn't have a file backing. */
+ KLDRFOFF offFile;
+ /** The number of fixups. */
+ KU32 cFixups;
+ /** The array of fixups. (lazy loaded) */
+ macho_relocation_info_t *paFixups;
+ /** The file offset of the fixups for this section.
+ * This is -1 if the section doesn't have any fixups. */
+ KLDRFOFF offFixups;
+ /** Mach-O section flags. */
+ KU32 fFlags;
+ /** kLdr segment index. */
+ KU32 iSegment;
+ /** Pointer to the Mach-O section structure. */
+ void *pvMachoSection;
+} KLDRMODMACHOSECT, *PKLDRMODMACHOSECT;
+
+/**
+ * Extra per-segment info.
+ *
+ * This is corresponds to a kLdr segment, not a Mach-O segment!
+ */
+typedef struct KLDRMODMACHOSEG
+{
+ /** The orignal segment number (in case we had to resort it). */
+ KU32 iOrgSegNo;
+ /** The number of sections in the segment. */
+ KU32 cSections;
+ /** Pointer to the sections belonging to this segment.
+ * The array resides in the big memory chunk allocated for
+ * the module handle, so it doesn't need freeing. */
+ PKLDRMODMACHOSECT paSections;
+
+} KLDRMODMACHOSEG, *PKLDRMODMACHOSEG;
+
+/**
+ * Instance data for the Mach-O MH_OBJECT module interpreter.
+ * @todo interpret the other MH_* formats.
+ */
+typedef struct KLDRMODMACHO
+{
+ /** Pointer to the module. (Follows the section table.) */
+ PKLDRMOD pMod;
+ /** Pointer to the RDR file mapping of the raw file bits. NULL if not mapped. */
+ const void *pvBits;
+ /** Pointer to the user mapping. */
+ void *pvMapping;
+ /** The module open flags. */
+ KU32 fOpenFlags;
+
+ /** The offset of the image. (FAT fun.) */
+ KLDRFOFF offImage;
+ /** The link address. */
+ KLDRADDR LinkAddress;
+ /** The size of the mapped image. */
+ KLDRADDR cbImage;
+ /** Whether we're capable of loading the image. */
+ KBOOL fCanLoad;
+ /** Whether we're creating a global offset table segment.
+ * This dependes on the cputype and image type. */
+ KBOOL fMakeGot;
+ /** The size of a indirect GOT jump stub entry.
+ * This is 0 if not needed. */
+ KU8 cbJmpStub;
+ /** Effective file type. If the original was a MH_OBJECT file, the
+ * corresponding MH_DSYM needs the segment translation of a MH_OBJECT too.
+ * The MH_DSYM normally has a separate __DWARF segment, but this is
+ * automatically skipped during the transation. */
+ KU8 uEffFileType;
+ /** Pointer to the load commands. (endian converted) */
+ KU8 *pbLoadCommands;
+ /** The Mach-O header. (endian converted)
+ * @remark The reserved field is only valid for real 64-bit headers. */
+ mach_header_64_t Hdr;
+
+ /** The offset of the symbol table. */
+ KLDRFOFF offSymbols;
+ /** The number of symbols. */
+ KU32 cSymbols;
+ /** The pointer to the loaded symbol table. */
+ void *pvaSymbols;
+ /** The offset of the string table. */
+ KLDRFOFF offStrings;
+ /** The size of the of the string table. */
+ KU32 cchStrings;
+ /** Pointer to the loaded string table. */
+ char *pchStrings;
+
+ /** The image UUID, all zeros if not found. */
+ KU8 abImageUuid[16];
+
+ /** The RVA of the Global Offset Table. */
+ KLDRADDR GotRVA;
+ /** The RVA of the indirect GOT jump stubs. */
+ KLDRADDR JmpStubsRVA;
+
+ /** The number of sections. */
+ KU32 cSections;
+ /** Pointer to the section array running in parallel to the Mach-O one. */
+ PKLDRMODMACHOSECT paSections;
+
+ /** Array of segments parallel to the one in KLDRMOD. */
+ KLDRMODMACHOSEG aSegments[1];
+} KLDRMODMACHO, *PKLDRMODMACHO;
+
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+#if 0
+static KI32 kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits);
+#endif
+static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+
+static int kldrModMachODoCreate(PKRDR pRdr, KLDRFOFF offImage, KU32 fOpenFlags, PKLDRMODMACHO *ppMod);
+static int kldrModMachOPreParseLoadCommands(KU8 *pbLoadCommands, const mach_header_32_t *pHdr, PKRDR pRdr, KLDRFOFF offImage,
+ KU32 fOpenFlags, KU32 *pcSegments, KU32 *pcSections, KU32 *pcbStringPool,
+ PKBOOL pfCanLoad, PKLDRADDR pLinkAddress, KU8 *puEffFileType);
+static int kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, KU32 cbStringPool);
+static int kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pModMachO, PKLDRADDR pBaseAddress);
+
+/*static int kldrModMachOLoadLoadCommands(PKLDRMODMACHO pModMachO);*/
+static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pModMachO);
+static int kldrModMachOLoadFixups(PKLDRMODMACHO pModMachO, KLDRFOFF offFixups, KU32 cFixups, macho_relocation_info_t **ppaFixups);
+static int kldrModMachOMapVirginBits(PKLDRMODMACHO pModMachO);
+
+static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms, const char *pchStrings,
+ KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol,
+ KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind);
+static int kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms, const char *pchStrings,
+ KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol,
+ KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind);
+static int kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms,
+ const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser);
+static int kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms,
+ const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser);
+static int kldrModMachOObjDoImports(PKLDRMODMACHO pModMachO, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int kldrModMachOObjDoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress);
+static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+ macho_nlist_32_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress);
+static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+ macho_nlist_64_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress);
+
+static int kldrModMachOMakeGOT(PKLDRMODMACHO pModMachO, void *pvBits, KLDRADDR NewBaseAddress);
+
+/*static int kldrModMachODoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress);
+static int kldrModMachODoImports(PKLDRMODMACHO pModMachO, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);*/
+
+
+/**
+ * Create a loader module instance interpreting the executable image found
+ * in the specified file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ * On failure, a non-zero OS specific error code is returned.
+ * @param pOps Pointer to the registered method table.
+ * @param pRdr The file provider instance to use.
+ * @param fFlags Flags, MBZ.
+ * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means
+ * anything goes, but with a preference for the current
+ * host architecture.
+ * @param offNewHdr The offset of the new header in MZ files. -1 if not found.
+ * @param ppMod Where to store the module instance pointer.
+ */
+static int kldrModMachOCreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod)
+{
+ PKLDRMODMACHO pModMachO;
+ int rc;
+
+ /*
+ * Create the instance data and do a minimal header validation.
+ */
+ rc = kldrModMachODoCreate(pRdr, offNewHdr == -1 ? 0 : offNewHdr, fFlags, &pModMachO);
+ if (!rc)
+ {
+
+ /*
+ * Match up against the requested CPU architecture.
+ */
+ if ( enmCpuArch == KCPUARCH_UNKNOWN
+ || pModMachO->pMod->enmArch == enmCpuArch)
+ {
+ pModMachO->pMod->pOps = pOps;
+ pModMachO->pMod->u32Magic = KLDRMOD_MAGIC;
+ *ppMod = pModMachO->pMod;
+ return 0;
+ }
+ rc = KLDR_ERR_CPU_ARCH_MISMATCH;
+ }
+ if (pModMachO)
+ {
+ kHlpFree(pModMachO->pbLoadCommands);
+ kHlpFree(pModMachO);
+ }
+ return rc;
+}
+
+
+/**
+ * Separate function for reading creating the Mach-O module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModMachODoCreate(PKRDR pRdr, KLDRFOFF offImage, KU32 fOpenFlags, PKLDRMODMACHO *ppModMachO)
+{
+ union
+ {
+ mach_header_32_t Hdr32;
+ mach_header_64_t Hdr64;
+ } s;
+ PKLDRMODMACHO pModMachO;
+ PKLDRMOD pMod;
+ KU8 *pbLoadCommands;
+ KU32 cSegments = 0; /* (MSC maybe used uninitialized) */
+ KU32 cSections = 0; /* (MSC maybe used uninitialized) */
+ KU32 cbStringPool = 0; /* (MSC maybe used uninitialized) */
+ KSIZE cchFilename;
+ KSIZE cb;
+ KBOOL fMakeGot;
+ KBOOL fCanLoad = K_TRUE;
+ KLDRADDR LinkAddress = NIL_KLDRADDR; /* (MSC maybe used uninitialized) */
+ KU8 cbJmpStub;
+ KU8 uEffFileType = 0; /* (MSC maybe used uninitialized) */
+ int rc;
+ *ppModMachO = NULL;
+
+ kHlpAssert(&s.Hdr32.magic == &s.Hdr64.magic);
+ kHlpAssert(&s.Hdr32.flags == &s.Hdr64.flags);
+
+ /*
+ * Read the Mach-O header.
+ */
+ rc = kRdrRead(pRdr, &s, sizeof(s), offImage);
+ if (rc)
+ return rc;
+ if ( s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE
+ && s.Hdr32.magic != IMAGE_MACHO64_SIGNATURE
+ )
+ {
+ if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ return KLDR_ERR_MACHO_OTHER_ENDIAN_NOT_SUPPORTED;
+ return KLDR_ERR_UNKNOWN_FORMAT;
+ }
+
+ /* sanity checks. */
+ if ( s.Hdr32.sizeofcmds > kRdrSize(pRdr) - sizeof(mach_header_32_t)
+ || s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds
+ || (s.Hdr32.flags & ~MH_VALID_FLAGS))
+ return KLDR_ERR_MACHO_BAD_HEADER;
+ switch (s.Hdr32.cputype)
+ {
+ case CPU_TYPE_X86:
+ fMakeGot = K_FALSE;
+ cbJmpStub = 0;
+ break;
+ case CPU_TYPE_X86_64:
+ fMakeGot = s.Hdr32.filetype == MH_OBJECT;
+ cbJmpStub = fMakeGot ? 8 : 0;
+ break;
+ default:
+ return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+ }
+ if ( s.Hdr32.filetype != MH_OBJECT
+ && s.Hdr32.filetype != MH_EXECUTE
+ && s.Hdr32.filetype != MH_DYLIB
+ && s.Hdr32.filetype != MH_BUNDLE
+ && s.Hdr32.filetype != MH_DSYM
+ && s.Hdr32.filetype != MH_KEXT_BUNDLE)
+ return KLDR_ERR_MACHO_UNSUPPORTED_FILE_TYPE;
+
+ /*
+ * Read and pre-parse the load commands to figure out how many segments we'll be needing.
+ */
+ pbLoadCommands = kHlpAlloc(s.Hdr32.sizeofcmds);
+ if (!pbLoadCommands)
+ return KERR_NO_MEMORY;
+ rc = kRdrRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds,
+ s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+ || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(mach_header_32_t) + offImage
+ : sizeof(mach_header_64_t) + offImage);
+ if (!rc)
+ rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags,
+ &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType);
+ if (rc)
+ {
+ kHlpFree(pbLoadCommands);
+ return rc;
+ }
+ cSegments += fMakeGot;
+
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ cchFilename = kHlpStrLen(kRdrName(pRdr));
+ cb = K_ALIGN_Z( K_OFFSETOF(KLDRMODMACHO, aSegments[cSegments])
+ + sizeof(KLDRMODMACHOSECT) * cSections, 16)
+ + K_OFFSETOF(KLDRMOD, aSegments[cSegments])
+ + cchFilename + 1
+ + cbStringPool;
+ pModMachO = (PKLDRMODMACHO)kHlpAlloc(cb);
+ if (!pModMachO)
+ return KERR_NO_MEMORY;
+ *ppModMachO = pModMachO;
+ pModMachO->pbLoadCommands = pbLoadCommands;
+ pModMachO->offImage = offImage;
+
+ /* KLDRMOD */
+ pMod = (PKLDRMOD)((KU8 *)pModMachO + K_ALIGN_Z( K_OFFSETOF(KLDRMODMACHO, aSegments[cSegments])
+ + sizeof(KLDRMODMACHOSECT) * cSections, 16));
+ pMod->pvData = pModMachO;
+ pMod->pRdr = pRdr;
+ pMod->pOps = NULL; /* set upon success. */
+ pMod->cSegments = cSegments;
+ pMod->cchFilename = (KU32)cchFilename;
+ pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments];
+ kHlpMemCopy((char *)pMod->pszFilename, kRdrName(pRdr), cchFilename + 1);
+ pMod->pszName = kHlpGetFilename(pMod->pszFilename);
+ pMod->cchName = (KU32)(cchFilename - (pMod->pszName - pMod->pszFilename));
+ pMod->fFlags = 0;
+ switch (s.Hdr32.cputype)
+ {
+ case CPU_TYPE_X86:
+ pMod->enmArch = KCPUARCH_X86_32;
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype)
+ {
+ case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
+ pMod->enmCpu = KCPU_X86_32_BLEND;
+ break;
+ case CPU_SUBTYPE_486:
+ pMod->enmCpu = KCPU_I486;
+ break;
+ case CPU_SUBTYPE_486SX:
+ pMod->enmCpu = KCPU_I486SX;
+ break;
+ case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
+ pMod->enmCpu = KCPU_I586;
+ break;
+ case CPU_SUBTYPE_PENTPRO:
+ case CPU_SUBTYPE_PENTII_M3:
+ case CPU_SUBTYPE_PENTII_M5:
+ case CPU_SUBTYPE_CELERON:
+ case CPU_SUBTYPE_CELERON_MOBILE:
+ case CPU_SUBTYPE_PENTIUM_3:
+ case CPU_SUBTYPE_PENTIUM_3_M:
+ case CPU_SUBTYPE_PENTIUM_3_XEON:
+ pMod->enmCpu = KCPU_I686;
+ break;
+ case CPU_SUBTYPE_PENTIUM_M:
+ case CPU_SUBTYPE_PENTIUM_4:
+ case CPU_SUBTYPE_PENTIUM_4_M:
+ case CPU_SUBTYPE_XEON:
+ case CPU_SUBTYPE_XEON_MP:
+ pMod->enmCpu = KCPU_P4;
+ break;
+
+ default:
+ /* Hack for kextutil output. */
+ if ( s.Hdr32.cpusubtype == 0
+ && s.Hdr32.filetype == MH_OBJECT)
+ break;
+ return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ case CPU_TYPE_X86_64:
+ pMod->enmArch = KCPUARCH_AMD64;
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
+ {
+ case CPU_SUBTYPE_X86_64_ALL: pMod->enmCpu = KCPU_AMD64_BLEND; break;
+ default:
+ return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ default:
+ return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+ }
+
+ pMod->enmFmt = KLDRFMT_MACHO;
+ switch (s.Hdr32.filetype)
+ {
+ case MH_OBJECT: pMod->enmType = KLDRTYPE_OBJECT; break;
+ case MH_EXECUTE: pMod->enmType = KLDRTYPE_EXECUTABLE_FIXED; break;
+ case MH_DYLIB: pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_BUNDLE: pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_KEXT_BUNDLE:pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_DSYM: pMod->enmType = KLDRTYPE_DEBUG_INFO; break;
+ default:
+ return KLDR_ERR_MACHO_UNSUPPORTED_FILE_TYPE;
+ }
+ pMod->u32Magic = 0; /* set upon success. */
+
+ /* KLDRMODMACHO */
+ pModMachO->pMod = pMod;
+ pModMachO->pvBits = NULL;
+ pModMachO->pvMapping = NULL;
+ pModMachO->fOpenFlags = fOpenFlags;
+ pModMachO->Hdr = s.Hdr64;
+ if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+ || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ pModMachO->Hdr.reserved = 0;
+ pModMachO->LinkAddress = LinkAddress;
+ pModMachO->cbImage = 0;
+ pModMachO->fCanLoad = fCanLoad;
+ pModMachO->fMakeGot = fMakeGot;
+ pModMachO->cbJmpStub = cbJmpStub;
+ pModMachO->uEffFileType = uEffFileType;
+ pModMachO->offSymbols = 0;
+ pModMachO->cSymbols = 0;
+ pModMachO->pvaSymbols = NULL;
+ pModMachO->offStrings = 0;
+ pModMachO->cchStrings = 0;
+ pModMachO->pchStrings = NULL;
+ kHlpMemSet(pModMachO->abImageUuid, 0, sizeof(pModMachO->abImageUuid));
+ pModMachO->GotRVA = NIL_KLDRADDR;
+ pModMachO->JmpStubsRVA = NIL_KLDRADDR;
+ pModMachO->cSections = cSections;
+ pModMachO->paSections = (PKLDRMODMACHOSECT)&pModMachO->aSegments[pModMachO->pMod->cSegments];
+
+ /*
+ * Setup the KLDRMOD segment array.
+ */
+ rc = kldrModMachOParseLoadCommands(pModMachO, (char *)pMod->pszFilename + pMod->cchFilename + 1, cbStringPool);
+ if (rc)
+ return rc;
+
+ /*
+ * We're done.
+ */
+ return 0;
+}
+
+
+/**
+ * Converts, validates and preparses the load commands before we carve
+ * out the module instance.
+ *
+ * The conversion that's preformed is format endian to host endian. The
+ * preparsing has to do with segment counting, section counting and string pool
+ * sizing.
+ *
+ * Segment are created in two different ways, depending on the file type.
+ *
+ * For object files there is only one segment command without a given segment
+ * name. The sections inside that segment have different segment names and are
+ * not sorted by their segname attribute. We create one segment for each
+ * section, with the segment name being 'segname.sectname' in order to hopefully
+ * keep the names unique. Debug sections does not get segments.
+ *
+ * For non-object files, one kLdr segment is created for each Mach-O segment.
+ * Debug segments is not exposed by kLdr via the kLdr segment table, but via the
+ * debug enumeration callback API.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MACHO_* on failure.
+ * @param pbLoadCommands The load commands to parse.
+ * @param pHdr The header.
+ * @param pRdr The file reader.
+ * @param offImage The image header (FAT fun).
+ * @param pcSegments Where to store the segment count.
+ * @param pcSegments Where to store the section count.
+ * @param pcbStringPool Where to store the string pool size.
+ * @param pfCanLoad Where to store the can-load-image indicator.
+ * @param pLinkAddress Where to store the image link address (i.e. the
+ * lowest segment address).
+ */
+static int kldrModMachOPreParseLoadCommands(KU8 *pbLoadCommands, const mach_header_32_t *pHdr, PKRDR pRdr, KLDRFOFF offImage,
+ KU32 fOpenFlags, KU32 *pcSegments, KU32 *pcSections, KU32 *pcbStringPool,
+ PKBOOL pfCanLoad, PKLDRADDR pLinkAddress, KU8 *puEffFileType)
+{
+ union
+ {
+ KU8 *pb;
+ load_command_t *pLoadCmd;
+ segment_command_32_t *pSeg32;
+ segment_command_64_t *pSeg64;
+ thread_command_t *pThread;
+ symtab_command_t *pSymTab;
+ uuid_command_t *pUuid;
+ } u;
+ const KU64 cbFile = kRdrSize(pRdr) - offImage;
+ KU32 cSegments = 0;
+ KU32 cSections = 0;
+ KSIZE cbStringPool = 0;
+ KU32 cLeft = pHdr->ncmds;
+ KU32 cbLeft = pHdr->sizeofcmds;
+ KU8 *pb = pbLoadCommands;
+ int cSegmentCommands = 0;
+ int cSymbolTabs = 0;
+ int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
+ KU8 uEffFileType = *puEffFileType = pHdr->filetype;
+
+ *pcSegments = 0;
+ *pcSections = 0;
+ *pcbStringPool = 0;
+ *pfCanLoad = K_TRUE;
+ *pLinkAddress = ~(KLDRADDR)0;
+
+ while (cLeft-- > 0)
+ {
+ u.pb = pb;
+
+ /*
+ * Convert and validate command header.
+ */
+ KLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ if (fConvertEndian)
+ {
+ u.pLoadCmd->cmd = K_E2E_U32(u.pLoadCmd->cmd);
+ u.pLoadCmd->cmdsize = K_E2E_U32(u.pLoadCmd->cmdsize);
+ }
+ KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ cbLeft -= u.pLoadCmd->cmdsize;
+ pb += u.pLoadCmd->cmdsize;
+
+ /*
+ * Convert endian if needed, parse and validate the command.
+ */
+ switch (u.pLoadCmd->cmd)
+ {
+ case LC_SEGMENT_32:
+ {
+ segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd;
+ section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
+ section_32_t *pSect = pFirstSect;
+ KU32 cSectionsLeft = pSrcSeg->nsects;
+ KU64 offSect = 0;
+
+ /* Convert and verify the segment. */
+ KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ KLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE, KLDR_ERR_MACHO_BIT_MIX);
+ if (fConvertEndian)
+ {
+ pSrcSeg->vmaddr = K_E2E_U32(pSrcSeg->vmaddr);
+ pSrcSeg->vmsize = K_E2E_U32(pSrcSeg->vmsize);
+ pSrcSeg->fileoff = K_E2E_U32(pSrcSeg->fileoff);
+ pSrcSeg->filesize = K_E2E_U32(pSrcSeg->filesize);
+ pSrcSeg->maxprot = K_E2E_U32(pSrcSeg->maxprot);
+ pSrcSeg->initprot = K_E2E_U32(pSrcSeg->initprot);
+ pSrcSeg->nsects = K_E2E_U32(pSrcSeg->nsects);
+ pSrcSeg->flags = K_E2E_U32(pSrcSeg->flags);
+ }
+
+ /* Validation code shared with the 64-bit variant. */
+ #define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
+ do { \
+ KBOOL fSkipSeg = !kHlpStrComp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \
+ || ( !kHlpStrComp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \
+ && pSrcSeg->vmsize == 0) /* overlapping vmaddr and zero vmsize. */ \
+ || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \
+ \
+ /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \
+ if ( uEffFileType == MH_DSYM \
+ && cSegmentCommands == 0 \
+ && pSrcSeg->segname[0] == '\0') \
+ *puEffFileType = uEffFileType = MH_OBJECT; \
+ \
+ KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
+ || ( pSrcSeg->fileoff <= cbFile \
+ && (KU64)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
+ KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+ KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
+ || (fSkipSeg && !kHlpStrComp(pSrcSeg->segname, "__CTF") /* see above */), \
+ KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+ KLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
+ KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+ KLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \
+ KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+ KLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
+ <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
+ KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+ KLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \
+ || cSegmentCommands == 0 \
+ || ( cSegmentCommands == 1 \
+ && uEffFileType == MH_OBJECT \
+ && pHdr->filetype == MH_DSYM \
+ && fSkipSeg), \
+ KLDR_ERR_MACHO_BAD_OBJECT_FILE); \
+ cSegmentCommands++; \
+ \
+ /* Add the segment, if not object file. */ \
+ if (!fSkipSeg && uEffFileType != MH_OBJECT) \
+ { \
+ cbStringPool += kHlpStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \
+ cSegments++; \
+ if (cSegments == 1) /* The link address is set by the first segment. */ \
+ *pLinkAddress = pSrcSeg->vmaddr; \
+ } \
+ } while (0)
+
+ VALIDATE_AND_ADD_SEGMENT(32);
+
+ /*
+ * Convert, validate and parse the sections.
+ */
+ cSectionsLeft = pSrcSeg->nsects;
+ pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1);
+ while (cSectionsLeft-- > 0)
+ {
+ if (fConvertEndian)
+ {
+ pSect->addr = K_E2E_U32(pSect->addr);
+ pSect->size = K_E2E_U32(pSect->size);
+ pSect->offset = K_E2E_U32(pSect->offset);
+ pSect->align = K_E2E_U32(pSect->align);
+ pSect->reloff = K_E2E_U32(pSect->reloff);
+ pSect->nreloc = K_E2E_U32(pSect->nreloc);
+ pSect->flags = K_E2E_U32(pSect->flags);
+ pSect->reserved1 = K_E2E_U32(pSect->reserved1);
+ pSect->reserved2 = K_E2E_U32(pSect->reserved2);
+ }
+
+ /* Validation code shared with the 64-bit variant. */
+ #define VALIDATE_AND_ADD_SECTION(a_cBits) \
+ do { \
+ int fFileBits; \
+ \
+ /* validate */ \
+ if (uEffFileType != MH_OBJECT) \
+ KLDRMODMACHO_CHECK_RETURN(!kHlpStrComp(pSect->segname, pSrcSeg->segname),\
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ \
+ switch (pSect->flags & SECTION_TYPE) \
+ { \
+ case S_ZEROFILL: \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+ fFileBits = 0; \
+ break; \
+ case S_REGULAR: \
+ case S_CSTRING_LITERALS: \
+ case S_COALESCED: \
+ case S_4BYTE_LITERALS: \
+ case S_8BYTE_LITERALS: \
+ case S_16BYTE_LITERALS: \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_SYMBOL_STUBS: \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+ /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
+ KLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, KLDR_ERR_MACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_NON_LAZY_SYMBOL_POINTERS: \
+ case S_LAZY_SYMBOL_POINTERS: \
+ case S_LAZY_DYLIB_SYMBOL_POINTERS: \
+ /* (reserved 1 = is indirect symbol table index) */ \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+ *pfCanLoad = K_FALSE; \
+ fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \
+ break; \
+ \
+ case S_MOD_INIT_FUNC_POINTERS: \
+ /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \
+ KLDRMODMACHO_CHECK_RETURN(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO, \
+ KLDR_ERR_MACHO_UNSUPPORTED_INIT_SECTION); \
+ /* Falls through. */ \
+ case S_MOD_TERM_FUNC_POINTERS: \
+ /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
+ KLDRMODMACHO_CHECK_RETURN(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO, \
+ KLDR_ERR_MACHO_UNSUPPORTED_TERM_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; /* ignored */ \
+ \
+ case S_LITERAL_POINTERS: \
+ case S_DTRACE_DOF: \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_INTERPOSING: \
+ case S_GB_ZEROFILL: \
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_SECTION); \
+ \
+ default: \
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNKNOWN_SECTION); \
+ } \
+ KLDRMODMACHO_CHECK_RETURN(!(pSect->flags & ~( S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS \
+ | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE \
+ | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC \
+ | S_ATTR_LOC_RELOC | SECTION_TYPE)), \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
+ KLDR_ERR_MACHO_MIXED_DEBUG_SECTION_FLAGS); \
+ \
+ KLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
+ || !kHlpStrComp(pSrcSeg->segname, "__CTF") /* see above */, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \
+ /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14. Just adjust the alignment down. */ \
+ if ( ((K_BIT32(pSect->align) - KU32_C(1)) & pSect->addr) \
+ && pSect->align == 4 \
+ && kHlpStrComp(pSect->sectname, "__unwind_info") == 0) \
+ pSect->align = 2; \
+ KLDRMODMACHO_CHECK_RETURN(!((K_BIT32(pSect->align) - KU32_C(1)) & pSect->addr), \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN(!((K_BIT32(pSect->align) - KU32_C(1)) & pSrcSeg->vmaddr), \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ \
+ /* Adjust the section offset before we check file offset. */ \
+ offSect = (offSect + K_BIT64(pSect->align) - KU64_C(1)) & ~(K_BIT64(pSect->align) - KU64_C(1)); \
+ if (pSect->addr) \
+ { \
+ KLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, KLDR_ERR_MACHO_BAD_SECTION); \
+ if (offSect < pSect->addr - pSrcSeg->vmaddr) \
+ offSect = pSect->addr - pSrcSeg->vmaddr; \
+ } \
+ \
+ if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \
+ fFileBits = 0; \
+ if (fFileBits) \
+ { \
+ if (uEffFileType != MH_OBJECT) \
+ { \
+ KLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
+ KLDR_ERR_MACHO_NON_CONT_SEG_BITS); \
+ KLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ } \
+ KLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN((KU64)pSect->offset + pSect->size <= cbFile, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ } \
+ else \
+ KLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, KLDR_ERR_MACHO_BAD_SECTION); \
+ \
+ if (!pSect->nreloc) \
+ KLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ else \
+ { \
+ KLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ KLDRMODMACHO_CHECK_RETURN( (KU64)pSect->reloff \
+ + (KLDRFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
+ <= cbFile, \
+ KLDR_ERR_MACHO_BAD_SECTION); \
+ } \
+ \
+ /* Validate against file type (pointless?) and count the section, for object files add segment. */ \
+ switch (uEffFileType) \
+ { \
+ case MH_OBJECT: \
+ if ( !(pSect->flags & S_ATTR_DEBUG) \
+ && kHlpStrComp(pSect->segname, "__DWARF")) \
+ { \
+ cbStringPool += kHlpStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
+ cbStringPool += kHlpStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
+ cSegments++; \
+ if (cSegments == 1) /* The link address is set by the first segment. */ \
+ *pLinkAddress = pSect->addr; \
+ } \
+ /* Falls through. */ \
+ case MH_EXECUTE: \
+ case MH_DYLIB: \
+ case MH_BUNDLE: \
+ case MH_DSYM: \
+ case MH_KEXT_BUNDLE: \
+ cSections++; \
+ break; \
+ default: \
+ KLDRMODMACHO_FAILED_RETURN(KERR_INVALID_PARAMETER); \
+ } \
+ \
+ /* Advance the section offset, since we're also aligning it. */ \
+ offSect += pSect->size; \
+ } while (0) /* VALIDATE_AND_ADD_SECTION */
+
+ VALIDATE_AND_ADD_SECTION(32);
+
+ /* next */
+ pSect++;
+ }
+ break;
+ }
+
+ case LC_SEGMENT_64:
+ {
+ segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd;
+ section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
+ section_64_t *pSect = pFirstSect;
+ KU32 cSectionsLeft = pSrcSeg->nsects;
+ KU64 offSect = 0;
+
+ /* Convert and verify the segment. */
+ KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ KLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE, KLDR_ERR_MACHO_BIT_MIX);
+ if (fConvertEndian)
+ {
+ pSrcSeg->vmaddr = K_E2E_U64(pSrcSeg->vmaddr);
+ pSrcSeg->vmsize = K_E2E_U64(pSrcSeg->vmsize);
+ pSrcSeg->fileoff = K_E2E_U64(pSrcSeg->fileoff);
+ pSrcSeg->filesize = K_E2E_U64(pSrcSeg->filesize);
+ pSrcSeg->maxprot = K_E2E_U32(pSrcSeg->maxprot);
+ pSrcSeg->initprot = K_E2E_U32(pSrcSeg->initprot);
+ pSrcSeg->nsects = K_E2E_U32(pSrcSeg->nsects);
+ pSrcSeg->flags = K_E2E_U32(pSrcSeg->flags);
+ }
+
+ VALIDATE_AND_ADD_SEGMENT(64);
+
+ /*
+ * Convert, validate and parse the sections.
+ */
+ while (cSectionsLeft-- > 0)
+ {
+ if (fConvertEndian)
+ {
+ pSect->addr = K_E2E_U64(pSect->addr);
+ pSect->size = K_E2E_U64(pSect->size);
+ pSect->offset = K_E2E_U32(pSect->offset);
+ pSect->align = K_E2E_U32(pSect->align);
+ pSect->reloff = K_E2E_U32(pSect->reloff);
+ pSect->nreloc = K_E2E_U32(pSect->nreloc);
+ pSect->flags = K_E2E_U32(pSect->flags);
+ pSect->reserved1 = K_E2E_U32(pSect->reserved1);
+ pSect->reserved2 = K_E2E_U32(pSect->reserved2);
+ }
+
+ VALIDATE_AND_ADD_SECTION(64);
+
+ /* next */
+ pSect++;
+ }
+ break;
+ } /* LC_SEGMENT_64 */
+
+
+ case LC_SYMTAB:
+ {
+ KSIZE cbSym;
+ if (fConvertEndian)
+ {
+ u.pSymTab->symoff = K_E2E_U32(u.pSymTab->symoff);
+ u.pSymTab->nsyms = K_E2E_U32(u.pSymTab->nsyms);
+ u.pSymTab->stroff = K_E2E_U32(u.pSymTab->stroff);
+ u.pSymTab->strsize = K_E2E_U32(u.pSymTab->strsize);
+ }
+
+ /* verify */
+ cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(macho_nlist_32_t)
+ : sizeof(macho_nlist_64_t);
+ if ( u.pSymTab->symoff >= cbFile
+ || (KU64)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ if ( u.pSymTab->stroff >= cbFile
+ || (KU64)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+
+ /* only one string in objects, please. */
+ cSymbolTabs++;
+ if ( uEffFileType == MH_OBJECT
+ && cSymbolTabs != 1)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_OBJECT_FILE);
+ break;
+ }
+
+ case LC_DYSYMTAB:
+ /** @todo deal with this! */
+ break;
+
+ case LC_THREAD:
+ case LC_UNIXTHREAD:
+ {
+ KU32 *pu32 = (KU32 *)(u.pb + sizeof(load_command_t));
+ KU32 cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(KU32);
+ while (cItemsLeft)
+ {
+ /* convert & verify header items ([0] == flavor, [1] == KU32 count). */
+ if (cItemsLeft < 2)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ if (fConvertEndian)
+ {
+ pu32[0] = K_E2E_U32(pu32[0]);
+ pu32[1] = K_E2E_U32(pu32[1]);
+ }
+ if (pu32[1] + 2 > cItemsLeft)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+
+ /* convert & verify according to flavor. */
+ switch (pu32[0])
+ {
+ /** @todo */
+ default:
+ break;
+ }
+
+ /* next */
+ cItemsLeft -= pu32[1] + 2;
+ pu32 += pu32[1] + 2;
+ }
+ break;
+ }
+
+ case LC_UUID:
+ if (u.pUuid->cmdsize != sizeof(uuid_command_t))
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ /** @todo Check anything here need converting? */
+ break;
+
+ case LC_CODE_SIGNATURE:
+ if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ break;
+
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ if (u.pUuid->cmdsize != sizeof(version_min_command_t))
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+ break;
+
+ case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */
+ case LC_DATA_IN_CODE: /* Ignore */
+ case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */
+ /** @todo valid command size. */
+ break;
+
+ case LC_FUNCTION_STARTS: /** @todo dylib++ */
+ /* Ignore for now. */
+ break;
+ case LC_ID_DYLIB: /** @todo dylib */
+ case LC_LOAD_DYLIB: /** @todo dylib */
+ case LC_LOAD_DYLINKER: /** @todo dylib */
+ case LC_TWOLEVEL_HINTS: /** @todo dylib */
+ case LC_LOAD_WEAK_DYLIB: /** @todo dylib */
+ case LC_ID_DYLINKER: /** @todo dylib */
+ case LC_RPATH: /** @todo dylib */
+ case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */
+ case LC_REEXPORT_DYLIB: /** @todo dylib */
+ case LC_DYLD_INFO: /** @todo dylib */
+ case LC_DYLD_INFO_ONLY: /** @todo dylib */
+ case LC_LOAD_UPWARD_DYLIB: /** @todo dylib */
+ case LC_DYLD_ENVIRONMENT: /** @todo dylib */
+ case LC_MAIN: /** @todo parse this and find and entry point or smth. */
+ /** @todo valid command size. */
+ if (!(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO))
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND);
+ *pfCanLoad = K_FALSE;
+ break;
+
+ case LC_LOADFVMLIB:
+ case LC_IDFVMLIB:
+ case LC_IDENT:
+ case LC_FVMFILE:
+ case LC_PREPAGE:
+ case LC_PREBOUND_DYLIB:
+ case LC_ROUTINES:
+ case LC_ROUTINES_64:
+ case LC_SUB_FRAMEWORK:
+ case LC_SUB_UMBRELLA:
+ case LC_SUB_CLIENT:
+ case LC_SUB_LIBRARY:
+ case LC_PREBIND_CKSUM:
+ case LC_SYMSEG:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND);
+
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNKNOWN_LOAD_COMMAND);
+ }
+ }
+
+ /* be strict. */
+ if (cbLeft)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+
+ switch (uEffFileType)
+ {
+ case MH_OBJECT:
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DSYM:
+ case MH_KEXT_BUNDLE:
+ if (!cSegments)
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_OBJECT_FILE);
+ break;
+ }
+
+ *pcSegments = cSegments;
+ *pcSections = cSections;
+ *pcbStringPool = (KU32)cbStringPool;
+
+ return 0;
+}
+
+
+/**
+ * Parses the load commands after we've carved out the module instance.
+ *
+ * This fills in the segment table and perhaps some other properties.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MACHO_* on failure.
+ * @param pModMachO The module.
+ * @param pbStringPool The string pool
+ * @param cbStringPool The size of the string pool.
+ */
+static int kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, KU32 cbStringPool)
+{
+ union
+ {
+ const KU8 *pb;
+ const load_command_t *pLoadCmd;
+ const segment_command_32_t *pSeg32;
+ const segment_command_64_t *pSeg64;
+ const symtab_command_t *pSymTab;
+ const uuid_command_t *pUuid;
+ } u;
+ KU32 cLeft = pModMachO->Hdr.ncmds;
+ KU32 cbLeft = pModMachO->Hdr.sizeofcmds;
+ const KU8 *pb = pModMachO->pbLoadCommands;
+ PKLDRSEG pDstSeg = &pModMachO->pMod->aSegments[0];
+ PKLDRMODMACHOSEG pSegExtra = &pModMachO->aSegments[0];
+ PKLDRMODMACHOSECT pSectExtra = pModMachO->paSections;
+ const KU32 cSegments = pModMachO->pMod->cSegments;
+ PKLDRSEG pSegItr;
+ K_NOREF(cbStringPool);
+
+ while (cLeft-- > 0)
+ {
+ u.pb = pb;
+ cbLeft -= u.pLoadCmd->cmdsize;
+ pb += u.pLoadCmd->cmdsize;
+
+ /*
+ * Convert endian if needed, parse and validate the command.
+ */
+ switch (u.pLoadCmd->cmd)
+ {
+ case LC_SEGMENT_32:
+ {
+ const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd;
+ section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
+ section_32_t *pSect = pFirstSect;
+ KU32 cSectionsLeft = pSrcSeg->nsects;
+
+ /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */
+ #define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \
+ do { \
+ pDstSeg->pvUser = NULL; \
+ pDstSeg->pchName = pbStringPool; \
+ pDstSeg->cchName = (KU32)kHlpStrNLen(a_achName1, sizeof(a_achName1)); \
+ kHlpMemCopy(pbStringPool, a_achName1, pDstSeg->cchName); \
+ pbStringPool += pDstSeg->cchName; \
+ if (a_fObjFile) \
+ { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
+ KSIZE cchName2 = kHlpStrNLen(a_achName2, sizeof(a_achName2)); \
+ *pbStringPool++ = '.'; \
+ kHlpMemCopy(pbStringPool, a_achName2, cchName2); \
+ pbStringPool += cchName2; \
+ pDstSeg->cchName += (KU32)cchName2; \
+ } \
+ *pbStringPool++ = '\0'; \
+ pDstSeg->SelFlat = 0; \
+ pDstSeg->Sel16bit = 0; \
+ pDstSeg->fFlags = 0; \
+ pDstSeg->enmProt = KPROT_EXECUTE_WRITECOPY; /** @todo fixme! */ \
+ pDstSeg->cb = (a_cbSeg); \
+ pDstSeg->Alignment = 1; /* updated while parsing sections. */ \
+ pDstSeg->LinkAddress = (a_SegAddr); \
+ if (a_fFileBits) \
+ { \
+ pDstSeg->offFile = (KLDRFOFF)((a_offFile) + pModMachO->offImage); \
+ pDstSeg->cbFile = (KLDRFOFF)(a_cbFile); \
+ } \
+ else \
+ { \
+ pDstSeg->offFile = -1; \
+ pDstSeg->cbFile = -1; \
+ } \
+ pDstSeg->RVA = (a_SegAddr) - pModMachO->LinkAddress; \
+ pDstSeg->cbMapped = 0; \
+ pDstSeg->MapAddress = 0; \
+ \
+ pSegExtra->iOrgSegNo = (KU32)(pSegExtra - &pModMachO->aSegments[0]); \
+ pSegExtra->cSections = 0; \
+ pSegExtra->paSections = pSectExtra; \
+ } while (0)
+
+ /* Closes the new segment - part of NEW_SEGMENT. */
+ #define CLOSE_SEGMENT() \
+ do { \
+ pSegExtra->cSections = (KU32)(pSectExtra - pSegExtra->paSections); \
+ pSegExtra++; \
+ pDstSeg++; \
+ } while (0)
+
+
+ /* Shared with the 64-bit variant. */
+ #define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
+ do { \
+ KBOOL fAddSegOuter = K_FALSE; \
+ \
+ /* \
+ * Check that the segment name is unique. We couldn't do that \
+ * in the preparsing stage. \
+ */ \
+ if (pModMachO->uEffFileType != MH_OBJECT) \
+ for (pSegItr = &pModMachO->pMod->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
+ if (!kHlpStrNComp(pSegItr->pchName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_DUPLICATE_SEGMENT_NAME); \
+ \
+ /* \
+ * Create a new segment, unless we're supposed to skip this one. \
+ */ \
+ if ( pModMachO->uEffFileType != MH_OBJECT \
+ && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
+ && kHlpStrComp(pSrcSeg->segname, "__DWARF") \
+ && kHlpStrComp(pSrcSeg->segname, "__CTF") ) \
+ { \
+ NEW_SEGMENT(a_cBits, pSrcSeg->segname, K_FALSE /*a_fObjFile*/, 0 /*a_achName2*/, \
+ pSrcSeg->vmaddr, pSrcSeg->vmsize, \
+ pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
+ fAddSegOuter = K_TRUE; \
+ } \
+ \
+ /* \
+ * Convert and parse the sections. \
+ */ \
+ while (cSectionsLeft-- > 0) \
+ { \
+ /* New segment if object file. */ \
+ KBOOL fAddSegInner = K_FALSE; \
+ if ( pModMachO->uEffFileType == MH_OBJECT \
+ && !(pSect->flags & S_ATTR_DEBUG) \
+ && kHlpStrComp(pSrcSeg->segname, "__DWARF") \
+ && kHlpStrComp(pSrcSeg->segname, "__CTF") ) \
+ { \
+ kHlpAssert(!fAddSegOuter); \
+ NEW_SEGMENT(a_cBits, pSect->segname, K_TRUE /*a_fObjFile*/, pSect->sectname, \
+ pSect->addr, pSect->size, \
+ pSect->offset != 0, pSect->offset, pSect->size); \
+ fAddSegInner = K_TRUE; \
+ } \
+ \
+ /* Section data extract. */ \
+ pSectExtra->cb = pSect->size; \
+ pSectExtra->RVA = pSect->addr - pDstSeg->LinkAddress; \
+ pSectExtra->LinkAddress = pSect->addr; \
+ if (pSect->offset) \
+ pSectExtra->offFile = pSect->offset + pModMachO->offImage; \
+ else \
+ pSectExtra->offFile = -1; \
+ pSectExtra->cFixups = pSect->nreloc; \
+ pSectExtra->paFixups = NULL; \
+ if (pSect->nreloc) \
+ pSectExtra->offFixups = pSect->reloff + pModMachO->offImage; \
+ else \
+ pSectExtra->offFixups = -1; \
+ pSectExtra->fFlags = pSect->flags; \
+ pSectExtra->iSegment = (KU32)(pSegExtra - &pModMachO->aSegments[0]); \
+ pSectExtra->pvMachoSection = pSect; \
+ \
+ /* Update the segment alignment, if we're not skipping it. */ \
+ if ( (fAddSegOuter || fAddSegInner) \
+ && pDstSeg->Alignment < ((KLDRADDR)1 << pSect->align)) \
+ pDstSeg->Alignment = (KLDRADDR)1 << pSect->align; \
+ \
+ /* Next section, and if object file next segment. */ \
+ pSectExtra++; \
+ pSect++; \
+ if (fAddSegInner) \
+ CLOSE_SEGMENT(); \
+ } \
+ \
+ /* Close the segment and advance. */ \
+ if (fAddSegOuter) \
+ CLOSE_SEGMENT(); \
+ } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
+
+ ADD_SEGMENT_AND_ITS_SECTIONS(32);
+ break;
+ }
+
+ case LC_SEGMENT_64:
+ {
+ const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd;
+ section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
+ section_64_t *pSect = pFirstSect;
+ KU32 cSectionsLeft = pSrcSeg->nsects;
+
+ ADD_SEGMENT_AND_ITS_SECTIONS(64);
+ break;
+ }
+
+ case LC_SYMTAB:
+ switch (pModMachO->uEffFileType)
+ {
+ case MH_OBJECT:
+ case MH_EXECUTE:
+ case MH_DYLIB: /** @todo ??? */
+ case MH_BUNDLE: /** @todo ??? */
+ case MH_DSYM:
+ case MH_KEXT_BUNDLE:
+ pModMachO->offSymbols = u.pSymTab->symoff + pModMachO->offImage;
+ pModMachO->cSymbols = u.pSymTab->nsyms;
+ pModMachO->offStrings = u.pSymTab->stroff + pModMachO->offImage;
+ pModMachO->cchStrings = u.pSymTab->strsize;
+ break;
+ }
+ break;
+
+ case LC_UUID:
+ kHlpMemCopy(pModMachO->abImageUuid, u.pUuid->uuid, sizeof(pModMachO->abImageUuid));
+ break;
+
+ default:
+ break;
+ } /* command switch */
+ } /* while more commands */
+
+ kHlpAssert(pDstSeg == &pModMachO->pMod->aSegments[cSegments - pModMachO->fMakeGot]);
+
+ /*
+ * Adjust mapping addresses calculating the image size.
+ */
+ {
+ KBOOL fLoadLinkEdit = K_FALSE;
+ PKLDRMODMACHOSECT pSectExtraItr;
+ KLDRADDR uNextRVA = 0;
+ KLDRADDR cb;
+ KU32 cSegmentsToAdjust = cSegments - pModMachO->fMakeGot;
+ KU32 c;
+
+ for (;;)
+ {
+ /* Check if there is __DWARF segment at the end and make sure it's left
+ out of the RVA negotiations and image loading. */
+ if ( cSegmentsToAdjust > 0
+ && !kHlpStrComp(pModMachO->pMod->aSegments[cSegmentsToAdjust - 1].pchName, "__DWARF"))
+ {
+ cSegmentsToAdjust--;
+ pModMachO->pMod->aSegments[cSegmentsToAdjust].RVA = NIL_KLDRADDR;
+ pModMachO->pMod->aSegments[cSegmentsToAdjust].cbMapped = 0;
+ continue;
+ }
+
+ /* If we're skipping the __LINKEDIT segment, check for it and adjust
+ the number of segments we'll be messing with here. ASSUMES it's
+ last (by now anyway). */
+ if ( !fLoadLinkEdit
+ && cSegmentsToAdjust > 0
+ && !kHlpStrComp(pModMachO->pMod->aSegments[cSegmentsToAdjust - 1].pchName, "__LINKEDIT"))
+ {
+ cSegmentsToAdjust--;
+ pModMachO->pMod->aSegments[cSegmentsToAdjust].RVA = NIL_KLDRADDR;
+ pModMachO->pMod->aSegments[cSegmentsToAdjust].cbMapped = 0;
+ continue;
+ }
+ break;
+ }
+
+ /* Adjust RVAs. */
+ c = cSegmentsToAdjust;
+ for (pDstSeg = &pModMachO->pMod->aSegments[0]; c-- > 0; pDstSeg++)
+ {
+ cb = pDstSeg->RVA - uNextRVA;
+ if (cb >= 0x00100000) /* 1MB */
+ {
+ pDstSeg->RVA = uNextRVA;
+ pModMachO->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
+ }
+ uNextRVA = pDstSeg->RVA + KLDR_ALIGN_ADDR(pDstSeg->cb, pDstSeg->Alignment);
+ }
+
+ /* Calculate the cbMapping members. */
+ c = cSegmentsToAdjust;
+ for (pDstSeg = &pModMachO->pMod->aSegments[0]; c-- > 1; pDstSeg++)
+ {
+
+ cb = pDstSeg[1].RVA - pDstSeg->RVA;
+ pDstSeg->cbMapped = (KSIZE)cb == cb ? (KSIZE)cb : KSIZE_MAX;
+ }
+
+ cb = KLDR_ALIGN_ADDR(pDstSeg->cb, pDstSeg->Alignment);
+ pDstSeg->cbMapped = (KSIZE)cb == cb ? (KSIZE)cb : KSIZE_MAX;
+
+ /* Set the image size. */
+ pModMachO->cbImage = pDstSeg->RVA + cb;
+
+ /* Fixup the section RVAs (internal). */
+ c = cSegmentsToAdjust;
+ uNextRVA = pModMachO->cbImage;
+ pDstSeg = &pModMachO->pMod->aSegments[0];
+ for (pSectExtraItr = pModMachO->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
+ {
+ if (pSectExtraItr->iSegment < c)
+ pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].RVA;
+ else
+ {
+ pSectExtraItr->RVA = uNextRVA;
+ uNextRVA += KLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
+ }
+ }
+ }
+
+ /*
+ * Make the GOT segment if necessary.
+ */
+ if (pModMachO->fMakeGot)
+ {
+ KU32 cbPtr = ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ ? sizeof(KU32)
+ : sizeof(KU64);
+ KU32 cbGot = pModMachO->cSymbols * cbPtr;
+ KU32 cbJmpStubs;
+
+ pModMachO->GotRVA = pModMachO->cbImage;
+
+ if (pModMachO->cbJmpStub)
+ {
+ cbGot = K_ALIGN_Z(cbGot, 64);
+ pModMachO->JmpStubsRVA = pModMachO->GotRVA + cbGot;
+ cbJmpStubs = pModMachO->cbJmpStub * pModMachO->cSymbols;
+ }
+ else
+ {
+ pModMachO->JmpStubsRVA = NIL_KLDRADDR;
+ cbJmpStubs = 0;
+ }
+
+ pDstSeg = &pModMachO->pMod->aSegments[cSegments - 1];
+ pDstSeg->pvUser = NULL;
+ pDstSeg->pchName = "GOT";
+ pDstSeg->cchName = 3;
+ pDstSeg->SelFlat = 0;
+ pDstSeg->Sel16bit = 0;
+ pDstSeg->fFlags = 0;
+ pDstSeg->enmProt = KPROT_READONLY;
+ pDstSeg->cb = cbGot + cbJmpStubs;
+ pDstSeg->Alignment = 64;
+ pDstSeg->LinkAddress = pModMachO->LinkAddress + pModMachO->GotRVA;
+ pDstSeg->offFile = -1;
+ pDstSeg->cbFile = -1;
+ pDstSeg->RVA = pModMachO->GotRVA;
+ pDstSeg->cbMapped = (KSIZE)KLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->Alignment);
+ pDstSeg->MapAddress = 0;
+
+ pSegExtra->iOrgSegNo = KU32_MAX;
+ pSegExtra->cSections = 0;
+ pSegExtra->paSections = NULL;
+
+ pModMachO->cbImage += pDstSeg->cbMapped;
+ }
+
+ return 0;
+}
+
+
+/** @copydoc KLDRMODOPS::pfnDestroy */
+static int kldrModMachODestroy(PKLDRMOD pMod)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc = 0;
+ KU32 i, j;
+ KLDRMODMACHO_ASSERT(!pModMachO->pvMapping);
+
+ i = pMod->cSegments;
+ while (i-- > 0)
+ {
+ j = pModMachO->aSegments[i].cSections;
+ while (j-- > 0)
+ {
+ kHlpFree(pModMachO->aSegments[i].paSections[j].paFixups);
+ pModMachO->aSegments[i].paSections[j].paFixups = NULL;
+ }
+ }
+
+ if (pMod->pRdr)
+ {
+ rc = kRdrClose(pMod->pRdr);
+ pMod->pRdr = NULL;
+ }
+ pMod->u32Magic = 0;
+ pMod->pOps = NULL;
+ kHlpFree(pModMachO->pbLoadCommands);
+ pModMachO->pbLoadCommands = NULL;
+ kHlpFree(pModMachO->pchStrings);
+ pModMachO->pchStrings = NULL;
+ kHlpFree(pModMachO->pvaSymbols);
+ pModMachO->pvaSymbols = NULL;
+ kHlpFree(pModMachO);
+ return rc;
+}
+
+
+/**
+ * Gets the right base address.
+ *
+ * @returns 0 on success.
+ * @returns A non-zero status code if the BaseAddress isn't right.
+ * @param pModMachO The interpreter module instance
+ * @param pBaseAddress The base address, IN & OUT. Optional.
+ */
+static int kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pModMachO, PKLDRADDR pBaseAddress)
+{
+ /*
+ * Adjust the base address.
+ */
+ if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP)
+ *pBaseAddress = pModMachO->pMod->aSegments[0].MapAddress;
+ else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK)
+ *pBaseAddress = pModMachO->LinkAddress;
+
+ return 0;
+}
+
+
+/**
+ * Resolves a linker generated symbol.
+ *
+ * The Apple linker generates symbols indicating the start and end of sections
+ * and segments. This function checks for these and returns the right value.
+ *
+ * @returns 0 or KLDR_ERR_SYMBOL_NOT_FOUND.
+ * @param pModMachO The interpreter module instance.
+ * @param pMod The generic module instance.
+ * @param pchSymbol The symbol.
+ * @param cchSymbol The length of the symbol.
+ * @param BaseAddress The base address to apply when calculating the
+ * value.
+ * @param puValue Where to return the symbol value.
+ */
+static int kldrModMachOQueryLinkerSymbol(PKLDRMODMACHO pModMachO, PKLDRMOD pMod, const char *pchSymbol, KSIZE cchSymbol,
+ KLDRADDR BaseAddress, PKLDRADDR puValue)
+{
+ /*
+ * Match possible name prefixes.
+ */
+ static const struct
+ {
+ const char *pszPrefix;
+ KU8 cchPrefix;
+ KBOOL fSection;
+ KBOOL fStart;
+ } s_aPrefixes[] =
+ {
+ { "section$start$", (KU8)sizeof("section$start$") - 1, K_TRUE, K_TRUE },
+ { "section$end$", (KU8)sizeof("section$end$") - 1, K_TRUE, K_FALSE},
+ { "segment$start$", (KU8)sizeof("segment$start$") - 1, K_FALSE, K_TRUE },
+ { "segment$end$", (KU8)sizeof("segment$end$") - 1, K_FALSE, K_FALSE},
+ };
+ KSIZE cchSectName = 0;
+ const char *pchSectName = "";
+ KSIZE cchSegName = 0;
+ const char *pchSegName = NULL;
+ KU32 iPrefix = K_ELEMENTS(s_aPrefixes) - 1;
+ KU32 iSeg;
+ KLDRADDR uValue;
+
+ for (;;)
+ {
+ KU8 const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
+ if ( cchSymbol > cchPrefix
+ && kHlpStrNComp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
+ {
+ pchSegName = pchSymbol + cchPrefix;
+ cchSegName = cchSymbol - cchPrefix;
+ break;
+ }
+
+ /* next */
+ if (!iPrefix)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ iPrefix--;
+ }
+
+ /*
+ * Split the remainder into segment and section name, if necessary.
+ */
+ if (s_aPrefixes[iPrefix].fSection)
+ {
+ pchSectName = kHlpMemChr(pchSegName, '$', cchSegName);
+ if (!pchSectName)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ cchSegName = pchSectName - pchSegName;
+ pchSectName++;
+ cchSectName = cchSymbol - (pchSectName - pchSymbol);
+ }
+
+ /*
+ * Locate the segment.
+ */
+ if (!pMod->cSegments)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ for (iSeg = 0; iSeg < pMod->cSegments; iSeg++)
+ {
+ if ( pMod->aSegments[iSeg].cchName >= cchSegName
+ && kHlpMemComp(pMod->aSegments[iSeg].pchName, pchSegName, cchSegName) == 0)
+ {
+ section_32_t const *pSect;
+ if ( pMod->aSegments[iSeg].cchName == cchSegName
+ && pModMachO->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
+ break;
+
+ pSect = (section_32_t *)pModMachO->aSegments[iSeg].paSections[0].pvMachoSection;
+ if ( pModMachO->uEffFileType == MH_OBJECT
+ && pMod->aSegments[iSeg].cchName > cchSegName + 1
+ && pMod->aSegments[iSeg].pchName[cchSegName] == '.'
+ && kHlpStrNComp(&pMod->aSegments[iSeg].pchName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
+ && pMod->aSegments[iSeg].cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
+ break;
+ }
+ }
+ if (iSeg >= pMod->cSegments)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ if (!s_aPrefixes[iPrefix].fSection)
+ {
+ /*
+ * Calculate the segment start/end address.
+ */
+ uValue = pMod->aSegments[iSeg].RVA;
+ if (!s_aPrefixes[iPrefix].fStart)
+ uValue += pMod->aSegments[iSeg].cb;
+ }
+ else
+ {
+ /*
+ * Locate the section.
+ */
+ KU32 iSect = pModMachO->aSegments[iSeg].cSections;
+ if (!iSect)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ for (;;)
+ {
+ section_32_t *pSect = (section_32_t *)pModMachO->aSegments[iSeg].paSections[iSect].pvMachoSection;
+ if ( cchSectName <= sizeof(pSect->sectname)
+ && kHlpMemComp(pSect->sectname, pchSectName, cchSectName) == 0
+ && ( cchSectName == sizeof(pSect->sectname)
+ || pSect->sectname[cchSectName] == '\0') )
+ break;
+ /* next */
+ if (!iSect)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ iSect--;
+ }
+
+ uValue = pModMachO->aSegments[iSeg].paSections[iSect].RVA;
+ if (!s_aPrefixes[iPrefix].fStart)
+ uValue += pModMachO->aSegments[iSeg].paSections[iSect].cb;
+ }
+
+ /*
+ * Convert from RVA to load address.
+ */
+ uValue += BaseAddress;
+ if (puValue)
+ *puValue = uValue;
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModQuerySymbol */
+static int kldrModMachOQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc;
+ K_NOREF(pvBits);
+ K_NOREF(pszVersion);
+ K_NOREF(pfnGetForwarder);
+ K_NOREF(pvUser);
+
+ /*
+ * Resolve defaults.
+ */
+ rc = kldrModMachOAdjustBaseAddress(pModMachO, &BaseAddress);
+ if (rc)
+ return rc;
+
+ /*
+ * Refuse segmented requests for now.
+ */
+ KLDRMODMACHO_CHECK_RETURN( !pfKind
+ || (*pfKind & KLDRSYMKIND_REQ_TYPE_MASK) == KLDRSYMKIND_REQ_FLAT,
+ KLDR_ERR_TODO);
+
+ /*
+ * Take action according to file type.
+ */
+ if ( pModMachO->Hdr.filetype == MH_OBJECT
+ || pModMachO->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+ || pModMachO->Hdr.filetype == MH_DYLIB
+ || pModMachO->Hdr.filetype == MH_BUNDLE
+ || pModMachO->Hdr.filetype == MH_DSYM
+ || pModMachO->Hdr.filetype == MH_KEXT_BUNDLE)
+ {
+ rc = kldrModMachOLoadObjSymTab(pModMachO);
+ if (!rc)
+ {
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ rc = kldrModMachODoQuerySymbol32Bit(pModMachO, (macho_nlist_32_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+ pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, iSymbol, pchSymbol,
+ (KU32)cchSymbol, puValue, pfKind);
+ else
+ rc = kldrModMachODoQuerySymbol64Bit(pModMachO, (macho_nlist_64_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+ pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, iSymbol, pchSymbol,
+ (KU32)cchSymbol, puValue, pfKind);
+ }
+
+ /*
+ * Check for link-editor generated symbols and supply what we can.
+ *
+ * As small service to clients that insists on adding a '_' prefix
+ * before querying symbols, we will ignore the prefix.
+ */
+ if ( rc == KLDR_ERR_SYMBOL_NOT_FOUND
+ && cchSymbol > sizeof("section$end$") - 1
+ && ( pchSymbol[0] == 's'
+ || (pchSymbol[1] == 's' && pchSymbol[0] == '_') )
+ && kHlpMemChr(pchSymbol, '$', cchSymbol) )
+ {
+ if (pchSymbol[0] == '_')
+ rc = kldrModMachOQueryLinkerSymbol(pModMachO, pMod, pchSymbol + 1, cchSymbol - 1, BaseAddress, puValue);
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pModMachO, pMod, pchSymbol, cchSymbol, BaseAddress, puValue);
+ }
+ }
+ else
+ rc = KLDR_ERR_TODO;
+
+ return rc;
+}
+
+
+/**
+ * Lookup a symbol in a 32-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModMachO
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
+ * @param iSymbol See kLdrModQuerySymbol.
+ * @param pchSymbol See kLdrModQuerySymbol.
+ * @param cchSymbol See kLdrModQuerySymbol.
+ * @param puValue See kLdrModQuerySymbol.
+ * @param pfKind See kLdrModQuerySymbol.
+ */
+static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms,
+ const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind)
+{
+ /*
+ * Find a valid symbol matching the search criteria.
+ */
+ if (iSymbol == NIL_KLDRMOD_SYM_ORDINAL)
+ {
+ /* simplify validation. */
+ if (cchStrings <= cchSymbol)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ cchStrings -= cchSymbol;
+
+ /* external symbols are usually at the end, so search the other way. */
+ for (iSymbol = cSyms - 1; iSymbol != KU32_MAX; iSymbol--)
+ {
+ const char *psz;
+
+ /* Skip irrellevant and non-public symbols. */
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+ if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+
+ /* get name */
+ if (!paSyms[iSymbol].n_un.n_strx)
+ continue;
+ if ((KU32)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+ continue;
+ psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+ if (psz[cchSymbol])
+ continue;
+ if (kHlpMemComp(psz, pchSymbol, cchSymbol))
+ continue;
+
+ /* match! */
+ break;
+ }
+ if (iSymbol == KU32_MAX)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ if (iSymbol >= cSyms)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ }
+
+ /*
+ * Calc the return values.
+ */
+ if (pfKind)
+ {
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ *pfKind = KLDRSYMKIND_32BIT | KLDRSYMKIND_NO_TYPE;
+ else
+ *pfKind = KLDRSYMKIND_64BIT | KLDRSYMKIND_NO_TYPE;
+ if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+ *pfKind |= KLDRSYMKIND_WEAK;
+ }
+
+ switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSect;
+ KLDRADDR offSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSymbol].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSect = &pModMachO->paSections[paSyms[iSymbol].n_sect - 1];
+
+ offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+ KLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
+ || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+ && offSect == 0U - pSect->RVA
+ && pModMachO->uEffFileType != MH_OBJECT),
+ KLDR_ERR_MACHO_BAD_SYMBOL);
+ if (puValue)
+ *puValue = BaseAddress + pSect->RVA + offSect;
+
+ if ( pfKind
+ && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+ *pfKind = (*pfKind & ~KLDRSYMKIND_TYPE_MASK) | KLDRSYMKIND_CODE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ if (puValue)
+ *puValue = paSyms[iSymbol].n_value;
+ /*if (pfKind)
+ pfKind |= KLDRSYMKIND_ABS;*/
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Lookup a symbol in a 64-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModMachO
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
+ * @param iSymbol See kLdrModQuerySymbol.
+ * @param pchSymbol See kLdrModQuerySymbol.
+ * @param cchSymbol See kLdrModQuerySymbol.
+ * @param puValue See kLdrModQuerySymbol.
+ * @param pfKind See kLdrModQuerySymbol.
+ */
+static int kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms,
+ const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind)
+{
+ /*
+ * Find a valid symbol matching the search criteria.
+ */
+ if (iSymbol == NIL_KLDRMOD_SYM_ORDINAL)
+ {
+ /* simplify validation. */
+ if (cchStrings <= cchSymbol)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ cchStrings -= cchSymbol;
+
+ /* external symbols are usually at the end, so search the other way. */
+ for (iSymbol = cSyms - 1; iSymbol != KU32_MAX; iSymbol--)
+ {
+ const char *psz;
+
+ /* Skip irrellevant and non-public symbols. */
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+ if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+
+ /* get name */
+ if (!paSyms[iSymbol].n_un.n_strx)
+ continue;
+ if ((KU32)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+ continue;
+ psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+ if (psz[cchSymbol])
+ continue;
+ if (kHlpMemComp(psz, pchSymbol, cchSymbol))
+ continue;
+
+ /* match! */
+ break;
+ }
+ if (iSymbol == KU32_MAX)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ if (iSymbol >= cSyms)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ }
+
+ /*
+ * Calc the return values.
+ */
+ if (pfKind)
+ {
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ *pfKind = KLDRSYMKIND_32BIT | KLDRSYMKIND_NO_TYPE;
+ else
+ *pfKind = KLDRSYMKIND_64BIT | KLDRSYMKIND_NO_TYPE;
+ if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+ *pfKind |= KLDRSYMKIND_WEAK;
+ }
+
+ switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSect;
+ KLDRADDR offSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSymbol].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSect = &pModMachO->paSections[paSyms[iSymbol].n_sect - 1];
+
+ offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+ KLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
+ || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+ && offSect == 0U - pSect->RVA
+ && pModMachO->uEffFileType != MH_OBJECT),
+ KLDR_ERR_MACHO_BAD_SYMBOL);
+ if (puValue)
+ *puValue = BaseAddress + pSect->RVA + offSect;
+
+ if ( pfKind
+ && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+ *pfKind = (*pfKind & ~KLDRSYMKIND_TYPE_MASK) | KLDRSYMKIND_CODE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ if (puValue)
+ *puValue = paSyms[iSymbol].n_value;
+ /*if (pfKind)
+ pfKind |= KLDRSYMKIND_ABS;*/
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ }
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModEnumSymbols */
+static int kldrModMachOEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc;
+ K_NOREF(pvBits);
+
+ /*
+ * Resolve defaults.
+ */
+ rc = kldrModMachOAdjustBaseAddress(pModMachO, &BaseAddress);
+ if (rc)
+ return rc;
+
+ /*
+ * Take action according to file type.
+ */
+ if ( pModMachO->Hdr.filetype == MH_OBJECT
+ || pModMachO->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+ || pModMachO->Hdr.filetype == MH_DYLIB
+ || pModMachO->Hdr.filetype == MH_BUNDLE
+ || pModMachO->Hdr.filetype == MH_DSYM
+ || pModMachO->Hdr.filetype == MH_KEXT_BUNDLE)
+ {
+ rc = kldrModMachOLoadObjSymTab(pModMachO);
+ if (!rc)
+ {
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ rc = kldrModMachODoEnumSymbols32Bit(pModMachO, (macho_nlist_32_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+ pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress,
+ fFlags, pfnCallback, pvUser);
+ else
+ rc = kldrModMachODoEnumSymbols64Bit(pModMachO, (macho_nlist_64_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+ pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress,
+ fFlags, pfnCallback, pvUser);
+ }
+ }
+ else
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+
+ return rc;
+}
+
+
+/**
+ * Enum a 32-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModMachO
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
+ * @param fFlags See kLdrModEnumSymbols.
+ * @param pfnCallback See kLdrModEnumSymbols.
+ * @param pvUser See kLdrModEnumSymbols.
+ */
+static int kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms,
+ const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ const KU32 fKindBase = pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT;
+ KU32 iSym;
+ int rc;
+
+ /*
+ * Iterate the symbol table.
+ */
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ KU32 fKind;
+ KLDRADDR uValue;
+ const char *psz;
+ KSIZE cch;
+
+ /* Skip debug symbols and undefined symbols. */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+
+ /* Skip non-public symbols unless they are requested explicitly. */
+ if (!(fFlags & KLDRMOD_ENUM_SYMS_FLAGS_ALL))
+ {
+ if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+ if (!paSyms[iSym].n_un.n_strx)
+ continue;
+ }
+
+ /*
+ * Gather symbol info
+ */
+
+ /* name */
+ KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+ psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+ cch = kHlpStrLen(psz);
+ if (!cch)
+ psz = NULL;
+
+ /* kind & value */
+ fKind = fKindBase;
+ if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ fKind |= KLDRSYMKIND_WEAK;
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSym].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+
+ uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+ KLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
+ || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+ && uValue == 0U - pSect->RVA
+ && pModMachO->uEffFileType != MH_OBJECT),
+ KLDR_ERR_MACHO_BAD_SYMBOL);
+ uValue += BaseAddress + pSect->RVA;
+
+ if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+ fKind |= KLDRSYMKIND_CODE;
+ else
+ fKind |= KLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ uValue = paSyms[iSym].n_value;
+ fKind |= KLDRSYMKIND_NO_TYPE /*KLDRSYMKIND_ABS*/;
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ }
+
+ /*
+ * Do callback.
+ */
+ rc = pfnCallback(pModMachO->pMod, iSym, psz, cch, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+
+/**
+ * Enum a 64-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModMachO
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
+ * @param fFlags See kLdrModEnumSymbols.
+ * @param pfnCallback See kLdrModEnumSymbols.
+ * @param pvUser See kLdrModEnumSymbols.
+ */
+static int kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms,
+ const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ const KU32 fKindBase = pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
+ ? KLDRSYMKIND_64BIT : KLDRSYMKIND_32BIT;
+ KU32 iSym;
+ int rc;
+
+ /*
+ * Iterate the symbol table.
+ */
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ KU32 fKind;
+ KLDRADDR uValue;
+ const char *psz;
+ KSIZE cch;
+
+ /* Skip debug symbols and undefined symbols. */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+
+ /* Skip non-public symbols unless they are requested explicitly. */
+ if (!(fFlags & KLDRMOD_ENUM_SYMS_FLAGS_ALL))
+ {
+ if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+ if (!paSyms[iSym].n_un.n_strx)
+ continue;
+ }
+
+ /*
+ * Gather symbol info
+ */
+
+ /* name */
+ KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+ psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+ cch = kHlpStrLen(psz);
+ if (!cch)
+ psz = NULL;
+
+ /* kind & value */
+ fKind = fKindBase;
+ if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ fKind |= KLDRSYMKIND_WEAK;
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSym].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+
+ uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+ KLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
+ || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+ && uValue == 0U - pSect->RVA
+ && pModMachO->uEffFileType != MH_OBJECT),
+ KLDR_ERR_MACHO_BAD_SYMBOL);
+ uValue += BaseAddress + pSect->RVA;
+
+ if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+ fKind |= KLDRSYMKIND_CODE;
+ else
+ fKind |= KLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ uValue = paSyms[iSym].n_value;
+ fKind |= KLDRSYMKIND_NO_TYPE /*KLDRSYMKIND_ABS*/;
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ }
+
+ /*
+ * Do callback.
+ */
+ rc = pfnCallback(pModMachO->pMod, iSym, psz, cch, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+
+/** @copydoc kLdrModGetImport */
+static int kldrModMachOGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ K_NOREF(pvBits);
+ K_NOREF(iImport);
+ K_NOREF(pszName);
+ K_NOREF(cchName);
+
+ if (pModMachO->Hdr.filetype == MH_OBJECT)
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /* later */
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+}
+
+
+/** @copydoc kLdrModNumberOfImports */
+static KI32 kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ K_NOREF(pvBits);
+
+ if (pModMachO->Hdr.filetype == MH_OBJECT)
+ return 0;
+
+ /* later */
+ return 0;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModMachOGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ /*PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;*/
+ K_NOREF(pMod);
+ K_NOREF(pvBits);
+ K_NOREF(BaseAddress);
+
+ pStackInfo->Address = NIL_KLDRADDR;
+ pStackInfo->LinkAddress = NIL_KLDRADDR;
+ pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
+ /* later */
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModMachOQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+#if 0
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc;
+
+ /*
+ * Resolve base address alias if any.
+ */
+ rc = kldrModMachOBitsAndBaseAddress(pModMachO, NULL, &BaseAddress);
+ if (rc)
+ return rc;
+
+ /*
+ * Convert the address from the header.
+ */
+ *pMainEPAddress = pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint
+ ? BaseAddress + pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint
+ : NIL_KLDRADDR;
+#else
+ *pMainEPAddress = NIL_KLDRADDR;
+ K_NOREF(pvBits);
+ K_NOREF(BaseAddress);
+ K_NOREF(pMod);
+#endif
+ return 0;
+}
+
+
+/** @copydoc kLdrModQueryImageUuid */
+static int kldrModMachOQueryImageUuid(PKLDRMOD pMod, const void *pvBits, void *pvUuid, KSIZE cbUuid)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ K_NOREF(pvBits);
+
+ kHlpMemSet(pvUuid, 0, cbUuid);
+ if (kHlpMemComp(pvUuid, pModMachO->abImageUuid, sizeof(pModMachO->abImageUuid)) == 0)
+ return KLDR_ERR_NO_IMAGE_UUID;
+
+ kHlpMemCopy(pvUuid, pModMachO->abImageUuid, sizeof(pModMachO->abImageUuid));
+ return 0;
+}
+
+
+/** @copydoc kLdrModEnumDbgInfo */
+static int kldrModMachOEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc = 0;
+ KU32 iSect;
+ K_NOREF(pvBits);
+
+ for (iSect = 0; iSect < pModMachO->cSections; iSect++)
+ {
+ section_32_t *pMachOSect = pModMachO->paSections[iSect].pvMachoSection; /* (32-bit & 64-bit starts the same way) */
+ char szTmp[sizeof(pMachOSect->sectname) + 1];
+
+ if (kHlpStrComp(pMachOSect->segname, "__DWARF"))
+ continue;
+
+ kHlpMemCopy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
+ szTmp[sizeof(pMachOSect->sectname)] = '\0';
+
+ rc = pfnCallback(pMod, iSect, KLDRDBGINFOTYPE_DWARF, 0, 0, szTmp,
+ pModMachO->paSections[iSect].offFile,
+ pModMachO->paSections[iSect].LinkAddress,
+ pModMachO->paSections[iSect].cb,
+ NULL, pvUser);
+ if (rc != 0)
+ break;
+ }
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModMachOHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+ /*PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;*/
+
+#if 0
+ /*
+ * Base this entirely on the presence of a debug directory.
+ */
+ if ( pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
+ < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return KLDR_ERR_NO_DEBUG_INFO;
+ return 0;
+#else
+ K_NOREF(pMod);
+ K_NOREF(pvBits);
+ return KLDR_ERR_NO_DEBUG_INFO;
+#endif
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModMachOMap(PKLDRMOD pMod)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ unsigned fFixed;
+ KU32 i;
+ void *pvBase;
+ int rc;
+
+ if (!pModMachO->fCanLoad)
+ return KLDR_ERR_TODO;
+
+ /*
+ * Already mapped?
+ */
+ if (pModMachO->pvMapping)
+ return KLDR_ERR_ALREADY_MAPPED;
+
+ /*
+ * Map it.
+ */
+ /* fixed image? */
+ fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
+ || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
+ if (!fFixed)
+ pvBase = NULL;
+ else
+ {
+ pvBase = (void *)(KUPTR)pMod->aSegments[0].LinkAddress;
+ if ((KUPTR)pvBase != pMod->aSegments[0].LinkAddress)
+ return KLDR_ERR_ADDRESS_OVERFLOW;
+ }
+
+ /* try do the prepare */
+ rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
+ if (rc)
+ return rc;
+
+ /*
+ * Update the segments with their map addresses.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ if (pMod->aSegments[i].RVA != NIL_KLDRADDR)
+ pMod->aSegments[i].MapAddress = (KUPTR)pvBase + (KUPTR)pMod->aSegments[i].RVA;
+ }
+ pModMachO->pvMapping = pvBase;
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModMachOUnmap(PKLDRMOD pMod)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ KU32 i;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (!pModMachO->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Try unmap the image.
+ */
+ rc = kRdrUnmap(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments);
+ if (rc)
+ return rc;
+
+ /*
+ * Update the segments to reflect that they aren't mapped any longer.
+ */
+ pModMachO->pvMapping = NULL;
+ for (i = 0; i < pMod->cSegments; i++)
+ pMod->aSegments[i].MapAddress = 0;
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModMachOAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if ( pvMapping == KLDRMOD_INT_MAP
+ && !pModMachO->pvMapping )
+ return KLDR_ERR_NOT_MAPPED;
+ return 0;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModMachOFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ K_NOREF(pMod);
+ K_NOREF(pvMapping);
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModMachOReload(PKLDRMOD pMod)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if (!pModMachO->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /* the file provider does it all */
+ return kRdrRefresh(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments);
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModMachOFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc, rc2;
+
+ /*
+ * Mapped?
+ */
+ if (!pModMachO->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Before doing anything we'll have to make all pages writable.
+ */
+ rc = kRdrProtect(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
+ if (rc)
+ return rc;
+
+ /*
+ * Resolve imports and apply base relocations.
+ */
+ rc = kldrModMachORelocateBits(pMod, pModMachO->pvMapping, (KUPTR)pModMachO->pvMapping, pModMachO->LinkAddress,
+ pfnGetImport, pvUser);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kRdrProtect(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
+ if (!rc && rc2)
+ rc = rc2;
+ return rc;
+}
+
+
+/**
+ * MH_OBJECT: Resolves undefined symbols (imports).
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModMachOObjDoImports(PKLDRMODMACHO pModMachO, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ const KU32 cSyms = pModMachO->cSymbols;
+ KU32 iSym;
+ int rc;
+
+ /*
+ * Ensure that we've got the symbol table and section fixups handy.
+ */
+ rc = kldrModMachOLoadObjSymTab(pModMachO);
+ if (rc)
+ return rc;
+
+ /*
+ * Iterate the symbol table and resolve undefined symbols.
+ * We currently ignore REFERENCE_TYPE.
+ */
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pModMachO->pvaSymbols;
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ /* skip stabs */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ {
+ const char *pszSymbol;
+ KSIZE cchSymbol;
+ KU32 fKind = KLDRSYMKIND_REQ_FLAT;
+ KLDRADDR Value = NIL_KLDRADDR;
+
+ /** @todo Implement N_REF_TO_WEAK. */
+ KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), KLDR_ERR_TODO);
+
+ /* Get the symbol name. */
+ KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < pModMachO->cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pszSymbol = &pModMachO->pchStrings[paSyms[iSym].n_un.n_strx];
+ cchSymbol = kHlpStrLen(pszSymbol);
+
+ /* Check for linker defined symbols relating to sections and segments. */
+ if ( cchSymbol > sizeof("section$end$") - 1
+ && *pszSymbol == 's'
+ && kHlpMemChr(pszSymbol, '$', cchSymbol))
+ rc = kldrModMachOQueryLinkerSymbol(pModMachO, pModMachO->pMod, pszSymbol, cchSymbol, BaseAddress, &Value);
+ else
+ rc = KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ /* Ask the user for an address to the symbol. */
+ if (rc)
+ rc = pfnGetImport(pModMachO->pMod, NIL_KLDRMOD_IMPORT, iSym, pszSymbol, cchSymbol, NULL,
+ &Value, &fKind, pvUser);
+ if (rc)
+ {
+ /* weak reference? */
+ if (!(paSyms[iSym].n_desc & N_WEAK_REF))
+ break;
+ Value = 0;
+ }
+
+ /* Update the symbol. */
+ paSyms[iSym].n_value = (KU32)Value;
+ if (paSyms[iSym].n_value != Value)
+ {
+ rc = KLDR_ERR_ADDRESS_OVERFLOW;
+ break;
+ }
+ }
+ else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ {
+ /** @todo implement weak symbols. */
+ /*return KLDR_ERR_TODO; - ignored for now. */
+ }
+ }
+ }
+ else
+ {
+ /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
+ macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pModMachO->pvaSymbols;
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ /* skip stabs */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ {
+ const char *pszSymbol;
+ KSIZE cchSymbol;
+ KU32 fKind = KLDRSYMKIND_REQ_FLAT;
+ KLDRADDR Value = NIL_KLDRADDR;
+
+ /** @todo Implement N_REF_TO_WEAK. */
+ KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), KLDR_ERR_TODO);
+
+ /* Get the symbol name. */
+ KLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pModMachO->cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pszSymbol = &pModMachO->pchStrings[paSyms[iSym].n_un.n_strx];
+ cchSymbol = kHlpStrLen(pszSymbol);
+
+ /* Check for linker defined symbols relating to sections and segments. */
+ if ( cchSymbol > sizeof("section$end$") - 1
+ && *pszSymbol == 's'
+ && kHlpMemChr(pszSymbol, '$', cchSymbol))
+ rc = kldrModMachOQueryLinkerSymbol(pModMachO, pModMachO->pMod, pszSymbol, cchSymbol, BaseAddress, &Value);
+ else
+ rc = KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ /* Ask the user for an address to the symbol. */
+ if (rc)
+ rc = pfnGetImport(pModMachO->pMod, NIL_KLDRMOD_IMPORT, iSym, pszSymbol, cchSymbol, NULL,
+ &Value, &fKind, pvUser);
+ if (rc)
+ {
+ /* weak reference? */
+ if (!(paSyms[iSym].n_desc & N_WEAK_REF))
+ break;
+ Value = 0;
+ }
+
+ /* Update the symbol. */
+ paSyms[iSym].n_value = Value;
+ if (paSyms[iSym].n_value != Value)
+ {
+ rc = KLDR_ERR_ADDRESS_OVERFLOW;
+ break;
+ }
+ }
+ else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ {
+ /** @todo implement weak symbols. */
+ /*return KLDR_ERR_TODO; - ignored for now. */
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ * @param pvMapping The mapping to fixup.
+ * @param NewBaseAddress The address to fixup the mapping to.
+ * @param OldBaseAddress The address the mapping is currently fixed up to.
+ */
+static int kldrModMachOObjDoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress)
+{
+ KU32 iSeg;
+ int rc;
+
+
+ /*
+ * Ensure that we've got the symbol table and section fixups handy.
+ */
+ rc = kldrModMachOLoadObjSymTab(pModMachO);
+ if (rc)
+ return rc;
+
+ /*
+ * Iterate over the segments and their sections and apply fixups.
+ */
+ for (iSeg = rc = 0; !rc && iSeg < pModMachO->pMod->cSegments; iSeg++)
+ {
+ PKLDRMODMACHOSEG pSeg = &pModMachO->aSegments[iSeg];
+ KU32 iSect;
+
+ for (iSect = 0; iSect < pSeg->cSections; iSect++)
+ {
+ PKLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
+ KU8 *pbSectBits;
+
+ /* skip sections without fixups. */
+ if (!pSect->cFixups)
+ continue;
+
+ /* lazy load (and endian convert) the fixups. */
+ if (!pSect->paFixups)
+ {
+ rc = kldrModMachOLoadFixups(pModMachO, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
+ if (rc)
+ break;
+ }
+
+ /*
+ * Apply the fixups.
+ */
+ pbSectBits = (KU8 *)pvMapping + (KUPTR)pSect->RVA;
+ if (pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
+ rc = kldrModMachOFixupSectionGeneric32Bit(pModMachO, pbSectBits, pSect,
+ (macho_nlist_32_t *)pModMachO->pvaSymbols,
+ pModMachO->cSymbols, NewBaseAddress);
+ else if ( pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ && pModMachO->Hdr.cputype == CPU_TYPE_X86_64)
+ rc = kldrModMachOFixupSectionAMD64(pModMachO, pbSectBits, pSect,
+ (macho_nlist_64_t *)pModMachO->pvaSymbols,
+ pModMachO->cSymbols, NewBaseAddress);
+ else
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ if (rc)
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Applies generic fixups to a section in an image of the same endian-ness
+ * as the host CPU.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ * @param pbSectBits Pointer to the section bits.
+ * @param pFixupSect The section being fixed up.
+ * @param NewBaseAddress The new base image address.
+ */
+static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+ macho_nlist_32_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress)
+{
+ const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
+ const KU32 cFixups = pFixupSect->cFixups;
+ KSIZE cbSectBits = (KSIZE)pFixupSect->cb;
+ const KU8 *pbSectVirginBits;
+ KU32 iFixup;
+ KLDRPU uFixVirgin;
+ KLDRPU uFix;
+ KLDRADDR SymAddr = ~(KLDRADDR)0;
+ int rc;
+
+ /*
+ * Find the virgin bits.
+ */
+ if (pFixupSect->offFile != -1)
+ {
+ rc = kldrModMachOMapVirginBits(pModMachO);
+ if (rc)
+ return rc;
+ pbSectVirginBits = (const KU8 *)pModMachO->pvBits + pFixupSect->offFile;
+ }
+ else
+ pbSectVirginBits = NULL;
+
+ /*
+ * Iterate the fixups and apply them.
+ */
+ for (iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ union
+ {
+ macho_relocation_info_t r;
+ scattered_relocation_info_t s;
+ } Fixup;
+ Fixup.r = paFixups[iFixup];
+
+ if (!(Fixup.r.r_address & R_SCATTERED))
+ {
+ /* sanity */
+ if ((KU32)Fixup.r.r_address >= cbSectBits)
+ return KLDR_ERR_BAD_FIXUP;
+
+ /* calc fixup addresses. */
+ uFix.pv = pbSectBits + Fixup.r.r_address;
+ uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.r.r_address : 0;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* Calc the linked symbol address / addend. */
+ switch (Fixup.r.r_length)
+ {
+ /** @todo Deal with unaligned accesses on non x86 platforms. */
+ case 0: SymAddr = *uFixVirgin.pi8; break;
+ case 1: SymAddr = *uFixVirgin.pi16; break;
+ case 2: SymAddr = *uFixVirgin.pi32; break;
+ case 3: SymAddr = *uFixVirgin.pi64; break;
+ }
+ if (Fixup.r.r_pcrel)
+ SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
+
+ /* Add symbol / section address. */
+ if (Fixup.r.r_extern)
+ {
+ const macho_nlist_32_t *pSym;
+ if (Fixup.r.r_symbolnum >= cSyms)
+ return KLDR_ERR_BAD_FIXUP;
+ pSym = &paSyms[Fixup.r.r_symbolnum];
+
+ if (pSym->n_type & MACHO_N_STAB)
+ return KLDR_ERR_BAD_FIXUP;
+
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+
+ SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+ }
+ }
+ else if (Fixup.r.r_symbolnum != R_ABS)
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ if (Fixup.r.r_symbolnum > pModMachO->cSections)
+ return KLDR_ERR_BAD_FIXUP;
+ pSymSect = &pModMachO->paSections[Fixup.r.r_symbolnum - 1];
+
+ SymAddr -= pSymSect->LinkAddress;
+ SymAddr += pSymSect->RVA + NewBaseAddress;
+ }
+
+ /* adjust for PC relative */
+ if (Fixup.r.r_pcrel)
+ SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
+ }
+ else
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KU32 iSymSect;
+ KLDRADDR Value;
+
+ /* sanity */
+ KLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
+ if ((KU32)Fixup.s.r_address >= cbSectBits)
+ return KLDR_ERR_BAD_FIXUP;
+
+ /* calc fixup addresses. */
+ uFix.pv = pbSectBits + Fixup.s.r_address;
+ uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.s.r_address : 0;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* The addend is stored in the code. */
+ switch (Fixup.s.r_length)
+ {
+ case 0: SymAddr = *uFixVirgin.pi8; break;
+ case 1: SymAddr = *uFixVirgin.pi16; break;
+ case 2: SymAddr = *uFixVirgin.pi32; break;
+ case 3: SymAddr = *uFixVirgin.pi64; break;
+ }
+ if (Fixup.s.r_pcrel)
+ SymAddr += Fixup.s.r_address;
+ Value = Fixup.s.r_value;
+ SymAddr -= Value; /* (-> addend only) */
+
+ /* Find the section number from the r_value. */
+ pSymSect = NULL;
+ for (iSymSect = 0; iSymSect < pModMachO->cSections; iSymSect++)
+ {
+ KLDRADDR off = Value - pModMachO->paSections[iSymSect].LinkAddress;
+ if (off < pModMachO->paSections[iSymSect].cb)
+ {
+ pSymSect = &pModMachO->paSections[iSymSect];
+ break;
+ }
+ else if (off == pModMachO->paSections[iSymSect].cb) /* edge case */
+ pSymSect = &pModMachO->paSections[iSymSect];
+ }
+ if (!pSymSect)
+ return KLDR_ERR_BAD_FIXUP;
+
+ /* Calc the symbol address. */
+ SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ if (Fixup.s.r_pcrel)
+ SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
+
+ Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
+ Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
+ }
+
+ /*
+ * Write back the fixed up value.
+ */
+ if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
+ {
+ switch (Fixup.r.r_length)
+ {
+ case 0: *uFix.pu8 = (KU8)SymAddr; break;
+ case 1: *uFix.pu16 = (KU16)SymAddr; break;
+ case 2: *uFix.pu32 = (KU32)SymAddr; break;
+ case 3: *uFix.pu64 = (KU64)SymAddr; break;
+ }
+ }
+ else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
+ return KLDR_ERR_MACHO_UNSUPPORTED_FIXUP_TYPE;
+ else
+ return KLDR_ERR_BAD_FIXUP;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Applies AMD64 fixups to a section.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ * @param pbSectBits Pointer to the section bits.
+ * @param pFixupSect The section being fixed up.
+ * @param NewBaseAddress The new base image address.
+ */
+static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+ macho_nlist_64_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress)
+{
+ const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
+ const KU32 cFixups = pFixupSect->cFixups;
+ KSIZE cbSectBits = (KSIZE)pFixupSect->cb;
+ const KU8 *pbSectVirginBits;
+ KU32 iFixup;
+ KLDRPU uFixVirgin;
+ KLDRPU uFix;
+ KLDRADDR SymAddr;
+ int rc;
+
+ /*
+ * Find the virgin bits.
+ */
+ if (pFixupSect->offFile != -1)
+ {
+ rc = kldrModMachOMapVirginBits(pModMachO);
+ if (rc)
+ return rc;
+ pbSectVirginBits = (const KU8 *)pModMachO->pvBits + pFixupSect->offFile;
+ }
+ else
+ pbSectVirginBits = NULL;
+
+ /*
+ * Iterate the fixups and apply them.
+ */
+ for (iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ union
+ {
+ macho_relocation_info_t r;
+ scattered_relocation_info_t s;
+ } Fixup;
+ Fixup.r = paFixups[iFixup];
+
+ /* AMD64 doesn't use scattered fixups. */
+ KLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), KLDR_ERR_BAD_FIXUP);
+
+ /* sanity */
+ KLDRMODMACHO_CHECK_RETURN((KU32)Fixup.r.r_address < cbSectBits, KLDR_ERR_BAD_FIXUP);
+
+ /* calc fixup addresses. */
+ uFix.pv = pbSectBits + Fixup.r.r_address;
+ uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.r.r_address : 0;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* Calc the linked symbol address / addend. */
+ switch (Fixup.r.r_length)
+ {
+ /** @todo Deal with unaligned accesses on non x86 platforms. */
+ case 2: SymAddr = *uFixVirgin.pi32; break;
+ case 3: SymAddr = *uFixVirgin.pi64; break;
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+ }
+
+ /* Add symbol / section address. */
+ if (Fixup.r.r_extern)
+ {
+ const macho_nlist_64_t *pSym;
+
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, KLDR_ERR_BAD_FIXUP);
+ pSym = &paSyms[Fixup.r.r_symbolnum];
+ KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), KLDR_ERR_BAD_FIXUP);
+
+ switch (Fixup.r.r_type)
+ {
+ /* GOT references just needs to have their symbol verified.
+ Later, we'll optimize GOT building here using a parallel sym->got array. */
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ break;
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+ }
+ SymAddr = sizeof(KU64) * Fixup.r.r_symbolnum + pModMachO->GotRVA + NewBaseAddress;
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, KLDR_ERR_BAD_FIXUP);
+ SymAddr -= 4;
+ break;
+
+ /* Verify the r_pcrel field for signed fixups on the way into the default case. */
+ case X86_64_RELOC_BRANCH:
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+ /* Falls through. */
+ default:
+ {
+ /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
+ switch (Fixup.r.r_type)
+ {
+ case X86_64_RELOC_UNSIGNED:
+ KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+ break;
+ case X86_64_RELOC_BRANCH:
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, KLDR_ERR_BAD_FIXUP);
+ SymAddr -= 4;
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ SymAddr -= 4;
+ break;
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+ }
+
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+ SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ /* branch to an external symbol may have to take a short detour. */
+ if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
+ && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
+ - pSym->n_value
+ + KU64_C(0x80000000)
+ >= KU64_C(0xffffff20))
+ SymAddr += pModMachO->cbJmpStub * Fixup.r.r_symbolnum + pModMachO->JmpStubsRVA + NewBaseAddress;
+ else
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_ABS:
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+ }
+ break;
+ }
+
+ /*
+ * This is a weird customer, it will always be follows by an UNSIGNED fixup.
+ */
+ case X86_64_RELOC_SUBTRACTOR:
+ {
+ macho_relocation_info_t Fixup2;
+
+ /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+ SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ SymAddr -= pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+ }
+
+ /* Load the 2nd fixup, check sanity. */
+ iFixup++;
+ KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, KLDR_ERR_BAD_FIXUP);
+ Fixup2 = paFixups[iFixup];
+ KLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
+ && Fixup2.r_length == Fixup.r.r_length
+ && Fixup2.r_type == X86_64_RELOC_UNSIGNED
+ && !Fixup2.r_pcrel
+ && Fixup2.r_symbolnum < cSyms,
+ KLDR_ERR_BAD_FIXUP);
+
+ if (Fixup2.r_extern)
+ {
+ KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, KLDR_ERR_BAD_FIXUP);
+ pSym = &paSyms[Fixup2.r_symbolnum];
+ KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), KLDR_ERR_BAD_FIXUP);
+
+ /* Add it's value to SymAddr. */
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+ SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+ }
+ }
+ else if (Fixup2.r_symbolnum != R_ABS)
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pModMachO->cSections, KLDR_ERR_BAD_FIXUP);
+ pSymSect = &pModMachO->paSections[Fixup2.r_symbolnum - 1];
+ SymAddr += pSymSect->RVA + NewBaseAddress;
+ }
+ else
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* verify against fixup type and make adjustments */
+ switch (Fixup.r.r_type)
+ {
+ case X86_64_RELOC_UNSIGNED:
+ KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+ break;
+ case X86_64_RELOC_BRANCH:
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+ SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+ break;
+ /*case X86_64_RELOC_GOT_LOAD:*/
+ /*case X86_64_RELOC_GOT: */
+ /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+ }
+ if (Fixup.r.r_symbolnum != R_ABS)
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pModMachO->cSections, KLDR_ERR_BAD_FIXUP);
+ pSymSect = &pModMachO->paSections[Fixup.r.r_symbolnum - 1];
+
+ SymAddr -= pSymSect->LinkAddress;
+ SymAddr += pSymSect->RVA + NewBaseAddress;
+ if (Fixup.r.r_pcrel)
+ SymAddr += Fixup.r.r_address;
+ }
+ }
+
+ /* adjust for PC relative */
+ if (Fixup.r.r_pcrel)
+ SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
+
+ /*
+ * Write back the fixed up value.
+ */
+ switch (Fixup.r.r_length)
+ {
+ case 3:
+ *uFix.pu64 = (KU64)SymAddr;
+ break;
+ case 2:
+ KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, KLDR_ERR_BAD_FIXUP);
+ KLDRMODMACHO_CHECK_RETURN((KI32)SymAddr == (KI64)SymAddr, KLDR_ERR_ADDRESS_OVERFLOW);
+ *uFix.pu32 = (KU32)SymAddr;
+ break;
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Loads the symbol table for a MH_OBJECT file.
+ *
+ * The symbol table is pointed to by KLDRMODMACHO::pvaSymbols.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ */
+static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pModMachO)
+{
+ int rc = 0;
+
+ if ( !pModMachO->pvaSymbols
+ && pModMachO->cSymbols)
+ {
+ KSIZE cbSyms;
+ KSIZE cbSym;
+ void *pvSyms;
+ void *pvStrings;
+
+ /* sanity */
+ KLDRMODMACHO_CHECK_RETURN( pModMachO->offSymbols
+ && (!pModMachO->cchStrings || pModMachO->offStrings),
+ KLDR_ERR_MACHO_BAD_OBJECT_FILE);
+
+ /* allocate */
+ cbSym = pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(macho_nlist_32_t)
+ : sizeof(macho_nlist_64_t);
+ cbSyms = pModMachO->cSymbols * cbSym;
+ KLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pModMachO->cSymbols, KLDR_ERR_SIZE_OVERFLOW);
+ rc = KERR_NO_MEMORY;
+ pvSyms = kHlpAlloc(cbSyms);
+ if (pvSyms)
+ {
+ if (pModMachO->cchStrings)
+ pvStrings = kHlpAlloc(pModMachO->cchStrings);
+ else
+ pvStrings = kHlpAllocZ(4);
+ if (pvStrings)
+ {
+ /* read */
+ rc = kRdrRead(pModMachO->pMod->pRdr, pvSyms, cbSyms, pModMachO->offSymbols);
+ if (!rc && pModMachO->cchStrings)
+ rc = kRdrRead(pModMachO->pMod->pRdr, pvStrings, pModMachO->cchStrings, pModMachO->offStrings);
+ if (!rc)
+ {
+ pModMachO->pvaSymbols = pvSyms;
+ pModMachO->pchStrings = (char *)pvStrings;
+
+ /* perform endian conversion? */
+ if (pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ KU32 cLeft = pModMachO->cSymbols;
+ macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
+ while (cLeft-- > 0)
+ {
+ pSym->n_un.n_strx = K_E2E_U32(pSym->n_un.n_strx);
+ pSym->n_desc = (KI16)K_E2E_U16(pSym->n_desc);
+ pSym->n_value = K_E2E_U32(pSym->n_value);
+ pSym++;
+ }
+ }
+ else if (pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ KU32 cLeft = pModMachO->cSymbols;
+ macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
+ while (cLeft-- > 0)
+ {
+ pSym->n_un.n_strx = K_E2E_U32(pSym->n_un.n_strx);
+ pSym->n_desc = (KI16)K_E2E_U16(pSym->n_desc);
+ pSym->n_value = K_E2E_U64(pSym->n_value);
+ pSym++;
+ }
+ }
+
+ return 0;
+ }
+ kHlpFree(pvStrings);
+ }
+ kHlpFree(pvSyms);
+ }
+ }
+ else
+ KLDRMODMACHO_ASSERT(pModMachO->pchStrings || pModMachO->Hdr.filetype == MH_DSYM);
+
+ return rc;
+}
+
+
+/**
+ * Loads the fixups at the given address and performs endian
+ * conversion if necessary.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ * @param offFixups The file offset of the fixups.
+ * @param cFixups The number of fixups to load.
+ * @param ppaFixups Where to put the pointer to the allocated fixup array.
+ */
+static int kldrModMachOLoadFixups(PKLDRMODMACHO pModMachO, KLDRFOFF offFixups, KU32 cFixups, macho_relocation_info_t **ppaFixups)
+{
+ macho_relocation_info_t *paFixups;
+ KSIZE cbFixups;
+ int rc;
+
+ /* allocate the memory. */
+ cbFixups = cFixups * sizeof(*paFixups);
+ KLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, KLDR_ERR_SIZE_OVERFLOW);
+ paFixups = (macho_relocation_info_t *)kHlpAlloc(cbFixups);
+ if (!paFixups)
+ return KERR_NO_MEMORY;
+
+ /* read the fixups. */
+ rc = kRdrRead(pModMachO->pMod->pRdr, paFixups, cbFixups, offFixups);
+ if (!rc)
+ {
+ *ppaFixups = paFixups;
+
+ /* do endian conversion if necessary. */
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ KU32 iFixup;
+ for (iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ KU32 *pu32 = (KU32 *)&paFixups[iFixup];
+ pu32[0] = K_E2E_U32(pu32[0]);
+ pu32[1] = K_E2E_U32(pu32[1]);
+ }
+ }
+ }
+ else
+ kHlpFree(paFixups);
+ return rc;
+}
+
+
+/**
+ * Maps the virgin file bits into memory if not already done.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModMachO The Mach-O module interpreter instance.
+ */
+static int kldrModMachOMapVirginBits(PKLDRMODMACHO pModMachO)
+{
+ int rc = 0;
+ if (!pModMachO->pvBits)
+ rc = kRdrAllMap(pModMachO->pMod->pRdr, &pModMachO->pvBits);
+ return rc;
+}
+
+
+/** @copydoc kLdrModCallInit */
+static int kldrModMachOCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ /* later */
+ K_NOREF(pMod);
+ K_NOREF(pvMapping);
+ K_NOREF(uHandle);
+ return 0;
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModMachOCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ /* later */
+ K_NOREF(pMod);
+ K_NOREF(pvMapping);
+ K_NOREF(uHandle);
+ return 0;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModMachOCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+ /* Relevant for Mach-O? */
+ K_NOREF(pMod);
+ K_NOREF(pvMapping);
+ K_NOREF(uHandle);
+ K_NOREF(fAttachingOrDetaching);
+ return 0;
+}
+
+
+/** @copydoc kLdrModSize */
+static KLDRADDR kldrModMachOSize(PKLDRMOD pMod)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ return pModMachO->cbImage;
+}
+
+
+/** @copydoc kLdrModGetBits */
+static int kldrModMachOGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ KU32 i;
+ int rc;
+
+ if (!pModMachO->fCanLoad)
+ return KLDR_ERR_TODO;
+
+ /*
+ * Zero the entire buffer first to simplify things.
+ */
+ kHlpMemSet(pvBits, 0, (KSIZE)pModMachO->cbImage);
+
+ /*
+ * When possible use the segment table to load the data.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ /* skip it? */
+ if ( pMod->aSegments[i].cbFile == -1
+ || pMod->aSegments[i].offFile == -1
+ || pMod->aSegments[i].LinkAddress == NIL_KLDRADDR
+ || !pMod->aSegments[i].Alignment)
+ continue;
+ rc = kRdrRead(pMod->pRdr,
+ (KU8 *)pvBits + pMod->aSegments[i].RVA,
+ pMod->aSegments[i].cbFile,
+ pMod->aSegments[i].offFile);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Perform relocations.
+ */
+ return kldrModMachORelocateBits(pMod, pvBits, BaseAddress, pModMachO->LinkAddress, pfnGetImport, pvUser);
+}
+
+
+/** @copydoc kLdrModRelocateBits */
+static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+ int rc;
+ K_NOREF(OldBaseAddress);
+
+ /*
+ * Call workers to do the jobs.
+ */
+ if (pModMachO->Hdr.filetype == MH_OBJECT)
+ {
+ rc = kldrModMachOObjDoImports(pModMachO, NewBaseAddress, pfnGetImport, pvUser);
+ if (!rc)
+ rc = kldrModMachOObjDoFixups(pModMachO, pvBits, NewBaseAddress);
+
+ }
+ else
+ rc = KLDR_ERR_TODO;
+ /*{
+ rc = kldrModMachODoFixups(pModMachO, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
+ if (!rc)
+ rc = kldrModMachODoImports(pModMachO, pvBits, pfnGetImport, pvUser);
+ }*/
+
+ /*
+ * Construct the global offset table if necessary, it's always the last
+ * segment when present.
+ */
+ if (!rc && pModMachO->fMakeGot)
+ rc = kldrModMachOMakeGOT(pModMachO, pvBits, NewBaseAddress);
+
+ return rc;
+}
+
+
+/**
+ * Builds the GOT.
+ *
+ * Assumes the symbol table has all external symbols resolved correctly and that
+ * the bits has been cleared up front.
+ */
+static int kldrModMachOMakeGOT(PKLDRMODMACHO pModMachO, void *pvBits, KLDRADDR NewBaseAddress)
+{
+ KU32 iSym = pModMachO->cSymbols;
+ if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pModMachO->pvaSymbols;
+ KU32 *paGOT = (KU32 *)((KU8 *)pvBits + pModMachO->GotRVA);
+ while (iSym-- > 0)
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSymSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+ paGOT[iSym] = (KU32)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ paGOT[iSym] = paSyms[iSym].n_value;
+ break;
+ }
+ }
+ else
+ {
+ macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pModMachO->pvaSymbols;
+ KU64 *paGOT = (KU64 *)((KU8 *)pvBits + pModMachO->GotRVA);
+ while (iSym-- > 0)
+ {
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PKLDRMODMACHOSECT pSymSect;
+ KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+ pSymSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+ paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ paGOT[iSym] = paSyms[iSym].n_value;
+ break;
+ }
+ }
+
+ if (pModMachO->JmpStubsRVA != NIL_KLDRADDR)
+ {
+ iSym = pModMachO->cSymbols;
+ switch (pModMachO->Hdr.cputype)
+ {
+ /*
+ * AMD64 is simple since the GOT and the indirect jmps are parallel
+ * arrays with entries of the same size. The relative offset will
+ * be the the same for each entry, kind of nice. :-)
+ */
+ case CPU_TYPE_X86_64:
+ {
+ KU64 *paJmps = (KU64 *)((KU8 *)pvBits + pModMachO->JmpStubsRVA);
+ KI32 off;
+ KU64 u64Tmpl;
+ union
+ {
+ KU8 ab[8];
+ KU64 u64;
+ } Tmpl;
+
+ /* create the template. */
+ off = (KI32)(pModMachO->GotRVA - (pModMachO->JmpStubsRVA + 6));
+ Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
+ Tmpl.ab[1] = 0x25;
+ Tmpl.ab[2] = off & 0xff;
+ Tmpl.ab[3] = (off >> 8) & 0xff;
+ Tmpl.ab[4] = (off >> 16) & 0xff;
+ Tmpl.ab[5] = (off >> 24) & 0xff;
+ Tmpl.ab[6] = 0xcc;
+ Tmpl.ab[7] = 0xcc;
+ u64Tmpl = Tmpl.u64;
+
+ /* copy the template to every jmp table entry. */
+ while (iSym-- > 0)
+ paJmps[iSym] = u64Tmpl;
+ break;
+ }
+
+ default:
+ KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+ }
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * The Mach-O module interpreter method table.
+ */
+KLDRMODOPS g_kLdrModMachOOps =
+{
+ "Mach-O",
+ NULL,
+ kldrModMachOCreate,
+ kldrModMachODestroy,
+ kldrModMachOQuerySymbol,
+ kldrModMachOEnumSymbols,
+ kldrModMachOGetImport,
+ kldrModMachONumberOfImports,
+ NULL /* can execute one is optional */,
+ kldrModMachOGetStackInfo,
+ kldrModMachOQueryMainEntrypoint,
+ kldrModMachOQueryImageUuid,
+ NULL,
+ NULL,
+ kldrModMachOEnumDbgInfo,
+ kldrModMachOHasDbgInfo,
+ kldrModMachOMap,
+ kldrModMachOUnmap,
+ kldrModMachOAllocTLS,
+ kldrModMachOFreeTLS,
+ kldrModMachOReload,
+ kldrModMachOFixupMapping,
+ kldrModMachOCallInit,
+ kldrModMachOCallTerm,
+ kldrModMachOCallThread,
+ kldrModMachOSize,
+ kldrModMachOGetBits,
+ kldrModMachORelocateBits,
+ NULL, /** @todo mostly done */
+ 42 /* the end */
+};
+
diff --git a/src/lib/kStuff/kLdr/kLdrModNative.c b/src/lib/kStuff/kLdr/kLdrModNative.c
new file mode 100644
index 0000000..01ff4a6
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrModNative.c
@@ -0,0 +1,1206 @@
+/* $Id: kLdrModNative.c 117 2020-03-15 15:23:36Z bird $ */
+/** @file
+ * kLdr - The Module Interpreter for the Native Loaders.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+
+#if K_OS == K_OS_OS2
+# define INCL_BASE
+# include <os2.h>
+
+# ifndef LIBPATHSTRICT
+# define LIBPATHSTRICT 3
+# endif
+ extern APIRET DosQueryHeaderInfo(HMODULE hmod, ULONG ulIndex, PVOID pvBuffer, ULONG cbBuffer, ULONG ulSubFunction);
+# define QHINF_EXEINFO 1 /* NE exeinfo. */
+# define QHINF_READRSRCTBL 2 /* Reads from the resource table. */
+# define QHINF_READFILE 3 /* Reads from the executable file. */
+# define QHINF_LIBPATHLENGTH 4 /* Gets the libpath length. */
+# define QHINF_LIBPATH 5 /* Gets the entire libpath. */
+# define QHINF_FIXENTRY 6 /* NE only */
+# define QHINF_STE 7 /* NE only */
+# define QHINF_MAPSEL 8 /* NE only */
+
+#elif K_OS == K_OS_WINDOWS
+# undef IMAGE_NT_SIGNATURE
+# undef IMAGE_DOS_SIGNATURE
+# include <windows.h>
+# ifndef IMAGE_SCN_TYPE_NOLOAD
+# define IMAGE_SCN_TYPE_NOLOAD 0x00000002
+# endif
+
+/*#elif defined(__NT__)
+#include <winnt.h> */
+
+#elif K_OS == K_OS_DARWIN
+# include <dlfcn.h>
+# include <errno.h>
+
+#else
+# error "port me"
+#endif
+
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRMODNATIVE_STRICT
+ * Define KLDRMODNATIVE_STRICT to enabled strict checks in KLDRMODNATIVE. */
+#define KLDRMODNATIVE_STRICT 1
+
+/** @def KLDRMODNATIVE_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMODNATIVE_STRICT
+# define KLDRMODNATIVE_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRMODNATIVE_ASSERT(expr) do {} while (0)
+#endif
+
+#if K_OS == K_OS_WINDOWS
+/** @def KLDRMODNATIVE_RVA2TYPE
+ * Converts a RVA to a pointer of the specified type.
+ * @param pvBits The bits (image base).
+ * @param uRVA The image relative virtual address.
+ * @param type The type to cast to.
+ */
+# define KLDRMODNATIVE_RVA2TYPE(pvBits, uRVA, type) \
+ ( (type) ((KUPTR)(pvBits) + (uRVA)) )
+
+#endif /* PE OSes */
+
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Instance data for the module interpreter for the Native Loaders.
+ */
+typedef struct KLDRMODNATIVE
+{
+ /** Pointer to the module. (Follows the section table.) */
+ PKLDRMOD pMod;
+ /** Reserved flags. */
+ KU32 f32Reserved;
+ /** The number of imported modules.
+ * If ~(KU32)0 this hasn't been determined yet. */
+ KU32 cImportModules;
+#if K_OS == K_OS_OS2
+ /** The module handle. */
+ HMODULE hmod;
+
+#elif K_OS == K_OS_WINDOWS
+ /** The module handle. */
+ HANDLE hmod;
+ /** Pointer to the NT headers. */
+ const IMAGE_NT_HEADERS *pNtHdrs;
+ /** Pointer to the section header array. */
+ const IMAGE_SECTION_HEADER *paShdrs;
+
+#elif K_OS == K_OS_DARWIN
+ /** The dlopen() handle.*/
+ void *pvMod;
+
+#else
+# error "Port me"
+#endif
+} KLDRMODNATIVE, *PKLDRMODNATIVE;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static KI32 kldrModNativeNumberOfImports(PKLDRMOD pMod, const void *pvBits);
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern KLDRMODOPS g_kLdrModNativeOps;
+
+
+
+/**
+ * Use native loader to load the file opened by pRdr.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ * On failure, a non-zero OS specific error code is returned.
+ * @param pOps Pointer to the registered method table.
+ * @param pRdr The file provider instance to use.
+ * @param offNewHdr The offset of the new header in MZ files. -1 if not found.
+ * @param ppMod Where to store the module instance pointer.
+ */
+static int kldrModNativeCreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch,
+ KLDRFOFF offNewHdr, PPKLDRMOD ppMod)
+{
+ int rc = kLdrModOpenNative(kRdrName(pRdr), fFlags, ppMod);
+ if (rc)
+ return rc;
+ rc = kRdrClose(pRdr);
+ KLDRMODNATIVE_ASSERT(!rc);
+ return 0;
+}
+
+
+/**
+ * Loads a module using the native module loader.
+ *
+ * @returns 0 on success.
+ * @returns non-zero native or kLdr status code on failure.
+ * @param pszFilename The filename or module name to be loaded.
+ * @param fFlags Module open flags, KLDRMOD_OPEN_FLAGS_XXX.
+ * @param ppMod Where to store the module interpreter instance pointer.
+ */
+int kLdrModOpenNative(const char *pszFilename, KU32 fFlags, PPKLDRMOD ppMod)
+{
+ int rc;
+
+ /*
+ * Load the image.
+ */
+#if K_OS == K_OS_OS2
+ HMODULE hmod;
+ kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER);
+
+ rc = DosLoadModule(NULL, 0, (PCSZ)pszFilename, &hmod);
+ if (rc)
+ return rc;
+ rc = kLdrModOpenNativeByHandle((KUPTR)hmod, fFlags, ppMod);
+ if (rc)
+ DosFreeModule(hmod);
+
+#elif K_OS == K_OS_WINDOWS
+ HMODULE hmod;
+ kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER);
+
+ hmod = LoadLibrary(pszFilename);
+ if (!hmod)
+ return GetLastError();
+ rc = kLdrModOpenNativeByHandle((KUPTR)hmod, fFlags, ppMod);
+ if (rc)
+ FreeLibrary(hmod);
+
+#elif K_OS == K_OS_DARWIN
+ void *pvMod;
+ kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER);
+
+ pvMod = dlopen(pszFilename, 0);
+ if (!pvMod)
+ return ENOENT;
+ rc = kLdrModOpenNativeByHandle((KUPTR)pvMod, fFlags, ppMod);
+ if (rc)
+ dlclose(pvMod);
+
+#else
+# error "Port me"
+#endif
+ return rc;
+}
+
+
+/**
+ * Creates a native module interpret for an already module already
+ * loaded by the native loader.
+ *
+ * @returns 0 on success.
+ * @returns non-zero native or kLdr status code on failure.
+ * @param pszFilename The filename or module name to be loaded.
+ * @param fFlags Module open flags, KLDRMOD_OPEN_FLAGS_XXX.
+ * @param ppMod Where to store the module interpreter instance pointer.
+ * @remark This will not make the native loader increment the load count.
+ */
+int kLdrModOpenNativeByHandle(KUPTR uHandle, KU32 fFlags, PPKLDRMOD ppMod)
+{
+ KSIZE cb;
+ KU32 cchFilename;
+ KU32 cSegments;
+ PKLDRMOD pMod;
+ PKLDRMODNATIVE pModNative;
+
+ /*
+ * Delcare variables, parse the module header or whatever and determin the
+ * size of the module instance.
+ */
+#if K_OS == K_OS_OS2
+ char szFilename[CCHMAXPATH];
+ int rc;
+
+ /* get the filename. */
+ rc = DosQueryModuleName((HMODULE)uHandle, sizeof(szFilename), szFilename);
+ if (rc)
+ {
+ KLDRMODNATIVE_ASSERT(rc);
+ szFilename[0] = '\0';
+ }
+
+ /* get the segment count. */
+ /** @todo DosQueryHeaderInfo should be able to get us what we want on OS/2. */
+ cSegments = 1;
+
+#elif K_OS == K_OS_WINDOWS
+ DWORD dw;
+ char szFilename[MAX_PATH];
+ const IMAGE_NT_HEADERS *pNtHdrs;
+ const IMAGE_SECTION_HEADER *paShdrs;
+ const IMAGE_DOS_HEADER *pDosHdr = (const IMAGE_DOS_HEADER *)uHandle;
+ unsigned i;
+
+ /* get the filename. */
+ dw = GetModuleFileName((HANDLE)uHandle, szFilename, sizeof(szFilename));
+ if (dw <= 0)
+ {
+ KLDRMODNATIVE_ASSERT(dw <= 0);
+ szFilename[0] = '\0';
+ }
+
+ /* get the segment count. */
+ if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ pNtHdrs = (const IMAGE_NT_HEADERS *)((KUPTR)pDosHdr + pDosHdr->e_lfanew);
+ else
+ pNtHdrs = (const IMAGE_NT_HEADERS *)pDosHdr;
+ if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
+ {
+ KLDRMODNATIVE_ASSERT(!"bad signature");
+ return KLDR_ERR_UNKNOWN_FORMAT;
+ }
+ if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER))
+ {
+ KLDRMODNATIVE_ASSERT(!"bad optional header size");
+ return KLDR_ERR_UNKNOWN_FORMAT;
+ }
+ cSegments = pNtHdrs->FileHeader.NumberOfSections + 1;
+ paShdrs = (const IMAGE_SECTION_HEADER *)(pNtHdrs + 1);
+
+#elif K_OS == K_OS_DARWIN
+ char szFilename[1] = "";
+ cSegments = 0; /** @todo Figure out the Mac OS X dynamic loader. */
+
+#else
+# error "Port me"
+#endif
+
+ kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER);
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ cchFilename = (KU32)kHlpStrLen(szFilename);
+ cb = K_ALIGN_Z(sizeof(KLDRMODNATIVE), 16)
+ + K_OFFSETOF(KLDRMOD, aSegments[cSegments])
+ + cchFilename + 1;
+ pModNative = (PKLDRMODNATIVE)kHlpAlloc(cb);
+ if (!pModNative)
+ return KERR_NO_MEMORY;
+
+ /* KLDRMOD */
+ pMod = (PKLDRMOD)((KU8 *)pModNative + K_ALIGN_Z(sizeof(KLDRMODNATIVE), 16));
+ pMod->pvData = pModNative;
+ pMod->pRdr = NULL;
+ pMod->pOps = NULL; /* set upon success. */
+ pMod->cSegments = cSegments;
+ pMod->cchFilename = cchFilename;
+ pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments];
+ kHlpMemCopy((char *)pMod->pszFilename, szFilename, cchFilename + 1);
+ pMod->pszName = kHlpGetFilename(pMod->pszFilename); /** @todo get soname */
+ pMod->cchName = cchFilename - (KU32)(pMod->pszName - pMod->pszFilename);
+ pMod->fFlags = fFlags;
+#if defined(__i386__) || defined(__X86__) || defined(_M_IX86)
+ pMod->enmCpu = KCPU_I386;
+ pMod->enmArch = KCPUARCH_X86_32;
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+#elif defined(__X86_64__) || defined(__x86_64__) || defined(__AMD64__) || defined(_M_IX64)
+ pMod->enmCpu = KCPU_K8;
+ pMod->enmArch = KCPUARCH_AMD64;
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+#else
+# error "Port me"
+#endif
+ pMod->enmFmt = KLDRFMT_NATIVE;
+ pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
+ pMod->u32Magic = 0; /* set upon success. */
+
+ /* KLDRMODNATIVE */
+ pModNative->pMod = pMod;
+ pModNative->f32Reserved = 0;
+ pModNative->cImportModules = ~(KU32)0;
+
+ /*
+ * Set native instance data.
+ */
+#if K_OS == K_OS_OS2
+ pModNative->hmod = (HMODULE)uHandle;
+
+ /* just fake a segment for now. */
+ pMod->aSegments[0].pvUser = NULL;
+ pMod->aSegments[0].pchName = "fake";
+ pMod->aSegments[0].cchName = sizeof("fake") - 1;
+ pMod->aSegments[0].enmProt = KPROT_NOACCESS;
+ pMod->aSegments[0].cb = 0;
+ pMod->aSegments[0].Alignment = 0;
+ pMod->aSegments[0].LinkAddress = NIL_KLDRADDR;
+ pMod->aSegments[0].offFile = -1;
+ pMod->aSegments[0].cbFile = 0;
+ pMod->aSegments[0].RVA = NIL_KLDRADDR;
+ pMod->aSegments[0].cbMapped = 0;
+ pMod->aSegments[0].MapAddress = 0;
+
+#elif K_OS == K_OS_WINDOWS
+ pModNative->hmod = (HMODULE)uHandle;
+ pModNative->pNtHdrs = pNtHdrs;
+ pModNative->paShdrs = paShdrs;
+
+ if (pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL)
+ pMod->enmType = !(pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
+ ? KLDRTYPE_SHARED_LIBRARY_RELOCATABLE
+ : KLDRTYPE_SHARED_LIBRARY_FIXED;
+ else
+ pMod->enmType = !(pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
+ ? KLDRTYPE_EXECUTABLE_RELOCATABLE
+ : KLDRTYPE_EXECUTABLE_FIXED;
+
+ /* The implied headers section. */
+ pMod->aSegments[0].pvUser = NULL;
+ pMod->aSegments[0].pchName = "TheHeaders";
+ pMod->aSegments[0].cchName = sizeof("TheHeaders") - 1;
+ pMod->aSegments[0].enmProt = KPROT_READONLY;
+ pMod->aSegments[0].cb = pNtHdrs->OptionalHeader.SizeOfHeaders;
+ pMod->aSegments[0].Alignment = pNtHdrs->OptionalHeader.SectionAlignment;
+ pMod->aSegments[0].LinkAddress = pNtHdrs->OptionalHeader.ImageBase;
+ pMod->aSegments[0].offFile = 0;
+ pMod->aSegments[0].cbFile = pNtHdrs->OptionalHeader.SizeOfHeaders;
+ pMod->aSegments[0].RVA = 0;
+ if (pMod->cSegments > 1)
+ pMod->aSegments[0].cbMapped = paShdrs[0].VirtualAddress;
+ else
+ pMod->aSegments[0].cbMapped = pNtHdrs->OptionalHeader.SizeOfHeaders;
+ pMod->aSegments[0].MapAddress = uHandle;
+
+ /* The section headers. */
+ for (i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++)
+ {
+ const char *pch;
+ KU32 cchSegName;
+
+ /* unused */
+ pMod->aSegments[i + 1].pvUser = NULL;
+
+ /* name */
+ pMod->aSegments[i + 1].pchName = pch = &paShdrs[i].Name[0];
+ cchSegName = IMAGE_SIZEOF_SHORT_NAME;
+ while ( cchSegName > 0
+ && (pch[cchSegName - 1] == ' ' || pch[cchSegName - 1] == '\0'))
+ cchSegName--;
+ pMod->aSegments[i + 1].cchName = cchSegName;
+
+ /* size and addresses */
+ if (!(paShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ {
+ pMod->aSegments[i + 1].cb = paShdrs[i].Misc.VirtualSize;
+ pMod->aSegments[i + 1].RVA = paShdrs[i].VirtualAddress;
+ pMod->aSegments[i + 1].LinkAddress = paShdrs[i].VirtualAddress + pNtHdrs->OptionalHeader.ImageBase;
+ pMod->aSegments[i + 1].MapAddress = paShdrs[i].VirtualAddress + uHandle;
+ pMod->aSegments[i + 1].cbMapped = paShdrs[i].Misc.VirtualSize;
+ if (i + 2 < pMod->cSegments)
+ pMod->aSegments[i + 1].cbMapped = paShdrs[i + 1].VirtualAddress
+ - paShdrs[i].VirtualAddress;
+ }
+ else
+ {
+ pMod->aSegments[i + 1].cb = 0;
+ pMod->aSegments[i + 1].cbMapped = 0;
+ pMod->aSegments[i + 1].LinkAddress = NIL_KLDRADDR;
+ pMod->aSegments[i + 1].RVA = 0;
+ pMod->aSegments[i + 1].MapAddress = 0;
+ }
+
+ /* file location */
+ pMod->aSegments[i + 1].offFile = paShdrs[i].PointerToRawData;
+ pMod->aSegments[i + 1].cbFile = paShdrs[i].SizeOfRawData;
+ if ( pMod->aSegments[i + 1].cbMapped > 0 /* if mapped */
+ && (KLDRSIZE)pMod->aSegments[i + 1].cbFile > pMod->aSegments[i + 1].cbMapped)
+ pMod->aSegments[i + 1].cbFile = (KLDRFOFF)pMod->aSegments[i + 1].cbMapped;
+
+ /* protection */
+ switch ( paShdrs[i].Characteristics
+ & (IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
+ {
+ case 0:
+ case IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_NOACCESS;
+ break;
+ case IMAGE_SCN_MEM_READ:
+ case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_READONLY;
+ break;
+ case IMAGE_SCN_MEM_WRITE:
+ case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_WRITECOPY;
+ break;
+ case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED:
+ case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_READWRITE;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_READ;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_WRITECOPY;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_READWRITE;
+ break;
+ }
+
+ /* alignment. */
+ switch (paShdrs[i].Characteristics & IMAGE_SCN_ALIGN_MASK)
+ {
+ case 0: /* hope this is right... */
+ pMod->aSegments[i + 1].Alignment = pNtHdrs->OptionalHeader.SectionAlignment;
+ break;
+ case IMAGE_SCN_ALIGN_1BYTES: pMod->aSegments[i + 1].Alignment = 1; break;
+ case IMAGE_SCN_ALIGN_2BYTES: pMod->aSegments[i + 1].Alignment = 2; break;
+ case IMAGE_SCN_ALIGN_4BYTES: pMod->aSegments[i + 1].Alignment = 4; break;
+ case IMAGE_SCN_ALIGN_8BYTES: pMod->aSegments[i + 1].Alignment = 8; break;
+ case IMAGE_SCN_ALIGN_16BYTES: pMod->aSegments[i + 1].Alignment = 16; break;
+ case IMAGE_SCN_ALIGN_32BYTES: pMod->aSegments[i + 1].Alignment = 32; break;
+ case IMAGE_SCN_ALIGN_64BYTES: pMod->aSegments[i + 1].Alignment = 64; break;
+ case IMAGE_SCN_ALIGN_128BYTES: pMod->aSegments[i + 1].Alignment = 128; break;
+ case IMAGE_SCN_ALIGN_256BYTES: pMod->aSegments[i + 1].Alignment = 256; break;
+ case IMAGE_SCN_ALIGN_512BYTES: pMod->aSegments[i + 1].Alignment = 512; break;
+ case IMAGE_SCN_ALIGN_1024BYTES: pMod->aSegments[i + 1].Alignment = 1024; break;
+ case IMAGE_SCN_ALIGN_2048BYTES: pMod->aSegments[i + 1].Alignment = 2048; break;
+ case IMAGE_SCN_ALIGN_4096BYTES: pMod->aSegments[i + 1].Alignment = 4096; break;
+ case IMAGE_SCN_ALIGN_8192BYTES: pMod->aSegments[i + 1].Alignment = 8192; break;
+ default: kHlpAssert(0); pMod->aSegments[i + 1].Alignment = 0; break;
+ }
+ }
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Figure out the Mac OS X dynamic loader. */
+
+#else
+# error "Port me"
+#endif
+
+ /*
+ * We're done.
+ */
+ pMod->u32Magic = KLDRMOD_MAGIC;
+ pMod->pOps = &g_kLdrModNativeOps;
+ *ppMod = pMod;
+ return 0;
+}
+
+
+/** @copydoc KLDRMODOPS::pfnDestroy */
+static int kldrModNativeDestroy(PKLDRMOD pMod)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+ int rc;
+
+#if K_OS == K_OS_OS2
+ rc = DosFreeModule(pModNative->hmod);
+
+#elif K_OS == K_OS_WINDOWS
+ if (FreeLibrary(pModNative->hmod))
+ rc = 0;
+ else
+ rc = GetLastError();
+
+#elif K_OS == K_OS_DARWIN
+ dlclose(pModNative->pvMod);
+
+#else
+# error "Port me"
+#endif
+
+ pMod->u32Magic = 0;
+ pMod->pOps = NULL;
+ kHlpFree(pModNative);
+ return rc;
+}
+
+
+/** @copydoc kLdrModQuerySymbol */
+static int kldrModNativeQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+ const char *pszSymbol = pchSymbol;
+#if K_OS == K_OS_OS2
+ APIRET rc;
+ PFN pfn;
+#elif K_OS == K_OS_WINDOWS
+ FARPROC pfn;
+#elif K_OS == K_OS_DARWIN
+ void *pfn;
+#else
+# error "Port me"
+#endif
+
+ /* make stack copy of the symbol if it isn't zero terminated. */
+ if (pszSymbol && pszSymbol[cchSymbol])
+ {
+ char *pszCopy = kHlpAllocA(cchSymbol + 1);
+ kHlpMemCopy(pszCopy, pchSymbol, cchSymbol);
+ pszCopy[cchSymbol] = '\0';
+ pszSymbol = pszCopy;
+ }
+
+#if K_OS == K_OS_OS2
+ if (!pchSymbol && iSymbol >= 0x10000)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ if (puValue)
+ {
+ rc = DosQueryProcAddr(pModNative->hmod,
+ pszSymbol ? 0 : iSymbol,
+ (PCSZ)pszSymbol,
+ &pfn);
+ if (rc)
+ return rc == ERROR_PROC_NOT_FOUND ? KLDR_ERR_SYMBOL_NOT_FOUND : rc;
+ *puValue = (KUPTR)pfn;
+ }
+ if (pfKind)
+ {
+ ULONG ulProcType;
+ rc = DosQueryProcType(pModNative->hmod,
+ pszSymbol ? 0 : iSymbol,
+ (PCSZ)pszSymbol,
+ &ulProcType);
+ if (rc)
+ {
+ if (puValue)
+ *puValue = 0;
+ return rc == ERROR_PROC_NOT_FOUND ? KLDR_ERR_SYMBOL_NOT_FOUND : rc;
+ }
+ *pfKind = (ulProcType & PT_32BIT ? KLDRSYMKIND_32BIT : KLDRSYMKIND_16BIT)
+ | KLDRSYMKIND_NO_TYPE;
+ }
+
+#elif K_OS == K_OS_WINDOWS
+ if (!pszSymbol && iSymbol >= 0x10000)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ pfn = GetProcAddress(pModNative->hmod, pszSymbol ? pszSymbol : (const char *)(KUPTR)iSymbol);
+ if (puValue)
+ *puValue = (KUPTR)pfn;
+ if (pfKind)
+ *pfKind = (pModNative->pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
+ ? KLDRSYMKIND_32BIT : KLDRSYMKIND_16BIT)
+ | KLDRSYMKIND_NO_TYPE;
+
+#elif K_OS == K_OS_DARWIN
+ if (!pszSymbol && iSymbol != NIL_KLDRMOD_SYM_ORDINAL)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ pfn = dlsym(pModNative->pvMod, pszSymbol);
+ if (!pfn)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ if (puValue)
+ *puValue = (KUPTR)pfn;
+ if (pfKind)
+ *pfKind = (sizeof(KUPTR) == 4 ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT)
+ | KLDRSYMKIND_NO_TYPE;
+
+#else
+# error "Port me"
+#endif
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModEnumSymbols */
+static int kldrModNativeEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement export enumeration on OS/2. */
+ (void)pModNative;
+ return ERROR_NOT_SUPPORTED;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ const KU32 *paFunctions;
+ const IMAGE_EXPORT_DIRECTORY *pExpDir;
+ const KU32 *paRVANames;
+ const KU16 *paOrdinals;
+ KU32 iFunction;
+ KU32 cFunctions;
+ KU32 cNames;
+ int rc;
+
+ /*
+ * Make sure we've got mapped bits and resolve any base address aliases.
+ */
+ if ( pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
+ < sizeof(IMAGE_EXPORT_DIRECTORY))
+ return 0; /* no exports to enumerate, return success. */
+
+ pExpDir = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod,
+ pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress,
+ PIMAGE_EXPORT_DIRECTORY);
+
+ /*
+ * Enumerate the ordinal exports.
+ */
+ paRVANames = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pExpDir->AddressOfNames, const KU32 *);
+ paOrdinals = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pExpDir->AddressOfNameOrdinals, const KU16 *);
+ paFunctions = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pExpDir->AddressOfFunctions, const KU32 *);
+ cFunctions = pExpDir->NumberOfFunctions;
+ cNames = pExpDir->NumberOfNames;
+ for (iFunction = 0; iFunction < cFunctions; iFunction++)
+ {
+ unsigned fFoundName;
+ KU32 iName;
+ const KU32 uRVA = paFunctions[iFunction];
+ const KLDRADDR uValue = BaseAddress + uRVA;
+ KU32 fKind = (pModNative->pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
+ ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT)
+ | KLDRSYMKIND_NO_TYPE;
+ if ( uRVA - pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
+ < pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
+ fKind |= KLDRSYMKIND_FORWARDER;
+
+ /*
+ * Any symbol names?
+ */
+ fFoundName = 0;
+ for (iName = 0; iName < cNames; iName++)
+ {
+ const char *pszName;
+ if (paOrdinals[iName] != iFunction)
+ continue;
+ fFoundName = 1;
+
+ pszName = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, paRVANames[iName], const char *);
+ rc = pfnCallback(pMod, iFunction + pExpDir->Base, pszName, strlen(pszName), NULL,
+ uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * If no names, call once with the ordinal only.
+ */
+ if (!fFoundName)
+ {
+ rc = pfnCallback(pMod, iFunction + pExpDir->Base, NULL, 0, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+ }
+ return 0;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo implement enumeration on darwin. */
+ (void)pModNative;
+ return KLDR_ERR_TODO;
+
+#else
+# error "Port me"
+#endif
+
+}
+
+
+/** @copydoc kLdrModGetImport */
+static int kldrModNativeGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement import enumeration on OS/2. */
+ (void)pModNative;
+ return ERROR_NOT_SUPPORTED;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ const IMAGE_IMPORT_DESCRIPTOR *pImpDesc;
+ const char *pszImportName;
+ KSIZE cchImportName;
+ int rc;
+
+ /*
+ * Simple bounds check.
+ */
+ if (iImport >= (KU32)kldrModNativeNumberOfImports(pMod, pvBits))
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /*
+ * Get the name.
+ */
+ pImpDesc = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod,
+ pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
+ + sizeof(IMAGE_IMPORT_DESCRIPTOR) * iImport,
+ const IMAGE_IMPORT_DESCRIPTOR *);
+ pszImportName = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pImpDesc->Name, const char *);
+ cchImportName = kHlpStrLen(pszImportName);
+ if (cchImportName < cchName)
+ {
+ kHlpMemCopy(pszName, pszImportName, cchImportName + 1);
+ rc = 0;
+ }
+ else
+ {
+ kHlpMemCopy(pszName, pszImportName, cchName);
+ if (cchName)
+ pszName[cchName - 1] = '\0';
+ rc = KERR_BUFFER_OVERFLOW;
+ }
+
+ return rc;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement import enumeration on darwin. */
+ (void)pModNative;
+ return KLDR_ERR_TODO;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModNumberOfImports */
+static KI32 kldrModNativeNumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement import counting on OS/2. */
+ (void)pModNative;
+ return -1;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ if (pModNative->cImportModules == ~(KU32)0)
+ {
+ /*
+ * We'll have to walk the import descriptors to figure out their number.
+ */
+ pModNative->cImportModules = 0;
+ if ( pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
+ && pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
+ {
+ const IMAGE_IMPORT_DESCRIPTOR *pImpDesc;
+
+ pImpDesc = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod,
+ pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,
+ const IMAGE_IMPORT_DESCRIPTOR *);
+ while (pImpDesc->Name && pImpDesc->FirstThunk)
+ {
+ pModNative->cImportModules++;
+ pImpDesc++;
+ }
+ }
+ }
+ return pModNative->cImportModules;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement import counting on Darwin. */
+ (void)pModNative;
+ return -1;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModNativeGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement stack info on OS/2. */
+ (void)pModNative;
+ return ERROR_NOT_SUPPORTED;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ pStackInfo->Address = NIL_KLDRADDR;
+ pStackInfo->LinkAddress = NIL_KLDRADDR;
+ pStackInfo->cbStack = pStackInfo->cbStackThread = pModNative->pNtHdrs->OptionalHeader.SizeOfStackReserve;
+
+ return 0;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement stack info on Darwin. */
+ (void)pModNative;
+ return KLDR_ERR_TODO;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModNativeQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement me on OS/2. */
+ (void)pModNative;
+ return ERROR_NOT_SUPPORTED;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ /*
+ * Convert the address from the header.
+ */
+ *pMainEPAddress = pModNative->pNtHdrs->OptionalHeader.AddressOfEntryPoint
+ ? BaseAddress + pModNative->pNtHdrs->OptionalHeader.AddressOfEntryPoint
+ : NIL_KLDRADDR;
+ return 0;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement me on Darwin. */
+ (void)pModNative;
+ return KLDR_ERR_TODO;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModEnumDbgInfo */
+static int kldrModNativeEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement me on OS/2. */
+ (void)pModNative;
+ return ERROR_NOT_SUPPORTED;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ const IMAGE_DEBUG_DIRECTORY *pDbgDir;
+ KU32 iDbgInfo;
+ KU32 cb;
+ int rc;
+
+ /*
+ * Check that there is a debug directory first.
+ */
+ cb = pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ if ( cb < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return 0;
+
+ /*
+ * Enumerate the debug directory.
+ */
+ pDbgDir = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod,
+ pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress,
+ const IMAGE_DEBUG_DIRECTORY *);
+ for (iDbgInfo = 0;; iDbgInfo++, pDbgDir++, cb -= sizeof(IMAGE_DEBUG_DIRECTORY))
+ {
+ KLDRDBGINFOTYPE enmDbgInfoType;
+
+ /* convert the type. */
+ switch (pDbgDir->Type)
+ {
+ case IMAGE_DEBUG_TYPE_UNKNOWN:
+ case IMAGE_DEBUG_TYPE_FPO:
+ case IMAGE_DEBUG_TYPE_COFF: /*stabs dialect??*/
+ case IMAGE_DEBUG_TYPE_MISC:
+ case IMAGE_DEBUG_TYPE_EXCEPTION:
+ case IMAGE_DEBUG_TYPE_FIXUP:
+ case IMAGE_DEBUG_TYPE_BORLAND:
+ default:
+ enmDbgInfoType = KLDRDBGINFOTYPE_UNKNOWN;
+ break;
+ case IMAGE_DEBUG_TYPE_CODEVIEW:
+ enmDbgInfoType = KLDRDBGINFOTYPE_CODEVIEW;
+ break;
+ }
+
+ rc = pfnCallback(pMod, iDbgInfo,
+ enmDbgInfoType, pDbgDir->MajorVersion, pDbgDir->MinorVersion, NULL /*pszPartNm*/,
+ pDbgDir->PointerToRawData ? pDbgDir->PointerToRawData : -1,
+ pDbgDir->AddressOfRawData ? pDbgDir->AddressOfRawData : NIL_KLDRADDR,
+ pDbgDir->SizeOfData,
+ NULL /*pszExtFile*/, pvUser);
+ if (rc)
+ break;
+
+ /* next */
+ if (cb <= sizeof(IMAGE_DEBUG_DIRECTORY))
+ break;
+ }
+
+ return rc;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement me on Darwin. */
+ (void)pModNative;
+ return KLDR_ERR_TODO;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModNativeHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+#if K_OS == K_OS_OS2
+
+ /** @todo implement me on OS/2. */
+ (void)pModNative;
+ return KLDR_ERR_NO_DEBUG_INFO;
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ /*
+ * Base this entirely on the presence of a debug directory.
+ */
+ if ( pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
+ < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return KLDR_ERR_NO_DEBUG_INFO;
+ return 0;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement me on Darwin. */
+ (void)pModNative;
+ return KLDR_ERR_NO_DEBUG_INFO;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModNativeMap(PKLDRMOD pMod)
+{
+ return 0;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModNativeUnmap(PKLDRMOD pMod)
+{
+ return 0;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModNativeAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ return 0;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModNativeFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModNativeReload(PKLDRMOD pMod)
+{
+ return 0;
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModNativeFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ return 0;
+}
+
+
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS || defined(__NT__)
+/** Common worker on platforms where there is one entrypoint which does both. */
+static int kLdrModNativeCallInitTerm(PKLDRMODNATIVE pModNative, KUPTR uHandle, KBOOL fInit)
+{
+# if K_OS == K_OS_WINDOWS || defined(__NT__)
+ /* No TLS init/term for now. */
+ KU32 const uRvaEntrypoint = pModNative->pNtHdrs->OptionalHeader.AddressOfEntryPoint;
+ if ( uRvaEntrypoint != 0
+ && uRvaEntrypoint < pModNative->pNtHdrs->OptionalHeader.SizeOfCode
+ && (pModNative->pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL))
+ {
+ KI32 rc = kldrModPEDoCall(uRvaEntrypoint + (KUPTR)pModNative->hmod, uHandle,
+ fInit ? DLL_PROCESS_ATTACH : DLL_PROCESS_DETACH, NULL /*pvReserved*/);
+ if (rc)
+ return 0;
+ return fInit ? KLDR_ERR_MODULE_INIT_FAILED : KLDR_ERR_THREAD_ATTACH_FAILED;
+ }
+# elif K_OS == K_OS_OS2
+ //KI32 kldrModLXDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved)
+# endif
+ return 0;
+}
+#endif
+
+/** @copydoc kLdrModCallInit */
+static int kldrModNativeCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS || defined(__NT__)
+ if (pMod->fFlags & KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM)
+ return kLdrModNativeCallInitTerm((PKLDRMODNATIVE)pMod->pvData, uHandle, K_TRUE /*fInit*/);
+#endif
+ return 0;
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModNativeCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS || defined(__NT__)
+ if (pMod->fFlags & KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM)
+ return kLdrModNativeCallInitTerm((PKLDRMODNATIVE)pMod->pvData, uHandle, K_FALSE /*fInit*/);
+#endif
+ return 0;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModNativeCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+ return 0;
+}
+
+
+/** @copydoc kLdrModSize */
+static KLDRADDR kldrModNativeSize(PKLDRMOD pMod)
+{
+#if K_OS == K_OS_OS2
+ return 0; /* don't bother */
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ /* just because we can. */
+ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData;
+ return pModNative->pNtHdrs->OptionalHeader.SizeOfImage;
+
+#elif K_OS == K_OS_DARWIN
+ /** @todo Implement me on Darwin. */
+ return 0;
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModGetBits */
+static int kldrModNativeGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+#if K_OS == K_OS_OS2
+ return ERROR_NOT_SUPPORTED; /* don't bother */
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ return ERROR_NOT_SUPPORTED; /* don't bother even if we could implement this. */
+
+#elif K_OS == K_OS_DARWIN
+ return KLDR_ERR_TODO; /* don't bother. */
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/** @copydoc kLdrModRelocateBits */
+static int kldrModNativeRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+#if K_OS == K_OS_OS2
+ return ERROR_NOT_SUPPORTED; /* don't bother */
+
+#elif K_OS == K_OS_WINDOWS || defined(__NT__)
+ return ERROR_NOT_SUPPORTED; /* don't bother even if we could implement this. */
+
+#elif K_OS == K_OS_DARWIN
+ return KLDR_ERR_TODO; /* don't bother. */
+
+#else
+# error "Port me"
+#endif
+}
+
+
+/**
+ * The native module interpreter method table.
+ */
+KLDRMODOPS g_kLdrModNativeOps =
+{
+ "Native",
+ NULL,
+ kldrModNativeCreate,
+ kldrModNativeDestroy,
+ kldrModNativeQuerySymbol,
+ kldrModNativeEnumSymbols,
+ kldrModNativeGetImport,
+ kldrModNativeNumberOfImports,
+ NULL /* can execute one is optional */,
+ kldrModNativeGetStackInfo,
+ kldrModNativeQueryMainEntrypoint,
+ NULL /* pfnQueryImageUuid */,
+ NULL /* fixme */,
+ NULL /* fixme */,
+ kldrModNativeEnumDbgInfo,
+ kldrModNativeHasDbgInfo,
+ kldrModNativeMap,
+ kldrModNativeUnmap,
+ kldrModNativeAllocTLS,
+ kldrModNativeFreeTLS,
+ kldrModNativeReload,
+ kldrModNativeFixupMapping,
+ kldrModNativeCallInit,
+ kldrModNativeCallTerm,
+ kldrModNativeCallThread,
+ kldrModNativeSize,
+ kldrModNativeGetBits,
+ kldrModNativeRelocateBits,
+ NULL /* fixme */,
+ 42 /* the end */
+};
+
diff --git a/src/lib/kStuff/kLdr/kLdrModPE.c b/src/lib/kStuff/kLdr/kLdrModPE.c
new file mode 100644
index 0000000..1a9bbc2
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrModPE.c
@@ -0,0 +1,2044 @@
+/* $Id: kLdrModPE.c 117 2020-03-15 15:23:36Z bird $ */
+/** @file
+ * kLdr - The Module Interpreter for the Portable Executable (PE) Format.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+#include <k/kLdrFmts/pe.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @def KLDRMODPE_STRICT
+ * Define KLDRMODPE_STRICT to enabled strict checks in KLDRMODPE. */
+#define KLDRMODPE_STRICT 1
+
+/** @def KLDRMODPE_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMODPE_STRICT
+# define KLDRMODPE_ASSERT(expr) kHlpAssert(expr)
+#else
+# define KLDRMODPE_ASSERT(expr) do {} while (0)
+#endif
+
+/** @def KLDRMODPE_RVA2TYPE
+ * Converts a RVA to a pointer of the specified type.
+ * @param pvBits The bits (image base).
+ * @param uRVA The image relative virtual address.
+ * @param type The type to cast to.
+ */
+#define KLDRMODPE_RVA2TYPE(pvBits, uRVA, type) \
+ ( (type) ((KUPTR)(pvBits) + (KUPTR)(uRVA)) )
+
+/** @def KLDRMODPE_VALID_RVA
+ * Checks that the specified RVA value is non-zero and within the bounds of the image.
+ * @returns true/false.
+ * @param pModPE The PE module interpreter instance.
+ * @param uRVA The RVA to validate.
+ */
+#define KLDRMODPE_VALID_RVA(pModPE, uRVA) \
+ ( (uRVA) && (uRVA) < (pModPE)->Hdrs.OptionalHeader.SizeOfImage )
+
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Instance data for the PE module interpreter.
+ */
+typedef struct KLDRMODPE
+{
+ /** Pointer to the module. (Follows the section table.) */
+ PKLDRMOD pMod;
+ /** Pointer to the RDR mapping of the raw file bits. NULL if not mapped. */
+ const void *pvBits;
+ /** Pointer to the user mapping. */
+ const void *pvMapping;
+ /** Reserved flags. */
+ KU32 f32Reserved;
+ /** The number of imported modules.
+ * If ~(KU32)0 this hasn't been determined yet. */
+ KU32 cImportModules;
+ /** The offset of the NT headers. */
+ KLDRFOFF offHdrs;
+ /** Copy of the NT headers. */
+ IMAGE_NT_HEADERS64 Hdrs;
+ /** The section header table . */
+ IMAGE_SECTION_HEADER aShdrs[1];
+} KLDRMODPE, *PKLDRMODPE;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static KI32 kldrModPENumberOfImports(PKLDRMOD pMod, const void *pvBits);
+static int kldrModPERelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+
+static int kldrModPEDoCreate(PKRDR pRdr, KLDRFOFF offNewHdr, PKLDRMODPE *ppMod);
+/*static void kldrModPEDoLoadConfigConversion(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg); */
+static int kLdrModPEDoOptionalHeaderValidation(PKLDRMODPE pModPE);
+static int kLdrModPEDoSectionHeadersValidation(PKLDRMODPE pModPE);
+static void kldrModPEDoOptionalHeaderConversion(PIMAGE_OPTIONAL_HEADER64 pOptionalHeader);
+static int kldrModPEDoForwarderQuery(PKLDRMODPE pModPE, const void *pvBits, const char *pszForwarder,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser, PKLDRADDR puValue, KU32 *pfKind);
+static int kldrModPEDoFixups(PKLDRMODPE pModPE, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress);
+static int kldrModPEDoImports32Bit(PKLDRMODPE pModPE, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int kldrModPEDoImports64Bit(PKLDRMODPE pModPE, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int kldrModPEDoImports(PKLDRMODPE pModPE, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int kldrModPEDoCallDLL(PKLDRMODPE pModPE, void *pvMapping, unsigned uOp, KUPTR uHandle);
+static int kldrModPEDoCallTLS(PKLDRMODPE pModPE, void *pvMapping, unsigned uOp, KUPTR uHandle);
+
+
+/**
+ * Create a loader module instance interpreting the executable image found
+ * in the specified file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ * On failure, a non-zero OS specific error code is returned.
+ * @param pOps Pointer to the registered method table.
+ * @param pRdr The file provider instance to use.
+ * @param fFlags Flags, MBZ.
+ * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means
+ * anything goes, but with a preference for the current
+ * host architecture.
+ * @param offNewHdr The offset of the new header in MZ files. -1 if not found.
+ * @param ppMod Where to store the module instance pointer.
+ */
+static int kldrModPECreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod)
+{
+ PKLDRMODPE pModPE;
+ int rc;
+ K_NOREF(fFlags);
+
+ /*
+ * Create the instance data and do a minimal header validation.
+ */
+ rc = kldrModPEDoCreate(pRdr, offNewHdr, &pModPE);
+ if (!rc)
+ {
+ /*
+ * Match up against the requested CPU architecture.
+ */
+ if ( enmCpuArch == KCPUARCH_UNKNOWN
+ || pModPE->pMod->enmArch == enmCpuArch)
+ {
+ pModPE->pMod->pOps = pOps;
+ pModPE->pMod->u32Magic = KLDRMOD_MAGIC;
+ *ppMod = pModPE->pMod;
+ return 0;
+ }
+ rc = KLDR_ERR_CPU_ARCH_MISMATCH;
+ }
+ kHlpFree(pModPE);
+ return rc;
+}
+
+
+/**
+ * Separate function for reading creating the PE module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModPEDoCreate(PKRDR pRdr, KLDRFOFF offNewHdr, PKLDRMODPE *ppModPE)
+{
+ struct
+ {
+ KU32 Signature;
+ IMAGE_FILE_HEADER FileHdr;
+ } s;
+ PKLDRMODPE pModPE;
+ PKLDRMOD pMod;
+ KSIZE cb;
+ KSIZE cchFilename;
+ KLDRFOFF off;
+ KU32 i;
+ int rc;
+ *ppModPE = NULL;
+
+ /*
+ * Read the signature and file header.
+ */
+ rc = kRdrRead(pRdr, &s, sizeof(s), offNewHdr > 0 ? offNewHdr : 0);
+ if (rc)
+ return rc;
+ if (s.Signature != IMAGE_NT_SIGNATURE)
+ return KLDR_ERR_UNKNOWN_FORMAT;
+
+ /* sanity checks. */
+ if ( s.FileHdr.NumberOfSections > 4096
+ || ( s.FileHdr.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER32)
+ && s.FileHdr.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64))
+ || !(s.FileHdr.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)
+ )
+ return KLDR_ERR_PE_BAD_FILE_HEADER;
+ if ( s.FileHdr.Machine != IMAGE_FILE_MACHINE_I386
+ && s.FileHdr.Machine != IMAGE_FILE_MACHINE_AMD64
+ )
+ return KLDR_ERR_PE_UNSUPPORTED_MACHINE;
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ cchFilename = kHlpStrLen(kRdrName(pRdr));
+ cb = K_ALIGN_Z(K_OFFSETOF(KLDRMODPE, aShdrs[s.FileHdr.NumberOfSections]), 16)
+ + K_OFFSETOF(KLDRMOD, aSegments[s.FileHdr.NumberOfSections + 1])
+ + cchFilename + 1;
+ pModPE = (PKLDRMODPE)kHlpAlloc(cb);
+ if (!pModPE)
+ return KERR_NO_MEMORY;
+ *ppModPE = pModPE;
+
+ /* KLDRMOD */
+ pMod = (PKLDRMOD)((KU8 *)pModPE + K_ALIGN_Z(K_OFFSETOF(KLDRMODPE, aShdrs[s.FileHdr.NumberOfSections]), 16));
+ pMod->pvData = pModPE;
+ pMod->pRdr = pRdr;
+ pMod->pOps = NULL; /* set upon success. */
+ pMod->cSegments = s.FileHdr.NumberOfSections + 1;
+ pMod->cchFilename = (KU32)cchFilename;
+ pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments];
+ kHlpMemCopy((char *)pMod->pszFilename, kRdrName(pRdr), cchFilename + 1);
+ pMod->pszName = kHlpGetFilename(pMod->pszFilename);
+ pMod->cchName = (KU32)(cchFilename - (pMod->pszName - pMod->pszFilename));
+ pMod->fFlags = 0;
+ switch (s.FileHdr.Machine)
+ {
+ case IMAGE_FILE_MACHINE_I386:
+ pMod->enmCpu = KCPU_I386;
+ pMod->enmArch = KCPUARCH_X86_32;
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+ break;
+
+ case IMAGE_FILE_MACHINE_AMD64:
+ pMod->enmCpu = KCPU_K8;
+ pMod->enmArch = KCPUARCH_AMD64;
+ pMod->enmEndian = KLDRENDIAN_LITTLE;
+ break;
+ default:
+ kHlpAssert(0);
+ break;
+ }
+ pMod->enmFmt = KLDRFMT_PE;
+ if (s.FileHdr.Characteristics & IMAGE_FILE_DLL)
+ pMod->enmType = !(s.FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
+ ? KLDRTYPE_SHARED_LIBRARY_RELOCATABLE
+ : KLDRTYPE_SHARED_LIBRARY_FIXED;
+ else
+ pMod->enmType = !(s.FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
+ ? KLDRTYPE_EXECUTABLE_RELOCATABLE
+ : KLDRTYPE_EXECUTABLE_FIXED;
+ pMod->u32Magic = 0; /* set upon success. */
+
+ /* KLDRMODPE */
+ pModPE->pMod = pMod;
+ pModPE->pvBits = NULL;
+ pModPE->pvMapping = NULL;
+ pModPE->f32Reserved = 0;
+ pModPE->cImportModules = ~(KU32)0;
+ pModPE->offHdrs = offNewHdr >= 0 ? offNewHdr : 0;
+ pModPE->Hdrs.Signature = s.Signature;
+ pModPE->Hdrs.FileHeader = s.FileHdr;
+
+ /*
+ * Read the optional header and the section table.
+ */
+ off = pModPE->offHdrs + sizeof(pModPE->Hdrs.Signature) + sizeof(pModPE->Hdrs.FileHeader);
+ rc = kRdrRead(pRdr, &pModPE->Hdrs.OptionalHeader, pModPE->Hdrs.FileHeader.SizeOfOptionalHeader, off);
+ if (rc)
+ return rc;
+ if (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader != sizeof(pModPE->Hdrs.OptionalHeader))
+ kldrModPEDoOptionalHeaderConversion(&pModPE->Hdrs.OptionalHeader);
+ off += pModPE->Hdrs.FileHeader.SizeOfOptionalHeader;
+ rc = kRdrRead(pRdr, &pModPE->aShdrs[0], sizeof(IMAGE_SECTION_HEADER) * pModPE->Hdrs.FileHeader.NumberOfSections, off);
+ if (rc)
+ return rc;
+
+ /*
+ * Validate the two.
+ */
+ rc = kLdrModPEDoOptionalHeaderValidation(pModPE);
+ if (rc)
+ return rc;
+ for (i = 0; i < pModPE->Hdrs.FileHeader.NumberOfSections; i++)
+ {
+ rc = kLdrModPEDoSectionHeadersValidation(pModPE);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Setup the KLDRMOD segment array.
+ */
+ /* The implied headers section. */
+ pMod->aSegments[0].pvUser = NULL;
+ pMod->aSegments[0].pchName = "TheHeaders";
+ pMod->aSegments[0].cchName = sizeof("TheHeaders") - 1;
+ pMod->aSegments[0].enmProt = KPROT_READONLY;
+ pMod->aSegments[0].cb = pModPE->Hdrs.OptionalHeader.SizeOfHeaders;
+ pMod->aSegments[0].Alignment = pModPE->Hdrs.OptionalHeader.SectionAlignment;
+ pMod->aSegments[0].LinkAddress = pModPE->Hdrs.OptionalHeader.ImageBase;
+ pMod->aSegments[0].offFile = 0;
+ pMod->aSegments[0].cbFile = pModPE->Hdrs.OptionalHeader.SizeOfHeaders;
+ pMod->aSegments[0].RVA = 0;
+ if (pMod->cSegments > 1)
+ pMod->aSegments[0].cbMapped = pModPE->aShdrs[0].VirtualAddress;
+ else
+ pMod->aSegments[0].cbMapped = pModPE->Hdrs.OptionalHeader.SizeOfHeaders;
+ pMod->aSegments[0].MapAddress = 0;
+
+ /* The section headers. */
+ for (i = 0; i < pModPE->Hdrs.FileHeader.NumberOfSections; i++)
+ {
+ const char *pch;
+ KU32 cb2;
+
+ /* unused */
+ pMod->aSegments[i + 1].pvUser = NULL;
+ pMod->aSegments[i + 1].MapAddress = 0;
+ pMod->aSegments[i + 1].SelFlat = 0;
+ pMod->aSegments[i + 1].Sel16bit = 0;
+ pMod->aSegments[i + 1].fFlags = 0;
+
+ /* name */
+ pMod->aSegments[i + 1].pchName = pch = (const char *)&pModPE->aShdrs[i].Name[0];
+ cb2 = IMAGE_SIZEOF_SHORT_NAME;
+ while ( cb2 > 0
+ && (pch[cb2 - 1] == ' ' || pch[cb2 - 1] == '\0'))
+ cb2--;
+ pMod->aSegments[i + 1].cchName = cb2;
+
+ /* size and addresses */
+ if (!(pModPE->aShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ {
+ /* Kluge to deal with wlink ".reloc" sections that has a VirtualSize of 0 bytes. */
+ cb2 = pModPE->aShdrs[i].Misc.VirtualSize;
+ if (!cb2)
+ cb2 = K_ALIGN_Z(pModPE->aShdrs[i].SizeOfRawData, pModPE->Hdrs.OptionalHeader.SectionAlignment);
+ pMod->aSegments[i + 1].cb = pModPE->aShdrs[i].Misc.VirtualSize;
+ pMod->aSegments[i + 1].LinkAddress = pModPE->aShdrs[i].VirtualAddress
+ + pModPE->Hdrs.OptionalHeader.ImageBase;
+ pMod->aSegments[i + 1].RVA = pModPE->aShdrs[i].VirtualAddress;
+ pMod->aSegments[i + 1].cbMapped = cb2;
+ if (i + 2 < pMod->cSegments)
+ pMod->aSegments[i + 1].cbMapped= pModPE->aShdrs[i + 1].VirtualAddress
+ - pModPE->aShdrs[i].VirtualAddress;
+ }
+ else
+ {
+ pMod->aSegments[i + 1].cb = 0;
+ pMod->aSegments[i + 1].cbMapped = 0;
+ pMod->aSegments[i + 1].LinkAddress = NIL_KLDRADDR;
+ pMod->aSegments[i + 1].RVA = 0;
+ }
+
+ /* file location */
+ pMod->aSegments[i + 1].offFile = pModPE->aShdrs[i].PointerToRawData;
+ pMod->aSegments[i + 1].cbFile = pModPE->aShdrs[i].SizeOfRawData;
+ if ( pMod->aSegments[i + 1].cbMapped > 0 /* if mapped */
+ && (KLDRSIZE)pMod->aSegments[i + 1].cbFile > pMod->aSegments[i + 1].cbMapped)
+ pMod->aSegments[i + 1].cbFile = (KLDRFOFF)(pMod->aSegments[i + 1].cbMapped);
+
+ /* protection */
+ switch ( pModPE->aShdrs[i].Characteristics
+ & (IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
+ {
+ case 0:
+ case IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_NOACCESS;
+ break;
+ case IMAGE_SCN_MEM_READ:
+ case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_READONLY;
+ break;
+ case IMAGE_SCN_MEM_WRITE:
+ case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_WRITECOPY;
+ break;
+ case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED:
+ case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_READWRITE;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_READ;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_WRITECOPY;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED:
+ case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ:
+ pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_READWRITE;
+ break;
+ }
+
+ /* alignment. */
+ switch (pModPE->aShdrs[i].Characteristics & IMAGE_SCN_ALIGN_MASK)
+ {
+ case 0: /* hope this is right... */
+ pMod->aSegments[i + 1].Alignment = pModPE->Hdrs.OptionalHeader.SectionAlignment;
+ break;
+ case IMAGE_SCN_ALIGN_1BYTES: pMod->aSegments[i + 1].Alignment = 1; break;
+ case IMAGE_SCN_ALIGN_2BYTES: pMod->aSegments[i + 1].Alignment = 2; break;
+ case IMAGE_SCN_ALIGN_4BYTES: pMod->aSegments[i + 1].Alignment = 4; break;
+ case IMAGE_SCN_ALIGN_8BYTES: pMod->aSegments[i + 1].Alignment = 8; break;
+ case IMAGE_SCN_ALIGN_16BYTES: pMod->aSegments[i + 1].Alignment = 16; break;
+ case IMAGE_SCN_ALIGN_32BYTES: pMod->aSegments[i + 1].Alignment = 32; break;
+ case IMAGE_SCN_ALIGN_64BYTES: pMod->aSegments[i + 1].Alignment = 64; break;
+ case IMAGE_SCN_ALIGN_128BYTES: pMod->aSegments[i + 1].Alignment = 128; break;
+ case IMAGE_SCN_ALIGN_256BYTES: pMod->aSegments[i + 1].Alignment = 256; break;
+ case IMAGE_SCN_ALIGN_512BYTES: pMod->aSegments[i + 1].Alignment = 512; break;
+ case IMAGE_SCN_ALIGN_1024BYTES: pMod->aSegments[i + 1].Alignment = 1024; break;
+ case IMAGE_SCN_ALIGN_2048BYTES: pMod->aSegments[i + 1].Alignment = 2048; break;
+ case IMAGE_SCN_ALIGN_4096BYTES: pMod->aSegments[i + 1].Alignment = 4096; break;
+ case IMAGE_SCN_ALIGN_8192BYTES: pMod->aSegments[i + 1].Alignment = 8192; break;
+ default: kHlpAssert(0); pMod->aSegments[i + 1].Alignment = 0; break;
+ }
+ }
+
+ /*
+ * We're done.
+ */
+ *ppModPE = pModPE;
+ return 0;
+}
+
+
+/**
+ * Converts a 32-bit optional header to a 64-bit one
+ *
+ * @param pOptHdr The optional header to convert.
+ */
+static void kldrModPEDoOptionalHeaderConversion(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
+{
+ /* volatile everywhere! */
+ IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
+ IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
+ KU32 volatile *pu32Dst;
+ KU32 volatile *pu32Src;
+ KU32 volatile *pu32SrcLast;
+ KU32 u32;
+
+ /* From LoaderFlags and out the difference is 4 * 32-bits. */
+ pu32Dst = (KU32 *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
+ pu32Src = (KU32 *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
+ pu32SrcLast = (KU32 *)&pOptHdr32->LoaderFlags;
+ while (pu32Src >= pu32SrcLast)
+ *pu32Dst-- = *pu32Src--;
+
+ /* The previous 4 fields are 32/64 and needs special attention. */
+ pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
+ pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
+ pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
+ u32 = pOptHdr32->SizeOfStackReserve;
+ pOptHdr64->SizeOfStackReserve = u32;
+
+ /*
+ * The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version.
+ * Thus, ImageBase needs some special treatement. It will probably work fine assigning one to the
+ * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
+ */
+ u32 = pOptHdr32->ImageBase;
+ pOptHdr64->ImageBase = u32;
+}
+
+
+#if 0
+/**
+ * Converts a 32-bit load config directory to a 64 bit one.
+ *
+ * @param pOptHdr The load config to convert.
+ */
+static void kldrModPEDoLoadConfigConversion(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
+{
+ /* volatile everywhere! */
+ IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *)pLoadCfg;
+ IMAGE_LOAD_CONFIG_DIRECTORY64 volatile *pLoadCfg64 = pLoadCfg;
+ KU32 u32;
+
+ pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
+ pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
+ pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
+ pLoadCfg64->EditList = pLoadCfg32->EditList;
+ pLoadCfg64->Reserved1 = pLoadCfg32->Reserved1;
+ pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
+ /* (ProcessHeapFlags switched place with ProcessAffinityMask, but we're
+ * more than 16 byte off by now so it doesn't matter.) */
+ pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags;
+ pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
+ pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
+ pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
+ pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
+ pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
+ u32 = pLoadCfg32->DeCommitFreeBlockThreshold;
+ pLoadCfg64->DeCommitFreeBlockThreshold = u32;
+ /* the remainder matches. */
+}
+#endif
+
+
+/**
+ * Internal worker which validates the section headers.
+ */
+static int kLdrModPEDoOptionalHeaderValidation(PKLDRMODPE pModPE)
+{
+ const unsigned fIs32Bit = pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32);
+
+ /* the magic */
+ if ( pModPE->Hdrs.OptionalHeader.Magic
+ != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return KLDR_ERR_PE_BAD_OPTIONAL_HEADER;
+
+ /** @todo validate more */
+ return 0;
+}
+
+
+/**
+ * Internal worker which validates the section headers.
+ */
+static int kLdrModPEDoSectionHeadersValidation(PKLDRMODPE pModPE)
+{
+ /** @todo validate shdrs */
+ K_NOREF(pModPE);
+ return 0;
+}
+
+
+/** @copydoc KLDRMODOPS::pfnDestroy */
+static int kldrModPEDestroy(PKLDRMOD pMod)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc = 0;
+ KLDRMODPE_ASSERT(!pModPE->pvMapping);
+
+ if (pMod->pRdr)
+ {
+ rc = kRdrClose(pMod->pRdr);
+ pMod->pRdr = NULL;
+ }
+ pMod->u32Magic = 0;
+ pMod->pOps = NULL;
+ kHlpFree(pModPE);
+ return rc;
+}
+
+
+/**
+ * Performs the mapping of the image.
+ *
+ * This can be used to do the internal mapping as well as the
+ * user requested mapping. fForReal indicates which is desired.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pModPE The interpreter module instance
+ * @param fForReal If set, do the user mapping. if clear, do the internal mapping.
+ */
+static int kldrModPEDoMap(PKLDRMODPE pModPE, unsigned fForReal)
+{
+ PKLDRMOD pMod = pModPE->pMod;
+ KBOOL fFixed;
+ void *pvBase;
+ int rc;
+ KU32 i;
+
+ /*
+ * Map it.
+ */
+ /* fixed image? */
+ fFixed = fForReal
+ && ( pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
+ || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED);
+ if (!fFixed)
+ pvBase = NULL;
+ else
+ {
+ pvBase = (void *)(KUPTR)pMod->aSegments[0].LinkAddress;
+ if ((KUPTR)pvBase != pMod->aSegments[0].LinkAddress)
+ return KLDR_ERR_ADDRESS_OVERFLOW;
+ }
+
+ /* try do the prepare */
+ rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
+ if (rc)
+ return rc;
+
+ /*
+ * Update the segments with their map addresses.
+ */
+ if (fForReal)
+ {
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ if (pMod->aSegments[i].RVA != NIL_KLDRADDR)
+ pMod->aSegments[i].MapAddress = (KUPTR)pvBase + (KUPTR)pMod->aSegments[i].RVA;
+ }
+ pModPE->pvMapping = pvBase;
+ }
+ else
+ pModPE->pvBits = pvBase;
+ return 0;
+}
+
+
+/**
+ * Unmaps a image mapping.
+ *
+ * This can be used to do the internal mapping as well as the
+ * user requested mapping. fForReal indicates which is desired.
+ *
+ * @returns 0 on success, non-zero OS or kLdr status code on failure.
+ * @param pModPE The interpreter module instance
+ * @param pvMapping The mapping to unmap.
+ */
+static int kldrModPEDoUnmap(PKLDRMODPE pModPE, const void *pvMapping)
+{
+ PKLDRMOD pMod = pModPE->pMod;
+ int rc;
+ KU32 i;
+
+ /*
+ * Try unmap the image.
+ */
+ rc = kRdrUnmap(pMod->pRdr, (void *)pvMapping, pMod->cSegments, pMod->aSegments);
+ if (rc)
+ return rc;
+
+ /*
+ * Update the segments to reflect that they aren't mapped any longer.
+ */
+ if (pModPE->pvMapping == pvMapping)
+ {
+ pModPE->pvMapping = NULL;
+ for (i = 0; i < pMod->cSegments; i++)
+ pMod->aSegments[i].MapAddress = 0;
+ }
+ if (pModPE->pvBits == pvMapping)
+ pModPE->pvBits = NULL;
+
+ return 0;
+}
+
+
+/**
+ * Gets usable bits and the right base address.
+ *
+ * @returns 0 on success.
+ * @returns A non-zero status code if the BaseAddress isn't right or some problem is encountered
+ * featch in a temp mapping the bits.
+ * @param pModPE The interpreter module instance
+ * @param ppvBits The bits address, IN & OUT.
+ * @param pBaseAddress The base address, IN & OUT. Optional.
+ */
+static int kldrModPEBitsAndBaseAddress(PKLDRMODPE pModPE, const void **ppvBits, PKLDRADDR pBaseAddress)
+{
+ int rc = 0;
+
+ /*
+ * Correct the base address.
+ *
+ * We don't use the base address for interpreting the bits in this
+ * interpreter, which makes things relativly simple.
+ */
+ if (pBaseAddress)
+ {
+ if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP)
+ *pBaseAddress = pModPE->pMod->aSegments[0].MapAddress;
+ else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK)
+ *pBaseAddress = pModPE->Hdrs.OptionalHeader.ImageBase;
+ }
+
+ /*
+ * Get bits.
+ */
+ if (ppvBits && !*ppvBits)
+ {
+ if (pModPE->pvMapping)
+ *ppvBits = pModPE->pvMapping;
+ else if (pModPE->pvBits)
+ *ppvBits = pModPE->pvBits;
+ else
+ {
+ /* create an internal mapping. */
+ rc = kldrModPEDoMap(pModPE, 0 /* not for real */);
+ if (rc)
+ return rc;
+ KLDRMODPE_ASSERT(pModPE->pvBits);
+ *ppvBits = pModPE->pvBits;
+ }
+ }
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModQuerySymbol */
+static int kldrModPEQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ const KU32 *paExportRVAs;
+ const IMAGE_EXPORT_DIRECTORY *pExpDir;
+ KU32 iExpOrd;
+ KU32 uRVA;
+ int rc;
+
+ /*
+ * Make sure we've got mapped bits and resolve any base address aliases.
+ */
+ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, &BaseAddress);
+ if (rc)
+ return rc;
+ if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
+ < sizeof(IMAGE_EXPORT_DIRECTORY))
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ if (pszVersion && *pszVersion)
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+ pExpDir = KLDRMODPE_RVA2TYPE(pvBits,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress,
+ PIMAGE_EXPORT_DIRECTORY);
+ if (!pchSymbol)
+ {
+ /*
+ * Simple, calculate the unbased ordinal and bounds check it.
+ */
+ iExpOrd = iSymbol - pExpDir->Base;
+ if (iExpOrd >= K_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions))
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ /*
+ * Do a binary search for the name.
+ * (The name table is sorted in ascending ordered by the linker.)
+ */
+ const KU32 *paRVANames = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, const KU32 *);
+ const KU16 *paOrdinals = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, const KU16 *);
+ KI32 iStart = 1; /* one based binary searching is simpler. */
+ KI32 iEnd = pExpDir->NumberOfNames;
+
+ for (;;)
+ {
+ KI32 i;
+ int diff;
+ const char *pszName;
+
+ /* done? */
+ if (iStart > iEnd)
+ {
+#ifdef KLDRMODPE_STRICT /* Make sure the linker and we both did our job right. */
+ for (i = 0; i < (KI32)pExpDir->NumberOfNames; i++)
+
+ {
+ pszName = KLDRMODPE_RVA2TYPE(pvBits, paRVANames[i], const char *);
+ KLDRMODPE_ASSERT(kHlpStrNComp(pszName, pchSymbol, cchSymbol) || pszName[cchSymbol]);
+ KLDRMODPE_ASSERT(i == 0 || kHlpStrComp(pszName, KLDRMODPE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *)));
+ }
+#endif
+ return KLDR_ERR_SYMBOL_NOT_FOUND;
+ }
+
+ i = (iEnd - iStart) / 2 + iStart;
+ pszName = KLDRMODPE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *);
+ diff = kHlpStrNComp(pszName, pchSymbol, cchSymbol);
+ if (!diff)
+ diff = pszName[cchSymbol] - 0;
+ if (diff < 0)
+ iStart = i + 1; /* The symbol must be after the current name. */
+ else if (diff)
+ iEnd = i - 1; /* The symbol must be before the current name. */
+ else
+ {
+ iExpOrd = paOrdinals[i - 1]; /* match! */
+ break;
+ }
+ }
+ }
+
+ /*
+ * Lookup the address in the 'symbol' table.
+ */
+ paExportRVAs = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, const KU32 *);
+ uRVA = paExportRVAs[iExpOrd];
+ if ( uRVA - pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
+ < pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
+ return kldrModPEDoForwarderQuery(pModPE, pvBits, KLDRMODPE_RVA2TYPE(pvBits, uRVA, const char *),
+ pfnGetForwarder, pvUser, puValue, pfKind);
+
+ /*
+ * Set the return value.
+ */
+ if (puValue)
+ *puValue = BaseAddress + uRVA;
+ if (pfKind)
+ *pfKind = (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
+ ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT)
+ | KLDRSYMKIND_NO_TYPE;
+ return 0;
+}
+
+
+/**
+ * Deal with a forwarder entry.
+ *
+ * We do this seprately from kldrModPEQuerySymbol because the code is clumsy (as is all PE code
+ * thanks to the descriptive field names), and because it uses quite a bit more stack and we're
+ * trying to avoid allocating stack unless we have to.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvBits Where to read the image from.
+ * @param pszForwarder The forwarder entry name.
+ * @param pfnGetForwarder The callback for resolving forwarder symbols. (optional)
+ * @param pvUser The user argument for the callback.
+ * @param puValue Where to put the value. (optional)
+ * @param pfKind Where to put the symbol kind. (optional)
+ */
+static int kldrModPEDoForwarderQuery(PKLDRMODPE pModPE, const void *pvBits, const char *pszForwarder,
+ PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+ const IMAGE_IMPORT_DESCRIPTOR *paImpDir;
+ KU32 iImpModule;
+ KU32 cchImpModule;
+ const char *pszSymbol;
+ KU32 iSymbol;
+ int rc;
+
+ if (!pfnGetForwarder)
+ return KLDR_ERR_FORWARDER_SYMBOL;
+
+ /*
+ * Separate the name into a module name and a symbol name or ordinal.
+ *
+ * The module name ends at the first dot ('.').
+ * After the dot follows either a symbol name or a hash ('#') + ordinal.
+ */
+ pszSymbol = pszForwarder;
+ while (*pszSymbol != '.')
+ pszSymbol++;
+ if (!*pszSymbol)
+ return KLDR_ERR_PE_BAD_FORWARDER;
+ cchImpModule = (KU32)(pszSymbol - pszForwarder);
+
+ pszSymbol++; /* skip the dot */
+ if (!*pszSymbol)
+ return KLDR_ERR_PE_BAD_FORWARDER;
+ if (*pszSymbol == '#')
+ {
+ unsigned uBase;
+ pszSymbol++; /* skip the hash */
+
+ /* base detection */
+ uBase = 10;
+ if (pszSymbol[0] == '0' && (pszSymbol[1] == 'x' || pszSymbol[1] == 'X'))
+ {
+ uBase = 16;
+ pszSymbol += 2;
+ }
+
+ /* ascii to integer */
+ iSymbol = 0;
+ for (;;)
+ {
+ /* convert char to digit. */
+ unsigned uDigit = *pszSymbol++;
+ if (uDigit >= '0' && uDigit <= '9')
+ uDigit -= '0';
+ else if (uDigit >= 'a' && uDigit <= 'z')
+ uDigit -= 'a' + 10;
+ else if (uDigit >= 'A' && uDigit <= 'Z')
+ uDigit -= 'A' + 10;
+ else if (!uDigit)
+ break;
+ else
+ return KLDR_ERR_PE_BAD_FORWARDER;
+ if (uDigit >= uBase)
+ return KLDR_ERR_PE_BAD_FORWARDER;
+
+ /* insert the digit */
+ iSymbol *= uBase;
+ iSymbol += uDigit;
+ }
+
+ pszSymbol = NULL; /* no symbol name. */
+ }
+ else
+ iSymbol = NIL_KLDRMOD_SYM_ORDINAL; /* no ordinal number. */
+
+
+ /*
+ * Find the import module name.
+ *
+ * We ASSUME the linker will make sure there is an import
+ * entry for the module... not sure if this is right though.
+ */
+ if ( !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
+ || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
+ return KLDR_ERR_PE_FORWARDER_IMPORT_NOT_FOUND;
+ paImpDir = KLDRMODPE_RVA2TYPE(pvBits,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,
+ const IMAGE_IMPORT_DESCRIPTOR *);
+
+ kldrModPENumberOfImports(pModPE->pMod, pvBits);
+ for (iImpModule = 0; iImpModule < pModPE->cImportModules; iImpModule++)
+ {
+ const char *pszName = KLDRMODPE_RVA2TYPE(pvBits, paImpDir[iImpModule].Name, const char *);
+ KSIZE cchName = kHlpStrLen(pszName);
+ if ( ( cchName == cchImpModule
+ || ( cchName > cchImpModule
+ && pszName[cchImpModule] == '.'
+ && (pszName[cchImpModule + 1] == 'd' || pszName[cchImpModule + 1] == 'D')
+ && (pszName[cchImpModule + 2] == 'l' || pszName[cchImpModule + 2] == 'L')
+ && (pszName[cchImpModule + 3] == 'l' || pszName[cchImpModule + 3] == 'L'))
+ )
+ && kHlpMemICompAscii(pszName, pszForwarder, cchImpModule)
+ )
+ {
+ /*
+ * Now the rest is up to the callback (almost).
+ */
+ rc = pfnGetForwarder(pModPE->pMod, iImpModule, iSymbol, pszSymbol,
+ pszSymbol ? kHlpStrLen(pszSymbol) : 0, NULL, puValue, pfKind, pvUser);
+ if (!rc && pfKind)
+ *pfKind |= KLDRSYMKIND_FORWARDER;
+ return rc;
+ }
+ }
+ return KLDR_ERR_PE_FORWARDER_IMPORT_NOT_FOUND;
+}
+
+
+/** @copydoc kLdrModEnumSymbols */
+static int kldrModPEEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress,
+ KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ const KU32 *paFunctions;
+ const IMAGE_EXPORT_DIRECTORY *pExpDir;
+ const KU32 *paRVANames;
+ const KU16 *paOrdinals;
+ KU32 iFunction;
+ KU32 cFunctions;
+ KU32 cNames;
+ int rc;
+ K_NOREF(fFlags);
+
+ /*
+ * Make sure we've got mapped bits and resolve any base address aliases.
+ */
+ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, &BaseAddress);
+ if (rc)
+ return rc;
+
+ if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
+ < sizeof(IMAGE_EXPORT_DIRECTORY))
+ return 0; /* no exports to enumerate, return success. */
+
+ pExpDir = KLDRMODPE_RVA2TYPE(pvBits,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress,
+ PIMAGE_EXPORT_DIRECTORY);
+
+ /*
+ * Enumerate the ordinal exports.
+ */
+ paRVANames = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, const KU32 *);
+ paOrdinals = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, const KU16 *);
+ paFunctions = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, const KU32 *);
+ cFunctions = pExpDir->NumberOfFunctions;
+ cNames = pExpDir->NumberOfNames;
+ for (iFunction = 0; iFunction < cFunctions; iFunction++)
+ {
+ unsigned fFoundName;
+ KU32 iName;
+ const KU32 uRVA = paFunctions[iFunction];
+ const KLDRADDR uValue = BaseAddress + uRVA;
+ KU32 fKind = (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
+ ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT)
+ | KLDRSYMKIND_NO_TYPE;
+ if ( uRVA - pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
+ < pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
+ fKind |= KLDRSYMKIND_FORWARDER;
+
+ /*
+ * Any symbol names?
+ */
+ fFoundName = 0;
+ for (iName = 0; iName < cNames; iName++)
+ {
+ const char *pszName;
+ if (paOrdinals[iName] != iFunction)
+ continue;
+ fFoundName = 1;
+ pszName = KLDRMODPE_RVA2TYPE(pvBits, paRVANames[iName], const char *);
+ rc = pfnCallback(pMod, iFunction + pExpDir->Base, pszName, kHlpStrLen(pszName), NULL,
+ uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * If no names, call once with the ordinal only.
+ */
+ if (!fFoundName)
+ {
+ rc = pfnCallback(pMod, iFunction + pExpDir->Base, NULL, 0, NULL, uValue, fKind, pvUser);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModGetImport */
+static int kldrModPEGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ const IMAGE_IMPORT_DESCRIPTOR *pImpDesc;
+ const char *pszImportName;
+ KSIZE cchImportName;
+ int rc;
+
+ /*
+ * Make sure we've got mapped bits and resolve any base address aliases.
+ */
+ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, NULL);
+ if (rc)
+ return rc;
+
+ /*
+ * Simple bounds check.
+ */
+ if (iImport >= (KU32)kldrModPENumberOfImports(pMod, pvBits))
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /*
+ * Get the name.
+ */
+ pImpDesc = KLDRMODPE_RVA2TYPE(pvBits,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
+ + sizeof(IMAGE_IMPORT_DESCRIPTOR) * iImport,
+ const IMAGE_IMPORT_DESCRIPTOR *);
+ pszImportName = KLDRMODPE_RVA2TYPE(pvBits, pImpDesc->Name, const char *);
+ cchImportName = kHlpStrLen(pszImportName);
+ if (cchImportName < cchName)
+ {
+ kHlpMemCopy(pszName, pszImportName, cchImportName + 1);
+ rc = 0;
+ }
+ else
+ {
+ kHlpMemCopy(pszName, pszImportName, cchName);
+ if (cchName)
+ pszName[cchName - 1] = '\0';
+ rc = KERR_BUFFER_OVERFLOW;
+ }
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModNumberOfImports */
+static KI32 kldrModPENumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ if (pModPE->cImportModules == ~(KU32)0)
+ {
+ /*
+ * We'll have to walk the import descriptors to figure out their number.
+ * First, make sure we've got mapped bits.
+ */
+ if (kldrModPEBitsAndBaseAddress(pModPE, &pvBits, NULL))
+ return -1;
+ pModPE->cImportModules = 0;
+ if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
+ && pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
+ {
+ const IMAGE_IMPORT_DESCRIPTOR *pImpDesc;
+
+ pImpDesc = KLDRMODPE_RVA2TYPE(pvBits,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,
+ const IMAGE_IMPORT_DESCRIPTOR *);
+ while (pImpDesc->Name && pImpDesc->FirstThunk)
+ {
+ pModPE->cImportModules++;
+ pImpDesc++;
+ }
+ }
+ }
+ return pModPE->cImportModules;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModPEGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ K_NOREF(pvBits);
+ K_NOREF(BaseAddress);
+
+ pStackInfo->Address = NIL_KLDRADDR;
+ pStackInfo->LinkAddress = NIL_KLDRADDR;
+ pStackInfo->cbStack = pStackInfo->cbStackThread = pModPE->Hdrs.OptionalHeader.SizeOfStackReserve;
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModPEQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc;
+ K_NOREF(pvBits);
+
+ /*
+ * Resolve base address alias if any.
+ */
+ rc = kldrModPEBitsAndBaseAddress(pModPE, NULL, &BaseAddress);
+ if (rc)
+ return rc;
+
+ /*
+ * Convert the address from the header.
+ */
+ *pMainEPAddress = pModPE->Hdrs.OptionalHeader.AddressOfEntryPoint
+ ? BaseAddress + pModPE->Hdrs.OptionalHeader.AddressOfEntryPoint
+ : NIL_KLDRADDR;
+ return 0;
+}
+
+
+/** @copydoc kLdrModEnumDbgInfo */
+static int kldrModPEEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ const IMAGE_DEBUG_DIRECTORY *pDbgDir;
+ KU32 iDbgInfo;
+ KU32 cb;
+ int rc;
+
+ /*
+ * Check that there is a debug directory first.
+ */
+ cb = pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ if ( cb < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return 0;
+
+ /*
+ * Make sure we've got mapped bits.
+ */
+ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, NULL);
+ if (rc)
+ return rc;
+
+ /*
+ * Enumerate the debug directory.
+ */
+ pDbgDir = KLDRMODPE_RVA2TYPE(pvBits,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress,
+ const IMAGE_DEBUG_DIRECTORY *);
+ for (iDbgInfo = 0;; iDbgInfo++, pDbgDir++, cb -= sizeof(IMAGE_DEBUG_DIRECTORY))
+ {
+ KLDRDBGINFOTYPE enmDbgInfoType;
+
+ /* convert the type. */
+ switch (pDbgDir->Type)
+ {
+ case IMAGE_DEBUG_TYPE_UNKNOWN:
+ case IMAGE_DEBUG_TYPE_FPO:
+ case IMAGE_DEBUG_TYPE_COFF: /*stabs dialect??*/
+ case IMAGE_DEBUG_TYPE_MISC:
+ case IMAGE_DEBUG_TYPE_EXCEPTION:
+ case IMAGE_DEBUG_TYPE_FIXUP:
+ case IMAGE_DEBUG_TYPE_BORLAND:
+ default:
+ enmDbgInfoType = KLDRDBGINFOTYPE_UNKNOWN;
+ break;
+ case IMAGE_DEBUG_TYPE_CODEVIEW:
+ enmDbgInfoType = KLDRDBGINFOTYPE_CODEVIEW;
+ break;
+ }
+
+ rc = pfnCallback(pMod, iDbgInfo,
+ enmDbgInfoType, pDbgDir->MajorVersion, pDbgDir->MinorVersion, NULL,
+ pDbgDir->PointerToRawData ? (KLDRFOFF)pDbgDir->PointerToRawData : -1,
+ pDbgDir->AddressOfRawData ? pDbgDir->AddressOfRawData : NIL_KLDRADDR,
+ pDbgDir->SizeOfData,
+ NULL,
+ pvUser);
+ if (rc)
+ break;
+
+ /* next */
+ if (cb <= sizeof(IMAGE_DEBUG_DIRECTORY))
+ break;
+ }
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModPEHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ K_NOREF(pvBits);
+
+ /*
+ * Base this entirely on the presence of a debug directory.
+ */
+ if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
+ < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return KLDR_ERR_NO_DEBUG_INFO;
+ return 0;
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModPEMap(PKLDRMOD pMod)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc;
+
+ /*
+ * Already mapped?
+ */
+ if (pModPE->pvMapping)
+ return KLDR_ERR_ALREADY_MAPPED;
+
+ /*
+ * We've got a common worker which does this.
+ */
+ rc = kldrModPEDoMap(pModPE, 1 /* the real thing */);
+ if (rc)
+ return rc;
+ KLDRMODPE_ASSERT(pModPE->pvMapping);
+ return 0;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModPEUnmap(PKLDRMOD pMod)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (!pModPE->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * We've got a common worker which does this.
+ */
+ rc = kldrModPEDoUnmap(pModPE, pModPE->pvMapping);
+ if (rc)
+ return rc;
+ KLDRMODPE_ASSERT(!pModPE->pvMapping);
+ return 0;
+
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModPEAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModPE->pvMapping;
+ if (!pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ }
+
+ /*
+ * If no TLS directory then there is nothing to do.
+ */
+ if ( !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size
+ || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress)
+ return 0;
+ /** @todo implement TLS. */
+ return -1;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModPEFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModPE->pvMapping;
+ if (!pvMapping)
+ return;
+ }
+
+ /*
+ * If no TLS directory then there is nothing to do.
+ */
+ if ( !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size
+ || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress)
+ return;
+ /** @todo implement TLS. */
+ return;
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModPEReload(PKLDRMOD pMod)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if (!pModPE->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /* the file provider does it all */
+ return kRdrRefresh(pMod->pRdr, (void *)pModPE->pvMapping, pMod->cSegments, pMod->aSegments);
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModPEFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc, rc2;
+
+ /*
+ * Mapped?
+ */
+ if (!pModPE->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Before doing anything we'll have to make all pages writable.
+ */
+ rc = kRdrProtect(pMod->pRdr, (void *)pModPE->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
+ if (rc)
+ return rc;
+
+ /*
+ * Apply base relocations.
+ */
+ rc = kldrModPEDoFixups(pModPE, (void *)pModPE->pvMapping, (KUPTR)pModPE->pvMapping,
+ pModPE->Hdrs.OptionalHeader.ImageBase);
+
+ /*
+ * Resolve imports.
+ */
+ if (!rc)
+ rc = kldrModPEDoImports(pModPE, (void *)pModPE->pvMapping, pfnGetImport, pvUser);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kRdrProtect(pMod->pRdr, (void *)pModPE->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
+ if (!rc && rc2)
+ rc = rc2;
+ return rc;
+}
+
+
+/**
+ * Applies base relocations to a (unprotected) image mapping.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvMapping The mapping to fixup.
+ * @param NewBaseAddress The address to fixup the mapping to.
+ * @param OldBaseAddress The address the mapping is currently fixed up to.
+ */
+static int kldrModPEDoFixups(PKLDRMODPE pModPE, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress)
+{
+ const KLDRADDR Delta = NewBaseAddress - OldBaseAddress;
+ KU32 cbLeft = pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
+ const IMAGE_BASE_RELOCATION *pBR, *pFirstBR;
+
+ /*
+ * Don't don anything if the delta is 0 or there aren't any relocations.
+ */
+ if ( !Delta
+ || !cbLeft
+ || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)
+ return 0;
+
+ /*
+ * Process the fixups block by block.
+ * (These blocks appears to be 4KB on all archs despite the native page size.)
+ */
+ pBR = pFirstBR = KLDRMODPE_RVA2TYPE(pvMapping,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,
+ const IMAGE_BASE_RELOCATION *);
+ while ( cbLeft > sizeof(IMAGE_BASE_RELOCATION)
+ && pBR->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION) /* paranoia */)
+ {
+ union
+ {
+ KU8 *pu8;
+ KU16 *pu16;
+ KU32 *pu32;
+ KU64 *pu64;
+ } uChunk,
+ u;
+ const KU16 *poffFixup = (const KU16 *)(pBR + 1);
+ const KU32 cbBlock = K_MIN(cbLeft, pBR->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION); /* more caution... */
+ KU32 cFixups = cbBlock / sizeof(poffFixup[0]);
+ uChunk.pu8 = KLDRMODPE_RVA2TYPE(pvMapping, pBR->VirtualAddress, KU8 *);
+
+ /*
+ * Loop thru the fixups in this chunk.
+ */
+ while (cFixups > 0)
+ {
+ u.pu8 = uChunk.pu8 + (*poffFixup & 0xfff);
+ switch (*poffFixup >> 12) /* ordered by value. */
+ {
+ /* 0 - Alignment placeholder. */
+ case IMAGE_REL_BASED_ABSOLUTE:
+ break;
+
+ /* 1 - 16-bit, add 2nd 16-bit part of the delta. (rare) */
+ case IMAGE_REL_BASED_HIGH:
+ *u.pu16 += (KU16)(Delta >> 16);
+ break;
+
+ /* 2 - 16-bit, add 1st 16-bit part of the delta. (rare) */
+ case IMAGE_REL_BASED_LOW:
+ *u.pu16 += (KU16)Delta;
+ break;
+
+ /* 3 - 32-bit, add delta. (frequent in 32-bit images) */
+ case IMAGE_REL_BASED_HIGHLOW:
+ *u.pu32 += (KU32)Delta;
+ break;
+
+ /* 4 - 16-bit, add 2nd 16-bit of the delta, sign adjust for the lower 16-bit. one arg. (rare) */
+ case IMAGE_REL_BASED_HIGHADJ:
+ {
+ KI32 i32;
+ if (cFixups <= 1)
+ return KLDR_ERR_PE_BAD_FIXUP;
+
+ i32 = (KU32)*u.pu16 << 16;
+ i32 |= *++poffFixup; cFixups--; /* the addend argument */
+ i32 += (KU32)Delta;
+ i32 += 0x8000;
+ *u.pu16 = (KU16)(i32 >> 16);
+ break;
+ }
+
+ /* 5 - 32-bit MIPS JMPADDR, no implemented. */
+ case IMAGE_REL_BASED_MIPS_JMPADDR:
+ *u.pu32 = (*u.pu32 & 0xc0000000)
+ | ((KU32)((*u.pu32 << 2) + (KU32)Delta) >> 2);
+ break;
+
+ /* 6 - Intra section? Reserved value in later specs. Not implemented. */
+ case IMAGE_REL_BASED_SECTION:
+ KLDRMODPE_ASSERT(!"SECTION");
+ return KLDR_ERR_PE_BAD_FIXUP;
+
+ /* 7 - Relative intra section? Reserved value in later specs. Not implemented. */
+ case IMAGE_REL_BASED_REL32:
+ KLDRMODPE_ASSERT(!"SECTION");
+ return KLDR_ERR_PE_BAD_FIXUP;
+
+ /* 8 - reserved according to binutils... */
+ case 8:
+ KLDRMODPE_ASSERT(!"RESERVERED8");
+ return KLDR_ERR_PE_BAD_FIXUP;
+
+ /* 9 - IA64_IMM64 (/ MIPS_JMPADDR16), no specs nor need to support the platform yet.
+ * Bet this requires more code than all the other fixups put together in good IA64 spirit :-) */
+ case IMAGE_REL_BASED_IA64_IMM64:
+ KLDRMODPE_ASSERT(!"IA64_IMM64 / MIPS_JMPADDR16");
+ return KLDR_ERR_PE_BAD_FIXUP;
+
+ /* 10 - 64-bit, add delta. (frequently in 64-bit images) */
+ case IMAGE_REL_BASED_DIR64:
+ *u.pu64 += (KU64)Delta;
+ break;
+
+ /* 11 - 16-bit, add 3rd 16-bit of the delta, sign adjust for the lower 32-bit. two args. (rare) */
+ case IMAGE_REL_BASED_HIGH3ADJ:
+ {
+ KI64 i64;
+ if (cFixups <= 2)
+ return KLDR_ERR_PE_BAD_FIXUP;
+
+ i64 = (KU64)*u.pu16 << 32
+ | ((KU32)poffFixup[2] << 16)
+ | poffFixup[1];
+ i64 += Delta;
+ i64 += 0x80008000UL;
+ *u.pu16 = (KU16)(i64 >> 32);
+ /* skip the addends arguments */
+ poffFixup += 2;
+ cFixups -= 2;
+ break;
+ }
+
+ /* the rest are yet to be defined.*/
+ default:
+ return KLDR_ERR_PE_BAD_FIXUP;
+ }
+
+ /*
+ * Next relocation.
+ */
+ poffFixup++;
+ cFixups--;
+ }
+
+
+ /*
+ * Next block.
+ */
+ cbLeft -= pBR->SizeOfBlock;
+ pBR = (PIMAGE_BASE_RELOCATION)((KUPTR)pBR + pBR->SizeOfBlock);
+ }
+
+ return 0;
+}
+
+
+
+/**
+ * Resolves imports.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvMapping The mapping which imports should be resolved.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModPEDoImports(PKLDRMODPE pModPE, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ const IMAGE_IMPORT_DESCRIPTOR *pImpDesc;
+
+ /*
+ * If no imports, there is nothing to do.
+ */
+ kldrModPENumberOfImports(pModPE->pMod, pvMapping);
+ if (!pModPE->cImportModules)
+ return 0;
+
+ pImpDesc = KLDRMODPE_RVA2TYPE(pvMapping,
+ pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,
+ const IMAGE_IMPORT_DESCRIPTOR *);
+ if (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
+ return kldrModPEDoImports32Bit(pModPE, pvMapping, pImpDesc, pfnGetImport, pvUser);
+ return kldrModPEDoImports64Bit(pModPE, pvMapping, pImpDesc, pfnGetImport, pvUser);
+}
+
+
+/**
+ * Resolves imports, 32-bit image.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvMapping The mapping which imports should be resolved.
+ * @param pImpDesc Pointer to the first import descriptor.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModPEDoImports32Bit(PKLDRMODPE pModPE, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMOD pMod = pModPE->pMod;
+ KU32 iImp;
+
+ /*
+ * Iterate the import descriptors.
+ */
+ for (iImp = 0; iImp < pModPE->cImportModules; iImp++, pImpDesc++)
+ {
+ PIMAGE_THUNK_DATA32 pFirstThunk = KLDRMODPE_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, PIMAGE_THUNK_DATA32);
+ const IMAGE_THUNK_DATA32 *pThunk = pImpDesc->u.OriginalFirstThunk
+ ? KLDRMODPE_RVA2TYPE(pvMapping, pImpDesc->u.OriginalFirstThunk, const IMAGE_THUNK_DATA32 *)
+ : KLDRMODPE_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, const IMAGE_THUNK_DATA32 *);
+
+ /* Iterate the thunks. */
+ while (pThunk->u1.Ordinal != 0)
+ {
+ KLDRADDR Value;
+ KU32 fKind = KLDRSYMKIND_REQ_FLAT;
+ int rc;
+
+ /* Ordinal or name import? */
+ if (IMAGE_SNAP_BY_ORDINAL32(pThunk->u1.Ordinal))
+ rc = pfnGetImport(pMod, iImp, IMAGE_ORDINAL32(pThunk->u1.Ordinal), NULL, 0, NULL, &Value, &fKind, pvUser);
+ else if (KLDRMODPE_VALID_RVA(pModPE, pThunk->u1.Ordinal))
+ {
+ const IMAGE_IMPORT_BY_NAME *pName = KLDRMODPE_RVA2TYPE(pvMapping, pThunk->u1.Ordinal, const IMAGE_IMPORT_BY_NAME *);
+ rc = pfnGetImport(pMod, iImp, NIL_KLDRMOD_SYM_ORDINAL, (const char *)pName->Name,
+ kHlpStrLen((const char *)pName->Name), NULL, &Value, &fKind, pvUser);
+ }
+ else
+ {
+ KLDRMODPE_ASSERT(!"bad 32-bit import");
+ return KLDR_ERR_PE_BAD_IMPORT;
+ }
+ if (rc)
+ return rc;
+
+ /* Apply it. */
+ pFirstThunk->u1.Function = (KU32)Value;
+ if (pFirstThunk->u1.Function != Value)
+ {
+ KLDRMODPE_ASSERT(!"overflow");
+ return KLDR_ERR_ADDRESS_OVERFLOW;
+ }
+
+ /* next */
+ pThunk++;
+ pFirstThunk++;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Resolves imports, 64-bit image.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvMapping The mapping which imports should be resolved.
+ * @param pImpDesc Pointer to the first import descriptor.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModPEDoImports64Bit(PKLDRMODPE pModPE, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMOD pMod = pModPE->pMod;
+ KU32 iImp;
+
+ /*
+ * Iterate the import descriptors.
+ */
+ for (iImp = 0; iImp < pModPE->cImportModules; iImp++, pImpDesc++)
+ {
+ PIMAGE_THUNK_DATA64 pFirstThunk = KLDRMODPE_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, PIMAGE_THUNK_DATA64);
+ const IMAGE_THUNK_DATA64 *pThunk = pImpDesc->u.OriginalFirstThunk
+ ? KLDRMODPE_RVA2TYPE(pvMapping, pImpDesc->u.OriginalFirstThunk, const IMAGE_THUNK_DATA64 *)
+ : KLDRMODPE_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, const IMAGE_THUNK_DATA64 *);
+
+ /* Iterate the thunks. */
+ while (pThunk->u1.Ordinal != 0)
+ {
+ KLDRADDR Value;
+ KU32 fKind = KLDRSYMKIND_REQ_FLAT;
+ int rc;
+
+ /* Ordinal or name import? */
+ if (IMAGE_SNAP_BY_ORDINAL64(pThunk->u1.Ordinal))
+ rc = pfnGetImport(pMod, iImp, (KU32)IMAGE_ORDINAL64(pThunk->u1.Ordinal), NULL, 0, NULL, &Value, &fKind, pvUser);
+ else if (KLDRMODPE_VALID_RVA(pModPE, pThunk->u1.Ordinal))
+ {
+ const IMAGE_IMPORT_BY_NAME *pName = KLDRMODPE_RVA2TYPE(pvMapping, pThunk->u1.Ordinal, const IMAGE_IMPORT_BY_NAME *);
+ rc = pfnGetImport(pMod, iImp, NIL_KLDRMOD_SYM_ORDINAL, (const char *)pName->Name,
+ kHlpStrLen((const char *)pName->Name), NULL, &Value, &fKind, pvUser);
+ }
+ else
+ {
+ KLDRMODPE_ASSERT(!"bad 64-bit import");
+ return KLDR_ERR_PE_BAD_IMPORT;
+ }
+ if (rc)
+ return rc;
+
+ /* Apply it. */
+ pFirstThunk->u1.Function = Value;
+
+ /* next */
+ pThunk++;
+ pFirstThunk++;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/** @copydoc kLdrModCallInit */
+static int kldrModPECallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModPE->pvMapping;
+ if (!pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ }
+
+ /*
+ * Do TLS callbacks first and then call the init/term function if it's a DLL.
+ */
+ rc = kldrModPEDoCallTLS(pModPE, pvMapping, DLL_PROCESS_ATTACH, uHandle);
+ if ( !rc
+ && (pModPE->Hdrs.FileHeader.Characteristics & IMAGE_FILE_DLL))
+ {
+ rc = kldrModPEDoCallDLL(pModPE, pvMapping, DLL_PROCESS_ATTACH, uHandle);
+ if (rc)
+ kldrModPEDoCallTLS(pModPE, pvMapping, DLL_PROCESS_DETACH, uHandle);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Call the DLL entrypoint.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MODULE_INIT_FAILED or KLDR_ERR_THREAD_ATTACH_FAILED on failure.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvMapping The module mapping to use (resolved).
+ * @param uOp The operation (DLL_*).
+ * @param uHandle The module handle to present.
+ */
+static int kldrModPEDoCallDLL(PKLDRMODPE pModPE, void *pvMapping, unsigned uOp, KUPTR uHandle)
+{
+ int rc;
+
+ /*
+ * If no entrypoint there isn't anything to be done.
+ */
+ if (!pModPE->Hdrs.OptionalHeader.AddressOfEntryPoint)
+ return 0;
+
+ /*
+ * Invoke the entrypoint and convert the boolean result to a kLdr status code.
+ */
+ rc = kldrModPEDoCall((KUPTR)pvMapping + pModPE->Hdrs.OptionalHeader.AddressOfEntryPoint, uHandle, uOp, NULL);
+ if (rc)
+ rc = 0;
+ else if (uOp == DLL_PROCESS_ATTACH)
+ rc = KLDR_ERR_MODULE_INIT_FAILED;
+ else if (uOp == DLL_THREAD_ATTACH)
+ rc = KLDR_ERR_THREAD_ATTACH_FAILED;
+ else /* detach: ignore failures */
+ rc = 0;
+ return rc;
+}
+
+
+/**
+ * Call the TLS entrypoints.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_THREAD_ATTACH_FAILED on failure.
+ * @param pModPE The PE module interpreter instance.
+ * @param pvMapping The module mapping to use (resolved).
+ * @param uOp The operation (DLL_*).
+ * @param uHandle The module handle to present.
+ */
+static int kldrModPEDoCallTLS(PKLDRMODPE pModPE, void *pvMapping, unsigned uOp, KUPTR uHandle)
+{
+ /** @todo implement TLS support. */
+ K_NOREF(pModPE);
+ K_NOREF(pvMapping);
+ K_NOREF(uOp);
+ K_NOREF(uHandle);
+ return 0;
+}
+
+
+/**
+ * Do a 3 parameter callback.
+ *
+ * @returns 32-bit callback return.
+ * @param uEntrypoint The address of the function to be called.
+ * @param uHandle The first argument, the module handle.
+ * @param uOp The second argumnet, the reason we're calling.
+ * @param pvReserved The third argument, reserved argument. (figure this one out)
+ */
+KI32 kldrModPEDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved)
+{
+ KI32 rc;
+
+/** @todo try/except */
+#if defined(__X86__) || defined(__i386__) || defined(_M_IX86)
+ /*
+ * Be very careful.
+ * Not everyone will have got the calling convention right.
+ */
+# ifdef __GNUC__
+ __asm__ __volatile__(
+ "pushl %2\n\t"
+ "pushl %1\n\t"
+ "pushl %0\n\t"
+ "lea 12(%%esp), %2\n\t"
+ "call *%3\n\t"
+ "movl %2, %%esp\n\t"
+ : "=a" (rc)
+ : "d" (uOp),
+ "S" (0),
+ "c" (uEntrypoint),
+ "0" (uHandle));
+# elif defined(_MSC_VER)
+ __asm {
+ mov eax, [uHandle]
+ mov edx, [uOp]
+ mov ecx, 0
+ mov ebx, [uEntrypoint]
+ push edi
+ mov edi, esp
+ push ecx
+ push edx
+ push eax
+ call ebx
+ mov esp, edi
+ pop edi
+ mov [rc], eax
+ }
+# else
+# error "port me!"
+# endif
+
+#elif defined(__AMD64__) || defined(__x86_64__) || defined(_M_IX86)
+ /*
+ * For now, let's just get the work done...
+ */
+ /** @todo Deal with GCC / MSC differences in some sensible way. */
+ int (*pfn)(KUPTR uHandle, KU32 uOp, void *pvReserved);
+ pfn = (int (*)(KUPTR uHandle, KU32 uOp, void *pvReserved))uEntrypoint;
+ rc = pfn(uHandle, uOp, NULL);
+
+#else
+# error "port me"
+#endif
+ K_NOREF(pvReserved);
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModPECallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModPE->pvMapping;
+ if (!pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ }
+
+ /*
+ * Do TLS callbacks first.
+ */
+ kldrModPEDoCallTLS(pModPE, pvMapping, DLL_PROCESS_DETACH, uHandle);
+ if (pModPE->Hdrs.FileHeader.Characteristics & IMAGE_FILE_DLL)
+ kldrModPEDoCallDLL(pModPE, pvMapping, DLL_PROCESS_DETACH, uHandle);
+
+ return 0;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModPECallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ unsigned uOp = fAttachingOrDetaching ? DLL_THREAD_ATTACH : DLL_THREAD_DETACH;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (pvMapping == KLDRMOD_INT_MAP)
+ {
+ pvMapping = (void *)pModPE->pvMapping;
+ if (!pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ }
+
+ /*
+ * Do TLS callbacks first and then call the init/term function if it's a DLL.
+ */
+ rc = kldrModPEDoCallTLS(pModPE, pvMapping, uOp, uHandle);
+ if (!fAttachingOrDetaching)
+ rc = 0;
+ if ( !rc
+ && (pModPE->Hdrs.FileHeader.Characteristics & IMAGE_FILE_DLL))
+ {
+ rc = kldrModPEDoCallDLL(pModPE, pvMapping, uOp, uHandle);
+ if (!fAttachingOrDetaching)
+ rc = 0;
+ if (rc)
+ kldrModPEDoCallTLS(pModPE, pvMapping, uOp, uHandle);
+ }
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModSize */
+static KLDRADDR kldrModPESize(PKLDRMOD pMod)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ return pModPE->Hdrs.OptionalHeader.SizeOfImage;
+}
+
+
+/** @copydoc kLdrModGetBits */
+static int kldrModPEGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ KU32 i;
+ int rc;
+
+ /*
+ * Zero the entire buffer first to simplify things.
+ */
+ kHlpMemSet(pvBits, 0, pModPE->Hdrs.OptionalHeader.SizeOfImage);
+
+ /*
+ * Iterate the segments and read the data within them.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ /* skip it? */
+ if ( pMod->aSegments[i].cbFile == -1
+ || pMod->aSegments[i].offFile == -1
+ || pMod->aSegments[i].LinkAddress == NIL_KLDRADDR
+ || !pMod->aSegments[i].Alignment)
+ continue;
+ rc = kRdrRead(pMod->pRdr,
+ (KU8 *)pvBits + (pMod->aSegments[i].LinkAddress - pModPE->Hdrs.OptionalHeader.ImageBase),
+ pMod->aSegments[i].cbFile,
+ pMod->aSegments[i].offFile);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Perform relocations.
+ */
+ return kldrModPERelocateBits(pMod, pvBits, BaseAddress, pModPE->Hdrs.OptionalHeader.ImageBase, pfnGetImport, pvUser);
+
+}
+
+
+/** @copydoc kLdrModRelocateBits */
+static int kldrModPERelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+ PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData;
+ int rc;
+
+ /*
+ * Call workers to do the jobs.
+ */
+ rc = kldrModPEDoFixups(pModPE, pvBits, NewBaseAddress, OldBaseAddress);
+ if (!rc)
+ rc = kldrModPEDoImports(pModPE, pvBits, pfnGetImport, pvUser);
+
+ return rc;
+}
+
+
+/**
+ * The PE module interpreter method table.
+ */
+KLDRMODOPS g_kLdrModPEOps =
+{
+ "PE",
+ NULL,
+ kldrModPECreate,
+ kldrModPEDestroy,
+ kldrModPEQuerySymbol,
+ kldrModPEEnumSymbols,
+ kldrModPEGetImport,
+ kldrModPENumberOfImports,
+ NULL /* can execute one is optional */,
+ kldrModPEGetStackInfo,
+ kldrModPEQueryMainEntrypoint,
+ NULL /* pfnQueryImageUuid */,
+ NULL, /** @todo resources */
+ NULL, /** @todo resources */
+ kldrModPEEnumDbgInfo,
+ kldrModPEHasDbgInfo,
+ kldrModPEMap,
+ kldrModPEUnmap,
+ kldrModPEAllocTLS,
+ kldrModPEFreeTLS,
+ kldrModPEReload,
+ kldrModPEFixupMapping,
+ kldrModPECallInit,
+ kldrModPECallTerm,
+ kldrModPECallThread,
+ kldrModPESize,
+ kldrModPEGetBits,
+ kldrModPERelocateBits,
+ NULL, /** @todo mostly done */
+ 42 /* the end */
+};
diff --git a/src/lib/kStuff/kLdr/testcase/Makefile.kmk b/src/lib/kStuff/kLdr/testcase/Makefile.kmk
new file mode 100644
index 0000000..7b3efb6
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/Makefile.kmk
@@ -0,0 +1,305 @@
+# $Id: Makefile.kmk 29 2009-07-01 20:30:29Z bird $
+## @file
+# kBuild Makefile for the kLdr testcases.
+#
+
+#
+# Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+
+# generate rules.
+DEPTH ?= ../..
+SUB_DEPTH = ../..
+include $(PATH_KBUILD)/subheader.kmk
+
+
+#
+# Templates for the testcases.
+#
+TEMPLATE_TST = Testcase template
+ifeq ($(BUILD_TARGET),win)
+ ifeq ($(BUILD_TARGET_ARCH),x86)
+ TEMPLATE_TST_TOOL = VCC70
+ TEMPLATE_TST_CFLAGS = -W3 -Zi -Zl -MD
+ TEMPLATE_TST_CXXFLAGS = -W3 -Zi -Zl -MD
+ TEMPLATE_TST_LIBS = \
+ $(PATH_TOOL_VCC70_LIB)/oldnames.lib \
+ $(PATH_TOOL_VCC70_LIB)/msvcrt.lib
+ else
+ TEMPLATE_TST_TOOL = VCC80AMD64
+ TEMPLATE_TST_CFLAGS = -W3 -Zi -Zl -MD
+ TEMPLATE_TST_CXXFLAGS = -W3 -Zi -Zl -MD
+ TEMPLATE_TST_LIBS = \
+ $(PATH_TOOL_VCC80AMD64_LIB)/oldnames.lib \
+ $(PATH_TOOL_VCC80AMD64_LIB)/msvcrt.lib
+ endif
+ TEMPLATE_TST_CFLAGS.release = -O2
+ TEMPLATE_TST_CXXFLAGS.release = -O2
+ TEMPLATE_TST_ASFLAGS = -f win
+ TEMPLATE_TST_DEFS = __WIN__
+ TEMPLATE_TST_SDKS.x86 = WIN32SDK
+ TEMPLATE_TST_SDKS.amd64 = WIN64SDK
+
+else
+ TEMPLATE_TST_CFLAGS = -Wall -pedantic -g
+ TEMPLATE_TST_CFLAGS.release = -O2
+ TEMPLATE_TST_LDFLAGS =
+ ifneq ($(filter os2,$(BUILD_TARGET)),)
+ TEMPLATE_TST_TOOL = GCC3OMF
+ TEMPLATE_TST_ASFLAGS = -f obj
+ TEMPLATE_TST_LIBS = os2 gcc end
+ else ifneq ($(filter darwin,$(BUILD_TARGET)),)
+ TEMPLATE_TST_TOOL = GCC4MACHO
+ TEMPLATE_TST_ASFLAGS = -f macho
+ TEMPLATE_TST_DEFS = __DARWIN__
+ TEMPLATE_TST_LIBS =
+ else
+ TEMPLATE_TST_TOOL = GCC3
+ TEMPLATE_TST_ASFLAGS = -f elf
+ TEMPLATE_TST_LIBS = gcc
+ endif
+endif
+TEMPLATE_TST_INCS := $(PATH_SUB_CURRENT) $(PATH_SUB_ROOT)/include
+
+
+TEMPLATE_TSTPROG = Testcase program template
+TEMPLATE_TSTPROG_EXTENDS = TST
+
+
+TEMPLATE_TSTDLL = Testcase dll template
+TEMPLATE_TSTDLL_EXTENDS = TST
+
+
+TEMPLATE_TSTBARE = Bare bone testcase template
+ifeq ($(BUILD_TARGET),win)
+ ifeq ($(BUILD_TARGET_ARCH),x86)
+ TEMPLATE_TSTBARE_TOOL = VCC70
+ else
+ TEMPLATE_TSTBARE_TOOL = VCC80AMD64
+ endif
+ TEMPLATE_TSTBARE_CFLAGS = -W3 -Zi -Zl
+ TEMPLATE_TSTBARE_CFLAGS.release = -O2
+ TEMPLATE_TSTBARE_CXXFLAGS = -W3 -Zi -Zl
+ TEMPLATE_TSTBARE_CXXFLAGS.release = -O2
+ TEMPLATE_TSTBARE_ASFLAGS = -f win
+ TEMPLATE_TSTBARE_DEFS = __WIN__
+ TEMPLATE_TSTBARE_SDKS.x86 = WIN32SDK
+ TEMPLATE_TSTBARE_SDKS.amd64 = WIN64SDK
+
+else
+ TEMPLATE_TSTBARE_CFLAGS = -Wall -pedantic -g
+ TEMPLATE_TSTBARE_CFLAGS.release = -O2
+ TEMPLATE_TSTBARE_LDFLAGS = -nostdlib -lgcc
+ ifeq ($(filter-out os2,$(BUILD_TARGET)),)
+ TEMPLATE_TSTBARE_TOOL = GCC3OMF
+ TEMPLATE_TSTBARE_ASFLAGS = -f obj
+ TEMPLATE_TSTBARE_ASTOOL = NASM
+ TEMPLATE_TSTBARE_DEFS = main=main_wrapped
+ TEMPLATE_TSTBARE_LIBS = os2
+ else ifeq ($(filter-out darwin,$(BUILD_TARGET)),)
+ TEMPLATE_TSTBARE_TOOL = GCC4MACHO
+ TEMPLATE_TSTBARE_ASFLAGS = -f macho
+ TEMPLATE_TSTBARE_ASTOOL = NASM
+ TEMPLATE_TSTBARE_DEFS = __DARWIN__
+ TEMPLATE_TSTBARE_LIBS =
+ TEMPLATE_TSTBARE_CFLAGS += -static -fno-common
+ TEMPLATE_TSTBARE_LDFLAGS += -nostdlib -r
+ else
+ TEMPLATE_TSTBARE_TOOL = GCC3
+ TEMPLATE_TSTBARE_ASFLAGS = -f elf
+ TEMPLATE_TSTBARE_LIBS = gcc
+ endif
+endif
+TEMPLATE_TSTBARE_INCS := $(PATH_SUB_CURRENT) $(PATH_SUB_ROOT)/include
+
+TEMPLATE_TSTBAREPROG = Bare bone testcase program template
+TEMPLATE_TSTBAREPROG_EXTENDS = TSTBARE
+ifneq ($(filter win win32 win64,$(BUILD_TARGET)),)
+TEMPLATE_TSTBAREPROG_LDFLAGS += -Entry:WindowsMain -FIXED:NO
+else
+TEMPLATE_TSTBAREPROG_LDFLAGS.nt += -FIXED:NO
+endif
+
+
+TEMPLATE_TSTBAREDLL = Bare bone testcase dll template
+TEMPLATE_TSTBAREDLL_EXTENDS = TSTBARE
+ifeq ($(BUILD_TARGET),win)
+ TEMPLATE_TSTBAREDLL_LDFLAGS += -Entry:DllMain
+else ifeq ($(BUILD_TARGET),darwin)
+# TEMPLATE_TSTBAREDLL_CFLAGS += -dynamiclib
+# TEMPLATE_TSTBAREDLL_LDFLAGS += -dynamiclib
+endif
+
+
+
+
+#
+# tst-0: four dlls, three of which depends on the 4th and no external dependencies.
+# The purpose of this testcase is to debug the dynamic loader without
+# messing with the native loader at all.
+#
+PROGRAMS += tst-0 tst-0-driver
+DLLS += tst-0-a tst-0-b tst-0-c tst-0-d
+
+tst-0-driver_TEMPLATE = TSTPROG
+tst-0-driver_SOURCES = tst-0-driver.c
+
+tst-0-a_TEMPLATE = TSTBAREDLL
+tst-0-a_SOURCES = tst-0-a.c tstDllMainStub.c
+tst-0-a_SOURCES.os2= tstDllMainStub-os2.asm
+
+tst-0-b_TEMPLATE = TSTBAREDLL
+tst-0-b_SOURCES = tst-0-b.c tstDllMainStub.c
+tst-0-b_SOURCES.os2= tstDllMainStub-os2.asm
+
+tst-0-c_TEMPLATE = TSTBAREDLL
+tst-0-c_SOURCES = tst-0-c.c tstDllMainStub.c
+tst-0-c_SOURCES.os2= tstDllMainStub-os2.asm
+
+tst-0-d_TEMPLATE = TSTBAREDLL
+tst-0-d_SOURCES = tst-0-d.c tstDllMainStub.c
+tst-0-d_SOURCES.os2= tstDllMainStub-os2.asm
+
+tst-0_TEMPLATE = TSTBAREPROG
+tst-0_SOURCES = tst-0.c tstExeMainStub.c
+tst-0_SOURCES.os2= tstExeMainStub-os2.asm
+
+ifeq ($(BUILD_TARGET),win)
+tst-0-driver_LIBS= $(PATH_LIB)/kLdr.lib
+tst-0-a_LIBS = $(PATH_TARGET)/tst-0-d/tst-0-d.lib
+tst-0-b_LIBS = $(PATH_TARGET)/tst-0-d/tst-0-d.lib
+tst-0-c_LIBS = $(PATH_TARGET)/tst-0-d/tst-0-d.lib
+tst-0_LIBS = $(TARGET_tst-0-a:.dll=.lib) $(TARGET_tst-0-b:.dll=.lib) $(TARGET_tst-0-c:.dll=.lib)
+else
+tst-0-driver_LIBS= $(PATH_DLL)/kLdr$(SUFF_DLL)
+tst-0-a_LIBS = $(subst -a,-d,$(TARGET_tst-0-a))
+tst-0-b_LIBS = $(subst -b,-d,$(TARGET_tst-0-b))
+tst-0-c_LIBS = $(subst -c,-d,$(TARGET_tst-0-c))
+tst-0_LIBS = $(TARGET_tst-0-a) $(TARGET_tst-0-b) $(TARGET_tst-0-c)
+endif
+
+
+#
+# tst-1: four dlls, three of which depends on the 4th and the testcase depends on those three again.
+#
+PROGRAMS += tst-1
+DLLS += tst-1-a tst-1-b tst-1-c tst-1-d
+
+tst-1-a_TEMPLATE = TSTDLL
+tst-1-a_SOURCES = tst-1-a.c tstDllMain.c
+
+tst-1-b_TEMPLATE = TSTDLL
+tst-1-b_SOURCES = tst-1-b.c tstDllMain.c
+
+tst-1-c_TEMPLATE = TSTDLL
+tst-1-c_SOURCES = tst-1-c.c tstDllMain.c
+
+tst-1-d_TEMPLATE = TSTDLL
+tst-1-d_SOURCES = tst-1-d.c tstDllMain.c
+
+tst-1_TEMPLATE = TSTPROG
+tst-1_SOURCES = tst-1.c
+
+ifeq ($(BUILD_TARGET),win)
+tst-1-a_LIBS = $(PATH_TARGET)/tst-1-d/tst-1-d.lib
+tst-1-b_LIBS = $(PATH_TARGET)/tst-1-d/tst-1-d.lib
+tst-1-c_LIBS = $(PATH_TARGET)/tst-1-d/tst-1-d.lib
+tst-1_LIBS = $(TARGET_tst-1-a:.dll=.lib) $(TARGET_tst-1-b:.dll=.lib) $(TARGET_tst-1-c:.dll=.lib)
+else
+tst-1-a_LIBS = $(subst -a,-d,$(TARGET_tst-1-a))
+tst-1-b_LIBS = $(subst -b,-d,$(TARGET_tst-1-b))
+tst-1-c_LIBS = $(subst -c,-d,$(TARGET_tst-1-c))
+tst-1_LIBS = $(TARGET_tst-1-a) $(TARGET_tst-1-b) $(TARGET_tst-1-c)
+endif
+
+
+#
+# tst-2: four dlls, three of which depends on the 1st, and the testcase depends on those all of them.
+#
+PROGRAMS += tst-2
+DLLS += tst-2-a tst-2-b tst-2-c tst-2-d
+
+tst-2-a_TEMPLATE = TSTDLL
+tst-2-a_SOURCES = tst-2-a.c tstDllMain.c
+
+tst-2-b_TEMPLATE = TSTDLL
+tst-2-b_SOURCES = tst-2-b.c tstDllMain.c
+
+tst-2-c_TEMPLATE = TSTDLL
+tst-2-c_SOURCES = tst-2-c.c tstDllMain.c
+
+tst-2-d_TEMPLATE = TSTDLL
+tst-2-d_SOURCES = tst-2-d.c tstDllMain.c
+
+tst-2_TEMPLATE = TSTPROG
+tst-2_SOURCES = tst-2.c
+
+ifeq ($(BUILD_TARGET),win)
+tst-2-b_LIBS = $(PATH_TARGET)/tst-2-a/tst-2-a.lib
+tst-2-c_LIBS = $(PATH_TARGET)/tst-2-a/tst-2-a.lib
+tst-2-d_LIBS = $(PATH_TARGET)/tst-2-a/tst-2-a.lib
+tst-2_LIBS = $(TARGET_tst-2-b:.dll=.lib) $(TARGET_tst-2-c:.dll=.lib) $(TARGET_tst-2-d:.dll=.lib) $(TARGET_tst-2-a:.dll=.lib)
+else
+tst-2-b_LIBS = $(subst -b,-a,$(TARGET_tst-2-b))
+tst-2-c_LIBS = $(subst -c,-a,$(TARGET_tst-2-c))
+tst-2-d_LIBS = $(subst -d,-a,$(TARGET_tst-2-d))
+tst-2_LIBS = $(TARGET_tst-2-a) $(TARGET_tst-2-b) $(TARGET_tst-2-c) $(TARGET_tst-2-d)
+endif
+
+
+#
+# tst-3: Single module.
+#
+PROGRAMS += tst-3-driver
+ifeq ($(BUILD_TARGET),darwin)
+SYSMODS += tst-3
+else
+DLLS += tst-3
+LIBRARIES.win += tst-3-imp
+LIBRARIES.os2 += tst-3-imp
+endif
+
+tst-3_TEMPLATE = TSTBAREDLL
+tst-3_SOURCES = tst-3.c tst-3-ext.c tstDllMainStub.c
+tst-3_SOURCES.os2= tstDllMainStub-os2.asm
+tst-3_LIBS.os2 = $(TARGET_tst-3-imp)
+tst-3_LIBS.win = $(TARGET_tst-3-imp)
+
+tst-3-imp_TEMPLATE = TSTBAREDLL
+tst-3-imp_SOURCES.win = tst-3-imp-win.def
+tst-3-imp_SOURCES.os2 = tst-3-imp-os2.def
+
+tst-3-driver_TEMPLATE = TSTPROG
+tst-3-driver_SOURCES = tst-3-driver.c
+
+ifeq ($(BUILD_TARGET),win)
+tst-3-driver_LIBS = $(PATH_LIB)/kLdr.lib
+else
+tst-3-driver_LIBS = $(PATH_DLL)/kLdr$(SUFF_DLL)
+endif
+
+
+# generate rules.
+include $(PATH_KBUILD)/subfooter.kmk
+
diff --git a/src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86 b/src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86
new file mode 100644
index 0000000..2a48ccb
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86
Binary files differ
diff --git a/src/lib/kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86 b/src/lib/kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86
new file mode 100644
index 0000000..a18a919
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86
Binary files differ
diff --git a/src/lib/kStuff/kLdr/testcase/tst-0-a.c b/src/lib/kStuff/kLdr/testcase/tst-0-a.c
new file mode 100644
index 0000000..a5533f2
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-0-a.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "a";
+
+MY_IMPORT(int) FuncD(void);
+
+MY_EXPORT(int) FuncA(void)
+{
+ return FuncD() | (g_pszName[0] == 'a' ? 0x42 : 0x0001);
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-0-b.c b/src/lib/kStuff/kLdr/testcase/tst-0-b.c
new file mode 100644
index 0000000..286b179
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-0-b.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "b";
+
+MY_IMPORT(int) FuncD(void);
+
+MY_EXPORT(int) FuncB(void)
+{
+ return FuncD() | (g_pszName[0] == 'b' ? 0x4200 : 0x0010);
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-0-c.c b/src/lib/kStuff/kLdr/testcase/tst-0-c.c
new file mode 100644
index 0000000..3c9d449
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-0-c.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "c";
+
+MY_IMPORT(int) FuncD(void);
+
+MY_EXPORT(int) FuncC(void)
+{
+ return FuncD() | (g_pszName[0] == 'c' ? 0x420000 : 0x0100);
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-0-d.c b/src/lib/kStuff/kLdr/testcase/tst-0-d.c
new file mode 100644
index 0000000..a5501b0
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-0-d.c
@@ -0,0 +1,8 @@
+#include "tst.h"
+const char *g_pszName = "d";
+
+MY_EXPORT(int) FuncD(void)
+{
+ return g_pszName[0] == 'd' ? 0x42000000 : 0x1000;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-0-driver.c b/src/lib/kStuff/kLdr/testcase/tst-0-driver.c
new file mode 100644
index 0000000..be304d5
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-0-driver.c
@@ -0,0 +1,502 @@
+/* $Id: tst-0-driver.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Dynamic Loader testcase no. 0, Driver.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "tst.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** Select the appropriate KLDRSYMKIND bit define. */
+#define MY_KLDRSYMKIND_BITS ( sizeof(void *) == 4 ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT )
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The numbers of errors. */
+static int g_cErrors = 0;
+
+
+
+/**
+ * Report failure.
+ */
+static int Failure(const char *pszFormat, ...)
+{
+ va_list va;
+
+ g_cErrors++;
+
+ printf("tst-0-driver: ");
+ va_start(va, pszFormat);
+ vprintf(pszFormat, va);
+ va_end(va);
+ printf("\n");
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ const char *pszErrInit = "Error, szErr wasn't zapped";
+ char szErr[512];
+ char szBuf[512];
+ char *psz;
+ KSIZE cch;
+ HKLDRMOD hMod;
+ int rc;
+
+ /*
+ * The first thing to do is a simple load / unload test
+ * using the tst-0-a library (it'll drag in tst-0-d).
+ */
+ printf("tst-0-driver: Basic API test using 'tst-0-a'...\n");
+ hMod = (HKLDRMOD)0xffffeeee;
+ strcpy(szErr, pszErrInit);
+ rc = kLdrDyldLoad("tst-0-a", NULL, NULL, KLDRDYLD_SEARCH_HOST,
+ KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT, &hMod, szErr, sizeof(szErr));
+ if (rc)
+ Failure("kLdrDyldLoad(\"tst-0\",...) failed, rc=%d (%#x). szErr='%s'.\n", rc, rc, szErr);
+ if (!strcmp(szErr, pszErrInit))
+ Failure("szErr wasn't set.\n");
+ if (hMod == (HKLDRMOD)0xffffeeee)
+ Failure("hMod wasn't set.\n");
+ if (hMod == NIL_HKLDRMOD && !rc)
+ Failure("rc=0 but hMod=NIL_HKLDRMOD\n");
+ if (!rc)
+ {
+ HKLDRMOD hMod2;
+ HKLDRMOD hMod3;
+ printf("tst-0-driver: hMod=%p ('tst-0-a')\n", (void *)hMod);
+
+ /*
+ * Simple test of kLdrDyldFindByName.
+ */
+ hMod2 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName("tst-0", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod2);
+ if (!rc)
+ Failure("kLdrDyldFindByName(\"tst-0\",,,) didn't fail!\n");
+ if (rc && hMod2 != NIL_HKLDRMOD)
+ Failure("hMod2 wasn't set correctly on kLdrDyldFindByName failure!\n");
+
+ hMod2 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName("tst-0-a", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod2);
+ if (rc)
+ Failure("kLdrDyldFindByName(\"tst-0-a\",,,) failed, rc=%d (%#x)\n", rc, rc);
+ if (!rc && hMod2 != hMod)
+ Failure("kLdrDyldFindByName(\"tst-0-a\",,,) returned the wrong module handle: %p instead of %p\n",
+ (void *)hMod2, (void *)hMod);
+
+ hMod2 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName("tst-0-d", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod2);
+ if (!rc)
+ printf("tst-0-driver: hMod2=%p ('tst-0-d')\n", (void *)hMod2);
+ else
+ Failure("kLdrDyldFindByName(\"tst-0-d\",,,) failed, rc=%d (%#x)\n", rc, rc);
+
+ /*
+ * Get the name and filename for each of the two modules.
+ */
+ rc = kLdrDyldGetName(hMod2, szBuf, sizeof(szBuf));
+ if (!rc)
+ {
+ printf("tst-0-driver: name: '%s' ('tst-0-d')\n", szBuf);
+ psz = strstr(szBuf, "-0-");
+ if ( !psz
+ || strnicmp(psz, "-0-d", sizeof("-0-d") - 1))
+ Failure("kLdrDyldGetName(\"tst-0-d\",,,) -> '%s': pattern '-0-d' not found\n", szBuf);
+
+ /* overflow test. */
+ cch = strlen(szBuf);
+ szBuf[cch + 1] = szBuf[cch] = szBuf[cch - 1] = 'x';
+ szBuf[cch + 2] = '\0';
+ rc = kLdrDyldGetName(hMod2, szBuf, cch);
+ if (rc == KERR_BUFFER_OVERFLOW)
+ {
+ if (!szBuf[0])
+ Failure("kLdrDyldGetName didn't return partial result on overflow\n");
+ else if (szBuf[cch - 1])
+ Failure("kLdrDyldGetName didn't terminate partial result correctly overflow: '%s'\n", szBuf);
+ else if (szBuf[cch] != 'x')
+ Failure("kLdrDyldGetName exceeded the buffer limit on partial overflow: '%s'\n", szBuf);
+ }
+ else
+ Failure("kLdrDyldGetName(\"tst-0-d\",,,) -> rc=%d (%#x) instead of KERR_BUFFER_OVERFLOW\n", rc, rc);
+
+ /* check that we can query the module by the returned name. */
+ rc = kLdrDyldGetName(hMod2, szBuf, sizeof(szBuf));
+ if (!rc)
+ {
+ hMod3 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName(szBuf, NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod3);
+ if (rc || hMod3 != hMod2)
+ Failure("kLdrDyldFindByName(\"%s\",,,) failed, rc=%d (%#x) hMod3=%p hMod2=%p\n",
+ szBuf, rc, rc, (void *)hMod3, (void *)hMod2);
+ }
+ else
+ Failure("kLdrDyldGetName(\"tst-0-d\",,,) failed (b), rc=%d (%#x)\n", rc, rc);
+ }
+ else
+ Failure("kLdrDyldGetName(\"tst-0-d\",,,) failed, rc=%d (%#x)\n", rc, rc);
+
+ rc = kLdrDyldGetFilename(hMod2, szBuf, sizeof(szBuf));
+ if (!rc)
+ {
+ printf("tst-0-driver: filename: '%s' ('tst-0-d')\n", szBuf);
+
+ /* overflow test. */
+ cch = strlen(szBuf);
+ szBuf[cch + 1] = szBuf[cch] = szBuf[cch - 1] = 'x';
+ szBuf[cch + 2] = '\0';
+ rc = kLdrDyldGetFilename(hMod2, szBuf, cch);
+ if (rc == KERR_BUFFER_OVERFLOW)
+ {
+ if (!szBuf[0])
+ Failure("kLdrDyldGetFilename didn't return partial result on overflow\n");
+ else if (szBuf[cch - 1])
+ Failure("kLdrDyldGetFilename didn't terminate partial result correctly overflow: '%s'\n", szBuf);
+ else if (szBuf[cch] != 'x')
+ Failure("kLdrDyldGetFilename exceeded the buffer limit on partial overflow: '%s'\n", szBuf);
+ }
+ else
+ Failure("kLdrDyldGetFilename(\"tst-0-d\",,,) -> rc=%d (%#x) instead of KERR_BUFFER_OVERFLOW\n", rc, rc);
+
+ /* check that we can query the module by the returned filename. */
+ rc = kLdrDyldGetFilename(hMod2, szBuf, sizeof(szBuf));
+ if (!rc)
+ {
+ hMod3 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName(szBuf, NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod3);
+ if (rc || hMod3 != hMod2)
+ Failure("kLdrDyldFindByName(\"%s\",,,) failed, rc=%d (%#x) hMod3=%p hMod2=%p\n",
+ szBuf, rc, rc, (void *)hMod3, (void *)hMod2);
+ }
+ else
+ Failure("kLdrDyldGetName(\"tst-0-d\",,,) failed (b), rc=%d (%#x)\n", rc, rc);
+ }
+ else
+ Failure("kLdrDyldGetFilename(\"tst-0-d\",,,) failed, rc=%d (%#x)\n", rc, rc);
+
+ /* the other module */
+ rc = kLdrDyldGetName(hMod, szBuf, sizeof(szBuf));
+ if (!rc)
+ {
+ printf("tst-0-driver: name: '%s' ('tst-0-a')\n", szBuf);
+ psz = strstr(szBuf, "-0-");
+ if ( !psz
+ || strnicmp(psz, "-0-a", sizeof("-0-a") - 1))
+ Failure("kLdrDyldGetName(\"tst-0-a\",,,) -> '%s': pattern '-0-a' not found\n", szBuf);
+
+ /* check that we can query the module by the returned name. */
+ hMod3 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName(szBuf, NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod3);
+ if (rc || hMod3 != hMod)
+ Failure("kLdrDyldFindByName(\"%s\",,,) failed, rc=%d (%#x) hMod3=%p hMod=%p\n",
+ szBuf, rc, rc, (void *)hMod3, (void *)hMod);
+ }
+ else
+ Failure("kLdrDyldGetName(\"tst-0-a\",,,) failed, rc=%d (%#x)\n", rc, rc);
+
+ rc = kLdrDyldGetFilename(hMod, szBuf, sizeof(szBuf));
+ if (!rc)
+ {
+ printf("tst-0-driver: filename: '%s' ('tst-0-a')\n", szBuf);
+
+ /* check that we can query the module by the returned filename. */
+ hMod3 = (HKLDRMOD)0xffffeeee;
+ rc = kLdrDyldFindByName(szBuf, NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod3);
+ if (rc || hMod3 != hMod)
+ Failure("kLdrDyldFindByName(\"%s\",,,) failed, rc=%d (%#x) hMod3=%p hMod=%p\n",
+ szBuf, rc, rc, (void *)hMod3, (void *)hMod);
+ }
+ else
+ Failure("kLdrDyldGetFilename(\"tst-0-a\",,,) failed, rc=%d (%#x)\n", rc, rc);
+
+
+ /*
+ * Resolve the symbol exported by each of the two modules and call them.
+ */
+ if (!g_cErrors)
+ {
+ KUPTR uValue;
+ KU32 fKind;
+
+ fKind = 0xffeeffee;
+ uValue = ~(KUPTR)42;
+ rc = kLdrDyldQuerySymbol(hMod, NIL_KLDRMOD_SYM_ORDINAL, MY_NAME("FuncA"), NULL, &uValue, &fKind);
+ if (!rc)
+ {
+ if (uValue == ~(KUPTR)42)
+ Failure("kLdrDyldQuerySymbol(\"tst-0-a\",,\"FuncA\",): uValue wasn't set.\n");
+ if (fKind == 0xffeeffee)
+ Failure("kLdrDyldQuerySymbol(\"tst-0-a\",,\"FuncA\",): fKind wasn't set.\n");
+ if ( (fKind & KLDRSYMKIND_BIT_MASK) != KLDRSYMKIND_NO_BIT
+ && (fKind & KLDRSYMKIND_BIT_MASK) != MY_KLDRSYMKIND_BITS)
+ Failure("fKind=%#x indicates a different code 'bit' mode than we running at.\n", fKind);
+ if ( (fKind & KLDRSYMKIND_TYPE_MASK) != KLDRSYMKIND_NO_TYPE
+ && (fKind & KLDRSYMKIND_TYPE_MASK) != KLDRSYMKIND_CODE)
+ Failure("fKind=%#x indicates that \"FuncA\" isn't code.\n", fKind);
+ if (fKind & KLDRSYMKIND_FORWARDER)
+ Failure("fKind=%#x indicates that \"FuncA\" is a forwarder. it isn't.\n", fKind);
+
+ /* call it. */
+ if (!g_cErrors)
+ {
+ int (*pfnFuncA)(void) = (int (*)(void))uValue;
+ rc = pfnFuncA();
+ if (rc != 0x42000042)
+ Failure("FuncA returned %#x expected 0x42000042\n", rc);
+ }
+
+ /*
+ * Test kLdrDyldFindByAddress now that we've got an address.
+ */
+ hMod3 = (HKLDRMOD)0xeeeeffff;
+ rc = kLdrDyldFindByAddress(uValue, &hMod3, NULL, NULL);
+ if (!rc)
+ {
+ KUPTR offSegment;
+ KU32 iSegment;
+
+ if (hMod3 != hMod)
+ Failure("kLdrDyldFindByAddress(%#p/*FuncA*/, \"tst-0-a\",,,) return incorrect hMod3=%p instead of %p.\n",
+ uValue, hMod3, hMod);
+
+ hMod3 = (HKLDRMOD)0xeeeeffff;
+ iSegment = 0x42424242;
+ rc = kLdrDyldFindByAddress(uValue, &hMod3, &iSegment, &offSegment);
+ if (!rc)
+ {
+ if (hMod3 != hMod)
+ Failure("Bad hMod3 on 2nd kLdrDyldFindByAddress call.\n");
+ if (iSegment > 0x1000) /* safe guess */
+ Failure("Bad iSegment=%#x\n", iSegment);
+ if (offSegment > 0x100000) /* guesswork */
+ Failure("Bad offSegment=%p\n", (void *)offSegment);
+ }
+ else
+ Failure("kLdrDyldFindByAddress(%#p/*FuncA*/, \"tst-0-a\",,,) failed (b), rc=%d (%#x)\n",
+ uValue, rc, rc);
+
+ /* negative test */
+ hMod3 = (HKLDRMOD)0xeeeeffff;
+ iSegment = 0x42424242;
+ offSegment = 0x87654321;
+ rc = kLdrDyldFindByAddress(~(KUPTR)16, &hMod3, &iSegment, &offSegment);
+ if (!rc)
+ Failure("negative kLdrDyldFindByAddress test returned successfully!\n");
+ if (iSegment != ~(KU32)0)
+ Failure("negative kLdrDyldFindByAddress: bad iSegment=%#x\n", iSegment);
+ if (offSegment != ~(KUPTR)0)
+ Failure("negative kLdrDyldFindByAddress: bad offSegment=%p\n", (void *)offSegment);
+ if (hMod3 != NIL_HKLDRMOD)
+ Failure("negative kLdrDyldFindByAddress: bad hMod3=%p\n", (void *)hMod3);
+ }
+ else
+ Failure("kLdrDyldFindByAddress(%#p/*FuncA*/, \"tst-0-a\",,,) failed, rc=%d (%#x)\n",
+ uValue, rc, rc);
+ }
+ else
+ Failure("kLdrDyldQuerySymbol(\"tst-0-a\",,\"FuncA\",) failed, rc=%d (%#x)\n", rc, rc);
+
+ fKind = 0xffeeffee;
+ uValue = ~(KUPTR)42;
+ rc = kLdrDyldQuerySymbol(hMod2, NIL_KLDRMOD_SYM_ORDINAL, MY_NAME("FuncD"), NULL, &uValue, &fKind);
+ if (!rc)
+ {
+ if (uValue == ~(KUPTR)42)
+ Failure("kLdrDyldQuerySymbol(\"tst-0-d\",,\"FuncD\",): uValue wasn't set.\n");
+ if (fKind == 0xffeeffee)
+ Failure("kLdrDyldQuerySymbol(\"tst-0-d\",,\"FuncD\",): fKind wasn't set.\n");
+ if ( (fKind & KLDRSYMKIND_BIT_MASK) != KLDRSYMKIND_NO_BIT
+ && (fKind & KLDRSYMKIND_BIT_MASK) != MY_KLDRSYMKIND_BITS)
+ Failure("fKind=%#x indicates a different code 'bit' mode than we running at.\n", fKind);
+ if ( (fKind & KLDRSYMKIND_TYPE_MASK) != KLDRSYMKIND_NO_TYPE
+ && (fKind & KLDRSYMKIND_TYPE_MASK) != KLDRSYMKIND_CODE)
+ Failure("fKind=%#x indicates that \"FuncD\" isn't code.\n", fKind);
+ if (fKind & KLDRSYMKIND_FORWARDER)
+ Failure("fKind=%#x indicates that \"FuncD\" is a forwarder. it isn't.\n", fKind);
+
+ /* call it. */
+ if (!g_cErrors)
+ {
+ int (*pfnFuncD)(void) = (int (*)(void))uValue;
+ rc = pfnFuncD();
+ if (rc != 0x42000000)
+ Failure("FuncD returned %#x expected 0x42000000\n", rc);
+ }
+
+ /* use the address to get the module handle. */
+ hMod3 = (HKLDRMOD)0xeeeeffff;
+ rc = kLdrDyldFindByAddress(uValue, &hMod3, NULL, NULL);
+ if (!rc)
+ {
+ if (hMod3 != hMod2)
+ Failure("kLdrDyldFindByAddress(%#p/*FuncD*/,,,) return incorrect hMod3=%p instead of %p.\n",
+ uValue, hMod3, hMod2);
+ }
+ else
+ Failure("kLdrDyldFindByAddress(%#p/*FuncD*/,,,) failed, rc=%d (%#x)\n",
+ uValue, rc, rc);
+ }
+ else
+ Failure("kLdrDyldQuerySymbol(\"tst-0-a\",,\"FuncA\",) failed, rc=%d (%#x)\n", rc, rc);
+
+ }
+
+ /*
+ * Finally unload it.
+ */
+ rc = kLdrDyldUnload(hMod);
+ if (rc)
+ Failure("kLdrDyldUnload() failed. rc=%d (%#x)\n", rc, rc);
+ if (!rc)
+ {
+ rc = kLdrDyldFindByName("tst-0-d", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod2);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ Failure("kLdrDyldFindByName(\"tst-0-d\",,,) return rc=%d (%#x), expected KLDR_ERR_MODULE_NOT_FOUND\n", rc, rc);
+
+ rc = kLdrDyldFindByName("tst-0-a", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod2);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ Failure("kLdrDyldFindByName(\"tst-0-a\",,,) return rc=%d (%#x), expected KLDR_ERR_MODULE_NOT_FOUND\n", rc, rc);
+ }
+ }
+
+ /*
+ * Now do what tst-0 would do; load the three dlls, resolve and call their functions.
+ */
+ if (!g_cErrors)
+ {
+ HKLDRMOD hModA;
+ int (*pfnFuncA)(void);
+ HKLDRMOD hModB;
+ int (*pfnFuncB)(void);
+ HKLDRMOD hModC;
+ int (*pfnFuncC)(void);
+ KUPTR uValue;
+
+ rc = kLdrDyldLoad("tst-0-a", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hModA, NULL, 0);
+ if (rc)
+ Failure("kLdrDyldLoad(\"tst-0-a\",,,,) -> %d (%#x)\n", rc, rc);
+ if (!rc)
+ {
+ rc = kLdrDyldLoad("tst-0-b", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hModB, szErr, sizeof(szErr));
+ if (rc)
+ Failure("kLdrDyldLoad(\"tst-0-b\",,,,) -> %d (%#x) szErr='%s'\n", rc, rc, szErr);
+ }
+ if (!rc)
+ {
+ rc = kLdrDyldLoad("tst-0-c", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hModC, szErr, sizeof(szErr));
+ if (rc)
+ Failure("kLdrDyldLoad(\"tst-0-c\",,,,) -> %d (%#x) szErr='%s'\n", rc, rc, szErr);
+ }
+ if (!rc)
+ {
+ rc = kLdrDyldQuerySymbol(hModA, NIL_KLDRMOD_SYM_ORDINAL, MY_NAME("FuncA"), NULL, &uValue, NULL);
+ if (!rc)
+ pfnFuncA = (int (*)(void))uValue;
+ else
+ Failure("kLdrDyldQuerySymbol(,,\"FuncA\",,) -> %d (%#x)\n", rc, rc);
+ }
+ if (!rc)
+ {
+ rc = kLdrDyldQuerySymbol(hModB, NIL_KLDRMOD_SYM_ORDINAL, MY_NAME("FuncB"), NULL, &uValue, NULL);
+ if (!rc)
+ pfnFuncB = (int (*)(void))uValue;
+ else
+ Failure("kLdrDyldQuerySymbol(,,\"FuncB\",,) -> %d (%#x)\n", rc, rc);
+ }
+ if (!rc)
+ {
+ rc = kLdrDyldQuerySymbol(hModC, NIL_KLDRMOD_SYM_ORDINAL, MY_NAME("FuncC"), NULL, &uValue, NULL);
+ if (!rc)
+ pfnFuncC = (int (*)(void))uValue;
+ else
+ Failure("kLdrDyldQuerySymbol(,,\"FuncA\",,) -> %d (%#x)\n", rc, rc);
+ }
+ if (!rc)
+ {
+ int u = pfnFuncA() | pfnFuncB() | pfnFuncC();
+ if (u == 0x42424242)
+ printf("tst-0-driver: FuncA/B/C => %#x (correct)\n", u);
+ else
+ Failure("FuncA/B/C => %#x\n", u);
+
+ rc = kLdrDyldUnload(hModA);
+ if (rc)
+ Failure("Unload A failed, rc=%d (%#x)\n", rc, rc);
+ u = pfnFuncB() | pfnFuncC();
+ if (u != 0x42424200)
+ Failure("FuncB/C returns %#x instead of 0x42424200 after unloading A\n", u);
+
+ rc = kLdrDyldUnload(hModB);
+ if (rc)
+ Failure("Unload B failed, rc=%d (%#x)\n", rc, rc);
+ u = pfnFuncC();
+ if (u != 0x42420000)
+ Failure("FuncC returns %#x instead of 0x42420000 after unloading A\n", u);
+
+ rc = kLdrDyldUnload(hModC);
+ if (rc)
+ Failure("Unload C failed, rc=%d (%#x)\n", rc, rc);
+
+ rc = kLdrDyldFindByName("tst-0-d", NULL, NULL, KLDRDYLD_SEARCH_HOST, 0, &hMod);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ Failure("Query for \"tst-0-d\" after unloading A,B and C returns rc=%d (%#x) instead of KLDR_ERR_MODULE_NOT_FOUND\n",
+ rc, rc);
+ }
+ }
+
+ /*
+ * Now invoke the executable stub which launches the tst-0 program.
+ */
+ if (!g_cErrors)
+ {
+ /// @todo
+ }
+
+ /*
+ * Summary
+ */
+ if (!g_cErrors)
+ printf("tst-0-driver: SUCCESS\n");
+ else
+ printf("tst-0-driver: FAILURE - %d errors\n", g_cErrors);
+ return !!g_cErrors;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-0.c b/src/lib/kStuff/kLdr/testcase/tst-0.c
new file mode 100644
index 0000000..d19c35c
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-0.c
@@ -0,0 +1,13 @@
+#include "tst.h"
+
+MY_IMPORT(int) FuncA(void);
+MY_IMPORT(int) FuncB(void);
+MY_IMPORT(int) FuncC(void);
+
+int main()
+{
+ unsigned u;
+ u = FuncA() | FuncB() | FuncC();
+ return u == 0x42424242 ? 0 : 1;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-1-a.c b/src/lib/kStuff/kLdr/testcase/tst-1-a.c
new file mode 100644
index 0000000..d554112
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-1-a.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "a";
+
+MY_IMPORT(int) FuncD(void);
+
+MY_EXPORT(int) FuncA(void)
+{
+ return FuncD();
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-1-b.c b/src/lib/kStuff/kLdr/testcase/tst-1-b.c
new file mode 100644
index 0000000..5156e58
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-1-b.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "b";
+
+MY_IMPORT(int) FuncD(void);
+
+MY_EXPORT(int) FuncB(void)
+{
+ return FuncD();
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-1-c.c b/src/lib/kStuff/kLdr/testcase/tst-1-c.c
new file mode 100644
index 0000000..c40f55d
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-1-c.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "c";
+
+MY_IMPORT(int) FuncD(void);
+
+MY_EXPORT(int) FuncC(void)
+{
+ return FuncD();
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-1-d.c b/src/lib/kStuff/kLdr/testcase/tst-1-d.c
new file mode 100644
index 0000000..ab8bf93
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-1-d.c
@@ -0,0 +1,8 @@
+#include "tst.h"
+const char *g_pszName = "d";
+
+MY_EXPORT(int) FuncD(void)
+{
+ return 0;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-1.c b/src/lib/kStuff/kLdr/testcase/tst-1.c
new file mode 100644
index 0000000..58b9770
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-1.c
@@ -0,0 +1,15 @@
+#include "tst.h"
+#include <stdio.h>
+
+MY_IMPORT(int) FuncA(void);
+MY_IMPORT(int) FuncB(void);
+MY_IMPORT(int) FuncC(void);
+
+int main()
+{
+ printf("graph:\n"
+ " tst-1 -> a -> d\n"
+ " b -> d\n"
+ " c -> d\n");
+ return FuncA() + FuncB() + FuncC();
+}
diff --git a/src/lib/kStuff/kLdr/testcase/tst-2-a.c b/src/lib/kStuff/kLdr/testcase/tst-2-a.c
new file mode 100644
index 0000000..274f92e
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-2-a.c
@@ -0,0 +1,8 @@
+#include "tst.h"
+const char *g_pszName = "a";
+
+MY_EXPORT(int) FuncA(void)
+{
+ return 0;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-2-b.c b/src/lib/kStuff/kLdr/testcase/tst-2-b.c
new file mode 100644
index 0000000..63c2c58
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-2-b.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "b";
+
+MY_IMPORT(int) FuncA(void);
+
+MY_EXPORT(int) FuncB(void)
+{
+ return FuncA();
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-2-c.c b/src/lib/kStuff/kLdr/testcase/tst-2-c.c
new file mode 100644
index 0000000..29ab68f
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-2-c.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "c";
+
+MY_IMPORT(int) FuncA(void);
+
+MY_EXPORT(int) FuncC(void)
+{
+ return FuncA();
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-2-d.c b/src/lib/kStuff/kLdr/testcase/tst-2-d.c
new file mode 100644
index 0000000..34efd0a
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-2-d.c
@@ -0,0 +1,10 @@
+#include "tst.h"
+const char *g_pszName = "d";
+
+MY_IMPORT(int) FuncA(void);
+
+MY_EXPORT(int) FuncD(void)
+{
+ return FuncA();
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-2.c b/src/lib/kStuff/kLdr/testcase/tst-2.c
new file mode 100644
index 0000000..6110a4b
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-2.c
@@ -0,0 +1,16 @@
+#include "tst.h"
+
+MY_IMPORT(int) FuncA(void);
+MY_IMPORT(int) FuncB(void);
+MY_IMPORT(int) FuncC(void);
+MY_IMPORT(int) FuncD(void);
+
+int main()
+{
+ printf("graph:\n"
+ " tst-2 -> b -> a\n"
+ " c -> a\n"
+ " d -> a\n"
+ " a\n");
+ return FuncA() + FuncB() + FuncC() + FuncD();
+}
diff --git a/src/lib/kStuff/kLdr/testcase/tst-3-driver.c b/src/lib/kStuff/kLdr/testcase/tst-3-driver.c
new file mode 100644
index 0000000..483a585
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-3-driver.c
@@ -0,0 +1,216 @@
+/* $Id: tst-3-driver.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Dynamic Loader testcase no. 3, Driver.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "tst.h"
+#include <k/kErr.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+# include <malloc.h>
+#endif
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** Select the appropriate KLDRSYMKIND bit define. */
+#define MY_KLDRSYMKIND_BITS ( sizeof(void *) == 4 ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT )
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The numbers of errors. */
+static int g_cErrors = 0;
+
+
+/**
+ * Report failure.
+ */
+static int Failure(const char *pszFormat, ...)
+{
+ va_list va;
+
+ g_cErrors++;
+
+ printf("tst-3-driver: ");
+ va_start(va, pszFormat);
+ vprintf(pszFormat, va);
+ va_end(va);
+ printf("\n");
+ return 1;
+}
+
+
+/**
+ * External symbol used by the testcase module.
+ */
+static int Tst3Ext(int iFortyTwo)
+{
+ if (iFortyTwo != 42)
+ return 256;
+ return 42;
+}
+
+
+/**
+ * Callback for resolving the Tst3Ext import.
+ */
+static int GetImport(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
+ const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
+{
+ if (*pfKind != KLDRSYMKIND_REQ_FLAT)
+ return -1;
+
+ if ( !strncmp(pchSymbol, "Tst3Ext", strlen("Tst3Ext"))
+ || !strncmp(pchSymbol, "_Tst3Ext", strlen("_Tst3Ext")))
+ {
+ *puValue = (KUPTR)&Tst3Ext;
+ *pfKind = KLDRSYMKIND_CODE | (sizeof(pfKind) == 4 ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT);
+ return 0;
+ }
+
+ return -2;
+}
+
+
+/**
+ * Performs the tests on one module.
+ * @returns non sense.
+ */
+int TestModule(const char *pszFile)
+{
+ PKLDRMOD pMod;
+ KLDRSIZE cbImage;
+ void *pvBits;
+ int rc;
+
+ printf("tst-3-driver: testing '%s'...\n", pszFile);
+
+ /* open it. */
+ rc = kLdrModOpen(pszFile, &pMod);
+ if (rc)
+ return Failure("kLdrModOpen(%s,) -> %#d (%s)\n", pszFile, rc, kErrName(rc));
+
+ /* get bits. */
+ cbImage = kLdrModSize(pMod);
+ pvBits = malloc((KSIZE)cbImage + 0xfff);
+ if (pvBits)
+ {
+ void *pvBits2 = (void *)( ((KUPTR)pvBits + 0xfff) & ~(KUPTR)0xfff );
+
+ KLDRADDR BaseAddress = (KUPTR)pvBits2;
+ rc = kLdrModGetBits(pMod, pvBits2, BaseAddress, GetImport, NULL);
+ if (!rc)
+ {
+ KLDRADDR EntryPoint;
+
+ /* call into it */
+ rc = kLdrModQuerySymbol(pMod, pvBits2, BaseAddress, NIL_KLDRMOD_SYM_ORDINAL, "_Tst3", strlen("_Tst3"), NULL, NULL, NULL,
+ &EntryPoint, NULL);
+ if (rc == KLDR_ERR_SYMBOL_NOT_FOUND)
+ rc = kLdrModQuerySymbol(pMod, pvBits2, BaseAddress, NIL_KLDRMOD_SYM_ORDINAL, "Tst3", strlen("Tst3"), NULL, NULL, NULL,
+ &EntryPoint, NULL);
+ if (!rc)
+ {
+ int (*pfnEntryPoint)(int) = (int (*)(int)) ((KUPTR)EntryPoint);
+ rc = pfnEntryPoint(42);
+ if (rc == 42)
+ {
+ /* relocate twice and try again. */
+ rc = kLdrModRelocateBits(pMod, pvBits2, BaseAddress + 0x22000, BaseAddress, GetImport, NULL);
+ if (!rc)
+ {
+ rc = kLdrModRelocateBits(pMod, pvBits2, BaseAddress, BaseAddress + 0x22000, GetImport, NULL);
+ if (!rc)
+ {
+ rc = pfnEntryPoint(42);
+ if (rc == 42)
+ {
+ printf("tst-3-driver: success.\n");
+ }
+ else
+ Failure("pfnEntryPoint(42) -> %d (2nd)\n", rc);
+ }
+ else
+ Failure("kLdrModRelocateBits(,,, + 0x22000,,,) -> %#x (%s)\n", rc, kErrName(rc));
+ }
+ else
+ Failure("kLdrModRelocateBits(,, + 0x22000,,,,) -> %#x (%s)\n", rc, kErrName(rc));
+ }
+ else
+ Failure("pfnEntryPoint(42) -> %d (1st)\n", rc);
+ }
+ else
+ Failure("kLdrModQuerySymbol -> %#x (%s)\n", rc, kErrName(rc));
+ }
+ else
+ Failure("kLdrModGetBits -> %#x (%s)\n", rc, kErrName(rc));
+ free(pvBits);
+ }
+ else
+ Failure("malloc(%lx) -> NULL\n", (long)cbImage);
+
+ /* clean up */
+ rc = kLdrModClose(pMod);
+ if (rc)
+ Failure("kLdrModOpen(%s,) -> %#x (%s)\n", pszFile, rc, kErrName(rc));
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ int i;
+
+ /*
+ * Test all the given modules (requires arguments).
+ */
+ for (i = 1; i < argc; i++)
+ {
+ TestModule(argv[i]);
+ }
+
+
+ /*
+ * Summary
+ */
+ if (!g_cErrors)
+ printf("tst-3-driver: SUCCESS\n");
+ else
+ printf("tst-3-driver: FAILURE - %d errors\n", g_cErrors);
+ return !!g_cErrors;
+}
diff --git a/src/lib/kStuff/kLdr/testcase/tst-3-ext.c b/src/lib/kStuff/kLdr/testcase/tst-3-ext.c
new file mode 100644
index 0000000..2b4c839
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-3-ext.c
@@ -0,0 +1,39 @@
+/* $Id: tst-3-ext.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Dynamic Loader testcase no. 3, 2nd object module.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "tst.h"
+
+extern int g_i1;
+
+int Tst3Sub(int iFortyTwo)
+{
+ return iFortyTwo * 11 * g_i1;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-3-imp-os2.def b/src/lib/kStuff/kLdr/testcase/tst-3-imp-os2.def
new file mode 100644
index 0000000..9ec3b13
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-3-imp-os2.def
@@ -0,0 +1,34 @@
+; $Id: tst-3-imp-os2.def 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - Dynamic Loader testcase no. 3, Fake module import library - OS/2.
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+LIBRARY tst-3-imp
+EXPORTS
+ _Tst3Ext
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-3-imp-win.def b/src/lib/kStuff/kLdr/testcase/tst-3-imp-win.def
new file mode 100644
index 0000000..7381804
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-3-imp-win.def
@@ -0,0 +1,34 @@
+; $Id: tst-3-imp-win.def 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - Dynamic Loader testcase no. 3, Fake module import library - Windows.
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+LIBRARY tst-3-imp
+EXPORTS
+ Tst3Ext
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst-3.c b/src/lib/kStuff/kLdr/testcase/tst-3.c
new file mode 100644
index 0000000..ed89d9e
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst-3.c
@@ -0,0 +1,78 @@
+/* $Id: tst-3.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Dynamic Loader testcase no. 3, Driver.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "tst.h"
+
+
+int g_i1 = 1;
+int g_i2 = 2;
+int *g_pi1 = &g_i1;
+
+extern int Tst3Sub(int);
+int (*g_pfnTst3Sub)(int) = &Tst3Sub;
+
+MY_IMPORT(int) Tst3Ext(int);
+int (*g_pfnTst3Ext)(int) = &Tst3Ext;
+
+char g_achBss[256];
+
+
+MY_EXPORT(int) Tst3(int iFortyTwo)
+{
+ int rc;
+
+ if (iFortyTwo != 42)
+ return 0;
+ if (g_i1 != 1)
+ return 1;
+ if (g_i2 != 2)
+ return 2;
+ if (g_pi1 != &g_i1)
+ return 3;
+ if (g_pfnTst3Sub != &Tst3Sub)
+ return 4;
+ rc = Tst3Sub(iFortyTwo);
+ if (rc != g_pfnTst3Sub(iFortyTwo))
+ return 5;
+ rc = Tst3Ext(iFortyTwo);
+ if (rc != 42)
+ return 6;
+ rc = g_pfnTst3Ext(iFortyTwo);
+ if (rc != 42)
+ return 7;
+ for (rc = 0; rc < sizeof(g_achBss); rc++)
+ if (g_achBss[rc])
+ return 8;
+ if (g_achBss[0] || g_achBss[1] || g_achBss[255])
+ return 9;
+
+ return 42;
+}
+
diff --git a/src/lib/kStuff/kLdr/testcase/tst.h b/src/lib/kStuff/kLdr/testcase/tst.h
new file mode 100644
index 0000000..f06dba7
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tst.h
@@ -0,0 +1,57 @@
+/* $Id: tst.h 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr testcase header.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ___tst_h___
+#define ___tst_h___
+
+#include <k/kLdr.h>
+#include <k/kHlp.h>
+
+#if K_OS == K_OS_OS2 \
+ || K_OS == K_OS_WINDOWS
+# define MY_EXPORT(type) __declspec(dllexport) type
+/*# define MY_IMPORT(type) extern __declspec(dllimport) type*/
+# define MY_IMPORT(type) extern type
+#else
+# define MY_EXPORT(type) type
+# define MY_IMPORT(type) extern type
+#endif
+
+#if K_OS == K_OS_OS2 \
+ || K_OS == K_OS_DARWIN
+# define MY_NAME(a) "_" a
+#else
+# define MY_NAME(a) a
+#endif
+
+extern const char *g_pszName;
+
+#endif
+
diff --git a/src/lib/kStuff/kLdr/testcase/tstDllMain.c b/src/lib/kStuff/kLdr/testcase/tstDllMain.c
new file mode 100644
index 0000000..b86819c
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tstDllMain.c
@@ -0,0 +1,192 @@
+/* $Id: tstDllMain.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr testcase.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "tst.h"
+
+#if K_OS == K_OS_OS2
+# define INCL_BASE
+# include <os2.h>
+# include <string.h>
+
+#elif K_OS == K_OS_WINDOWS
+# include <windows.h>
+# include <string.h>
+
+#elif K_OS == K_OS_DARWIN
+# include <unistd.h>
+# include <string.h>
+
+#else
+# error "port me"
+#endif
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+void tstWrite(const char *psz);
+
+
+
+#if K_OS == K_OS_OS2
+/**
+ * OS/2 DLL 'main'
+ */
+ULONG _System _DLL_InitTerm(HMODULE hmod, ULONG fFlags)
+{
+ switch (fFlags)
+ {
+ case 0:
+ tstWrite("init: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return TRUE;
+
+ case 1:
+ tstWrite("term: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return TRUE;
+
+ default:
+ tstWrite("!invalid!: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return FALSE;
+ }
+}
+
+#elif K_OS == K_OS_WINDOWS
+
+/**
+ * OS/2 DLL 'main'
+ */
+BOOL __stdcall DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ tstWrite("init: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return TRUE;
+
+ case DLL_PROCESS_DETACH:
+ tstWrite("term: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return TRUE;
+
+ case DLL_THREAD_ATTACH:
+ tstWrite("thread init: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return TRUE;
+
+ case DLL_THREAD_DETACH:
+ tstWrite("thread term: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return TRUE;
+
+ default:
+ tstWrite("!invalid!: ");
+ tstWrite(g_pszName);
+ tstWrite("\n");
+ return FALSE;
+ }
+}
+
+#elif K_OS == K_OS_DARWIN
+/* later */
+
+#else
+# error "port me"
+#endif
+
+
+/**
+ * Writes a string with unix lineendings.
+ *
+ * @param pszMsg The string.
+ */
+void tstWrite(const char *pszMsg)
+{
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
+ /*
+ * Line by line.
+ */
+ ULONG cbWritten;
+ const char *pszNl = strchr(pszMsg, '\n');
+
+ while (pszNl)
+ {
+ cbWritten = pszNl - pszMsg;
+
+#if K_OS == K_OS_OS2
+ if (cbWritten)
+ DosWrite((HFILE)2, pszMsg, cbWritten, &cbWritten);
+ DosWrite((HFILE)2, "\r\n", 2, &cbWritten);
+#else
+ if (cbWritten)
+ WriteFile((HANDLE)STD_ERROR_HANDLE, pszMsg, cbWritten, &cbWritten, NULL);
+ WriteFile((HANDLE)STD_ERROR_HANDLE, "\r\n", 2, &cbWritten, NULL);
+#endif
+
+ /* next */
+ pszMsg = pszNl + 1;
+ pszNl = strchr(pszMsg, '\n');
+ }
+
+ /*
+ * Remaining incomplete line.
+ */
+ if (*pszMsg)
+ {
+ cbWritten = strlen(pszMsg);
+#if K_OS == K_OS_OS2
+ DosWrite((HFILE)2, pszMsg, cbWritten, &cbWritten);
+#else
+ WriteFile((HANDLE)STD_ERROR_HANDLE, pszMsg, cbWritten, &cbWritten, NULL);
+#endif
+ }
+
+#elif K_OS == K_OS_DARWIN
+ write(STDERR_FILENO, pszMsg, strlen(pszMsg));
+
+#else
+# error "port me"
+#endif
+}
+
+
diff --git a/src/lib/kStuff/kLdr/testcase/tstDllMainStub-os2.asm b/src/lib/kStuff/kLdr/testcase/tstDllMainStub-os2.asm
new file mode 100644
index 0000000..76dad01
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tstDllMainStub-os2.asm
@@ -0,0 +1,40 @@
+; $Id: tstDllMainStub-os2.asm 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - OS/2 entry point thingy...
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+segment TEXT32 public CLASS=CODE align=16 use32
+extern _DLL_InitTerm
+..start:
+ jmp _DLL_InitTerm
+
+segment DATA32 stack CLASS=DATA align=16 use32
+
+global WEAK$ZERO
+WEAK$ZERO EQU 0
+
diff --git a/src/lib/kStuff/kLdr/testcase/tstDllMainStub.c b/src/lib/kStuff/kLdr/testcase/tstDllMainStub.c
new file mode 100644
index 0000000..67681c8
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tstDllMainStub.c
@@ -0,0 +1,76 @@
+/* $Id: tstDllMainStub.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr testcase - DLL Stub.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "tst.h"
+
+#if K_OS == K_OS_OS2
+# define INCL_BASE
+# include <os2.h>
+
+#elif K_OS == K_OS_WINDOWS
+# include <windows.h>
+
+#elif K_OS == K_OS_DARWIN
+/* later */
+
+#else
+# error "port me"
+#endif
+
+
+#if K_OS == K_OS_OS2
+/**
+ * OS/2 DLL 'main'
+ */
+ULONG _System _DLL_InitTerm(HMODULE hmod, ULONG fFlag)
+{
+ return TRUE;
+}
+
+#elif K_OS == K_OS_WINDOWS
+
+/**
+ * Window DLL 'main'
+ */
+BOOL __stdcall DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
+{
+ return TRUE;
+}
+
+#elif K_OS == K_OS_DARWIN
+/* later */
+
+#else
+# error "port me"
+#endif
+
diff --git a/src/lib/kStuff/kLdr/testcase/tstExeMainStub-os2.asm b/src/lib/kStuff/kLdr/testcase/tstExeMainStub-os2.asm
new file mode 100644
index 0000000..d4a8ee9
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tstExeMainStub-os2.asm
@@ -0,0 +1,40 @@
+; $Id: tstExeMainStub-os2.asm 29 2009-07-01 20:30:29Z bird $
+;; @file
+; kLdr - OS/2 entry point thingy...
+;
+
+;
+; Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+segment TEXT32 public CLASS=CODE align=16 use32
+extern OS2Main
+..start:
+ jmp OS2Main
+
+segment DATA32 stack CLASS=DATA align=16 use32
+
+global WEAK$ZERO
+WEAK$ZERO EQU 0
+
diff --git a/src/lib/kStuff/kLdr/testcase/tstExeMainStub.c b/src/lib/kStuff/kLdr/testcase/tstExeMainStub.c
new file mode 100644
index 0000000..9ec9f47
--- /dev/null
+++ b/src/lib/kStuff/kLdr/testcase/tstExeMainStub.c
@@ -0,0 +1,93 @@
+/* $Id: tstExeMainStub.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr testcase - DLL Stub.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "tst.h"
+
+#if K_OS == K_OS_OS2
+# define INCL_BASE
+# include <os2.h>
+
+#elif K_OS == K_OS_WINDOWS
+/* nothing */
+
+#elif K_OS == K_OS_NT
+# include <ddk/ntapi.h> /** @todo fix the nt port. */
+
+#else
+# error "port me"
+#endif
+
+
+extern int main();
+
+
+#if K_OS == K_OS_OS2
+/**
+ * OS/2 'main'.
+ */
+ULONG _System OS2Main(HMODULE hmod, ULONG fFlag, ULONG ulReserved, PSZ pszzEnv, PSZ pszzCmdLine)
+{
+ int rc;
+ rc = main();
+ return rc;
+}
+
+#elif K_OS == K_OS_WINDOWS
+/**
+ * Windows'main'
+ */
+int WindowsMain(void)
+{
+ int rc;
+ rc = main();
+ return rc;
+}
+
+#elif K_OS == K_OS_NT
+/**
+ * Windows NT 'main'
+ */
+VOID NtProcess(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
+{
+ int rc;
+ rc = main();
+ /* (no way around this) */
+ for (;;)
+ ZwTerminateProcess(NtCurrentProcess(), rc);
+}
+
+#else
+# error "port me"
+#endif
+
+
diff --git a/src/lib/kStuff/kLdr/tg/KLDRSTATE.gif b/src/lib/kStuff/kLdr/tg/KLDRSTATE.gif
new file mode 100644
index 0000000..2c9004d
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tg/KLDRSTATE.gif
Binary files differ
diff --git a/src/lib/kStuff/kLdr/tg/KLDRSTATE.txvstc b/src/lib/kStuff/kLdr/tg/KLDRSTATE.txvstc
new file mode 100644
index 0000000..fc4f3c8
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tg/KLDRSTATE.txvstc
@@ -0,0 +1,529 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodeSet version="1.0">
+ <view uin="id83t9setug73fpetug74ah">
+ <property name="$metaclass" value="State Diagram"/>
+ <property name="@__options" value=""/>
+ <reference df-class-name="reference" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="90,40,80,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd.linkid7iy6aetug73fpetugal1c:id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd">
+ <property name="sourceAnchor" value="130,80"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="130,110"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd.linkid14j0oetug73fpetugbmyx:id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd">
+ <property name="sourceAnchor" value="90,70"/>
+ <property name="bendpoints" value="30,70,30,1000"/>
+ <property name="targetAnchor" value="150,1000"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd.linkid14j0oetug73fpetugbmyx:id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd">
+ <property name="bounds" value="10,50,77,16"/>
+ </reference>
+ </reference>
+ </reference>
+ <reference df-class-name="reference1" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid67r7zetug73fpetug87g4">
+ <property name="$shortcutReference" value="true"/>
+ <property name="background_color" value="0,0,0"/>
+ <property name="bounds" value="120,0,12,12"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid67r7zetug73fpetug87g4.linkid2j70ketug73fpetug8gs8:id83t9setug73fpetug74ah.nodeid67r7zetug73fpetug87g4">
+ <property name="sourceAnchor" value="126,12"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="126,40"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference2" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="90,110,80,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak.linkid7cno0etug73fpetuganpl:id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak">
+ <property name="sourceAnchor" value="130,150"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="130,190"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak.linkid4elkbetug73fpetugbpa9:id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak">
+ <property name="sourceAnchor" value="90,140"/>
+ <property name="bendpoints" value="30,140,30,990"/>
+ <property name="targetAnchor" value="150,990"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference3" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="320,110,110,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9.linkidd5q0etug73fpetugl30f:id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9">
+ <property name="sourceAnchor" value="380,150"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="380,190"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9.linkid2yl8cetug73fpetugl74g:id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9">
+ <property name="sourceAnchor" value="430,140"/>
+ <property name="bendpoints" value="550,140,550,840"/>
+ <property name="targetAnchor" value="284,840"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9.linkid2yl8cetug73fpetugl74g:id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9">
+ <property name="bounds" value="470,120,77,16"/>
+ </reference>
+ </reference>
+ </reference>
+ <reference df-class-name="reference4" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="80,190,135,40"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l.linkidr4phetug73fpetugcezv:id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l">
+ <property name="sourceAnchor" value="130,230"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="130,270"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l.linkid82puetug73fpetugbwui:id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l">
+ <property name="sourceAnchor" value="80,220"/>
+ <property name="bendpoints" value="30,220,30,980"/>
+ <property name="targetAnchor" value="150,980"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference5" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="90,270,80,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj.linkid4fiuhetug73fpetugbs4x:id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj">
+ <property name="sourceAnchor" value="90,300"/>
+ <property name="bendpoints" value="30,300,30,970"/>
+ <property name="targetAnchor" value="150,970"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj.linkid8ewc7etug73fpetughpzu:id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj">
+ <property name="sourceAnchor" value="130,310"/>
+ <property name="bendpoints" value="130,330,190,330"/>
+ <property name="targetAnchor" value="190,350"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference6" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,950,134,60"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg.linkid80127etug73fpetugd66b:id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg">
+ <property name="sourceAnchor" value="210,1010"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,1040"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference7" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,1040,134,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e.linkid8h4qnetug73fpetugf6j3:id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e">
+ <property name="sourceAnchor" value="208,1080"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="208,1110"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference8" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,880,134,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p.linkid6ngyletug73fpetugehbp:id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p">
+ <property name="sourceAnchor" value="210,920"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,950"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference9" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,790,134,60"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5.linkid6vibeetug73fpetugephi:id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5">
+ <property name="sourceAnchor" value="210,850"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,880"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5.linkid1izxmetug73fpetugesem:id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5">
+ <property name="sourceAnchor" value="260,790"/>
+ <property name="bendpoints" value="260,770,590,770,590,90,380,90"/>
+ <property name="targetAnchor" value="380,110"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5.linkid1izxmetug73fpetugesem:id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5">
+ <property name="bounds" value="290,750,84,16"/>
+ </reference>
+ </reference>
+ </reference>
+ <reference df-class-name="reference10" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid17bz7etug73fpetugf23i">
+ <property name="$shortcutReference" value="true"/>
+ <property name="background_color" value="0,0,0"/>
+ <property name="bounds" value="200,1110,15,15"/>
+ </reference>
+ <reference df-class-name="reference11" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,380,129,40"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i.linkidy5coetug73fpetughmwy:id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i">
+ <property name="sourceAnchor" value="210,420"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,460"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i.linkid989qmetug73fpetugi623:id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i">
+ <property name="sourceAnchor" value="150,410"/>
+ <property name="bendpoints" value="60,410,60,810"/>
+ <property name="targetAnchor" value="150,810"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i.linkid989qmetug73fpetugi623:id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i">
+ <property name="bounds" value="50,390,94,16"/>
+ </reference>
+ </reference>
+ </reference>
+ <reference df-class-name="reference12" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,460,134,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj.linkid7b0d0etug73fpetughibe:id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj">
+ <property name="sourceAnchor" value="284,490"/>
+ <property name="bendpoints" value="460,490"/>
+ <property name="targetAnchor" value="460,540"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj.linkid7b0d0etug73fpetughibe:id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj">
+ <property name="bounds" value="290,490,42,16"/>
+ </reference>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj.linkid2yeeuetug73fpetughfff:id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj">
+ <property name="sourceAnchor" value="210,500"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,540"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference13" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,540,134,50"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv.linkid5imvsetug73fpetughcca:id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv">
+ <property name="sourceAnchor" value="210,590"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,630"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference14" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,630,131,40"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3.linkid7ockpetuqdc66etuqdq4c:id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3">
+ <property name="sourceAnchor" value="281,650"/>
+ <property name="bendpoints" value="310,650,310,570"/>
+ <property name="targetAnchor" value="284,570"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3.linkid7ockpetuqdc66etuqdq4c:id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3">
+ <property name="bounds" value="290,650,84,16"/>
+ </reference>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3.linkid3jvhdetug73fpetugiz5h:id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3">
+ <property name="sourceAnchor" value="210,670"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,710"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference15" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="410,540,121,50"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4.linkidotudetug73fpetugilak:id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4">
+ <property name="sourceAnchor" value="460,590"/>
+ <property name="bendpoints" value="460,910"/>
+ <property name="targetAnchor" value="284,910"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4.linkidotudetug73fpetugilak:id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4">
+ <property name="bounds" value="470,600,45,16"/>
+ </reference>
+ </reference>
+ </reference>
+ <reference df-class-name="reference16" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="150,710,134,40"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0.linkids5u8etug73fpetugj2bq:id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0">
+ <property name="sourceAnchor" value="210,750"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,790"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference17" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="290,190,189,40"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4.linkid529snetug73fpetuglmq0:id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4">
+ <property name="sourceAnchor" value="479,220"/>
+ <property name="bendpoints" value="550,220,550,830"/>
+ <property name="targetAnchor" value="284,830"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4.linkid733w4etug73fpetuglagw:id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4">
+ <property name="sourceAnchor" value="380,230"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="380,270"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference18" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65">
+ <property name="$shortcutReference" value="true"/>
+ <property name="bounds" value="320,270,118,40"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65.linkidcf8yetug73fpetuglj2g:id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65">
+ <property name="sourceAnchor" value="438,300"/>
+ <property name="bendpoints" value="550,300,550,820"/>
+ <property name="targetAnchor" value="284,820"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65.linkid5watyetug73fpetugle5c:id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65">
+ <property name="sourceAnchor" value="380,310"/>
+ <property name="bendpoints" value="380,330,230,330"/>
+ <property name="targetAnchor" value="230,350"/>
+ <property name="bounds_setted_by_user" value="true"/>
+ </reference>
+ </reference>
+ <reference df-class-name="reference19" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3">
+ <property name="$shortcutReference" value="true"/>
+ <property name="background_color" value="0,0,0"/>
+ <property name="bounds" value="180,350,60,6"/>
+ <reference referencedUin="design:link:::id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3.linkid4uffpetui3nn8etui6xmx:id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3">
+ <property name="sourceAnchor" value="210,356"/>
+ <property name="bendpoints" value=""/>
+ <property name="targetAnchor" value="210,380"/>
+ <reference referencedUin="linklabel:$transitionInplaceEditing:design:link:::id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3.linkid4uffpetui3nn8etui6xmx:id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3">
+ <property name="bounds" value="220,360,122,16"/>
+ </reference>
+ </reference>
+ </reference>
+ </view>
+ <node uin="id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Open"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd.linkid7iy6aetug73fpetugal1c">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd.linkid14j0oetug73fpetugbmyx">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="done (failed)"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid67r7zetug73fpetug87g4">
+ <property name="$metaclass" value="Start State"/>
+ <property name="$name" value="StartState1"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid67r7zetug73fpetug87g4.linkid2j70ketug73fpetug8gs8">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid4wa62etug73fpetug7hpd"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid67r7zetug73fpetug87g4"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Mapped"/>
+ <link uin="id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak.linkid7cno0etug73fpetuganpl">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak.linkid4elkbetug73fpetugbpa9">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidd21uetug73fpetug8kak"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Reloaded"/>
+ <link uin="id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9.linkidd5q0etug73fpetugl30f">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9.linkid2yl8cetug73fpetugl74g">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="done (failed)"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="LoadedPrerequisites"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l.linkid82puetug73fpetugbwui">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l.linkidr4phetug73fpetugcezv">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7et8etug73fpetug938l"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="FixedUp"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj.linkid4fiuhetug73fpetugbs4x">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj.linkid8ewc7etug73fpetughpzu">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid71qrcetug73fpetuga3zj"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="PendingDestroy"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg.linkid80127etug73fpetugd66b">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Destroyed"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e.linkid8h4qnetug73fpetugf6j3">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid17bz7etug73fpetugf23i"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7vd8retug73fpetugcr3e"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="GC"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p.linkid6ngyletug73fpetugehbp">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3wom6etug73fpetugb9cg"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="PendingGC"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5.linkid6vibeetug73fpetugephi">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5.linkid1izxmetug73fpetugesem">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidq28metug73fpetug8uf9"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="Loaded again"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid17bz7etug73fpetugf23i">
+ <property name="$metaclass" value="End State"/>
+ <property name="$name" value="EndState1"/>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="PendingInitialization"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i.linkidy5coetug73fpetughmwy">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i.linkid989qmetug73fpetugi623">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="Other init failure"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Initializing"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj.linkid2yeeuetug73fpetughfff">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj.linkid7b0d0etug73fpetughibe">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid2vt9ietug73fpetugfjhj"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="Failed"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Good"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv.linkid5imvsetug73fpetughcca">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="PendingTermination"/>
+ <link uin="id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3.linkid3jvhdetug73fpetugiz5h">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3.linkid7ockpetuqdc66etuqdq4c">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid8048etug73fpetugfpwv"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeidswftetug73fpetugfwd3"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="Loaded again"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="InitializationFailed"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4.linkidotudetug73fpetugilak">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid417yeetug73fpetugeb3p"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid3z4oketug73fpetuggjl4"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="Do GC"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="Terminating"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0.linkids5u8etug73fpetugj2bq">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid9bbfsetug73fpetugirr0"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="ReloadedLoadedPrerequisites"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4.linkid733w4etug73fpetuglagw">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4.linkid529snetug73fpetuglmq0">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6t6upetug73fpetugj6k4"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65">
+ <property name="$metaclass" value="State"/>
+ <property name="$name" value="ReloadedFixedUp"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65.linkid5watyetug73fpetugle5c">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ <link uin="id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65.linkidcf8yetug73fpetuglj2g">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid7q41getug73fpetugejq5"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid5e0mxetug73fpetugjk65"/>
+ <property name="$metaclass" value="Transition"/>
+ </link>
+ </node>
+ <node uin="id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3">
+ <property name="$orientation" value="horizontal"/>
+ <property name="$metaclass" value="Synchronization Bar"/>
+ <property name="$name" value="SyncBar1"/>
+ <link uin="id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3.linkid4uffpetui3nn8etui6xmx">
+ <participant role="Supplier" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6b4f8etug73fpetugfa3i"/>
+ <participant role="Client" referencedUin="design:node:::id83t9setug73fpetug74ah.nodeid6yrvpetui3nn8etui62p3"/>
+ <property name="$metaclass" value="Transition"/>
+ <property name="$event_name" value="Fixed up all modules"/>
+ </link>
+ </node>
+</nodeSet>
diff --git a/src/lib/kStuff/kLdr/tg/default.txvpck b/src/lib/kStuff/kLdr/tg/default.txvpck
new file mode 100644
index 0000000..b253f0f
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tg/default.txvpck
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodeSet version="1.0">
+ <view uin="id309n7etug73fpetug73wh">
+ <property name="$metaclass" value="Package Diagram"/>
+ <property name="@__options" value=""/>
+ <property name="$defaultDiagram" value=""/>
+ </view>
+</nodeSet>
diff --git a/src/lib/kStuff/kLdr/tg/kLdr.tpr b/src/lib/kStuff/kLdr/tg/kLdr.tpr
new file mode 100644
index 0000000..fb3d016
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tg/kLdr.tpr
@@ -0,0 +1,23 @@
+[Project]
+Language=cpp
+Root.0=$PROJECT_DIR$
+Root.0.access=writable
+Root.0.file_types=cpp_source;cpp_header;diagram
+Root.0.package_prefix=
+Version=3.0
+projectfile.encoding=MS932
+Root.1=$TGH$/jdk/jre/lib/rt.jar
+Root.1.access=import
+Root.1.non_removable=
+[workspace]
+Developer.CodingWorkspace={{0,2,-1,0,0,0,0,0,0,1,0,50,75,25,-1,-1,-1,-1}${0,1,-1,0,0,0,0,1,0,1,0,50,75,25,-1,-1,-1,-1}${0,3,-1,0,0,0,0,1,0,1,0,66,50,25,-1,-1,-1,-1}${0,3,-1,0,0,0,0,1,0,1,0,66,50,25,-1,-1,-1,-1}${0,-1,-1,0,0,0,0,0,0,1,0,5,5,5,-1,-1,-1,-1}${0,5,-1,0,0,0,0,0,0,1,0,50,50,25,-1,-1,-1,-1}${0,4,-1,0,0,0,0,1,0,1,1,66,50,25,-1,-1,-1,-1}${0,7,-1,0,0,0,0,0,0,1,0,50,50,75,-1,-1,-1,-1}}
+Developer.DebugWorkspace={{0,2,-1,0,0,0,0,0,0,1,0,50,75,25,-1,-1,-1,-1}${0,1,-1,0,0,0,0,1,0,1,1,50,75,25,-1,-1,-1,-1}${0,3,-1,0,0,0,0,0,0,1,0,50,50,25,-1,-1,-1,-1}${0,3,-1,0,0,0,0,0,0,1,0,50,50,25,-1,-1,-1,-1}${0,-1,-1,0,0,0,0,0,0,1,0,5,5,5,-1,-1,-1,-1}${0,4,-1,0,0,0,0,0,0,1,0,50,50,25,-1,-1,-1,-1}${0,0,-1,0,0,0,0,1,0,1,0,75,5,5,-1,-1,-1,-1}${0,7,-1,0,0,0,0,0,0,1,0,50,50,75,-1,-1,-1,-1}}
+Developer.DesignWorkspace={{0,2,-1,0,0,0,0,1,0,1,0,50,75,22,-1,-1,-1,-1}${0,1,-1,0,0,0,0,0,0,1,0,50,75,22,-1,-1,-1,-1}${0,3,-1,0,0,0,0,1,0,1,0,50,50,22,-1,-1,-1,-1}${0,3,-1,0,0,0,0,1,0,1,0,50,50,22,-1,-1,-1,-1}${0,-1,-1,0,0,0,0,0,0,1,0,5,5,5,-1,-1,-1,-1}${0,4,-1,0,0,0,0,1,0,1,1,50,50,22,-1,-1,-1,-1}${0,0,-1,0,0,0,0,0,0,1,0,75,5,5,-1,-1,-1,-1}${0,7,-1,0,0,0,0,0,0,1,0,50,50,75,-1,-1,-1,-1}}
+Developer.kLdr={{0,2,-1,0,0,0,0,1,0,1,1,50,75,22,-1,-1,-1,-1}${0,1,-1,0,0,0,0,0,0,1,0,50,75,22,-1,-1,-1,-1}${0,3,-1,0,0,0,0,1,0,1,0,50,50,22,-1,-1,-1,-1}${0,3,-1,0,0,0,0,1,0,1,0,50,50,22,-1,-1,-1,-1}${0,-1,-1,0,0,0,0,0,0,1,0,5,5,5,-1,-1,-1,-1}${0,4,-1,0,0,0,0,1,0,1,0,50,50,22,-1,-1,-1,-1}${0,0,-1,0,0,0,0,0,0,1,0,75,5,5,-1,-1,-1,-1}${0,7,-1,0,0,0,0,0,0,1,0,50,50,75,-1,-1,-1,-1}}
+names.Developer={kLdr,DesignWorkspace,CodingWorkspace,DebugWorkspace}
+[vcs]
+provider.class=CVS LAN
+[lastOpenProjectName]
+Developer=kLdr
+[model]
+showDiagramContents=true
diff --git a/src/lib/kStuff/kLdr/tg/kLdr.tws b/src/lib/kStuff/kLdr/tg/kLdr.tws
new file mode 100644
index 0000000..4730025
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tg/kLdr.tws
@@ -0,0 +1,2 @@
+workspace.diagram.active = <oiref:design#Class#id83t9setug73fpetug74ah.diagram:oiref>
+workspace.diagram.open.0 = <oiref:design#Class#id83t9setug73fpetug74ah.diagram:oiref>
diff --git a/src/lib/kStuff/kLdr/tstkLdrHeap.c b/src/lib/kStuff/kLdr/tstkLdrHeap.c
new file mode 100644
index 0000000..a5891dd
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tstkLdrHeap.c
@@ -0,0 +1,223 @@
+/* $Id: tstkLdrHeap.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Heap testcase.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include <k/kHlp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+#define CHECK_FATAL(expr) \
+ do { if (!(expr)) { printf("tstkLdrHeap(%d): FATAL FAILURE - %s\n", __LINE__, #expr); return 1; } \
+ } while (0)
+
+#define CHECK(expr) \
+ do { if (!(expr)) { printf("tstkLdrHeap(%d): ERROR - %s\n", __LINE__, #expr); cErrors++; kHlpAssertBreakpoint();} \
+ } while (0)
+
+
+/**
+ * Get a random size.
+ * @returns random size.
+ */
+static unsigned RandSize(void)
+{
+ unsigned i = (unsigned)rand() % (256*1024 - 1);
+ return i ? i : 1;
+}
+
+/**
+ * Get a random index.
+ * @returns random index.
+ * @param cEntries The number of entries in the table.
+ */
+static unsigned RandIdx(unsigned cEntries)
+{
+ unsigned i = (unsigned)rand();
+ while (i >= cEntries)
+ i >>= 1;
+ return i;
+}
+
+#if 0
+# define kHlpAlloc(a) malloc(a)
+# define kHlpFree(a) free(a)
+#endif
+
+int main()
+{
+ int cErrors = 0;
+ int rc;
+#define MAX_ALLOCS 256
+ static struct
+ {
+ void *pv;
+ unsigned cb;
+ } s_aAllocs[MAX_ALLOCS];
+ unsigned cAllocs;
+ unsigned i;
+ unsigned j;
+
+ /*
+ * Some simple init / term.
+ */
+ rc = kHlpHeapInit();
+ CHECK_FATAL(!rc);
+ kHlpHeapTerm();
+
+ rc = kHlpHeapInit();
+ CHECK_FATAL(!rc);
+ kHlpHeapTerm();
+
+
+ /*
+ * Simple alloc all, free all in FIFO order.
+ */
+ rc = kHlpHeapInit();
+ CHECK_FATAL(!rc);
+
+ /* 1. allocate all slots. */
+ for (i = 0; i < MAX_ALLOCS; i++)
+ {
+ s_aAllocs[i].cb = RandSize();
+ s_aAllocs[i].pv = kHlpAlloc(s_aAllocs[i].cb);
+ CHECK(s_aAllocs[i].pv);
+ }
+
+ /* 2. free all slots. */
+ for (i = 0; i < MAX_ALLOCS; i++)
+ kHlpFree(s_aAllocs[i].pv);
+
+ /* terminate */
+ kHlpHeapTerm();
+
+
+ /*
+ * Simple alloc all, free all in LIFO order.
+ */
+ rc = kHlpHeapInit();
+ CHECK_FATAL(!rc);
+
+ /* 1. allocate all slots. */
+ for (i = 0; i < MAX_ALLOCS; i++)
+ {
+ s_aAllocs[i].cb = RandSize();
+ s_aAllocs[i].pv = kHlpAlloc(s_aAllocs[i].cb);
+ CHECK(s_aAllocs[i].pv);
+ }
+
+ /* 2. free all slots. */
+ i = MAX_ALLOCS;
+ while (i-- > 0)
+ kHlpFree(s_aAllocs[i].pv);
+
+ /* terminate */
+ kHlpHeapTerm();
+
+
+ /*
+ * Bunch of allocations, free half, allocate and free in pairs, free all.
+ */
+ rc = kHlpHeapInit();
+ CHECK_FATAL(!rc);
+
+ /* 1. allocate all slots. */
+ for (i = 0; i < MAX_ALLOCS; i++)
+ {
+ s_aAllocs[i].cb = RandSize();
+ s_aAllocs[i].pv = kHlpAlloc(s_aAllocs[i].cb);
+ CHECK(s_aAllocs[i].pv);
+ }
+ cAllocs = MAX_ALLOCS;
+
+ /* 2. free half (random order). */
+ while (cAllocs > MAX_ALLOCS / 2)
+ {
+ i = RandIdx(cAllocs);
+ kHlpFree(s_aAllocs[i].pv);
+ cAllocs--;
+ if (i != cAllocs)
+ s_aAllocs[i] = s_aAllocs[cAllocs];
+ }
+
+ /* 3. lots of alloc and free activity. */
+ for (j = 0; j < MAX_ALLOCS * 32; j++)
+ {
+ /* allocate */
+ unsigned cMax = RandIdx(MAX_ALLOCS / 4) + 1;
+ while (cAllocs < MAX_ALLOCS && cMax-- > 0)
+ {
+ i = cAllocs;
+ s_aAllocs[i].cb = RandSize();
+ s_aAllocs[i].pv = kHlpAlloc(s_aAllocs[i].cb);
+ CHECK(s_aAllocs[i].pv);
+ cAllocs++;
+ }
+
+ /* free */
+ cMax = RandIdx(MAX_ALLOCS / 4) + 1;
+ while (cAllocs > MAX_ALLOCS / 2 && cMax-- > 0)
+ {
+ i = RandIdx(cAllocs);
+ kHlpFree(s_aAllocs[i].pv);
+ cAllocs--;
+ if (i != cAllocs)
+ s_aAllocs[i] = s_aAllocs[cAllocs];
+ }
+ }
+
+ /* 4. free all */
+ while (cAllocs > 0)
+ {
+ i = RandIdx(cAllocs);
+ kHlpFree(s_aAllocs[i].pv);
+ cAllocs--;
+ if (i != cAllocs)
+ s_aAllocs[i] = s_aAllocs[cAllocs];
+ }
+
+ /* terminate */
+ kHlpHeapTerm();
+
+
+ /* summary */
+ if (!cErrors)
+ printf("tstkLdrHeap: SUCCESS\n");
+ else
+ printf("tstkLdrHeap: FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
diff --git a/src/lib/kStuff/kLdr/tstkLdrMod.c b/src/lib/kStuff/kLdr/tstkLdrMod.c
new file mode 100644
index 0000000..c49907b
--- /dev/null
+++ b/src/lib/kStuff/kLdr/tstkLdrMod.c
@@ -0,0 +1,629 @@
+/* $Id: tstkLdrMod.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - Module interpreter testcase.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include <k/kErr.h>
+#include <k/kErrors.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** The default base address used in the tests. */
+#define MY_BASEADDRESS 0x2400000
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The numbers of errors. */
+static int g_cErrors = 0;
+
+
+
+/**
+ * Report failure.
+ */
+static int Failure(const char *pszFormat, ...)
+{
+ va_list va;
+
+ g_cErrors++;
+
+ printf("tstLdrMod: ");
+ va_start(va, pszFormat);
+ vprintf(pszFormat, va);
+ va_end(va);
+ printf("\n");
+ return 1;
+}
+
+
+/** Dummy import resolver callback. */
+static int BasicTestsGetImport(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
+ const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
+{
+ *puValue = 0xdeadface;
+ *pfKind = KLDRSYMKIND_NO_BIT | KLDRSYMKIND_NO_TYPE;
+ return 0;
+}
+
+
+/**
+ * Verbose memcmp().
+ */
+static int TestMemComp(const void *pv1, const void *pv2, KSIZE cb)
+{
+ KSIZE off;
+ const KU8 *pb1 = (const KU8 *)pv1;
+ const KU8 *pb2 = (const KU8 *)pv2;
+ if (!memcmp(pb1, pb2, cb))
+ return 0;
+ printf("Mismatching blocks pv1=%p pv2=%p cb=%#x:\n", pv1, pv2, cb);
+ for (off = 0; off < cb; off++)
+ {
+ if (pb1[off] == pb2[off])
+ continue;
+ printf("%08x %02x != %02x\n", off, pb1[off], pb2[off]);
+ }
+ return memcmp(pb1, pb2, cb); /* lazy */
+}
+
+
+/**
+ * Performs basic relocation tests.
+ */
+static int BasicTestsRelocate(PKLDRMOD pMod, void *pvBits, void *pvBits2)
+{
+ const KSIZE cbImage = (KSIZE)kLdrModSize(pMod);
+ int rc;
+
+ printf("* Relocation test...\n");
+
+ /*
+ * Get the same bits again to check that we get the same result.
+ */
+ memset(pvBits2, 0xfe, cbImage);
+ rc = kLdrModGetBits(pMod, pvBits2, (KUPTR)pvBits, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s) (a)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (a)");
+
+ /*
+ * Short relocation round trip.
+ */
+ rc = kLdrModRelocateBits(pMod, pvBits2, 0x1000, (KUPTR)pvBits, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d (%s) (b1)", rc, kErrName(rc));
+ rc = kLdrModRelocateBits(pMod, pvBits2, (KUPTR)pvBits, 0x1000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d (%s) (b2)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (b)");
+
+ /*
+ * Longer trip where we also check the intermediate results.
+ */
+ /* stage one */
+ rc = kLdrModRelocateBits(pMod, pvBits, 0x1000000, (KUPTR)pvBits, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d (%s) (c1)", rc, kErrName(rc));
+ memset(pvBits2, 0xfe, cbImage);
+ rc = kLdrModGetBits(pMod, pvBits2, 0x1000000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s) (c1)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (c1)");
+
+ /* stage two */
+ rc = kLdrModRelocateBits(pMod, pvBits, ~(KUPTR)0x1010000, 0x1000000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d (%s) (c2)", rc, kErrName(rc));
+ memset(pvBits2, 0xef, cbImage);
+ rc = kLdrModGetBits(pMod, pvBits2, ~(KUPTR)0x1010000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s) (c2)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (c2)");
+
+ /* stage three */
+ rc = kLdrModRelocateBits(pMod, pvBits, MY_BASEADDRESS, ~(KUPTR)0x1010000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d (%s) (c3)", rc, kErrName(rc));
+ memset(pvBits2, 0xef, cbImage);
+ rc = kLdrModGetBits(pMod, pvBits2, MY_BASEADDRESS, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s) (c3)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (c3)");
+
+ /* stage four */
+ rc = kLdrModRelocateBits(pMod, pvBits, ~(KUPTR)0 / 2 - 0x10000, MY_BASEADDRESS, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d %(s) (c4)", rc, kErrName(rc));
+ memset(pvBits2, 0xdc, cbImage);
+ rc = kLdrModGetBits(pMod, pvBits2, ~(KUPTR)0 / 2 - 0x10000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s) (c4)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (c4)");
+
+ /* return */
+ rc = kLdrModRelocateBits(pMod, pvBits, (KUPTR)pvBits, ~(KUPTR)0 / 2 - 0x10000, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to relocate, rc=%d (%s) (c5)", rc, kErrName(rc));
+ memset(pvBits2, 0xcd, cbImage);
+ rc = kLdrModGetBits(pMod, pvBits2, (KUPTR)pvBits, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s) (c5)", rc, kErrName(rc));
+ if (TestMemComp(pvBits2, pvBits, cbImage))
+ return Failure("relocation test failed, mismatching bits (c5)");
+
+ return 0;
+}
+
+
+/**
+ * Dump symbols and check that we can query each of them recursivly.
+ */
+static int BasicTestsEnumSymCallback(PKLDRMOD pMod, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
+ const char *pszVersion, KLDRADDR uValue, KU32 fKind, void *pvUser)
+{
+ KLDRADDR uValue2;
+ KU32 fKind2;
+ int rc;
+
+ /* dump */
+ printf("#0x%08x: %016" PRI_KLDRADDR " %#08x", iSymbol, uValue, fKind);
+ if (pchSymbol)
+ printf(" %.*s", cchSymbol, pchSymbol);
+ printf("\n");
+
+ /* query by ordinal */
+ if (iSymbol != NIL_KLDRMOD_SYM_ORDINAL)
+ {
+ fKind2 = 0;
+ rc = kLdrModQuerySymbol(pMod, pvUser, MY_BASEADDRESS, iSymbol, NULL, 0, NULL, NULL, NULL,
+ &uValue2, &fKind2);
+ if (rc)
+ return Failure("Couldn't find symbol %#x (%.*s) by ordinal. rc=%d (%s)", iSymbol, cchSymbol, pchSymbol, rc, kErrName(rc));
+ if (uValue != uValue2)
+ return Failure("Symbol %#x (%.*s): Value mismatch %016" PRI_KLDRADDR " != %016" PRI_KLDRADDR " (enum!=query/ord) pvBits=%p",
+ iSymbol, cchSymbol, pchSymbol, uValue, uValue2, pvUser);
+ if (fKind != fKind2)
+ return Failure("Symbol %#x (%.*s): Kind mismatch %#x != %#x (enum!=query/ord) pvBits=%p",
+ iSymbol, cchSymbol, pchSymbol, fKind, fKind2, pvUser);
+ }
+
+ /* query by name. */
+ if (pchSymbol)
+ {
+ fKind2 = 0;
+ rc = kLdrModQuerySymbol(pMod, pvUser, MY_BASEADDRESS, NIL_KLDRMOD_SYM_ORDINAL, pchSymbol, cchSymbol, pszVersion,
+ NULL, NULL, &uValue2, &fKind2);
+ if (rc)
+ return Failure("Couldn't find symbol %#x (%.*s) by name. rc=%d (%s)", iSymbol, cchSymbol, pchSymbol, rc, kErrName(rc));
+ if (uValue != uValue2)
+ return Failure("Symbol %#x (%.*s): Value mismatch %016" PRI_KLDRADDR " != %016" PRI_KLDRADDR " (enum!=query/name) pvBits=%p",
+ iSymbol, cchSymbol, pchSymbol, uValue, uValue2, pvUser);
+ if (fKind != fKind2)
+ return Failure("Symbol %#x (%.*s): Kind mismatch %#x != %#x (enum!=query/name) pvBits=%p",
+ iSymbol, cchSymbol, pchSymbol, fKind, fKind2, pvUser);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Dump debugger information and check it for correctness.
+ */
+static int BasicTestEnumDbgInfoCallback(PKLDRMOD pMod, KU32 iDbgInfo, KLDRDBGINFOTYPE enmType,
+ KI16 iMajorVer, KI16 iMinorVer, KLDRFOFF offFile, KLDRADDR LinkAddress,
+ KLDRSIZE cb, const char *pszExtFile, void *pvUser)
+{
+ printf("#0x%08x: enmType=%d %d.%d offFile=0x%" PRI_KLDRADDR " LinkAddress=%" PRI_KLDRADDR " cb=%" PRI_KLDRSIZE " pvUser=%p\n",
+ iDbgInfo, enmType, iMajorVer, iMinorVer, (KLDRADDR)offFile, LinkAddress, cb, pvUser);
+ if (pszExtFile)
+ printf(" pszExtFile=%p '%s'\n", pszExtFile, pszExtFile);
+
+ if (enmType >= KLDRDBGINFOTYPE_END || enmType <= KLDRDBGINFOTYPE_INVALID)
+ return Failure("Bad enmType");
+ if (pvUser != NULL)
+ return Failure("pvUser");
+
+ return 0;
+}
+
+
+/**
+ * Performs the basic module loader test on the specified module and image bits.
+ */
+static int BasicTestsSub2(PKLDRMOD pMod, void *pvBits)
+{
+ KI32 cImports;
+ KI32 i;
+ int rc;
+ KU32 fKind;
+ KLDRADDR Value;
+ KLDRADDR MainEPAddress;
+ KLDRSTACKINFO StackInfo;
+
+ printf("* Testing queries with pvBits=%p...\n", pvBits);
+
+ /*
+ * Get the import modules.
+ */
+ cImports = kLdrModNumberOfImports(pMod, pvBits);
+ printf("cImports=%d\n", cImports);
+ if (cImports < 0)
+ return Failure("failed to query the number of import, cImports=%d", cImports);
+ for (i = 0; i < cImports; i++)
+ {
+ char szImportModule[260];
+ rc = kLdrModGetImport(pMod, pvBits, i, szImportModule, sizeof(szImportModule));
+ if (rc)
+ return Failure("failed to get import module name, rc=%d (%s). (%.260s)", rc, kErrName(rc), szImportModule);
+ printf("import #%d: '%s'\n", i, szImportModule);
+ }
+
+ /*
+ * Query stack info.
+ */
+ StackInfo.Address = ~(KLDRADDR)42;
+ StackInfo.LinkAddress = ~(KLDRADDR)42;
+ StackInfo.cbStack = ~(KLDRSIZE)42;
+ StackInfo.cbStackThread = ~(KLDRSIZE)42;
+ rc = kLdrModGetStackInfo(pMod, pvBits, MY_BASEADDRESS, &StackInfo);
+ if (rc)
+ return Failure("kLdrModGetStackInfo failed with rc=%d (%s)", rc, kErrName(rc));
+ printf("Stack: Address=%016" PRI_KLDRADDR " LinkAddress=%016" PRI_KLDRADDR "\n"
+ " cbStack=%016" PRI_KLDRSIZE " cbStackThread=%016" PRI_KLDRSIZE "\n",
+ StackInfo.Address, StackInfo.LinkAddress, StackInfo.cbStack, StackInfo.cbStackThread);
+ if (StackInfo.Address == ~(KLDRADDR)42)
+ return Failure("Bad StackInfo.Address");
+ if (StackInfo.LinkAddress == ~(KLDRADDR)42)
+ return Failure("Bad StackInfo.LinkAddress");
+ if (StackInfo.cbStack == ~(KLDRSIZE)42)
+ return Failure("Bad StackInfo.cbStack");
+ if (StackInfo.cbStackThread == ~(KLDRSIZE)42)
+ return Failure("Bad StackInfo.cbStackThread");
+
+ /*
+ * Query entrypoint.
+ */
+ MainEPAddress = ~(KLDRADDR)42;
+ rc = kLdrModQueryMainEntrypoint(pMod, pvBits, MY_BASEADDRESS, &MainEPAddress);
+ if (rc)
+ return Failure("kLdrModQueryMainEntrypoint failed with rc=%d (%s)", rc, kErrName(rc));
+ printf("Entrypoint: %016" PRI_KLDRADDR "\n", MainEPAddress);
+ if (MainEPAddress == ~(KLDRADDR)42)
+ return Failure("MainEPAddress wasn't set.");
+ if (MainEPAddress != NIL_KLDRADDR && MainEPAddress < MY_BASEADDRESS)
+ return Failure("Bad MainEPAddress (a).");
+ if (MainEPAddress != NIL_KLDRADDR && MainEPAddress >= MY_BASEADDRESS + kLdrModSize(pMod))
+ return Failure("Bad MainEPAddress (b).");
+
+ /*
+ * Debugger information.
+ */
+ rc = kLdrModHasDbgInfo(pMod, pvBits);
+ if (!rc)
+ printf("Has Debugger Information\n");
+ else if (rc == KLDR_ERR_NO_DEBUG_INFO)
+ printf("NO Debugger Information\n");
+ else
+ return Failure("kLdrModHasDbgInfo failed with rc=%d (%s)", rc, kErrName(rc));
+ rc = kLdrModEnumDbgInfo(pMod, pvBits, BasicTestEnumDbgInfoCallback, NULL);
+ if (rc)
+ return Failure("kLdrModEnumDbgInfo failed with rc=%d (%s)", rc, kErrName(rc));
+
+
+ /*
+ * Negative symbol query tests.
+ */
+ fKind = 0;
+ Value = 0x0badc0de;
+ rc = kLdrModQuerySymbol(pMod, pvBits, MY_BASEADDRESS, NIL_KLDRMOD_SYM_ORDINAL - 20, NULL, 0, NULL, NULL, NULL,
+ &Value, &fKind);
+ if (rc)
+ {
+ if (Value != 0)
+ return Failure("Value wasn't cleared on failure.");
+ }
+
+ fKind = 0;
+ Value = 0x0badc0de;
+ rc = kLdrModQuerySymbol(pMod, pvBits, MY_BASEADDRESS, NIL_KLDRMOD_SYM_ORDINAL, NULL, 0, NULL, NULL, NULL,
+ &Value, &fKind);
+ if (!rc)
+ return Failure("NIL ordinal succeeded!");
+ if (Value != 0)
+ return Failure("Value wasn't cleared on failure.");
+
+ /*
+ * Enumerate and query all symbols.
+ */
+ printf("\n"
+ "Symbols:\n");
+ rc = kLdrModEnumSymbols(pMod, pvBits, MY_BASEADDRESS, 0, BasicTestsEnumSymCallback, pvBits);
+ if (rc)
+ return Failure("kLdrModEnumSymbols failed with rc=%d (%s)", rc, kErrName(rc));
+
+
+/*int kLdrModCanExecuteOn(PKLDRMOD pMod, const void *pvBits, KCPUARCH enmArch, KCPU enmCpu);
+*/
+
+ return 0;
+}
+
+
+/**
+ * Performs the basic module loader test on the specified module
+ */
+static int BasicTestsSub(PKLDRMOD pMod)
+{
+ int rc;
+ KU32 i;
+ void *pvBits;
+ KSIZE cbImage;
+
+ /*
+ * Check/dump the module structure.
+ */
+ printf("pMod=%p u32Magic=%#x cSegments=%d\n", (void *)pMod, pMod->u32Magic, pMod->cSegments);
+ printf("enmType=%d enmFmt=%d enmArch=%d enmCpu=%d enmEndian=%d\n",
+ pMod->enmType, pMod->enmFmt, pMod->enmArch, pMod->enmCpu, pMod->enmEndian);
+ printf("Filename: %s (%d bytes)\n", pMod->pszFilename, pMod->cchFilename);
+ printf(" Name: %s (%d bytes)\n", pMod->pszName, pMod->cchName);
+ printf("\n");
+
+ if (pMod->u32Magic != KLDRMOD_MAGIC)
+ return Failure("Bad u32Magic");
+ if (strlen(pMod->pszFilename) != pMod->cchFilename)
+ return Failure("Bad cchFilename");
+ if (strlen(pMod->pszName) != pMod->cchName)
+ return Failure("Bad cchName");
+ if (pMod->enmFmt >= KLDRFMT_END || pMod->enmFmt <= KLDRFMT_INVALID)
+ return Failure("Bad enmFmt");
+ if (pMod->enmType >= KLDRTYPE_END || pMod->enmType <= KLDRTYPE_INVALID)
+ return Failure("Bad enmType: %d", pMod->enmType);
+ if (!K_ARCH_IS_VALID(pMod->enmArch))
+ return Failure("Bad enmArch");
+ if (pMod->enmCpu >= KCPU_END || pMod->enmCpu <= KCPU_INVALID)
+ return Failure("Bad enmCpu");
+ if (pMod->enmEndian >= KLDRENDIAN_END || pMod->enmEndian <= KLDRENDIAN_INVALID)
+ return Failure("Bad enmEndian");
+
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ printf("seg #%d: pvUser=%p enmProt=%d Name: '%.*s' (%d bytes)\n",
+ i, pMod->aSegments[i].pvUser, pMod->aSegments[i].enmProt,
+ pMod->aSegments[i].cchName, pMod->aSegments[i].pchName, pMod->aSegments[i].cchName);
+ printf("LinkAddress: %016" PRI_KLDRADDR " cb: %016" PRI_KLDRSIZE " Alignment=%08" PRI_KLDRADDR " \n",
+ pMod->aSegments[i].LinkAddress, pMod->aSegments[i].cb, pMod->aSegments[i].Alignment);
+ printf(" RVA: %016" PRI_KLDRADDR " cbMapped: %016" PRI_KLDRSIZE " MapAddress=%p\n",
+ pMod->aSegments[i].RVA, (KLDRSIZE)pMod->aSegments[i].cbMapped, (void *)pMod->aSegments[i].MapAddress);
+ printf(" offFile: %016" PRI_KLDRADDR " cbFile: %016" PRI_KLDRSIZE "\n",
+ (KLDRADDR)pMod->aSegments[i].offFile, (KLDRSIZE)pMod->aSegments[i].cbFile);
+ printf("\n");
+
+ if (pMod->aSegments[i].pvUser != NULL)
+ return Failure("Bad pvUser");
+ if (pMod->aSegments[i].enmProt >= KPROT_END || pMod->aSegments[i].enmProt <= KPROT_INVALID)
+ return Failure("Bad enmProt");
+ if (pMod->aSegments[i].MapAddress != 0)
+ return Failure("Bad MapAddress");
+ if (pMod->aSegments[i].cbMapped < pMod->aSegments[i].cb)
+ return Failure("Bad cbMapped (1)");
+ if (pMod->aSegments[i].cbMapped && !pMod->aSegments[i].Alignment)
+ return Failure("Bad cbMapped (2)");
+ if (pMod->aSegments[i].cbMapped > kLdrModSize(pMod))
+ return Failure("Bad cbMapped (3)");
+ if ( pMod->aSegments[i].Alignment
+ && (pMod->aSegments[i].RVA & (pMod->aSegments[i].Alignment - 1)))
+ return Failure("Bad RVA (1)");
+ if (pMod->aSegments[i].RVA != NIL_KLDRADDR && !pMod->aSegments[i].Alignment)
+ return Failure("Bad RVA (2)");
+ if ( pMod->aSegments[i].RVA != NIL_KLDRADDR
+ && pMod->aSegments[i].RVA >= kLdrModSize(pMod))
+ return Failure("Bad RVA (3)");
+ if ( pMod->aSegments[i].RVA != NIL_KLDRADDR
+ && pMod->aSegments[i].RVA + pMod->aSegments[i].cbMapped > kLdrModSize(pMod))
+ return Failure("Bad RVA/cbMapped (4)");
+ if (pMod->aSegments[i].LinkAddress != NIL_KLDRADDR && !pMod->aSegments[i].Alignment)
+ return Failure("Bad LinkAddress");
+ if ( pMod->aSegments[i].LinkAddress != NIL_KLDRADDR
+ && (pMod->aSegments[i].LinkAddress) & (pMod->aSegments[i].Alignment - 1))
+ return Failure("Bad LinkAddress alignment");
+ if (pMod->aSegments[i].offFile != -1 && pMod->aSegments[i].cbFile == -1)
+ return Failure("Bad offFile");
+ if (pMod->aSegments[i].offFile == -1 && pMod->aSegments[i].cbFile != -1)
+ return Failure("Bad cbFile");
+ }
+
+
+ /*
+ * Get image the size and query the image bits.
+ */
+ printf("* Testing user mapping...\n");
+
+ cbImage = (KSIZE)kLdrModSize(pMod);
+ if (cbImage != kLdrModSize(pMod))
+ return Failure("aborting test because the image is too huge!");
+ pvBits = malloc((KSIZE)cbImage);
+ if (!pvBits)
+ return Failure("failed to allocate %d bytes for the image", cbImage);
+
+ rc = kLdrModGetBits(pMod, pvBits, (KUPTR)pvBits, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("failed to get image bits, rc=%d (%s)", rc, kErrName(rc));
+
+ /*
+ * Another cleanup nesting.
+ */
+ rc = BasicTestsSub2(pMod, pvBits);
+ if (!rc)
+ {
+ /*
+ * Test relocating the bits in a few different ways before we're done with them.
+ */
+ void *pvBits2 = malloc((KSIZE)cbImage);
+ if (pvBits2)
+ {
+ rc = BasicTestsRelocate(pMod, pvBits, pvBits2);
+ free(pvBits2);
+ }
+ else
+ rc = Failure("failed to allocate %d bytes for the 2nd image", cbImage);
+ }
+
+ free(pvBits);
+ return rc;
+}
+
+
+/**
+ * Tests the mapping related api, after mapping.
+ */
+static int BasicTestsSubMap2(PKLDRMOD pMod)
+{
+ int rc;
+
+ rc = kLdrModFixupMapping(pMod, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("kLdrModFixupMapping (a) failed, rc=%d (%s)", rc, kErrName(rc));
+
+ rc = kLdrModReload(pMod);
+ if (rc)
+ return Failure("kLdrModReload (a) failed, rc=%d (%s)", rc, kErrName(rc));
+
+ rc = kLdrModReload(pMod);
+ if (rc)
+ return Failure("kLdrModReload (b) failed, rc=%d (%s)", rc, kErrName(rc));
+
+ rc = kLdrModFixupMapping(pMod, BasicTestsGetImport, NULL);
+ if (rc)
+ return Failure("kLdrModFixupMapping (b) failed, rc=%d (%s)", rc, kErrName(rc));
+
+ rc = kLdrModAllocTLS(pMod);
+ if (rc)
+ return Failure("kLdrModAllocTLS (a) failed, rc=%d (%s)", rc, kErrName(rc));
+ kLdrModFreeTLS(pMod);
+
+ rc = kLdrModAllocTLS(pMod);
+ if (rc)
+ return Failure("kLdrModAllocTLS (b) failed, rc=%d (%s)", rc, kErrName(rc));
+ kLdrModFreeTLS(pMod);
+
+ /*
+ * Repeat the BasicTestsSub2 with pvBits as NULL to test module
+ * interpreters that can utilize the mapping.
+ */
+ rc = BasicTestsSub2(pMod, NULL);
+ if (rc)
+ return Failure("BasicTestsSub2 in Map2 failed, rc=%d (%s)", rc, kErrName(rc));
+ return 0;
+}
+
+
+/**
+ * Tests the mapping related api.
+ */
+static int BasicTestsSubMap(PKLDRMOD pMod)
+{
+ int rc, rc2;
+ printf("* Mapping tests...\n");
+
+ rc = kLdrModMap(pMod);
+ if (rc)
+ return Failure("kLdrModMap failed, rc=%d (%s)", rc, kErrName(rc));
+ rc = BasicTestsSubMap2(pMod);
+ rc2 = kLdrModUnmap(pMod);
+ if (rc2)
+ {
+ Failure("kLdrModUnmap failed, rc=%d (%s)", rc2, kErrName(rc2));
+ rc = rc ? rc : rc2;
+ }
+
+ printf("* Mapping tests done.\n");
+ return rc;
+}
+
+
+/**
+ * Performs basic module loader tests on the specified file.
+ */
+static int BasicTests(const char *pszFilename)
+{
+ PKLDRMOD pMod;
+ int rc, rc2;
+
+ printf("tstLdrMod: Testing '%s'", pszFilename);
+ rc = kLdrModOpen(pszFilename, &pMod);
+ if (!rc)
+ {
+ rc = BasicTestsSub(pMod);
+ if (!rc)
+ rc = BasicTestsSubMap(pMod);
+ if (!rc)
+ rc = BasicTestsSub2(pMod, NULL);
+ rc2 = kLdrModClose(pMod);
+ if (rc2)
+ Failure("failed to close '%s', rc=%d (%s)", pszFilename, rc, kErrName(rc));
+ if (rc2 && !rc)
+ rc = rc2;
+ }
+ else
+ Failure("Failed to open '%s', rc=%d (%s)", pszFilename, rc, kErrName(rc));
+ return rc ? 1 : 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ BasicTests(argv[argc-1]);
+
+ if (!g_cErrors)
+ printf("tstLdrMod: SUCCESS\n");
+ else
+ printf("tstLdrMod: FAILURE - %d errors\n", g_cErrors);
+ return !!g_cErrors;
+}
+