2017-05-26 55 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; 
相關問題