如何在结构体转储中获取字段的相对地址。[C]

14

我们正在Linux下使用arm-eabi-gcc编译C程序。

我们正在使用一个大型结构的转储,并且我们无法确定应该读取结构的各个字段(例如50个字段)的地址(内存对齐和填充对我来说不太可预测)。

是否有任何方法可以获取由我们的编译器生成的结构的内存映射。 Gdb中的选项?或者任何帮助我们在转储中找到字段和地址对应关系的工具?


我可能在这里漏掉了一些明显的东西,但是如果你有一个声明为结构体实例(或者结构体指针)的变量(比如说x),那么你应该能够在 gdb 中只输入 print x (或者如果是指针,则为 print *x)来转储所有成员。 - Mark Wilkins
我不是在尝试获取我想要转储的地址。我已经有了这个转储,并需要所有成员的相对地址。 - Akhneyzar
4个回答

49

您可以使用 gdb 来完成此操作。作为一个例子,我将使用这个源码:

struct A {
  int a;
  char b;
  short c;
};

int main() {
  struct A a;
}

gdb 中加载二进制文件:

(gdb) print (int)&((struct A*)0)->a
$1 = 0
(gdb) print (int)&((struct A*)0)->b
$2 = 4
(gdb) print (int)&((struct A*)0)->c
$3 = 6

更新:

如果你需要对大量字段进行操作,那么使用GDB的新Python接口可能会很方便(你需要使用最新版本的GDB才能使用它,我正在使用7.4版本)。我创建了offsets.py文件:

import gdb

class Offsets(gdb.Command):
    def __init__(self):
        super (Offsets, self).__init__ ('offsets-of', gdb.COMMAND_DATA)

    def invoke(self, arg, from_tty):
        argv = gdb.string_to_argv(arg)
        if len(argv) != 1:
            raise gdb.GdbError('offsets-of takes exactly 1 argument.')

        stype = gdb.lookup_type(argv[0])

        print argv[0], '{'
        for field in stype.fields():
            print '    %s => %d' % (field.name, field.bitpos//8)
        print '}'

Offsets()

然后你可以在你的 .gdbinit 文件中添加:

python
sys.path.insert(0, '/path/to/script/dir')
import offsets
end

然后在GDB中使用它,如下所示:

(gdb) offsets-of "struct A"
struct A {
    a => 0
    b => 4
    c => 6
}

这个脚本做了一些简化的假设,比如你不使用位域,并且它不会深入嵌套的结构体,但是如果需要,这些更改相对来说还是比较简单的。


1
我理解,但我想在那里列出需要的50个字段。 - Akhneyzar
我制作了一个更完整的版本,感谢你的启发。点击 - AStupidNoob
1
你的.gdbinit看起来很奇怪。为什么不使用source /path/to/script/dir/offsets.py呢? - youfu
据我所知,source 用于运行其他 gdb 命令。因此,您可以将该片段放入其他文件中并进行源化,但我不认为您可以直接源化 Python 脚本。 - FatalError
@FatalError: 这里有文档[https://sourceware.org/gdb/onlinedocs/gdb/Python-Commands.html]。请注意Python版本(2/3),确保您没有使用gdb-minimal。 - youfu
@zhangyoufu:很好,谢谢:-)。我不确定这是新的(我已经有几年没有使用gdb了)还是我只是错过了它,但知道这一点很方便。 - FatalError

15

如果有人在几年之后阅读这篇文章,或者通过搜索引擎结果找到了它:自GDB 8.1以来,其内置的ptype命令具有用于此目的的/o标志。
它将打印MyType成员的偏移量和大小,还会显示“空洞”(填充),例如:

(gdb) ptype /o MyType
/* offset    |  size */  type = class MyType {
/*  0        |     4 */    int foo;
/* XXX  4-byte hole  */
/*  8        |     8 */    void* ptr;
                           /* total size (bytes):  16 */
                         }


1
在我看来,这应该是被接受的答案。我很高兴不必再为了这些基本信息而费心地调试pahole。谢谢(: - jwd

4

您可以使用标准的stddef.h中定义的offsetof()宏从C程序中执行此操作。但是我不确定这是否是您想要的,因为您可能无法运行它(在主机上编译将返回错误的偏移量)。

#include <stdio.h>
#include <stddef.h>

struct A {
  int a;
  char b;
  short c;
};

int main() {
    printf("Offset of b in A is %zu\n", offsetof(struct A, b));
    return 0;
}

然而,您可以使用一些技巧来获取已编译二进制文件的偏移量而不执行它。也许将偏移值分配给静态变量,并找到某种方式来获取其值。


使用在线GCC或在线ASM编译器快速获取值 :) - kolypto

-2

对我来说,你可以编写一些类似于这样的代码来处理必填字段

struct MyStruct S;
int Offset_of_X=((long)&(S.X))-((long)&S);

计算在此编译情况下的字节偏移量。

这应该考虑到编译器可能存在的任何对齐问题。


但是对于50个或更多字段,它缺乏效率。 - Akhneyzar
这段代码无法编译:指针算术运算不允许你从指向 X 的指针中减去指向 S 的指针。 - Didier Trosset

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接