3

如果我有tmp.c

char constantFOO[0x12];
char constantBAR[0x34];

我看gcc -c tmp.c -o tmp.o && nm tmp.o节目

0000000000000034 C constantBAR
0000000000000012 C constantFOO

但是,如果我使用 编译-flto -ffat-lto-objectsnm则符号值的输出为零:

00000000 C constantBAR
00000000 C constantFOO

我可以在两个文件的十六进制转储中使用34和值。12.o

我的问题是

  1. nmLTO fat 文件上的行为是预期的吗?我只是给它不期望的输入并且它正在输出垃圾吗?

  2. 什么解释了原始输出(符号值匹配未初始化的数组长度)?这个问题似乎对数组的问题没有帮助,但也许我误解了。

4

1 回答 1

2

我使用 GCC 8.3以模式(输出汇编语言)编译tmp.c了带和不带. 在这两种情况下,都会发出相同的常量基本定义:-flto -ffat-lto-objects-S

    .comm   constantFOO,18,16
    .comm   constantBAR,52,32

LTO 发出的大部分附加数据进入名为.gnu.lto_.something. LTO 模式添加了一个额外的标记对象:

   .comm   __gnu_lto_v1,1,1

出现在 LTO 编译的对象中,但不在没有的对象中。

从表面上看,这根本不应该影响nm这些符号的输出,并且较低级别的工具readelf -s会为它们生成匹配的输出:

$ readelf -s tmp-normal.o

Symbol table '.symtab' contains 9 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     7: 0000000000000010    18 OBJECT  GLOBAL DEFAULT  COM constantFOO
     8: 0000000000000020    52 OBJECT  GLOBAL DEFAULT  COM constantBAR

$ readelf -s tmp-lto.o

Symbol table '.symtab' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT   10 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT   12 
    13: 0000000000000000     0 SECTION LOCAL  DEFAULT   11 
    14: 0000000000000010    18 OBJECT  GLOBAL DEFAULT  COM constantFOO
    15: 0000000000000020    52 OBJECT  GLOBAL DEFAULT  COM constantBAR
    16: 0000000000000001     1 OBJECT  GLOBAL DEFAULT  COM __gnu_lto_v1

因此我认为 的行为nm是一个错误,应该向 GNU binutils 的维护者报告(参见https://sourceware.org/binutils/)。

至于符号值与数组长度匹配的“原始输出”,通常情况下,符号的值如图所示nm是它在目标文件的部分中的偏移量。但是,通用符号不在任何部分中,也没有偏移量,因此nm将符号的大小打印为其值。这是,IIRC,历史行为一直追溯到 System V 的任何迭代添加了对 FORTRAN 类公共数据的支持。请注意如何将readelf -s18 和 52 打印为对象的大小,并将第三个参数.comm(每个符号的所需对齐方式)打印为它们的值。

如果你编译-fno-common你会看到不同的输出:

$ gcc -c -fno-common tmp.c -o tmp-nc.o
$ nm tmp-nc.o 
0000000000000020 B constantBAR
0000000000000000 B constantFOO
$ readelf -s tmp-nc.o | grep constant
     7: 0000000000000000    18 OBJECT  GLOBAL DEFAULT    3 constantFOO
     8: 0000000000000020    52 OBJECT  GLOBAL DEFAULT    3 constantBAR

因为现在您的数组在该部分中,并且在该.bss部分中有一个定义的偏移量。

请注意,它char constantFOO[0x12];定义了一个0x12 的可写数组char。如果你希望它实际上是恒定的,你需要说const char。(然后它将被放在目标文件的部分中,并且.rodata的输出将再次不同。)nmreadelf

于 2019-03-12T17:13:20.893 回答