Object/COFF: Support large relocation table.

NumberOfRelocations field in COFF section table is only 16-bit wide. If an
object has more than 65535 relocations, the number of relocations is stored
to VirtualAddress field in the first relocation field, and a special flag
(IMAGE_SCN_LNK_NRELOC_OVFL) is set to Characteristics field.

In test we cheated a bit. I made up a test file so that it has
IMAGE_SCN_LNK_NRELOC_OVFL flag but the number of relocations is much smaller
than 65535. This is to avoid checking in a large test file just to test a
file with many relocations.

Differential Revision: http://llvm-reviews.chandlerc.com/D3139

llvm-svn: 204418
This commit is contained in:
Rui Ueyama 2014-03-21 00:44:19 +00:00
parent 6e9dde5149
commit 14b8467bd2
4 changed files with 53 additions and 11 deletions

View File

@ -250,6 +250,13 @@ struct coff_section {
support::ulittle16_t NumberOfRelocations;
support::ulittle16_t NumberOfLinenumbers;
support::ulittle32_t Characteristics;
// Returns true if the actual number of relocations is stored in
// VirtualAddress field of the first relocation table entry.
bool hasExtendedRelocations() const {
return Characteristics & COFF::IMAGE_SCN_LNK_NRELOC_OVFL &&
NumberOfRelocations == UINT16_MAX;
};
};
struct coff_relocation {

View File

@ -367,25 +367,46 @@ error_code COFFObjectFile::sectionContainsSymbol(DataRefImpl SecRef,
relocation_iterator COFFObjectFile::section_rel_begin(DataRefImpl Ref) const {
const coff_section *Sec = toSec(Ref);
DataRefImpl Ret;
if (Sec->NumberOfRelocations == 0)
if (Sec->NumberOfRelocations == 0) {
Ret.p = 0;
else
Ret.p = reinterpret_cast<uintptr_t>(base() + Sec->PointerToRelocations);
} else {
auto begin = reinterpret_cast<const coff_relocation*>(
base() + Sec->PointerToRelocations);
if (Sec->hasExtendedRelocations()) {
// Skip the first relocation entry repurposed to store the number of
// relocations.
begin++;
}
Ret.p = reinterpret_cast<uintptr_t>(begin);
}
return relocation_iterator(RelocationRef(Ret, this));
}
static uint32_t getNumberOfRelocations(const coff_section *Sec,
const uint8_t *base) {
// The field for the number of relocations in COFF section table is only
// 16-bit wide. If a section has more than 65535 relocations, 0xFFFF is set to
// NumberOfRelocations field, and the actual relocation count is stored in the
// VirtualAddress field in the first relocation entry.
if (Sec->hasExtendedRelocations()) {
auto *FirstReloc = reinterpret_cast<const coff_relocation*>(
base + Sec->PointerToRelocations);
return FirstReloc->VirtualAddress;
}
return Sec->NumberOfRelocations;
}
relocation_iterator COFFObjectFile::section_rel_end(DataRefImpl Ref) const {
const coff_section *Sec = toSec(Ref);
DataRefImpl Ret;
if (Sec->NumberOfRelocations == 0)
if (Sec->NumberOfRelocations == 0) {
Ret.p = 0;
else
Ret.p = reinterpret_cast<uintptr_t>(
reinterpret_cast<const coff_relocation*>(
base() + Sec->PointerToRelocations)
+ Sec->NumberOfRelocations);
} else {
auto begin = reinterpret_cast<const coff_relocation*>(
base() + Sec->PointerToRelocations);
uint32_t NumReloc = getNumberOfRelocations(Sec, base());
Ret.p = reinterpret_cast<uintptr_t>(begin + NumReloc);
}
return relocation_iterator(RelocationRef(Ret, this));
}

Binary file not shown.

View File

@ -0,0 +1,14 @@
// Test that llvm-objdump can handle IMAGE_SCN_LNK_NRELOC_OVFL.
// RUN: llvm-objdump -r %p/Inputs/many-relocs.obj-i386 | FileCheck %s
CHECK: RELOCATION RECORDS FOR [.text]:
CHECK-NEXT: IMAGE_REL_I386_DIR16 foo
CHECK-NEXT: IMAGE_REL_I386_REL16 foo
CHECK-NEXT: IMAGE_REL_I386_DIR32 foo
CHECK-NEXT: IMAGE_REL_I386_DIR32NB foo
CHECK-NEXT: IMAGE_REL_I386_SEG12 foo
CHECK-NEXT: IMAGE_REL_I386_SECTION foo
CHECK-NEXT: IMAGE_REL_I386_SECREL foo
CHECK-NEXT: IMAGE_REL_I386_TOKEN foo
CHECK-NEXT: IMAGE_REL_I386_SECREL7 foo
CHECK-NEXT: IMAGE_REL_I386_REL32 foo