我想获取库中某个部分的开始和结束指针,以便可以从程序所链接的程序中覆盖它。
这允许我在程序中指定一些关于库应该如何加载的参数。这是一个具体的例子:
foo.c
,图书馆:
#include <stdio.h>
typedef void (*fptr)();
void lib_function();
void dummy()
{
printf("NO -- I should be overriden by prog_function\n");
}
fptr section_fptrlist __attribute__((weak, section("fptrlist"))) = (fptr)dummy;
extern fptr __start_fptrlist;
extern fptr __stop_fptrlist;
void __attribute__((constructor)) setup()
{
// setup library: call pre-init functions;
for (fptr *f = &__start_fptrlist; f != &__stop_fptrlist; f++)
(*f)();
}
void lib_function()
{
}
bar.c
,该计划:
#include <stdio.h>
void lib_function();
typedef void (*fptr)();
void pre_init()
{
printf("OK -- run me from library constructor\n");
}
fptr section_fptrlist __attribute__((section("fptrlist"))) = (fptr)pre_init;
int main()
{
lib_function();
return 0;
}
我从libfoo.so
构建foo.c
,然后从bar.c
和libfoo.so
构建测试程序,例如:
gcc -g -O0 -fPIC -shared foo.c -o libfoo.so
gcc -g -O0 bar.c -L. -lfoo -o test
这曾经工作得很好,即使用ld版本2.26.1我得到了预期的结果:
$ ./test
OK -- run me from library constructor
现在使用ld版本2.29.1我得到:
$ ./test
NO -- I should be overriden by prog_function
我已经在一台机器上编译了所有内容,并且只通过将目标文件复制到另一台机器,运行ld -shared foo.o -o libfoo.so
并将库复制回来改变了链接器步骤,所以据我所知,链接器是这只是工作与不工作之间的区别。
我进一步使用gcc 7.2.0和glibc 2.22-62,但如上所述,似乎并不具有决定性作用。链接器脚本中的差异似乎很小,使用一个而不是另一个似乎没有对结果产生任何影响(2.26与2.29&#39; s脚本确实作为例外,2.29与2.26&#39; s脚本没有)。无论如何,这就是差异:
--- ld_script_v2.26 2018-02-02 21:52:56.038573732 +0100
+++ ld_script_v2.29 2018-02-02 21:52:41.154504340 +0100
@@ -1,4 +1,4 @@
@@ -53,5 +53,5 @@ SECTIONS
.plt : { *(.plt) *(.iplt) }
.plt.got : { *(.plt.got) }
-.plt.bnd : { *(.plt.bnd) }
+.plt.sec : { *(.plt.sec) }
.text :
{
@@ -226,4 +226,5 @@ SECTIONS
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
+ .debug_addr 0 : { *(.debug_addr) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
查看动态符号表(带readelf -Ws
)我注意到在2.29版本中符号现在受到保护:
with ld 2.29> readelf -Ws libfoo.so | grep fptr
8: 0000000000201028 0 NOTYPE GLOBAL PROTECTED 24 __start_fptrlist
14: 0000000000201028 8 OBJECT WEAK DEFAULT 24 section_fptrlist
16: 0000000000201030 0 NOTYPE GLOBAL PROTECTED 24 __stop_fptrlist
54: 0000000000201028 8 OBJECT WEAK DEFAULT 24 section_fptrlist
58: 0000000000201028 0 NOTYPE GLOBAL PROTECTED 24 __start_fptrlist
62: 0000000000201030 0 NOTYPE GLOBAL PROTECTED 24 __stop_fptrlist
whith ld 2.26> readelf -Ws libfoo.so | grep fptrlist
9: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 23 __start_fptrlist
15: 0000000000201028 8 OBJECT WEAK DEFAULT 23 section_fptrlist
17: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 23 __stop_fptrlist
53: 0000000000201028 8 OBJECT WEAK DEFAULT 23 section_fptrlist
57: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 23 __start_fptrlist
61: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 23 __stop_fptrlist
我知道,from this answer,我依赖的功能更加阴暗,定义明确。我能够追踪到this change was intentional的事实。现在,对于我来说,实现从库设置中调用程序功能的目标,最好的方法是什么? 我还能使这种方法有效吗?有没有办法取消保护这些符号?
即使这个例子很小,这个问题实际上发生在一个相当大的C ++项目中,所以变化越小越好。
答案 0 :(得分:2)
我认为问题是
for (fptr *f = &__start_fptrlist; f != &__stop_fptrlist; f++)
(*f)();
此处for循环预计会在您的应用中定义__start_fptrlist
和__stop_fptrlist
。虽然.protected
会从.so
本身解析这些符号。
一个简单的解决方法是:
// foo.c
/* ... */
fptr *my_start = &__start_fptrlist;
fptr *my_stop = &__stop_fptrlist;
void __attribute__((constructor)) setup()
{
// setup library: call pre-init functions;
for (fptr *f = my_start; f != my_stop; f++)
(*f)();
}
这里my_*
函数的确切值并不重要,因为这两个名称应该绑定到app的值。
// bar.c
/* ... */
fptr section_fptrlist __attribute__((section("fptrlist"))) = (fptr)pre_init;
extern fptr __start_fptrlist;
extern fptr __stop_fptrlist;
fptr *my_start = &__start_fptrlist;
fptr *my_stop = &__stop_fptrlist;
这会强制您的for循环遍历应用中的地址,而不是.so
。因为my_*
符号是全局的,所以它们将首先从应用中解析。
警告:代码未经过测试,因为我没有您所描述的环境。请告诉我这种方法是否适用于您的计算机。