最近我将我的项目从ubuntu 14.04移到了ubuntu 16.04。当我使用从os存储库安装的gcc 4.8.5编译我的项目时,我遇到了一个错误,“undefined __warn_memset_zero_len”。我从来没有在使用gcc 4.8.4的ubuntu 14.04上看到这个错误。
__ warn_memset_zero_len,在string3.h中声明,从memset中调用,以警告程序员的错误。为了重现错误,我制作了以下示例代码并使用gcc --save-temps test_mem.c -D_FORTIFY_SOURCE=1 -Wall -O1 -v -Wl,-v
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "almost every programmer should know memset!";
memset (str, 1, 0);
puts (str);
return 0;
}
此代码也仅在ubuntu 14.04中使用gcc 4.8.4编译。所以,首先,我假设新的libc可能缺少__warn_memset_zero_len的定义。为了找出定义,我将ubuntu 14.04的旧libc与ubuntu 16.04的旧libc进行了比较。这两个版本都没有它,只有string3.h中的函数声明。我错了。 :其中
下一步,我假设gcc可能会在编译期间删除对函数的引用,因为它是一个特殊的警告函数。但是,两个系统创建的目标文件仍然具有引用,由nm检查。然后,源代码仍然需要__warn_memset_zero_len的定义。我的想法转移到与测试代码链接的共享对象上。
/usr/bin/ld --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 \
--hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
-z relro /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o \
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o \
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8 \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib \
-L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu \
-L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../..
test_mem.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc \
--as-needed -lgcc_s --no-as-needed \
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o \
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o
这是gcc用于链接ubuntu 14.04上的测试代码的链接命令。我查找了链接对象和库的所有符号,但找不到__warn_memset_zero_len。神秘感越来越严重。
作为最后的尝试,我假设属性“ warning ”__warn_memset_zero_len的声明使用可能允许旧的gcc编译没有定义的代码。所以我制作了以下测试代码。
extern void __warning_test (void) __attribute__((__warning__ ("test_warning")));
int main(){
__warning_test();
return 0;
}
现在,我可以在两个系统上获得未定义的符号错误。
/tmp/ccN1UbZh.o: In function `main':
test.c:(.text+0x5): undefined reference to `__warning_test'
collect2: error: ld returned 1 exit status
我不知道为什么编译在没有__warn_memset_zero_len定义的旧系统中成功以及为什么它在新系统中失败。另外,为什么与__warn_memset_zero_len看起来完全相同的自定义函数甚至在旧系统上都会失败?你对这个问题有什么想法吗?
答案 0 :(得分:1)
__ warn_memset_zero_len()在/usr/lib/x86_64-linux-gnu/libc.a中声明,它是gcc的隐式库。您可以使用nm命令进行确认。
答案 1 :(得分:0)
我怀疑这是故意的错误,是由这一行引起的:
memset (str, 1, 0);
memset
的声明是:
void *memset(void *s, int c, size_t n);
请注意,您的尺寸/长度值为零。你真正想要的可能是这样的:
char str[] = "almost every programmer should know memset!";
memset (str, 0, sizeof str);
编辑:重新阅读您的帖子,我意识到您可能故意创建此错误?但也许你正试图过多地阅读它。可能是在编译器优化的某个晚期,gcc看到了问题,但是为了给出特定的错误太迟了,因此导致链接错误迫使停止。
另一个想法是:编译器可能会将str
置于写保护内存中,因为它认为这是一个const
字符串。如果发生这种情况,您的memset
无论如何都会死亡。只要str
存储在堆栈中(即在函数内部声明,而不是静态的),我认为你没事。