将scanf与x86-64 GAS组件一起使用 [英] Using scanf with x86-64 GAS assembly

查看:99
本文介绍了将scanf与x86-64 GAS组件一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试调用系统功能scanf使其在我的x86汇编程序中工作时,我遇到了很多问题.目前,我已经从标准中读取了它,但是它只会读取chars而不会出现段错误(我不知道为什么,指定的字符串是%d).我在x86在线版中看到过scanf的示例使用quarky或使用NASM语法编写,因此我尝试将它们改编为我的程序.

I have been having loads of issues trying to get a call the the system function scanf to work in my x86 assembly program. Currently I have got it to read from standard in however, it only will read chars without a segfault (I have no idea why, the specifying string is %d). The examples I've seen of scanf in x86 online use quarky or are written with NASM syntax, thus I have tried to adapt them for my program.

f:
    .string "%d"

_main:
    movq    $0,    %rax    #Clean rax
    movq    $f,    %rdi    #Load string format
    movq    %rcx,  %rsi    #Set storage to rcx (Not sure if this is valid)
    call    scanf
    ret

输入char或字符串后,使用printf检查rcx和rax分别返回1和0(这是程序不进行段错误的方式).

Checking rcx and rax using printf return 1 and 0 respectively after inputting a char or string (only way the program doesn't segfault).

任何对我们如何在x86气体组件中正确扫描的见解将不胜感激!

Any insight on how to us scanf properly in x86 gas assembly would be very much appreciated!

推荐答案

您担心,movq %rcx, %rsi是不正确的.您需要传递一个指向内存的指针.寄存器不是存储器地址空间的一部分,因此您不能拥有指向它们的指针.您需要全局或本地分配存储.顺便说一句,您不应将数据(尤其是可写的)放入默认的.text部分,因为该部分用于代码,并且通常是只读的.另外,调用约定通常要求16字节堆栈指针对齐,因此您也应该注意这一点.

As you feared, movq %rcx, %rsi is not correct. You need to pass a pointer to memory. Registers are not part of the memory address space and thus you can't have pointers to them. You need to allocate storage either globally or locally. Incidentally, you should not put your data (especially writable) into the default .text section, as that is intended for code and is typically read-only. Also, calling convention usually mandates 16 byte stack pointer alignment, so you should take care of that too.

.globl main

main:
    push %rbp           # keep stack aligned
    mov  $0, %eax       # clear AL (zero FP args in XMM registers)
    leaq f(%rip), %rdi  # load format string
    leaq x(%rip), %rsi  # set storage to address of x
    call scanf
    pop %rbp
    ret

.data

f:  .string "%d"         # could be in .rodata instead
x:  .long 0

(如果您的环境要求符号前有下划线,请使用_main,并且可能使用_scanf.)

(If your environment expects a leading underscore on symbols, then use _main, and probably _scanf.)

实际上有3种选择可以将符号/标签的地址放入寄存器中. RIP相对LEA是x86-64上的标准方法. 如何加载函数地址或将标签贴到GNU汇编器中注册

There are actually 3 choices for putting addresses of symbols / labels into registers. RIP-relative LEA is the standard way on x86-64. How to load address of function or label into register in GNU Assembler

作为优化,如果您的变量位于地址空间的较低4GiB中,例如在Linux非PIE(与位置有关 )可执行文件中,您可以使用32位绝对立即数:

As an optimization if your variables are in the lower 4GiB of the address space, e.g. in a Linux non-PIE (position-dependent) executable, you can use 32-bit absolute immediates:

    mov  $f, %edi       # load format string
    mov  $x, %esi       # set storage to address of x

movq $f, %rdi将使用32位符号扩展立即数(而不是从编写EDI到RDI的隐式零扩展),但其代码大小与相对于RIP的LEA相同.

movq $f, %rdi would use a 32-bit sign-extended immediate (instead of implicit zero-extension into RDI from writing EDI), but has the same code-size as a RIP-relative LEA.

您还可以使用助记符movabsq加载完整的64位绝对地址.但是不要这样做,因为10字节的指令对代码大小不利,并且由于与位置无关,因此仍需要运行时修复.

You can also load the full 64 bit absolute address using the mnemonic movabsq. But don't do that because a 10-byte instruction is bad for code-size, and still needs a runtime fixup because it's not position-independent.

    movabsq $f, %rdi # load format string
    movabsq $x, %rsi # set storage to address of x


根据请求:为输出使用本地变量可能类似于:

    subq  $8, %rsp       # allocate 8 bytes from stack
    xor   %eax, %eax     # clear AL (and RAX)
    leaq  f(%rip), %rdi  # load format string
    movq  %rsp, %rsi     # set storage to local variable
    call  scanf
    addq  $8, %rsp       # restore stack
    ret

这篇关于将scanf与x86-64 GAS组件一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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