Skip to content

Commit bb94611

Browse files
alvinhochunmstorsjo
authored andcommitted
[COFF] Check table ptr more thoroughly and ignore empty sections
When loading split debug files for PE/COFF executables (produced with `objcopy --only-keep-debug`), the tables or directories in such files may point to data inside sections that may have been stripped. COFFObjectFile shall detect and gracefully handle this, to allow the object file be loaded without considering these tables or directories. This is required for LLDB to load these files for use as debug symbols. COFFObjectFile shall also check these pointers more carefully to account for cases in which the section contains less raw data than the size given by VirtualSize, to prevent going out of bounds. This commit also changes COFFDump in llvm-objdump to reuse the pointers that are already range-checked in COFFObjectFile. This fixes a crash when trying to dump the TLS directory from a stripped file. Fixes mstorsjo/llvm-mingw#284 Reviewed By: rnk Differential Revision: https://reviews.llvm.org/D126898
1 parent cac6094 commit bb94611

File tree

7 files changed

+203
-32
lines changed

7 files changed

+203
-32
lines changed

llvm/include/llvm/Object/COFF.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,12 @@ struct FpoData {
12981298
frame_type getFP() const { return static_cast<frame_type>(Attributes >> 14); }
12991299
};
13001300

1301+
class SectionStrippedError
1302+
: public ErrorInfo<SectionStrippedError, BinaryError> {
1303+
public:
1304+
SectionStrippedError() { setErrorCode(object_error::section_stripped); }
1305+
};
1306+
13011307
} // end namespace object
13021308

13031309
} // end namespace llvm

llvm/include/llvm/Object/Error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum class object_error {
3434
invalid_section_index,
3535
bitcode_section_not_found,
3636
invalid_symbol_index,
37+
section_stripped,
3738
};
3839

