“nm”报告相同类型的变量的不同大小。我如何知道它们的真实尺寸?

'nm' reports different sizes for variables of the same type. How do I find out their real size?

提问人:Dmitry Grigoryev 提问时间:4/6/2023 更新时间:4/11/2023 访问量:165

问:

我试图找出我的程序中变量的地址和大小,我刚刚意识到我的一大堆变量出乎意料地大。我制作了以下测试文件“test.c”:nm

static char test1 = 0;
static char test2 = 0;

char test_f(void)
{
    test1 = test2;
    return test2;
}

int main(void)
{
    return test_f();
}

然后我运行以下命令:

gcc test.c
nm -C -S --size-sort a.exe | findstr /rc:"test"

输出是

0000000140007040 0000000000000001 b test1
0000000140007041 000000000000000f b test2
0000000140001540 000000000000001a T test_f

我假设这里有某种填充/对齐在起作用,但我不明白为什么填充会成为符号的一部分。有没有办法生成类似的文本日志,其中 和 的大小均为 1?test1test2

C 明格纳米

评论

1赞 teapot418 4/6/2023
符号大小计算为符号值与下一个较高值的符号值之间的差值
0赞 stark 4/6/2023
nm 不报告变量的大小;它报告对象文件中符号的大小。其中一些符号对应于为符号分配的空间中包含的程序变量。

答:

2赞 Eric Postpischil 4/6/2023 #1

...我不明白为什么填充物会成为符号的一部分。

nm 手册页显示“大小计算为符号值与具有下一个较高值的符号值之间的差值。因此,如果在变量 A 之后和变量 B 之前有填充,则填充将显示为 A 大小的一部分。

在您的示例中,显然紧随其后的是 ,因此 的大小被计算为一个字节。 在其程序部分中没有任何明确的符号;使用的下一个“符号”可能是下一节的开头或其中的第一个符号。下一部分有一些对齐要求,因此在下一部分之后和之前有未使用的空间,也称为填充。因此,与下一个“符号”之间的差异包括该填充,并且它显示在手册页所述的“大小”中。test1test2test1test2nmtest2test2test2

评论

0赞 Dmitry Grigoryev 4/6/2023
有没有办法找出符号的实际大小,也许使用链接器映射或调试信息?
2赞 teapot418 4/6/2023 #2

我不明白为什么填充会成为符号的一部分

根据手册页: https://man7.org/linux/man-pages/man1/nm.1.html

ELF 格式记录符号的大小,其他格式(如 EXE)将仅报告从此符号开始到下一个符号开始的间隔大小。

有没有办法生成类似的文本日志,其中 test1 和 test2 两者的大小都是 1 吗?

在使用 ELF 二进制文件的 Linux 中执行相同的步骤。

0000000000004011 0000000000000001 b test1
0000000000004012 0000000000000001 b test2

评论

0赞 Dmitry Grigoryev 4/6/2023
不幸的是,我需要此信息来获取 .exe。从技术上讲,我可以为 Linux 构建它,发现那里的可变大小,并将其与 中的偏移信息合并,但这听起来很复杂且容易出错。是否有可能通过链接器映射从 MinGW 中获得可变大小?nm
0赞 teapot418 4/6/2023
@DmitryGrigoryev只做编译而不做链接是没有帮助的,mingw 会生成 COFF 对象,这同样不是 ELF。我目前没有看到一个简单而实用的可能性。
1赞 ikegami 4/6/2023
@Dmitry Grigoryev,Re “我可以为 Linux 构建它,发现那里的可变大小”,那也行不通。它们可能不同。特别是,在 Windows 和 Linux 中具有不同的大小,在类似的硬件上。long
0赞 Dmitry Grigoryev 4/11/2023
@ikegami确实容易出错!
0赞 Dmitry Grigoryev 4/11/2023 #3

最后,如果没有调试信息,似乎无法知道 PE 中的符号大小。使用调试信息,可以提取它,然后用 查找符号类型,然后用 找出该类型的大小:objdump -W a.exeDW_AT_typeDW_AT_byte_size

 <1><28ce>: Abbrev Number: 2 (DW_TAG_variable)
    <28cf>   DW_AT_name        : test1
    <28d5>   DW_AT_decl_file   : 1
    <28d6>   DW_AT_decl_line   : 3
    <28d7>   DW_AT_decl_column : 13
    <28d8>   DW_AT_type        : <0x28e6>
    <28dc>   DW_AT_location    : 9 byte block: 3 40 70 0 40 1 0 0 0     (DW_OP_addr: 140007040)
 <1><28e6>: Abbrev Number: 3 (DW_TAG_base_type)
    <28e7>   DW_AT_byte_size   : 1
    <28e8>   DW_AT_encoding    : 6  (signed char)
    <28e9>   DW_AT_name        : char
 <1><28ee>: Abbrev Number: 2 (DW_TAG_variable)
    <28ef>   DW_AT_name        : test2
    <28f5>   DW_AT_decl_file   : 1
    <28f6>   DW_AT_decl_line   : 4
    <28f7>   DW_AT_decl_column : 13
    <28f8>   DW_AT_type        : <0x28e6>
    <28fc>   DW_AT_location    : 9 byte block: 3 41 70 0 40 1 0 0 0     (DW_OP_addr: 140007041)