3

尝试在我的 asm 程序中为 iphone (arm64) 创建跳转表时,我遇到了一个很奇怪的问题:

.globl my_func
my_func:
...
//jump (switch) table
.L.f_switch:
    .short .L.case0 - .L.f_switch
    .short .L.case1 - .L.f_switch
    ...
.L.case0:
//some case code
...
.L.case1:
//other case code 

编译后,此表由零填充,而不是实际值。通过转储编译的目标文件可以看到。

(__TEXT,__text) section
_my_func:
0000000000000000    adr x4, #16
0000000000000004    ldrh    w5, [x4, x3, lsl #1]
0000000000000008    add x4, x4, w5, uxth
000000000000000c    br  x4
.L.f_switch:
0000000000000010    .long   0x00000000
0000000000000014    .long   0x00000000
0000000000000018    .long   0x00000000
000000000000001c    nop

如何解决?

4

2 回答 2

1

我相信您所观察到的条目设置为 0 与重定位有关。编译器可能会发出链接器最终会解析的重定位信息。为此,我创建了这个小示例程序:

测试.s

.text
.align 4
.globl _main
_main:
    adr  x0, .L.f_switch
    ldr  w1, [x0, x1, LSL#2]
    add  x0, x0, x1
    br   x0

.L.f_switch:
    .word  .L.case0 - .L.f_switch
    .word  .L.case1 - .L.f_switch
    .word  .L.case2 - .L.f_switch

.L.case0:
    nop

.L.case1:
    nop

.L.case2:
    nop

    ret

我正在使用 XCode 7,并且 clang 报告了这个版本信息clang --version

Apple LLVM version 7.0.0 (clang-700.0.72)
Target: x86_64-apple-darwin14.5.0
Thread model: posix

为了简化命令行中的操作,我设置了一个环境变量以指向我的 iPhone SDK:

export ISYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/"

第一个实验是编译test.stest.o. 我使用这个命令:

clang -x assembler  -arch arm64 test.s -o test.o -c

现在,如果我使用以下方式转储 test.o otool

otool -drGtv test.o

我明白了:

test.o:
Data in code table (0 entries)
offset     length kind
Relocation information (__TEXT,__text) 6 entries
address  pcrel length extern type    scattered symbolnum/value
00000018 False long   True   SUB     False     .L.f_switch
00000018 False long   True   UNSIGND False     .L.case2
00000014 False long   True   SUB     False     .L.f_switch
00000014 False long   True   UNSIGND False     .L.case1
00000010 False long   True   SUB     False     .L.f_switch
00000010 False long   True   UNSIGND False     .L.case0
(__TEXT,__text) section
_main:
0000000000000000        adr     x0, #16
0000000000000004        ldr     w1, [x0, x1, lsl #2]
0000000000000008        add      x0, x0, x1
000000000000000c        br      x0
.L.f_switch:
0000000000000010        .long   0x00000000
0000000000000014        .long   0x00000000
0000000000000018        .long   0x00000000
.L.case0:
000000000000001c        nop
.L.case1:
0000000000000020        nop
.L.case2:
0000000000000024        nop
0000000000000028        ret

.L.case#编译器(汇编器)已经为方程 (和.L.F_switch)的两个部分发出了 00000010、00000014 和 00000018 的重定位条目。表格本身充满了占位符零。解决重定位问题将是链接器的工作。我可以test.o使用以下命令手动链接上述内容:

ld  -demangle -dynamic -arch arm64 -iphoneos_version_min 5.0.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/ -o test -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk//usr/lib/system test.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/lib/darwin/libclang_rt.ios.a

我现在可以使用otool以下命令转储最终的可执行文件:

otool -drGtv test

并得到这个输出:

test:
Data in code table (0 entries)
offset     length kind
(__TEXT,__text) section
_main:
0000000100007f80        adr     x0, #16
0000000100007f84        ldr     w1, [x0, x1, lsl #2]
0000000100007f88        add      x0, x0, x1
0000000100007f8c        br      x0
.L.f_switch:
0000000100007f90        .long   0x0000000c
0000000100007f94        .long   0x00000010
0000000100007f98        .long   0x00000014
.L.case0:
0000000100007f9c        nop
.L.case1:
0000000100007fa0        nop
.L.case2:
0000000100007fa4        nop
0000000100007fa8        ret

请注意,链接器已在最终可执行文件中解析了所有重定位。

或者,我可以一步编译并链接所有内容,test以使用如下命令生成可执行文件:

clang -x assembler  -arch arm64 -L$ISYSROOT/usr/lib/system --sysroot=$ISYSROOT test.s -o test

我将其拆分以显示目标文件的外观,然后显示链接后生成的可执行文件。

于 2015-09-19T06:53:30.157 回答
0

首先,我要感谢 Michael Petch 对这次讨论的贡献,这非常有帮助。

其次,我想强调一下跳转表中数据的大小很重要。Clang 对“.word”(4 字节)偏移没有任何问题。当使用其他“.byte”(1 字节)或“.short”/“.hword”(2 字节)偏移时,麻烦就开始了。



测试 1.数据类型为 '.short' (2 Byte)

my_func:
...
//jump (switch) table
.L.f_switch:
    .short .L.case0 - .L.f_switch
    .short .L.case1 - .L.f_switch
    ...
.L.case0:
//some case code
...
.L.case1:
//other case code 

转储是:

Relocation information (__TEXT,__text) 10 entries
address  pcrel length extern type    scattered symbolnum/value
00000018 False word   True   SUB     False     .L.f_switch
00000018 False word   True   UNSIGND False     .L.case4
00000016 False word   True   SUB     False     .L.f_switch
00000016 False word   True   UNSIGND False     .L.case3
00000014 False word   True   SUB     False     .L.f_switch
00000014 False word   True   UNSIGND False     .L.case2
00000012 False word   True   SUB     False     .L.f_switch
00000012 False word   True   UNSIGND False     .L.case1
00000010 False word   True   SUB     False     .L.f_switch
00000010 False word   True   UNSIGND False     .L.case0

(__TEXT,__text) section
_my_func:
0000000000000000 adr x4, #16
0000000000000004 ldrh w5, [x4, x3, lsl #1]
0000000000000008 add x4, x4, w5, uxth
000000000000000c br x4
.L.f_switch:
0000000000000010 .long 0x00000000
0000000000000014 .long 0x00000000
0000000000000018 .long 0x00000000
000000000000001c nop

到目前为止,一切都如迈克尔在他的回答中所描述的那样进行(除了保留 2 字节偏移实体)

之后链接器返回错误:

in section __TEXT,__text reloc 0: ARM64_RELOC_SUBTRACTOR must have r_length of 2 or 3 for architecture arm64

请注意,如果使用4 字节实体,则不会出现任何错误。



测试 2。可以被视为解决方法。

    .set case_0,     .L.case0 - .L.f_switch
    .set case_1,     .L.case1 - .L.f_switch
    .set case_2,     .L.case2 - .L.f_switch
    ...

.L.f_switch:
    .hword  case_0
    .hword  case_1
    .hword  case_2
    ...

这种方法的转储是:

(__TEXT,__text) section
_my_func:
0000000000000000 adr x4, #16
0000000000000004 ldrh w5, [x4, x3, lsl #1]
0000000000000008 add x4, x4, w5, uxth
000000000000000c br x4
.L.f_switch:
0000000000000010 .long 0x01200020
0000000000000014 .long 0x06900240
0000000000000018 .long 0x00000cc0
000000000000001c nop

正如您可能注意到的,编译器直接通过正确的偏移值填充跳转表。因此,没有重定位信息和链接器的任何问题。


我还想提请注意以下事实。

  • GNU GCC 编译器为“Test 1”和“Test 2”代码生成“Test 2”(带有填充跳转表)的结果。
  • 如果表中的偏移量无法适合当前数据类型,GNU GCC 编译器会生成错误。例如,使用 1 字节数据类型且偏移量大于 255。在这种情况下,Clang 不会产生任何错误,因此程序员应该手动控制它。
于 2015-09-30T02:09:08.770 回答