C可执行文件的生成包括如下步骤:预处理、编译、链接;下边分析编译和链接。
一、源程序
vi hello.c
#include <stdio.h>
int main()
{
printf("helloworld!\n");
return 0;
}
二、编译及连接
gcc -v hello.c -o hello
......
/usr/lib/gcc/x86_64-linux-gnu/4.4.6/cc1 -quiet -v -imultilib . -imultiarch x86_64-linux-gnu hello.c -D_FORTIFY_SOURCE=2 -quiet -dumpbase hello.c -mtune=generic -auxbase hello -version -fstack-protector -o /tmp/ccXepOpC.s
......
as -V -Qy -o /tmp/cc2TF8MY.o /tmp/ccXepOpC.s
......
/usr/lib/gcc/x86_64-linux-gnu/4.4.6/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello -z relro /usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../.. /tmp/cc2TF8MY.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.4.6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu/crtn.o
1.将C语言编译成汇编语言——cc1
/usr/lib/gcc/x86_64-linux-gnu/4.4.6/cc1 -quiet -v -imultilib . -imultiarch x86_64-linux-gnu hello.c -D_FORTIFY_SOURCE=2 -quiet -dumpbase hello.c -mtune=generic -auxbase hello -version -fstack-protector -o /tmp/ccXepOpC.s
查看:
vi /tmp/ccXepOpC.s
2.将汇编语言编译成目标文件——as
as -V -Qy -o /tmp/cc2TF8MY.o /tmp/ccXepOpC.s
查看:
readelf -a /tmp/cc2TF8MY.o >hello.o
vi hello.o
3.将目标文件连接成可执行文件——collect2
/usr/lib/gcc/x86_64-linux-gnu/4.4.6/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello -z relro /usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../.. /tmp/cc2TF8MY.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.4.6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.6/../../../x86_64-linux-gnu/crtn.o
查看:
readelf -a hello >hello.bin
vi hello.bin
三、说明
1.上述用到了gcc的cc1和collect2(实际是binutils的ld)、binutils的ar及ld连接器以及glibc的crt*.o(c runtime);所以,没有binutils和glibc的支持、gcc便无法运行。
2.crt*.o文件说明:c runtime;完成执行man函数之前的初始化以及man函数执行完后的扫尾工作。
3.ld默认连接脚本路径:isr/lib/ldscripts/elf32_x86_64.*。
4.elf文件的默认入口点是crt1.o中的_start,不是main:
原因如下,连接脚本决定入口函数:
vi sr/lib/ldscripts/elf32_x86_64.*
......
ENTRY(_start)
......
下面看gcc4.4.6中crt1.o的源码:
glibc-2.14.1/sysdeps/x86_64/elf/start.S
......
_start:
/* Clear the frame pointer. The ABI suggests this be done, to mark
the outermost frame obviously. */
xorl %ebp, %ebp
/* Extract the arguments as encoded on the stack and set up
the arguments for __libc_start_main (int (*main) (int, char **, char **),
int argc, char *argv,
void (*init) (void), void (*fini) (void),
void (*rtld_fini) (void), void *stack_end).
The arguments are passed via registers and on the stack:
main: %rdi
argc: %rsi
argv: %rdx
init: %rcx
fini: %r8
rtld_fini: %r9
stack_end: stack. */
movq %rdx, %r9 /* Address of the shared library termination
function. */
popq %rsi /* Pop the argument count. */
movq %rsp, %rdx /* argv starts just at the current stack top. */
/* Align the stack to a 16 byte boundary to follow the ABI. */
andq $~15, %rsp
pushq %rax /* Push garbage because we push 8 more bytes. */
/* Provide the highest stack address to the user code (for stacks
which grow downwards). */
pushq %rsp
#ifdef SHARED
/* Pass address of our own entry points to .fini and .init. */
movq __libc_csu_fini@GOTPCREL(%rip), %r8
movq __libc_csu_init@GOTPCREL(%rip), %rcx
movq BP_SYM (main)@GOTPCREL(%rip), %rdi
/* Call the user's main function, and exit with its value.
But let the libc call main. */
call BP_SYM (__libc_start_main)@PLT
......