【问题标题】:How can I invoke a function with multiple distinct signatures (as glibc invokes main)?如何调用具有多个不同签名的函数(如 glibc 调用 main)?
【发布时间】:2016-12-17 10:59:15
【问题描述】:

C++ 标准表明main 可以有two possible signatures

int main()               // (1)
int main(int, char*[])   // (2)

此外,main() 通常是从__libc_start_main 调用的,它在csu/libc-start.c 源代码中的csu/libc-start.c 中定义。该函数内部对main的调用复制如下。

result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);

我们观察到这里的调用使用了三个参数,这似乎不会产生任何链接器错误,尽管签名与上述两种形式都不匹配。


我正在编写一个定义main() 的线程库,并希望应用程序能够定义以下两个函数之一。

int AppMain()               // (1)
int AppMain(int, char*[])   // (2)

不幸的是,如果我使用上面的第二种形式声明 AppMain 并从我的库的 main 调用它,而应用程序使用第一种形式,那么我得到一个未解决的引用链接器错误,表明我调用的函数确实不存在。

由于相对于main,这对于glibc 来说似乎不是问题,我怎样才能以一种避免链接器错误的方式编写我的调用或声明?


这是重现问题的简单方法。

生成文件

CCFLAGS=-Wall -Werror

all: libLib.a App

libLib.a: Lib.o
    ar rcs $@ $^

App: App.o libLib.a
    g++ -o $@ $< -L. -lLib

%.o: %.cc
    g++ $(CCFLAGS)  -O3 $(LIBS) -fPIC -c  -o $@ $<

Lib.cc

#include <stdio.h>

int AppMain(int argc, char** argv);

#undef main
int main(int argc, char** argv){
    printf("Hello World\n");    
    AppMain(argc, argv);
}

App.cc(工作版)

#include <stdio.h>

int AppMain(int argc, char** argv){
    printf("Application says Hello world\n");
    return 0;
}

App.cc(非工作版)

#include <stdio.h>

int AppMain(){
    printf("Application says Hello world\n");
    return 0;
}

在第二个版本下,这里是链接器错误。

./libLib.a(Lib.o): In function `main':
Lib.cc:(.text.startup+0x24): undefined reference to `AppMain(int, char**)'
collect2: error: ld returned 1 exit status
Makefile:9: recipe for target 'App' failed
make: *** [App] Error 1

【问题讨论】:

  • main() 由运行时库专门处理,您无法对常规函数做同样的事情。
  • 您应该只要求您的 AppMain() 函数始终使用第二个签名。
  • 总的来说,我认为您使用了错误的策略。强制使用您的库的开发人员使用您的 main() 函数会限制在已经具有 main() 函数的任何现有应用程序中的采用,如果其他第三方库的代码(例如静态构造)需要在您的库中的代码之前运行,等等。您最好捆绑示例main() 函数(在文档中,或不属于库本身的说明性源文件中),以便开发人员适应他们的应用程序。跨度>
  • @peter,感谢您的反馈。我对这个决定很矛盾,很高兴获得第二意见。

标签: c++ gcc linker glibc


【解决方案1】:

不管有什么不同的建议,我只是回答你的问题

如何以一种避免链接器错误的方式编写我的调用或声明?

你可以将你的定义写在App.cc as

extern "C" int AppMain()
{
    …

以及您在Lib.cc 中的声明

extern "C" int AppMain(int argc, char** argv);

为了避免name mangling,就像在glibc 中最有可能做的一样。可能有一些 C++ 编译器无法使用,但它确实适用于 GCC。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 2016-05-10
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 2018-06-24
    • 1970-01-01
    相关资源
    最近更新 更多