天天看點

編譯并連接配接從helloworld.c生成的彙編代碼的方法步驟

    最近在學堂線上上重溫彙編語言課程,整理了用gcc将helloworld.c程式生成彙編代碼,并将彙編代碼進行編譯和連接配接生成可執行ELF檔案的過程。方法和操作步驟記錄如下以做備忘,同時也供各位感興趣的同學學習參考。

    整個過程是在ubuntu1604下進行的。(uname -srvio: Linux 4.4.0-83-generic #106-Ubuntu SMP Mon Jun 26 17:54:43 UTC 2017 x86_64 GNU/Linux)

0. 用c編寫hello world程式helloworld.c helloworld.c内容如下: ---------------

#include <stdlib.h>      
#include <stdio.h>      
int add(int x, int y) {      
        return x + y;      
}      
void main() {      
        char *msg = "hello world \n";      
        printf("%s", msg);      
        int x = 12;      
        int y = 8;      
        printf("%d + %d = %d \n", x, y, add(x, y));      
        return;      
}      

-------- 1. 通過gcc -S 指令輸出asm源檔案helloworld64.s $ gcc -S helloworld.c -o helloworld64.s

helloworld64.s --------------

        .file   "helloworld.c"
        .text
        .globl  add
        .type   add, @function
add:
.LFB2:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    -4(%rbp), %edx
        movl    -8(%rbp), %eax
        addl    %edx, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE2:
        .size   add, .-add
        .section        .rodata
.LC0:
        .string "hello world \n"
.LC1:
        .string "%s"
.LC2:
        .string "%d + %d = %d \n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB3:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movq    $.LC0, -8(%rbp)
        movq    -8(%rbp), %rax
        movq    %rax, %rsi
        movl    $.LC1, %edi
        movl    $0, %eax
        call    printf
        movl    $12, -16(%rbp)
        movl    $8, -12(%rbp)
        movl    -12(%rbp), %edx
        movl    -16(%rbp), %eax
        movl    %edx, %esi
        movl    %eax, %edi
        call    add
        movl    %eax, %ecx
        movl    -12(%rbp), %edx
        movl    -16(%rbp), %eax
        movl    %eax, %esi
        movl    $.LC2, %edi
        movl    $0, %eax
        call    printf
        nop
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE3:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
        .section        .note.GNU-stack,"",@progbits

      

------------

2. 通過as指令編譯成目标檔案helloworld64.o $ as helloworld64.s -o helloworld64.o

3. 通過ld指令進行連接配接,生成可執行檔案helloworld64 $ ld -m elf_x86_64 -dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 "helloworld64.o" /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -lc -o "helloworld64"

注:除需連接配接libc庫(-lc)外,還需連結c的運作時庫crt*。如果版本不一樣,可通過locate指令查找到相應的庫檔案,并把目錄替換。如果不指定連接配接crt庫,如$ ld -o helloworld64  helloworld64.o -lc 則會報以下錯誤:

ld: 警告: 無法找到項目符号 _start; 預設為 0000000000400270

這是因為編譯程式的預設起始執行位址(啟動函數)為_start,此函數的crt庫中。雖然可以通過-e選項進行修改,但c庫的初始化等都需要crt中先執行。

4.執行helloworld64 $ ./helloworld64

輸出結果如下:

----- hello world 12 + 8 = 20

-----

對于在64位系統下生成32位程式,需指定32參數,并安裝32位的庫

$ sudo apt-get install g++-multilib

1. 通過gcc -S 指令輸出asm源檔案helloworld32.s,使用-m32選項訓示生成32位asm代碼 $ gcc -m32 -S helloworld.c -o helloworld32.s 生成的 helloworld32.s内容如下:

-----

        .file   "helloworld.c"
        .text
        .globl  add
        .type   add, @function
add:
.LFB2:
        .cfi_startproc
        pushl   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        movl    8(%ebp), %edx
        movl    12(%ebp), %eax
        addl    %edx, %eax
        popl    %ebp
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE2:
        .size   add, .-add
        .section        .rodata
.LC0:
        .string "hello world \n"
.LC1:
        .string "%s"
.LC2:
        .string "%d + %d = %d \n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB3:
        .cfi_startproc
        leal    4(%esp), %ecx
        .cfi_def_cfa 1, 0
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        .cfi_escape 0x10,0x5,0x2,0x75,0
        movl    %esp, %ebp
        pushl   %ecx
        .cfi_escape 0xf,0x3,0x75,0x7c,0x6
        subl    $20, %esp
        movl    $.LC0, -20(%ebp)
        subl    $8, %esp
        pushl   -20(%ebp)
        pushl   $.LC1
        call    printf

        addl    $16, %esp
        movl    $12, -16(%ebp)
        movl    $8, -12(%ebp)
        subl    $8, %esp
        pushl   -12(%ebp)
        pushl   -16(%ebp)
        call    add
        addl    $16, %esp
        pushl   %eax
        pushl   -12(%ebp)
        pushl   -16(%ebp)
        pushl   $.LC2
        call    printf
        addl    $16, %esp
        nop
        movl    -4(%ebp), %ecx
        .cfi_def_cfa 1, 0
        leave
        .cfi_restore 5
        leal    -4(%ecx), %esp
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE3:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
        .section        .note.GNU-stack,"",@progbits

      

----- 2. 通過as指令編譯成目标檔案helloworld32.o,通過--32選項指定32位模式 $ as --32 helloworld32.s -o helloworld32.o

3. 通過ld指令進行連接配接,生成可執行檔案helloworld32,使用-melf_i386指定生成32位ELF可執行檔案,并連接配接32位的crt運作時庫等。 $ ld -melf_i386 -dynamic-linker /lib32/ld-linux.so.2 "helloworld32.o" /usr/lib32/crt1.o /usr/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/32/crtbegin.o /usr/lib/gcc/x86_64-linux-gnu/5/32/crtend.o /usr/lib32/crtn.o -lc -o "helloworld32"

注:除需連接配接libc庫(-lc)外,還需連結c的運作時庫crt*。如果版本不一樣,可通過locate指令查找到相應的庫檔案,并把目錄替換。

4.執行helloworld32 $ ./helloworld32

輸出結果如下:

----- hello world 12 + 8 = 20