Skip to content

Commit 72f821d

Browse files
author
George Rimar
committedMay 20, 2019
[llvm-readelf] - Rework how we parse the .dynamic section.
This is a result of what I found during my work on https://bugs.llvm.org/show_bug.cgi?id=41679. Previously LLVM readelf took the information about .dynamic section from its PT_DYNAMIC segment only. GNU tools have a bit different logic. They also use the information from the .dynamic section header if it is available. This patch changes the code to improve the compatibility with the GNU Binutils. Differential revision: https://reviews.llvm.org/D61937 llvm-svn: 361165
1 parent 055906e commit 72f821d

8 files changed

+214
-20
lines changed
 
Binary file not shown.

‎llvm/test/Object/corrupt.test

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ RUN: not llvm-readobj --dyn-relocations \
6262
RUN: %p/Inputs/corrupt-invalid-dynamic-table-offset.elf.x86-64 2>&1 | \
6363
RUN: FileCheck --check-prefix=DYN-TABLE-OFFSET %s
6464

65-
DYN-TABLE-OFFSET: error: Invalid data was encountered while parsing the file
65+
DYN-TABLE-OFFSET: error: PT_DYNAMIC segment offset + size exceeds the size of the file
6666

6767

6868
RUN: not llvm-readobj --dyn-relocations \
6969
RUN: %p/Inputs/corrupt-invalid-dynamic-table-too-large.elf.x86-64 2>&1 | \
7070
RUN: FileCheck --check-prefix=DYN-TABLE-TOO-LARGE %s
7171

72-
DYN-TABLE-TOO-LARGE: error: Invalid data was encountered while parsing the file
72+
DYN-TABLE-TOO-LARGE: error: PT_DYNAMIC segment offset + size exceeds the size of the file
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
## Show that llvm-readobj/llvm-readelf tools can dump the .dynamic
2+
## section when it is not in a PT_DYNAMIC segment.
3+
4+
# RUN: yaml2obj %s -o %t.o
5+
# RUN: llvm-readobj --dynamic-table %t.o 2>&1 | FileCheck %s
6+
# RUN: llvm-readelf --dynamic-table %t.o 2>&1 | FileCheck %s
7+
8+
# CHECK: warning: The SHT_DYNAMIC section '.dynamic' is not contained within the PT_DYNAMIC segment
9+
# CHECK: DynamicSection [ (2 entries)
10+
# CHECK-NEXT: Tag Type Name/Value
11+
# CHECK-NEXT: 0x0000000000000018 BIND_NOW 0x1
12+
# CHECK-NEXT: 0x0000000000000000 NULL 0x0
13+
# CHECK-NEXT: ]
14+
15+
--- !ELF
16+
FileHeader:
17+
Class: ELFCLASS64
18+
Data: ELFDATA2LSB
19+
Type: ET_EXEC
20+
Machine: EM_X86_64
21+
Sections:
22+
- Name: .dynamic
23+
Type: SHT_DYNAMIC
24+
Flags: [SHF_ALLOC]
25+
Address: 0x1000
26+
AddressAlign: 0x1000
27+
Entries:
28+
- Tag: DT_BIND_NOW
29+
Value: 0x1
30+
- Tag: DT_NULL
31+
Value: 0x0
32+
- Name: .text
33+
Type: SHT_PROGBITS
34+
Flags: [SHF_ALLOC]
35+
Address: 0x1100
36+
AddressAlign: 0x100
37+
Content: "00"
38+
ProgramHeaders:
39+
- Type: PT_LOAD
40+
VAddr: 0x1000
41+
Sections:
42+
- Section: .dynamic
43+
- Section: .text
44+
- Type: PT_DYNAMIC
45+
VAddr: 0x1000
46+
Sections:
47+
- Section: .text

‎llvm/test/tools/llvm-readobj/elf-malformed-pt-dynamic.test

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# If the offset and/or size fields of the PT_DYNAMIC field become corrupted,
2-
# it will be impossible to read the dynamic segment validly. This test shows
3-
# that a sensible error message is given in this situation.
2+
# we should report a sensible error message.
43

54
# Creating such a malformed file is hard. The easiest way to simulate it is to
65
# truncate the file. Note that the section headers must first be stripped or
@@ -21,7 +20,7 @@
2120
# RUN: %python -c "with open(r'%t.truncated2', 'r+') as f: f.truncate(0xFFF)"
2221
# RUN: not llvm-readobj %t.truncated2 --dynamic-table 2>&1 | FileCheck %s
2322

