一、符号
理解符号前,先回顾一下什么是Mach-O :
Mach-O(Mach Object)是macOS、iOS,iPadOS存储程序和库文件格式。对应系统通过应用二进制接口(application binary interface,缩写为ABI)来运行该格式文件。
Mach-O格式用来替代BSD系统的a.out格式。Mach-O文件格式保存了在编译过程和链接过程中产生的机器代码和数据,从而静态链接和动态链接的代码提供了单一文件格式。
附配置文件表:
1.global&local
查看基础符号:
otool -h /Users/./Products/Debug/MachOAndSymbol //查看解析前的结果
objdump --macho --private-header /Users/./Products/Debug/MachOAndSymbol //查看解析后的结果
附大小端图:
通过命令:
hexdump /Users/./Products/Debug/MachOAndSymbol
查看大小端在二进制上的地址分布。
我们看到这是小端的存放地址分布,
cf fa ed fe -> 0xfeedfacf 大端正好相反 :cffaedfe -> 0xcffaedfe
- global:代表函数全局可见性(未static标识),
exec 符号冲突不能在定义global,但是可以定义同名local - local:代表函数只在当前文件可用(被static标识),只要不在当前文件,随便定义同名local函数
打开终端运行 printenv >/dev/ttys000
然后在:.xcconfig 配置
MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/*
// objdump -t
CMD = objdump --macho -t ${MACH_PATH}
TTY=/dev/ttys000
OTHER_LDFLAGS = -Xlinker -S -Xlinker -exported_symbol -Xlinker _localFunc
// -Xlinker -U -Xlinker _staticFunc -Xlinker -exported_symbol -Xlinker _staticFunc
// -Xlinker -U -Xlinker _NSLog
// -Wl,-flat_namespace -Xlinker -undefined -Xlinker suppress
// 代表二级命名空间转一级
这是运行结果:
会打印二进制文件的—函数名称
OTHER_LDFLAGS = -Xlinker -unexported_symbol -Xlinker _global_function 可以_global_function 改为local函数符号
- U symbol_name 表示会被标识为dynamic_lookup 会在动态库里找这个符号
创建一个动态库NYDYframework:
1.创建NYDYFunc
//NYDYFunc.h
#import <Foundation/Foundation.h>
extern void NYDYFunc();
//NYDYFunc.m
#import "NYDYFunc.h"
void NYDYFunc(){
NSLog(@"NYDYFunc");
}
2.创建Muti.xcworkspace工程->添加NYDYframework和MachOAndSymbol
3.链接动态库NYDYframework
4.编译发现报错
找到NYDYframework 库地址导入项目中。
在mian函数中加入代码:
void NYDYFunc();
int main(int argc, char *argv[]) {
NYDYFunc();
return 0;
}
5.需要反相依赖
void global_func();
// global_func 相互依赖 --》 App --〉 dy --> App
void NYDYFunc() {
global_func();
NSLog(@"NYDYFunc");
}
创建Config.xcconfig 配置clang
OTHER_LDFLAGS = -Xlinker -U -Xlinker _global_func
运行打印:
如果有两个静态库-相同名称的类-编译会报错。如何才能不报错又保留两个静态库呢?
在链接link->本地->删除对应符号.这里用到了【重定位符号表】 --> exec{符号的具体信息}
直接上代码:
// 动态库 --》global --〉local -->strip
//duplicate symbol '_OBJC_CLASS_$_NXXXXX' in:
// /user/Muti-henigonvvtufsjaqaejgwtxkizfd/Build/Intermediates.noindex/MachOAndSymbol.build/Debug/MachOAndSymbol.build/Objects-normal/x86_64/Cat.o
// /Users/Xcode/DerivedData/Muti-henigonvvtufsjaqaejgwtxkizfd/Build/Intermediates.noindex/MachOAndSymbol.build/Debug/MachOAndSymbol.build/Objects-normal/x86_64/Dog.o
//duplicate symbol '_OBJC_METACLASS_$_Cat'
2.objcopy
我们这里要用到llvm-objcopy的工具,在终端运行llvm-objcopy --help。
--strip-symbol=symbol Strip <symbol>
--strip-symbols=filename
Reads a list of symbols from <filename> and removes th
移除指定文件的符号
利用llvm-objcopy命令行工具可以通过如下命令来解决上面两个库同类名冲突问题。
llvm-objcopy = strip
--redefine-sym=old=new Change the name of a symbol old to new 老符号
--redefine-syms=filename 新符号
autolink --> 在链接过程有可能会不生效,在xcode链接过程中会默认引入autolink
duplicate symbol '_OBJC_CLASS_$_LGCat' in:
duplicate symbol '_OBJC_METACLASS_$_LGCat' in:
还可以通过 nm-pa 查看符号表(a表示所有符号):
Symbol Table
Symbol Table:就是用来保存符号。
String Table:就是用来保存符号的名称。
Indirect Symbol Table:间接符号表。保存使用的外部符号。更准确一点就是使用外部动态库的符号。是Symbol Table的子集。
按功能分
按照符号种类划分: ①:⼩写代表local symbol
| Symbol Type | 说明 |
|---|---|
| U | undefined (未定义) |
| A | absolute (绝对符号) |
| T① | text section symbol(__TEXT.__text) |
| D① | data section symbol(__DATA.__data) |
| B① | bss section symbol(__DATA.__bss) |
| C | common symbol (只能出现在'MH_OBJECT' 类型的的 Mach-O文件中) |
| S① | 除了上面所述的,存放在其他'section'的内容,例如未初始化的全局变量存放在(__DATA,__common中) |
| I | indirect symbol (符号信息相同,代表同一符号) |
| u | 动态共享库中的小写u表示一个未定义引用对同一库中另一个模块中似有外部符号 |
3.weak
Weak Symbol
Weak Reference Symbol: 表示此未定义符号是弱引用。如果动态连接器找不到该符号的定义,则将其设置为0。链接器会将此符号设置弱链接标志。Weak defintion Symbol: 表示此符号为弱定义符号。如果静态链接器或动态链接器为此符号找到另一个(非弱)定义,则弱定义将被忽略。只能将合并部分中的符号标记为弱定义。
通过一个例子了解WeakSymbol:
//WeakSymbol.h
// weak define
void weak_function(void) __attribute__((weak));
void weak_hidden_function(void) __attribute__((weak, visibility("hidden")));
//WeakSymbol.m
void weak_function(void) {
NSLog(@"weak_function");
}
void weak_hidden_function(void) {
NSLog(@"weak_hidden_function");
}
//WeakImportSymbol
// macho -->不能直接使用
void weak_import_function(void) __attribute__((weak_import));
void weak_import_function(void) {
NSLog(@"weak_import_function");
}
//main
void weak_function(void) {
printf("main\n");
}
int main(int argc, char *argv[]) {
weak_function();
if (weak_import_function) {
weak_import_function();
}
return 0;
}
这里weak_import_function不能直接使用需要通过 xcode命令声明
OTHER_LDFLAGS=$(inherited) -Xlinker -S -Xlinker -U -Xlinker _weak_import_function
运行结果:
4.mach-o
oc是运行时语言,是在编译的时候把所有类的信息及符号,编译在mach-o这个文件中。在运行的时候runtime中在通过二进制符号动态创建出对应的结构体(struct).
可执行文件调用过程:
- 调用
fork函数,创建一个process - 调用
execve或其衍生函数,在该进程上加载,执行我们的mach-o文件。当我们调用时execve(程序加载器),内核实际上在执行以下操作:
(1):将文件加载到内存
(2):开始分析Mach-O中的mach_header,以确认它是有效的Mach-O文件
链接的本质就是把多个目标文件组合成一个文件.
可以通过命令查看二进制exec内的符号
objdump --macho -x ..../path/OBJCClass