C语言基本上大学都教过,但是很多人应该和我一样学习的时候还使用的是Windows平台,对于其中要用到的编译等知识都不了解。今天就针对这种情况来重新学习一遍C语言。
#include <stdio.h> main() { printf("hello world\n"); }
以上代码应该是最简单的C语言程序了,将上面的内容报错为hello.c,并通过gcc去编译它,具体使用到的指令为:
$ gcc -g -Wall hello.c -o hello
该命令将文件‘hello.c’中的代码编译为机器码并存储在可执行文件‘hello’中。机器码的文件名是通过 -o 选项指定的。该选项通常作为命令行中的最后一个参数。如果被省略,输出文件默认为 ‘a.out’。如果当前目录中与可执行文件重名的文件已经存在,它将被覆盖。
选项 -Wall 开启编译器几乎所有常用的警告。编译器有很多其他的警告选项,但 -Wall 是最常用的。默认情况下GCC 不会产生任何警告信息。当编写 C 或 C++ 程序时编译器警告非常有助于检测程序存在的问题。 注意如果有用到math.h库等非gcc默认调用的标准库,请使用-lm参数。
选项 “”-g”” 表示在生成的目标文件中带调试信息,调试信息可以在程序异常中止产生core后,帮助分析错误产生的源头,包括产生错误的文件名和行号等非常多有用的信息。
执行万上述指令后我们发现有报错,具体内容为:
hello.c:2:1: 警告: 返回类型默认为‘int’ [-Wreturn-type] hello.c: 在函数‘main’中: hello.c:5:1: 警告: 在有返回值的函数中,控制流程到达函数尾 [-Wreturn-type]
解决上诉问题的方法非常的简单,只要将代码修改为:
#include <stdio.h> int main() { printf("hello world\n"); return 0; }
不管采用上诉的哪种代码,在命令行直接执行编译出来的hello文件,即可看到输出的hello world。下面就来细讲整个编译的过程。
上图是一个hello的c程序由gcc编译器从源码文件hello.c中读取内容并将其翻译成为一个可执行的对象文件hello的过程。这个过程包含了几个阶段:
预处理过程
预处理(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c中的第一行的#include <stdio.h> 命令告诉预处理器读取系统头文件stdio.h的内容,插入到程序文本中。结果就得到里另一个C程序,通常是以*.i作为文件扩展名。
可通过执行如下指令获得:gcc -E hello.c -o hello.i
用记事本打开,可查看到如下的内容:
# 1 "hello.c" # 1 "<built-in>" # 1 "<命令行>" # 1 "hello.c" # 1 "/usr/include/stdio.h" 1 3 4 # 28 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/features.h" 1 3 4 # 324 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/predefs.h" 1 3 4 # 325 "/usr/include/features.h" 2 3 4 # 357 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4 # 378 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 379 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 358 "/usr/include/features.h" 2 3 4 # 389 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 5 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4 # 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4 # 390 "/usr/include/features.h" 2 3 4 # 29 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h" 1 3 4 # 212 "/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h" 3 4 typedef long unsigned int size_t; # 35 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/types.h" 1 3 4 # 28 "/usr/include/x86_64-linux-gnu/bits/types.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 29 "/usr/include/x86_64-linux-gnu/bits/types.h" 2 3 4 typedef unsigned char __u_char; typedef unsigned short int __u_short; typedef unsigned int __u_int; typedef unsigned long int __u_long; typedef signed char __int8_t; typedef unsigned char __uint8_t; typedef signed short int __int16_t; typedef unsigned short int __uint16_t; typedef signed int __int32_t; typedef unsigned int __uint32_t; typedef signed long int __int64_t; typedef unsigned long int __uint64_t; typedef long int __quad_t; typedef unsigned long int __u_quad_t; # 131 "/usr/include/x86_64-linux-gnu/bits/types.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/typesizes.h" 1 3 4 # 132 "/usr/include/x86_64-linux-gnu/bits/types.h" 2 3 4 typedef unsigned long int __dev_t; typedef unsigned int __uid_t; typedef unsigned int __gid_t; typedef unsigned long int __ino_t; typedef unsigned long int __ino64_t; typedef unsigned int __mode_t; typedef unsigned long int __nlink_t; typedef long int __off_t; typedef long int __off64_t; typedef int __pid_t; typedef struct { int __val[2]; } __fsid_t; typedef long int __clock_t; typedef unsigned long int __rlim_t; typedef unsigned long int __rlim64_t; typedef unsigned int __id_t; typedef long int __time_t; typedef unsigned int __useconds_t; typedef long int __suseconds_t; typedef int __daddr_t; typedef long int __swblk_t; typedef int __key_t; typedef int __clockid_t; typedef void * __timer_t; typedef long int __blksize_t; typedef long int __blkcnt_t; typedef long int __blkcnt64_t; typedef unsigned long int __fsblkcnt_t; typedef unsigned long int __fsblkcnt64_t; typedef unsigned long int __fsfilcnt_t; typedef unsigned long int __fsfilcnt64_t; typedef long int __ssize_t; typedef __off64_t __loff_t; typedef __quad_t *__qaddr_t; typedef char *__caddr_t; typedef long int __intptr_t; typedef unsigned int __socklen_t; # 37 "/usr/include/stdio.h" 2 3 4 # 45 "/usr/include/stdio.h" 3 4 struct _IO_FILE; typedef struct _IO_FILE FILE; # 65 "/usr/include/stdio.h" 3 4 typedef struct _IO_FILE __FILE; # 75 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/libio.h" 1 3 4 # 32 "/usr/include/libio.h" 3 4 # 1 "/usr/include/_G_config.h" 1 3 4 # 15 "/usr/include/_G_config.h" 3 4 # 1 "/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h" 1 3 4 # 16 "/usr/include/_G_config.h" 2 3 4 # 1 "/usr/include/wchar.h" 1 3 4 # 83 "/usr/include/wchar.h" 3 4 typedef struct { int __count; union { unsigned int __wch; char __wchb[4]; } __value; } __mbstate_t; # 21 "/usr/include/_G_config.h" 2 3 4 typedef struct { __off_t __pos; __mbstate_t __state; } _G_fpos_t; typedef struct { __off64_t __pos; __mbstate_t __state; } _G_fpos64_t; # 53 "/usr/include/_G_config.h" 3 4 typedef int _G_int16_t __attribute__ ((__mode__ (__HI__))); typedef int _G_int32_t __attribute__ ((__mode__ (__SI__))); typedef unsigned int _G_uint16_t __attribute__ ((__mode__ (__HI__))); typedef unsigned int _G_uint32_t __attribute__ ((__mode__ (__SI__))); # 33 "/usr/include/libio.h" 2 3 4 # 53 "/usr/include/libio.h" 3 4 # 1 "/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h" 1 3 4 # 40 "/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h" 3 4 typedef __builtin_va_list __gnuc_va_list; # 54 "/usr/include/libio.h" 2 3 4 # 172 "/usr/include/libio.h" 3 4 struct _IO_jump_t; struct _IO_FILE; # 182 "/usr/include/libio.h" 3 4 typedef void _IO_lock_t; struct _IO_marker { struct _IO_marker *_next; struct _IO_FILE *_sbuf; int _pos; # 205 "/usr/include/libio.h" 3 4 }; enum __codecvt_result { __codecvt_ok, __codecvt_partial, __codecvt_error, __codecvt_noconv }; # 273 "/usr/include/libio.h" 3 4 struct _IO_FILE { int _flags; char* _IO_read_ptr; char* _IO_read_end; char* _IO_read_base; char* _IO_write_base; char* _IO_write_ptr; char* _IO_write_end; char* _IO_buf_base; char* _IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; int _flags2; __off_t _old_offset; unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; _IO_lock_t *_lock; # 321 "/usr/include/libio.h" 3 4 __off64_t _offset; # 330 "/usr/include/libio.h" 3 4 void *__pad1; void *__pad2; void *__pad3; void *__pad4; size_t __pad5; int _mode; char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)]; }; typedef struct _IO_FILE _IO_FILE; struct _IO_FILE_plus; extern struct _IO_FILE_plus _IO_2_1_stdin_; extern struct _IO_FILE_plus _IO_2_1_stdout_; extern struct _IO_FILE_plus _IO_2_1_stderr_; # 366 "/usr/include/libio.h" 3 4 typedef __ssize_t __io_read_fn (void *__cookie, char *__buf, size_t __nbytes); typedef __ssize_t __io_write_fn (void *__cookie, __const char *__buf, size_t __n); typedef int __io_seek_fn (void *__cookie, __off64_t *__pos, int __w); typedef int __io_close_fn (void *__cookie); # 418 "/usr/include/libio.h" 3 4 extern int __underflow (_IO_FILE *); extern int __uflow (_IO_FILE *); extern int __overflow (_IO_FILE *, int); # 462 "/usr/include/libio.h" 3 4 extern int _IO_getc (_IO_FILE *__fp); extern int _IO_putc (int __c, _IO_FILE *__fp); extern int _IO_feof (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__)); extern int _IO_ferror (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__)); extern int _IO_peekc_locked (_IO_FILE *__fp); extern void _IO_flockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); extern void _IO_funlockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); extern int _IO_ftrylockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); # 492 "/usr/include/libio.h" 3 4 extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict, __gnuc_va_list, int *__restrict); extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict, __gnuc_va_list); extern __ssize_t _IO_padn (_IO_FILE *, int, __ssize_t); extern size_t _IO_sgetn (_IO_FILE *, void *, size_t); extern __off64_t _IO_seekoff (_IO_FILE *, __off64_t, int, int); extern __off64_t _IO_seekpos (_IO_FILE *, __off64_t, int); extern void _IO_free_backup_area (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); # 76 "/usr/include/stdio.h" 2 3 4 typedef __gnuc_va_list va_list; # 91 "/usr/include/stdio.h" 3 4 typedef __off_t off_t; # 103 "/usr/include/stdio.h" 3 4 typedef __ssize_t ssize_t; typedef _G_fpos_t fpos_t; # 165 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/stdio_lim.h" 1 3 4 # 166 "/usr/include/stdio.h" 2 3 4 extern struct _IO_FILE *stdin; extern struct _IO_FILE *stdout; extern struct _IO_FILE *stderr; extern int remove (__const char *__filename) __attribute__ ((__nothrow__ , __leaf__)); extern int rename (__const char *__old, __const char *__new) __attribute__ ((__nothrow__ , __leaf__)); extern int renameat (int __oldfd, __const char *__old, int __newfd, __const char *__new) __attribute__ ((__nothrow__ , __leaf__)); extern FILE *tmpfile (void) ; # 210 "/usr/include/stdio.h" 3 4 extern char *tmpnam (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ; extern char *tmpnam_r (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ; # 228 "/usr/include/stdio.h" 3 4 extern char *tempnam (__const char *__dir, __const char *__pfx) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ; extern int fclose (FILE *__stream); extern int fflush (FILE *__stream); # 253 "/usr/include/stdio.h" 3 4 extern int fflush_unlocked (FILE *__stream); # 267 "/usr/include/stdio.h" 3 4 extern FILE *fopen (__const char *__restrict __filename, __const char *__restrict __modes) ; extern FILE *freopen (__const char *__restrict __filename, __const char *__restrict __modes, FILE *__restrict __stream) ; # 296 "/usr/include/stdio.h" 3 4 # 307 "/usr/include/stdio.h" 3 4 extern FILE *fdopen (int __fd, __const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ; # 320 "/usr/include/stdio.h" 3 4 extern FILE *fmemopen (void *__s, size_t __len, __const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ; extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __attribute__ ((__nothrow__ , __leaf__)) ; extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)); extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf, int __modes, size_t __n) __attribute__ ((__nothrow__ , __leaf__)); extern void setbuffer (FILE *__restrict __stream, char *__restrict __buf, size_t __size) __attribute__ ((__nothrow__ , __leaf__)); extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int fprintf (FILE *__restrict __stream, __const char *__restrict __format, ...); extern int printf (__const char *__restrict __format, ...); extern int sprintf (char *__restrict __s, __const char *__restrict __format, ...) __attribute__ ((__nothrow__)); extern int vfprintf (FILE *__restrict __s, __const char *__restrict __format, __gnuc_va_list __arg); extern int vprintf (__const char *__restrict __format, __gnuc_va_list __arg); extern int vsprintf (char *__restrict __s, __const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__nothrow__)); extern int snprintf (char *__restrict __s, size_t __maxlen, __const char *__restrict __format, ...) __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 4))); extern int vsnprintf (char *__restrict __s, size_t __maxlen, __const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 0))); # 418 "/usr/include/stdio.h" 3 4 extern int vdprintf (int __fd, __const char *__restrict __fmt, __gnuc_va_list __arg) __attribute__ ((__format__ (__printf__, 2, 0))); extern int dprintf (int __fd, __const char *__restrict __fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); extern int fscanf (FILE *__restrict __stream, __const char *__restrict __format, ...) ; extern int scanf (__const char *__restrict __format, ...) ; extern int sscanf (__const char *__restrict __s, __const char *__restrict __format, ...) __attribute__ ((__nothrow__ , __leaf__)); # 449 "/usr/include/stdio.h" 3 4 extern int fscanf (FILE *__restrict __stream, __const char *__restrict __format, ...) __asm__ ("" "__isoc99_fscanf") ; extern int scanf (__const char *__restrict __format, ...) __asm__ ("" "__isoc99_scanf") ; extern int sscanf (__const char *__restrict __s, __const char *__restrict __format, ...) __asm__ ("" "__isoc99_sscanf") __attribute__ ((__nothrow__ , __leaf__)) ; # 469 "/usr/include/stdio.h" 3 4 extern int vfscanf (FILE *__restrict __s, __const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__format__ (__scanf__, 2, 0))) ; extern int vscanf (__const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__format__ (__scanf__, 1, 0))) ; extern int vsscanf (__const char *__restrict __s, __const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0))); # 500 "/usr/include/stdio.h" 3 4 extern int vfscanf (FILE *__restrict __s, __const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vfscanf") __attribute__ ((__format__ (__scanf__, 2, 0))) ; extern int vscanf (__const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vscanf") __attribute__ ((__format__ (__scanf__, 1, 0))) ; extern int vsscanf (__const char *__restrict __s, __const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vsscanf") __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0))); # 528 "/usr/include/stdio.h" 3 4 extern int fgetc (FILE *__stream); extern int getc (FILE *__stream); extern int getchar (void); # 556 "/usr/include/stdio.h" 3 4 extern int getc_unlocked (FILE *__stream); extern int getchar_unlocked (void); # 567 "/usr/include/stdio.h" 3 4 extern int fgetc_unlocked (FILE *__stream); extern int fputc (int __c, FILE *__stream); extern int putc (int __c, FILE *__stream); extern int putchar (int __c); # 600 "/usr/include/stdio.h" 3 4 extern int fputc_unlocked (int __c, FILE *__stream); extern int putc_unlocked (int __c, FILE *__stream); extern int putchar_unlocked (int __c); extern int getw (FILE *__stream); extern int putw (int __w, FILE *__stream); extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream) ; extern char *gets (char *__s) ; # 662 "/usr/include/stdio.h" 3 4 extern __ssize_t __getdelim (char **__restrict __lineptr, size_t *__restrict __n, int __delimiter, FILE *__restrict __stream) ; extern __ssize_t getdelim (char **__restrict __lineptr, size_t *__restrict __n, int __delimiter, FILE *__restrict __stream) ; extern __ssize_t getline (char **__restrict __lineptr, size_t *__restrict __n, FILE *__restrict __stream) ; extern int fputs (__const char *__restrict __s, FILE *__restrict __stream); extern int puts (__const char *__s); extern int ungetc (int __c, FILE *__stream); extern size_t fread (void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream) ; extern size_t fwrite (__const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s); # 734 "/usr/include/stdio.h" 3 4 extern size_t fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream) ; extern size_t fwrite_unlocked (__const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream); extern int fseek (FILE *__stream, long int __off, int __whence); extern long int ftell (FILE *__stream) ; extern void rewind (FILE *__stream); # 770 "/usr/include/stdio.h" 3 4 extern int fseeko (FILE *__stream, __off_t __off, int __whence); extern __off_t ftello (FILE *__stream) ; # 789 "/usr/include/stdio.h" 3 4 extern int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos); extern int fsetpos (FILE *__stream, __const fpos_t *__pos); # 812 "/usr/include/stdio.h" 3 4 # 821 "/usr/include/stdio.h" 3 4 extern void clearerr (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int feof (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern int ferror (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void clearerr_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int feof_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern int ferror_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void perror (__const char *__s); # 1 "/usr/include/x86_64-linux-gnu/bits/sys_errlist.h" 1 3 4 # 27 "/usr/include/x86_64-linux-gnu/bits/sys_errlist.h" 3 4 extern int sys_nerr; extern __const char *__const sys_errlist[]; # 851 "/usr/include/stdio.h" 2 3 4 extern int fileno (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; # 870 "/usr/include/stdio.h" 3 4 extern FILE *popen (__const char *__command, __const char *__modes) ; extern int pclose (FILE *__stream); extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__)); # 910 "/usr/include/stdio.h" 3 4 extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); # 940 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 int main() { printf("hello world\n"); return 0; }
以上部分代码可以看出除了#include <stdio.h>指令之外其他指令并未被改变。
编译阶段
编译器将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文件格式确切地描述了一条低级机器语言指令。
可通过如下指令获得:gcc -S hello.c -o hello.s
用记事本打开,可查看到如下的内容:
.file "hello.c" .section .rodata .LC0: .string "hello world" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %edi call puts movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits
汇编阶段
汇编器将hello.s翻译成机器语言指令,把这些指令打包成一种可重定位目标程序的格式,并把结果保存在hello.o中,hello.o是一个二进制文件,它的字节编码是机器语言指令,而不是字符。
可通过如下指令获得:gcc -c hello.c
,最终生成的hello.o文件需要使用objdump打开,具体指令为:objdump -d hello.o
获取的内容为:
hello.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <main+0xe> e: b8 00 00 00 00 mov $0x0,%eax 13: 5d pop %rbp 14: c3 retq
链接阶段
hello.c程序中调用了printf函数,而printf函数存在于一个名为printf.o的单独的预编译好的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中。连接器就负责处理这种合并,结果就得到hello文件,它是一个可执行文件,可以被加载到内存中由系统执行。
使用的指令为:gcc hello.o -o hello
,最终生成了一个hello文件,同样此hello文件可用通过objdump打开:objdump -d hello
打开后的内容为:
hello: file format elf64-x86-64 Disassembly of section .init: 00000000004003c8 <_init>: 4003c8: 48 83 ec 08 sub $0x8,%rsp 4003cc: e8 6b 00 00 00 callq 40043c <call_gmon_start> 4003d1: e8 fa 00 00 00 callq 4004d0 <frame_dummy> 4003d6: e8 d5 01 00 00 callq 4005b0 <__do_global_ctors_aux> 4003db: 48 83 c4 08 add $0x8,%rsp 4003df: c3 retq Disassembly of section .plt: 00000000004003e0 <puts@plt-0x10>: 4003e0: ff 35 0a 0c 20 00 pushq 0x200c0a(%rip) # 600ff0 <_GLOBAL_OFFSET_TABLE_+0x8> 4003e6: ff 25 0c 0c 20 00 jmpq *0x200c0c(%rip) # 600ff8 <_GLOBAL_OFFSET_TABLE_+0x10> 4003ec: 0f 1f 40 00 nopl 0x0(%rax) 00000000004003f0 <puts@plt>: 4003f0: ff 25 0a 0c 20 00 jmpq *0x200c0a(%rip) # 601000 <_GLOBAL_OFFSET_TABLE_+0x18> 4003f6: 68 00 00 00 00 pushq $0x0 4003fb: e9 e0 ff ff ff jmpq 4003e0 <_init+0x18> 0000000000400400 <__libc_start_main@plt>: 400400: ff 25 02 0c 20 00 jmpq *0x200c02(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x20> 400406: 68 01 00 00 00 pushq $0x1 40040b: e9 d0 ff ff ff jmpq 4003e0 <_init+0x18> Disassembly of section .text: 0000000000400410 <_start>: 400410: 31 ed xor %ebp,%ebp 400412: 49 89 d1 mov %rdx,%r9 400415: 5e pop %rsi 400416: 48 89 e2 mov %rsp,%rdx 400419: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 40041d: 50 push %rax 40041e: 54 push %rsp 40041f: 49 c7 c0 a0 05 40 00 mov $0x4005a0,%r8 400426: 48 c7 c1 10 05 40 00 mov $0x400510,%rcx 40042d: 48 c7 c7 f4 04 40 00 mov $0x4004f4,%rdi 400434: e8 c7 ff ff ff callq 400400 <__libc_start_main@plt> 400439: f4 hlt 40043a: 90 nop 40043b: 90 nop 000000000040043c <call_gmon_start>: 40043c: 48 83 ec 08 sub $0x8,%rsp 400440: 48 8b 05 99 0b 20 00 mov 0x200b99(%rip),%rax # 600fe0 <_DYNAMIC+0x190> 400447: 48 85 c0 test %rax,%rax 40044a: 74 02 je 40044e <call_gmon_start+0x12> 40044c: ff d0 callq *%rax 40044e: 48 83 c4 08 add $0x8,%rsp 400452: c3 retq 400453: 90 nop 400454: 90 nop 400455: 90 nop 400456: 90 nop 400457: 90 nop 400458: 90 nop 400459: 90 nop 40045a: 90 nop 40045b: 90 nop 40045c: 90 nop 40045d: 90 nop 40045e: 90 nop 40045f: 90 nop 0000000000400460 <__do_global_dtors_aux>: 400460: 55 push %rbp 400461: 48 89 e5 mov %rsp,%rbp 400464: 53 push %rbx 400465: 48 83 ec 08 sub $0x8,%rsp 400469: 80 3d b0 0b 20 00 00 cmpb $0x0,0x200bb0(%rip) # 601020 <__bss_start> 400470: 75 4b jne 4004bd <__do_global_dtors_aux+0x5d> 400472: bb 40 0e 60 00 mov $0x600e40,%ebx 400477: 48 8b 05 aa 0b 20 00 mov 0x200baa(%rip),%rax # 601028 <dtor_idx.6533> 40047e: 48 81 eb 38 0e 60 00 sub $0x600e38,%rbx 400485: 48 c1 fb 03 sar $0x3,%rbx 400489: 48 83 eb 01 sub $0x1,%rbx 40048d: 48 39 d8 cmp %rbx,%rax 400490: 73 24 jae 4004b6 <__do_global_dtors_aux+0x56> 400492: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 400498: 48 83 c0 01 add $0x1,%rax 40049c: 48 89 05 85 0b 20 00 mov %rax,0x200b85(%rip) # 601028 <dtor_idx.6533> 4004a3: ff 14 c5 38 0e 60 00 callq *0x600e38(,%rax,8) 4004aa: 48 8b 05 77 0b 20 00 mov 0x200b77(%rip),%rax # 601028 <dtor_idx.6533> 4004b1: 48 39 d8 cmp %rbx,%rax 4004b4: 72 e2 jb 400498 <__do_global_dtors_aux+0x38> 4004b6: c6 05 63 0b 20 00 01 movb $0x1,0x200b63(%rip) # 601020 <__bss_start> 4004bd: 48 83 c4 08 add $0x8,%rsp 4004c1: 5b pop %rbx 4004c2: 5d pop %rbp 4004c3: c3 retq 4004c4: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1) 4004cb: 00 00 00 00 00 00000000004004d0 <frame_dummy>: 4004d0: 48 83 3d 70 09 20 00 cmpq $0x0,0x200970(%rip) # 600e48 <__JCR_END__> 4004d7: 00 4004d8: 55 push %rbp 4004d9: 48 89 e5 mov %rsp,%rbp 4004dc: 74 12 je 4004f0 <frame_dummy+0x20> 4004de: b8 00 00 00 00 mov $0x0,%eax 4004e3: 48 85 c0 test %rax,%rax 4004e6: 74 08 je 4004f0 <frame_dummy+0x20> 4004e8: 5d pop %rbp 4004e9: bf 48 0e 60 00 mov $0x600e48,%edi 4004ee: ff e0 jmpq *%rax 4004f0: 5d pop %rbp 4004f1: c3 retq 4004f2: 90 nop 4004f3: 90 nop 00000000004004f4 <main>: 4004f4: 55 push %rbp 4004f5: 48 89 e5 mov %rsp,%rbp 4004f8: bf fc 05 40 00 mov $0x4005fc,%edi 4004fd: e8 ee fe ff ff callq 4003f0 <puts@plt> 400502: b8 00 00 00 00 mov $0x0,%eax 400507: 5d pop %rbp 400508: c3 retq 400509: 90 nop 40050a: 90 nop 40050b: 90 nop 40050c: 90 nop 40050d: 90 nop 40050e: 90 nop 40050f: 90 nop 0000000000400510 <__libc_csu_init>: 400510: 48 89 6c 24 d8 mov %rbp,-0x28(%rsp) 400515: 4c 89 64 24 e0 mov %r12,-0x20(%rsp) 40051a: 48 8d 2d 03 09 20 00 lea 0x200903(%rip),%rbp # 600e24 <__init_array_end> 400521: 4c 8d 25 fc 08 20 00 lea 0x2008fc(%rip),%r12 # 600e24 <__init_array_end> 400528: 4c 89 6c 24 e8 mov %r13,-0x18(%rsp) 40052d: 4c 89 74 24 f0 mov %r14,-0x10(%rsp) 400532: 4c 89 7c 24 f8 mov %r15,-0x8(%rsp) 400537: 48 89 5c 24 d0 mov %rbx,-0x30(%rsp) 40053c: 48 83 ec 38 sub $0x38,%rsp 400540: 4c 29 e5 sub %r12,%rbp 400543: 41 89 fd mov %edi,%r13d 400546: 49 89 f6 mov %rsi,%r14 400549: 48 c1 fd 03 sar $0x3,%rbp 40054d: 49 89 d7 mov %rdx,%r15 400550: e8 73 fe ff ff callq 4003c8 <_init> 400555: 48 85 ed test %rbp,%rbp 400558: 74 1c je 400576 <__libc_csu_init+0x66> 40055a: 31 db xor %ebx,%ebx 40055c: 0f 1f 40 00 nopl 0x0(%rax) 400560: 4c 89 fa mov %r15,%rdx 400563: 4c 89 f6 mov %r14,%rsi 400566: 44 89 ef mov %r13d,%edi 400569: 41 ff 14 dc callq *(%r12,%rbx,8) 40056d: 48 83 c3 01 add $0x1,%rbx 400571: 48 39 eb cmp %rbp,%rbx 400574: 75 ea jne 400560 <__libc_csu_init+0x50> 400576: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx 40057b: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp 400580: 4c 8b 64 24 18 mov 0x18(%rsp),%r12 400585: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13 40058a: 4c 8b 74 24 28 mov 0x28(%rsp),%r14 40058f: 4c 8b 7c 24 30 mov 0x30(%rsp),%r15 400594: 48 83 c4 38 add $0x38,%rsp 400598: c3 retq 400599: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 00000000004005a0 <__libc_csu_fini>: 4005a0: f3 c3 repz retq 4005a2: 90 nop 4005a3: 90 nop 4005a4: 90 nop 4005a5: 90 nop 4005a6: 90 nop 4005a7: 90 nop 4005a8: 90 nop 4005a9: 90 nop 4005aa: 90 nop 4005ab: 90 nop 4005ac: 90 nop 4005ad: 90 nop 4005ae: 90 nop 4005af: 90 nop 00000000004005b0 <__do_global_ctors_aux>: 4005b0: 55 push %rbp 4005b1: 48 89 e5 mov %rsp,%rbp 4005b4: 53 push %rbx 4005b5: 48 83 ec 08 sub $0x8,%rsp 4005b9: 48 8b 05 68 08 20 00 mov 0x200868(%rip),%rax # 600e28 <__CTOR_LIST__> 4005c0: 48 83 f8 ff cmp $0xffffffffffffffff,%rax 4005c4: 74 19 je 4005df <__do_global_ctors_aux+0x2f> 4005c6: bb 28 0e 60 00 mov $0x600e28,%ebx 4005cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 4005d0: 48 83 eb 08 sub $0x8,%rbx 4005d4: ff d0 callq *%rax 4005d6: 48 8b 03 mov (%rbx),%rax 4005d9: 48 83 f8 ff cmp $0xffffffffffffffff,%rax 4005dd: 75 f1 jne 4005d0 <__do_global_ctors_aux+0x20> 4005df: 48 83 c4 08 add $0x8,%rsp 4005e3: 5b pop %rbx 4005e4: 5d pop %rbp 4005e5: c3 retq 4005e6: 90 nop 4005e7: 90 nop Disassembly of section .fini: 00000000004005e8 <_fini>: 4005e8: 48 83 ec 08 sub $0x8,%rsp 4005ec: e8 6f fe ff ff callq 400460 <__do_global_dtors_aux> 4005f1: 48 83 c4 08 add $0x8,%rsp 4005f5: c3 retq
经过上面四个过程,我们就可以把一个源代码文件编译成机器能运行的可执行文件。这个可执行文件刚开始是保存在磁盘上,当计算机要运行这个程序的时候,hello就被加载到内存中,接着程序指令被不断复制到寄存器中由CPU来执行,最后把”hello world”从寄存器中打到显示设备上。这就是hello程序整个执行过程。