24-
# CHECK: error: Invalid data was encountered while parsing the file
23+
# CHECK: error: PT_DYNAMIC segment offset + size exceeds the size of the file
2524

2625
--- !ELF
2726
FileHeader:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
## Show that llvm-readobj/llvm-readelf tools can dump the .dynamic section which
2+
## is not alone in PT_DYNAMIC segment.
3+
4+
## In the first case .text is placed before .dynamic.
5+
## We check that we warn about this case.
6+
7+
# RUN: yaml2obj --docnum=1 %s -o %t.o
8+
# RUN: llvm-readobj --dynamic-table %t.o 2>&1 | FileCheck %s --check-prefixes=WARNING,CHECK
9+
# RUN: llvm-readelf --dynamic-table %t.o 2>&1 | FileCheck %s --check-prefixes=WARNING,CHECK
10+
11+
# WARNING: warning: The SHT_DYNAMIC section '.dynamic' is not at the start of PT_DYNAMIC segment
12+
# CHECK: DynamicSection [ (2 entries)
13+
# CHECK-NEXT: Tag Type Name/Value
14+
# CHECK-NEXT: 0x0000000000000018 BIND_NOW 0x1
15+
# CHECK-NEXT: 0x0000000000000000 NULL 0x0
16+
# CHECK-NEXT: ]
17+
18+
--- !ELF
19+
FileHeader:
20+
Class: ELFCLASS64
21+
Data: ELFDATA2LSB
22+
Type: ET_EXEC
23+
Machine: EM_X86_64
24+
Sections:
25+
- Name: .text
26+
Type: SHT_PROGBITS
27+
Flags: [SHF_ALLOC]
28+
Address: 0x1000
29+
AddressAlign: 0x100
30+
Content: "00"
31+
- Name: .dynamic
32+
Type: SHT_DYNAMIC
33+
Flags: [SHF_ALLOC]
34+
Address: 0x1100
35+
AddressAlign: 0x1000
36+
Entries:
37+
- Tag: DT_BIND_NOW
38+
Value: 0x1
39+
- Tag: DT_NULL
40+
Value: 0x0
41+
ProgramHeaders:
42+
- Type: PT_LOAD
43+
VAddr: 0x1000
44+
Sections:
45+
- Section: .text
46+
- Section: .dynamic
47+
- Type: PT_DYNAMIC
48+
VAddr: 0x1000
49+
Sections:
50+
- Section: .text
51+
- Section: .dynamic
52+
53+
## In the second case .text goes after .dynamic and we don't display any warnings.
54+
55+
# RUN: yaml2obj --docnum=2 %s -o %t.o
56+
# RUN: llvm-readobj --dynamic-table %t.o | FileCheck %s --implicit-check-not="warning"
57+
# RUN: llvm-readelf --dynamic-table %t.o | FileCheck %s --implicit-check-not="warning"
58+
59+
--- !ELF
60+
FileHeader:
61+
Class: ELFCLASS64
62+
Data: ELFDATA2LSB
63+
Type: ET_EXEC
64+
Machine: EM_X86_64
65+
Sections:
66+
- Name: .dynamic
67+
Type: SHT_DYNAMIC
68+
Flags: [SHF_ALLOC]
69+
Address: 0x1000
70+
AddressAlign: 0x1000
71+
Entries:
72+
- Tag: DT_BIND_NOW
73+
Value: 0x1
74+
- Tag: DT_NULL
75+
Value: 0x0
76+
- Name: .text
77+
Type: SHT_PROGBITS
78+
Flags: [SHF_ALLOC]
79+
Address: 0x1100
80+
AddressAlign: 0x100
81+
Content: "00"
82+
ProgramHeaders:
83+
- Type: PT_LOAD
84+
VAddr: 0x1000
85+
Sections:
86+
- Section: .dynamic
87+
- Section: .text
88+
- Type: PT_DYNAMIC
89+
VAddr: 0x1000
90+
Sections:
91+
- Section: .dynamic
92+
- Section: .text

‎llvm/tools/llvm-readobj/ELFDumper.cpp

+65-15
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
203203
{ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize});
204204
}
205205

206-
void parseDynamicTable(ArrayRef<const Elf_Phdr *> LoadSegments);
206+
void loadDynamicTable(const ELFFile<ELFT> *Obj);
207+
void parseDynamicTable();
207208

208209
void printValue(uint64_t Type, uint64_t Value);
209210

@@ -1329,21 +1330,72 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) {
13291330
}
13301331

