问题
I trying to input four floats using scanf
, store them onto the stack, and then use vmovupd
to copy them over to a register for use. My problem is when I try to output those 4 numbers, the program seg faults at printf
.
I presume it is something with the stack but I've tried popping numerous times (multiple instructions at once) to no avail. I'm still new to Assembly coding so using gdb
is a bit too advanced for me.
You will notice that I have included a file called debug
. It allows me to look at registers and the stack (that's why there's a dumpstack
instruction.) That was provided by my professor and it did help some but obviously not enough (or maybe I am just missing something).
Here's the .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;
}
And here's the ASM
code:
%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:
The problem is with your stack usage.
First, the ABI docs mandate rsp
be 16 byte aligned before a call
.
Since a call
will push an 8 byte return address on the stack, you need to adjust rsp
by a multiple of 16 plus 8 to get back to 16-byte alignment. The 16 * n + 8
is including any push
instructions or other changes to RSP, not just sub rsp, 24
. This is the immediate cause of the segfault, because printf
will use aligned SSE
instructions which will fault for unaligned addresses.
If you fix that, your stack is still unbalanced, because you keep pushing values but never pop them. It's hard to understand what you want to do with the stack.
The usual way is to allocate space for the locals in the beginning of your function (the prologue) and free this at the end (epilogue). As discussed above, this amount (including any pushes) should be a multiple of 16 plus 8 because RSP on function entry (after the caller's call
) is 8 bytes away from a 16-byte boundary.
In most builds of glibc, printf
will only care about 16-byte stack alignment when AL != 0. (Because that means there are FP args, so it dumps all the XMM registers to the stack so it can index them for %f
conversions.)
It's still a bug if you call it with a misaligned stack even if it happens to work on your system; a future glibc version could include code that depends on 16-byte stack alignment even without FP args. For example, scanf
already does crash on misaligned stacks even with AL=0
on most GNU/Linux distros.
来源:https://stackoverflow.com/questions/25693827/segmentation-fault-on-printf-nasm-64bit-linux