I present to your attention the translation of my article from the blog of the Darling Project. A little help on the concepts used: Darwin - the open source operating system underlying macOS, iOS and other operating systems from Apple; Mach-O is a binary format of executable files and libraries used in Darwin; dyld is a dynamic loader used in Darwin for downloading Mach-O files; dylib is a dynamically loaded library (usually with the extension .dylib
).
The goal of the Darling Project is to make it possible to run macOS applications under Linux, and the ability to download binary files in the Mach-O format is one of the key steps towards achieving this goal.
Initially, Darling was built around its own implementation of the Mach-O loader and the idea of ​​translating calls between the high-level Darwin API and its Linux counterparts. Since then, our focus has shifted to running code in an increasingly isolated Darwin container. Since we switched to using Mach-O for internal components of Darling , we have the opportunity to use Apple's original dyld, as well as build many other open-source components of Darwin. We still need a simple Mach-O loader to load the dyld itself.
Mach-O, along with Mach itself, is probably the most noticeable distinguishing features of Darwin, and the various libraries and frameworks supplied by Apple make extensive use of the little-known features of the Mach-O format. This makes working with Mach-O one of the most important and visible tasks in the development of Darling. From implementing your own Mach-O loaders to building Darwin components (first as special ELF files, and now as true Mach-O) - we need to understand the internal structure of Mach-O at a much deeper level than is usually required from ordinary developers under the Darwin-platform.
Let's finish with this introduction and proceed to discuss some of the tricks that Mach-O format can offer us.
In Windows and Linux, dynamic libraries are referenced by their name (for example, libc.so
), after which the task of the dynamic linker is to find a library with the same name in one of the standard directories for libraries. In Darwin, the (almost) full path to the library file, called the install name of this library, is used instead. Supposedly, this was done to prevent the substitution of dylib [dylib hijacking] - an attack in which a fake dylib is placed in the directory in which it will be found by the dynamic linker before the real one, which allows the fake dylib to execute arbitrary code on behalf of the program, which was thus convinced dylib download.
Not only executable files and libraries store the full installation names of their dependencies, but Mach-O dependencies themselves “know” their own installation names. Actually, this is how the linker learns which setup names to use for the dependencies: he reads them from the dependencies themselves.
When linking dylib, you specify its installation name using the ld -install_name
or -dylib_install_name
:
$ ld -o libfoo.dylib foo.o -install_name /usr/local/lib/libfoo.dylib
Now, when you link another Mach-O file (say, libbar.dylib
) to libfoo.dylib
, ld will write the libfoo installation name /usr/local/lib/libfoo.dylib
in the libbar
dependency libbar
, and that’s where dyld is will look for libfoo
at runtime.
Using the full path works quite well for system libraries, which are indeed installed in previously known places in the file system; but with libraries that come as part of [app bundles], there is a problem. Although each application might assume that it will be installed in /Applications/AppName.app
, generally speaking, it is understood that application assemblies are portable and can be freely moved around the file system, so a specific library path inside such assemblies cannot be known in advance.
As a solution to this problem, Darwin allows the installation names to begin with @executable_path
, @loader_path
, or @rpath
— that is, not to be absolute, but to specify the path to the library relative to the path to the main executable file, the path to the “load” (executable file or library, which directly depends on this library) or relative to the list of paths defined by the main executable file, respectively. @executable_path
and @loader_path
work without additional complexity, but if at least one of your dependencies (or their transitive dependencies) has an installation name using @rpath
, you must explicitly specify @rpath
when linking your executable file using the ld -rpath
option times as you need:
$ ld -o runme -rpath @executable_path/../Frameworks -rpath @executable_path/../bin/lib
(The concept of rpath to some extent destroys the original idea of ​​previously known paths to libraries, and opens up the possibility of carrying out attacks to replace dylib. We can assume that this makes everything associated with the installation names rather useless.)
When the source code of a project occupies several files, it is perfectly normal if these files are mutually dependent on each other. This works fine as long as all these files are compiled into one binary — an executable file or a library. What does not work is when several dynamic libraries are dependent on each other.
You can argue that instead of using circular dependencies between dynamic libraries, it is worth redesigning the project architecture, and I agree with you. But if there is something typical for Apple, it is that they never stop to think things over and do it right; instead, they lay crutches and stunts on top of each other. In this case, we need to make circular dependencies work for Darling, since the various libSystem libSystem
, such as libsystem_dyld
, libsystem_kernel
and libsystem_pthread
, all depend on each other. (Until recently, we also had to loop Cocoa frameworks such as AppKit, Core Graphics and Core OpenGL cyclically, because of how Core OpenGL was implemented in The Cocotron, but we redesigned the architecture of our Core OpenGL and were able to get rid of this cyclic dependencies.)
In principle, cyclic dependencies should work fine: the dynamic linker can already load each library only once, so that it will not have problems with endless recursion. The problem is that such libraries cannot be simply linked , since each call to the linker creates only one library, and when linking any binary it is necessary to transfer to the linker all its dependencies already linked. We have to link one of our libraries first, and at this moment the rest are not ready yet, so we can not transfer them to the linker.
The trick here is to link some (or, for simplicity, all) of these libraries twice . For the first time, tell the linker to ignore the missing dependencies, and indeed, do not pass the dependencies:
$ ld -o libfoo.dylib foo.o -flat_namespace -undefined suppress $ ld -o libbar.dylib bar.o -flat_namespace -undefined suppress
(See below about -flat_namespace
.)
Of course, if you try to directly use the resulting dylib-ki, you will get dynamic linking errors at runtime. Instead, link these libraries a second time, passing the resulting dylib-ki as dependencies:
$ ld -o libfoo.dylib foo.o libbar.dylib $ ld -o libbar.dylib bar.o libfoo.dylib
This time the linker sees all the characters, so we do not tell him to ignore errors (and if there are really not enough characters, you will get an error).
Despite the fact that some, if not all, of the libraries are linked to the "wrong" copies of their dependencies, during the execution of dyld they will see the correct versions. To make this work, make sure that both copies of each library have the same installation name.
Another detail here is the order of initialization. Any code can declare initialization functions using the __attribute__((constructor))
compiler magic command (the list of such initializers falls into the __mod_init_func
section in the Mach-O file). These functions are called by dyld when loading the binary in which they are located before calling main()
. Usually, the initializers of each library are executed after the initializers of its dependencies, so each initializer can count on the fact that the dependency libraries are already initialized and ready to go. Of course, this cannot be guaranteed for circular dependencies; dyld will execute their initializers in some order. You can mark dependencies as dependencies up [upward dependencies] to customize this order; dyld will initialize the libraries that someone has marked as their dependency up, last. So, to get libfoo
initialize after libbar
, link them like this:
$ ld -o libfoo.dylib foo.o libbar.dylib $ ld -o libbar.dylib bar.o -upward_library libfoo.dylib
To make it more convenient, we in Darling have a CMake function called add_circular
, which takes on all the difficulties and allows us to use it in such a simple and declarative way:
set(DYLIB_INSTALL_NAME "/usr/lib/system/libdispatch.dylib") add_circular(libdispatch_shared FAT SOURCES ${dispatch_SRCS} SIBLINGS system_c system_kernel system_malloc system_blocks system_pthread system_dyld system_duct unwind platform compiler_rt UPWARD objc )
Character tables in Mach-O do not just store the names of characters, they also “remember” which library (or executable file) which character comes from. In other words, symbol names exist in namespaces, given by which binary defines them; hence the “two-level namespace] (another level refers to the names of symbols themselves).
A two-level namespace is introduced to prevent character name conflicts. Usually, if several libraries define characters with the same name, you will get an error during linking; but it may not work if you load the libraries at runtime (for example, plugins) or when the versions of the libraries during linking and runtime are different. For libraries that use a two-level namespace, this is not a problem — it allows several libraries to define characters with the same name without creating conflicts.
Two-level namespaces can be disabled by returning to using the "flat namespace" (one of the reasons for this is that the use of a two-level namespace implies that each character must be resolved during linking, so flat namespace is required for -undefined_suppress
we saw above). Ld has two flags that allow you to disable the two-level namespace during linking: -flat_namespace
, which affects only one Mach-O file, and -force_flat_namespace
, which only works with executable files, not libraries, and makes the whole process use a flat namespace. In addition, you can force dyld to use flat namespace at runtime by setting the environment variable DYLD_FORCE_FLAT_NAMESPACE
.
One feature of using a two-level namespace is that you always have to explicitly link each Mach-O to all the libraries and frameworks on which it depends. For example, if you link to AppKit, you can't just use Foundation; You will have to link explicitly to her. Another feature is that, as the author of a library or framework, you cannot freely move the implementation of the “down” symbol along a chain of dependencies, as you could get used to doing (for example, you can't just move the code from AppKit to Foundation). To allow this, Mach-O, ld and dyld have several additional features, namely, sub-libraries , re-export of symbols and meta-symbols .
Sub-libraries — a mechanism that allows one library (called a facade library [facade library] or umbrella library [umbrella library]) to delegate the implementation of a part of its functionality to another library (called its sub-library); or, if you look at it from the other side, allowing the library to publicly re-export symbols provided by another library.
The main place where it is used is, again, the libSystem
with its sub-libraries, which are located in /usr/lib/system
; but it can be used with any pair of libraries:
$ ld -o libfoo.dylib foo.o -lobjc -sub_library libobjc # : $ ld -o libfoo.dylib foo.o -reexport-lobjc
The only thing that affects this compared to just linking to that library is that the command LC_REEXPORT_DYLIB
written to the resulting file instead of the usual LC_LOAD_DYLIB
(including, the symbols from the library sub-library are not copied to the umbrella library, so it is not even relink if new symbols are later added to the sublibrary). At runtime, LC_REEXPORT_DYLIB
also works similar to LC_LOAD_DYLIB
: dyld loads the sub-library and makes its characters available to others (but unlike LC_LOAD_DYLIB
, from the point of view of the two-level namespace, characters will come from the umbrella-library).
What really is different about LC_REEXPORT_DYLIB
is what ld does when you link another library to libfoo
: instead of just looking for characters in all the object and dylib files that were passed to it, ld will also open and view the re-exported sub-library (in this example libobjc
).
How does he know where to find her? The only thing that is stored in libfoo.dylib
is the installation name libobjc.dylib
, so this is where ld expects to find it. This means that the library must be installed in its place before it can be used as a sub-library for anything else; this works fine for system libraries like libobjc
, but it can be very inconvenient or completely impossible if you are trying to re-export your own sub-library.
To solve this problem, ld provides the option -dylib_file
, which allows you to specify a different path to dylib for use during linking:
$ ld -o libfoo.dylib foo.o -reexport_library /path/to/libsubfoo.dylib $ ld -o libbar.dylib bar.o libfoo.dylib -dylib_file @executable_path/lib/foo/libsubfoo.dylib:/path/to/libsubfoo.dylib
Although libSystem
and some other system libraries re-export their sub-libraries, you do not have to use -dylib_file
when linking each of the executable files to macOS; This is because the system libraries are already installed according to their installation name. But when building Darling on Linux, we have to pass several options -dylib_file
(and other common arguments) to each ld call. We do this using a special function that is automatically applied when using add_darling_library
, add_darling_executable
and others.
Sometimes the library may need to re-export some of the characters — but not all at once — from another library. For example, the Core Foundation NSObject
, which in recent versions is implemented within the Objective-C runtime, for the sake of compatibility.
(If you are wondering why NSObject
was once at all in the Core Foundation instead of the Foundation, then this is due to the fact that the free conversion [toll-free bridging] is implemented, the ability to directly cast between the corresponding Core Foundation and the Foundation types without additional conversion], requires that private wrapper classes over types from the Core Foundation (for example, __NSCFString
) be implemented in the Core Foundation, and being Objective-C objects, they must inherit from NSObject
. another, leaving NSObject
with all his heirs in the Foundation and cyclic The Core Foundation and Foundation are NSObject
together, but Apple decided to move these helper private classes along with the NSObject
to the Core Foundation, and in Darling we do it the same way to maintain compatibility.)
You can pass ld a list of characters to be -reexported_symbols_list
using its option -reexported_symbols_list
:
$ echo .objc_class_name_NSObject > reexport_list.exp $ ld -o CoreFoundation CFFiles.o -lobjc -reexported_symbols_list reexport_list.exp
Although re-exporting some characters sounds very similar to re-exporting all characters, the mechanism by which this is implemented is very different from how sub-libraries work. No special LC_*_DYLIB
-command is used; instead, a special indirect character is inserted into the name table (denoted by the N_INDIR
flag), and it behaves like the character defined in this library. If the library itself uses this symbol, the second , “undefined” copy of the symbol will appear in the table of names (as it happens without any re-export).
One important thing to keep in mind when using explicitly re-exporting characters is that you most likely need to re-export characters with different names for different architectures. Indeed, the name resolution convention of [name mangling convention] for Objective-C and the Objective-C binary interface [ABI] for i386 and x86-64 architectures are different, so on i386 you need to .objc_class_name_NSObject
only .objc_class_name_NSObject
, and on x86-64 - _OBJC_CLASS_$_NSObject
, _OBJC_IVAR_$_NSObject.isa
and _OBJC_METACLASS_$_NSObject
. This does not have to be thought of when using sub-libraries, since all the available symbols for each architecture are automatically re-exported there.
Most of the tools for working with Mach-O are transparently understood with thick, or universal, binaries (Mach-O files containing several sub-Mach-O for several architectures). Clang can build universal binaries with all the requested architectures, dyld chooses which architecture to load from dylib, looking at which architectures the executable file supports, and tools such as ld, otool and nm work with the architecture corresponding to the computer architecture (i.e. .x86-64), unless explicitly requiring a different architecture with a special flag. The only thing that still reminds you that several architectures are processed is that when you compile you get errors and warnings twice, once for each architecture.
. ld , , dylib- lipo:
$ ld -o CF_i386.dylib CFFiles.o -arch i386 -lobjc -reexported_symbols_list reexport_i386.exp $ ld -o CF_x86-64.dylib CFFiles.o -arch x86_64 -lobjc -reexported_symbols_list reexport_x86_64.exp $ lipo -arch i386 CF_i386.dylib -arch x86_64 CF_x86-64.dylib -create -output CoreFoundation
Darling CMake- add_separated_framework
, lipo, CMake- Core Foundation :
add_separated_framework(CoreFoundation CURRENT_VERSION SOURCES ${cf_sources} VERSION "A" DEPENDENCIES objc system icucore LINK_FLAGS # ... ) set_property(TARGET CoreFoundation_i386 APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-reexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/reexport_i386.exp") set_property(TARGET CoreFoundation_x86_64 APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-reexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/reexport_x86_64.exp")
- – , , Apple , .
Mach-O- macOS, , -mmacosx-version-min=10.x
( iOS, tvOS, watchOS , Apple ). ; , AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
C++ libstdc++
( GNU) libc++
( LLVM). , Mach-O. , ld -macosx_version_min
( m
), Mach-O- LC_VERSION_MIN_MACOSX
( dyld, , ).
, ld -macosx_version_min
, - Mach-O- . - – , $ld$
, ld, , : , . $ld$$$
. os10.5
, - – , Mach-O- , ; add
, hide
, install_name
compatibility_version
, ld , ,
( ) , .
, , , ; , -,
libobjc
, NSObject
, macOS:
$ld$hide$os10.0$_OBJC_CLASS_$_NSObject $ld$hide$os10.0$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.0$_OBJC_METACLASS_$_NSObject $ld$hide$os10.1$_OBJC_CLASS_$_NSObject $ld$hide$os10.1$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.1$_OBJC_METACLASS_$_NSObject $ld$hide$os10.2$_OBJC_CLASS_$_NSObject $ld$hide$os10.2$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.2$_OBJC_METACLASS_$_NSObject $ld$hide$os10.3$_OBJC_CLASS_$_NSObject $ld$hide$os10.3$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.3$_OBJC_METACLASS_$_NSObject $ld$hide$os10.4$_OBJC_CLASS_$_NSObject $ld$hide$os10.4$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.4$_OBJC_METACLASS_$_NSObject $ld$hide$os10.5$_OBJC_CLASS_$_NSObject $ld$hide$os10.5$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.5$_OBJC_METACLASS_$_NSObject $ld$hide$os10.6$_OBJC_CLASS_$_NSObject $ld$hide$os10.6$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.6$_OBJC_METACLASS_$_NSObject $ld$hide$os10.7$_OBJC_CLASS_$_NSObject $ld$hide$os10.7$_OBJC_IVAR_$_NSObject.isa $ld$hide$os10.7$_OBJC_METACLASS_$_NSObject
, , , , .
dyld – [symbol resolvers], . , , , dyld , .
; foo _foo1: movl 1, %eax ret _foo2: movl 2, %eax ret .symbol_resolver _foo ; - call _condition jz .ret_foo2 movq _foo1, %rax ret .ret_foo2: movq _foo2, %rax ret ; , _foo ; , , ; . .global _foo _foo:
C , , C:
static int foo1() { return 1; } static int foo2() { return 2; } int foo() { // return 0; } static void *foo_resolver() { __asm__(".symbol_resolver _foo"); return condition() ? &foo1 : &foo2; }
( _foo
foo
, Darwin C, . , Mach-O Darling, ELF-, .)
_foo
, ( ), foo()
foo_resolver()
, :
void *foo() { __asm__(".symbol_resolver _foo"); return condition() ? &foo1 : &foo2; }
, – - , foo()
, ( int
-). , , : dlsym("_foo")
_foo
– , , , – . , , , foo()
_foo
.
. Apple libplatform
:
#define _OS_VARIANT_RESOLVER(s, v, ...) \ __attribute__((visibility(OS_STRINGIFY(v)))) extern void* s(void); \ void* s(void) { \ __asm__(".symbol_resolver _" OS_STRINGIFY(s)); \ __VA_ARGS__ \ } #define _OS_VARIANT_UPMP_RESOLVER(s, v) \ _OS_VARIANT_RESOLVER(s, v, \ uint32_t *_c = (void*)(uintptr_t)_COMM_PAGE_CPU_CAPABILITIES; \ if (*_c & kUP) { \ extern void OS_VARIANT(s, up)(void); \ return &OS_VARIANT(s, up); \ } else { \ extern void OS_VARIANT(s, mp)(void); \ return &OS_VARIANT(s, mp); \ })
, – – ( kUP
, commpage ), , , [spinlock]. , .
Darling : Mach-O- ELF- Linux, [host, , Darling] – , libX11
libcairo
.
ELF- – libelfloader
, ELF, , ld-linux, dyld Linux, ld-linux ELF-. libelfloader
Mach-O /usr/lib/darling/libelfloader.dylib
Darwin-chroot-; , Darwin-.
, libelfloader
Mach-O ELF. ( _elfcalls
), libSystem
, Darwin , ELF- Linux. «» Darwin Linux – , C ( libSystem_c
glibc
, ).
ELF- Darwin, - libelfloader
API _elfcalls->dlsym_fatal(_elfcalls->dlopen_fatal("libX11.so"), "XOpenDisplay")
. , wrapgen , ELF- , , The Cocotron – Linux – . wrapgen ELF- (, libX11.so
), :
#include <elfcalls.h> extern struct elf_calls* _elfcalls; static void* lib_handle; __attribute__((constructor)) static void initializer() { lib_handle = _elfcalls->dlopen_fatal("libX11.so"); } __attribute__((destructor)) static void destructor() { _elfcalls->dlclose_fatal(lib_handle); } void* XOpenDisplay() { __asm__(".symbol_resolver _XOpenDisplay"); return _elfcalls->dlsym_fatal(lib_handle, "XOpenDisplay"); }
Mach-O- /usr/lib/native/libX11.dylib
, Mach-O- , libX11.so
, Mach-O. , CMake- wrap_elf
, wrapgen, Mach-O- /usr/lib/native
: wrap_elf(X11 libX11.so)
libX11
, Mach-O-.
Linux . , Darling , Darwin Linux, . Darling – Darwin ( , Darwin); , , , Darwin, libSystem
, dyld, XNU launchd, , , commpage. , libsystem_kernel
, , Linux, «» Darwin- – Linux GNU/Linux . Linux- , Linux ( X server) , [witnessing a magic trick] – libelfloader
, wrapgen, , . , , , , , .
- , Mach-O-, ld . ( , – , Apple, , .)
, , , , [order file], ld :
$ ld -o libfoo.dylib foo.o -order_file foo.order
-reexported_symbols_list
, , -order_file
, :
symbol1 symbol2 # . # # , , # ( C) # . foo.o: _static_function3 # , , # ; # # lipo, # . i386:symbol4
( , , ) «» . , , , , .subsections_via_symbols
, Mach-O- , , , , .
, Apple – libdispatch
. libdispatch
, «OS object», , . Objective-C, libdispatch
( Core Foundation), libdispatch
- Objective-C- , Objective-C-. , dispatch_data_t
NSData *
API Cocoa ( ).
, , Objective-C- OS object vtables . , DISPATCH_OBJECT_TFB
, , Objective-C libdispatch
-, isa
vtable
- dispatch_object
object
:
#define DISPATCH_OBJECT_TFB(f, o, ...) \ if (slowpath((uintptr_t)((o)._os_obj->os_obj_isa) & 1) || \ slowpath((Class)((o)._os_obj->os_obj_isa) < \ (Class)OS_OBJECT_VTABLE(dispatch_object)) || \ slowpath((Class)((o)._os_obj->os_obj_isa) >= \ (Class)OS_OBJECT_VTABLE(object))) { \ return f((o), ##__VA_ARGS__); \ }
( ) – DYLD_INSERT_LIBRARIES
, dyld Mach-O- . , , , DYLD_FORCE_FLAT_NAMESPACE
.
, , - . ( ), dlsym()
RTLD_NEXT
, :
int open(const char* path, int flags, mode_t mode) { printf(" open(%s)\n", path); // " " if (strcmp(path, "foo") == 0) { path = "bar"; } int (*original_open)(const char *, int, mode_t); original_open = dlsym(RTLD_NEXT, "open"); return original_open(path, flags, mode); }
, dyld , dyld- [dyld iterposing]. Mach-O- __interpose
, dyld , – .
, – , __interpose
– [implicit interposing]. , __interpose
( ), dyld . , dyld- , - . , dyld , - , - ( Mach-O-):
static int my_open(const char* path, int flags, mode_t mode) { printf("Called open(%s)\n", path); // " " if (strcmp(path, "foo") == 0) { path = "bar"; } // , // open() my_open(). return open(path, flags, mode); } // __interpose __attribute__ ((section ("__DATA,__interpose"))) static struct { void *replacement, *replacee; } replace_pair = { my_open, open };
, – – Mach-O- - [relocation table]. , dyld ( ), .
, dyld_dynamic_interpose
, :
typedef struct { void *replacement, *replacee; } replacement_tuple; extern const struct mach_header __dso_handle; extern void dyld_dynamic_interpose(const struct mach_header*, const replacement_tuple replacements[], size_t count); void interpose() { replacement_tuple replace_pair = { my_open, open }; dyld_dynamic_interpose(&__dso_handle, &replace_pair, 1); }
, , , .
DYLD_INSERT_LIBRARIES
dyld- Objective-C, C, - , Objective-C- ( IMP
), Objective-C , method swizzling ( isa swizzling ).
Darling xtrace, .
Darwin Darwin ( – BSD Mach- [Mach traps]). libsystem_kernel
, ABI userspace. Darling, libsystem_kernel
Darwin Linux Darling-Mach , Linux, Mach .
strace, Linux, , Linux-; strace Darwin, Darling, Linux, Darwin ( Linux, ELF- ). , Darwin Linux , , Darwin – , – .
, xtrace . strace, , ptrace()
API, xtrace . DYLD_INSERT_LIBRARIES=/usr/lib/darling/libxtrace.dylib
, - [trampoline functions] , . xtrace , strace, , , :
Darling [~]$ xtrace arch <......> [223] mach_timebase_info_trap (...) [223] mach_timebase_info_trap () -> KERN_SUCCESS [223] issetugid (...) [223] issetugid () -> 0 [223] host_self_trap () [223] host_self_trap () -> port right 2563 [223] mach_msg_trap (...) [223] mach_msg_trap () -> KERN_SUCCESS [223] _kernelrpc_mach_port_deallocate_trap (task=2563, name=-6) [223] _kernelrpc_mach_port_deallocate_trap () -> KERN_SUCCESS [223] ioctl (...) [223] ioctl () -> 0 [223] fstat64 (...) [223] fstat64 () -> 0 [223] ioctl (...) [223] ioctl () -> 0 [223] write_nocancel (...) i386 [223] write_nocancel () -> 5 [223] exit (...)
, BSD Mach. , write()
exit()
, Linux-, . , Mach- ioctl
- /dev/mach
, ; BSD- ioctl()
, stdio , stdin stdout ( tty) readlink()
/proc/self/fd/
.
Mach-O, , dyld. :
-bundle_loader
.LC_LOAD_DYLIB
, LC_REEXPORT_DYLIB
LC_DYLIB_ID
, [compatibility version, current version] , – , . ld -current_version
-compatibility_version
, . dyld , , , .LC_SOURCE_VERSION
. ld -source_version
, , Mach-O- – -add_source_version
-no_source_version
.Info.plist
__info_plist
Mach-O- [codesign] , Info.plist
. ad-hoc Security.framework, , CFBundle
/ NSBundle
API, , , ., , , ld dyld , « », libSystem
, -. /usr/lib/
.
Source: https://habr.com/ru/post/417507/