【发布时间】:2020-09-22 19:52:25
【问题描述】:
我编写了以下程序,我想知道它的行为(1)是否会一直循环;或 (2) 它有意外的行为。我知道我在函数末尾缺少ret(这使程序运行良好),但我想知道为什么程序在不包含时会循环:
# file.s
.include "utils.s"
.data
str: .string "Tony"
.text
.globl _start
_start:
mov $str, %rdi
call print_string
mov $10, %edi
mov $SYS_EXIT, %eax
syscall
# utils.o
SYS_EXIT = 60
SYS_WRITE = 1
SYS_STDOUT = 1
print_string:
# void print_string(char *)
mov %rdi, %rsi
mov $1, %rdi
mov $4, %rdx
mov $SYS_WRITE, %eax
syscall
组装/链接/运行:
$ as utils.s -o utils.o && as file.s -o file.o && ld file.o utils.o -o file
$ ./file
在print_string.syscall 之后在gdb 中查看此内容时,它会返回到mov $str, %rdi,但为什么会出现这种情况?
注意:segfaults 全部在一个文件中,但当 print_string 移动到第二个文件时它会循环,所以这可能与组装/链接有关,而不是代码本身。
【问题讨论】:
-
在调试器中单步执行,以查看在您编写的内容结束后机器代码执行的字节数。如果这是一个简单的静态可执行文件,它可能只是零,并且会在
00 00解码到的add %al, (%rax)指令上出错。显示一个实际的minimal reproducible example 你是如何将它构建到一个可执行文件中的。如果您链接print_stringfirst,则执行从它到_start,与您在问题中显示的相反。这就是为什么 SO 问题需要真正的minimal reproducible examples,而不是你认为可能等同的东西。 -
UB。你可能已经跳了,你可能已经将一个寄存器弹出到一个跳转位置,很多事情都可能导致这种情况。不过,您正在阅读程序的末尾,谁知道...
-
就像现在一样,您的执行已经执行了 print_string 两次。你不想让你的 start 函数返回来防止这种情况发生吗?
-
嗨 @PeterCordes 更新了您的建议
-
你做了
.include "utils.s",所以call print_string将调用你包含的print_string的副本,而不是另一个.o文件中的副本(不是.globl所以不是'甚至无法到达)。不要将代码放在你要去.include的文件中。