【问题标题】:valgrind: obtain address of unininitialized memoryvalgrind:获取未初始化内存的地址
【发布时间】:2017-05-26 05:59:52
【问题描述】:

我正在调试一个仅在我的程序的 PPC64 端口中出现的问题。

我有一个测试用例,其中 C 库 qsort 被赋予一个 libffi 生成的闭包作为字符串比较回调。字符串正确传递给回调,返回值精确存储到libffi传递给闭包函数的返回值缓冲区中。

但是,数组未按qsort 正确排序。此外,Valgrind 报告 C 库 qsort 代码正在访问未初始化的内存,--track-orgins=yes 显示该内存是由 Libffi 堆栈分配的。我强烈怀疑这是返回值,因此由于垃圾比较,排序不正确。

即Libffi 为返回值分配了缓冲区,并将该值传播给回调调用者;但是我的闭包调度函数被赋予了错误的指针,因此没有将返回值放在正确的位置。

出于某种奇怪的原因,Valgrind 不报告未初始化内存的地址,只报告代码中使用的位置和分配的位置。

我只是想将该位置的地址与传递给闭包函数的指针进行比较:它们甚至是远程关闭的吗?

有没有办法从 Valgrind 获取这些信息?


更新:我正在使用没有 root 权限的 GCC Compile Farm 机器;安装的 libffi 没有调试信息。它是 3.0.13 版本。

但是,我刚刚构建的 libffi git 头会重现此问题。

我已经确认是未初始化的返回值区。

我在闭包调度汇编代码ffi_closure_LINUX64 中添加了一条指令,用于在闭包调度堆栈帧的RETVAL 部分的底部初始化一个双字大小的区域。这使得 Valgrind 错误消失了;但当然返回值是垃圾。它还证实了一个基本的理智:调用闭包调度助手之前的代码和之后的代码引用相同的返回值区域。 (堆栈指针没有意外移动,并且帧引用是正确的。)只是用户代码最终获得的地址并不指向该返回值。

接下来,我将返回区域的初始化向下移动到名为ffi_closure_helper_LINUX64 的 C 函数中,靠近函数的入口。这也仍然使未初始化的错误消失,确认助手通过%r6(参数4)获取正确的返回值区域地址。

【问题讨论】:

  • 您使用的是哪个版本的 Valgrind?在什么操作系统上?
  • @PaulFloyd 好点。 Valgrind 是 3.9.0,在带有 Glibc 2.18 的 Fedora 系统上。
  • 可以用3.12吗? (3.13即将发布)
  • @PaulFloyd 我现在正在通过 libffi 调试它,使用 git head。

标签: valgrind powerpc libffi


【解决方案1】:

在 valgrind 中没有报告 uninit 内存地址的功能,因为这(在大多数情况下)对用户没有帮助:堆栈地址或堆地址实际上并不能说明太多。

您可能会通过在 Valgrind 报告的帧中设置断点来获得更多信息,并使用 gdb+vgdb+memcheck 监视器命令将堆栈的各个部分标记为已初始化。 将错误位置设置为已初始化时,valgrind 不应再报告错误。您可能需要运行多次,每次都标记堆栈的其他变量/区域。

请参阅http://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.monitor-commands 和 GDB 用户手册,了解如何编写(复杂的)命令在到达断点时运行。

【讨论】:

    【解决方案2】:

    出于某种奇怪的原因,Valgrind 没有报告 未初始化的内存,仅在代码中发生使用的地方和 它被分配到哪里。

    这是 Valgrind Memcheck 工具的记录行为,请参阅manual 的这部分关于--track-orgins=yes

    对于源自堆栈分配的未初始化值, Memcheck 可以告诉您哪个函数分配了该值,但仅此而已 不止于此——通常它会向您显示开口的源位置 函数的大括号。所以你应该仔细检查所有的 函数的局部变量已正确初始化。

    【讨论】:

      【解决方案3】:

      好的,我已经调试好了问题。

      问题在于 LibFFI 中的 PPC64 代码包含与我的期望不符的 big endian 案例。

      我应用了这个测试补丁:

      --- a/src/powerpc/linux64_closure.S
      +++ b/src/powerpc/linux64_closure.S
      @@ -27,7 +27,8 @@
       #define LIBFFI_ASM
       #include <fficonfig.h>
       #include <ffi.h>
      -
      +#undef __LITTLE_ENDIAN__
      +#define __LITTLE_ENDIAN__ 1
              .file   "linux64_closure.S"
      
       #ifdef POWERPC64
      

      我的所有测试都通过了。 __LITTLE_ENDIAN__ 控制的是有条件包含的代码块,如下所示:

      # case FFI_TYPE_INT
      # ifdef __LITTLE_ENDIAN__
              lwa %r3, RETVAL+0(%r1)
      # else
              lwa %r3, RETVAL+4(%r1)
      # endif
              mtlr %r0
              addi %r1, %r1, STACKFRAME
              .cfi_def_cfa_offset 0
              blr
              .cfi_def_cfa_offset STACKFRAME
      

      客户端代码,大端,预计将替换存储的返回值,使其与 8 字节字的顶部对齐。

      所以要存储一个int(四个字节),代码应该是*(int *)(retptr+4) = val,而不是像我的代码那样简单的*(int *)retptr = val

      似乎期望应用程序应该将一个 8 字节的字存储到返回值中,而不管 FFI 类型如何:无论是 char、short、int 还是(64 位)long。也就是说:

      (int64_t)retptr = val; / val 是 char,short,随便 */

      这样,值的最低有效字节位于retptr + 7,因此如果实际类型为char,则使用该地址;如果是short,则使用retptr + 6,以此类推。 FFI 代码以这种方式有意义。问题是它不方便且不一致; FFI 参数不需要这样处理。

      例如,以下调用中的 int 参数不会被 4 个字节移位;它只是写入给 libffi 的缓冲区的基地址

      This is the TXR Lisp interactive listener of TXR 176.
      Use the :quit command or type Ctrl-D on empty line to exit.
      1> (with-dyn-lib nil (deffi printf "printf" int (str : int)))
      #:lib-0185
      2> (printf "foo %d\n" 1)
      foo 1 
      0
      

      但是,哦,看;返回值是假的!外部函数调用返回值也有类似的问题。

      看起来我被一些 libffi 文档中的一个示例所愚弄,即这个:

       #include <stdio.h>
       #include <ffi.h>
      
       int main()
       {
         ffi_cif cif;
         ffi_type *args[1];
         void *values[1];
         char *s;
         int rc;
      
         /* ... abbreviated ... */
             s = "This is cool!";
             ffi_call(&cif, puts, &rc, values);
             /* rc now holds the result of the call to puts */
      
         /* ... */
       }
      

      事实证明,这是不正确的;其他一些 libffi 文档说必须使用类型 ffi_arg 捕获返回值(令人困惑的是,它不用于参数)。所以我认为上面的示例应该是这样的:

      ffi_arg rc_buf;
      int rc;
      /*...*/
      s = "Turned out uncool, but we promise this is really cool now!";
      ffi_call(&cif, puts, &rc_buf, values);
      rc = (int) rc_buf;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        • 2014-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-19
        • 2016-11-03
        相关资源
        最近更新 更多