Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 从不';t对齐RSP_Linux_Assembly_Nasm_X86 64_Calling Convention - Fatal编程技术网

Linux 从不';t对齐RSP

Linux 从不';t对齐RSP,linux,assembly,nasm,x86-64,calling-convention,Linux,Assembly,Nasm,X86 64,Calling Convention,编译以下代码时: global main extern printf, scanf section .data msg: db "Enter a number: ",10,0 format:db "%d",0 section .bss number resb 4 section .text main: mov rdi, msg mov al, 0 call printf mov rsi, number mov rdi, format

编译以下代码时:

global main
extern printf, scanf

section .data
   msg: db "Enter a number: ",10,0
   format:db "%d",0

section .bss
   number resb 4

section .text
main:
   mov rdi, msg
   mov al, 0
   call printf

   mov rsi, number
   mov rdi, format
   mov al, 0
   call scanf

   mov rdi,format
   mov rsi,[number]
   inc rsi
   mov rax,0
   call printf 

   ret
使用:

nasm -f elf64 example.asm -o example.o
gcc -no-pie -m64 example.o -o example
然后跑

./example
它运行时,打印:输入一个数字: 但随后崩溃并打印: 分段故障(堆芯转储)

所以printf可以正常工作,但scanf不行。
scanf有什么问题吗?

在函数开始/结束时使用
子rsp,8
/
添加rsp,8
,在函数执行
调用之前将堆栈重新对齐到16个字节

或者更好地推/弹出一个虚拟寄存器,例如,
push rdx
/
pop rcx
,或者一个保留呼叫的寄存器,比如您实际上想要保存的RBP您需要对RSP的总更改为8的奇数倍,将所有推送和
子RSP
从函数输入到任何
调用

i、 e.
8+16*n
字节表示整数
n

在函数输入时,RSP距离16字节对齐距离为8字节,因为
调用
推送了一个8字节的返回地址。看见 ,及。这是一个ABI要求,当printf没有任何FP参数时,您可以不违反该要求。但现在不是了

另见


gcc用于glibc scanf的代码生成器现在取决于16字节堆栈对齐方式
即使
AL==0

它似乎在
\uu GI\uu IO\u vfscanf
中的某个位置自动向量化复制了16个字节,常规
scanf
在将其寄存器参数溢出到堆栈1后调用。(调用scanf的许多类似方法共享一个大型实现,作为各种libc入口点(如
scanf
fscanf
)的后端。)

我下载了Ubuntu 18.04的libc6二进制软件包:并提取了文件(使用
7z x blah.deb
tar xf data.tar
,因为7z知道如何提取很多文件格式)

我可以用
LD_LIBRARY_PATH=/tmp/bionic libc/lib/x86_64-linux-gnu./bad printf
重新编写您的bug,而且在我的Arch linux桌面上的系统glibc 2.27-3中也可以实现

有了GDB,我在您的程序上运行了它,并设置了env LD_LIBRARY_PATH/tmp/bionic libc/lib/x86_64-linux-gnu
,然后运行了
run
。使用
layout reg
,反汇编窗口在接收到SIGSEGV的点处如下所示:

   │0x7ffff786b49a <_IO_vfscanf+602>        cmp    r12b,0x25                                                                                             │
   │0x7ffff786b49e <_IO_vfscanf+606>        jne    0x7ffff786b3ff <_IO_vfscanf+447>                                                                      │
   │0x7ffff786b4a4 <_IO_vfscanf+612>        mov    rax,QWORD PTR [rbp-0x460]                                                                             │
   │0x7ffff786b4ab <_IO_vfscanf+619>        add    rax,QWORD PTR [rbp-0x458]                                                                             │
   │0x7ffff786b4b2 <_IO_vfscanf+626>        movq   xmm0,QWORD PTR [rbp-0x460]                                                                            │
   │0x7ffff786b4ba <_IO_vfscanf+634>        mov    DWORD PTR [rbp-0x678],0x0                                                                             │
   │0x7ffff786b4c4 <_IO_vfscanf+644>        mov    QWORD PTR [rbp-0x608],rax                                                                             │
   │0x7ffff786b4cb <_IO_vfscanf+651>        movzx  eax,BYTE PTR [rbx+0x1]                                                                                │
   │0x7ffff786b4cf <_IO_vfscanf+655>        movhps xmm0,QWORD PTR [rbp-0x608]                                                                            │
  >│0x7ffff786b4d6 <_IO_vfscanf+662>        movaps XMMWORD PTR [rbp-0x470],xmm0                                                                          │
│0x7ffff786b49a cmp r12b,0x25│
│0x7ffff786b49e jne 0x7ffff786b3ff│
│0x7ffff786b4a4 mov rax,QWORD PTR[rbp-0x460]│
│0x7ffff786b4ab添加rax,QWORD PTR[rbp-0x458]│
│0x7ffff786b4b2 movq xmm0,QWORD PTR[rbp-0x460]│
│0x7ffff786b4ba mov DWORD PTR[rbp-0x678],0x0│
│0x7ffff786b4c4 mov QWORD PTR[rbp-0x608],rax│
│0x7ffff786b4cb movzx eax,字节PTR[rbx+0x1]│
│0x7ffff786b4cf movhps xmm0,QWORD PTR[rbp-0x608]│
>│0x7ffff786b4d6 movaps XMMWORD PTR[rbp-0x470],xmm0│
因此,它将两个8字节的对象复制到堆栈中,并使用
movq
+
movhps
加载和
movaps
存储。但是当堆栈未对齐时,
movaps[rbp-0x470],xmm0
出现故障

我并没有抓取一个调试构建来准确地找出C源代码的哪一部分变成了这个,但是这个函数是用C编写的,由GCC编译并启用了优化功能。GCC一直被允许这样做,但直到最近它才变得足够聪明,以这种方式更好地利用SSE2



脚注1:printf/scanf带
AL!=0始终需要16字节对齐,因为在这种情况下,gcc的可变函数代码生成器使用test al、al/je将完整的16字节XMM regs xmm0..7与对齐的存储溢出<代码>\uuuM128i
可以是可变函数的参数,而不仅仅是
double
,而且gcc不会检查函数是否实际读取任何16字节的FP参数。

在调用C库例程(根据AMD64 ABI)之前,堆栈未与16字节边界对齐可能存在问题<当
number
为4个字节时,code>mov-rsi,[number]将8个字节从number移动到rsi。也许你想要
movesi,[number]
仅仅因为答案被接受并不意味着它没有问题。如果在
main:
之后和
ret
add
pop rbp
之前添加
push rbp
,会发生什么情况(这应处理对齐问题)。将
mov rsi,[number]
更改为
mov esi,[number]
。GDB或任何调试器都是理想的工具。若一个调试器吓到了你们,那个么程序集可能不是你们应该使用的程序集。push-rbp和pop-rbp解决了这个问题。谢谢!Ubuntu18.04(仿生海狸)64比特edb调试器现在不是在18.04的宇宙存储库中可用吗?看看吧,它可能有一些更简单的UI,尽管它没有gdb那么强大和通用。如果您只打算用短小的代码学习汇编基础知识,edb应该足够了。(如果它还没有在repos中,那么您必须从源代码[从github]编译它,但是