众所周知,目标文件的内容是编译后的机器指令代码,数据,符号表,调试信息,字符串等,同时,是分别以"段"的形式存储,分段都是已经设定好的,那么,我们是否想过,将一个图片,音乐之类的东西作为目标文件中的一个段或者我们自定义段呢?
objcopy
objcopy是GNU的一个实用工具,其作用是将目标文件的一部分或者全部内容拷贝到另外一个目标文件中,或者实现目标文件的格式转换。
执行下面命令,将图片zzy.jpg转化为.o文件
objcopy -I binary -O elf64-x86-64 -B i386 zzy.jpg zzy.o
-I bfdname 指定输入文件的bfdname,上面为二进制
-O bfdname 指定输出文件的bfdname
-B 将无体系的输入文件转化为对象文件
命令中的i386即Intel 80386。通常被用来作为对Intel(英特尔)32位微处理器的统称,即修改成此体系下的对象文件
再查看该文件,可以看到文件格式是elf64-x86-64,即我们指定的输出格式
zzy:~/Study/Self_cultivation_of_programmers$ objdump -ht zzy.o
zzy.o: 文件格式 elf64-x86-64
节:
Idx Name Size VMA LMA File off Algn
0 .data 000ce50c 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 _binary_zzy_1_png_start
00000000000ce50c g .data 0000000000000000 _binary_zzy_1_png_end
00000000000ce50c g *ABS* 0000000000000000 _binary_zzy_1_png_size
上面的三个段"_binary_zzy_1_png_start","_binary_zzy_1_png_end","_binary_zzy_1_png_size",分别表示该文件的起始地址,结束地址和大小,这三个值可以在程序中直接使用它们
自定义段
除去程序自己默认的一些段,我们还可以在自己定义一些段,将数据存入进去,定义如下
__attribute__((section("自定义的段名")))
程序实例
先来看一下下面的程序
#include<stdio.h>
__attribute__((section("INIT_VAR"))) int global_init_var = 88;
__attribute__((section("UNINIT_VAR"))) int global_uninit_var;
extern char _binary_zzy_1_png_start;
extern char _binary_zzy_1_png_end;
extern char _binary_zzy_1_png_size;
extern char INIT_VAR;
void func1( int i ){
printf(" %d \n",i);
}
int main(){
static int static_var = 85;
static int static_var_2;
static int static_var_3 = 0;
int a = 1;
int b;
func1(static_var + static_var_2 + a + b);
printf("_binary_test_png_start %#lx\n",(unsigned long)&_binary_zzy_1_png_start);
printf("_binary_test_png_end %#lx\n",(unsigned long)&_binary_zzy_1_png_end);
printf("_binary_test_png_size %ld\n",(unsigned long)&_binary_zzy_1_png_end - (unsigned long)&_binary_zzy_1_png_start);
printf("INIT_VAR %#lx\n",(unsigned long)&INIT_VAR);
return 0;
}
生成目标文件
gcc test.c -c
查看目标文件信息
objdump参数
-d
从objfile中反汇编那些特定指令机器码的section
-h
–section-headers
–headers
显示目标文件各个section的头部摘要信息。
-s
–full-contents
显示指定section的完整内容。默认所有的非空section都会被显示。
-t
–syms
显示文件的符号表入口。类似于nm -s提供的信息
objdump -h test.o
test.o: 文件格式 elf64-x86-64
节:
Idx Name Size VMA LMA File off Algn
0 .text 000000d2 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000004 0000000000000000 0000000000000000 00000114 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000008 0000000000000000 0000000000000000 00000118 2**2
ALLOC
3 INIT_VAR 00000004 0000000000000000 0000000000000000 00000118 2**2
CONTENTS, ALLOC, LOAD, DATA
4 UNINIT_VAR 00000004 0000000000000000 0000000000000000 0000011c 2**2
CONTENTS, ALLOC, LOAD, DATA
5 .rodata 00000068 0000000000000000 0000000000000000 00000120 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .comment 0000002c 0000000000000000 0000000000000000 00000188 2**0
CONTENTS, READONLY
7 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000001b4 2**0
CONTENTS, READONLY
8 .eh_frame 00000058 0000000000000000 0000000000000000 000001b8 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
从输出中可以看出,我们在最开始自定义的两个段,定义成功(3,4行)
另外,值得注意的是
1.static_var存到了.data中,但static int static_var_3 = 0其实和未进行初始化的static_var_2一样,存在了.bss中
2..eh_frame区域
.eh_frame 的格式与 .debug_frame 是很相似的(不完全相同),属于 DWARF 标准中的一部分。所有由 GCC 编译生成的需要支持异常处理的程序都包含了 DWARF 格式的数据与字节码,这些数据与字节码的主要作用有两个:
1)描述函数调用栈的结构(layout)
2)异常发生后,指导 unwinder 怎么进行 unwind。
我们将两个目标文件链接编译成一个可执行文件
gcc test.o zzy.o -o test
程序的输出转化后目标文件的地址,目标文件的大小以及我们自定义的段的地址:
zzy:~/Study/Self_cultivation_of_programmers$ ./test
86
_binary_test_png_start 0x5607bde0d014
_binary_test_png_end 0x5607bdedb520
_binary_test_png_size 845068
INIT_VAR 0x5607bdedb520
在这里其实输出大小应该是直接用下面这个输出
printf("_binary_test_png_size %#lx\n",(unsigned long)&_binary_zzy_1_png_size);
但最终输出如下
_binary_test_png_size 0x5615cc94e50c
暂时还没理解清楚,之后有再探索探索