是否可以检测C中保留标识符的使用冲突?

时间:2018-10-26 15:09:28

标签: c gcc

根据C标准,如果程序定义或声明了保留标识符,则该行为未定义。保留标识符的一类是在C标准库中定义的具有外部链接的标识符。

以行为未定义的程序为例,请考虑以下内容:file1.c定义了一个名为time的具有外部链接的变量,该变量与标准库中的time函数及时声明了冲突。H。

file1.c:

int time;

int foo( void )
{
    return time;
}

file2.c:

#include <time.h>
#include <stdio.h>

extern int foo( void );

int main( void )
{
    foo();
    printf( "current time = %ld\n", time( NULL ) );
    return 0;
}

编译并运行程序时,会发生段错误,因为file2.c中引用的time符号已链接到file1.c中的time变量,而不是C库。

$ gcc -c -o file1.o file1.c
$ gcc -c -o file2.o file2.c
$ gcc -o test file1.o file2.o 
$ ./test
Segmentation fault (core dumped)

我想知道GCC是否有任何方法可以在编译或链接时检测用户代码中冲突的保留标识符的使用情况。这是我的动力:我正在开发一个应用程序,用户可以在该应用程序中编写C扩展,这些扩展经过编译并链接到该应用程序的其余部分。如果用户的C代码像上面的示例一样使用保留的标识符,则生成的程序可能会以难以预测的方式失败。

想到的一种解决方案是在用户的目标文件上运行类似nm的内容,并将定义的符号与C库中的保留标识符列表进行比较。但是,我希望在GCC中找到可以检测到此问题的内容。有谁知道这是否有可能或有任何建议?

2 个答案:

答案 0 :(得分:4)

您可以获取一个libc实现,该实现可以与-Wl,--whole-archive静态链接,然后尝试将其拍打到目标文件上。

main.c

int time=42;
int main(){}

将其与整个libc链接:

$ musl-gcc main.c -static -Wl,--whole-archive

如果遇到多定义错误或符号更改的类型/大小/对齐方式警告,则说明您与libc冲突。

/usr/local/bin/ld: /usr/local/musl/lib/libc.a(time.lo): in function `time':
/home/petr/f/proj/bxdeps/musl/src/time/time.c:5: multiple definition of `time'; /tmp/cc3bL3pP.o:(.data+0x0): first defined here

或者(更强大),您可以预包含和all-of-C(所有位置)标头,并让编译器告诉您与它冲突的位置(我只想在其中进行一次)同时,否则它会在某种程度上降低您的构建时间(尽管即使包括所有POSIX也不比仅包含一个C ++标头还差)。

答案 1 :(得分:3)

  

我想知道GCC是否有任何方法可以在编译或链接时检测用户代码中冲突的保留标识符的使用情况。

详细说明@PSkocik
检测许多冲突的一种方法是包括所有头文件。编译时间可能会明显增加。

Determine version

#if defined(__STDC__)
# define STANDARD_C89
# if defined(__STDC_VERSION__)
#  define STANDARD_C90
#  if (__STDC_VERSION__ >= 199409L)
#   define STANDARD_C95
#  endif
#  if (__STDC_VERSION__ >= 199901L)
#   define STANDARD_C99
#  endif
#  if (__STDC_VERSION__ >= 201112L)
#   define STANDARD_C11
#  endif
#  if (__STDC_VERSION__ >= 201710L)
#   define STANDARD_C18
#  endif
# endif
#endif

包括它们,有选择地包括它们。

#include <assert.h>
//#include <complex.h>
#include <ctype.h>
#include <errno.h>
//#include <fenv.h>
#include <float.h>
//#include <inttypes.h>
//#include <iso646.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
//#include <stdalign.h>
//#include <stdatomic.h>
//#include <stdbool.h>
#include <stddef.h>
//#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
//#include <stdnoreturn.h>
#include <string.h>
//#include <tgmath.h>
//#include <threads.h>
#include <time.h>
//#include <uchar.h>
//#include <wchar.h>
//#include <wctype.h>

//////////////////////////////
#ifdef STANDARD_C95
#include <iso646.h>
#include <wchar.h>
#include <wctype.h>
#endif

//////////////////////////////
#ifdef STANDARD_C99
#ifndef __STDC_NO_COMPLEX__
#include <complex.h>
#endif
#include <fenv.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <tgmath.h>
#endif

//////////////////////////////
#ifdef STANDARD_C11
#include <stdalign.h>
#ifndef __STDC_NO_THREADS__
#include <stdatomic.h>
#include <threads.h>
#endif
#include <stdnoreturn.h>
#include <uchar.h>
#endif

我敢肯定,上述内容需要一些改进,并且希望您能提出建议。


avoid additions到名称空间,请使用宏代码测试代替#define STANDARD_C11这样的代码

// #ifdef STANDARD_C11
//  ... C11 includes
// #endif

#if defined(__STDC__)
# if defined(__STDC_VERSION__)
#  if (__STDC_VERSION__ >= 201112L)
     ... C11 includes
#  endif
# endif
#endif

尽管目标是“根据C标准...”,但可能需要其他代码来适应流行的编译器扩展和对该标准的细微变化。