2017-05-26 53 views
0

我正在调试只发生在我的程序的PPC64端口的问题。valgrind:获取未初始化内存的地址

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

然而,该阵列未正确通过qsort排序。此外,Valgrind报告说C库qsort代码正在访问未初始化的内存,并且--track-orgins=yes显示这个内存是由Libffi堆栈分配的。我强烈怀疑这是返回值,因此垃圾比较排序不正确。

I.e. Libffi为返回值分配缓冲区,并将其值传播给回调调用者;但是我的闭包调度函数正在被赋予错误的指针,所以不会将返回值放在正确的位置。

由于一些离奇的原因,Valgrind没有报告未初始化内存的地址,只有在代码中使用发生的地方和分配的地方。

我只是希望那个位置的地址比较传递给closure函数指针:他们甚至远程关闭?

是否有某种方式来获取信息了Valgrind的吗?


更新:我正在一个GCC编译农业机械在那里我没有根上;已安装的libffi没有调试信息。它是3.0.13版。

然而,这个问题与我刚刚建立了libffi混帐头再现。

我已经证实了它的返回值区域是未初始化。

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

接下来,我感动折返区域的初始化下来到名为ffi_closure_helper_LINUX64 C函数,进入功能附近。这还会使未初始化的错误消失,确认帮助者通过%r6(参数4)获得正确的返回值区域地址。

+0

您正在使用哪个版本的Valgrind?并在什么操作系统? –

+0

@PaulFloyd好点。在Glibc 2.18的Fedora系统上,Valgrind是3.9.0。 – Kaz

+0

是否可以使用3.12? (3.13即将发布) –

回答

0

有一个在Valgrind的无功能报告UNINIT内存的地址,因为这将(在大多数情况下)没有帮助用户:一个堆栈地址或堆地址不能真正表明了。

通过在Valgrind报告的框架中设置一个断点,并使用gdb + vgdb + memcheck monitor命令将堆栈的各个部分标记为初始化,您可能会获得更多信息。 当设置故障位置初始化,Valgrind的应该不会再报告错误。您可能需要执行多次运行,每次标记堆栈的其他变量/区域。

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

0

对于一些奇怪的原因,Valgrind的不报告 未初始化的内存,并且它被分配的地址,只在代码发生使用 。

这是记录Valgrind的MEMCHECK工具的行为,看到的manual这部分约--track-orgins=yes

对于从堆栈分配始发未初始化值, MEMCHECK可以告诉你哪些函数分配的值,但没有比这更 - 通常,它显示了该功能的开放 括号的源位置。所以你应该仔细检查所有 函数的局部变量是否被正确初始化。

0

好吧,我调试的问题。

的问题是,在LibFFI的PPC64代码包含大端不符合我的预期的情况下。

我应用这个测试补丁:

--- 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类型的:它是一个字符,短,int或(64位)长。这就是说:

(的int64_t)retptr = VAL;/ val为字符,短,无论*/

这样的值的至少显著字节位于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; 
相关问题