提问人:codesmith 提问时间:11/13/2023 更新时间:11/14/2023 访问量:65
x86_64程序集中的分段错误:系统调用问题 [重复]
Segmentation Fault in x86_64 Assembly: Issue with System Calls [duplicate]
问:
我只是尝试在屏幕上打印“Hello!”,但我遇到了 SegmentationFault 错误。 我有两个程序,一个工作正常,另一个出错。
工作正常的代码:
global _start
section .text
_start:
mov rdi,1
mov rsi,hello_world
mov rdx,13
mov rax,1
syscall
; exit(result)
mov rax,60
mov rdi,0
syscall
hello_world: db "Hello World!",10
给出 SegmentationFault 的代码:
global _start
section .text
_start:
mov rdi,1
mov rsi,hello_world
mov rdx,13
mov rax,1
syscall
hello_world: db "Hello World!",10
; exit(result)
mov rax,60
mov rdi,0
syscall
第二个代码的期望输出应该是:Hello world!
但它给出:分段错误
有人可以解释为什么第一个代码工作正常而第二个代码出现错误。
答:
正如迈克尔在他的评论中所说,问题出在这里。hello_world: db "Hello World!",10
要理解为什么会发生这种情况,您首先必须知道程序在内存中是如何表示的。维基百科的对象文件页面可能是更详细地理解这一点的良好开端。但简而言之,在此示例中,我们对 2 种段类型感兴趣:数据段和代码段。
程序的代码段(或文本段)包含机器运行程序必须执行的指令。通常,当加载到主存储器中时(当您执行程序时),此代码段将变为只读。这主要是出于安全原因,您可以在此处阅读有关此内容的更多信息。
程序的数据段包含初始化的静态变量。程序中的字符串“Hello World!”就是此类变量的一个很好的示例。
因此,要“使用良好实践”编写一个汇编示例,您应该使用这两个不同的部分,可以在此处找到一个 Hello World 示例。
现在,当一个程序被执行时,这个想法是CPU将加载程序计数器(PC)指向的操作,执行它,并增加PC以使其指向下一条指令。这之所以有效,是因为 CPU 知道指令的大小,因此可以正确地增加 PC。在您的代码中,这一切都很顺利,CPU 将正确执行,直到出现 .在那里,接下来的 10 个字节设置为“Hello World!”。因此,当 PC 递增时,它将指向此字符串中的某个位置。也许一开始它会在字符串中遇到有效指令,但在某些时候,它会遇到一个内存指令,其地址指向未分配给此程序的内存区域。尝试取消引用此地址时,将发出段错误。如果您想确切地知道正在使用什么指令和地址,Peter Cordes 的评论为您提供了如何操作的良好开端。hello_world: db "Hello World!",10
评论
db
将尝试写入只读段“这是无稽之谈,指令在组装/构建时写入(就像您组装的任何指令都会将机器代码写入当前部分一样),因此它们同样可以很好地写入只读数据或代码部分。db
db
db 0x90
nop
00 00
add [rax], al
objdump -drwC -Mintel
评论
hello_world: db "Hello World!",10
jmp