Mac下动态库简单分析

1. 简单了解动态库

windows动态库有dll,linux动态库有so,mac下动态库有dylib。dylib本质上也是Mach-O文件。只不过和可执行文件不一样的是:第一,可执行文件的标志是MH_EXECUTE,dylib的标志是MH_DYLIB,在系统的/usr/lib目录下存放着动态库文件。

看一下libz.1.dylib:

bichonfrisedeMacBook-Air:lib bichonfrise$ file /usr/lib/libz.1.dylib 
/usr/lib/libz.1.dylib: Mach-O 64-bit dynamically linked shared library x86_64
这个文件只包含了一种架构就是x86_64位的。同样也可以用otool来查看:
bichonfrisedeMacBook-Air:lib bichonfrise$ otool -L /usr/lib/libz.1.dylib 
/usr/lib/libz.1.dylib:
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.0.0)

2. 构建动态库(Xcode)

  1. 在Xcode中File->New->Project切换到macOS选择Library。

  2. 设置好bundleid和项目路径(Type记得选择Dynamic动态库)。

  3. 在test.h和test.m中写一个简单的测试。

test.h:

#import <Foundation/Foundation.h>

@interface test : NSObject
-(void) test;
@end

test.m:


#import "test.h"

@implementation test

-(void)test{
    NSLog(@"test");
}
@end
  1. 然后build或者Command+B就可以Build出来一个libtest.dylib,可以看到在Product中有了一个libtest.dylib文件了。

  2. 查看动态库编译过程,Terminal进入到项目目录执行xcodebuild

bichonfrisedeMacBook-Air:test bichonfrise$ ll
total 16
 0 drwxr-xr-x  5 bichonfrise  staff   160  4  6 16:48 .
 0 drwxr-xr-x  7 bichonfrise  staff   224  4  6 16:38 ..
16 -rw-r--r--@ 1 bichonfrise  staff  6148  4  6 16:48 .DS_Store
 0 drwxr-xr-x  4 bichonfrise  staff   128  4  6 16:42 test
 0 drwxr-xr-x@ 5 bichonfrise  staff   160  4  6 16:38 test.xcodeproj
