最近在学堂在线上重温汇编语言课程,整理了用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

   

Logo

更多推荐