在研究gcc的编译 链接时,遇到的比较有意思的东西。号称最小的程序。
这个东西来源于书 程序员的自我修养(链接 装载与库) P124.
传统的helloworld
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
对于这个代码的运行需要glibc库等很多库,可以利用gcc -static --verbose -fno-builtin hello.c将编译链接的中间过程打印出来
所以下面的helloworld几个不同点
1、脱离c语言运行库
2、一般c代码的函数入口在库里面的_start,然后调用main函数,此次直接使用c中nomain作为整个程序的入口
3、一般c代码的函数会生成好多段text 段 data段,此次将所有的段合并到一个我们自己命名的tinytext段
tinyhelloworld.c代码如下
char * str = "hello world from tinyos\n";
void printf()
{
asm("movl $24 ,%%edx\n\t"
"movl %0 ,%%ecx\n\t"
"movl $0 ,%%ebx\n\t"
"movl $4 ,%%eax\n\t"
"int $0x80\n\t"
::"r"(str):"edx","ecx","ebx");
}
void exit()
{
asm("movl $42,%ebx\n\t"
"movl $1,%eax\n\t"
"int $0x80\n\t");
}
int nomain()
{
printf();
exit();
}
这段源代码为书中,分析一下
这里的printf 是直接使用linux的write系统调用exit也是如此。
gcc -c -fno-builtin tinyhelloworld.c只编译不链接
-fno-builtin参数为不使用gcc内部函数优化
ld -static -e nomain -o tinyhelloworld tinyhelloworld.o
使用ld链接,
-static是静态链接
-e nomain 是修改程序入口为 nomain
这里说明一下
在链接脚本中也可以设置程序入口,ENTRY(name),会有一个优先级。从高到底为:
1、ld -e参数命令
2、链接脚本ENTRY(name)
3、如果定义了_start则以此为入口
4、如果有text段,则text的第一个字节地址
<span style="color:#FF0000;">readelf -h tinyhelloworld</span>
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
<span style="color:#FF0000;">Entry point address: 0x80480c4</span>
Start of program headers: 52 (bytes into file)
Start of section headers: 472 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 6
从上面可知程序的入口地址为0x80480c4
<spa