c01_由一个hello.c源文件到生成hello可执行文件的有趣过程。

对于c语言,总是想搞搞,以前编写c语言都是在IDE中一个build一个run就看结果,虽然对预处理汇编之类的中间过程也有过了解,但时间一久就忘了,so,今天就写下个笔记,来看看由一个hello.c源文件到产生一个可执行文件的整个过程。本例子在linux平台下运行:

写个hello.c源文件吧.

#include<stdio.h>
int main()

{

  printf("hello,world\n");

  return 0;

}

ok,hello.c源文件写完。

一个.c源文件到产生目标文件要经历4个步骤,分别是:预处理阶段 -> 编译阶段 -> 汇编阶段 -> 链接阶段。

(1)预处理阶段(加入相应库,宏替换等)

gcc hello.c -E > e.txt  ( >号是重定向,把预处理的结果输入到e.txt文件中 )

预处理阶段是用预处理器处理的,它只是简单的把stdio.h库简单的加入到文件中,查看e.txt文件如下:

...前面省略很多

extern char *ctermid (char *__s) __attribute__ ((__nothrow__));
# 908 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__));

extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__)) ;

extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__));
# 938 "/usr/include/stdio.h" 3 4

# 2 "hello.c" 2
int main()
{
printf("hello,world\n");

return 0;
}

可以看到,在这里预处理阶段只是把相应的库文件添加到程序中。

(2)编译阶段(生成汇编程序)

gcc hello.c -S   这里hello.c经过预处理和编译,生成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
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)"
.section .note.GNU-stack,"",@progbits

(3)汇编阶段(把汇编语言程序汇编成机器语言指令,并使用可重定位目标程序的格式来打包指令)

gcc hello.c -c   运行会把hello.c文件通过预处理,编译,汇编三个阶段,生成.o文件,hello.o文件是一个二进制文件不是文本文件,在这个阶段汇编器会把第二阶段生成的hello.s文件翻译成机器语言指令,并把这些指令打包成一种叫做可重定位目标程序的格式,然后将结果存放在hello.o文件中,使用vim -b hello.o查看可以发现都是乱码,其中一行比较醒目如下:

^@UH<89><e5><bf>^@^@^@^@<e8>^@^@^@^@<b8>^@^@^@^@<c9><c3>^@^@^@hello,world^@^@GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^Ax^P^A

看到hello,world字符,就是我们要打印的字符。

(4)链接阶段(合并各个打包好的机器语言指令,生成可执行二进制文件)

gcc hello.o  -o hello 

在这个阶段,链接器把printf.o文件和hello.o文件合并在一起,生成一个可执行文件hello,通过运行:./hello

结果输入如下:

hello,world

比较有意思的事情是,当我们使用vim -b hello查看可执行目标文件时,发现很有意思的一行如下:

3>^@^@^A^@^B^@^@^@^@^@^@^@^@^@^@^@^@^@hello,world^@^A^[^C; ^@^@^@^C^@^@^@<e0><fe><ff><ff><^@^@^@<fc><fe><ff><ff>\^@^@^@^L<ff><ff><ff>t^@^@^@^T^@^@^@^@^@^@^@^AzR^@^A

看到没,那个hello,world简直就是黑夜中的灯火,太亮眼了,我想,如果我直接改这几个字符再运行,会不会输出另外的结果呢?

于是把 hello,world 改成 hello,c ,然后保存,在期待中运行: ./hello

输出:

段错误 (core dumped)

这是什么情况???

查查发现,这应该和汇编阶段产生的可重定位目标程序的格式有关,链接时已经使用该格式来描述程序。关于可重定位的内容比较复杂,这里就不说了,在这个可执行hello程序中,如果想通过改变hello的内容来让结果改变,要做的就是一个萝卜一个坑,使用其他文本代替hello,world文件即可。

在这里使用 ilovehaha,c 来代替 hello,world, 要注意代替的字符必须和被代替的字符长度相同,好了,更改后在期待中输入:./hello

输出:

ilovehaha,c

验证结论,这就是一个hello.c源文件经过处理后生成hello可执行文件的有趣过程,over

猜你喜欢

转载自www.cnblogs.com/ilove-haha/p/11871344.html
今日推荐