分支链接到putchar导致ARM中的分段错误

问题描述 投票:0回答:2

我已经分离了这样一个事实:无论我怎么努力,我都无法将链接分支到putchar。

甚至连两条线就像

mov r0,$48
bl putchar

当我期望它打印ASCII 0时,总是会出现段错误

我可以分支到putchar,它会工作,但我不能分支链接。含义

mov r0,$48
b putchar

将工作

我觉得我错过了一些非常基本的东西,但我无法弄明白为什么。我只能假设它与putchar的返回有关,但我不知道是什么。

对不起,如果这看起来像一个愚蠢的问题,但老实说我找不到这方面的资源。

编辑:虽然上面的陈述对于我来说甚至是一个独立的程序也是如此,但我最终在子程序中实现了这一点,我认为这可能很重要

assembly arm
2个回答
1
投票

这很难说,因为您没有提供足够的代码,但您可能缺少符合ARM calling conventions所需的代码。 完整的代码应该保存堆栈上的fp,lr,而不是调用putchar,然后恢复fp,lr并返回或恢复fp,pc,这基本相同。

使用以下内容创建名为example.s的文件:

        .arch armv7-a
        .align  2
        .globl main
        .arch armv7-a
        .syntax unified
        .arm
main:
         push    {fp, lr}
         mov     r0, #48
         bl      putchar
         pop     {fp, pc}

编译并链接它 - 我编译了一个静态版本,因为我测试了qemu-arm:

/opt/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -static -O0 -o example  example.s

执行它 - 在我的情况下使用qemu-arm

/opt/qemu-3.1.0-static/bin/qemu-arm example
0

请注意:

pop     {fp, pc}

相当于:

pop     {fp, lr}
ret

我希望这有帮助。

更新

putchar()确实返回传递的字符或r0中的EOF。由于r0未在main中修改,因此它包含的值将返回给被调用者,即bash,并且可以使用echo $?命令查看:

opt/qemu-3.1.0/bin/qemu-arm example
0
echo $?
48

根据ARM calling conventions的第15页,r4-r8在子程序调用中被保留,但是r0-r3可能不是。

使用objdump反汇编示例程序:

/opt/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-objdump -D example > example.lst

在example.lst中,您可以看到putchar()是: 1)根据ARM Calling Convention保留r4,r5,r6,r7,r8,lr, 2)利用您提到的已修改的寄存器:

