1.从程序员的自我修养上摘录的。
2.这不是卖弄技巧,这是深入了解链接和编译。
开始
使用c语言输出hello world,不用C语言库,不用main函数。怎么做?
1. 我们需要printf这样的功能。
2. 一段程序需要一个程序入口,C语言默认是main。
有这样的武器:
1. GCC支持内嵌汇编。
2. 使用汇编直接产生中断,可以使用系统调用。
3. ld连接器可以指定程序入口。
好了,我们要做的就是,在程序中使用GCC内嵌汇编,产生中断,进行write的系统调用, 在链接程序时,指定自己的程序入口。代码如下:
/*
* @FileName: TinyHelloWorld.c
* @Author: wzj
* @Brief:
*
*
*
*
*
* @History:
*
*
*
* @Date: 2012年06月19日星期二22:46:23
*
*/
char* str = "Hello world\n";
void
print()
{ //使用write的系统调用, write的调用号为4,原型为int write(int filedesc, char* buffer, int size)
//这里的系统调用,先将参数写入寄存器,之后传入write调用
asm(
"movl $13, %%edx \n\t" //str的长度
"movl %0, %%ecx \n\t" //缓冲区,这里的%0,指的是“r”后面的字符串地址,也就是传入到这段汇编的参数。
"movl $0, %%ebx \n\t" //打印到标准输出, 也就是0
"movl $4, %%eax \n\t" //将系统调用号传入eax。
"int $0x80 \n\t" //执行中断, 调用write函数
::"r" (str):"edx","ecx", "ebx" //传入的参数列表, 被重命名的寄存器列表。
);
}
void
exit()
{
asm(
"movl $42,%ebx \n\t "
"movl $1, %eax \n\t"
"int $0x80 \n\t"
);
}
//这里是程序的入口
void
nomain()
{
print();
exit();
}
进行编译:
gcc -c -fno-builtin TinyHelloWorld.c
ld -static -e nomain -o TinyHelloWorld TinyHelloWorld.o
注:-fno-buildtin 说明禁止使用gcc的内置函数, -static指明使用静态链接进行链接。 -e nomain 指定函数入口为 nomain。
或者,我们可以使用一份链接脚本:
ENTRY(nomain) :指定程序入口
SECTIONS
{
. = 0x08048000 + SIZEOF_HEADERS; ;指明程序开始的地址。
tinytext : { *(.text) *(.data) *(.rodata) } ;将后面三段,合成当前一段。
/DISCARD/ : { *(.comment) }
}
进行编译:
gcc -c -fno-builtin TinyHelloWorld.c
ld -static -T TinyHelloWorld.ld -o TinyHelloWorld TinyHelloWorld.o
注:使用这份脚本编译之后,与前面的不同之处是,.text .data .rodata 被合并成了.tinytext。 使用objdump -h 可以看到两者最终生成文件的不同之处。