- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
编译以下代码时:
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 做错了什么?
最佳答案
在函数的开头/结尾使用sub rsp, 8
/add rsp, 8
将堆栈重新对齐为 16函数执行 调用
之前的字节数。
或者更好地压入/弹出一个虚拟寄存器,例如push rdx
/pop rcx
,或者你实际上想要保存的调用保留寄存器,如 RBP。 您需要对 RSP 的总更改是 8 的奇数倍,计算所有推送和sub rsp
,从函数入口到任何调用
。< br/>即 8 + 16*n
字节为整数 n
。
在函数入口处,RSP 距离 16 字节对齐有 8 个字节,因为 call
推送了一个 8 字节的返回地址。参见 Printing floating point numbers from x86-64 seems to require %rbp to be saved , main and stack alignment , 和 Calling printf in x86_64 using GNU assembler .这是一个 ABI 要求,当 printf 没有任何 FP args 时,您过去可以避免违反。但现在不是了。
另见 Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?
换句话说,RSP % 16 == 8
在函数入口,你需要确保 RSP % 16 == 0
在你调用之前
一个函数。你如何做到这一点并不重要。 (如果你不这样做,并不是所有的功能都会崩溃,但 ABI 确实需要/保证它。)
gcc 的 glibc scanf 代码生成现在取决于 16 字节堆栈对齐
即使 AL == 0
。
它似乎在 __GI__IO_vfscanf
中的某处自动向量化复制了 16 个字节,在将其寄存器参数溢出到堆栈1scanf 调用>。 (调用 scanf 的许多类似方法共享一个大实现作为各种 libc 入口点的后端,如 scanf
、fscanf
等)
我下载了 Ubuntu 18.04 的 libc6 二进制包:https://packages.ubuntu.com/bionic/amd64/libc6/download并提取文件(使用 7z x blah.deb
和 tar xf data.tar
,因为 7z 知道如何提取很多文件格式)。
我可以用 LD_LIBRARY_PATH=/tmp/bionic-libc/lib/x86_64-linux-gnu ./bad-printf
来重现你的错误,结果也是 glibc 2.27-3 系统在我的 Arch Linux 桌面上。
使用 GDB,我在您的程序上运行它并set env LD_LIBRARY_PATH/tmp/bionic-libc/lib/x86_64-linux-gnu
然后运行
。使用 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 │
所以它将两个 8 字节的对象复制到堆栈中,使用 movq
+ movhps
加载和 movaps
存储。但是由于堆栈未对齐,movaps [rbp-0x470],xmm0
出错。
我没有抓取调试版本来准确找出 C 源代码的哪一部分变成了这个,但该函数是用 C 编写的,并由启用了优化的 GCC 编译。 GCC 一直被允许这样做,但直到最近它才变得足够聪明,以这种方式更好地利用 SSE2。
脚注 1:带有 AL != 0
的 printf/scanf 始终需要 16 字节对齐,因为 gcc 的可变参数函数代码生成使用 test al,al/je 来溢出完整的 16 字节在这种情况下,XMM regs xmm0..7 与对齐的商店。 __m128i
可以是可变参数函数的参数,而不仅仅是 double
,并且 gcc 不会检查该函数是否实际读取过任何 16 字节的 FP args。
关于linux - 从不对齐 RSP 的函数调用时,glibc scanf 出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51070716/
这个问题在这里已经有了答案: Multiple glibc libraries on a single host (11 个回答) 5年前关闭。 有没有办法在本地文件夹中安装新版本的 GLIBC?我将
这个问题在这里已经有了答案: Deploying Yesod to Heroku, can't build statically (4 个回答) 1年前关闭。 我有两台机器 RHEL 4机 RHEL
我在 C 启动时读取 article ,它指的是函数 __libc_csu_init 。谁能告诉我CSU代表什么? 最佳答案 我在 libc p 中找到了对“ C s tart u p”的引用。 这远
我正在使用 AlpineLinux(安装在我的计算机上,而不是安装在 docker 中)并且我正在尝试使用依赖于 glibc 的共享库。 由于 Glibc 没有集成到这个发行版中,我看到开发了一个替代
所以我正在玩 Alpine Linux,它使用 musl 与 glibc。 我发现由于这个选择,我无法运行 Oracle Java 或在 glibc 下构建的静态二进制文件。 musl 是否应该与更广
在构建 glibc 时,出现此错误。 *glibc/build/elf/dl-allobjs.os: file not recognized: File format not recognized c
我一直在寻找没有好的结果。 我想知道在文本中解释为 Malloc Maleficarum 或 Malloc Des-Malleficarum 的技术在 glibc 2.12.1 版本中是否有效。 在提
我在 CentOS 上安装 gcc 时遇到问题。 我的命令的输出sudo yum install gcc: Loaded plugins: fastestmirror Loading mirror s
我想下载gzip类型的mariadb,但是我发现它有很多文件可以下载,例如mariadb-10.2.6-linux-x86_64.tar.gz,mariadb-10.2.6-linux-glibc_2
我想尝试使用不同的 glibc 的 sin 函数。他们是否可以在不编译整个 glibc 的情况下使用不同 glibc 版本的 sin 函数? 我已经在我的虚拟 ubuntu 机器上下载了不同版本的 g
我要在超算中心的目录下安装一个软件,但是sudo,root,连apt-get,yum都不能用...这个软件需要glibc2.7,所以我得手动安装gcc和glibc(因为我无法更改内置 gcc 的路径来
我正在研究需要在多个不同的嵌入式平台上运行的 C 和 C++ 程序,为此我有交叉编译器,因此我可以在我的 x86 桌面上进行构建。 我在某些功能上遇到了一个可怕的问题,例如“strtod()”。这是我
我遇到了一个非常烦人的问题:我有一个程序,它在开始时创建一个线程,该线程将在执行期间启动其他内容(fork() 紧随其后的是 execve())。 这是我的程序达到(我认为)死锁时两个线程的 bt:
我刚刚发现,glibc 2.23 有一个关于 stdio 函数 fmemopen() 的错误,参见例如Using rewind() on a FILE* opened with fmemopen .
[root@localhost Keystone2]# sudo rpm -ivh glibc-2.3.4-2.9.audit.x86_64.rpm --nodeps --force warning:
从该系列中很容易找到如何使用 dlsym()和其他函数,但是在内部如何工作呢?是否可以编写自己的,容易实现的dlsym()? 我想知道是否有可能实现类似的行为,但在不将与-ldl链接的情况下实现(可以
嗨,我目前使用的是 ubuntu 9.10 和 glibc 版本 2.11.1-0,好吧,我正在做一个项目,我想用 glibc 的另一个版本(2.5-58)进行测试,我想知道以下与此相关的事情: 如何
我已经下载了glibc,这样我就可以修改它的代码然后使用它。所以只是为了玩玩它,我更改了 glibc 的 stdio-common 目录中的 printf 函数,以便它打印“你可以看到我吗?”。 in
我正在集群上的 conda 环境中部署 R 脚本。该脚本加载了一些需要 glibc 版本 >= 2.14 的包。虽然我的系统的 glibc 版本为 2.14,但集群的 glibc 版本为 2.12,我
所以。我有一个问题,我在一台机器上有两个版本的 GCC。 3.4.6 和 4.1 这是由于新软件的一些依赖性问题。 (需要 glibc 4.1) 当我将这个新软件与 4.1 库链接时,它链接得很好。但
我是一名优秀的程序员,十分优秀!