最近在學堂線上上重溫彙編語言課程,整理了用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