xref: /llvm-project/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test (revision 0d3f7d2ab333f55cef634e7af834b84e1153e9cf)
1## Here we test the --elf-hash-histogram command line option.
2
3## This test case checks how we built histograms for hash sections.
4
5# RUN: yaml2obj --docnum=1 -D BITS=32 %s -o %t1-32.o
6# RUN: llvm-readelf --elf-hash-histogram %t1-32.o | FileCheck %s --check-prefix=HIST
7
8## Test --histogram and -I aliases.
9# RUN: llvm-readelf --histogram %t1-32.o | FileCheck %s --check-prefix=HIST
10# RUN: llvm-readelf -I %t1-32.o | FileCheck %s --check-prefix=HIST
11
12# RUN: yaml2obj --docnum=1 -D BITS=64 %s -o %t1-64.o
13# RUN: llvm-readelf --elf-hash-histogram %t1-64.o | FileCheck %s --check-prefix=HIST
14
15## Check that LLVM output has the expected format.
16# RUN: llvm-readobj --elf-hash-histogram %t1-32.o | FileCheck %s --check-prefix=LLVM-HIST
17# RUN: llvm-readobj --elf-hash-histogram %t1-64.o | FileCheck %s --check-prefix=LLVM-HIST
18
19# HIST:      Histogram for bucket list length (total of 3 buckets)
20# HIST-NEXT:  Length  Number     % of total  Coverage
21# HIST-NEXT:       0  2          ( 66.7%)       0.0%
22# HIST-NEXT:       1  0          (  0.0%)       0.0%
23# HIST-NEXT:       2  0          (  0.0%)       0.0%
24# HIST-NEXT:       3  1          ( 33.3%)     100.0%
25# HIST-NEXT: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
26# HIST-NEXT:  Length  Number     % of total  Coverage
27# HIST-NEXT:       0  1          ( 33.3%)       0.0%
28# HIST-NEXT:       1  1          ( 33.3%)      25.0%
29# HIST-NEXT:       2  0          (  0.0%)      25.0%
30# HIST-NEXT:       3  1          ( 33.3%)     100.0%
31# HIST-NOT:  {{.}}
32
33#      LLVM-HIST: HashHistogram {
34# LLVM-HIST-NEXT:   TotalBuckets: 3
35# LLVM-HIST-NEXT:   Chains [
36# LLVM-HIST-NEXT:     Chain {
37# LLVM-HIST-NEXT:       Length: 0
38# LLVM-HIST-NEXT:       Count: 2
39# LLVM-HIST-NEXT:       Percentage:  66.7
40# LLVM-HIST-NEXT:       Coverage:   0.0
41# LLVM-HIST-NEXT:     }
42# LLVM-HIST-NEXT:     Chain {
43# LLVM-HIST-NEXT:       Length: 1
44# LLVM-HIST-NEXT:       Count: 0
45# LLVM-HIST-NEXT:       Percentage:   0.0
46# LLVM-HIST-NEXT:       Coverage:   0.0
47# LLVM-HIST-NEXT:     }
48# LLVM-HIST-NEXT:     Chain {
49# LLVM-HIST-NEXT:       Length: 2
50# LLVM-HIST-NEXT:       Count: 0
51# LLVM-HIST-NEXT:       Percentage:   0.0
52# LLVM-HIST-NEXT:       Coverage:   0.0
53# LLVM-HIST-NEXT:     }
54# LLVM-HIST-NEXT:     Chain {
55# LLVM-HIST-NEXT:       Length: 3
56# LLVM-HIST-NEXT:       Count: 1
57# LLVM-HIST-NEXT:       Percentage:  33.3
58# LLVM-HIST-NEXT:       Coverage: 100.0
59# LLVM-HIST-NEXT:     }
60# LLVM-HIST-NEXT:   ]
61# LLVM-HIST-NEXT: }
62# LLVM-HIST-NEXT: GnuHashHistogram {
63# LLVM-HIST-NEXT:   TotalBuckets: 3
64# LLVM-HIST-NEXT:   Buckets [
65# LLVM-HIST-NEXT:     Bucket {
66# LLVM-HIST-NEXT:       Length: 0
67# LLVM-HIST-NEXT:       Count: 1
68# LLVM-HIST-NEXT:       Percentage:  33.3
69# LLVM-HIST-NEXT:       Coverage:   0.0
70# LLVM-HIST-NEXT:     }
71# LLVM-HIST-NEXT:     Bucket {
72# LLVM-HIST-NEXT:       Length: 1
73# LLVM-HIST-NEXT:       Count: 1
74# LLVM-HIST-NEXT:       Percentage:  33.3
75# LLVM-HIST-NEXT:       Coverage:  25.0
76# LLVM-HIST-NEXT:     }
77# LLVM-HIST-NEXT:     Bucket {
78# LLVM-HIST-NEXT:       Length: 2
79# LLVM-HIST-NEXT:       Count: 0
80# LLVM-HIST-NEXT:       Percentage:   0.0
81# LLVM-HIST-NEXT:       Coverage:  25.0
82# LLVM-HIST-NEXT:     }
83# LLVM-HIST-NEXT:     Bucket {
84# LLVM-HIST-NEXT:       Length: 3
85# LLVM-HIST-NEXT:       Count: 1
86# LLVM-HIST-NEXT:       Percentage:  33.3
87# LLVM-HIST-NEXT:       Coverage: 100.0
88# LLVM-HIST-NEXT:     }
89# LLVM-HIST-NEXT:   ]
90# LLVM-HIST-NEXT: }
91
92--- !ELF
93FileHeader:
94  Class: ELFCLASS[[BITS]]
95  Data:  ELFDATA2LSB
96  Type:  ET_DYN
97Sections:
98  - Name:   .hash
99    Type:   SHT_HASH
100    Flags:  [ SHF_ALLOC ]
101    Bucket: [ 6, 4, 5 ]
102    Chain:  [ 0, 0, 1, 0, 2 ]
103  - Name:  .gnu.hash
104    Type:  SHT_GNU_HASH
105    Flags: [ SHF_ALLOC ]
106    Header:
107      SymNdx: 0x1
108      Shift2: 0x0
109    BloomFilter: [ 0x0 ]
110    HashBuckets: [ 0x00000001, 0x00000004, 0x00000000 ]
111    HashValues:  [ 0x0B887388, 0xECD54542, 0x7C92E3BB, 0x1C5871D9 ]
112  - Name:  .dynamic
113    Type:  SHT_DYNAMIC
114    Flags: [ SHF_WRITE, SHF_ALLOC ]
115    Entries:
116      - Tag:   DT_HASH
117        Value: 0x0
118      - Tag:   DT_GNU_HASH
119## sizeof(.hash) == 0x28.
120        Value: 0x28
121      - Tag:   DT_NULL
122        Value: 0x0
123DynamicSymbols:
124  - Name: a
125  - Name: b
126  - Name: c
127  - Name: d
128ProgramHeaders:
129  - Type:     PT_LOAD
130    FirstSec: .hash
131    LastSec:  .dynamic
132
133## Show that we report a warning for a hash table which contains an entry of
134## the bucket array pointing to a cycle.
135
136# RUN: yaml2obj --docnum=2 %s -o %t2.o
137# RUN: llvm-readelf --elf-hash-histogram 2>&1 %t2.o | \
138# RUN:   FileCheck -DFILE=%t2.o %s --check-prefix=BROKEN --implicit-check-not=warning:
139
140# BROKEN:       warning: '[[FILE]]': .hash section is invalid: bucket 1: a cycle was detected in the linked chain
141# BROKEN:       Histogram for bucket list length (total of 2 buckets)
142# BROKEN-NEXT:  Length  Number     % of total  Coverage
143# BROKEN-NEXT:       0  0          (  0.0%)       0.0%
144# BROKEN-NEXT:       1  2          (100.0%)     100.0%
145
146--- !ELF
147FileHeader:
148  Class: ELFCLASS32
149  Data:  ELFDATA2LSB
150  Type:  ET_REL
151Sections:
152  - Name:   .hash
153    Type:   SHT_HASH
154    Link:   .dynsym
155    Bucket: [ 1, 1 ]
156    Chain:  [ 0, 1 ]
157  - Name:  .dynamic
158    Type:  SHT_DYNAMIC
159    Flags: [ SHF_ALLOC ]
160    Entries:
161## llvm-readelf will read the hash table from the file offset
162## p_offset + (p_vaddr - DT_HASH) = p_offset + (0 - 0) = p_offset,
163## which is the start of PT_LOAD, i.e. the file offset of .hash.
164      - Tag:   DT_HASH
165        Value: 0x0
166      - Tag:   DT_NULL
167        Value: 0
168DynamicSymbols:
169  - Name: foo
170ProgramHeaders:
171  - Type:     PT_LOAD
172    FirstSec: .hash
173    LastSec:  .dynamic
174
175## Each SHT_HASH section starts with two 32-bit fields: nbucket and nchain.
176## Check we report an error when a DT_HASH value points to data that has size less than 8 bytes.
177
178# RUN: yaml2obj --docnum=3 %s -o %t3.o
179# RUN: llvm-readelf --elf-hash-histogram %t3.o 2>&1 | FileCheck %s --check-prefix=ERR1 -DFILE=%t3.o
180
181# ERR1: warning: '[[FILE]]': the hash table at offset 0x2b1 goes past the end of the file (0x2b8){{$}}
182
183--- !ELF
184FileHeader:
185  Class: ELFCLASS64
186  Data:  ELFDATA2LSB
187  Type:  ET_DYN
188Sections:
189  - Name:   .hash
190    Type:   SHT_HASH
191    Flags:  [ SHF_ALLOC ]
192    Bucket: [ 0 ]
193    Chain:  [ 0 ]
194  - Name:  .dynamic
195    Type:  SHT_DYNAMIC
196    Flags: [ SHF_WRITE, SHF_ALLOC ]
197    Entries:
198      - Tag:   DT_HASH
199        Value: 0x239
200      - Tag:   DT_NULL
201        Value: 0x0
202DynamicSymbols: []
203ProgramHeaders:
204  - Type:     PT_LOAD
205    FileSize: 0x23a
206    FirstSec: .hash
207    LastSec:  .dynamic
208
209## Check we report a warning when the hash table goes past the end of the file.
210
211## Case A.1: the hash table ends right before the EOF. We have a broken nbucket
212##           field that has a value larger than the number of buckets.
213# RUN: yaml2obj --docnum=4 %s -o %t4.1.o -DNBUCKET=0x5d -DNCHAIN=0x1
214# RUN: llvm-readelf --elf-hash-histogram %t4.1.o 2>&1 | \
215# RUN:   FileCheck %s --implicit-check-not={{.}} --allow-empty
216
217## Case A.2: the hash table ends 1 byte past the EOF. We have a broken nbucket
218##           field that has a value larger than the number of buckets.
219# RUN: yaml2obj --docnum=4 %s -o %t4.2.o -DNBUCKET=0x5e -DNCHAIN=0x1
220# RUN: llvm-readelf --elf-hash-histogram %t4.2.o 2>&1 | \
221# RUN:   FileCheck %s --check-prefix=ERR2 -DFILE=%t4.2.o --implicit-check-not="warning:"
222# ERR2: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 94, nchain = 1{{$}}
223
224## Case B.1: the hash table ends right before the EOF. We have a broken nchain
225##           field that has a value larger than the number of chains.
226# RUN: yaml2obj --docnum=4 %s -o %t4.3.o -DNBUCKET=0x1 -DNCHAIN=0x5d
227# RUN: llvm-readelf --elf-hash-histogram %t4.3.o 2>&1 | \
228# RUN:   FileCheck %s --check-prefix=ERR3 -DFILE=%t4.3.o --implicit-check-not="warning:"
229# ERR3: warning: '[[FILE]]': hash table nchain (93) differs from symbol count derived from SHT_DYNSYM section header (1){{$}}
230# ERR3: warning: '[[FILE]]': the size (0x5d0) of the dynamic symbol table at 0x78, derived from the hash table, goes past the end of the file (0x1d4) and will be ignored
231
232## Case B.2: the hash table ends 1 byte past the EOF. We have a broken nchain
233##           field that has a value larger than the number of chains.
234# RUN: yaml2obj --docnum=4 %s -o %t4.4.o -DNBUCKET=0x1 -DNCHAIN=0x5e
235# RUN: llvm-readelf --elf-hash-histogram %t4.4.o 2>&1 | \
236# RUN:   FileCheck %s --check-prefix=ERR4 -DFILE=%t4.4.o --implicit-check-not="warning:"
237# ERR4: warning: '[[FILE]]': hash table nchain (94) differs from symbol count derived from SHT_DYNSYM section header (1){{$}}
238# ERR4: warning: '[[FILE]]': the size (0x5e0) of the dynamic symbol table at 0x78, derived from the hash table, goes past the end of the file (0x1d4) and will be ignored
239# ERR4: warning: '[[FILE]]': the hash table at offset 0x54 goes past the end of the file (0x1d4), nbucket = 1, nchain = 94{{$}}
240
241--- !ELF
242FileHeader:
243  Class: ELFCLASS32
244  Data:  ELFDATA2LSB
245  Type:  ET_DYN
246Sections:
247  - Name:    .hash
248    Type:    SHT_HASH
249    Flags:   [ SHF_ALLOC ]
250    Bucket:  [ 0 ]
251    NBucket: [[NBUCKET]]
252    Chain:   [ 0 ]
253    NChain:  [[NCHAIN]]
254  - Name:  .dynamic
255    Type:  SHT_DYNAMIC
256    Flags: [ SHF_WRITE, SHF_ALLOC ]
257    Entries:
258      - Tag:   DT_HASH
259        Value: 0x0
260      - Tag:   DT_NULL
261        Value: 0x0
262DynamicSymbols: []
263ProgramHeaders:
264  - Type:     PT_LOAD
265    FirstSec: .hash
266    LastSec:  .dynamic
267
268## Check we dump a histogram for the .gnu.hash table even when the .hash table is skipped.
269
270## Case A: the .hash table has no data to build histogram and it is skipped.
271# RUN: yaml2obj --docnum=5 %s -o %t5.o
272# RUN: llvm-readelf --elf-hash-histogram %t5.o 2>&1 | \
273# RUN:   FileCheck %s --check-prefix=GNU-HASH --implicit-check-not="Histogram"
274
275## Case B: the .hash table has a broken nbucket field. We report a warning
276##         and skip dumping of the .hash table.
277# RUN: yaml2obj --docnum=5 -DNBUCKET=0xffffffff %s -o %t6.o
278# RUN: llvm-readelf --elf-hash-histogram %t6.o 2>&1 | \
279# RUN:   FileCheck %s -DFILE=%t6.o --check-prefixes=WARN,GNU-HASH
280
281# WARN:     warning: '[[FILE]]': the hash table at offset 0x78 goes past the end of the file (0x358), nbucket = 4294967295, nchain = 2
282# GNU-HASH: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
283
284--- !ELF
285FileHeader:
286  Class: ELFCLASS64
287  Data:  ELFDATA2LSB
288  Type:  ET_DYN
289Sections:
290  - Name:    .hash
291    Type:    SHT_HASH
292    Flags:   [ SHF_ALLOC ]
293    Bucket:  [ 0 ]
294## 0x2 is a no-op: it does not change the number of buckets described by the "Bucket" key
295    NBucket: [[NBUCKET=0x2]]
296    Chain:   [ 0, 0 ]
297  - Name:  .gnu.hash
298    Type:  SHT_GNU_HASH
299    Flags: [ SHF_ALLOC ]
300    Header:
301      SymNdx: 0x1
302      Shift2: 0x0
303    BloomFilter: [ 0x0 ]
304    HashBuckets: [ 0x00000001, 0x00000004, 0x00000000 ]
305    HashValues:  [ 0x0B887388 ]
306  - Name:  .dynamic
307    Type:  SHT_DYNAMIC
308    Flags: [ SHF_WRITE, SHF_ALLOC ]
309    Entries:
310      - Tag:   DT_HASH
311        Value: 0x0
312      - Tag:   DT_GNU_HASH
313## sizeof(.hash) == 0x14.
314        Value: 0x14
315      - Tag:   DT_NULL
316        Value: 0x0
317DynamicSymbols:
318  - Name: foo
319ProgramHeaders:
320  - Type:     PT_LOAD
321    FirstSec: .hash
322    LastSec:  .dynamic
323
324## Check we report a proper warning when the GNU hash table goes past the end of the file.
325
326## Case A: the 'maskwords' field is set so that the GNU hash table goes past the end of the file.
327# RUN: yaml2obj --docnum=6 -D MASKWORDS=0x80000000 %s -o %t7
328# RUN: llvm-readelf --elf-hash-histogram %t7 2>&1 | \
329# RUN:   FileCheck %s -DFILE=%t7 --check-prefix=ERR5 --implicit-check-not="Histogram"
330
331# ERR5: warning: '[[FILE]]': unable to dump the SHT_GNU_HASH section at 0x78: it goes past the end of the file
332
333## Case B: the 'nbuckets' field is set so that the GNU hash table goes past the end of the file.
334# RUN: yaml2obj --docnum=6 -D NBUCKETS=0x80000000 %s -o %t8
335# RUN: llvm-readelf --elf-hash-histogram %t8 2>&1 | \
336# RUN:   FileCheck %s -DFILE=%t8 --check-prefix=ERR5 --implicit-check-not="Histogram"
337
338--- !ELF
339FileHeader:
340  Class: ELFCLASS64
341  Data:  ELFDATA2LSB
342  Type:  ET_DYN
343Sections:
344  - Name:  .gnu.hash
345    Type:  SHT_GNU_HASH
346    Flags: [ SHF_ALLOC ]
347    Header:
348      SymNdx: 0x0
349      Shift2: 0x0
350## The number of words in the Bloom filter. The value of 1 is no-op.
351      MaskWords: [[MASKWORDS=1]]
352## The number of hash buckets. The value of 1 is no-op.
353      NBuckets:  [[NBUCKETS=1]]
354    BloomFilter: [ 0x0 ]
355    HashBuckets: [ 0x0 ]
356    HashValues:  [ 0x0 ]
357  - Name:  .dynamic
358    Type:  SHT_DYNAMIC
359    Flags: [ SHF_ALLOC ]
360    Link:  .dynstr
361    Entries:
362      - Tag:   DT_GNU_HASH
363        Value: 0x0
364      - Tag:   DT_NULL
365        Value: 0x0
366DynamicSymbols: []
367ProgramHeaders:
368  - Type:     PT_LOAD
369    FirstSec: .gnu.hash
370    LastSec:  .dynamic
371
372## Linkers might produce an empty no-op SHT_GNU_HASH section when
373## there are no dynamic symbols or when all dynamic symbols are undefined.
374## Such sections normally have a single zero entry in the bloom
375## filter, a single zero entry in the hash bucket and no values.
376##
377## The index of the first symbol in the dynamic symbol table
378## included in the hash table can be set to the number of dynamic symbols,
379## which is one larger than the index of the last dynamic symbol.
380## For empty tables however, this value is unimportant and can be ignored.
381
382## Check the case when a 'symndx' index of the first symbol in the dynamic symbol
383## table is larger than the number of dynamic symbols.
384
385## Case A: when the buckets array is not empty and has a non-zero value we report a warning.
386# RUN: yaml2obj --docnum=7 -DVAL=0x1 %s -o %t9
387# RUN: llvm-readelf --elf-hash-histogram %t9 2>&1 | \
388# RUN:   FileCheck %s -DFILE=%t9 --check-prefix=ERR6
389
390# ERR6: warning: '[[FILE]]': unable to print the GNU hash table histogram: the first hashed symbol index (16) is greater than or equal to the number of dynamic symbols (1)
391
392## Case B: we do not report a warning when the buckets array contains only zero values.
393# RUN: yaml2obj --docnum=7 -DVAL=0x0 %s -o %t10
394# RUN: llvm-readelf --elf-hash-histogram %t10 2>&1 | \
395# RUN:   FileCheck %s --allow-empty --implicit-check-not="Histogram"
396
397## Case C: we do not report a warning when the buckets array is empty.
398# RUN: yaml2obj --docnum=7 -DVAL="" %s -o %t11
399# RUN: llvm-readelf --elf-hash-histogram %t11 2>&1 | \
400# RUN:   FileCheck %s --allow-empty --implicit-check-not="Histogram"
401
402--- !ELF
403FileHeader:
404  Class: ELFCLASS64
405  Data:  ELFDATA2LSB
406  Type:  ET_DYN
407Sections:
408  - Name:  .gnu.hash
409    Type:  SHT_GNU_HASH
410    Flags: [ SHF_ALLOC ]
411    Header:
412      SymNdx: 0x10
413      Shift2: 0x0
414    BloomFilter: [ 0x0 ]
415    HashBuckets: [ [[VAL]] ]
416    HashValues:  [ 0x0 ]
417  - Name:  .dynamic
418    Type:  SHT_DYNAMIC
419    Flags: [ SHF_ALLOC ]
420    Link:  .dynstr
421    Entries:
422      - Tag:   DT_GNU_HASH
423        Value: 0x0
424      - Tag:   DT_NULL
425        Value: 0x0
426DynamicSymbols: []
427ProgramHeaders:
428  - Type:     PT_LOAD
429    FirstSec: .gnu.hash
430    LastSec:  .dynamic
431
432## Check we report warnings when the dynamic symbol table is absent or empty.
433
434## The code locates the dynamic symbol table by the section type. Use SHT_PROGBITS to hide it.
435# RUN: yaml2obj --docnum=8 -DTYPE=SHT_PROGBITS %s -o %t12
436# RUN: llvm-readelf --elf-hash-histogram %t12 2>&1 | \
437# RUN:   FileCheck %s -DFILE=%t12 --check-prefix=ERR7
438
439# ERR7: warning: '[[FILE]]': unable to print the GNU hash table histogram: no dynamic symbol table found
440
441# RUN: yaml2obj --docnum=8 -DTYPE=SHT_DYNSYM %s -o %t13
442# RUN: llvm-readelf --elf-hash-histogram %t13 2>&1 | \
443# RUN:   FileCheck %s -DFILE=%t13 --check-prefix=ERR8
444
445# ERR8: warning: '[[FILE]]': unable to print the GNU hash table histogram: the dynamic symbol table is empty
446
447--- !ELF
448FileHeader:
449  Class: ELFCLASS64
450  Data:  ELFDATA2LSB
451  Type:  ET_DYN
452Sections:
453  - Name:  .gnu.hash
454    Type:  SHT_GNU_HASH
455    Flags: [ SHF_ALLOC ]
456    Header:
457      SymNdx: 0x0
458      Shift2: 0x0
459    BloomFilter: [ 0x0 ]
460    HashBuckets: [ 0x0 ]
461    HashValues:  [ 0x0 ]
462  - Name:  .dynamic
463    Type:  SHT_DYNAMIC
464    Flags: [ SHF_ALLOC ]
465    Entries:
466      - Tag:   DT_GNU_HASH
467        Value: 0x0
468      - Tag:   DT_NULL
469        Value: 0x0
470  - Name: .dynsym
471    Type: [[TYPE]]
472    Size: 0
473ProgramHeaders:
474  - Type:     PT_LOAD
475    FirstSec: .gnu.hash
476    LastSec:  .gnu.hash
477