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

How to find the Undefined Behavior that replaces the passed function arguments?

我正在尝试调用 fileUtilCopyRecurse 并为其提供您可以在下面看到的参数。当我进入函数时,参数 2-4 被替换为废话(参数 1 可以)。

(下继续...)

Breakpoint 1, createTestDir (T=0x5555555cabc0 <StaticMem>, MakeTargetNewer=0 '[=10=]0') at ../backuper/backuperTest.c:64
64      fileUtilCopyRecurse(T, DirInBoth1, DirInBoth2, false);
(gdb) p DirInBoth1
 = (utf8 *) 0x5555555aac68 "../backuper/testdir/1/dir-in-both/"
(gdb) p DirInBoth2
 = (utf8 *) 0x5555555aac90 "../backuper/testdir/2/dir-in-both/"
(gdb) s
fileUtilCopyRecurse (T=0x5555555cabc0 <StaticMem>, P=0x5555556bef88 <nonstd_creat_f> "0\n4777", To=0x5555555a929e "creat", Recurse=1 '[=10=]1') 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
 = (pathC *) 0x5555556bef88 <nonstd_creat_f> "0\n4777"

起初我以为调用者.o-File 和被调用者.o-File 签名不一致。 typedef 是不同的,因为它们是代码库的两个不同部分,调用者使用 utf8,它是 typedef char,被调用者使用 pathC,它是 typedef const char。所以我们将一个 char* 赋给一个 const 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    [=11=]x0,%ecx
   0x5555555a7cb9 <createTestDir+262>:  mov    %rax,%rdi
   0x5555555a7cbc <createTestDir+265>:  callq  0x55555559a9de <fileUtilCopyRecurse>
(gdb) si
0x00005555555a7cac  64      fileUtilCopyRecurse(T, DirInBoth1, DirInBoth2, false);
1: x/i $pc
=> 0x5555555a7cac <createTestDir+249>:  mov    -0x30(%rbp),%rsi
(gdb) 
0x00005555555a7cb0  64      fileUtilCopyRecurse(T, DirInBoth1, DirInBoth2, false);
1: x/i $pc
=> 0x5555555a7cb0 <createTestDir+253>:  mov    -0x38(%rbp),%rax
(gdb) 
0x00005555555a7cb4  64      fileUtilCopyRecurse(T, DirInBoth1, DirInBoth2, false);
1: x/i $pc
=> 0x5555555a7cb4 <createTestDir+257>:  mov    [=11=]x0,%ecx
(gdb) 
0x00005555555a7cb9  64      fileUtilCopyRecurse(T, DirInBoth1, DirInBoth2, false);
1: x/i $pc
=> 0x5555555a7cb9 <createTestDir+262>:  mov    %rax,%rdi
(gdb) 
0x00005555555a7cbc  64      fileUtilCopyRecurse(T, DirInBoth1, DirInBoth2, 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) 

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

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