【问题标题】:Copying and calling Functions in x86 AT&T-Assembler in gcc在 gcc 中的 x86 AT&T-Assembler 中复制和调用函数
【发布时间】:2009-03-12 18:16:02
【问题描述】:

我在 AT&T Assembler Syntax for gcc 中编写了以下代码

.global main
.section .data

to_gen_inner: #x f, implicit n
        pushl %ebp
        movl %esp, %ebp
        movl $0xFF00FF00, %eax
        call printregs
        lret

.set to_gen_inner_len, . - to_gen_inner

.section .text

main:
        pushl %ebp
        movl %esp, %ebp

        #allocate memory
        pushl $to_gen_inner_len
        call malloc
        popl %ecx

        pushl $to_gen_inner_len
        pushl to_gen_inner
        pushl %eax
        call copy_bytes
        popl %eax
        popl %ecx
        popl %ecx

        lcall *(%eax)

        movl %ebp, %esp
        popl %ebp
        ret

printfregs:
        .ascii "eax: %8X\nebx: %8X\necx: %8X\nedx: %8X\n\0"

printregs:
        pushl %edx
        pushl %ecx
        pushl %ebx
        pushl %eax
        pushl $printfregs
        call printf
        popl %ecx
        popl %eax
        popl %ebx
        popl %ecx
        popl %edx
        lret

copy_bytes: #dest source length
        pushl %ebp
        movl %esp, %ebp

        subl $24, %esp

        movl 8(%ebp), %ecx # dest
        movl %eax, -4(%ebp)

        movl 12(%ebp), %ebx # source
        movl %eax, -8(%ebp)

        movl 16(%ebp), %eax # length
        movl %eax, -12(%ebp)

        addl %eax, %ecx # last dest-byte
        movl %ecx, -16(%ebp)

        addl %eax, %edx # last source-byte
        movl %ecx, -20(%ebp)

        movl -4(%ebp), %eax
        movl -8(%ebp), %ebx
        movl -16(%ebp), %ecx

        copy_bytes_2:
        movb (%ebx), %dl
        movb %dl, (%eax)
        incl %eax
        incl %ebx
        cmp %eax, %ecx
        jne copy_bytes_2

        movl %ebp, %esp
        popl %ebp
        ret

其实我要做的是将to_gen_inner的函数代码复制到我用malloc分配的内存中,然后跳转进去。此代码产生分段错误。 gdb sais:

Program received signal SIGSEGV, Segmentation fault.
main () at speicher3.S:32
32              lcall *(%eax)
Current language:  auto; currently asm
(gdb) disas $pc-5 $pc+5
Dump of assembler code from 0x80483eb to 0x80483f5:
0x080483eb <main+23>:   add    %al,(%eax)
0x080483ed <main+25>:   pop    %eax
0x080483ee <main+26>:   pop    %ecx
0x080483ef <main+27>:   pop    %ecx
0x080483f0 <main+28>:   lcall  *(%eax)
0x080483f2 <main+30>:   mov    %ebp,%esp
0x080483f4 <main+32>:   pop    %ebp
End of assembler dump.
(gdb) disas $pc-6 $pc+5
Dump of assembler code from 0x80483ea to 0x80483f5:
0x080483ea <main+22>:   add    %al,(%eax)
0x080483ec <main+24>:   add    %bl,0x59(%eax)
0x080483ef <main+27>:   pop    %ecx
0x080483f0 <main+28>:   lcall  *(%eax)
0x080483f2 <main+30>:   mov    %ebp,%esp
0x080483f4 <main+32>:   pop    %ebp
End of assembler dump.
(gdb)

我其实不知道为什么。我已经在使用 lcall 和 lret,我读过它被认为是绝对调用,使用 call 和 ret,它也不起作用,同样的错误。

我不知道我做错了什么。有人可以帮帮我吗?

【问题讨论】:

    标签: gcc assembly x86


    【解决方案1】:

    您有以下问题:

    • 在为调用 copy_bytes 设置堆栈时,您希望 pushl $to_gen_inner 而不是 pushl to_gen_inner(后者将内存内容推送到 to_gen_inner 指向的内容)

    • 将值复制到copy_bytes内的本地堆栈帧时,需要写入刚刚读取参数的寄存器,而不是总是写入EAX

    • lcall *(%eax) 期望从 EAX 指向的内存中读取地址,然后跳转到那里。此外,它预计读取 48 个字节,前 16 个是段。我已将您的 lcall 替换为 call *%eax;也相应地用 rets 替换了 lrets。

    • 对 printregs 的调用被汇编为相对调用,因为您正在执行的指令不再与目标的相对偏移量与汇编时相同。我已将其替换为

      movl $printregs, %ecx
      call *%ecx
      

    (这会破坏 %ecx)

    • 最后,to_gen_inner 在进入时设置了堆栈帧,但在退出时未能销毁它

    所有这些都修复后,代码如下所示:

    .global main
    .section .data
    
    to_gen_inner: #x f, implicit n
            pushl %ebp
            movl %esp, %ebp
            movl $0xFF00FF00, %eax
            movl $printregs, %ecx
            call *%ecx
            movl %ebp, %esp
            popl %ebp        
            ret
    
    .set to_gen_inner_len, . - to_gen_inner
    
    .section .text
    
    main:
            pushl %ebp
            movl %esp, %ebp
    
            #allocate memory
            pushl $to_gen_inner_len
            call malloc
            popl %ecx
    
            pushl $to_gen_inner_len
            pushl $to_gen_inner
            pushl %eax
            call copy_bytes
            popl %eax
            popl %ecx
            popl %ecx
    
            call *%eax
    
            movl %ebp, %esp
            popl %ebp
            ret
    
    printfregs:
            .ascii "eax: %8X\nebx: %8X\necx: %8X\nedx: %8X\n\0"
    
    printregs:
            pushl %edx
            pushl %ecx
            pushl %ebx
            pushl %eax
            pushl $printfregs
            call printf
            popl %ecx
            popl %eax
            popl %ebx
            popl %ecx
            popl %edx
            ret
    
    copy_bytes: #dest source length
            pushl %ebp
            movl %esp, %ebp
    
            subl $24, %esp
    
            movl 8(%ebp), %ecx # dest
            movl %ecx, -4(%ebp)
    
            movl 12(%ebp), %ebx # source
            movl %ebx, -8(%ebp)
    
            movl 16(%ebp), %eax # length
            movl %eax, -12(%ebp)
    
            addl %eax, %ecx # last dest-byte
            movl %ecx, -16(%ebp)
    
            addl %eax, %edx # last source-byte
            movl %ecx, -20(%ebp)
    
            movl -4(%ebp), %eax
            movl -8(%ebp), %ebx
            movl -16(%ebp), %ecx
    
            copy_bytes_2:
            movb (%ebx), %dl
            movb %dl, (%eax)
            incl %eax
            incl %ebx
            cmp %eax, %ecx
            jne copy_bytes_2
    
            movl %ebp, %esp
            popl %ebp
            ret
    

    ...它在这里为我构建和运行。希望对您有所帮助。

    【讨论】:

    • 嗯。奇怪的。您的代码也可以调用 *%eax (我首先做的)。似乎这不是我的代码失败的主要原因。反正。非常感谢。
    • 啊 - 看起来像是 call *%eax vs call *(%eax) - 前者分支到 %eax 的地址,后者调用 %eax 指向的内存中包含的地址。将编辑答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多