重命名功能而不更改其引用

时间:2019-10-19 05:40:15

标签: c gcc linker

我有一个带-ffunction-sections选项的gcc编译的目标文件。我可以访问源文件,但不允许修改它。

file.c
void foo(void)
{
    bar();
}
void bar(void)
{
    abc();
}

我要实现的目的是使对bar的所有引用都采用一个绝对地址(我将在链接器脚本中分配该地址),而bar将由链接器放置在其他某个地址。

一个可能的解决方案是将bar重命名为file_bar而不更改对foo()中的bar的调用。我尝试使用objcopy -redefine-syms,但似乎甚至重命名了对bar的调用。

除非功能在同一编译单元中,否则忙蜂提供的解决方案可以解决该问题。 foo1.c

#include <stdio.h>
extern void bar1();
void foo1(){
printf("foo1\n");
}
int main(){
printf("main\n");
foo1();
bar1();
}

bar1.c

#include <stdio.h>
void bar1(){
printf("bar1\n");
}

wrapper.c

#include <stdio.h>
void __wrap_foo1(){
printf("wrap_foo1\n");
}
void __wrap_bar1(){
printf("wrap_bar1\n");
}

现在

$ gcc -c -ffunction-sections foo1.c bar1.c wrapper.c
$ gcc -Wl,--wrap=foo1 -Wl,--wrap=bar1 -o output foo1.o bar1.o wrapper.o
$ ./output
main
foo1
wrap_bar1

1 个答案:

答案 0 :(得分:1)

所有要重定向的函数都在各自的编译单元中

链接器具有选项“ --wrap”,该选项将对符号“ xxx”的所有引用替换为“ __wrap_xxx”,并将符号本身替换为“ __real_xxx”。它用于在调用和函数之间放置包装函数,作为“拦截器”。

但是,使用此选项,您可以对链接脚本中的这些符号进行任何操作。您只需要用符号定义“ __wrap_xxx”,以便引用可以解析。

根据您的需要,您还可以编写一个甚至不调用“ __real_xxx()”的伪函数“ __wrap_xxx()”。或者,您可以在向量表中放置“ __real_xxx”,或者...无论您想到什么。

所有要重定向的函数都是非静态的(“全局”),会修补立即值

我浏览了发表在评论中的OP的the other question的答案。这给了我一个弱化所涉及符号并通过链接器用值覆盖它们的想法。

此示例可能会给您一些启示。我在具有address space layout randomization的Linux上进行了测试,因此所有地址都是随机基数的偏移量。但是对于OP的目标系统,它应该可以按预期工作。

foo1.c

由于重定向地址具有任意值,因此无法调用函数。但是该程序可以打印其地址。

#include <stdio.h>

void foo1(void) {
}

extern void bar1(void);

int main(void) {
  printf("%p\n", main);
  printf("%p\n", foo1);
  printf("%p\n", bar1);
  return 0;
}

bar1.c

void bar1(void) {
}

wrapper.ld

这是第一种为链接器提供要使用的地址的方法,即附加的链接器脚本。对于第二个,请参见下文。标准链接描述文件将在此处进行扩展,无需复制和打补丁。由于结构简单,这可能是提供许多可以轻松自动化的重定向地址的最简单方法。

foo1 = 0x1000;
bar1 = 0x2000;

注意:这不是C!正是“链接程序脚本”语法非常相似。

我如何构建和测试

此命令序列可以自动进行排序,以适合您的喜好。尤其是objcopy的调用可以通过对列表的某些循环来完成。

gcc -c -ffunction-sections foo1.c
objcopy --weaken-symbol=foo1 foo1.o foo2.o

gcc -c -ffunction-sections bar1.c
objcopy --weaken-symbol=bar1 bar1.o bar2.o

gcc foo1.o bar1.o -o original
echo original
./original

gcc foo2.o bar2.o -o weakened
echo weakened
./weakened

gcc foo2.o bar2.o wrapper.ld -o redirected
echo redirected
./redirected

也可以在命令行上给出符号定义,而不是附加的链接描述文件。这是上述第二种选择。

gcc foo2.o bar2.o -Wl,--defsym=foo1=0x1000 -Wl,--defsym=bar1=0x2000 -o redirected

顺便说一句,链接器可以理解@file从文件file中读取所有参数。因此,链接器命令的大小没有限制。

所有要重定向的功能都是非静态的(“全局”),并被新功能覆盖

除了提供即时值外,您当然还可以提供替代功能。这与上面的工作原理相同,但是您无需编写其他链接程序脚本或符号定义,而是编写源文件。

wrapper.c

是的,没错,名字等于原件的名字!因为我们将原始函数的符号设置为 weak ,所以当链接器用新函数的地址覆盖引用时,链接器不会收到任何错误消息。

void foo1(void) {
}

void bar1(void) {
}

以这种方式构建重定向程序(仅显示新命令):

gcc -c -ffunction-sections wrapper.c

gcc foo2.o bar2.o wrapper.o -o redirected

要重定向的功能是static

好吧,根据您的目标体系结构,可能将无法实现。这是因为引用的重定位条目。这将是一种相对的关系,它告诉链接器通过对函数部分的偏移进行解析,而不是通过函数的符号进行解析。

我没有对此做进一步的调查。