如何找到替换传递的函数参数的未定义行为?
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 其中传入的参数寄存器存储在堆栈中)。继续执行另一条语句应该允许序言允许填充这些影子变量并让调试器显示参数的正确值。
检查是否是这种情况的一种方法是查看反汇编视图以查看执行停止的确切位置。
我正在尝试调用 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 其中传入的参数寄存器存储在堆栈中)。继续执行另一条语句应该允许序言允许填充这些影子变量并让调试器显示参数的正确值。
检查是否是这种情况的一种方法是查看反汇编视图以查看执行停止的确切位置。