C 是否有编译器选项可用于将堆栈变量初始化为非零值(毒物)以帮助调试?

C 是否有编译器选项可用于将堆栈变量初始化为非零值(毒物)以帮助调试?,c,linux,gcc,memory,stack,C,Linux,Gcc,Memory,Stack,C程序中一种常见的错误类型是程序使用一些未初始化的数据,通常假设某个数据为零,而实际上它从未初始化为零。这样一个程序似乎可以工作,因为这些内存位置恰好是零,但有一天那里有一些垃圾,你的程序崩溃了 我知道valgrind是发现这些问题的一个很好的工具。但有时不能使用valgrind,例如,如果程序以非标准方式进行内存分配 我的问题:gcc(或clang)是否有一些编译器选项可以用来要求编译器将局部变量初始化为一些非零的“毒药”值,以暴露这种bug 我认为编译器在技术上应该可以做到这一点,在每次函数

C程序中一种常见的错误类型是程序使用一些未初始化的数据,通常假设某个数据为零,而实际上它从未初始化为零。这样一个程序似乎可以工作,因为这些内存位置恰好是零,但有一天那里有一些垃圾,你的程序崩溃了

我知道valgrind是发现这些问题的一个很好的工具。但有时不能使用valgrind,例如,如果程序以非标准方式进行内存分配

我的问题:gcc(或clang)是否有一些编译器选项可以用来要求编译器将局部变量初始化为一些非零的“毒药”值,以暴露这种bug

我认为编译器在技术上应该可以做到这一点,在每次函数调用时插入一些指令,将这些数据放入通常未初始化的堆栈变量的内存空间。可能会有一些性能成本,但与使用valgrind相比成本较低,而且在某些情况下可能无法使用valgrind


编辑:让我澄清一下,这个问题与编译器警告无关。当然,编译器警告非常有用,应该打开它们并加以处理,但这并不能解决未初始化数据的所有问题。例如,程序可能会获取一个局部变量的地址并将其传递给函数,那么编译器将不知道是否传递了该地址以允许函数在那里复制数据(这很好),或者函数是否将使用指向的数据(这意味着使用未初始化的数据)。

编译器默认情况下会这样做:

int foo()
{
    int y;

    if(y > 0) return 0;
    return 1;
}
编译器警告您:

source>: In function 'foo':

<source>:11:7: warning: 'y' is used uninitialized in this function [-Wuninitialized]

   11 |     if(y > 0) return 0;

      |       ^

Compiler returned: 0
source>:在函数“foo”中:
:11:7:警告:“y”在此函数中未初始化使用[-Wuninitialized]
11 |如果(y>0)返回0;
|       ^
编译器返回:0

只需启用所有警告,不要忽略它们。Yoiu不需要其他任何东西

默认情况下,编译器会这样做:

int foo()
{
    int y;

    if(y > 0) return 0;
    return 1;
}
编译器警告您:

source>: In function 'foo':

<source>:11:7: warning: 'y' is used uninitialized in this function [-Wuninitialized]

   11 |     if(y > 0) return 0;

      |       ^

Compiler returned: 0
source>:在函数“foo”中:
:11:7:警告:“y”在此函数中未初始化使用[-Wuninitialized]
11 |如果(y>0)返回0;
|       ^
编译器返回:0
只需启用所有警告,不要忽略它们。Yoiu不需要任何其他东西

是的——叮当声有这个功能

以下是这些文件的简短摘录:

如果检测到错误,程序将打印错误消息到 stderr和exit使用非零退出代码

% ./a.out WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x7f45944b418a in main umr.cc:6
    #1 0x7f45938b676c in __libc_start_main libc-start.c:226
您还可以使用
-fsanizize memory track origins
获取有关该问题的更多信息

有关用法、运行时成本和其他提示的详细信息,请参阅完整文档(链接在上面)。

是--clang有

以下是这些文件的简短摘录:

如果检测到错误,程序将打印错误消息到 stderr和exit使用非零退出代码

% ./a.out WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x7f45944b418a in main umr.cc:6
    #1 0x7f45938b676c in __libc_start_main libc-start.c:226
您还可以使用
-fsanizize memory track origins
获取有关该问题的更多信息


有关使用、运行时成本和其他提示的详细信息,请参阅完整文档(上面链接)。

要总结到目前为止的主题:

  • Q:自动初始化局部变量的编译器选项
  • init可能包括添加的代码
  • 已涵盖编译器警告及其优点
简短回答:没有明显的
gcc
选项来初始化局部变量 如前所述,至少不是靠它自己

虽然gcc-finstrument函数对于自定义评测非常有用, 由相关用户提供的例程执行的非常规工作可能会初始化空间 由调用方堆栈框架内的本地人使用。但这能做到吗 可靠地

使用
-finstrument函数构建源代码后,将出现编译器
生成了对
\uuuuucyg\u profile\u func\u enter()
\uuuuucyg\u profile\u func\u exit()
的调用。 为了便于讨论,此处将其别名为较短的符号
cyg\u enter()
cyg\u exit()

创建一个单独的文件,例如
cyg.c
,不使用
-仪表函数
——避免递归。添加存根例程
cyg\u exit()
并提供
cyg\u enter()
如下内容:

  • 获取调用方堆栈帧的信息
  • 为调用者的帧设置局部变量
    low\u addr
    size
    ,并根据需要进行调整
  • 并编写一个模式,例如:
    memset(low\u addr,'\x1f',size)
如果这一想法奏效——可能需要改进和限制: 从
cyg\u返回时,enter()
调用方 框架现在基于模式

====

一般来说,我发现一些编译器警告导致了改进/小心 避免意外的编码风格。在缺少编译器选项的情况下 如前所述,我不会提升私有堆栈编写器方法 对于一般用途,尽管它可能有一些实用性。记住这个想法是正确的 仅限于初始化局部变量的可能方法。而不是评论
是否在书堆上涂鸦的优点,根据需要创建一个单独的帖子。

要总结到目前为止的主题:

  • Q:自动初始化局部变量的编译器选项
  • init可能包括添加的代码
  • 已涵盖编译器警告及其优点
简短回答:没有明显的
gcc
选项来初始化局部变量 如前所述,至少不是靠它自己

虽然
gcc-finstrument函数对于自定义评测非常有用, 由相关用户提供的例程执行的非常规工作可能会初始化空间 由调用方堆栈框架内的本地人使用。但这能做到吗 可靠地

使用
-finstrument函数构建源之后,将