JHHK

欢迎来到我的个人网站
行者常至 为者常成

动态库

目录

0401 dead strip

echo "-----开始test.o to test EXEC"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -all_load \
-Xlinker -dead_strip \
-Xlinker -why_live -Xlinker _global_function \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}


# -dead_strip 官方解释:删除入口点或导出符号无法访问的函数和数据。
Remove functions and data that are unreachable by the entry point or exported symbols.



/**
 情形一:
 -dead_strip 关闭
 global_function:有
 static_function:无

 -dead_strip 打开
 global_function:无
 static_function:无
 */
// 本地
static void static_function() {
    NSLog(@"staticFunction");
}
// 全局
void global_function() {
    NSLog(@"globalFunction");
}
// 入口点
int main(){
    NSLog(@"testApp----");
    return 0;
}



/**
 情形二
 -dead_strip 关闭
 static_function:有
 global_function:有

 
 -dead_strip 打开
 static_function:无
 global_function:无
 */
// 本地
static void static_function() {
    NSLog(@"staticFunction");
}
// 全局
void global_function() {
    NSLog(@"globalFunction");
    static_function();
}
// 入口点
int main(){
    NSLog(@"testApp----");
    return 0;
}




/**
 情形三
 -dead_strip 关闭
 static_function:有
 global_function:有
 global_function2:有

 
 -dead_strip 打开
 static_function:无
 global_function:无
 global_function2:无
 */
// 本地
static void static_function() {
    NSLog(@"staticFunction");
}
// 全局
void global_function() {
    NSLog(@"globalFunction");
    static_function();
}
// 全局2
void global_function2() {
    NSLog(@"globalFunction2");
    global_function();
}

// 入口点
int main(){
    NSLog(@"testApp----");
    return 0;
}


lldb 执行 test可执行文件

└> lldb -file test
(lldb) target create "test"
Current executable set to '/Users/xxx13/LCFiles/2Content/3Learning/动态库/上课代码/dead strip/test' (x86_64).
(lldb) r

OC没有被干掉,因为动态性

0402 动态库原理

一 动态库介绍

静态库是.o的合集

动态库是最终链接的产物

动态库比静态库多一步链接的过程,所以动态库不能合并

二 链接afn动态库

链接动态库afn成功,执行时报错 image not found

1 编译test.m to test.o

echo "-------------编译test.m to test.o------------------"                                             
clang -x objective-c  \
-target x86_64-apple-macos11.1     \
-fobjc-arc          \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk  \
-I ./AFNetworking   \ 
-c test.m -o test.o

2 test.o 链接动态库 afn

echo "-------------test.o 链接动态库 afn------------------"                                             
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \    
-L./AFNetworking \
-lAFNetworking \
test.o -o test

3 image not found 错误

lldb -file test
(lldb) target create "test"
Current executable set to '/Users/xxx/test' (x86_64).
(lldb) r
Process 12419 launched: '/Users/xxx/test' (x86_64)
dyld: Library not loaded: @rpath/AFNetworking.framework/Versions/A/AFNetworking
  Referenced from: /Users/xxx/test
  Reason: image not found
Process 12419 stopped
* thread #1, stop reason = signal SIGABRT
    frame #0: 0x000000010006caea dyld`__abort_with_payload + 10
