arm-linux-gnueabihf-gcc-4.8 -o3 -mthumb main.c -o main.out
使用
main
拆卸objdump
显示开关表数据字以0x83d8
开头,如下所示:int main(int argc, char** argv){
int i = 0;
switch (argc) {
case 0: i++; break;
case 1: i+=2; break;
case 3: i+=3; break;
case 6: i+=6; break;
case 5: i+=8; break;
case 7: i+=10; break;
default: i+=87; break;
}
return i;
}
观察结果:开关表不偏移。
使用
ldr.w pc, [base, index, lsl #2]
实现间接分支,其中基数(在这里r2)存储数据字的开头地址,而索引(在这里r3)用于计算偏移量。 br /> 上述观点可以概括吗?换句话说,我是否可以假定这是(大多数)ARM编译器实现开关语句的(事实上)标准方法?我在这里看不到ARM / Thumb之间的任何模式切换。例如,可以在
0x8428
处找到默认情况,但相应的地址存储为0x00008429
。#1 楼
不,您不能假设大多数ARM编译器将采用这种方式来实现switch语句。例如,以下是gcc 5.2.1上的同一代码:cosimo:~ moyix$ arm-none-eabi-gcc-5.2.1 -o3 -mthumb -c x.c -o x.o
cosimo:~ moyix$ arm-none-eabi-objdump -d x.o
x.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <main>:
0: b580 push {r7, lr}
2: b084 sub sp, #16
4: af00 add r7, sp, #0
6: 6078 str r0, [r7, #4]
8: 6039 str r1, [r7, #0]
a: 2300 movs r3, #0
c: 60fb str r3, [r7, #12]
e: 687b ldr r3, [r7, #4]
10: 2b07 cmp r3, #7
12: d81d bhi.n 50 <main+0x50>
14: 687b ldr r3, [r7, #4]
16: 009a lsls r2, r3, #2
18: 4b13 ldr r3, [pc, #76] ; (68 <main+0x68>)
1a: 18d3 adds r3, r2, r3
1c: 681b ldr r3, [r3, #0]
1e: 469f mov pc, r3
20: 68fb ldr r3, [r7, #12]
22: 3301 adds r3, #1
24: 60fb str r3, [r7, #12]
26: e017 b.n 58 <main+0x58>
28: 68fb ldr r3, [r7, #12]
2a: 3302 adds r3, #2
2c: 60fb str r3, [r7, #12]
2e: e013 b.n 58 <main+0x58>
30: 68fb ldr r3, [r7, #12]
32: 3303 adds r3, #3
34: 60fb str r3, [r7, #12]
36: e00f b.n 58 <main+0x58>
38: 68fb ldr r3, [r7, #12]
3a: 3306 adds r3, #6
3c: 60fb str r3, [r7, #12]
3e: e00b b.n 58 <main+0x58>
40: 68fb ldr r3, [r7, #12]
42: 3308 adds r3, #8
44: 60fb str r3, [r7, #12]
46: e007 b.n 58 <main+0x58>
48: 68fb ldr r3, [r7, #12]
4a: 330a adds r3, #10
4c: 60fb str r3, [r7, #12]
4e: e003 b.n 58 <main+0x58>
50: 68fb ldr r3, [r7, #12]
52: 3357 adds r3, #87 ; 0x57
54: 60fb str r3, [r7, #12]
56: 46c0 nop ; (mov r8, r8)
58: 68fb ldr r3, [r7, #12]
5a: 0018 movs r0, r3
5c: 46bd mov sp, r7
5e: b004 add sp, #16
60: bc80 pop {r7}
62: bc02 pop {r1}
64: 4708 bx r1
66: 46c0 nop ; (mov r8, r8)
68: 00000000 .word 0x00000000
还有更复杂的方案,包括对开关值进行二进制搜索以查找正确的情况。通常,您需要进行更复杂的分析以恢复switch语句。有关此主题的一些学术著作已经存在,例如:
关于第二个问题,使用
ldr pc
进行的跳转必须明确指定模式。由于使用-mthumb
编译了代码,因此您处于拇指模式,并且要保持拇指模式,这些地址需要将位0设置为1(请参见本页上的注释[a])。