Skip to main content
 首页 » 编程设计

linux之使用 scanf 进入全局或局部变量(在堆栈上),32 位调用约定

2024年08月12日14zengkefu

给出以下代码:

    .section    .rodata 
str:    .string "Hello World!\n" 
input:  .long 2 
    ######## 
    .text 
.globl  main 
    .type main, @function 
main: 
    pushl   %ebp 
    movl    %esp,   %ebp 
 
    pushl   $str 
    call    printf 
 
    #return from printf: 
    movl    $0, %eax 
    movl    %ebp,%esp 
    popl    %ebp 
    ret 

输出将是“Hello World!”。

<小时 />

现在我尝试从用户那里获取一个号码,然后将其打印在屏幕上,但是 它不起作用(代码编译,但我做错了)。 我的错误在哪里?

    .section    .rodata 
input:  .long   2 
    ######## 
    .text 
.globl  main 
    .type main, @function 
main: 
    pushl   %ebp 
    movl    %esp,   %ebp 
    pushl   %ebx     
 
    call    scanf  # call scanf to get number from the user 
    popl    input  # store the number entered by user in input (variable) 
    pushl   input  # push it back into the stack 
    call    printf # print input 
 
    #return from printf: 
    movl    $0, %eax 
    movl    %ebp,%esp 
    popl    %ebp 
    ret 

请您参考如下方法:

我不太确定您使用的是哪种汇编器,但是我可以让您的代码使用 gcc 进行编译,因此我坚持使用您的格式风格(不是谈论 AT&T 语法)。

无论如何,你应该检查 documentation对于 scanf 并意识到它需要一个格式字符串指针,指向内存中存储读入值的位置。它还>返回成功读取的项目数,而不是读取的内容。

现在做同样的事情并检查 documention对于printf。您将看到需要格式字符串才能以可读形式打印您的号码。合适的格式字符串是 "%d\n" 来打印数字和换行符。

您的代码现在可能看起来像这样(它可以使用 gcc 编译并正常工作):

.section .rodata 
 
input_format:  .string  "%d" 
output_format: .string  "%d\n" 
 
.section .bss 
input:  .long  0          # reserve 4 bytes of space 
 
.section .text 
.globl  main 
    .type main, @function 
main: 
    pushl   %ebp 
    movl    %esp,   %ebp 
 
    pushl   $input    # push the ADDRESS of input to have the value stored in it 
    pushl   $input_format   # give scanf the ADDRESS of the format string 
    call    scanf    # call scanf to get number from the user 
    addl    $8, %esp # clean up the stack 
     
    # Note the return value of scanf is passed through eax (same for printf) 
     
    pushl   input    # pass the number to printf BY VALUE 
    pushl   $output_format  # pass the ADDRESSS of the output format string to printf 
    call    printf   # print input 
 
    #return 0 from main: 
    movl    $0, %eax 
    movl    %ebp,%esp 
    popl    %ebp 
    ret 

请注意,我通常会使用 db/dw/dd.(ro)data.bss 部分中分配内存,如下所示与 .string.long 相反,因此如果该部分做得稍有错误,您可以修复它。

您还可以使用堆栈空间来存储数字,但是您已经声明了 input,并且我希望代码尽可能与您的代码相似。对于 scanfprintf 之前和之后的所有其他内容也是如此,我只是将其保留为您的代码。

编辑:这是使用堆栈创建局部变量的示例,而不是在 .bss.data< 中声明变量 段:

.section .rodata 
 
input_format:  .string  "%d" 
output_format: .string  "%d\n" 
 
.section .text 
.globl  main 
    .type main, @function 
main: 
    pushl   %ebp 
    movl    %esp,   %ebp 
 
    subl    $4, %esp       # allocate 4 bytes on the stack for a local variable 
 
    # The local variable will be at -4(%ebp) 
 
    leal    -4(%ebp), %eax # get the ADDRESS of our local variable 
    pushl    %eax          # push the ADDRESS of the variable on the stack 
    pushl   $input_format  # give scanf the ADDRESS of the format string 
    call    scanf          # call scanf to get number from the user 
    addl    $8, %esp       # clean up the stack 
 
    # Note the return value of scanf is passed through eax (same for printf) 
 
    pushl   -4(%ebp)       # pass the number to printf BY VALUE 
    pushl   $output_format # pass the ADDRESSS of the output format string to printf 
    call    printf         # print the input 
 
    #return from printf: 
    movl    $0, %eax 
    movl    %ebp,%esp 
    popl    %ebp 
    ret