假设我有一个foo
从给定软件工具调用的函数(在 C/C++ 中)。
函数foo
只允许写入被foo
调用的函数或被调用的函数之一分配的foo
内存,而不能写入调用前已经执行的函数分配的内存foo
。
我强烈怀疑在某些地方foo
不允许写入内存。
有没有办法系统地调试这种行为?也许一些花哨的旗帜给 valgrind?
Valgrind 手册有一些你的程序可以调用的 Valgrind 函数。
看起来VALGRIND_MAKE_MEM_NOACCESS
可能是你想要的。
您可以使用自定义分配器(想到Boost Pool)来确保您要“保护”的所有内存都是连续分配的。
接下来,在该内存区域中的任何数据发生更改时设置硬件断点。
foo() 可以写入其范围之外的内存的唯一方法是,如果该内存是全局的,即外部变量,或者 foo() 具有一个或多个本应只读但不知何故被修改的参数。
要验证调用参数是否被修改,您可以创建一个结构来保存参数,并在返回之前将原始参数与保存的参数进行比较。
struct foo_args {
int a;
char *b;
};
void
foo(int a, char *b)
{
struct foo_args args;
args.a = a
args.b = strdup(b);
/* The rest of the foo() code. */
if (args.a != a || strcmp(args.b, b) != 0) {
printf("error - args got modified\n");
}
free(args.b);
}
如果上面没有发现它,那么可能的情况是全局、堆栈或堆内存正在损坏。
要让 valgrind 运行该工具可能不实用,在这种情况下,您需要为 foo() 创建一个“包装器”并确保使用 valgrind 或类似的东西,它不会做不应该做的事情。另一种选择是使用调试库来跟踪/监控内存使用情况并在内存错误发生时标记它们。
我会编写一个 GDB 脚本,在您的函数上设置断点,然后在您怀疑正在更改的内存上设置硬件监视,然后继续。
如果函数foo
正在修改该内存,则硬件手表将在执行该操作的指令上触发。
GDB 脚本可能如下所示:
break foo
commands
up
watch array
down
continue
end
我没有对此进行测试,它可能需要调整,尤其是手表表达式。您可能仅限于查看一个数组元素。我相信硬件观察点实际上只能观察一个整数大小的块:32 位上的 4 个字节或 64 位上的 8 个字节。