forked from OSchip/llvm-project
[ELF] Refactor ObjFile<ELFT>::initializeSymbols to enforce the invariant: InputFile::symbols has non null entry
Fixes PR46348. ObjFile<ELFT>::initializeSymbols contains two symbol iteration loops: ``` for each symbol if non-inheriting && non-local fill in this->symbols[i] for each symbol if local fill in this->symbols[i] else symbol resolution ``` Symbol resolution can trigger a duplicate symbol error which will call InputSectionBase::getObjMsg to iterate over InputFile::symbols. If a non-local symbol appears after the non-local symbol being resolved (violating ELF spec), its `this->symbols[i]` entry has not been filled in, InputSectionBase::getObjMsg will crash due to `dyn_cast<Defined>(nullptr)`. To fix the bug, reorganize the two loops to ensure this->symbols is complete before symbol resolution. This enforces the invariant: InputFile::symbols has none null entry when InputFile::getSymbols() is called. ``` for each symbol if non-inheriting fill in this->symbols[i] for each symbol starting from firstGlobal if non-local symbol resolution ``` Additionally, move the (non-local symbol in local part of .symtab) diagnostic from Writer<ELFT>::copyLocalSymbols() to initializeSymbols(). Reviewed By: grimar, jhenderson Differential Revision: https://reviews.llvm.org/D81988
This commit is contained in:
parent
2b87a44c49
commit
c4d13f72a6
|
@ -1049,51 +1049,68 @@ template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
|
|||
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
|
||||
this->symbols.resize(eSyms.size());
|
||||
|
||||
// Our symbol table may have already been partially initialized
|
||||
// Fill in InputFile::symbols. Some entries have been initialized
|
||||
// because of LazyObjFile.
|
||||
for (size_t i = 0, end = eSyms.size(); i != end; ++i)
|
||||
if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL)
|
||||
this->symbols[i] =
|
||||
symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
|
||||
|
||||
// Fill this->Symbols. A symbol is either local or global.
|
||||
for (size_t i = 0, end = eSyms.size(); i != end; ++i) {
|
||||
if (this->symbols[i])
|
||||
continue;
|
||||
const Elf_Sym &eSym = eSyms[i];
|
||||
|
||||
// Read symbol attributes.
|
||||
uint32_t secIdx = getSectionIndex(eSym);
|
||||
if (secIdx >= this->sections.size())
|
||||
fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
|
||||
if (eSym.getBinding() != STB_LOCAL) {
|
||||
if (i < firstGlobal)
|
||||
error(toString(this) + ": non-local symbol (" + Twine(i) +
|
||||
") found at index < .symtab's sh_info (" + Twine(firstGlobal) +
|
||||
")");
|
||||
this->symbols[i] =
|
||||
symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle local symbols. Local symbols are not added to the symbol
|
||||
// table because they are not visible from other object files. We
|
||||
// allocate symbol instances and add their pointers to symbols.
|
||||
if (i >= firstGlobal)
|
||||
errorOrWarn(toString(this) + ": STB_LOCAL symbol (" + Twine(i) +
|
||||
") found at index >= .symtab's sh_info (" +
|
||||
Twine(firstGlobal) + ")");
|
||||
|
||||
InputSectionBase *sec = this->sections[secIdx];
|
||||
uint8_t type = eSym.getType();
|
||||
if (type == STT_FILE)
|
||||
sourceFile = CHECK(eSym.getName(this->stringTable), this);
|
||||
if (this->stringTable.size() <= eSym.st_name)
|
||||
fatal(toString(this) + ": invalid symbol name offset");
|
||||
StringRefZ name = this->stringTable.data() + eSym.st_name;
|
||||
|
||||
if (eSym.st_shndx == SHN_UNDEF)
|
||||
this->symbols[i] =
|
||||
make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type);
|
||||
else if (sec == &InputSection::discarded)
|
||||
this->symbols[i] =
|
||||
make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type,
|
||||
/*discardedSecIdx=*/secIdx);
|
||||
else
|
||||
this->symbols[i] = make<Defined>(this, name, STB_LOCAL, eSym.st_other,
|
||||
type, eSym.st_value, eSym.st_size, sec);
|
||||
}
|
||||
|
||||
// Symbol resolution of non-local symbols.
|
||||
for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
|
||||
const Elf_Sym &eSym = eSyms[i];
|
||||
uint8_t binding = eSym.getBinding();
|
||||
if (binding == STB_LOCAL)
|
||||
continue; // Errored above.
|
||||
|
||||
uint32_t secIdx = getSectionIndex(eSym);
|
||||
InputSectionBase *sec = this->sections[secIdx];
|
||||
uint8_t stOther = eSym.st_other;
|
||||
uint8_t type = eSym.getType();
|
||||
uint64_t value = eSym.st_value;
|
||||
uint64_t size = eSym.st_size;
|
||||
StringRefZ name = this->stringTable.data() + eSym.st_name;
|
||||
|
||||
// Handle local symbols. Local symbols are not added to the symbol
|
||||
// table because they are not visible from other object files. We
|
||||
// allocate symbol instances and add their pointers to Symbols.
|
||||
if (binding == STB_LOCAL) {
|
||||
if (eSym.getType() == STT_FILE)
|
||||
sourceFile = CHECK(eSym.getName(this->stringTable), this);
|
||||
|
||||
if (this->stringTable.size() <= eSym.st_name)
|
||||
fatal(toString(this) + ": invalid symbol name offset");
|
||||
|
||||
if (eSym.st_shndx == SHN_UNDEF)
|
||||
this->symbols[i] = make<Undefined>(this, name, binding, stOther, type);
|
||||
else if (sec == &InputSection::discarded)
|
||||
this->symbols[i] = make<Undefined>(this, name, binding, stOther, type,
|
||||
/*DiscardedSecIdx=*/secIdx);
|
||||
else
|
||||
this->symbols[i] =
|
||||
make<Defined>(this, name, binding, stOther, type, value, size, sec);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle global undefined symbols.
|
||||
if (eSym.st_shndx == SHN_UNDEF) {
|
||||
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
|
||||
|
|
|
@ -763,9 +763,7 @@ template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
|
|||
for (InputFile *file : objectFiles) {
|
||||
ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
|
||||
for (Symbol *b : f->getLocalSymbols()) {
|
||||
if (!b->isLocal())
|
||||
fatal(toString(f) +
|
||||
": broken object: getLocalSymbols returns a non-local symbol");
|
||||
assert(b->isLocal() && "should have been caught in initializeSymbols()");
|
||||
auto *dr = dyn_cast<Defined>(b);
|
||||
|
||||
// No reason to keep local undefined symbol in symtab.
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# REQUIRES: x86
|
||||
## The ELF spec says all symbols with STB_LOCAL binding precede the weak and
|
||||
## global symbols. If a local symbol is found in the non-local part of the
|
||||
## symbol table, make sure we have filled in all entries of InputFile::symbols.
|
||||
## Otherwise a null entry can lead to a null pointer dereference when iterating
|
||||
## over InputFile::symbols.
|
||||
|
||||
# RUN: yaml2obj %s -o %t.o
|
||||
# RUN: not ld.lld %t.o %t.o -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
# CHECK: error: {{.*}}.o: STB_LOCAL symbol (2) found at index >= .symtab's sh_info (1)
|
||||
# CHECK-NEXT: error: {{.*}}.o: STB_LOCAL symbol (2) found at index >= .symtab's sh_info (1)
|
||||
# CHECK-NEXT: error: duplicate symbol: _start
|
||||
# CHECK-NEXT: >>> defined at {{.*}}.o:(.text+0x0)
|
||||
# CHECK-NEXT: >>> defined at {{.*}}.o:(.text+0x0)
|
||||
|
||||
# RUN: ld.lld --noinhibit-exec %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN
|
||||
# WARN: warning: {{.*}}.o: STB_LOCAL symbol (2) found at index >= .symtab's sh_info (1)
|
||||
|
||||
!ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_REL
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Type: SHT_PROGBITS
|
||||
Name: .text
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Symbols:
|
||||
- Name: _start
|
||||
Section: .text
|
||||
Binding: STB_GLOBAL
|
||||
- Name: local
|
||||
Section: .text
|
||||
Binding: STB_LOCAL
|
|
@ -22,8 +22,8 @@ Symbols:
|
|||
## sh_info has value 2 what says that non-local symbol `foo` is local.
|
||||
## Check we report this case.
|
||||
# RUN: yaml2obj --docnum=2 %s -o %t.o
|
||||
# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR2 %s
|
||||
# ERR2: broken object: getLocalSymbols returns a non-local symbol
|
||||
# RUN: not ld.lld --noinhibit-exec %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR2 %s
|
||||
# ERR2: error: {{.*}}.o: non-local symbol (1) found at index < .symtab's sh_info (2)
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
|
|
Loading…
Reference in New Issue