iOS工程10符号的作用

485 阅读5分钟

一、符号

理解符号前,先回顾一下什么是Mach-O :
Mach-O(Mach Object)是macOS、iOS,iPadOS存储程序和库文件格式。对应系统通过应用二进制接口(application binary interface,缩写为ABI)来运行该格式文件。

Mach-O格式用来替代BSD系统的a.out格式。Mach-O文件格式保存了在编译过程和链接过程中产生的机器代码和数据,从而静态链接和动态链接的代码提供了单一文件格式。

附配置文件表:

image.png

1.global&local

查看基础符号:

otool -h /Users/./Products/Debug/MachOAndSymbol //查看解析前的结果
objdump --macho --private-header /Users/./Products/Debug/MachOAndSymbol //查看解析后的结果

image.png

附大小端图: image.png

通过命令:

hexdump /Users/./Products/Debug/MachOAndSymbol

查看大小端在二进制上的地址分布。 image.png 我们看到这是小端的存放地址分布,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
// 代表二级命名空间转一级

这是运行结果: image.png 会打印二进制文件的—函数名称 OTHER_LDFLAGS = -Xlinker -unexported_symbol -Xlinker _global_function 可以_global_function 改为local函数符号

- U symbol_name 表示会被标识为dynamic_lookup 会在动态库里找这个符号

附Mach-o符号表地址

创建一个动态库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 image.png

3.链接动态库NYDYframework image.png

4.编译发现报错 image.png 找到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

运行打印: image.png

如果有两个静态库-相同名称的类-编译会报错。如何才能不报错又保留两个静态库呢?

在链接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'

image.png

2.objcopy

我们这里要用到llvm-objcopy的工具,在终端运行llvm-objcopy --help。

image.png

--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表示所有符号):

image.png

Symbol Table

Symbol Table:就是用来保存符号。

String Table:就是用来保存符号的名称。

Indirect Symbol Table:间接符号表。保存使用的外部符号。更准确一点就是使用外部动态库的符号。是Symbol Table的子集。

按功能分 image.png 按照符号种类划分: ①:⼩写代表local symbol

Symbol Type说明
Uundefined (未定义)
Aabsolute (绝对符号)
T①text section symbol(__TEXT.__text)
D①data section symbol(__DATA.__data)
B①bss section symbol(__DATA.__bss)
Ccommon symbol (只能出现在'MH_OBJECT' 类型的的 Mach-O文件中)
S①除了上面所述的,存放在其他'section'的内容,例如未初始化的全局变量存放在(__DATA,__common中)
Iindirect symbol (符号信息相同,代表同一符号)
u动态共享库中的小写u表示一个未定义引用对同一库中另一个模块中似有外部符号

3.weak

Weak Symbol

  1. Weak Reference Symbol: 表示此未定义符号是弱引用。如果动态连接器找不到该符号的定义,则将其设置为0。链接器会将此符号设置弱链接标志。
  2. 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

运行结果: image.png

4.mach-o

oc是运行时语言,是在编译的时候把所有类的信息及符号,编译在mach-o这个文件中。在运行的时候runtime中在通过二进制符号动态创建出对应的结构体(struct).

可执行文件调用过程:

  1. 调用fork函数,创建一个process
  2. 调用execve或其衍生函数,在该进程上加载,执行我们的mach-o文件。当我们调用时execve(程序加载器),内核实际上在执行以下操作:
    (1):将文件加载到内存
    (2):开始分析Mach-O中的mach_header,以确认它是有效的Mach-O文件

链接的本质就是把多个目标文件组合成一个文件.

可以通过命令查看二进制exec内的符号

objdump --macho -x ..../path/OBJCClass

image.png