3940
inline std::error_code make_error_code(object_error e) {

llvm/lib/Object/COFFObjectFile.cpp

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,25 @@ Error COFFObjectFile::getRvaPtr(uint32_t Addr, uintptr_t &Res,
477477
uint32_t SectionStart = Section->VirtualAddress;
478478
uint32_t SectionEnd = Section->VirtualAddress + Section->VirtualSize;
479479
if (SectionStart <= Addr && Addr < SectionEnd) {
480+
// A table/directory entry can be pointing to somewhere in a stripped
481+
// section, in an object that went through `objcopy --only-keep-debug`.
482+
// In this case we don't want to cause the parsing of the object file to
483+
// fail, otherwise it will be impossible to use this object as debug info
484+
// in LLDB. Return SectionStrippedError here so that
485+
// COFFObjectFile::initialize can ignore the error.
486+
if (Section->SizeOfRawData == 0)
487+
return make_error<SectionStrippedError>();
488+
if (Section->SizeOfRawData < Section->VirtualSize &&
489+
Addr >= SectionStart + Section->SizeOfRawData) {
490+
if (ErrorContext)
491+
return createStringError(object_error::parse_failed,
492+
"RVA 0x%" PRIx32
493+
" for %s found but data is incomplete",
494+
Addr, ErrorContext);
495+
return createStringError(
496+
object_error::parse_failed,
497+
"RVA 0x%" PRIx32 " found but data is incomplete", Addr);
498+
}
480499
uint32_t Offset = Addr - SectionStart;
481500
Res = reinterpret_cast<uintptr_t>(base()) + Section->PointerToRawData +
482501
Offset;
@@ -602,6 +621,9 @@ Error COFFObjectFile::initDelayImportTablePtr() {
602621
uintptr_t IntPtr = 0;
603622
if (Error E = getRvaPtr(RVA, IntPtr, "delay import table"))
604623
return E;
624+
if (Error E = checkOffset(Data, IntPtr, DataEntry->Size))
625+
return E;
626+
605627
DelayImportDirectory = reinterpret_cast<
606628
const delay_import_directory_table_entry *>(IntPtr);
607629
return Error::success();
@@ -623,6 +645,9 @@ Error COFFObjectFile::initExportTablePtr() {
623645
uintptr_t IntPtr = 0;
624646
if (Error E = getRvaPtr(ExportTableRva, IntPtr, "export table"))
625647
return E;
648+
if (Error E = checkOffset(Data, IntPtr, DataEntry->Size))
649+
return E;
650+
626651
ExportDirectory =
627652
reinterpret_cast<const export_directory_table_entry *>(IntPtr);
628653
return Error::success();
@@ -640,6 +665,9 @@ Error COFFObjectFile::initBaseRelocPtr() {
640665
if (Error E = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr,
641666
"base reloc table"))
642667
return E;
668+
if (Error E = checkOffset(Data, IntPtr, DataEntry->Size))
669+
return E;
670+
643671
BaseRelocHeader = reinterpret_cast<const coff_base_reloc_block_header *>(
644672
IntPtr);
645673
BaseRelocEnd = reinterpret_cast<coff_base_reloc_block_header *>(
@@ -668,6 +696,9 @@ Error COFFObjectFile::initDebugDirectoryPtr() {
668696
if (Error E = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr,
669697
"debug directory"))
670698
return E;
699+
if (Error E = checkOffset(Data, IntPtr, DataEntry->Size))
700+
return E;
701+
671702
DebugDirectoryBegin = reinterpret_cast<const debug_directory *>(IntPtr);
672703
DebugDirectoryEnd = reinterpret_cast<const debug_directory *>(
673704
IntPtr + DataEntry->Size);
@@ -700,6 +731,8 @@ Error COFFObjectFile::initTLSDirectoryPtr() {
700731
if (Error E =
701732
getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr, "TLS directory"))
702733
return E;
734+
if (Error E = checkOffset(Data, IntPtr, DataEntry->Size))
735+
return E;
703736

704737
if (is64())
705738
TLSDirectory64 = reinterpret_cast<const coff_tls_directory64 *>(IntPtr);
@@ -722,6 +755,8 @@ Error COFFObjectFile::initLoadConfigPtr() {
722755
if (Error E = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr,
723756
"load config table"))
724757
return E;
758+
if (Error E = checkOffset(Data, IntPtr, DataEntry->Size))
759+
return E;
725760

726761
LoadConfig = (const void *)IntPtr;
727762
return Error::success();
@@ -746,6 +781,14 @@ COFFObjectFile::COFFObjectFile(MemoryBufferRef Object)
746781
DebugDirectoryBegin(nullptr), DebugDirectoryEnd(nullptr),
747782
TLSDirectory32(nullptr), TLSDirectory64(nullptr) {}
748783

784+
static Error ignoreStrippedErrors(Error E) {
785+
if (E.isA<SectionStrippedError>()) {
786+
consumeError(std::move(E));
787+
return Error::success();
788+
}
789+
return std::move(E);
790+
}
791+
749792
Error COFFObjectFile::initialize() {
750793
// Check that we at least have enough room for a header.
751794
std::error_code EC;
@@ -861,28 +904,28 @@ Error COFFObjectFile::initialize() {
861904
}
862905

863906
// Initialize the pointer to the beginning of the import table.
864-
if (Error E = initImportTablePtr())
907+
if (Error E = ignoreStrippedErrors(initImportTablePtr()))
865908
return E;
866-
if (Error E = initDelayImportTablePtr())
909+
if (Error E = ignoreStrippedErrors(initDelayImportTablePtr()))
867910
return E;
868911

869912
// Initialize the pointer to the export table.
870-
if (Error E = initExportTablePtr())
913+
if (Error E = ignoreStrippedErrors(initExportTablePtr()))
871914
return E;
872915

873916
// Initialize the pointer to the base relocation table.
874-
if (Error E = initBaseRelocPtr())
917+
if (Error E = ignoreStrippedErrors(initBaseRelocPtr()))
875918
return E;
876919

877920
// Initialize the pointer to the debug directory.
878-
if (Error E = initDebugDirectoryPtr())
921+
if (Error E = ignoreStrippedErrors(initDebugDirectoryPtr()))
879922
return E;
880923

881924
// Initialize the pointer to the TLS directory.
882-
if (Error E = initTLSDirectoryPtr())
925+
if (Error E = ignoreStrippedErrors(initTLSDirectoryPtr()))
883926
return E;
884927

885-
if (Error E = initLoadConfigPtr())
928+
if (Error E = ignoreStrippedErrors(initLoadConfigPtr()))
886929
return E;
887930

888931
return Error::success();

llvm/lib/Object/Error.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ std::string _object_error_category::message(int EV) const {
5252
return "Bitcode section not found in object file";
5353
case object_error::invalid_symbol_index:
5454
return "Invalid symbol index";
55+
case object_error::section_stripped:
56+
return "Section has been stripped from the object file";
5557
}
5658
llvm_unreachable("An enumerator of object_error does not have a message "
5759
"defined.");
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
--- !COFF
2+
OptionalHeader:
3+
AddressOfEntryPoint: 4512
4+
ImageBase: 6442450944
5+
SectionAlignment: 4096
6+
FileAlignment: 512
7+
MajorOperatingSystemVersion: 6
8+
MinorOperatingSystemVersion: 0
9+
MajorImageVersion: 0
10+
MinorImageVersion: 0
11+
MajorSubsystemVersion: 6
12+
MinorSubsystemVersion: 0
13+
Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI
14+
DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
15+
SizeOfStackReserve: 1048576
16+
SizeOfStackCommit: 4096
17+
SizeOfHeapReserve: 1048576
18+
SizeOfHeapCommit: 4096
19+
ExportTable:
20+
RelativeVirtualAddress: 6962976
21+
Size: 75
22+
ImportTable:
23+
RelativeVirtualAddress: 6963051
24+
Size: 420
25+
ResourceTable:
26+
RelativeVirtualAddress: 0
27+
Size: 0
28+
ExceptionTable:
29+
RelativeVirtualAddress: 6995968
30+
Size: 1812
31+
CertificateTable:
32+
RelativeVirtualAddress: 0
33+
Size: 0
34+
BaseRelocationTable:
35+
RelativeVirtualAddress: 7004160
36+
Size: 156
37+
Debug:
38+
RelativeVirtualAddress: 6987776
39+
Size: 28
40+
Architecture:
41+
RelativeVirtualAddress: 0
42+
Size: 0
43+
GlobalPtr:
44+
RelativeVirtualAddress: 0
45+
Size: 0
46+
TlsTable:
47+
RelativeVirtualAddress: 6962168
48+
Size: 40
49+
LoadConfigTable:
50+
RelativeVirtualAddress: 0
51+
Size: 0
52+
BoundImport:
53+
RelativeVirtualAddress: 0
54+
Size: 0
55+
IAT:
56+
RelativeVirtualAddress: 6965496
57+
Size: 2024
58+
DelayImportDescriptor:
59+
RelativeVirtualAddress: 0
60+
Size: 0
61+
ClrRuntimeHeader:
62+
RelativeVirtualAddress: 0
63+
Size: 0
64+
header:
65+
Machine: IMAGE_FILE_MACHINE_AMD64
66+
Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE, IMAGE_FILE_DLL ]
67+
sections:
68+
- Name: .text
69+
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
70+
VirtualAddress: 4096
71+
VirtualSize: 59030
72+
SectionData: ''
73+
- Name: .rdata
74+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
75+
VirtualAddress: 65536
76+
VirtualSize: 6919008
77+
SectionData: ''
78+
- Name: .buildid
79+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
80+
VirtualAddress: 6987776
81+
VirtualSize: 53
82+
SectionData: 00000000A9D795620000000002000000190000001CA06A001C040000525344535450075756EAA5DB4C4C44205044422E0100000000
83+
- Name: .data
84+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
85+
VirtualAddress: 6991872
86+
VirtualSize: 1960
87+
SectionData: ''
88+
- Name: .pdata
89+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
90+
VirtualAddress: 6995968
91+
VirtualSize: 1812
92+
SectionData: ''
93+
- Name: .tls
94+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
95+
VirtualAddress: 7000064
96+
VirtualSize: 16
97+
SectionData: ''
98+
- Name: .reloc
99+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
100+
VirtualAddress: 7004160
101+
VirtualSize: 156
102+
SectionData: ''
103+
- Name: .debug_info
104+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
105+
VirtualAddress: 7008256
106+
VirtualSize: 64
107+
SectionData: DEADBEEFBAADF00D
108+
symbols: []
109+
...
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
RUN: yaml2obj %p/Inputs/COFF/dwarf-debug-only.yaml | llvm-objdump -p -h - | FileCheck %s
2+
3+
CHECK: The Data Directory
4+
CHECK-NEXT: Entry 0 00000000006a3f20 0000004b Export Directory [.edata (or where ever we found it)]
5+
CHECK-NEXT: Entry 1 00000000006a3f6b 000001a4 Import Directory [parts of .idata]
6+
CHECK-NEXT: Entry 2 0000000000000000 00000000 Resource Directory [.rsrc]
7+
CHECK-NEXT: Entry 3 00000000006ac000 00000714 Exception Directory [.pdata]
8+
CHECK-NEXT: Entry 4 0000000000000000 00000000 Security Directory
9+
CHECK-NEXT: Entry 5 00000000006ae000 0000009c Base Relocation Directory [.reloc]
10+
CHECK-NEXT: Entry 6 00000000006aa000 0000001c Debug Directory
11+
CHECK-NEXT: Entry 7 0000000000000000 00000000 Description Directory
12+
CHECK-NEXT: Entry 8 0000000000000000 00000000 Special Directory
13+
CHECK-NEXT: Entry 9 00000000006a3bf8 00000028 Thread Storage Directory [.tls]
14+
CHECK-NEXT: Entry a 0000000000000000 00000000 Load Configuration Directory
15+
CHECK-NEXT: Entry b 0000000000000000 00000000 Bound Import Directory
16+
CHECK-NEXT: Entry c 00000000006a48f8 000007e8 Import Address Table Directory
17+
CHECK-NEXT: Entry d 0000000000000000 00000000 Delay Import Directory
18+
CHECK-NEXT: Entry e 0000000000000000 00000000 CLR Runtime Header
19+
CHECK-NEXT: Entry f 0000000000000000 00000000 Reserved
20+
21+
CHECK: 0 .text 00000000 0000000180001000 TEXT
22+
CHECK-NEXT: 1 .rdata 00000000 0000000180010000 DATA
23+
CHECK-NEXT: 2 .buildid 00000035 00000001806aa000 DATA
24+
CHECK-NEXT: 3 .data 00000000 00000001806ab000 DATA
25+
CHECK-NEXT: 4 .pdata 00000000 00000001806ac000 DATA
26+
CHECK-NEXT: 5 .tls 00000000 00000001806ad000 DATA
27+
CHECK-NEXT: 6 .reloc 00000000 00000001806ae000 DATA
28+
CHECK-NEXT: 7 .debug_info 00000040 00000001806af000 DATA, DEBUG

llvm/tools/llvm-objdump/COFFDump.cpp

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -430,21 +430,12 @@ static void printTLSDirectory(const COFFObjectFile *Obj) {
430430
if (!PE32Header && !PE32PlusHeader)
431431
return;
432432

433-
const data_directory *DataDir = Obj->getDataDirectory(COFF::TLS_TABLE);
434-
if (!DataDir || DataDir->RelativeVirtualAddress == 0)
435-
return;
436-
437-
uintptr_t IntPtr = 0;
438-
if (Error E =
439-
Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))
440-
reportError(std::move(E), Obj->getFileName());
441-
442433
if (PE32Header) {
443-
auto *TLSDir = reinterpret_cast<const coff_tls_directory32 *>(IntPtr);
444-
printTLSDirectoryT(TLSDir);
434+
if (auto *TLSDir = Obj->getTLSDirectory32())
435+
printTLSDirectoryT(TLSDir);
445436
} else {
446-
auto *TLSDir = reinterpret_cast<const coff_tls_directory64 *>(IntPtr);
447-
printTLSDirectoryT(TLSDir);
437+
if (auto *TLSDir = Obj->getTLSDirectory64())
438+
printTLSDirectoryT(TLSDir);
448439
}
449440

450441
outs() << "\n";
@@ -459,19 +450,10 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) {
459450
if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386)
460451
return;
461452

462-
const data_directory *DataDir = Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE);
463-
if (!DataDir)
464-
reportError("no load config data dir", Obj->getFileName());
465-
466-
uintptr_t IntPtr = 0;
467-
if (DataDir->RelativeVirtualAddress == 0)
453+
auto *LoadConf = Obj->getLoadConfig32();
454+
if (!LoadConf)
468455
return;
469456

470-
if (Error E =
471-
Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))
472-
reportError(std::move(E), Obj->getFileName());
473-
474-
auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr);
475457
outs() << "Load configuration:"
476458
<< "\n Timestamp: " << LoadConf->TimeDateStamp
477459
<< "\n Major Version: " << LoadConf->MajorVersion
@@ -544,11 +526,11 @@ static void printImportTables(const COFFObjectFile *Obj) {
544526
// Prints export tables. The export table is a table containing the list of
545527
// exported symbol from the DLL.
546528
static void printExportTable(const COFFObjectFile *Obj) {
547-
outs() << "Export Table:\n";
548529
export_directory_iterator I = Obj->export_directory_begin();
549530
export_directory_iterator E = Obj->export_directory_end();
550531
if (I == E)
551532
return;
533+
outs() << "Export Table:\n";
552534
StringRef DllName;
553535
uint32_t OrdinalBase;
554536
if (I->getDllName(DllName))

0 commit comments

Comments
 (0)