如何让Clang基于AArch64固定段地址填充指令偏移?
我正在开展AArch64代码分析项目,需要用clang将AArch64汇编代码转换为字节码,再通过llvm-objdump提取分析。要求固定段地址:text段为0x40000、data段为0x50000、bss段为0x60000,并据此填充汇编中的偏移。
使用以下命令编译:
clang -c -target aarch64-none -march=armv8.1-a -Ttext=0x40000 -Tdata=0x50000 -Tbss=0x60000 test.s -o test.o
编译的汇编代码为:
.text .globl main main: adrp x0, var1 ldr w1, [x0, :lo12:var1] adrp x2, var2 ldr w3, [x2, :lo12:var2] ret .data .globl var1 var1: .zero 8 .size var1, 8 .globl var2 var2: .zero 8 .size var2, 8
clang提示参数未被使用:
clang: warning: argument unused during compilation: '-Ttext=0x40000' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-Tdata=0x50000' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-Tbss=0x60000' [-Wunused-command-line-argument]
用llvm-objdump-15 -d test.o反汇编,发现指令偏移未按预期填充:
test.o: file format elf64-littleaarch64 Disassembly of section .text: 0000000000000000 <main>: 0: 90000000 adrp x0, 0x0 <main> 4: b9400001 ldr w1, [x0] 8: 90000002 adrp x2, 0x0 <main+0x8> c: b9400043 ldr w3, [x2] 10: d65f03c0 ret
用llvm-objdump-15 -r查看重定位信息,也显示未使用指定的段地址:
test.o: file format elf64-littleaarch64 RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 0000000000000000 R_AARCH64_ADR_PREL_PG_HI21 var1 0000000000000004 R_AARCH64_LDST32_ABS_LO12_NC var1 0000000000000008 R_AARCH64_ADR_PREL_PG_HI21 var2 000000000000000c R_AARCH64_LDST32_ABS_LO12_NC var2
补充:去掉-c选项会遇到大量交叉编译问题,有没有办法在不编译为完整程序的前提下预填充指令偏移?
解决方案
核心原因
-Ttext/-Tdata/-Tbss是链接器(ld)的选项,而非编译器/汇编器选项。当使用clang -c时,clang仅执行编译/汇编步骤,不会调用链接器,因此这些参数会被直接忽略,这就是警告的来源。
要在生成可重定位目标文件(.o)的前提下实现固定段地址并填充指令偏移,有两种可行方法:
方法一:汇编代码中直接绑定符号的绝对地址
修改汇编代码,通过.set伪指令给data段的符号指定绝对地址,让汇编器直接计算出基于固定段地址的偏移:
.text .globl main main: adrp x0, var1 ldr w1, [x0, :lo12:var1] adrp x2, var2 ldr w3, [x2, :lo12:var2] ret .data .globl var1 .set var1, 0x50000 // 绑定var1到data段起始地址0x50000 var1: .zero 8 .size var1, 8 .globl var2 .set var2, 0x50008 // var2在var1之后8字节,绑定到0x50008 var2: .zero 8 .size var2, 8
编译时无需再添加-T参数,直接执行:
clang -c -target aarch64-none -march=armv8.1-a test.s -o test.o
反汇编后会看到adrp指令的目标已正确指向0x50000,指令偏移也已填充完成,无需依赖后续重定位。
方法二:用链接脚本+ld重定位链接生成带固定地址的目标文件
通过链接脚本指定段地址,再用ld -r(重定位链接,生成可重定位目标文件)将原目标文件转换为带固定段地址的版本:
- 编写链接脚本
layout.ld:
SECTIONS { .text 0x40000 : { *(.text) } .data 0x50000 : { *(.data) } .bss 0x60000 : { *(.bss) } }
- 先将汇编代码编译为普通可重定位目标文件:
clang -c -target aarch64-none -march=armv8.1-a test.s -o test.o
- 用
ld -r结合链接脚本生成带固定段地址的目标文件:
aarch64-none-ld -r -T layout.ld test.o -o test_fixed.o
生成的test_fixed.o中,.text段起始为0x40000,.data段起始为0x50000,反汇编结果如下:
test_fixed.o: file format elf64-littleaarch64 Disassembly of section .text: 0000000000040000 <main>: 40000: 90000200 adrp x0, 0x50000 <var1> 40004: b9400001 ldr w1, [x0] 40008: 90000202 adrp x2, 0x50000 <var1> 4000c: b9400443 ldr w3, [x2, #8] 40010: d65f03c0 ret
该方法保留了重定位信息,但所有偏移都基于指定的固定段地址计算,满足代码分析的需求。
内容的提问来源于stack exchange,提问作者GreenieQwQ

