如何找到替换传递的函数参数的未定义行为?

问题描述

我正在尝试调用 fileUtilcopyRecurse 为其提供您可以在下面看到的参数。当我进入函数时,参数 2-4 被替换为无意义(参数 1 没问题)。

(下面继续……)

Breakpoint 1,createTestDir (T=0x5555555cabc0 <Staticmem>,MakeTargetNewer=0 '\000') at ../backuper/backuperTest.c:64
64      fileUtilcopyRecurse(T,DirInBoth1,DirInBoth2,false);
(gdb) p DirInBoth1
$1 = (utf8 *) 0x5555555aac68 "../backuper/testdir/1/dir-in-both/"
(gdb) p DirInBoth2
$2 = (utf8 *) 0x5555555aac90 "../backuper/testdir/2/dir-in-both/"
(gdb) s
fileUtilcopyRecurse (T=0x5555555cabc0 <Staticmem>,P=0x5555556bef88 <nonstd_creat_f> "\360\n\354\367\377\177",To=0x5555555a929e "creat",Recurse=1 '\001') at ../fileUtil/fileUtil.c:271
271 fct void fileUtilcopyRecurse(void* T,pathC* P,pathC* To,bool Recurse) { // if dir and Recurse set then recursively copies it
(gdb) p P
$3 = (pathC *) 0x5555556bef88 <nonstd_creat_f> "\360\n\354\367\377\177"

一开始我以为调用者.o-File和被调用者.o-File在签名上不一致。 typedef 是不同的,因为它们是代码库的两个不同部分,调用者使用 utf8,即 typedef char,被调用者使用 pathC,即 typedef const char。所以我们给一个 const char* 一个 char* ,这很好。

为了确保一致性,我检查了 .c 文件是否包含相同的 .h 文件(指定 fileUtilcopyRecurse)并重新编译所有内容。然后我确定 fileUtilcopyRecurse 由于某种原因不是宏。

这发生在 64 位 Linux 上的 GCC 9.3.0。我启用了很多警告并且禁用了优化(-O0)。我将代码带到另一台装有 GCC 7.5.0(也是 64 位 Linux)的计算机上,并且运行正常。

所以我想我在某处有未定义的行为,我向您询问要寻找什么的提示

对拆解感兴趣的朋友:

(gdb) x/6i $pc
=> 0x5555555a7ca8 <createTestDir+245>:  mov    -0x28(%rbp),%rdx
   0x5555555a7cac <createTestDir+249>:  mov    -0x30(%rbp),%rsi
   0x5555555a7cb0 <createTestDir+253>:  mov    -0x38(%rbp),%rax
   0x5555555a7cb4 <createTestDir+257>:  mov    $0x0,%ecx
   0x5555555a7cb9 <createTestDir+262>:  mov    %rax,%rdi
   0x5555555a7cbc <createTestDir+265>:  callq  0x55555559a9de <fileUtilcopyRecurse>
(gdb) si
0x00005555555a7cac  64      fileUtilcopyRecurse(T,false);
1: x/i $pc
=> 0x5555555a7cac <createTestDir+249>:  mov    -0x30(%rbp),%rsi
(gdb) 
0x00005555555a7cb0  64      fileUtilcopyRecurse(T,false);
1: x/i $pc
=> 0x5555555a7cb0 <createTestDir+253>:  mov    -0x38(%rbp),%rax
(gdb) 
0x00005555555a7cb4  64      fileUtilcopyRecurse(T,false);
1: x/i $pc
=> 0x5555555a7cb4 <createTestDir+257>:  mov    $0x0,%ecx
(gdb) 
0x00005555555a7cb9  64      fileUtilcopyRecurse(T,false);
1: x/i $pc
=> 0x5555555a7cb9 <createTestDir+262>:  mov    %rax,%rdi
(gdb) 
0x00005555555a7cbc  64      fileUtilcopyRecurse(T,false);
1: x/i $pc
=> 0x5555555a7cbc <createTestDir+265>:  callq  0x55555559a9de <fileUtilcopyRecurse>
(gdb) info reg
rax            0x5555555cabc0      93824992717760
rbx            0x5555555a8670      93824992577136
rcx            0x0                 0
rdx            0x5555555aac90      93824992586896
rsi            0x5555555aac68      93824992586856
rdi            0x5555555cabc0      93824992717760
rbp            0x7fffffffdc90      0x7fffffffdc90
rsp            0x7fffffffdc50      0x7fffffffdc50
r8             0x0                 0
r9             0x7fffffffd830      140737488345136
r10            0x7ffff7db407c      140737351729276
r11            0x246               582
r12            0x5555555610d0      93824992284880
r13            0x7fffffffdf80      140737488347008
r14            0x0                 0
r15            0x0                 0
rip            0x5555555a7cbc      0x5555555a7cbc <createTestDir+265>
eflags         0x207               [ CF PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) 

解决方法

如果在函数序言中执行已停止,则调试器中显示的参数值可以是影子空间(在寄存器中传递的参数存储在堆栈中的堆栈空间)的未初始化值。继续执行另一个语句应该允许序言将允许填充这些影子变量并让调试器显示参数的正确值。

检查是否是这种情况的一种方法是查看反汇编视图以查看执行已停止的确切位置。