ARM Mac汇编:Linux跨平台编译GOT段加载异常问题
现有一段ARM64汇编代码可在macOS正常编译运行,但在Linux环境下编译时会提示找不到_write符号。代码中svc指令后的bl _write语句移除后程序运行崩溃。用户尝试通过dump stub和GOT段并嵌入汇编的方式解决,但GOT段始终无法被更新为有效函数指针,动态链接失效。核心需求:
- 不依赖
_write等macOS动态库符号实现程序正常加载运行 - 或在无macOS库/dylib的Linux环境下完成编译,同时能使用
malloc/free/realloc等高效动态函数(以write为测试用例) - 编译出的程序需能在macOS正常运行,优先保留动态库函数调用能力而非直接使用系统调用
方案1:直接使用macOS系统调用替代动态库函数
macOS ARM64架构下,系统调用通过svc #0触发,寄存器约定明确:
x16:存储系统调用编号x0-x7:传递系统调用参数- 返回值存入
x0
以write为例,macOS对应的系统调用编号为4(可通过macOS原生syscalls.master文件确认),直接替换动态库调用为系统调用即可彻底摆脱对_write符号的依赖:
修改后的汇编代码:
.global _main .align 2 _main: stp x29, x30, [sp, #-16]! mov x29, sp ; 调用write系统调用:fd=1(标准输出),buf=hello,count=6 mov x0, #1 ; 文件描述符 adr x1, hello ; 字符串地址 mov x2, #6 ; 输出字节数 mov x16, #4 ; macOS write系统调用号 svc #0 ; 触发系统调用 ; 调用exit系统调用:status=0 mov x0, #0 mov x16, #1 ; macOS exit系统调用号 svc #0 hello: .ascii "Hello\n"
这种方式完全不依赖动态库,可在Linux上用支持Mach-O输出的ARM64汇编器和链接器编译,示例命令:
aarch64-linux-gnu-as -o main.o main.s aarch64-linux-gnu-ld -o main -arch arm64 -macosx_version_min 11.0 main.o
方案2:Linux上交叉编译Mach-O并正确处理动态链接
如果必须保留malloc/free等动态库函数调用,需在Linux上配置ARM64到macOS的交叉编译环境,让链接器自动生成符合Mach-O规范的动态链接信息,而非手动嵌入stub和GOT段:
安装交叉编译工具链
使用osxcross工具链,它支持在Linux环境生成macOS兼容的Mach-O二进制。安装完成后,工具链前缀为aarch64-apple-darwin20-(对应macOS Big Sur及以上版本)。修正汇编代码
保留extern声明,但移除手动嵌入的stub/GOT段,让链接器自动处理动态链接逻辑:
.global _main .align 2 _main: stp x29, x30, [sp, #-16]! mov x29, sp mov x0, #1 adr x1, hello mov x2, #6 bl _write ; 由链接器自动生成动态链接相关的stub和GOT表 mov x0, #0 mov x16, #1 svc #0 hello: .ascii "Hello\n" .extern _malloc .extern _realloc .extern _free .extern _write
- 编译链接命令
使用交叉编译工具链的汇编器和链接器,指定macOS系统库和SDK路径:
aarch64-apple-darwin20-as -o main.o main.s aarch64-apple-darwin20-ld -o main -lSystem -syslibroot /path/to/osxcross/macosx.sdk main.o
其中-lSystem用于链接macOS系统库,-syslibroot指定macOS SDK的路径,链接器会自动生成正确的stub和GOT段,程序启动时macOS的dyld加载器会自动解析符号并更新GOT指针。
手动嵌入stub/GOT段无效的原因
Mach-O的动态链接依赖dyld加载器在程序启动时解析符号并更新GOT表,手动嵌入的GOT段没有被链接器标记为可重定位的动态符号表条目,dyld无法识别并更新这些地址。只有由链接器生成的GOT段才会包含正确的重定位信息,确保dyld在加载阶段完成符号解析。
内容的提问来源于stack exchange,提问作者Stan

