Index: include/lld/Core/DefinedAtom.h =================================================================== --- include/lld/Core/DefinedAtom.h +++ include/lld/Core/DefinedAtom.h @@ -201,6 +201,7 @@ codeMipsMicro, // microMIPS instruction encoding codeMipsMicroPIC, // microMIPS instruction encoding + PIC codeMips16, // MIPS-16 instruction encoding + codeARMThumb, // ARM Thumb instruction set }; struct Alignment { Index: lib/ReaderWriter/ELF/ARM/ARMELFFile.h =================================================================== --- lib/ReaderWriter/ELF/ARM/ARMELFFile.h +++ lib/ReaderWriter/ELF/ARM/ARMELFFile.h @@ -17,6 +17,52 @@ class ARMLinkingContext; +template class ARMELFDefinedAtom : public ELFDefinedAtom { + typedef llvm::object::Elf_Sym_Impl Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl Elf_Shdr; + +public: + ARMELFDefinedAtom(const ELFFile &file, StringRef symbolName, + StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector *> &referenceList) + : ELFDefinedAtom(file, symbolName, sectionName, symbol, section, + contentData, referenceStart, referenceEnd, + referenceList) {} + + static bool isThumbFunc(const Elf_Sym *symbol) { + return symbol->getType() == llvm::ELF::STT_FUNC && + (static_cast(symbol->st_value) & 0x1); + } + + DefinedAtom::CodeModel codeModel() const override { + if (isThumbFunc(this->_symbol)) + return DefinedAtom::codeARMThumb; + return DefinedAtom::codeNA; + } + + DefinedAtom::Alignment alignment() const override { + // Remove the least significant bit since it's used + // to point to Thumb symbols and should be dropped for calculations. + const uint64_t st_value = isThumbFunc(this->_symbol) + ? static_cast(this->_symbol->st_value) & ~0x1 + : this->_symbol->st_value; + + // Unallocated common symbols specify their alignment constraints in + // st_value. + if ((this->_symbol->getType() == llvm::ELF::STT_COMMON) || + this->_symbol->st_shndx == llvm::ELF::SHN_COMMON) { + return DefinedAtom::Alignment(llvm::Log2_64(st_value)); + } else if (this->_section->sh_addralign == 0) { + // sh_addralign of 0 means no alignment + return DefinedAtom::Alignment(0, st_value); + } + return DefinedAtom::Alignment(llvm::Log2_64(this->_section->sh_addralign), + st_value % this->_section->sh_addralign); + } +}; + template class ARMELFFile : public ELFFile { public: ARMELFFile(std::unique_ptr mb, bool atomizeStrings) @@ -27,6 +73,101 @@ return std::unique_ptr>( new ARMELFFile(std::move(mb), atomizeStrings)); } + +private: + typedef llvm::object::Elf_Sym_Impl Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl Elf_Shdr; + typedef typename Elf_Sym::Elf_Addr Elf_Addr; + typedef llvm::object::Elf_Rel_Impl Elf_Rel; + typedef llvm::object::Elf_Rel_Impl Elf_Rela; + typedef typename llvm::object::ELFFile::Elf_Rela_Iter Elf_Rela_Iter; + typedef typename llvm::object::ELFFile::Elf_Rel_Iter Elf_Rel_Iter; + + static bool isThumbFunc(const Elf_Sym *symbol) { + return symbol->getType() == llvm::ELF::STT_FUNC && + (static_cast(symbol->st_value) & 0x1); + } + + static ArrayRef correctContentAddress(ArrayRef content, + const Elf_Sym *symbol) { + if (content.empty() || !isThumbFunc(symbol)) + return content; + + return ArrayRef(content.data() - 1, content.size()); + } + + static uint64_t correctSymbolStValue(const Elf_Sym *symbol) { + const auto value = static_cast(symbol->st_value); + return isThumbFunc(symbol) ? value & ~0x1 : value; + } + + uint64_t symbolContentSize(const Elf_Shdr *section, const Elf_Sym *symbol, + const Elf_Sym *nextSymbol) override { + const auto symValue = correctSymbolStValue(symbol); + // if this is the last symbol, take up the remaining data. + return nextSymbol + ? correctSymbolStValue(nextSymbol) - symValue + : section->sh_size - symValue; + } + + /// Process the Absolute symbol and create an atom for it. + ErrorOr *> handleAbsoluteSymbol(StringRef symName, + const Elf_Sym *sym, int64_t value) override { + // Ugly, but no better way to check that the value should be corrected + if (static_cast(sym->st_value) == value) { + value = static_cast(correctSymbolStValue(sym)); + } + return new (this->_readerStorage) + ELFAbsoluteAtom(*this, symName, sym, value); + } + + /// Process the Defined symbol and create an atom for it. + ErrorOr *> handleDefinedSymbol(StringRef symName, + StringRef sectionName, + const Elf_Sym *sym, const Elf_Shdr *sectionHdr, + ArrayRef contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector *> &referenceList) override { + contentData = correctContentAddress(contentData, sym); + return new (this->_readerStorage) ARMELFDefinedAtom( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + void createRelocationReferences(const Elf_Sym &symbol, + ArrayRef content, + range rels) override { + auto symContent = correctContentAddress(content, &symbol); + const auto symValue = correctSymbolStValue(&symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || + symValue + symContent.size() <= rel.r_offset) + continue; + this->_references.push_back(new (this->_readerStorage) ELFReference( + &rel, rel.r_offset - symValue, this->kindArch(), + rel.getType(false), rel.getSymbol(false))); + } + } + + void createRelocationReferences(const Elf_Sym &symbol, + ArrayRef content, + ArrayRef secContent, + range rels) override { + auto symContent = correctContentAddress(content, &symbol); + const auto symValue = correctSymbolStValue(&symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || + symValue + symContent.size() <= rel.r_offset) + continue; + + this->_references.push_back(new (this->_readerStorage) ELFReference( + &rel, rel.r_offset - symValue, this->kindArch(), + rel.getType(false), rel.getSymbol(false))); + + // Calculated in the relocation handler. + this->_references.back()->setAddend(0); + } + } }; template class ARMDynamicFile : public DynamicFile { Index: lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h =================================================================== --- lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h +++ lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h @@ -12,6 +12,7 @@ #include "ExecutableWriter.h" #include "ARMLinkingContext.h" #include "ARMTargetHandler.h" +#include "ARMSymbolTable.h" namespace lld { namespace elf { @@ -35,6 +36,9 @@ ExecutableWriter::addDefaultAtoms(); } + /// \brief Create symbol table. + LLD_UNIQUE_BUMP_PTR(SymbolTable) createSymbolTable() override; + private: ARMLinkingContext &_context; ARMTargetLayout &_armLayout; @@ -53,6 +57,13 @@ return true; } +template +LLD_UNIQUE_BUMP_PTR(SymbolTable) + ARMExecutableWriter::createSymbolTable() { + return LLD_UNIQUE_BUMP_PTR(SymbolTable)( + new (this->_alloc) ARMSymbolTable(this->_context)); +} + } // namespace elf } // namespace lld Index: lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h =================================================================== --- /dev/null +++ lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h @@ -0,0 +1,46 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H +#define LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H + +namespace lld { +namespace elf { + +/// \brief The SymbolTable class represents the symbol table in a ELF file +template +class ARMSymbolTable : public SymbolTable { +public: + typedef llvm::object::Elf_Sym_Impl Elf_Sym; + + ARMSymbolTable(const ELFLinkingContext &context); + + void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) override; +}; + +template +ARMSymbolTable::ARMSymbolTable(const ELFLinkingContext &context) + : SymbolTable(context, ".symtab", + DefaultLayout::ORDER_SYMBOL_TABLE) {} + +template +void ARMSymbolTable::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable::addDefinedAtom(sym, da, addr); + + // Set zero bit to distinguish symbols addressing Thumb instructions + if (DefinedAtom::codeARMThumb == da->codeModel()) + sym.st_value = static_cast(sym.st_value) | 0x1; +} + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H Index: lib/ReaderWriter/ELF/ELFFile.h =================================================================== --- lib/ReaderWriter/ELF/ELFFile.h +++ lib/ReaderWriter/ELF/ELFFile.h @@ -213,9 +213,9 @@ /// Returns the symbol's content size. The nextSymbol should be null if the /// symbol is the last one in the section. - uint64_t symbolContentSize(const Elf_Shdr *section, - const Elf_Sym *symbol, - const Elf_Sym *nextSymbol); + virtual uint64_t symbolContentSize(const Elf_Shdr *section, + const Elf_Sym *symbol, + const Elf_Sym *nextSymbol); void createEdge(ELFDefinedAtom *from, ELFDefinedAtom *to, uint32_t edgeKind); @@ -263,7 +263,7 @@ } /// Process the Absolute symbol and create an atom for it. - ErrorOr *> + virtual ErrorOr *> handleAbsoluteSymbol(StringRef symName, const Elf_Sym *sym, int64_t value) { return new (_readerStorage) ELFAbsoluteAtom(*this, symName, sym, value); Index: lib/ReaderWriter/YAML/ReaderWriterYAML.cpp =================================================================== --- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -394,6 +394,7 @@ io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro); io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC); io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16); + io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb); } }; Index: test/elf/ARM/defsym.test =================================================================== --- test/elf/ARM/defsym.test +++ test/elf/ARM/defsym.test @@ -1,28 +1,24 @@ # Check that defined symbols are present in the generated executable # RUN: yaml2obj -format=elf %s > %t-o.o -# RUN: lld -flavor gnu -target arm-linux-gnu --defsym=main=fn -e=fn \ -# RUN: -static --noinhibit-exec %t-o.o -o %t +# RUN: lld -flavor gnu -target arm-linux-gnu --defsym=main=fn \ +# RUN: -Bstatic --noinhibit-exec %t-o.o -o %t # RUN: llvm-readobj -symbols %t | FileCheck %s -# CHECK: Symbol { # CHECK: Name: main (1) -# CHECK: Value: 0x400075 -# CHECK: Size: 0 -# CHECK: Binding: Global (0x1) -# CHECK: Type: Function (0x2) -# CHECK: Other: 0 -# CHECK: Section: .text (0x1) -# CHECK: } -# CHECK: Symbol { +# CHECK-NEXT: Value: 0x400074 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x1) # CHECK: Name: fn (6) -# CHECK: Value: 0x400075 -# CHECK: Size: 15 -# CHECK: Binding: Global (0x1) -# CHECK: Type: Function (0x2) -# CHECK: Other: 0 -# CHECK: Section: .text (0x1) -# CHECK: } +# CHECK-NEXT: Value: 0x400074 +# CHECK-NEXT: Size: {{[0-9]+}} +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x1) --- FileHeader: @@ -36,7 +32,7 @@ Type: SHT_PROGBITS Flags: [ SHF_ALLOC, SHF_EXECINSTR ] AddressAlign: 0x0000000000000004 - Content: 80B400AF00231846BD465DF8047B7047 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE1 - Name: .data Type: SHT_PROGBITS Flags: [ SHF_WRITE, SHF_ALLOC ] @@ -47,28 +43,9 @@ Flags: [ SHF_WRITE, SHF_ALLOC ] AddressAlign: 0x0000000000000001 Content: '' - - Name: .note.GNU-stack - Type: SHT_PROGBITS - AddressAlign: 0x0000000000000001 - Content: '' Symbols: - Local: - - Name: .text - Type: STT_SECTION - Section: .text - - Name: .data - Type: STT_SECTION - Section: .data - - Name: .bss - Type: STT_SECTION - Section: .bss - - Name: .note.GNU-stack - Type: STT_SECTION - Section: .note.GNU-stack Global: - Name: fn Type: STT_FUNC Section: .text - Value: 0x0000000000000001 - Size: 0x0000000000000010 ... Index: test/elf/ARM/symsize.test =================================================================== --- /dev/null +++ test/elf/ARM/symsize.test @@ -0,0 +1,82 @@ +# 1. Check that symbol content size formed from ARM instructions is valid. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-a.o +# RUN: lld -flavor gnu -target arm-linux-gnu \ +# RUN: -Bstatic --noinhibit-exec %t-a.o -o %t-a +# RUN: llvm-readobj -symbols %t-a | FileCheck -check-prefix=ARM-SIZE %s + +# ARM-SIZE: Name: main (1) +# ARM-SIZE-NEXT: Value: 0x{{[0-9a-f]+}} +# ARM-SIZE-NEXT: Size: 28 + +# 2. Check that symbol content size formed from Thumb instructions is valid. + +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-t.o +# RUN: lld -flavor gnu -target arm-linux-gnu \ +# RUN: -Bstatic --noinhibit-exec %t-t.o -o %t-t +# RUN: llvm-readobj -symbols %t-t | FileCheck -check-prefix=THM-SIZE %s + +# THM-SIZE: Name: main (1) +# THM-SIZE-NEXT: Value: 0x{{[0-9a-f]+}} +# THM-SIZE-NEXT: Size: 16 + +# syma.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text +# symt.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF00231846BD465DF8047B7047 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +... Index: test/elf/ARM/thm-symtab.test =================================================================== --- /dev/null +++ test/elf/ARM/thm-symtab.test @@ -0,0 +1,40 @@ +# Check that symbols addressing Thumb instructions have zero bit set. + +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm-linux-gnu -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-readobj -symbols %t | FileCheck %s + +# CHECK: Name: main (1) +# CHECK-NEXT: Value: 0x400075 + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF00231846BD465DF8047B7047 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +...