From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/lib/kStuff/kLdr/Doxyfile | 1252 +++++++ src/lib/kStuff/kLdr/Makefile.kmk | 224 ++ src/lib/kStuff/kLdr/kLdr-os2.c | 66 + src/lib/kStuff/kLdr/kLdr-os2.def | 115 + src/lib/kStuff/kLdr/kLdr-win.c | 77 + src/lib/kStuff/kLdr/kLdr-win.def | 113 + src/lib/kStuff/kLdr/kLdr.c | 145 + src/lib/kStuff/kLdr/kLdrA-os2.asm | 66 + src/lib/kStuff/kLdr/kLdrDyld.c | 1509 ++++++++ src/lib/kStuff/kLdr/kLdrDyldFind.c | 1086 ++++++ src/lib/kStuff/kLdr/kLdrDyldMod.c | 1300 +++++++ src/lib/kStuff/kLdr/kLdrDyldOS.c | 133 + src/lib/kStuff/kLdr/kLdrDyldSem.c | 198 ++ src/lib/kStuff/kLdr/kLdrExeStub-os2.asm | 72 + src/lib/kStuff/kLdr/kLdrExeStub-os2.c | 59 + src/lib/kStuff/kLdr/kLdrExeStub-os2A.asm | 41 + src/lib/kStuff/kLdr/kLdrExeStub-win.c | 62 + src/lib/kStuff/kLdr/kLdrHlp.h | 9 + src/lib/kStuff/kLdr/kLdrInternal.h | 463 +++ src/lib/kStuff/kLdr/kLdrMod.c | 914 +++++ src/lib/kStuff/kLdr/kLdrModLX.c | 2701 ++++++++++++++ src/lib/kStuff/kLdr/kLdrModMachO.c | 3729 ++++++++++++++++++++ src/lib/kStuff/kLdr/kLdrModNative.c | 1206 +++++++ src/lib/kStuff/kLdr/kLdrModPE.c | 2044 +++++++++++ src/lib/kStuff/kLdr/testcase/Makefile.kmk | 305 ++ src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86 | Bin 0 -> 3072 bytes .../kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86 | Bin 0 -> 2800 bytes src/lib/kStuff/kLdr/testcase/tst-0-a.c | 10 + src/lib/kStuff/kLdr/testcase/tst-0-b.c | 10 + src/lib/kStuff/kLdr/testcase/tst-0-c.c | 10 + src/lib/kStuff/kLdr/testcase/tst-0-d.c | 8 + src/lib/kStuff/kLdr/testcase/tst-0-driver.c | 502 +++ src/lib/kStuff/kLdr/testcase/tst-0.c | 13 + src/lib/kStuff/kLdr/testcase/tst-1-a.c | 10 + src/lib/kStuff/kLdr/testcase/tst-1-b.c | 10 + src/lib/kStuff/kLdr/testcase/tst-1-c.c | 10 + src/lib/kStuff/kLdr/testcase/tst-1-d.c | 8 + src/lib/kStuff/kLdr/testcase/tst-1.c | 15 + src/lib/kStuff/kLdr/testcase/tst-2-a.c | 8 + src/lib/kStuff/kLdr/testcase/tst-2-b.c | 10 + src/lib/kStuff/kLdr/testcase/tst-2-c.c | 10 + src/lib/kStuff/kLdr/testcase/tst-2-d.c | 10 + src/lib/kStuff/kLdr/testcase/tst-2.c | 16 + src/lib/kStuff/kLdr/testcase/tst-3-driver.c | 216 ++ src/lib/kStuff/kLdr/testcase/tst-3-ext.c | 39 + src/lib/kStuff/kLdr/testcase/tst-3-imp-os2.def | 34 + src/lib/kStuff/kLdr/testcase/tst-3-imp-win.def | 34 + src/lib/kStuff/kLdr/testcase/tst-3.c | 78 + src/lib/kStuff/kLdr/testcase/tst.h | 57 + src/lib/kStuff/kLdr/testcase/tstDllMain.c | 192 + .../kStuff/kLdr/testcase/tstDllMainStub-os2.asm | 40 + src/lib/kStuff/kLdr/testcase/tstDllMainStub.c | 76 + .../kStuff/kLdr/testcase/tstExeMainStub-os2.asm | 40 + src/lib/kStuff/kLdr/testcase/tstExeMainStub.c | 93 + src/lib/kStuff/kLdr/tg/KLDRSTATE.gif | Bin 0 -> 14294 bytes src/lib/kStuff/kLdr/tg/KLDRSTATE.txvstc | 529 +++ src/lib/kStuff/kLdr/tg/default.txvpck | 8 + src/lib/kStuff/kLdr/tg/kLdr.tpr | 23 + src/lib/kStuff/kLdr/tg/kLdr.tws | 2 + src/lib/kStuff/kLdr/tstkLdrHeap.c | 223 ++ src/lib/kStuff/kLdr/tstkLdrMod.c | 629 ++++ 61 files changed, 20862 insertions(+) create mode 100644 src/lib/kStuff/kLdr/Doxyfile create mode 100644 src/lib/kStuff/kLdr/Makefile.kmk create mode 100644 src/lib/kStuff/kLdr/kLdr-os2.c create mode 100644 src/lib/kStuff/kLdr/kLdr-os2.def create mode 100644 src/lib/kStuff/kLdr/kLdr-win.c create mode 100644 src/lib/kStuff/kLdr/kLdr-win.def create mode 100644 src/lib/kStuff/kLdr/kLdr.c create mode 100644 src/lib/kStuff/kLdr/kLdrA-os2.asm create mode 100644 src/lib/kStuff/kLdr/kLdrDyld.c create mode 100644 src/lib/kStuff/kLdr/kLdrDyldFind.c create mode 100644 src/lib/kStuff/kLdr/kLdrDyldMod.c create mode 100644 src/lib/kStuff/kLdr/kLdrDyldOS.c create mode 100644 src/lib/kStuff/kLdr/kLdrDyldSem.c create mode 100644 src/lib/kStuff/kLdr/kLdrExeStub-os2.asm create mode 100644 src/lib/kStuff/kLdr/kLdrExeStub-os2.c create mode 100644 src/lib/kStuff/kLdr/kLdrExeStub-os2A.asm create mode 100644 src/lib/kStuff/kLdr/kLdrExeStub-win.c create mode 100644 src/lib/kStuff/kLdr/kLdrHlp.h create mode 100644 src/lib/kStuff/kLdr/kLdrInternal.h create mode 100644 src/lib/kStuff/kLdr/kLdrMod.c create mode 100644 src/lib/kStuff/kLdr/kLdrModLX.c create mode 100644 src/lib/kStuff/kLdr/kLdrModMachO.c create mode 100644 src/lib/kStuff/kLdr/kLdrModNative.c create mode 100644 src/lib/kStuff/kLdr/kLdrModPE.c create mode 100644 src/lib/kStuff/kLdr/testcase/Makefile.kmk create mode 100644 src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86 create mode 100644 src/lib/kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86 create mode 100644 src/lib/kStuff/kLdr/testcase/tst-0-a.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-0-b.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-0-c.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-0-d.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-0-driver.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-0.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-1-a.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-1-b.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-1-c.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-1-d.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-1.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-2-a.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-2-b.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-2-c.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-2-d.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-2.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-3-driver.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-3-ext.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst-3-imp-os2.def create mode 100644 src/lib/kStuff/kLdr/testcase/tst-3-imp-win.def create mode 100644 src/lib/kStuff/kLdr/testcase/tst-3.c create mode 100644 src/lib/kStuff/kLdr/testcase/tst.h create mode 100644 src/lib/kStuff/kLdr/testcase/tstDllMain.c create mode 100644 src/lib/kStuff/kLdr/testcase/tstDllMainStub-os2.asm create mode 100644 src/lib/kStuff/kLdr/testcase/tstDllMainStub.c create mode 100644 src/lib/kStuff/kLdr/testcase/tstExeMainStub-os2.asm create mode 100644 src/lib/kStuff/kLdr/testcase/tstExeMainStub.c create mode 100644 src/lib/kStuff/kLdr/tg/KLDRSTATE.gif create mode 100644 src/lib/kStuff/kLdr/tg/KLDRSTATE.txvstc create mode 100644 src/lib/kStuff/kLdr/tg/default.txvpck create mode 100644 src/lib/kStuff/kLdr/tg/kLdr.tpr create mode 100644 src/lib/kStuff/kLdr/tg/kLdr.tws create mode 100644 src/lib/kStuff/kLdr/tstkLdrHeap.c create mode 100644 src/lib/kStuff/kLdr/tstkLdrMod.c (limited to 'src/lib/kStuff/kLdr') 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 , where is the value of +# the FILE_VERSION_FILTER tag, and 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 , where +# is the value of the INPUT_FILTER tag, and 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 +# +# 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 + * + * 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 + +#include +#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 +; +; 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 + * + * 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 + +#include +#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 +; +; 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 + * + * 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 +#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 +; +; 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 + * + * 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 +#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 + * + * 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 +#include "kLdrInternal.h" + +#if K_OS == K_OS_LINUX +# include + +#elif K_OS == K_OS_OS2 +# define INCL_BASE +# define INCL_ERRORS +# include +# 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 + +#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 + * + * 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 +#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 + * + * 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 +#include "kLdrInternal.h" + +#if K_OS == K_OS_OS2 +# define INCL_BASE +# define INCL_ERRORS +# include + +#elif K_OS == K_OS_WINDOWS +# undef IMAGE_DOS_SIGNATURE +# undef IMAGE_NT_SIGNATURE +# include + +#else +# include + +#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 + * + * 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 +#include +#include + +#if K_OS == K_OS_DARWIN +# include +# 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 + +#elif K_OS == K_OS_WINDOWS +# include + +#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 +; +; 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 + * + * 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 +#include + + +/******************************************************************************* +* 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 +; +; 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 + * + * 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 +#include +#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 + * + * 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 +#include + +#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 + * + * 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 +#include "kLdrInternal.h" +#include +#include +#if 1 /* testing headers */ +# include +# include +# include +#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 + * + * 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 +#include "kLdrInternal.h" +#include + + +/******************************************************************************* +* 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 +#include "kLdrInternal.h" +#include + + +/******************************************************************************* +* 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 + * + * 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 +#include "kLdrInternal.h" + +#if K_OS == K_OS_OS2 +# define INCL_BASE +# include + +# 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 +# ifndef IMAGE_SCN_TYPE_NOLOAD +# define IMAGE_SCN_TYPE_NOLOAD 0x00000002 +# endif + +/*#elif defined(__NT__) +#include */ + +#elif K_OS == K_OS_DARWIN +# include +# include + +#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 + * + * 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 +#include "kLdrInternal.h" +#include + + +/******************************************************************************* +* 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 +# +# 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 Binary files /dev/null and b/src/lib/kStuff/kLdr/testcase/bin/tst-3.dll.win.x86 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 Binary files /dev/null and b/src/lib/kStuff/kLdr/testcase/bin/tst-3.rel.darwin.x86 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 + * + * 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 +#include +#include +#include + + +/******************************************************************************* +* 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 + +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 + * + * 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 + +#include +#include +#include +#include +#ifdef _MSC_VER +# include +#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 + * + * 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 +; +; 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 +; +; 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 + * + * 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 + * + * 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 +#include + +#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 + * + * 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 +# include + +#elif K_OS == K_OS_WINDOWS +# include +# include + +#elif K_OS == K_OS_DARWIN +# include +# include + +#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 +; +; 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 + * + * 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 + +#elif K_OS == K_OS_WINDOWS +# include + +#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 +; +; 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 + * + * 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 + +#elif K_OS == K_OS_WINDOWS +/* nothing */ + +#elif K_OS == K_OS_NT +# include /** @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 Binary files /dev/null and b/src/lib/kStuff/kLdr/tg/KLDRSTATE.gif 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + 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 = +workspace.diagram.open.0 = 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 + * + * 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 +#include + +#include +#include + + +/******************************************************************************* +* 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 + * + * 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 +#include +#include + +#include +#include +#include +#include + + +/******************************************************************************* +* 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; +} + -- cgit v1.2.3