我试图找出我的程序中使用nm
变量的地址和大小,我刚刚意识到我的一大堆变量出乎意料地大。我创建了一个测试文件"test.c":
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
我假设某种填充/对齐在这里起作用,但我不明白为什么填充成为符号的一部分。是否有一种方法来产生一个类似的文本日志,其中test1
和test2
都有1的大小?
…我不明白为什么填充符会成为符号的一部分。
nm
手册页说:"大小计算为符号值与下一个高值的符号值之间的差值。"因此,如果在变量A之后和变量B之前有填充,则填充将作为A大小的一部分出现。
在您的示例中,test1
显然紧随test2
之后,因此test1
的大小被计算为一个字节。test2
的程序段后面没有任何显式符号;nm
使用的下一个"符号"可能是下一节的开始或其中的第一个符号。下一节有一些对齐要求,所以在test2
之后和下一节之前有未使用的空间,也称为填充。所以test2
和下一个"符号"之间的差异包括填充,它显示在test2
的"大小"中,如手册页所述。
我不明白为什么填充符会成为符号的一部分
根据手册页: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
最后,如果没有调试信息,似乎没有办法知道PE中的符号大小。使用调试信息,可以使用objdump -W a.exe
提取它,然后使用DW_AT_type
查找符号类型,然后使用DW_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)