调试-如何从优化的C程序中转储动态分配的数组?

r7xajy2e  于 2023-05-06  发布在  其他
关注(0)|答案(1)|浏览(98)

我有一个C程序,它有一个动态分配的数组,它的基指针是一个全局变量。下面是一个简短的示例程序:

double *array = NULL;

void foo() { /* complex calculations on array */ }
void bar() { /* complex calculations on array */ }

int main() {
  array = malloc( /* large size */ );
  foo();
  bar();
}

我必须用gcc -O2 -no-pie program.c -lm编译它。我可以用objdump -D -S a.out反汇编程序,找到array的位置和各种具体指令。我想写一个脚本,在没有我干预的情况下完成以下所有操作:
1.在某种调试状态下运行程序,并等待到达特定的指令--我假设指令总是在特定的内存位置,因为我在编译时使用了-no-pie
1.遵循全局array指针指向动态分配的内存-我再次假设-no-pie将始终确保全局指针位于特定的内存位置。
1.从动态分配的内存中转储特定数量的字节到文件中。
我看了一下gdb,但是大多数教程都提到使用-g -Og进行编译,我更愿意避免使用-g -Og,而使用检查点的原始内存位置和array的位置。使用gdb可以做到这一点吗?或者我必须使用其他工具?

更新:

我将使用一个架构模拟器来有意地在程序的各个点注入位翻转。我想确定位翻转是如何破坏程序数据的,特别是数组。我想知道数组在程序中不同点的值,以了解位翻转的效果如何传播。
我不能在源代码中的程序中间添加额外的打印函数,因为我正在测试的程序必须与一个一次完成整个计算而没有任何打印的程序相当。

0md85ypi

0md85ypi1#

用gdb可以吗
是的,广发行完全有能力做到这一点。
让我们从一个 * 实际 * 的工作示例开始:

#include <stdlib.h>
#include <stdio.h>

const int SZ = 20;
double *array = NULL;

void foo()
{
  for (int j = 0; j < SZ; j++)
    array[j] = 2.0;
}

void bar()
{
  for (int j = 0; j < SZ; j++)
    array[j] /= 3.0;
}

int main()
{
  array = malloc(sizeof(double) * SZ);
  foo();
  __asm__("nop");  // For a breakpoint
  bar();
  __asm__("nop");  // For a breakpoint

  return 0;
}

gcc -O2 -fno-inline -no-pie t.c编译它(使用-fno-inline,因为否则foo()会内联)。

gdb -q ./a.out

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000401040 <+0>:     sub    $0x8,%rsp
   0x0000000000401044 <+4>:     mov    $0xa0,%edi
   0x0000000000401049 <+9>:     callq  0x401030 <malloc@plt>
   0x000000000040104e <+14>:    mov    %rax,0x2fcb(%rip)        # 0x404020 <array>
   0x0000000000401055 <+21>:    xor    %eax,%eax
   0x0000000000401057 <+23>:    callq  0x401160 <foo>
   0x000000000040105c <+28>:    nop
   0x000000000040105d <+29>:    xor    %eax,%eax
   0x000000000040105f <+31>:    callq  0x401190 <bar>
   0x0000000000401064 <+36>:    nop
   0x0000000000401065 <+37>:    xor    %eax,%eax
   0x0000000000401067 <+39>:    add    $0x8,%rsp
   0x000000000040106b <+43>:    retq
End of assembler dump.

(gdb) b *0x000000000040105c
Breakpoint 1 at 0x40105c
(gdb) b *0x0000000000401064
Breakpoint 2 at 0x401064

(gdb) run
Starting program: /tmp/a.out

Breakpoint 1, 0x000000000040105c in main ()

(gdb) p &array
$1 = (<data variable, no debug info> *) 0x404020 <array>

(gdb) p (*(double**)0x404020)[0]@20
$2 = {2 <repeats 20 times>}

(gdb) c
Continuing.

Breakpoint 2, 0x0000000000401064 in main ()
(gdb) p (*(double**)0x404020)[0]@20
$3 = {0.66666666666666663 <repeats 20 times>}

正如你所看到的,我们已经观察到了我们想要的所有值。
您可以将两个断点的地址和array的地址硬编码到脚本中(这里是gdb.script):

break *0x40105c
break *0x401064
run
print (*(double**)0x404020)[0]@20
cont
print (*(double**)0x404020)[0]@20
cont

然后以gdb -batch -x gdb.script ./a.out的形式运行gdb,生成:

Breakpoint 1 at 0x40105c
Breakpoint 2 at 0x401064
Breakpoint 1, 0x000000000040105c in main ()
$1 = {2 <repeats 20 times>}

Breakpoint 2, 0x0000000000401064 in main ()
$2 = {0.66666666666666663 <repeats 20 times>}
[Inferior 1 (process 1838617) exited normally]

唯一剩下的部分是“将字节转储到文件中”,这可以通过dump memory GDB命令来实现:

(gdb) help dump memory
Write contents of memory to a raw binary file.
Arguments are FILE START STOP.  Writes the contents of memory within the
range [START .. STOP) to the specified FILE in raw target ordered bytes.

P.S.你也可以通过将断点设置为silent模式来摆脱Breakpoint 1, 0x000000000040105c in main ()输出,如下所示:

break *0x40105c
commands
  silent
  print (*(double**)0x404020)[0]@20
  cont
end

break *0x401064
commands
  silent
  print (*(double**)0x404020)[0]@20
  cont
end

run

输出变为:

Breakpoint 1 at 0x40105c
Breakpoint 2 at 0x401064
$1 = {2 <repeats 20 times>}
$2 = {0.66666666666666663 <repeats 20 times>}
[Inferior 1 (process 1839098) exited normally]

相关问题