簡體   English   中英

使用 GCC 將 C 編譯為 32 位程序集與書籍不匹配

[英]Compiling C to 32-bit assembly with GCC doesn't match a book

我一直在嘗試將此 C 程序編譯為匯編程序,但一直無法正常工作。

我正在為初學者閱讀 Dennis Yurichev 逆向工程,但我沒有得到相同的輸出。 它是一個簡單的 hello world 語句。 我正在嘗試獲取 32 位輸出

#include <stdio.h>
int main()
{
     printf("hello, world\n");
     return 0;
}

這就是這本書所說的輸出應該是什么

   main proc near
    var_10 = dword ptr -10h
    push ebp
    mov ebp, esp
    and esp, 0FFFFFFF0h
    sub esp, 10h
    mov eax, offset aHelloWorld ; "hello, world\n"
    mov [esp+10h+var_10], eax
    call _printf
    mov eax, 0
    leave
    retn
    main endp

以下是步驟;

  1. 將打印語句編譯為 32 位(我目前正在運行 64 位電腦)

    • gcc -m32 hello_world.c -o hello_world
  2. 使用gdb反匯編

    • .gdb 文件
    • 設置反匯編-風味英特爾
    • 設置架構 i386:intel disassemble main

我明白了;


    lea    ecx,[esp+0x4]
    and    esp,0xfffffff0
    push   DWORD PTR [ecx-0x4]
    push   ebp
    mov    ebp,esp
    push   ebx
    push   ecx
    call   0x565561d5 <__x86.get_pc_thunk.ax>
    add    eax,0x2e53
    sub    esp,0xc
    lea    edx,[eax-0x1ff8]
    push   edx
    mov    ebx,eax
    call   0x56556030 <puts@plt>
    add    esp,0x10
    mov    eax,0x0
    lea    esp,[ebp-0x8]
    pop    ecx
    pop    ebx
    pop    ebp
    lea    esp,[ecx-0x4]
    ret 

我也用過

objdump -D -M i386,intel hello_world> hello_world.txt

ndisasm -b32 hello_world > hello_world.txt

但這些都不起作用。 我只是無法弄清楚出了什么問題。 我需要幫助。 看着你 Peter Cordes ^^

這本書的輸出看起來像 MSVC,而不是 GCC。 GCC 絕對不會發出main proc因為那是 MASM 語法,而不是有效的 GAS 語法。 它不會做像var_10 = dword ptr -10h這樣的事情。
(即使這樣做,您也不會在反匯編中看到匯編時常量定義,只會在編譯器的 asm 輸出中看到這本書建議您查看的內容gcc -S -masm=intel輸出。 如何消除“噪音” “來自 GCC/clang 程序集輸出?

因此存在很多差異,因為您使用的是不同的編譯器。 即使是現代版本的 MSVC(在 Godbolt 編譯器資源管理器上)也會使 asm 有所不同,例如不必費心將 ESP 對齊 16,也許是因為更現代的 Windows 版本或 CRT 啟動代碼已經這樣做了?


此外,默認情況下,您的 GCC 正在制作 PIE 可執行文件,因此請使用-fno-pie -no-pie 32 位 PIE 在效率和易於理解方面很糟糕。 請參閱如何擺脫呼叫 __x86.get_pc_thunk.ax 在 x86-64 Linux 中也不再允許 32 位絕對地址?有關 PIE 可執行文件的更多信息,主要集中在 64 位代碼上)

main 的序言中額外笨重的堆棧對齊是 GCC8 為不需要alloca函數優化的東西。 但是,當您不啟用優化時,即使當前的 GCC10 似乎也會發出完整的未優化版本 :(。 為什么 gcc 生成額外的返回地址?試圖了解 gcc 在復制返回的 main 頂部的復雜堆棧對齊地址

將 printf 優化為 puts:請參閱如何讓 gcc 編譯器不優化像 printf 這樣的標准庫函數調用? 並且-O2 將 printf("%s\\n", str) 優化為 puts(str) gcc -fno-builtin-printf將是避免這種情況發生的一種方法,或者只是習慣它。 GCC 甚至在-O0處也進行了一些優化,而其他編譯器僅在更高的優化級別進行。


MSVC 19.10 像這樣編譯您的函數(在 Godbolt 編譯器資源管理器上)並禁用優化(默認情況下,沒有編譯器選項)。

_main   PROC
        push    ebp
        mov     ebp, esp
        push    OFFSET $SG4501
        call    _printf
        add     esp, 4
        xor     eax, eax
        pop     ebp
        ret     0
_main   ENDP

_DATA   SEGMENT
$SG4501 DB        'hello, world', 0aH, 00H

GCC10.2 在序言中仍然使用過於復雜的堆棧對齊方式。

.LC0:
        .string "hello, world"
main:
        lea     ecx, [esp+4]
        and     esp, -16
        push    DWORD PTR [ecx-4]
        push    ebp
        mov     ebp, esp
        push    ecx
        sub     esp, 4
# end of function prologue, I think.
        sub     esp, 12                  # make sure arg will be 16-byte aligned
        push    OFFSET FLAT:.LC0         # push a pointer
        call    puts
        add     esp, 16                  # pop the arg-passing space
        mov     eax, 0                   # return 0

        mov     ecx, DWORD PTR [ebp-4]   # undo stack alignment.
        leave
        lea     esp, [ecx-4]
        ret

是的,這是非常低效的。 如果你調用你的函數除了main之外的任何東西,它已經假設 ESP 在函數入口對齊 16:

# GCC10.2 -m32 -O0
.LC0:
        .string "hello, world"
foo:
        push    ebp
        mov     ebp, esp
        sub     esp, 8            # reach a 16-byte boundary, assuming ESP%16 = 12 on entry
#
        sub     esp, 12                   
        push    OFFSET FLAT:.LC0
        call    puts
        add     esp, 16
        mov     eax, 0
        leave
        ret

所以它仍然沒有結合這兩sub指令,但你確實告訴它不要優化,所以需要腦殘代碼。 請參閱為什么 clang 使用 -O0 產生效率低下的 asm(對於這個簡單的浮點和)? 例如。

我的 GCC 會非常熱切地將對printf的調用交換為puts 我沒有設法找到使編譯器不執行此操作的命令行選項。 即程序具有相同的外部行為,但機器代碼是

#include <stdio.h>
int main(void)
{
     puts("hello, world");
}

因此,您將很難嘗試獲得與書中完全相同的程序集,因為該書中的程序集調用printf而不是puts

首先你編譯而不是反編譯。

在沒有優化的情況下編譯時會產生很多噪音。 如果您使用優化進行編譯,您將獲得與您擁有的代碼幾乎相同的更小代碼(為了防止從 printf 更改為 puts,您需要刪除'\\n' https://godbolt.org/z/cs4qe9 ):

.LC0:
        .string "hello, world"
main:
        lea     ecx, [esp+4]
        and     esp, -16
        push    DWORD PTR [ecx-4]
        push    ebp
        mov     ebp, esp
        push    ecx
        sub     esp, 16
        push    OFFSET FLAT:.LC0
        call    puts
        mov     ecx, DWORD PTR [ebp-4]
        add     esp, 16
        xor     eax, eax
        leave
        lea     esp, [ecx-4]
        ret

https://godbolt.org/z/xMqo33

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM