x86_64程序集中的分段错误:系统调用问题 [重复]

Segmentation Fault in x86_64 Assembly: Issue with System Calls [duplicate]

提问人:codesmith 提问时间:11/13/2023 更新时间:11/14/2023 访问量:65

问:

我只是尝试在屏幕上打印“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!

但它给出:分段错误

有人可以解释为什么第一个代码工作正常而第二个代码出现错误。

Linux 程序集 分段故障 x86-64 NASM

评论

2赞 Michael Petch 11/13/2023
如果您像在底部示例中那样放置在代码的中间 - 该数据(字符串)将在第一个系统调用完成后作为指令执行。您希望将数据移出执行路径。如果您希望数据位于代码中间(不确定用例),那么您可以随时覆盖它。hello_world: db "Hello World!",10jmp

答:

-1赞 Romain46 11/13/2023 #1

正如迈克尔在他的评论中所说,问题出在这里。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

评论

1赞 ecm 11/13/2023
"db将尝试写入只读段“这是无稽之谈,指令在组装/构建时写入(就像您组装的任何指令都会将机器代码写入当前部分一样),因此它们同样可以很好地写入只读数据或代码部分。db
0赞 Peter Cordes 11/13/2023
x86 不是堆栈计算机。这是一台寄存器。(计算的抽象模型不区分 CPU 寄存器和 RAM,所以我认为最接近的标准抽象模型是 RASP。en.wikipedia.org/wiki/Random-access_stored-program_machine)。
1赞 Peter Cordes 11/13/2023
正如 ecm 所说,是在组装时处理的,而不是在运行时处理的。例如,完全等同于 ,每个字节将一个字节组合到正在创建的目标文件的当前部分中。所以你的要点 1.适用,而不是 2。已经有一个关于它的问答:在函数中使用DB(定义字节)时出现分段错误dbdb 0x90nop
1赞 Peter Cordes 11/14/2023
忘了前面说,如果字节不是有效的指令,你会得到 SIGILL 而不是 SIGSEGV。它们可能是一个有效的指令(大多数位模式解码为某种东西),但会取消引用恰好没有持有有效指针的寄存器。例如 是。或者特权指令(在 Linux 上也是段错误),或者某处的跳转。如果你好奇它到底是什么,可以检查它来反汇编机器代码,或者在调试器中单步执行它。大写 ASCII 范围的早期部分是 REX 前缀 (0x4x)00 00add [rax], alobjdump -drwC -Mintel