从ARM汇编语言调用c函数printf

时间:2011-11-05 18:50:52

标签: assembly arm

我需要从ARM汇编语言例程调用printf。我编写了一个执行相同操作的程序(printf("%d.%d",1,2))。我反汇编了编译器输出,但格式字符串的传递方式并不明显。你们中的任何一个都有代码的例子吗?

这是我用来尝试查看如何调用printf的测试例程。

 #include <stdio.h>
 #include <stdlib.h>

 int main(void) {
         printf("%d.%d\n",1,2);
         return EXIT_SUCCESS;
 }

我对主程序的反汇编如下所示:

000081c4 <main>:
81c4:       e1a0c00d        mov     ip, sp
81c8:       e92dd800        stmdb   sp!, {fp, ip, lr, pc}
81cc:       e24cb004        sub     fp, ip, #4      ; 0x4
81d0:       e59f0014        ldr     r0, [pc, #20]   ; 81ec <.text+0x11c>
81d4:       e3a01001        mov     r1, #1  ; 0x1 
81d8:       e3a02002        mov     r2, #2  ; 0x2
81dc:       eb000212        bl      8a2c <_IO_printf>
81e0:       e3a03000        mov     r3, #0  ; 0x0
81e4:       e1a00003        mov     r0, r3
81e8:       e89da800        ldmia   sp, {fp, sp, pc}
81ec:       00060120        andeq   r0, r6, r0, lsr #2

我看到了_IO_printf例程的分支,但我没有看到如何将格式字符串传递给它。

3 个答案:

答案 0 :(得分:2)

#include <stdio.h>
#include <stdlib.h>

int main(void) {
         printf("%d.%d\n",1,2);
         return EXIT_SUCCESS;
}

编译和反汇编:

0000842c <main>:
    842c:   e92d4008    push    {r3, lr}
    8430:   e3a01001    mov r1, #1
    8434:   e3a02002    mov r2, #2
    8438:   e59f0008    ldr r0, [pc, #8]    ; 8448 <main+0x1c>
    843c:   ebffffcc    bl  8374 <_init+0x44>
    8440:   e3a00000    mov r0, #0
    8444:   e8bd8008    pop {r3, pc}
    8448:   00008524    andeq   r8, r0, r4, lsr #10

r0是第一个参数,格式字符串,r1是第二个参数a 1,r2是第三个参数a 2.格式字符串是一个字符串,一个指向字节数组的指针。 r0加载了该指针,一个字节串的地址。在这种情况下,该地址是0x8524。

如果你好奇,你可以去看看0x8524,看看你的字符串,

8524:   252e6425    strcs   r6, [lr, #-1061]!   ; 0xfffffbdb
8528:   00000a64    andeq   r0, r0, r4, ror #20

0x25,0x64,0x2e,0x25,0x64,0x0A,0x00

同样在你的反汇编中,你的字符串的地址是

81d0:       e59f0014        ldr     r0, [pc, #20]   ; 81ec <.text+0x11c>
...
81ec:       00060120        andeq   r0, r6, r0, lsr #2

如果您查看地址0x60120的反汇编,您将看到您的字符串。

答案 1 :(得分:1)

在C中,字符串存储为字节序列。将字符串传递给函数时,实际上是在传递字符串中第一个字符的地址。

当您调用printf()(没有编译器优化)时,参数将以相反的顺序(即从右到左)压入堆栈。然后printf()弹出第一个参数,它是格式字符串的(指针)。它解析格式字符串以确定每个连续参数弹出的字节数,以及如何根据它们所代表的数据类型(int,string等)来解释它们。

更新:正如其他人所指出的,ARM处理器使用不同的调用约定。它不是使用堆栈,而是传递寄存器中的第一个参数。但是这些参数的内容与它们在堆栈上传递时的内容相同。 R0将包含指向格式字符串的指针,下面的等效代码仍然准确。

感谢那些提供更正的人。

因此,至少就printf()而言,您的代码与此相同:

const char formatString[] = "%d.%d";
printf(&formatString[0], 1, 2);

答案 2 :(得分:0)

  

我看到了_IO_printf例程的分支,但我没有看到如何将格式字符串传递给它。

清洁眼镜。寄存器R0是字符串的地址,R1是“1”,R2是“2”。 Adam Liss错了,在ARM中你使用R0-R4作为前4个函数参数。

该行

81d0: e59f0014 ldr r0, [pc, #20] ; 81ec <.text+0x11c>

加载存储在返回R0后面的函数“尾部”的地址。