13311332
template <typename ELFT>
1332-
ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
1333-
ScopedPrinter &Writer)
1334-
: ObjDumper(Writer), ObjF(ObjF) {
1335-
SmallVector<const Elf_Phdr *, 4> LoadSegments;
1336-
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
1333+
void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
1334+
const Elf_Phdr *DynamicPhdr = nullptr;
13371335
for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) {
1338-
if (Phdr.p_type == ELF::PT_DYNAMIC) {
1339-
DynamicTable = createDRIFrom(&Phdr, sizeof(Elf_Dyn));
1336+
if (Phdr.p_type != ELF::PT_DYNAMIC)
13401337
continue;
1341-
}
1342-
if (Phdr.p_type != ELF::PT_LOAD || Phdr.p_filesz == 0)
1338+
DynamicPhdr = &Phdr;
1339+
break;
1340+
}
1341+
1342+
// We do not want to dump dynamic section if we have no PT_DYNAMIC header.
1343+
// This matches GNU's behavior.
1344+
if (!DynamicPhdr)
1345+
return;
1346+
1347+
// Try to locate the .dynamic section in the sections header table.
1348+
const Elf_Shdr *DynamicSec = nullptr;
1349+
for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
1350+
if (Sec.sh_type != ELF::SHT_DYNAMIC)
13431351
continue;
1344-
LoadSegments.push_back(&Phdr);
1352+
DynamicSec = &Sec;
1353+
break;
13451354
}
13461355

1356+
// Information in the section header has priority over the information
1357+
// in a PT_DYNAMIC header.
1358+
// Ignore sh_entsize and use the expected value for entry size explicitly.
1359+
// This allows us to dump the dynamic sections with a broken sh_entsize
1360+
// field.
1361+
if (DynamicSec)
1362+
DynamicTable = checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset,
1363+
DynamicSec->sh_size, sizeof(Elf_Dyn)});
1364+
1365+
if (DynamicPhdr->p_offset + DynamicPhdr->p_filesz >
1366+
ObjF->getMemoryBufferRef().getBufferSize())
1367+
reportError(
1368+
"PT_DYNAMIC segment offset + size exceeds the size of the file");
1369+
1370+
if (!DynamicSec) {
1371+
DynamicTable = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn));
1372+
parseDynamicTable();
1373+
return;
1374+
}
1375+
1376+
StringRef Name = unwrapOrError(Obj->getSectionName(DynamicSec));
1377+
1378+
if (DynamicSec->sh_addr + DynamicSec->sh_size >
1379+
DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz ||
1380+
DynamicSec->sh_addr < DynamicPhdr->p_vaddr)
1381+
reportWarning("The SHT_DYNAMIC section '" + Name +
1382+
"' is not contained within the "
1383+
"PT_DYNAMIC segment");
1384+
1385+
if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr)
1386+
reportWarning("The SHT_DYNAMIC section '" + Name +
1387+
"' is not at the start of "
1388+
"PT_DYNAMIC segment");
1389+
1390+
parseDynamicTable();
1391+
}
1392+
1393+
template <typename ELFT>
1394+
ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
1395+
ScopedPrinter &Writer)
1396+
: ObjDumper(Writer), ObjF(ObjF) {
1397+
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
1398+
13471399
for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
13481400
switch (Sec.sh_type) {
13491401
case ELF::SHT_SYMTAB:
@@ -1390,17 +1442,15 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
13901442
}
13911443
}
13921444

1393-
parseDynamicTable(LoadSegments);
1445+
loadDynamicTable(Obj);
13941446

13951447
if (opts::Output == opts::GNU)
13961448
ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
13971449
else
13981450
ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
13991451
}
14001452

1401-
template <typename ELFT>
1402-
void ELFDumper<ELFT>::parseDynamicTable(
1403-
ArrayRef<const Elf_Phdr *> LoadSegments) {
1453+
template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() {
14041454
auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * {
14051455
auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr);
14061456
if (!MappedAddrOrError)

‎llvm/tools/llvm-readobj/llvm-readobj.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) {
373373
exit(1);
374374
}
375375

376+
void reportWarning(Twine Msg) {
377+
errs() << "\n";
378+
WithColor::warning(errs()) << Msg << "\n";
379+
}
380+
376381
void error(Error EC) {
377382
if (!EC)
378383
return;

‎llvm/tools/llvm-readobj/llvm-readobj.h

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace llvm {
2222

2323
// Various helper functions.
2424
LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg);
25+
void reportWarning(Twine Msg);
2526
void error(std::error_code EC);
2627
void error(llvm::Error EC);
2728
template <typename T> T error(llvm::Expected<T> &&E) {

0 commit comments

Comments
 (0)
Please sign in to comment.