printf 上的分段错误 - NASM 64 位 Linux [英] Segmentation fault on printf - NASM 64bit Linux

查看:36
本文介绍了printf 上的分段错误 - NASM 64 位 Linux的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用 scanf 输入四个浮点数,将它们存储到堆栈中,然后使用 vmovupd 将它们复制到寄存器以供使用.我的问题是当我尝试输出这 4 个数字时,程序在 printf 处出现段错误.

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.

我认为它与堆栈有关,但我尝试多次弹出(一次多条指令)但无济于事.我对汇编编码还是个新手,所以使用 gdb 对我来说有点太高级了.

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.

您会注意到我包含了一个名为 debug 的文件.它允许我查看寄存器和堆栈(这就是为什么有 dumpstack 指令.)这是由我的教授提供的,它确实有帮助,但显然还不够(或者我可能只是遗漏了一些东西).

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).

这是.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 代码:

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

推荐答案

问题在于您的堆栈使用.

The problem is with your stack usage.

首先,ABI 文档要求 rspcall 之前 对齐 16 字节.

First, the ABI docs mandate rsp be 16 byte aligned before a call.

由于 call 会将 8 字节的返回地址压入堆栈,因此您需要将 rsp 调整为 16 的倍数加 8 以恢复为 16 字节结盟.16 * n + 8 包括任何push指令或对RSP的其他更改,而不仅仅是sub rsp, 24.这是段错误的直接原因,因为 printf 将使用对齐的 SSE 指令,这将导致未对齐的地址出错.

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.

通常的方法是在函数的开头(序言)为局部变量分配空间,并在结尾(结尾)释放它.如上所述,这个数量(包括任何推送)应该是 16 加 8 的倍数,因为函数 entry 上的 RSP(在调用者的 call 之后)距离一个16 字节边界.

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.

在大多数 glibc 构建中,printf 只会在 AL != 0 时关心 16 字节堆栈对齐.(因为这意味着有 FP args,所以它会将所有 XMM 寄存器转储到堆栈,以便它可以为 %f 转换索引它们.)

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.)

如果您使用未对齐的堆栈调用它,即使它恰好在您的系统上工作,它仍然是一个错误;未来的 glibc 版本可能包括依赖于 16 字节堆栈对齐的代码,即使没有 FP args.例如,即使 AL=0 在大多数 GNU/Linux 发行版上,scanf 已经在未对齐的堆栈上崩溃.

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.

这篇关于printf 上的分段错误 - NASM 64 位 Linux的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