diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1049,51 +1049,68 @@ ArrayRef eSyms = this->getELFSyms(); 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(this, name, STB_LOCAL, eSym.st_other, type); + else if (sec == &InputSection::discarded) + this->symbols[i] = + make(this, name, STB_LOCAL, eSym.st_other, type, + /*discardedSecIdx=*/secIdx); + else + this->symbols[i] = make(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(this, name, binding, stOther, type); - else if (sec == &InputSection::discarded) - this->symbols[i] = make(this, name, binding, stOther, type, - /*DiscardedSecIdx=*/secIdx); - else - this->symbols[i] = - make(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}); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -763,9 +763,7 @@ for (InputFile *file : objectFiles) { ObjFile *f = cast>(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(b); // No reason to keep local undefined symbol in symtab. diff --git a/lld/test/ELF/invalid/symtab-sh-info-dup.test b/lld/test/ELF/invalid/symtab-sh-info-dup.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/invalid/symtab-sh-info-dup.test @@ -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 diff --git a/lld/test/ELF/invalid/symtab-sh-info.s b/lld/test/ELF/invalid/symtab-sh-info.s --- a/lld/test/ELF/invalid/symtab-sh-info.s +++ b/lld/test/ELF/invalid/symtab-sh-info.s @@ -22,8 +22,8 @@ ## 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: