【发布时间】:2022-01-23 01:48:22
【问题描述】:
我试图在不修改源代码的情况下模拟一个静态函数。这是因为我们有大量的遗留代码库,我们希望添加测试代码,而不需要开发人员检查和更改大量原始代码。
使用 objcopy,我可以在目标文件之间使用函数,但不能影响内部链接。换句话说,在下面的代码中,我可以让 main.cpp 从 bar.c 调用模拟的 foo(),但我不能让 UsesFoo() 从 bar.c 调用模拟的 foo()。
我知道这是因为 foo() 已经在 foo.c 中定义。除了更改源代码之外,我有什么方法可以使用 ld 或其他工具来删除 foo() 以便最终链接将其从我的 bar.c 中提取出来?
foo.c
#include <stdio.h>
static void foo()
{
printf("static foo\n");
}
void UsesFoo()
{
printf("UsesFoo(). Calling foo()\n");
foo();
}
bar.c
#include <stdio.h>
void foo()
{
printf("I am the foo from bar.c\n");
}
main.cpp
#include <iostream>
extern "C" void UsesFoo();
extern "C" void foo();
using namespace std;
int main()
{
cout << "Calling UsesFoo()\n\n";
UsesFoo();
cout << "Calling foo() directly\n";
foo();
return 0;
}
编译:
gcc -c foo.c
gcc -c bar.c
g++ -c main.c
(Below simulates how we consume code in the final output)
ar cr libfoo.a foo.o
ar cr libbar.a bar.o
g++ -o prog main.o -L. -lbar -lfoo
This works because the foo() from libbar.a gets included first, but doesn't affect the internal foo() in foo.o
我也试过了:
gcc -c foo.c
gcc -c bar.c
g++ -c main.c
(Below simulates how we consume code in the final output)
ar cr libfoo.a foo.o
ar cr libbar.a bar.o
objcopy --redefine-sym foo=_redefinedFoo libfoo.a libfoo-mine.a
g++ -o prog main.o -L. -lbar -lfoo-mine
This produces the same effect. main will call foo() from bar, but UsesFoo() still calls foo() from within foo.o
【问题讨论】:
-
static void foo()即使在同一个库中也不会在foo.c之外可见 - 从库外部完全无法访问它。符号名称foo可能根本不存在。 -
"We can solve any problem by introducing an extra level of indirection." 如果没有间接级别,则无法强制内部调用者使用您的模拟版本。我能想到的不涉及触及内部代码的唯一方法是编写一个代码处理器,该处理器作为构建过程的一部分运行,以创建可编译的实际代码。从那里您可以对其进行调整以替换对
foo的调用。我不知道这是否适合您的用例;最好以某种方式更改遗留代码。 -
@AndrewHenle 静态符号绝对可见。您可以使用“readelf -s foo.o”来查看它,但它是定义为 LOCAL 的,这与您所期望的完全一样。我尝试使用 objcopy 使其全局化,然后重新定义其名称,但它并没有改变结果。
-
@AndyG 谢谢,我可能不得不走那条路,虽然我希望避免它。
-
@Maxthecat 静态符号绝对是可见的。 这次,为了这次编译。尝试更改优化级别,或剥离生成的二进制文件。静态函数并不意味着在单个编译单元之外可见,因此它们甚至不必作为符号存在于最终二进制文件中。有人花时间让它们成为静态的事实意味着它们的名字本来就不应该被看到。鉴于 C 中的所有函数都驻留在一个命名空间中,盲目地更改函数从不意味着可见以使其可见是非常危险的。