我尝试使用输入四个浮点数scanf,将它们存储到堆栈中,然后使用vmovupd将它们复制到寄存器中以供使用。我的问题是,当我尝试输出这4个数字时,程序seg错误出现在printf。
scanf
vmovupd
printf
我认为它与堆栈有关,但是我尝试多次弹出(一次执行多个指令)无济于事。我还是汇编语言的新手,所以使用gdb它对于我来说有点太先进了。
gdb
您会注意到我包含了一个名为的文件debug。它使我能够查看寄存器和堆栈(这就是为什么要有dumpstack指令的原因。)这是由我的教授提供的,它确实提供了一些帮助,但显然还不够(或者也许我只是缺少了一些东西)。
debug
dumpstack
这是.cpp:
.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代码:
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
问题在于您的堆栈使用情况。
首先,ABI docs指令rsp必须 在 a 之前 对齐16个字节call。
rsp
call
由于a call会将8字节的返回地址压入堆栈,因此您需要将其rsp乘以16加上8的倍数进行调整,以返回到16字节对齐。将16 * n + 8被 包括 任何push指令或其他更改RSP,而不仅仅是sub rsp, 24。这是段错误的直接原因,因为printf将使用对齐的SSE指令,这将对未对齐的地址产生错误。
16 * n + 8
push
sub rsp, 24
SSE
如果您解决了该问题,则堆栈仍然不平衡,因为您一直在推入值,但从不弹出它们。很难理解您想对堆栈做什么。
通常的方法是在函数的开头(序言)为本地人分配空间,并在函数的结尾(结尾)释放空间。如上所述,此数量(包括任何推送)应为16加8的倍数,因为函数 项 上的RSP (在调用方之后call)距离16字节边界8字节。
在大多数glibc版本中,printf当AL!= 0时,它只关心16字节的堆栈对齐。(因为这意味着有FP args,因此它将所有XMM寄存器转储到堆栈中,以便可以为%f转换建立索引。)
%f
如果您使用未对齐的堆栈来调用它,即使它在您的系统上正常工作,这仍然是一个错误;将来的glibc版本 可能会 包含依赖16字节堆栈对齐的代码,即使没有FP args也是如此。例如,scanf即使AL=0在大多数GNU / Linux发行版中,也确实在未对齐的堆栈上崩溃。
AL=0