创建常量跳转表;xcode;叮当声;asm

创建常量跳转表;xcode;叮当声;asm,xcode,assembly,switch-statement,clang,jump-table,Xcode,Assembly,Switch Statement,Clang,Jump Table,在我的asm iphone程序(arm64)中尝试创建跳转表时,我遇到了一个非常奇怪的问题: 编译后,此表由零而不是实际值填充。可以通过转储编译的对象文件来查看它 (__TEXT,__text) section _my_func: 0000000000000000 adr x4, #16 0000000000000004 ldrh w5, [x4, x3, lsl #1] 0000000000000008 add x4, x4, w5, uxth 000000000000

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

编译后,此表由零而不是实际值填充。可以通过转储编译的对象文件来查看它

(__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

如何解决此问题?

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

测试 我使用的是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.s
编译成
test.o
。我使用以下命令:

clang -x assembler  -arch arm64 test.s -o test.o -c
现在,如果我使用
otool
转储test.o,请使用:

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_开关
)发出了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 -drGtv test
clang -x assembler  -arch arm64 -L$ISYSROOT/usr/lib/system --sysroot=$ISYSROOT test.s -o test
现在,我可以使用
otool
使用如下命令转储最终可执行文件:

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 -drGtv test
clang -x assembler  -arch arm64 -L$ISYSROOT/usr/lib/system --sysroot=$ISYSROOT test.s -o 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

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 -drGtv test
clang -x assembler  -arch arm64 -L$ISYSROOT/usr/lib/system --sysroot=$ISYSROOT test.s -o test

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

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

其次,我想强调的是,跳转表中数据的大小非常重要。Clang对“.word”(4字节)偏移量没有任何问题。当使用其他'.byte'(1字节)或'.short'/'.hword'(2字节)偏移量时,问题开始出现


测试1数据类型为“.short”(2字节)

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
到目前为止,一切都像Michael在回答中所描述的那样进行(除了保留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编译器为“测试1”和“测试2”代码生成“测试2”(带填充跳转表)中的结果
  • 若表中的偏移量不能适应当前数据类型,GNU GCC编译器将生成错误。例如,使用1字节数据类型&偏移量大于255。在这种情况下,叮当声不会产生任何错误,所以程序员应该手动控制它

如果对其执行
objdump-rD file.o
操作,是否会在表中显示重新定位信息?如果是,则应在链接过程中填写重新定位信息。如果这就是正在发生的事情,那么这个表将以零作为占位符填充。@MichaelPetch不,它没有。老实说,我不知道这个重新定位信息在反汇编程序中应该是什么样子。但是“decomposition of section.text:”是唯一一个不是我原始源文件中的指令的字符串。我认为如果您向我们展示转储编译后的对象文件的相关输出会有所帮助。如果您不说目标平台是什么(ARM/X86/X86_64),您可以标记为XCode(这意味着某种苹果)。如果以Intel x86-64(或x86)为目标,我看到的一个问题是,如果要创建跳转表,则需要绝对地址。通过寄存器使用间接
JMP
(如
JMP*.L.f_开关(,%rax,8)
),您需要存储在表中的绝对地址。事实上,缺少真正的代码使问题偏离主题。如果您实际上使用的是没有绝对地址的跳转表,我只能断定您必须以ARM64(或ARM)为目标这可能会建议使用iOS,但问题中没有提到。您基本上需要使用示例代码重写此问题,该示例代码可以编译以演示此问题,并且有助于在该示例代码上查看objdump的输出。更好地描述目标CPU/OS会有所帮助。我的猜测是,0是ceholders将在以后填写。如果跳转表位于
.data
部分(而不是
.text
部分),则可能会发生这种情况。Michael感谢您的指导。您所描述的一切都是正确的。与我的代码不同的是,我使用了2个字节来保留偏移量,似乎这正是问题所在。