2016-11-04 49 views
-1

我刚刚在一个C API中发现了一个缺少的函数(它编译但它没有链接)。我想测试整套API函数。当时的想法是让事情这样简单:如何测试C函数是否可链接

include <myapi.h> 
void main(void) { 
    func1; 
    func2; 
    ... 
} 

但这编译因为编译器(GCC)优化掉未使用的函数指针链接罚款。如果我使用func1()代替,如果该函数确实缺失,则会出现链接器错误。但是所有的函数调用都很复杂,需要花费几天时间为所有函数写明确的参数。

我可以使用nm来查询库,但首先它是一整套.so文件,其次它可能取决于API头中的#defines。

是否有一个简单的语法,我可以在C文件中使用?或者,也许只是一个(未)优化选项的GCC?

+0

尝试使用gcc“-O0”重新编译它应该禁用所有优化 – Arseniy

+1

问题很不明确。你能更具体一些,并给出实际的用例和动机。我甚至不明白“API中缺少的功能”的意思,因为API是一组记录的公共功能,所以请**编辑您的问题**以改进它并激励它。要更具体和具体。 –

+1

什么是实际用途?你想从标准中获取所有C函数,并检查你的编译器是否支持它们?这是很多工作。为什么不向编译器的作者询问编译器支持什么以及有哪些限制? –

回答

2

一个可能的解决方法可以是使用函数指针假用法:

include <myapi.h> 
typedef void Func_T(void); // Function type for easier handling 
Func_T * volatile Func; // Function pointer where functions are stored 
/* Macro to do the assignment */ 
#define TEST(f) Func = (Func_T*)f 
int main(void) { 
    TEST(func1); 
    TEST(func2); 
    ... 
} 

链接可能仍然删除的功能,但它是值得尝试。

编译器通常会提供属性或编译指示来强制保留符号。如果链接器试图删除它,保留Func可能会很有用。

2

你很不清楚的问题提到.so文件(但没有提及任何操作系统)和nm。所以我猜你在使用Linux,而我的答案只针对Linux。我不明白,如果你想编译&构建时或在运行时。

给定的共享对象/some/path/to/foo.so您可以使用dlopen(3)dlsym(3)功能找出(在运行时),如果该共享对象定义了一个给定的符号。但请注意,在ELF文件符号是将近无类型(例如,您无法从ELF共享对象的名称中知道某个函数的某些函数的签名,而没有某个C头文件声明它)。

或者,您可能需要更复杂的software build过程(例如,通过向您的Makefile添加临时规则)。请记住,您可以使用metaprogramming技术并在您的构建中使用一些专门的C代码生成器。如果你的软件足够复杂(例如花费数周时间在这些工具上),你甚至可以使用GCC MELT(或者编写你自己的GCC插件)来定制GCC编译器。

注意,一些头文件(对于给定的库)可以定义一个函数作为inline或可限定具有用于它参数的宏(例如,参见waitpid(2),POSIX API的一部分; WIFEXITED实际上是一个宏)。在这两种情况下,该函数都不是ELF共享库的符号,但可以从源代码正确使用该库(并正确使用该头文件)来使用该函数。换句话说,API与一组ELF符号不同。

另请参阅Drepper的Good Practices in Library Design, Implementation, and MaintenanceHow To Write Shared Libraries和D.Wheeler Program Library HOWTO

最后,如果根据已知始终为假的条件添加一些代码(例如,读取约opaque predicates),则编译器无法优化。

int main(int argc, char**argv) { 
    // in practice, all the tests above are false, 
    // but the compiler is not clever enough to optimize 
    if (getpid()==0) funct1(); // always false 
    if (argc<0) funct2(); //always false 
    if (argv[0][0]==(char)0) funct3(); //always false 
    /// etc 

如果职能的签名需要一些参数,你可以简单地测试他们的地址:

extern void func1(int); // actually, in some included header 
    if (argv[0]==NULL || (void*)func1 == NULL 
     || (void*)func1 == (void*)3) abort(); 

(我相信,在C标准允许编译器优化(void*)func1 == NULL -as始终为假 - 但它不会优化(void*)func1 == (void*)3其中练习在Linux上始终为false ...)

但是,API又超过了一组ELF符号和一个API“函数”实际上可以是inline或宏。您可能会对weak symbols感兴趣。

+0

有趣的阅读,谢谢。 – dargaud

相关问题