最近在看《程序员的自我修养—链接、装载与库》一书,对书中提到的一个小问题,自己做了个试验验证一番,然后记录之。其具体问题如下:
如果我们将一个二进制文件,比如图片,MP3音乐,词典一类的东西作为目标文件的一段,该怎么做?
可以使用objcopy工具,比如我们有一个图片文件”image.jpg” 大小为8846Bytes :
[james_xie@james-desk testforobjcopy]$ ls -al image.jpg
-rw-rw-r-- 1 james_xie james_xie 8846 Jun 21 12:51 image.jpg
[james_xie@james-desk testforobjcopy]$ objcopy -I binary -O elf64-x86-64 -B i386 image.jpg image.o
[james_xie@james-desk testforobjcopy]$ objdump -ht image.o
image.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 0000228e 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 _binary_test_jpg_start
000000000000228e g .data 0000000000000000 _binary_test_jpg_end
000000000000228e g *ABS* 0000000000000000 _binary_test_jpg_size
符号“_binary_test_jpg_start”,“ _binary_test_jpg_end”和“_binary_test_jpg_size”分别表示该图片文件在内存中的起始地址,结束地址和大小,我们可以在程序里面直接声明并使用它们。
下面结合一个简单的例子来尝试,新建一个文件名为test.c的文件:
#include
extern char _binary_test_jpg_start;
extern char _binary_test_jpg_end;
extern char _binary_test_jpg_size;
int main()
{
int i = 0;
unsigned char *p = NULL;
printf("_binary_test_jpg_start 0x%lx\n",(unsigned long)&_binary_test_jpg_start);
printf("_binary_test_jpg_end 0x%lx\n",(unsigned long)&_binary_test_jpg_end);
printf("_binary_test_jpg_size %ld\n",(unsigned long)&_binary_test_jpg_size);
p = (unsigned char *)(unsigned long)&_binary_test_jpg_start;
printf("First 8 bytes of the Image : \n");
for(i=0;i<8;++i){
printf("0x%02x ",*p++);
}
printf("\n");
return 0;
}
首先把该文件编译成目标文件(.o文件):
gcc -c test.c
链接生成最后的可执行文件:
gcc test.o image.o -o test
运行结果如下:
[james_xie@james-desk testforobjcopy]$ ./test
_binary_test_jpg_start 0x601044
_binary_test_jpg_end 0x6032d2
_binary_test_jpg_size 8846
First 8 bytes of the Image :
0xff 0xd8 0xff 0xe0 0x00 0x10 0x4a 0x46
代码非常简单,没什么值得深入分析的,但是还有个小小的疑问,符号的值(即symbol’s value),通常可通过如下命令去查看:
[james_xie@james-desk testforobjcopy]$ objdump -t test
test: file format elf64-x86-64
SYMBOL TABLE:
......
0000000000601044 g .data 0000000000000000 _binary_test_jpg_start
......
000000000000228e g *ABS* 0000000000000000 _binary_test_jpg_size
00000000006032d2 g .data 0000000000000000 _binary_test_jpg_end
......
为了更加直观,其他的符号表的输出结果被删除,其结果中第一列就是我所说的符号的值(即symbol’s value),按照我的理解这个值应该是该符号(symbol)的虚拟内存地址,为了验证我这个想法,我上面的例子代码中尝试去打印_binary_test_jpg_start开始位置的连续8个字节的值,跟我直接通过hexdump命令查看图片文件的值一样:
[james_xie@james-desk testforobjcopy]$ hexdump -C image.jpg
00000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 |......JFIF......|
00000010 00 01 00 00 ff db 00 43 00 0f 0f 0f 0f 0f 0f 0f |.......C........|
00000020 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
*
这说明我的理解是对的,但是我还有个疑问就是这个符号“_binary_test_jpg_size”,这个符号的值(即symbol’s value)是000000000000228e,转换位十进制刚好是8846,即为我们图片文件的大小,这很明显应该不是一个地址,但是我上面的代码中,确确实实是通过
printf("_binary_test_jpg_size %ld\n",(unsigned long)&_binary_test_jpg_size);
地址取操作符&来访问的,而且如果我要是去掉这个地址操作符,如下方操作的话:
printf("_binary_test_jpg_size %ld\n",(unsigned long)_binary_test_jpg_size);
会直接出现:Segmentation fault (core dumped),即我们常碰到的段错误,这时我就有点方了,为了验证这个问题,我绝对去研究下objcopy的代码,看看它里面定义绝对符号(Absolute Symbol)表是具体怎么操作的,将在下一篇文章中来进行分析,未完待续。