工具配置
我们在分析Mach-O
文件的时候,会在终端上输入一些命令,当我们修改代码后重新bulid
,还需要再次重复上次的操作。其实可以对Xcode进行简单配置,在bulid
后就直接输出信息。
比如我现在想在Xcodebulid
的时候,将一些打印信息在终端中输出。
1.打开终端,在终端输入tty
,记住红框中的内容:
2.在Xcode执行脚本的位置添加红框中的命令,并定位到终点输出
3.Xcode编译输出:
使用xcconfig
文件,该文件创建及配置可查看上一篇文章
1.在xcconfig
文件添加
2.脚本修改
3.Xcode编译输出:
使用脚本文件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
eval "$@" &>$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
# CMD = 运行到命令
# CMD_FLAG = 运行到命令参数
# TTY = 终端
if [[ -n "$CMD" ]]; then
RunCommand $CMD
else
EchoError "=========================================="
EchoError "ERROR:Failed to run CMD. THE CMD must not null"
fi
}
RunCMDToTTY
复制代码
将该文件拷贝到项目根目录,使用的时候只需要在xcconfig
文件中使用下面命令
# CMD = 运行到命令
# CMD_FLAG = 运行到命令参数(也可以不用,直接写到CMD上)
# TTY = 终端
复制代码
Xcode脚本配置:
使用nm
打印符号表为例配置:
编译的时候可能会出现下面问题:
解决方法:
cd
到xcode_run_cmd.sh
所在目录下,执行:
打印输出:
配置成功
Symbol Table
Symbol Table
:就是用来保存符号。String Table
:就是用来保存符号的名称。Indirect Symbol Table
:间接符号表。保存使用的外部符号。更准确一点就是使用的外部动态库的符号。是Symbol Table
的子集。
符号总类划分
按照存在的空间划分:
- non private external
- weak private external
按照模块划分:
- weak global
- weak local
按照功能划分:
Type | 说明 |
---|---|
f | File |
F | Function |
O | Data |
d | Debug |
*ABS* | Absolute |
*COM* | Common |
*UND* | ? |
按照符号种类划分:
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 文件中) |
– | debugger symbol table |
S | 除了上面所述的,存放在其他section 的内容,例如未初始化的全局变量存放在(__DATA,__common)中 |
I | indirect symbol(符号信息相同,代表同一符号) |
u | 动态共享库中的小写u表示一个未定义引用对同一库中另一个模块中私有外部符号 |
全局符号和本地符号
//全局符号
int global_uninit_value;
int global_init_value = 10;
复制代码
// visibility属性,控制文件导出符号,限制符号可见性
/**
visibility:clang参数
default:用它定义的符号将被导出。
hidden:用它定义的符号将不被导出。
*/
//本地符号
int hidden_y __attribute__((visibility("hidden"))) = 99;
static int static_init_value = 9;
static int static_uninit_value;
复制代码
将全局符号变成本地符号有两种方式:
1.使用static
关键字
2.使用__attribute__((visibility("hidden")))
修饰
全局符号对整个项目可见,本地符号只对当前文件可见
导入导出符号
当我们使用NSLog
的时候,当前可执行文件就导入了NSLog
符号,Foundation
就导出了NSLog
符号。
查看符号相关命令
MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/${PRODUCT_NAME}
// 查看符号表
// CMD = objdump --macho --syms ${MACH_PATH}
// 查看导出符号
//CMD = objdump --macho --exports-trie ${MACH_PATH}
// 查看间接符号表
//CMD = objdump --macho --indirect-symbols ${MACH_PATH}
TTY=/dev/ttys001
复制代码
能够看出导出符号和全局符号相对应
- 全局符号可以变成导出符号供外界使用
- 动态库在运行过程中加载,在编译连接阶段只需要提供符号
- strip动态库只能删除不是全局符号的符号
main
方法中就使用了NSLog
来自Foundation
由此可见间接符号表保存这当前可执行文件使用的其它动态库的符号
添加OC类后,查看导出符号表:
为了减小包的体积需要进行符号剥离,可使用连接器
使用命令:
//不导出调试符号
OTHER_LDFLAGS = $(inherited) -Xlinker -S
//不导出符号unexported_symbol
OTHER_LDFLAGS = $(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS_$_TestOC
OTHER_LDFLAGS = $(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_CLASS_$_TestOC
复制代码
在看导出符号已经没刚才的OC符号了
Weak Symbol
Weak Reference Symbol
: 表示此未定义符号是弱引用。如果动态链接器找不到该符号的定义,则将其设置为0。链接器会将此符号设置弱链接标志。
Weak defintion Symbol
: 表示此符号为弱定义符号。如果静态链接器或动态链接器为此符号找到另一个(非弱)定义,则弱定义将被忽略。只能将合并部分中的符号标记为弱定义。
// 弱引用
void weak_import_function(void) __attribute__((weak_import));
复制代码
//将符号声明成弱定义(全局)
void weak_function(void) __attribute__((weak));
//将符号声明成弱定义(本地)
void weak_hidden_function(void) __attribute__((weak, visibility("hidden")));
复制代码
当我只声明了符号,并没有去实现的时候,会报错
可使用如下命令,将其改成动态查找
//-U动态查找
OTHER_LDFLAGS = $(inherited) -Xlinker -U -Xlinker _weak_import_function
复制代码
如果想让动态库符号不仅能在当前可执行文件使用,也能在其他可执行文件使用时,可给该符号起别名
//给NSLog起别名New_NSLog
OTHER_LDFLAGS = $(inherited) -Xlinker -alias -Xlinker _NSLog -Xlinker New_NSLog
复制代码
strip
strip
:移除或者修改符号表中的符号
strip Style
Debugging Symbols
:调试符号(.o静态库/可执行文件 动态库)All Symbols
:所有符号Non-Global Symbols
:非全局符号
静态库/.o文件脱Debugging Symbols
:
动态库/可执行文件脱Debugging Symbols
:
脱All Symbols
脱Non-Global Symbols
工程默认是脱去所有符号的
开发模式剥离Debugging Symbols