00016f50 <putchar>:
   16f50:   e92d41f0    push    {r4, r5, r6, r7, r8, lr}
   16f54:   e30354a8    movw    r5, #13480  ; 0x34a8
   16f58:   e3405008    movt    r5, #8
   16f5c:   e1a06000    mov r6, r0
   16f60:   e5954000    ldr r4, [r5]
   16f64:   e5943000    ldr r3, [r4]
   16f68:   e3130902    tst r3, #32768  ; 0x8000
   16f6c:   1a000015    bne 16fc8 <putchar+0x78>
   16f70:   e5943048    ldr r3, [r4, #72]   ; 0x48
   16f74:   ee1d7f70    mrc 15, 0, r7, cr13, cr0, {3}
   16f78:   e2477d13    sub r7, r7, #1216   ; 0x4c0
   16f7c:   e5932008    ldr r2, [r3, #8]
   16f80:   e1520007    cmp r2, r7
   16f84:   0a000030    beq 1704c <putchar+0xfc>
   16f88:   e3a02001    mov r2, #1
   16f8c:   e1931f9f    ldrex   r1, [r3]
   16f90:   e3510000    cmp r1, #0
   16f94:   1a000003    bne 16fa8 <putchar+0x58>
   16f98:   e1830f92    strex   r0, r2, [r3]
   16f9c:   e3500000    cmp r0, #0
   16fa0:   1afffff9    bne 16f8c <putchar+0x3c>
   16fa4:   f57ff05b    dmb ish
   16fa8:   1a00002d    bne 17064 <putchar+0x114>
   16fac:   e5943048    ldr r3, [r4, #72]   ; 0x48
   16fb0:   e5950000    ldr r0, [r5]
   16fb4:   e5837008    str r7, [r3, #8]
   16fb8:   e5932004    ldr r2, [r3, #4]
   16fbc:   e2822001    add r2, r2, #1
   16fc0:   e5832004    str r2, [r3, #4]
   16fc4:   ea000000    b   16fcc <putchar+0x7c>
   16fc8:   e1a00004    mov r0, r4
   16fcc:   e5903014    ldr r3, [r0, #20]
   16fd0:   e6efc076    uxtb    ip, r6
   16fd4:   e5902018    ldr r2, [r0, #24]
   16fd8:   e1530002    cmp r3, r2
   16fdc:   32832001    addcc   r2, r3, #1
   16fe0:   35802014    strcc   r2, [r0, #20]
   16fe4:   35c36000    strbcc  r6, [r3]
   16fe8:   2a000019    bcs 17054 <putchar+0x104>
   16fec:   e5943000    ldr r3, [r4]
   16ff0:   e3130902    tst r3, #32768  ; 0x8000
   16ff4:   1a000005    bne 17010 <putchar+0xc0>
   16ff8:   e5940048    ldr r0, [r4, #72]   ; 0x48
   16ffc:   e5903004    ldr r3, [r0, #4]
   17000:   e2433001    sub r3, r3, #1
   17004:   e5803004    str r3, [r0, #4]
   17008:   e3530000    cmp r3, #0
   1700c:   0a000001    beq 17018 <putchar+0xc8>
   17010:   e1a0000c    mov r0, ip
   17014:   e8bd81f0    pop {r4, r5, r6, r7, r8, pc}
   17018:   e5803008    str r3, [r0, #8]
   1701c:   f57ff05b    dmb ish
   17020:   e1902f9f    ldrex   r2, [r0]
   17024:   e1801f93    strex   r1, r3, [r0]
   17028:   e3510000    cmp r1, #0
   1702c:   1afffffb    bne 17020 <putchar+0xd0>
   17030:   e3520001    cmp r2, #1
   17034:   dafffff5    ble 17010 <putchar+0xc0>
   17038:   e3a01081    mov r1, #129    ; 0x81
   1703c:   e3a02001    mov r2, #1
   17040:   e3a070f0    mov r7, #240    ; 0xf0
   17044:   ef000000    svc 0x00000000
   17048:   eafffff0    b   17010 <putchar+0xc0>
   1704c:   e1a00004    mov r0, r4
   17050:   eaffffd8    b   16fb8 <putchar+0x68>
   ...

0
投票

让编译器指导你...编译器并不完美,但如果我们假设已调试,那么它们的输出就可以了。

void next ( char );
void fun ( void )
{
    next(0x33);
}

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   e3a00033    mov r0, #51 ; 0x33
   8:   ebfffffe    bl  0 <next>
   c:   e8bd4010    pop {r4, lr}
  10:   e12fff1e    bx  lr

没有链接,但表明需要保留lr。 R4用于保持堆栈在64位边界上对齐,具体取决于编译器的版本,它可能在此规则之前,并且只会推送lr并弹出它。

最大兼容性,最老的拇指指令,armv4t到armv7a和armv8m的工作。

00000000 <fun>:
   0:   b510        push    {r4, lr}
   2:   2033        movs    r0, #51 ; 0x33
   4:   f7ff fffe   bl  0 <next>
   8:   bc10        pop {r4}
   a:   bc01        pop {r0}
   c:   4700        bx  r0

(确实符合堆栈对齐btw)。

你发现你可以在那里分支:

00000000 <fun>:
   0:   2033        movs    r0, #51 ; 0x33
   2:   f7ff bffe   b.w 0 <next>

但在这种情况下尾部优化。如果你想分支链接并返回这个不适合你。

int next ( char );
int fun ( char a )
{
    return(next(a)+1);
}

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   f7ff fffe   bl  0 <next>
   6:   3001        adds    r0, #1
   8:   bd08        pop {r3, pc}

在某些时候pop支持与pop {pc}非原创拇指互通,但后来是。像r3上面的r4这里只是用于64位堆栈对齐,许多较低的寄存器在这里工作,它是一种无关紧要的东西。

我们需要了解架构的另一个原因是你是否可以/应该使用bl,因为bl在模式之间不起作用,但是如果你的架构支持blx的话。

00001000 <fun>:
    1000:   b508        push    {r3, lr}
    1002:   f000 e804   blx 100c <next>
    1006:   3001        adds    r0, #1
    1008:   bd08        pop {r3, pc}
    100a:   bf00        nop

0000100c <next>:
    100c:   e2800002    add r0, r0, #2
    1010:   e12fff1e    bx  lr



00001000 <fun>:
    1000:   e92d4010    push    {r4, lr}
    1004:   eb000003    bl  1018 <__next_from_arm>
    1008:   e8bd4010    pop {r4, lr}
    100c:   e2800001    add r0, r0, #1
    1010:   e12fff1e    bx  lr

00001014 <next>:
    1014:   3002        adds    r0, #2
    1016:   4770        bx  lr

00001018 <__next_from_arm>:
    1018:   e59fc000    ldr ip, [pc]    ; 1020 <__next_from_arm+0x8>
    101c:   e12fff1c    bx  ip
    1020:   00001015    andeq   r1, r0, r5, lsl r0
    1024:   00000000    andeq   r0, r0, r0

在这两种情况下,链接器修复了问题,但旧版本的gnu不会这样做,只是构建错误的代码,如果你不注意,即使是更新的代码也会构建错误的代码。取决于你那天有多幸运。所以使用bl指令要非常小心。

我假设我们都相信问题是你没有读过bl修改lr这意味着如果你想从使用bl的函数返回你需要保存那个返回地址而不是销毁它。

© www.soinside.com 2019 - 2024. All rights reserved.