dyld`__abort_with_payload:
->  0x10006caea <+10>: jae    0x10006caf4               ; <+20>
    0x10006caec <+12>: movq   %rax, %rdi
    0x10006caef <+15>: jmp    0x10006acf0               ; cerror_nocancel
    0x10006caf4 <+20>: retq   
Target 0: (test) stopped.

0403 tbd与符号

tbd是对动态库的描述文件,告诉可执行文件去手机上哪里找动态库

tbd文件内容,只有符号没有实现

动态库在编译链接阶段只知道符号就可以

动态库是在运行时通过dyld动态加载,找到符号真实地址

-l -L -F -Framework本质是告诉导出符号的位置

静态库在链接时代码符号表都合并到一起了不需要这步操作

我们看一个SYCSSColor.tbd文件的内容

--- !tapi-tbd
tbd-version:     4
targets:         [ x86_64-ios-simulator ]
uuids:
  - target:          x86_64-ios-simulator
    value:           D4120855-94BD-324C-ACAE-10B7EEB4A991
flags:           [ not_app_extension_safe ]
install-name:    '@rpath/SYCSSColor.framework/SYCSSColor'
exports:
  - targets:         [ x86_64-ios-simulator ]
    symbols:         [ _SYIsASCIIAlphaCaselessEqual, _SYIsASCIIDigit, _SYIsASCIIHexDigit, 
                       _SYIsHTMLSpace, _SYToASCIIHexValue, _SYToASCIILowerUnchecked, 
                       __ZN2SY9findColorEPKcj, _displayP3ColorSpaceRef, _extendedSRGBColorSpaceRef, 
                       _linearRGBColorSpaceRef, _sRGBColorSpaceRef ]
    objc-classes:    [ SYColor, SYExtendedColor ]
...

0404 编译动态库framework

一 TBD

TBD文件生成 xcode搜索text

二 链接我们自己创建的动态库

目录结构如下

Frameworks
    MYLog.framework
        Headers
            MYLog.h
        MYLog.m

test.m

test.m内容如下

#import <Foundation/Foundation.h>
#import"MYLog.h"
int main(int argc, char * argv[]) {
    NSLog(@"testApp");
    
    MYLog * mlog = [MYLog new];
    [mlog myLogTest];
}

MYLog.h内容如下

#import <Foundation/Foundation.h>

@interface MYLog : NSObject
-(void)myLogTest;
@end

MYLog.m内容如下

#import "MYLog.h"

@implementation MYLog
-(void)myLogTest {
    NSLog(@"myLogTest");
}
@end

1 MYLog.m to MYLog.o

echo "-------------MYLog.m to MYLog.o------------------"                                             
clang -x objective-c  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./Headers \
-c MYLog.m -o MYLog.o

2 生成动态库

echo "MYLog.o --- libMYLog.dylib"

#1 首先变为静态库
libtool -static -arch_only x86_64 MYLog.o -o libMYLog.a

#2 -dynamiclib: 动态库 , dylib 最终链接产物 -》
ld -dylib -arch x86_64 \
-macosx_version_min 11.1 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-lsystem -framework Foundation \
#在第4步链接动态库变为可执行文件时报符号未定义,是因为静态库链接时未使用的符号会去掉 加上参数 -all_load
-all_load \
libMYLog.a -o libMYLog.dylib

或者用这种方式生成动态库

clang -dynamiclib  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
MYLog.o -o libMYLog.dylib

3 test.m to test.o

echo "-------------test.m to test.o------------------"                                             
clang -x objective-c  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./Frameworks/Headers \
-c test.m -o test.o

4 链接动态库

echo "链接libMYLog.dylib -- test EXEC"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-L./Frameworks/MYLog.framework \
-lMYLog \
test.o -o test

在第2步未添加-all_load时的报错

链接libMYLog.dylib -- test EXEC
Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_MYLog", referenced from:
      objc-class-ref in test.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我的分析:

编译阶段
词法分析,语法分析,汇编,二进制
编译current.m文件时,要把文件内的内容最终变为二进制.
外部的out.m文件内的符号,在current.m文件内它不知道是什么,那么变为二进制的过程中怎么处理呢?
头文件的作用是声明,这时就需要out.h头文件告诉current.m这些内容是符号,并且这些符号会被放入重定位符号表
所以在编译阶段要配置头文件路径的作用就是:声明这些外部的内容是符号.

链接 
链接的过程就是处理符号的过程
告诉符号在什么位置,去哪里找到符号并调用.
所以在链接时需要配置动态库路径的作用是:动态库中有对导出符号和路径的描述,告诉符号去哪里找.

上边报错分析
在链接动态库的时候找不到符号,就说明动态库内没有对_OBJC_CLASS_$_MYLog该导出符号的描述.
问题就出在动态库制作的过程中,该动态库是由静态库链接而成的.
而链接静态库时没有用到的类会被清除,所以要配置-all_load

5 执行时报错

./test 
dyld: Library not loaded: libMYLog.dylib
  Referenced from: /Users/xxx/./test
  Reason: image not found
[1]    49297 abort      ./test

三 生成framework

我们看到好多标准的framework动态库内的动态库并没有lib前缀和.dylib后缀

在上边我们演示了生成一个libMYLog.dylib,我们现在演示如何生产和链接一个标准的framework

生成动态库时可以直接去掉lib和.dylib

clang -dynamiclib  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
MYLog.o -o MYLog

链接一个framework

echo "链接MYLog.framework -- test EXEC"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-F./Frameworks \
-framework MYLog \
test.o -o test

0405 image not found的原因

1 dyld 加载macho文件,macho文件内LC_LOAD_DYLIB内存放了使用的动态库路径,运行时通过该路径找到使用的库

2 查看macho使用了哪些动态库,及动态库信息

#使用 -L print shared libraries used
└> otool -L test
test:
	libMYLog.dylib (compatibility version 0.0.0, current version 0.0.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1775.118.101)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.100.5)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1775.118.101)
#使用 -l print the load commands

└> otool -l test | grep "LC_LOAD_DYLIB" -A 5
          cmd LC_LOAD_DYLIB
      cmdsize 40
         name libMYLog.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 0.0.0
compatibility version 0.0.0
--
          cmd LC_LOAD_DYLIB
      cmdsize 96
         name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1775.118.101
compatibility version 300.0.0
--
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libobjc.A.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 228.0.0
compatibility version 1.0.0
--
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libSystem.B.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1292.100.5
compatibility version 1.0.0
--
          cmd LC_LOAD_DYLIB
      cmdsize 104
         name /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1775.118.101
compatibility version 150.0.0

3 明白为什么会报image not found 错误

#在可执行文件内我们自己的动态库信息
name libMYLog.dylib (offset 24)

#在可执行文件内系统库的信息
name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24)

所以报错:image not found 是因为我们的动态库在可执行文件内存储的路径不对

0406 install_name

一 查看文件类型

#目标文件
└> file test.o 
test.o: Mach-O 64-bit object x86_64

#可执行文件
└> file test
test: Mach-O 64-bit executable x86_64

#静态库
└> file libMYLog.a 
libMYLog.a: current ar archive random library

#动态库
└> file libMYLog.dylib 
libMYLog.dylib: Mach-O 64-bit dynamically linked shared library x86_64

二 otool查看动态库内存储的路径

otool -l libMYLog.dylib | grep "LC_ID_DYLIB" -A 5 -B 1

Load command 4
          cmd LC_ID_DYLIB
      cmdsize 40
         name libMYLog.dylib (offset 24)
   time stamp 1 Thu Jan  1 08:00:01 1970
      current version 0.0.0
compatibility version 0.0.0

三 修改 name

动态库将自己的加载路径存储到自身当中

当链接动态库形成可执行文件的时候会将这个路径拷贝到可执行文件内

install_name_tool 修改 name 存储的路径值

-id name
        Changes the shared library identification name of a dynamic shared library to name.  If the Mach-O binary  is  not  a  dynamic  shared
        library and the -id option is specified it is ignored.
└> install_name_tool -id /Users/xxx13/LCFiles/2Content/3Learning/test/Frameworks/libMYLog.dylib 

└> otool -l libMYLog.dylib|grep "LC_ID" -A 5 -B 1                                                                  
Load command 4
          cmd LC_ID_DYLIB
      cmdsize 104
         name /Users/xxx13/LCFiles/2Content/3Learning/test/Frameworks/libMYLog.dylib (offset 24)
   time stamp 1631174343 Thu Sep  9 15:59:03 2021
      current version 0.0.0
compatibility version 0.0.0

或者 使用ld 的 -install_name 在链接成动态库的时候直接设置路径

ld -dylib -arch x86_64 \
-macosx_version_min 11.1 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-lsystem -framework Foundation \
-ObjC \
-install_name  /Users/xxx13/LCFiles/2Content/3Learning/test/Frameworks/libMYLog.dylib \
libMYLog.a -o libMYLog.dylib

四 重新链接动态库生成可执行文件

└> echo "链接libMYLog.dylib -- test EXEC"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-L./Frameworks \
-lMYLog \
test.o -o test
链接libMYLog.dylib -- test EXEC

└> ls
Frameworks test       test.m     test.o

└> ./test 
2021-09-09 16:04:27.490 test[55821:1487861] testApp
2021-09-09 16:04:27.491 test[55821:1487861] myLogTest

0407 @rpath & @executable_path

一 什么是@rpath及其作用

我们看到name存储的路径是一个绝对路径,当我们把动态库放到别人的电脑的时候就又找不到了

name /Users/xxx13/LCFiles/2Content/3Learning/test/Frameworks/libMYLog.dylib (offset 24)

@rpath就是解决这个问题的,动态库只提供一部分路径,前边的@rpatH由链接方提供

└> otool -l libAFNetworking.dylib | grep "LC_ID" -A 5 -B 1
Load command 4
          cmd LC_ID_DYLIB
      cmdsize 80
         name @rpath/AFNetworking.framework/Versions/A/AFNetworking (offset 24)
   time stamp 1 Thu Jan  1 08:00:01 1970
      current version 1.0.0
compatibility version 1.0.0

二 @execute_path

@rpath配合@execute_path使用,就能最终确定动态库的路径

@execute_path 的意思是可执行文件所在的路径

1 修改动态库内存储的路径,使用@rpath

└> install_name_tool -id @rpath/libMYLog.dylib libMYLog.dylib 

└> otool -l libMYLog.dylib | grep "LC_ID_DYLIB" -A 5 -B 1
Load command 4
          cmd LC_ID_DYLIB
      cmdsize 64
         name @rpath/libMYLog.dylib (offset 24)
   time stamp 1631179753 Thu Sep  9 17:29:13 2021
      current version 0.0.0
compatibility version 0.0.0

2 重新链接动态库后查看可执行文件内LC_LOAD_PATH

└> otool -l test | grep "LC_LOAD_DYLIB" -A 5 -B 1
Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 48
         name @rpath/libMYLog.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 0.0.0
compatibility version 0.0.0

3 在可执行文件内添加@rpath

└> install_name_tool -add_rpath @executable_path/Frameworks test

└> otool -l test | grep "LC_RPATH" -A 5 -B 1
Load command 20
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/Frameworks (offset 12)

4 执行可执行文件

└> ./test
2021-09-09 17:51:35.645 test[60654:1620991] testApp
2021-09-09 17:51:35.645 test[60654:1620991] myLogTest

0408 loader_path

一 动态库内使用到了动态库的情形

test.o -> MYLog.framework -> YULog.framework

如果是这样的情况,使用@execute_path还是很麻烦,因为它代表的是可执行文件的路径

对于MYLog.framework的LC_RPATH的设置还是很繁琐,需要向下面这样

cmd LC_RPATH
path @executable_path/Frameworks/MYLog.framework/Frameworks

二 loader_path

@loader_path 代表谁链接就是谁的路径,像上边的例子就可以写成

cmd LC_RPATH
path @loader_path/Frameworks

三 整个链接过程实现

YULog.framework

echo "-------------YULog.m to YULog.o------------------" 
clang -x objective-c  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./Headers  \
-c YULog.m -o YULog.o


echo "-------------YULog.o to YULog------------------" 
clang -dynamiclib  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
YULog.o -o YULog


echo "-------------YULog 修改install_name------------------" 
install_name_tool -id @rpath/YULog.framework/YULog YULog
otool -l YULog | grep "LC_ID_DYLIB" -A 5 -B 1

MYLog.framework

echo "-------------MYLog.m to MYLog.o------------------" 
clang -x objective-c  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./Headers  \
-I./Frameworks/YULog.framework/Headers  \
-c MYLog.m -o MYLog.o


echo "-------------MYLog.o to MYLog------------------"  
clang -dynamiclib  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk  \
-F./Frameworks  \
-framework YULog  \
MYLog.o -o MYLog


echo "-------------MYLog 修改install_name------------------" 
install_name_tool -id @rpath/MYLog.framework/MYLog MYLog
otool -l MYLog | grep "LC_ID_DYLIB" -A 5 -B 1


echo "-------------MYLog 添加rpath------------------" 
install_name_tool -add_rpath @loader_path/Frameworks MYLog
otool -l MYLog | grep "LC_RPATH" -A 5 -B 1 

test可执行文件

echo "-------------test.m to test.o------------------" 
clang -x objective-c  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./Frameworks/MYLog.framework/Headers  \
-c test.m -o test.o


echo "-------------test.o to test------------------" 
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-F./Frameworks \
-framework MYLog \
test.o -o test


echo "-------------Test 添加rpath------------------" 
install_name_tool -add_rpath @loader_path/Frameworks test
otool -l test | grep "LC_RPATH" -A 5 -B 1 

0409 reexport_framework

一 修改可执行文件

可以修改可执行文件内寻找动态库的路径,指定到自己的库

修改后要强制签名

二 xcode内的rpath

xcode设置内搜索 install name 或者 rpath

三 重新导出符号

库文件本身就是一个大的符号,可执行文件能否直接调用动态库内链接的动态库?

重新导出符号

会增加一个loade 存储name LC_REEXPORT_DYLIB

重新导出符号

echo "-------------MYLog.o to MYLog------------------"  
clang -dynamiclib  \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk  \
-Xlinker -reexport_framework   -Xlinker YULog  \
-F./Frameworks  \
-framework YULog  \
-Xlinker -install_name  -Xlinker @rpath/MYLog.framework/MYLog \
-Xlinker -rpath  -Xlinker @loader_path/Frameworks \
MYLog.o -o MYLog

查看LC_REEXPORT_DYLIB

otool -l MYLog | grep "LC_REEXPORT_DYLIB" -A 5 -B 1
Load command 11
          cmd LC_REEXPORT_DYLIB
      cmdsize 56
         name @rpath/YULog.framework/YULog (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 0.0.0
compatibility version 0.0.0

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.