5.4 ARM: 8 arguments

我们再用之前9个参数的那个例子

  1. void printf_main2()
  2. {
  3. printf("a=%d; b=%d; c=%d; d=%d; e=%d; f=%d; g=%d; h=%d
  4. ", 1, 2, 3, 4, 5, 6, 7, 8);
  5. };

5.4.1 Optimizing Keil: ARM mode

  1. .text:00000028 printf_main2
  2. .text:00000028
  3. .text:00000028 var_18 = -0x18
  4. .text:00000028 var_14 = -0x14
  5. .text:00000028 var_4 = -4
  6. .text:00000028
  7. .text:00000028 04 E0 2D E5 STR LR, [SP,#var_4]!
  8. .text:0000002C 14 D0 4D E2 SUB SP, SP, #0x14
  9. .text:00000030 08 30 A0 E3 MOV R3, #8
  10. .text:00000034 07 20 A0 E3 MOV R2, #7
  11. .text:00000038 06 10 A0 E3 MOV R1, #6
  12. .text:0000003C 05 00 A0 E3 MOV R0, #5
  13. .text:00000040 04 C0 8D E2 ADD R12, SP, #0x18+var_14
  14. .text:00000044 0F 00 8C E8 STMIA R12, {R0-R3}
  15. .text:00000048 04 00 A0 E3 MOV R0, #4
  16. .text:0000004C 00 00 8D E5 STR R0, [SP,#0x18+var_18]
  17. .text:00000050 03 30 A0 E3 MOV R3, #3
  18. .text:00000054 02 20 A0 E3 MOV R2, #2
  19. .text:00000058 01 10 A0 E3 MOV R1, #1
  20. .text:0000005C 6E 0F 8F E2 ADR R0, aADBDCDDDEDFDGD ; "a=%d; b=%d; c=%d; d=%d;
  21. e=%d; f=%d; g=%"...
  22. .text:00000060 BC 18 00 EB BL __2printf
  23. .text:00000064 14 D0 8D E2 ADD SP, SP, #0x14
  24. .text:00000068 04 F0 9D E4 LDR PC, [SP+4+var_4],#4

这些代码可以分成几个部分:

Function prologue:

最开始的”STR LR, [SP,#var_4]!”指令将LR储存在栈中,因为我们将用这个寄存器调用printf()。

第二个” SUB SP, SP, #0x14”指令减了SP(栈指针),为了在栈上分配0x14(20)bytes的内存,实际上我们需要传递5个 32-bit的数据通过栈传递给printf()函数,而且每个占4bytes,也就是5*4=20。另外4个32-bit的数据将会传递给寄存器。

通过栈传递5,6,7和8:

然后,5,6,7,8分别被写入了R0,R1,R2及R3寄存器。然后ADD R12, SP,#0x18+var_14指令将栈中指针的地址写入,并且在这里会向R12写入4个值,var_14是一个汇编宏,相当于0x14,这些都由IDA简明的创建表示访问栈的变量,var_?在IDA中表示栈中的本地变量,所以SP+4将被写入R12寄存器。下一步的”STMIA R12, R0-R3”指令将R0-R3寄存器的内容写在了R2指向的指针处。STMIA指令指Store Multiple Increment After, Increment After指R12寄存器在有值写入后自增4。

通过栈传递4:

4存在R0中,然后这个值在” STR R0, [SP,#0x18+var_18]”指令帮助下,存在了栈上,var_18是0x18,偏移量为0.所以R0寄存器中的值将会写在SP指针指向的指针处。

通过寄存器传递1,2,3:

开始3个数(a,b,c)(分别是1,2,3)正好在printf()函数调用前被传递到了R1,R2,R3寄存器中。 然后另外5个值通过栈传递。

printf() 调用:

“ADD SP, SP, #0x14”指令将SP指针返回到之前的指针处,因此清除了栈,当然,栈中之前写入的数据还在那,但是当后来的函数被调用时那里则会被重写。 “LDR PC, [SP+4+var_4],#4”指令将LR中储存的值载入到PC指针,因此函数结束。

5.4.2 Optimizing Keil: thumb mode

  1. .text:0000001C printf_main2
  2. .text:0000001C
  3. .text:0000001C var_18 = -0x18
  4. .text:0000001C var_14 = -0x14
  5. .text:0000001C var_8 = -8
  6. .text:0000001C
  7. .text:0000001C 00 B5 PUSH {LR}
  8. .text:0000001E 08 23 MOVS R3, #8
  9. .text:00000020 85 B0 SUB SP, SP, #0x14
  10. .text:00000022 04 93 STR R3, [SP,#0x18+var_8]
  11. .text:00000024 07 22 MOVS R2, #7
  12. .text:00000026 06 21 MOVS R1, #6
  13. .text:00000028 05 20 MOVS R0, #5
  14. .text:0000002A 01 AB ADD R3, SP, #0x18+var_14
  15. .text:0000002C 07 C3 STMIA R3!, {R0-R2}
  16. .text:0000002E 04 20 MOVS R0, #4
  17. .text:00000030 00 90 STR R0, [SP,#0x18+var_18]
  18. .text:00000032 03 23 MOVS R3, #3
  19. .text:00000034 02 22 MOVS R2, #2
  20. .text:00000036 01 21 MOVS R1, #1
  21. .text:00000038 A0 A0 ADR R0, aADBDCDDDEDFDGD ; "a=%d; b=%d; c=%d; d=%d; e=%d; f=%d; g=%"...
  22. .text:0000003A 06 F0 D9 F8 BL __2printf
  23. .text:0000003E
  24. .text:0000003E loc_3E ; CODE XREF: example13_f+16
  25. .text:0000003E 05 B0 ADD SP, SP, #0x14
  26. .text:00000040 00 BD POP {PC}

几乎和之前的例子是一样的,然后这是thumb 代码,值入栈的确不同:先是8,然后5,6,7,第三个是4。

5.4.3 Optimizing Xcode (LLVM): ARM mode

  1. __text:0000290C _printf_main2
  2. __text:0000290C
  3. __text:0000290C var_1C = -0x1C
  4. __text:0000290C var_C = -0xC
  5. __text:0000290C
  6. __text:0000290C 80 40 2D E9 STMFD SP!, {R7,LR}
  7. __text:00002910 0D 70 A0 E1 MOV R7, SP
  8. __text:00002914 14 D0 4D E2 SUB SP, SP, #0x14
  9. __text:00002918 70 05 01 E3 MOV R0, #0x1570
  10. __text:0000291C 07 C0 A0 E3 MOV R12, #7
  11. __text:00002920 00 00 40 E3 MOVT R0, #0
  12. __text:00002924 04 20 A0 E3 MOV R2, #4
  13. __text:00002928 00 00 8F E0 ADD R0, PC, R0
  14. __text:0000292C 06 30 A0 E3 MOV R3, #6
  15. __text:00002930 05 10 A0 E3 MOV R1, #5
  16. __text:00002934 00 20 8D E5 STR R2, [SP,#0x1C+var_1C]
  17. __text:00002938 0A 10 8D E9 STMFA SP, {R1,R3,R12}
  18. __text:0000293C 08 90 A0 E3 MOV R9, #8
  19. __text:00002940 01 10 A0 E3 MOV R1, #1
  20. __text:00002944 02 20 A0 E3 MOV R2, #2
  21. __text:00002948 03 30 A0 E3 MOV R3, #3
  22. __text:0000294C 10 90 8D E5 STR R9, [SP,#0x1C+var_C]
  23. __text:00002950 A4 05 00 EB BL _printf
  24. __text:00002954 07 D0 A0 E1 MOV SP, R7
  25. __text:00002958 80 80 BD E8 LDMFD SP!, {R7,PC}

几乎和我们之前遇到的一样,除了STMFA(Store Multiple Full Ascending)指令,它和STMIB(Store Multiple Increment Before)指令一样,这个指令直到下个寄存器的值写入内存时会增加SP寄存器中的值,但是反过来却不同。

另外一个地方我们可以轻松的发现指令是随机分布的,例如,R0寄存器中的值在三个地方初始,在0x2918,0x2920,0x2928。而这一个指令就可以搞定。然而,optimizing compiler有它自己的原因,对于如何更好的放置指令,通常,处理器尝试同时执行并行的指令,例如像” MOVT R0, #0”和” ADD R0, PC,R0”就不能同时执行了,因为它们同时都在修改R0寄存器,另一方面”MOVT R0, #0”和”MOV R2, #4”指令却可以同时执行,因为执行效果并没有任何冲突。 大概,编译器就是这样尝试编译的,可能。

5.4.4 Optimizing Xcode (LLVM): thumb-2 mode

  1. __text:00002BA0 _printf_main2
  2. __text:00002BA0
  3. __text:00002BA0 var_1C = -0x1C
  4. __text:00002BA0 var_18 = -0x18
  5. __text:00002BA0 var_C = -0xC
  6. __text:00002BA0
  7. __text:00002BA0 80 B5 PUSH {R7,LR}
  8. __text:00002BA2 6F 46 MOV R7, SP
  9. __text:00002BA4 85 B0 SUB SP, SP, #0x14
  10. __text:00002BA6 41 F2 D8 20 MOVW R0, #0x12D8
  11. __text:00002BAA 4F F0 07 0C MOV.W R12, #7
  12. __text:00002BAE C0 F2 00 00 MOVT.W R0, #0
  13. __text:00002BB2 04 22 MOVS R2, #4
  14. __text:00002BB4 78 44 ADD R0, PC ; char *
  15. __text:00002BB6 06 23 MOVS R3, #6
  16. __text:00002BB8 05 21 MOVS R1, #5
  17. __text:00002BBA 0D F1 04 0E ADD.W LR, SP, #0x1C+var_18
  18. __text:00002BBE 00 92 STR R2, [SP,#0x1C+var_1C]
  19. __text:00002BC0 4F F0 08 09 MOV.W R9, #8
  20. __text:00002BC4 8E E8 0A 10 STMIA.W LR, {R1,R3,R12}
  21. __text:00002BC8 01 21 MOVS R1, #1
  22. __text:00002BCA 02 22 MOVS R2, #2
  23. __text:00002BCC 03 23 MOVS R3, #3
  24. __text:00002BCE CD F8 10 90 STR.W R9, [SP,#0x1C+var_C]
  25. __text:00002BD2 01 F0 0A EA BLX _printf
  26. __text:00002BD6 05 B0 ADD SP, SP, #0x14
  27. __text:00002BD8 80 BD POP {R7,PC}

几乎和前面的例子相同,除了thumb-instructions在这里被替代使用了