在程序集中扫描一个字符指针

Scanf a char pointer in Assembly

所以我有一个任务要做,这需要我scanf一个char*组装。我试过这段代码:

.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 , %esp
    pushl $INPUT_STRING 
    call printf #printf("Give me a string: ")
    addl , %esp
    pushl -12(%ebp) # char*
    pushl $SCANF_STRING # "%s"
    call scanf scanf("%s", char*)
    addl , %esp    
    pushl -12(%ebp)
    pushl PRINTF_STRING
    call printf #printf("String: %s\n")
    addl , %esp
    movl -4(%ebp), %ecx   
    xorl %eax, %eax
    leave
    leal -4(%ecx), %esp
    ret

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

我知道,char* 应该以某种方式初始化,但我如何从汇编级别进行初始化?

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

首先,到底是谁跟你说的那些废话:

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

这样做:

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

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

您想为字符串使用本地堆栈帧的 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 , %esp

    mov , %ecx
    mov %esp, %edi
    mov , %al
    rep stosb

    pushl $INPUT_STRING
    call printf                         # printf("Give me a string: ")
    addl , %esp

    leal -12(%ebp), %eax
    pushl %eax                          # char*
    pushl $SCANF_STRING                 # "%s"
    call scanf                          # scanf("%s", char*)
    addl , %esp

    leal -12(%ebp), %eax
    pushl %eax                          # char*
    pushl $PRINTF_STRING            ##### '$' was missing
    call printf                         # printf("String: %s\n")
    addl , %esp                   ##### 16 was wrong. Only 2 DWORD à 4 bytes were pushed

    leave
    ret