bichonfrisedeMacBook-Air:test bichonfrise$ xcodebuild
note: Using new build system
note: Planning build
// 1. 构造编译描述
note: Constructing build description
// 2. 创建编译目录
CreateBuildDirectory /Users/bichonfrise/asm/test/build (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    builtin-create-build-directory /Users/bichonfrise/asm/test/build
// 3. 写入辅助编译文件
WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-own-target-headers.hmap (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-own-target-headers.hmap

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-project-headers.hmap (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-project-headers.hmap

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test.hmap (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test.hmap

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-generated-files.hmap (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-generated-files.hmap

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-all-target-headers.hmap (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-all-target-headers.hmap

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-all-non-framework-target-headers.hmap (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-all-non-framework-target-headers.hmap

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/all-product-headers.yaml (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/all-product-headers.yaml
// 4. 开始编译
CompileC /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.o /Users/bichonfrise/asm/test/test/test.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    export LANG=en_US.US-ASCII
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x objective-c -target x86_64-apple-macos10.15 -fmessage-length=128 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -fcolor-diagnostics -std=gnu11 -fobjc-arc -fobjc-weak -fmodules -gmodules -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fbuild-session-file=/var/folders/8l/1spxxv7n51n8ry_wb7_gcv9h0000gn/C/org.llvm.clang/ModuleCache.noindex/Session.modulevalidation -fmodules-validate-once-per-build-session -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -Wno-trigraphs -fpascal-strings -Os -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Wno-implicit-atomic-properties -Werror=deprecated-objc-isa-usage -Wno-objc-interface-ivars -Werror=objc-root-class -Wno-arc-repeated-use-of-weak -Wimplicit-retain-self -Wduplicate-method-match -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -Wno-selector -Wno-strict-selector-match -Wundeclared-selector -Wdeprecated-implementations -DNS_BLOCK_ASSERTIONS=1 -DOBJC_OLD_DISPATCH_PROTOTYPES=0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -fasm-blocks -fstrict-aliasing -Wprotocol -Wdeprecated-declarations -g -Wno-sign-conversion -Winfinite-recursion -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wno-semicolon-before-method-body -Wunguarded-availability -iquote /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-generated-files.hmap -I/Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-own-target-headers.hmap -I/Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-all-target-headers.hmap -iquote /Users/bichonfrise/asm/test/build/test.build/Release/test.build/test-project-headers.hmap -I/Users/bichonfrise/asm/test/build/Release/include -I/Users/bichonfrise/asm/test/build/test.build/Release/test.build/DerivedSources-normal/x86_64 -I/Users/bichonfrise/asm/test/build/test.build/Release/test.build/DerivedSources/x86_64 -I/Users/bichonfrise/asm/test/build/test.build/Release/test.build/DerivedSources -F/Users/bichonfrise/asm/test/build/Release -MMD -MT dependencies -MF /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.d --serialize-diagnostics /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.dia -c /Users/bichonfrise/asm/test/test/test.m -o /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.o

WriteAuxiliaryFile /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.LinkFileList (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    write-file /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.LinkFileList
// 4. 开始链接
Ld /Users/bichonfrise/asm/test/build/Release/libtest.dylib normal x86_64 (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -target x86_64-apple-macos10.15 -dynamiclib -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -L/Users/bichonfrise/asm/test/build/Release -F/Users/bichonfrise/asm/test/build/Release -filelist /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test.LinkFileList -install_name /usr/local/lib/libtest.dylib -Xlinker -object_path_lto -Xlinker /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test_lto.o -fobjc-arc -fobjc-link-runtime -compatibility_version 1 -current_version 1 -Xlinker -dependency_info -Xlinker /Users/bichonfrise/asm/test/build/test.build/Release/test.build/Objects-normal/x86_64/test_dependency_info.dat -o /Users/bichonfrise/asm/test/build/Release/libtest.dylib
// 4. 生成符号文件
GenerateDSYMFile /Users/bichonfrise/asm/test/build/Release/libtest.dylib.dSYM /Users/bichonfrise/asm/test/build/Release/libtest.dylib (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/bichonfrise/asm/test/build/Release/libtest.dylib -o /Users/bichonfrise/asm/test/build/Release/libtest.dylib.dSYM
// 5. 开始签名
CodeSign /Users/bichonfrise/asm/test/build/Release/libtest.dylib (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    export CODESIGN_ALLOCATE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate
    
Signing Identity:     "Apple Development: hi beauty (girl)"

    /usr/bin/codesign --force --sign D4DA2795149243E12119DCB51CED6540AA0A5095 --timestamp=none /Users/bichonfrise/asm/test/build/Release/libtest.dylib
// 6. 开始执行执行策略异常
RegisterExecutionPolicyException /Users/bichonfrise/asm/test/build/Release/libtest.dylib (in target 'test' from project 'test')
    cd /Users/bichonfrise/asm/test
    builtin-RegisterExecutionPolicyException /Users/bichonfrise/asm/test/build/Release/libtest.dylib
note: Execution policy exception registration failed and was skipped: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" (in target 'test' from project 'test')

** BUILD SUCCEEDED **

这里编译用的是clang其实和linux下的gcc是一样的。本质上这两个东西并不是直接编译的东西,而是一个类似于软件包,再去调用其他的命令执行相应的编译过程,包括(预编译cpp,编译cc/cc1,链接ld这个是gcc的流程),里面步骤中生成符号文件也是给调试器用的。
编译好的动态库可以通过引用头文件加入库来调用也可以通过dlopen(),dlsym()来调用。

6. Mach-O可执行文件如何使用dylib动态库?

在Mach-O文件中有一个LC_LOAD_DYLINKER的segment,来执行让谁来加载动态库,这里一般都是/usr/lib/dyld。同样这里为了加载更快速一些采用了共享缓存技术(share cachr),在进程启动的时候去查看对应的加载的动态库是否已经在缓存中了,如果在缓存中了那就直接将缓存中的动态库映射到进程的地址空间中。进而实现对动态库的调用(这里也涉及到已加载动态库的更新),这里不过多赘述,可以查看dyld的加载流程。