【问题标题】:Scanf a char pointer in Assembly在程序集中扫描一个字符指针
【发布时间】:2017-04-22 22:34:06
【问题描述】:

所以我有一个任务要做,这需要我在汇编中使用 scanfchar*。我试过这段代码:

.data
INPUT_STRING:   .string "Give me a string: "
SCANF_STRING:   .string "%s"
PRINTF_STRING:  .string "String: %s\n"

.text
    .globl main
    .type main, @function
main:
    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    subl $32, %esp
    pushl $INPUT_STRING 
    call printf #printf("Give me a string: ")
    addl $4, %esp
    pushl -12(%ebp) # char*
    pushl $SCANF_STRING # "%s"
    call scanf scanf("%s", char*)
    addl $8, %esp    
    pushl -12(%ebp)
    pushl PRINTF_STRING
    call printf #printf("String: %s\n")
    addl $16, %esp
    movl -4(%ebp), %ecx   
    xorl %eax, %eax
    leave
    leal -4(%ecx), %esp
    ret

它首先正确地写下 printf,然后等待输入(所以 scanf 有效),然后当我输入任何内容时 -> Segmentation fault

我知道,char* 应该以某种方式初始化,但我怎样才能从汇编级别做到这一点?

我在 Manjaro 64 位上编译它,gcc -m32

【问题讨论】:

    标签: linux assembly att


    【解决方案1】:

    首先,谁他妈的告诉你这些废话:

    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    subl $32, %esp
    ...
    leave
    leal -4(%ecx), %esp
    ret
    

    这样做:

    pushl %ebp
    movl %esp, %ebp
    subl $32, %esp        # Place for 32 local bytes
    andl $-16, %esp       # Alignment 16 - not needed for 32-bit programs
    ...
    leave
    ret
    

    不仅不需要对齐,而且您使用以下PUSHes 取消对齐堆栈。但随心所欲... ;-)

    您想为字符串使用 12 个字节的本地堆栈帧。 scanf 需要这 12 个字节的起始地址。该区域的地址在编译时是未知的。 -12(%ebp) 为您提供此地址的值,而不是地址本身。 LEA 是计算地址的指令。所以你必须插入这条指令来在运行时获取地址并将其传递给 C 函数:

    leal -12(%ebp), %eax
    pushl %eax # char*
    

    这是工作示例(小错误也已更正):

    .data
    INPUT_STRING:   .string "Give me a string: "
    SCANF_STRING:   .string "%11s"      ##### Accept only 11 characters (-1 because terminating null)
    PRINTF_STRING:  .string "String: %s\n"
    
    .text
        .globl main
        .type main, @function
    main:
        pushl %ebp
        movl %esp, %ebp
        subl $32, %esp
    
        mov $32, %ecx
        mov %esp, %edi
        mov $88, %al
        rep stosb
    
        pushl $INPUT_STRING
        call printf                         # printf("Give me a string: ")
        addl $4, %esp
    
        leal -12(%ebp), %eax
        pushl %eax                          # char*
        pushl $SCANF_STRING                 # "%s"
        call scanf                          # scanf("%s", char*)
        addl $8, %esp
    
        leal -12(%ebp), %eax
        pushl %eax                          # char*
        pushl $PRINTF_STRING            ##### '$' was missing
        call printf                         # printf("String: %s\n")
        addl $8, %esp                   ##### 16 was wrong. Only 2 DWORD à 4 bytes were pushed
    
        leave
        ret
    

    【讨论】:

    • 但是,您在-12(%ebp) 上添加了char*,如果它的长度非常大怎么办?它在哪里存储所有字符?
    • @Frynio:在堆栈中。它是“subl $32, %esp”的一部分。对于非常大或可变的长度,您可以像在 C 中一样使用 malloc。如果您使用的是 C 库 - 像 C 程序员一样思考 :-)
    • 是的,我知道。但是那么字符数的限制是多少呢?
    • 堆栈设置限制。上次我溢出它时,默认值是 Windows 1 MB 和 Linux 8 MB。您的个人限制是 -12(%ebp) = 11 个字符 + 终止空值。因为堆栈帧不用于示例中的其他变量,所以您可以将其增加到 -32(%ebp) = 31 个字符 + 终止符。您需要 999 个字符和一个 null:: subl $1000, %esp-1000(%ebp)
    猜你喜欢
    • 2016-10-21
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2020-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多