Assembly “装配”;“你好,世界”;程序分段错误

Assembly “装配”;“你好,世界”;程序分段错误,assembly,x86-64,calling-convention,Assembly,X86 64,Calling Convention,为什么我会出错: 分段故障(堆芯转储) 以下是汇编代码: .intel_syntax noprefix .data message: .asciz "Hello World!\n" .text .global main main: lea rdi, message call printf ret 问题 SystemV ABI要求在调用函数之前将堆栈对齐到16字节。为了方便起见,ABI保证“在函数输入时,如果将堆栈指针分为8

为什么我会出错:

分段故障(堆芯转储)

以下是汇编代码:

.intel_syntax noprefix
    
.data

    message: .asciz "Hello World!\n"

.text

.global main

main:
    lea rdi, message
    call printf

    ret
问题 SystemV ABI要求在调用函数之前将堆栈对齐到16字节。为了方便起见,ABI保证“在函数输入时,如果将堆栈指针分为8*n(n是奇数),堆栈将对齐16字节”

如果不遵循此调用约定,其他库可能会崩溃,因为如果它们需要使用需要特殊对齐的指令(例如
movdqa
),它们无法正确对齐堆栈帧

解决方案
ammarfaizi2@integral:/tmp/test\u asm$cat test.S
.intel_语法noprefix
.数据
消息:.asciz“你好,世界!\n”
.文本
.全球主要
主要内容:
副区长,8
异或eax,eax
lea rdi,[rip+消息]
调用printf
加上rsp,8
ret
ammarfaizi2@integral:/tmp/test\u asm$gcc test.S-o test
ammarfaizi2@integral:/tmp/test\u asm$。/test
你好,世界!
ammarfaizi2@integral:/tmp/test\u asm$

推荐 如果您
调用
一个函数,接下来要做的就是
ret
,您可以通过尾部调用简化代码。它使用
jmp
来调用目标函数。如果在跳转之前设置了当前函数堆栈帧,请确保在跳转之前撤消它

为支持PUE和PIC,考虑使用RIP相对寻址来访问静态存储。它还提高了安全性。现在的编译器通常在默认情况下将目标编译为饼图

本部分是使用RIP相对寻址访问静态存储的示例:

lea rdi, [rip + message]
执行
ammarfaizi2@integral:/tmp/test\u asm$cat test.S
.intel_语法noprefix
.数据
消息:.asciz“你好,世界!\n”
.文本
.全球主要
主要内容:
异或eax,eax
lea rdi,[rip+消息]
jmp打印
ammarfaizi2@integral:/tmp/test\u asm$gcc test.S-o test
ammarfaizi2@integral:/tmp/test\u asm$。/test
你好,世界!
ammarfaizi2@integral:/tmp/test\u asm$

编辑
为安全起见,添加了
xor eax、eax
。请参阅:

由于1)未将堆栈对齐到16字节,2)未将AL寄存器归零以指示未使用向量寄存器,因此会出现SEGFULT。顺便说一句,早期的修订版没有问题#1,这也使得#2变得无关紧要,所以这应该是有效的。关于尾部调用的观点很好。可能值得补充的是,您将
lea
指令更改为
leardi[rip+message]
,以允许创建饼图可执行文件(否则会出现重新定位错误)。另一个解决方案是指示GCC创建一个带有
--static
(PIE是带有不可重写符号的SH ELF)的前ELF。@MargaretBloom哦,对了,我添加了关于RIP相对寻址的信息。让AL未设置仍然不安全。较旧的GCC版本编译可变函数,使用AL计算跳转到
movaps
存储序列中。(当前的GCC,以及libc的当前构建,只需检查0/non-0就可以有条件地运行所有8个,这样可能存在AL>8的ABI冲突实际上不会导致问题。)无论如何,您应该使用
xor eax,eax
。或者使用非可变的
puts
。对齐存储转储可变函数的可能XMM参数是printf出现故障(AL!=0)的常见原因,但某些其他函数的代码生成器有时包括16字节对齐加载或存储。e、 g.@AmmarFaizi:只修改现有RAX值的低位字节,而不是使用调零习惯用法将新值写入寄存器,这对正确性来说没问题,但对效率来说更糟
xor-al,al
在大多数CPU上并不是一种特殊的归零习惯用法,因此
mov-al,0
在某些CPU上实际上会更好。但仍然比xor eax、eax更糟糕。