目录
- 在xcconfig文件内配置
- 符号的可见性
- 0203 链接与符号表
- 0204 全局和本地符号
- 0205 同一文件相同全局符号
- 0206 导入导出符号
- 0207 weak符号表
- 0208 Swift符号
- 0209 strip
- 0210 llvm-strip
在xcconfig文件内配置
.xcconfig文件
//1.
//解决xcconfig文件的冲突
#include "../../../Pods/Target Support Files/Pods-LCClientDemo/Pods-LCClientDemo.debug.xcconfig"
//2.
// -p : 不排序
// -a : 显示所有符号,包括调试符号
MACHO_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/LCClientDemo.app/LCClientDemo
CMD = nm
CMD_FLAG = -pa ${MACHO_PATH}
TTY = /dev/ttys002
//3.
//$(inherited) 继承之前的设定,解决冲突
//Xlinker 传给连接器的参数
//查看连接器参数:man ld 然后输入 / -S
//-S Do not put debug information (STABS or DWARF) in the output file.
OTHER_LDFLAGS = $(inherited) -Xlinker -S
//4.
//设置条件
OTHER_LDFLAGS[config=Debug][sdk=iphonesimulator*][arch=x86_64] = $(inherited) -framework "Cat"
//5.
//解决 // 问题
//URL_TEST = http://127.0.0.1
FLASH = /
URL_TEST = http:${FLASH}/127.0.0.1
Build Phases -> Run Script
#./appSign.sh
echo "LC_Catxxx" > /dev/ttys005
echo "${LXY_TEST}" > /dev/ttys005
echo "$SRCROOT" > /dev/ttys005
echo "${MACHO_PATH}" > /dev/ttys005
/bin/sh "$SRCROOT/LCClientDemo/Script/xcode_run_cmd.sh"
xcode_run_cmd.sh
#!/bin/sh
RunCommand() {
#判断全局字符串VERBOSE_SCRIPT_LOGGING是否为空。-n string判断字符串是否非空
#[[是 bash 程序语言的关键字。用于判断
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
#作为一个字符串输出所有参数。使用时加引号"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数
if [[ -n "$TTY" ]]; then
echo "♦ $@" 1>$TTY
else
echo "♦ $*"
fi
echo "------------------------------------------------------------------------------" 1>$TTY
fi
#与$*相同。但是使用时加引号,并在引号中返回每个参数。"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数
if [[ -n "$TTY" ]]; then
echo `$@ &>$TTY`
else
"$@"
fi
#显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
return $?
}
EchoError() {
#在shell脚本中,默认情况下,总是有三个文件处于打开状态,标准输入(键盘输入)、标准输出(输出到屏幕)、标准错误(也是输出到屏幕),它们分别对应的文件描述符是0,1,2
# > 默认为标准输出重定向,与 1> 相同
# 2>&1 意思是把 标准错误输出 重定向到 标准输出.
# &>file 意思是把标准输出 和 标准错误输出 都重定向到文件file中
# 1>&2 将标准输出重定向到标准错误输出。实际上就是打印所有参数已标准错误格式
if [[ -n "$TTY" ]]; then
echo "$@" 1>&2>$TTY
else
echo "$@" 1>&2
fi
}
RunCMDToTTY() {
if [[ ! -e "$TTY" ]]; then
EchoError "=========================================="
EchoError "ERROR: Not Config tty to output."
exit -1
fi
if [[ -n "$CMD" ]]; then
RunCommand "$CMD" ${CMD_FLAG}
else
EchoError "=========================================="
EchoError "ERROR:Failed to run CMD. THE CMD must not null"
fi
}
RunCMDToTTY
符号的可见性
符号可见性
-O1 -Oz 生成目标文件时 在编译阶段的处理
dead code strip 死代码剥离 在链接阶段处理
strip 剥离符号 对mach-O修改 (deployment postprocessing 设置为yes)
xcode设置strip
1 找到下面的设置组
Build Settings -> Deployment
2 找到下面的设置项,设置为YES
Deployment PostProcessing 设置为 YES
3 找到下面的设置项进行配置
Strip style 的类型选择:
All Symbols
Non-Global Symbols
Debugging Symbols
0203 链接与符号表
一. GCC四步详解
源码 到 可执行文件 的过程
预处理 -> 编译 -> 汇编 -> 链接
第一步:预处理(也叫预编译)
gcc -E hello.c -o hello.i
或者 cpp hello.c > hello.i 【cpp是预编译器】
将所有#define删除,并且展开所有的宏定义
处理所有的条件预编译指令,如#if #ifdef #undef #ifndef #endif #elif
处理#include,将包含的文件插入到此处,这是一个递归的过程
删除所有注释 // /* */
添加行号和文件名标识,以便于编译时产生的错误警告能显示行号
保留#pragma编译器指令
第二步:编译
gcc -S hello.i -o hello.s
将预处理完的.i文件进行一系列的词法分析、语法分析、语义分析及优
化后生成响应的汇编代码文件,这是整个程序构建的最核心的部分,也是最复杂的部分
第三步:汇编
gcc -c hello.s -o hello.o或者 as hello.s -o hello.o
汇编是将第二步生成的汇编代码编程机器可执行的指令,每一个汇编语句几乎都对应一条机器指令
第四步:链接
链接动态库和静态库
二. 目标文件
目标文件就是源代码经过编译后但未进行链接的那些中间文件
.o目标文件的地址并未虚拟化
.o文件内用到的其它库文件(macho)内的API比如NSLog,会将符号放入重定位符号表
.o文件内用到的其它.o文件内的API,同样会将符号放入重定位符号表
重定位符号表 内放置的是当前 .m/.o 用到的API(.o需要链接,才能变为可执行文件)
查看重定位符号表
通过查看重定位符号表,就可以知道当前.o文件调用了哪些api.
objdump --macho -reloc xx.o文件
多个.o文件 -> 链接 -> 可执行文件
重定位符号表 合并到 一张表中
总结:链接的过程就是处理.o文件中符号的过程
0204 全局和本地符号
一 全局符号与本地符号
全局变量在符号表里全都是全局符号 g:代表全局符号 l:代表本地符号
查看可执行文件的符号表
objdump --macho -syms MachOAndSymbol2
MachOAndSymbol2:
SYMBOL TABLE:
0000000100008008 l O __DATA,__data __dyld_private
0000000100008010 l O __DATA,__data _global_init_value
0000000100008014 l O __DATA,__data _static_init_value
0000000100008018 l O __DATA,__bss _static_uninit_value
0000000100008028 l O __DATA,__common _default_x
0000000100000000 g F __TEXT,__text __mh_execute_header
0000000100008020 g O __DATA,__common _global_uninit_value
0000000100003f50 g F __TEXT,__text _main
0000000000000000 *UND* _NSLog
0000000000000000 *UND* ___CFConstantStringClassReference
0000000000000000 *UND* dyld_stub_binder
二 全局符号变为本地符号
1 全局符号
全局符号:对当前项目可见对其它项目也可见(对内对外都可见).
比如库A内有一个方法,但并未在头文件内暴露
void global_object () {
NSLog(@"global_object");
}
在app内引入库A,只声明一个void global_object ()方法并不写实现.
当调用global_object ()时,就会调用到库A内的方法.
2 全局符号变为本地符号
#方式一:使用属性修饰
double default_x __attribute__((visibility("hidden"))) ;
#方式二:static修饰
static int static_init_value = 9;
3 static只对定义它的文件可见,是本地符号
static void global_object () {
NSLog(@"global_object");
}
4 two_levelnamespace & flat_namespace:
二级命名空间与一级命名空间。
链接器默认采用二级命名空间,也就是除了会记录符号 名称,还会记录符号属于哪个Mach-O的,
比如会记录下来_NSLog来自Foundation。
0205 同一文件相同全局符号
同一个mach-o文件内有相同的全局符号,会报错.
0206 导入导出符号
一 查看导出符号
我们的APP使用Foundation框架下的NSLog
NSLog对我们的APP来说导入了NSLog
NSLog对Foundation框架来说导出了NSLog
全局符号默认是导出符号,但我们可以通过链接器控制它
查看可执行文件的导出符号
objdump --macho --exports-trie MachOAndSymbol3
MachOAndSymbol3:
Exports trie:
0x100000000 __mh_execute_header
0x100003F50 _main
0x100008014 _lxy_global_value
0x100008020 _global_uninit_value
二 间接符号表
动态库 -> 对外提供符号
间接符号表 -> 存储动态库符号
动态库全局符号 -> 变为导出符号 -> 使用动态库的APP的间接符号表
所以动态库的全局符号不可以通过 strip 脱掉
查看间接符号表
objdump --macho --indirect-symbols MachOAndSymbol3
MachOAndSymbol3:
Indirect symbols for (__TEXT,__stubs) 1 entries
address index name
0x0000000100003f90 9 _NSLog
Indirect symbols for (__DATA_CONST,__got) 1 entries
address index name
0x0000000100004000 11 dyld_stub_binder
Indirect symbols for (__DATA,__la_symbol_ptr) 1 entries
address index name
0x0000000100008000 9 _NSLog
三. 将导出符号变为非导出符号
OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS_$_LGOneObject
因为OC的动态性,OC符号不能使用 hidden
将全局符号变为了本地符号,这个时候就可以脱去该符号,减少macho文件的体积
weak符号表
一 -map参数的使用
-map map_file_path
Writes a map file to the specified path which details all symbols and their addresses in the output image.
二 weak symbol
1 弱引用符号
__attribute__((weak_import))
OTHER_LDFLAGS=$(inherited) -Xlinker -U -Xlinker _weak_import_function
//API
//弱引用:找不到符号就返回0
//没有找到符号不调用,找到符号就调用
if (weak_import_function) {
weak_function();
}
//可以把整个动态库声明为弱引用,当找不到这个库时就不会报错了.
在终端
man ld
然后查找 -S 或者 -U 代表的具体含义
2 弱定义符号
__attribute__((weak))
找到一个符号就会忽略其它的符号
对于一个全局符号不会影响符号是全局符号的特性(导出符号)
0208 Swift符号
1 re-export
OTHER_LDFLAGS=$(inherited) -Xlinker -alias -Xlinker _NSLog -Xlinker _Cat_log
[re-export] _Cat_log (_NSLog from Foundation)
2 swift符号
swift是静态语言
0209 strip
一 app中哪些符号可以脱掉
1 为什么符号能脱掉个人理解(猜测并非官方):
可执行文件在运行的时候,比如调用一个方法,是通过 call + 地址 这种方式.
应该首先区分符号跟符号表,符号起到的是一个占位的作用,符号信息存储在符号表.
在链接阶段会根据符号表找到真正要调用的地址,并替换.
到可执行文件这个时候符号表内有的符号已经没有用处了.
但这部分符号还在可执行文件内存储并占用包大小,所以可以脱掉,并减少包大小.
2 可以脱掉的符号
本地符号可以脱掉
APP中的全局符号可以脱掉,APP中的全局符号一般不需要导出
APP中的弱定义符号可以脱掉
间接符号表中的不能脱掉,间接符号表存储的是导入符号,不可以脱掉
3 静态库
静态库的本质是:.o合集 + 重定位符号表
4 xcode中符号类型
Debugging Symbols(.o静态库/可执行文件 动态库)
All Symbols
Non-Global Symbols
二 思考
就符号的性质来说,使用静态库体积小还是动态库体积小?
静态库的本质是.o目标文件的合集,链接之后,符号就可以脱掉.
动态库中的导出符号存储在APP的间接符号表中,不可以脱掉.
所以从符号的性质来说,使用静态库的体积小.
三 dead code strip
在链接阶段起作用
-dead_strip 官方解释:删除入口点或导出符号无法访问的函数和数据。
Remove functions and data that are unreachable by the entry point or exported symbols.
0210 llvm-strip
1 strip
strip 是在修改macho中的一个内容
2 llvm-strip的使用
3 断点信息
br read -f 文件
br list strip
br enable strip
br write -f 文件
行者常至,为者常成!