编译并连接从helloworld.c生成的汇编代码的方法步骤

来源:互联网 发布:java zip 解压 编辑:程序博客网 时间:2024/05/16 08:39

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