【问题标题】:Exporting symbols under a different name以不同的名称导出符号
【发布时间】:2020-07-25 03:55:29
【问题描述】:

我的情况如下:我需要构建一个库,在其中导出“包装”strcpy(请忽略这对我有什么用处),它唯一要做的就是调用标准(来自string.h ) strcpy 但返回 void,而不是返回 char *。这意味着,我需要#include string.h 但这会立即给我strcpygcc 不会让我按照我的意愿重新定义它,并构建一个.so 共享库,只导出新的,包装的@ 987654330@。我怎样才能做到这一点?

【问题讨论】:

  • 用户不需要使用来自strcpy的返回值。如果他忽略它,则行为相当于一个 void 返回值。
  • @PaulOgilvie 事情更复杂,最好忽略我为什么这样做。事实上,我使用的是强类型的 Haskell FFI,此外,我还想以更复杂的方式包装其他函数,而不是简单地忽略它们的返回值。
  • 这句话可能不是真的:“我需要建立一个库,在其中导出一个“包装”strcpy...”是什么让你相信你有这样的需求,尤其是它需要与 C strcpy 同名但类型不同?
  • @EricPostpischil 这是一个供内部使用的库,用于我的编译器(用另一种语言,Haskell 编写),而不是公共的。我在这里只对如何完成这个特殊的符号导出属性感兴趣,而不是我如何/为什么需要这个......那将是另一个话题......
  • 也许 GCC 可以让我静态链接并使旧的 strcpy 消失?然后,以某种方式允许我将符号函数“重命名”为另一个名称(因为显然在 C 中你不能有两个具有相同标识符、相同参数的不同函数,即没有重载)。 gcc 有这样的选择吗?

标签: c++ c gcc shared-libraries


【解决方案1】:

我的字符串.h

extern "C" void strcpy(char* dst, const char* src);

字符串内部.h

void mystrcpy(char* dst, const char* src);

mystring.c

#include "mystring.h"
#include "stringinternal.h" 
extern "C" void strcpy(char* dst, const char* src) {
  mystrcpy(dst, src);
}

stringinternal.c

#include "stringinternal.h" 
#include <string.h>
void  mystrcpy(char* dst, const char* src) {
  strcpy(dst, src);
}

main.c

#include "mystring.h" 
int main() {
  char dst[3];
  strcpy(dst, "ab");
  return 0;
}

标准strcpy 导出为弱函数,您可以在库中重新定义它。

要正确链接您的 .so,您必须禁用标准库以进行链接并以正确的顺序列出文件:strcpyinternal.o 最后需要标准库和 mystring.o。这将防止来自您的.so 的递归 strcpy 调用。 gcc 的示例可以是这样的

gcc -shared -nostdlib -o my.so stringinternal.o -lc mystring.o

【讨论】:

  • 问题是我希望我的.so 文件将strcpy 作为名称导出(但实际上是mystrcpy),而不是mystrcpy 作为名称。跨度>
  • 你好像误会了。我为演示制作了 main.c,它使用了你的 .so 中的 strcpy,而不是 mystrcpy。
  • 哦,我明白了,是的,很抱歉造成误会。让我马上试试这个。
  • 我们如何将这些文件编译成.o对应的文件?碰巧我得到:In file included from ./stlib.c:1:0: ./stlib.h:1:8: error: expected identifier or ‘(’ before string constant 它指的是行:extern "C" void puti(const int32_t n); 它表明“C”是错误的(?)
  • 在报告该行之前代码似乎有错误。
【解决方案2】:

如果你使用c++,你可以定义你自己的命名空间,例如:

在 my.h 中

namespace strcpy_v2
{
    void strcpy( char *dest, const char *src ); 
}

在关联的 my.cpp 中

#include <string>
#include "my.h"

void strcpy_v2::strcpy( char *dest, const char *src )
{

    auto val = std::strcpy(dest,src);

   // you should test val to ensure every thing is ok

}

来自将链接您的代码的 .so。你可以这样做:

using namespace strcpy_v2;

而不是

 using namespace std;

【讨论】:

  • 这似乎不是我想要的,因为我想直接使用我的.so共享库中的新包装函数,而不是通过命名空间......我只是想找到一种方法完全“隐藏”和“覆盖”标准strcpy,但在我的包装器上使用它......
  • 使用包装函数可以简单地通过以下方式完成: using namespace strcpy_v2;
  • 它的使用不会在 C/C++ 代码中,而是在另一种不识别命名空间的语言中(或者至少,它不会简单地这样做......)
【解决方案3】:

strcpy 是带有“C”链接的弱符号,因此可以无错误地覆盖

libstrcpy.cc:

#include <dlfcn.h>

extern "C" {
void strcpy(char *dest, char *src)
{
    auto real_strcpy = (char*(*)(char*,char*)) dlsym(RTLD_NEXT, "strcpy");
    real_strcpy(dest, src);
}
}

编译:g++ libstrcpy.cc -std=c++11 -shared -fPIC -ldl -o libstrcpy.so

验证strcpy 导出:readelf --dyn-syms libstrcpy.so

使用(main.cc):

extern "C"{
void strcpy(char *, char *);
}
int main()
{
    char a[10] = "test" , b[10] = {};
    strcpy(a, b);
}

编译:g++ main.cc -o main -lstrcpy -L./

请注意,如果您想使用正确的void 返回声明,则不能导入任何将定义strcpy 的标头(正确方式)。

【讨论】:

  • 太棒了!!这也可以通过静态链接以某种方式完成,即如果我想生成一个libstrcpy.a 并嵌入所有内容,而不依赖于dlsym 来动态加载strcpy
  • 如果你想在没有dlsym的情况下静态地做这个,你需要使用S.M.的解决方案(你可以把stringinternal.o放到一个归档库中)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多