假定我们有一个如下的数据结构:
struct foo_s {
char* name;
char* name;
int i1;
int i2;
};
我们需要动态的增加一些struct foo_s的数据,比较直观的方法是我们可以做一组接口,如将上面的结构增加一个链接指针,通过结构动态的增加struct foo_s的数据。但有时候我们仅仅需要动态的增加一些struct foo_s的对象(实例),因此通过这种方式仍然不够灵活/方便。
通过使用ELF文件格式的特殊字段,我们可以采用如下方法:
#define foo_section __attribute__((unused,section(".foo_sect")))
#define MAKE_FOO_ENTRY(nam, typ, x, y) \
struct foo_s __foo_s_##nam foo_section = { #name, #typ, x, y }
在代码的任何位置我们可以定义一个struct foo_s的对象,如:
MAKE_FOO_ENTRY("name1", "type1", 1, 2);
...
MAKE_FOO_ENTRY("name2", "type2", 3, 4);
这样所有的struct foo_s的对象都被放到了一个独立的ELF section,即.foo_sect。
到目前为止我们并不知道这些对象的具体地址,利用ld (gnu ld)的link script,我们可以这样做:
# foo.ld.S
...
. = .
__foo_start = .
.foo_sect : { *(.foo_sect) }
__foo_end = .
了解一点ld link script的应该很容易了解以上的代码的含义:
. = . /* 其实没什么含义,不过在`.'可能没有对其的时候自动对其而已 */
__foo_start = . /* __foo_start = 当前linker的地址 */
.foo_sect : { *(.foo_sect) } /* 将所有文件(*)的.foo_sect字段都加入.foo_sect字段 */
__foo_end = . /* 同__foo_start */
这样结合ld script,通过引入两个变量__foo_start与__foo_end,我们可以很容易的找到所有struct foo_s的对象,如:
struct foo_s *p = (struct foo_s*)&__foo_start;
while(p!= (struct foo_s*)&__foo_end) {
printf("name: %s, type: %s, i1: %d, i2: %d\n",
p->name, p->type, p->i1, p->i2);
++p;
}
当然由于引入的link script,我们需要自己手动调用ld来链接我们的程序。
注意:
1) 我们并不需要写一个完整的foo.ld.S,通过调用
ld --verbose
默认会生成一个ld.S模板,只需要增加上面我们提到的__foo_xxx相关内容即可,如:
...
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS)
}
. = .;
__foo_s_start = .;
.foo.sect : { *(.foo.sect) }
__foo_s_end = .;
.data1 : { *(.data1) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
...
2) 通过了ld链接我们需要指定使用这个foo.ld.S文件,而且链接需要不少crt??.o文件,这个我们可以通过编译一个hello.c文件:
gcc -v hello.c -o hello
会自动输出相应的ld输出命令,如:
GNU C version 3.4.3 (i686-pc-linux-gnu)
compiled by GNU C version 3.2.3 20030502 (Red Hat Linux 3.2.3-42).
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
as -V -Qy -o /tmp/ccaGnvUs.o /tmp/cc8ZSw0M.s
GNU assembler version 2.15.92.0.2 (i386-redhat-linux) using BFD version 2.15.92.0.2 20040927
/usr/i686-pc-linux-gnu/3.4.3/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o ex07 /usr/lib/crt1.o /usr/lib/crti.o /usr/i686-pc-linux-gnu/3.4.3/crtbegin.o -L/usr/i686-linux2.4/lib/gcc/i686-pc-linux-gnu/3.4.3 -L/usr/i686-linux2.4/lib/gcc/i686-pc-linux-gnu/3.4.3/../../.. /tmp/ccaGnvUs.o -lgcc -lc -lgcc /usr/i686-linux2.4/lib/gcc/i686-pc-linux-gnu/3.4.3/crtend.o /usr/lib/crtn.o
因此,我们可以写一个类似的Makefile:
all:
/bin/rm -f foo foo.o
gcc -c foo.c
ld -dynamic-linker /lib/ld-linux.so.2 -o foo -T foo.ld.S /usr/lib/crt1.o /usr/lib/crti.o /usr/i686-pc-linux-gnu/3.4.3/crtbegin.o -L/usr/i686-linux2.4/lib/gcc/i686-pc-linux-gnu/3.4.3 -L/usr/i686-linux2.4/lib/gcc/i686-pc-linux-gnu/3.4.3/../../.. ex25.o -lgcc -lc /usr/i686-linux2.4/lib/gcc/i686-pc-linux-gnu/3.4.3/crtend.o /usr/lib/crtn.o
通过readelf程序,我们可以看到section .foo_sect被正确创建:
readelf -S ./foo
...
[23] .foo.sect PROGBITS 0804967c 00067c 000040 00 WA 0 0 4
...
阅读(1206) | 评论(0) | 转发(0) |