【问题标题】:How to let the program exit with a 0 after printf finished?printf完成后如何让程序以0退出?
【发布时间】:2021-12-21 15:58:47
【问题描述】:

这个小程序只打印用 shell 编写的 2 个 argv。

.global main

main:
    ldr r2, [r1,#8]
    ldr r1, [r1,#4]
    ldr r0, =message_format
    bl  printf
    mov r0, #0

message_format:
    .asciz "argv: %s %s\n"

我希望它以 0 代码退出,所以我将 #0 放在 r0 中,但是:

root@kali-pi:~/Desktop/arm_test# ./arggggg 1 2
argv: 1 2
Segmentation fault
root@kali-pi:~/Desktop/arm_test# echo $?
139

我该如何解决这个问题?

【问题讨论】:

  • 也许用 0 调用 exit() ...我不知道 ARM ABI,所以我不知道该怎么做,但大概从 main 返回与从返回不完全相同一个函数...
  • 您必须调用退出函数或放置返回指令才能从main 函数返回。您的示例两者都没有,因此执行只是“落入”以下任何内容。

标签: assembly arm return printf


【解决方案1】:

我该如何解决这个问题?

汇编程序没有“结束”:

由于您计算机的内存通常比汇编程序长,因此汇编程序后面会有一些数据。并且由于 CPU 不区分数据和代码,它会将汇编程序后面的字节解释为代码。

例子:

假设您有 Th​​umb 代码,并且您的程序包含字节 1、2、3 和 4 之后的 RAM。然后 CPU 会将您的程序解释为:

ldr r2, [r1,#8]
ldr r1, [r1,#4]
ldr r0, =message_format
bl  printf
mov r0, #0
lsls r1, r0, #8
lsls r3, r0, #16
...

CPU 将一直运行,直到找到导致错误的指令为止。

您有其他用户已经在评论中写过的两种可能性:

1) 显式使用 exit() 函数或类似函数

您可以调用exit() 函数立即退出您的程序。

这就像调用 printf() 函数一样。

2) 从 main() 函数返回

您可以使用bx lrmain() 函数返回。

不幸的是,printf() 函数会覆盖 lr 的值,所以一个简单的bx lr 会导致死循环。

您必须在调用printf() 之前保存lr 寄存器,并在以后恢复它。

例子:

push {lr}
ldr r2, [r1,#8]
ldr r1, [r1,#4]
ldr r0, =message_format
bl  printf
mov r0, #0
pop {lr}
bx lr

如果您正在编译 Thumb 代码,pop {lr} 将不起作用。请改用pop {r1}bx r1

如果您确定main() 函数和调用main() 函数的函数属于相同的代码类型(ARM 或Thumb),您也可以在末尾只使用一条指令:pop {pc} .

在只支持 Thumb 代码的 Cortex M CPU 上工作或在不支持 Thumb 的旧 ARM CPU 上工作时就是这种情况。

【讨论】:

  • 感谢您的回答。我的教授和你做的一模一样,但只有一个不同。 push {ip, lr} 而不是 push {lr}pop {ip, lr} 而不是 pop {lr}。你能告诉我为什么我的教授还写ip而不仅仅是lr吗?
  • @Mnkisd: push {lr} 会使堆栈错位。 IIRC,ARM 调用约定需要 8 字节堆栈对齐,或者至少出于性能原因,GCC 默认保持这种对齐方式。例如godbolt.org/z/nfaW17 展示了 GCC 的作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-25
相关资源
最近更新 更多