printf 上的分段错误 - NASM 64 位 Linux
- 2024-10-31 08:38:00
- admin 原创
- 42
问题描述:
我尝试使用 输入四个浮点数scanf
,将它们存储到堆栈中,然后使用vmovupd
将它们复制到寄存器以供使用。我的问题是,当我尝试输出这 4 个数字时,程序在 处出现段错误printf
。
我推测是堆栈出了问题,但我尝试弹出多次(一次弹出多条指令),但无济于事。我对汇编语言编码还不熟悉,所以使用汇编语言gdb
对我来说有点太高级了。
你会注意到我包含了一个名为的文件debug
。它允许我查看寄存器和堆栈(这就是为什么有一条dumpstack
指令)。这是我的教授提供的,它确实有所帮助,但显然还不够(或者也许我只是错过了一些东西)。
以下是.cpp
:
#include <iostream>
using namespace std;
extern "C" double ComputeElectricity();
int main()
{
cout << "Welcome to electric circuit processing by Chris Tarazi." << endl;
double returnValue = ComputeElectricity();
cout << "The driver received this number: " << returnValue << endl;
return 0;
}
代码如下ASM
:
%include "debug.inc"
extern printf
extern scanf
global ComputeElectricity
;---------------------------------Declare variables-------------------------------------------
segment .data
greet db "This progam will help you analyze direct current circuits configured in parallel.", 10, 0
voltage db "Please enter the voltage of the entire circuit in volts: ", 0
first db "Enter the power consumption of device 1 (watts): ", 0
second db "Enter the power consumption of device 2 (watts): ", 0
third db "Enter the power consumption of device 3 (watts): ", 0
fourth db "Enter the power consumption of device 4 (watts): ", 0
thankyou db "Thank you. The computations have completed with the following results.", 10, 0
circuitV db "Curcuit total voltage: %1.18lf v", 10, 0
deviceNum db "Device number: 1 2 3 4", 10, 0
power db "Power (watts): %1.18lf %1.18lf %1.18lf %1.18lf", 10, 0
current db "Current (amps): %1.18lf %1.18lf %1.18lf %1.18lf", 10, 0
totalCurrent db "Total current in the circuit is %1.18lf amps.", 10, 0
totalPower db "Total power in the circuit is %1.18lf watts.", 10, 0
bye db "The analyzer program will now return total power to the driver.", 10, 0
string db "%s", 0
floatfmt db "%lf", 0
fourfloat db "%1.18lf %1.18lf %1.18lf %1.18lf", 0
;---------------------------------Begin segment of executable code------------------------------
segment .text
dumpstack 20, 10, 10
ComputeElectricity:
;dumpstack 30, 10, 10
;---------------------------------Output greet message------------------------------------------
mov qword rax, 0
mov rdi, string
mov rsi, greet
call printf
;---------------------------------Prompt for voltage--------------------------------------------
mov qword rax, 0
mov rdi, string
mov rsi, voltage
call printf
;---------------------------------Get voltage--------------------------------------------------
push qword 0
mov qword rax, 0
mov rdi, floatfmt
mov rsi, rsp
call scanf
vbroadcastsd ymm15, [rsp]
pop rax
;---------------------------------Prompt for watts 1--------------------------------------------
mov qword rax, 0
mov rdi, string
mov rsi, first
call printf
;---------------------------------Get watts 1---------------------------------------------------
push qword 0
mov qword rax, 0
mov rdi, floatfmt
mov rsi, rsp
call scanf
;---------------------------------Prompt for watts 2--------------------------------------------
mov qword rax, 0
mov rdi, string
mov rsi, second
call printf
;---------------------------------Get watts 2---------------------------------------------------
push qword 0
mov qword rax, 0
mov rdi, floatfmt
mov rsi, rsp
call scanf
;---------------------------------Prompt for watts 3--------------------------------------------
mov qword rax, 0
mov rdi, string
mov rsi, third
call printf
;---------------------------------Get watts 3---------------------------------------------------
push qword 0
mov qword rax, 0
mov rdi, floatfmt
mov rsi, rsp
call scanf
;---------------------------------Prompt for watts 4--------------------------------------------
mov qword rax, 0
mov rdi, string
mov rsi, fourth
call printf
;---------------------------------Get watts 4---------------------------------------------------
push qword 0
mov qword rax, 0
mov rdi, floatfmt
mov rsi, rsp
call scanf
;dumpstack 50, 10, 10
;---------------------------------Move data into correct registers------------------------------
vmovupd ymm14, [rsp] ; move all 4 numbers from the stack to ymm14
pop rax
pop rax
pop rax
pop rax
;dumpstack 55, 10, 10
vextractf128 xmm10, ymm14, 0 ; get lower half
vextractf128 xmm11, ymm14, 1 ; get upper half
;---------------------------------Move data into low xmm registers------------------------------
movsd xmm1, xmm11 ; move ymm[128-191] (3rd value) into xmm1
movhlps xmm0, xmm11 ; move from highest value from xmm11 to xmm0
movsd xmm3, xmm10
movhlps xmm2, xmm10
;showymmregisters 999
;---------------------------------Output results-------------------------------------------------
;dumpstack 60, 10, 10
mov rax, 4
mov rdi, fourfloat
push qword 0
call printf
pop rax
ret
解决方案 1:
问题在于您的堆栈使用情况。
首先,ABI 文档要求在 之前rsp
进行 16 字节对齐。call
由于call
将在堆栈上推送 8 字节返回地址,因此您需要调整rsp
16 的倍数加 8 才能恢复 16 字节对齐。16 * n + 8
包括任何指令或对 RSP 的其他更改,而不仅仅是。 这是导致段错误的直接原因,因为将使用对齐的指令,而这会导致未对齐地址的错误。push
`sub rsp, 24printf
SSE`
如果你修复了这个问题,你的堆栈仍然不平衡,因为你一直在推送值,但从不弹出它们。很难理解你想用堆栈做什么。
通常的做法是在函数开头(序言)为局部变量分配空间,并在结尾(结尾)释放空间。如上所述,这个数量(包括任何推送)应该是 16 加 8 的倍数,因为函数入口处的 RSP (在调用者的 之后call
)距离 16 字节边界有 8 个字节。
在大多数 glibc 版本中,printf
当 AL != 0 时才会关心 16 字节堆栈对齐。(因为这意味着有 FP 参数,所以它会将所有 XMM 寄存器转储到堆栈,以便可以对它们进行索引以进行%f
转换。)
如果您使用未对齐的堆栈调用它,即使它恰好在您的系统上运行,它仍然是一个错误;未来的 glibc 版本可能会包含依赖于 16 字节堆栈对齐的代码,即使没有 FP 参数也是如此。例如,即使在大多数 GNU/Linux 发行版上,scanf
未对齐的堆栈也已经崩溃。AL=0
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件