【问题标题】:Where is the definition of __warn_memset_zero_len in string3.hstring3.h 中 __warn_memset_zero_len 的定义在哪里
【发布时间】:2017-03-24 16:12:24
【问题描述】:

最近我将我的项目从 ubuntu 14.04 移到了 ubuntu 16.04。当我使用从 os 存储库安装的 gcc 4.8.5 编译我的项目时,我遇到了一个错误“undefined __warn_memset_zero_len”。我从来没有在 ubuntu 14.04 和 gcc 4.8.4 上看到过这个错误。

__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;
}

此代码也仅在带有 gcc 4.8.4 的 ubuntu 14.04 中编译。所以,首先,我假设新的 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 看起来一模一样的自定义函数即使在旧系统上也会失败?你对这个问题有什么想法吗?

【问题讨论】:

    标签: c linux gcc linker


    【解决方案1】:

    __warn_memset_zero_len() 在 /usr/lib/x86_64-linux-gnu/libc.a 中声明,它是 gcc 的隐式库。你可以使用 nm 命令来确认。

    【讨论】:

    • 非常感谢。我不知道为什么,但是 ubuntu 16.04 中的 libc.a 不包含该符号。
    • @Hani 如函数名所示,该函数用于检查memset的参数。从 Ubuntu16.04 的默认编译器 gcc5.0 开始,这个参数检查是在 gcc 中实现的,因此不需要在 libc.a 中包含检查功能。因此它在 Ubuntu16.04 中被删除。
    【解决方案2】:

    我怀疑这是一个故意的错误,是由这一行引起的:

    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 存储在堆栈中(即在函数中声明,而不是静态的),你就可以了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 2013-04-09
      • 2015-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多