printf 上的分段错误 - NASM 64 位 Linux

2024-10-31 08:38:00
admin
原创
43
摘要:问题描述:我尝试使用 输入四个浮点数scanf,将它们存储到堆栈中,然后使用vmovupd将它们复制到寄存器以供使用。我的问题是,当我尝试输出这 4 个数字时,程序在 处出现段错误printf。我推测是堆栈出了问题,但我尝试弹出多次(一次弹出多条指令),但无济于事。我对汇编语言编码还不熟悉,所以使用汇编语言g...

问题描述:

我尝试使用 输入四个浮点数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 字节返回地址,因此您需要调整rsp16 的倍数加 8 才能恢复 16 字节对齐。16 * n + 8包括任何指令或对 RSP 的其他更改,而不仅仅是。 这是导致段错误的直接原因,因为将使用对齐的指令,而这会导致未对齐地址的错误。push`sub rsp, 24printfSSE`

如果你修复了这个问题,你的堆栈仍然不平衡,因为你一直在推送值,但从不弹出它们。很难理解你想用堆栈做什么。

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


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

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用