Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -19,6 +19,8 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/StringSaver.h" +#include + using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; @@ -41,6 +43,24 @@ void run(); private: + // This describes the programm header that will be created in output. + // Each contains type, access flags and list of output sections that will be + // placed in segment. + struct Phdr { + Phdr(unsigned Type = 0, unsigned Flags = 0) : Type(Type), Flags(Flags) {} + unsigned Type; + unsigned Flags; + std::list *> OutSections; + }; + // Its a headers map which contains list of headers and also some additional + // parameters, like prefound IDs of specific headers. + struct PhdrMap { + std::vector> PhdrList; + size_t FirstLoadId = (size_t)-1; + size_t TlsPhdrId = (size_t)-1; + size_t RelroPhdrId = (size_t)-1; + }; + void copyLocalSymbols(); void addReservedSymbols(); bool createSections(); @@ -53,7 +73,9 @@ void scanRelocs(InputSection &C); void scanRelocs(InputSectionBase &S, const Elf_Shdr &RelSec); void updateRelro(Elf_Phdr *Cur, Elf_Phdr *GnuRelroPhdr, uintX_t VA); + PhdrMap scanHeaders(); void assignAddresses(); + void finalizeHeaders(Elf_Phdr *PH, const Phdr &Map); void buildSectionMap(); void fixAbsoluteSymbols(); void openFile(StringRef OutputPath); @@ -67,7 +89,6 @@ bool isOutputDynamic() const { return !Symtab.getSharedFiles().empty() || Config->Shared; } - int getPhdrsNum() const; OutputSection *getBss(); void addCommonSymbols(std::vector &Syms); @@ -1071,138 +1092,200 @@ void Writer::updateRelro(Elf_Phdr *Cur, Elf_Phdr *GnuRelroPhdr, uintX_t VA) { if (!GnuRelroPhdr->p_type) - setPhdr(GnuRelroPhdr, PT_GNU_RELRO, PF_R, Cur->p_offset, Cur->p_vaddr, + setPhdr(GnuRelroPhdr, PT_GNU_RELRO, 0, Cur->p_offset, Cur->p_vaddr, VA - Cur->p_vaddr, 1 /*p_align*/); GnuRelroPhdr->p_filesz = VA - Cur->p_vaddr; GnuRelroPhdr->p_memsz = VA - Cur->p_vaddr; } -// Visits all sections to create PHDRs and to assign incremental, -// non-overlapping addresses to output sections. -template void Writer::assignAddresses() { - uintX_t VA = Target->getVAStart() + sizeof(Elf_Ehdr); - uintX_t FileOff = sizeof(Elf_Ehdr); - - // Calculate and reserve the space for the program header first so that - // the first section can start right after the program header. - Phdrs.resize(getPhdrsNum()); - size_t PhdrSize = sizeof(Elf_Phdr) * Phdrs.size(); +// This method generates some kind of map where each phdr +// is associated with zero or more output sections. +// Output structure contains final list of phdrs used. +// assignAddresses() will use this structure to assign addressed to sections. +template +typename Writer::PhdrMap Writer::scanHeaders() { + PhdrMap Map; + auto AddHdr = [this, &Map](unsigned Type, unsigned Flags = 0) { + std::unique_ptr U = std::make_unique(Type, Flags); + return Map.PhdrList.emplace(Map.PhdrList.end(), std::move(U))->get(); + }; // The first phdr entry is PT_PHDR which describes the program header itself. - setPhdr(&Phdrs[0], PT_PHDR, PF_R, FileOff, VA, PhdrSize, /*Align=*/8); - FileOff += PhdrSize; - VA += PhdrSize; + AddHdr(PT_PHDR, PF_R); // PT_INTERP must be the second entry if exists. - int PhdrIdx = 0; - Elf_Phdr *Interp = nullptr; - if (needsInterpSection()) - Interp = &Phdrs[++PhdrIdx]; + if (needsInterpSection()) { + Phdr *Hdr = AddHdr(PT_INTERP, toPhdrFlags(Out::Interp->getFlags())); + Hdr->OutSections.push_back(Out::Interp); + } // Add the first PT_LOAD segment for regular output sections. - setPhdr(&Phdrs[++PhdrIdx], PT_LOAD, PF_R, 0, Target->getVAStart(), FileOff, - Target->PageSize); + uintX_t Flags = PF_R; + Map.FirstLoadId = Map.PhdrList.size(); + Phdr *Load = AddHdr(PT_LOAD, Flags); - Elf_Phdr GnuRelroPhdr = {}; - Elf_Phdr TlsPhdr{}; - bool RelroAligned = false; - uintX_t ThreadBssOffset = 0; - // Create phdrs as we assign VAs and file offsets to all output sections. + std::unique_ptr TlsHdr; for (OutputSectionBase *Sec : OutputSections) { - Elf_Phdr *PH = &Phdrs[PhdrIdx]; if (needsPhdr(Sec)) { - uintX_t Flags = toPhdrFlags(Sec->getFlags()); - bool InRelRo = Config->ZRelro && (Flags & PF_W) && isRelroSection(Sec); - bool FirstNonRelRo = GnuRelroPhdr.p_type && !InRelRo && !RelroAligned; - if (FirstNonRelRo || PH->p_flags != Flags) { - VA = alignTo(VA, Target->PageSize); - FileOff = alignTo(FileOff, Target->PageSize); - if (FirstNonRelRo) - RelroAligned = true; + // If flags changed then we want new load segment. + uintX_t NewFlags = toPhdrFlags(Sec->getFlags()); + if (Flags != NewFlags) { + uint32_t LoadType = (Config->EMachine == EM_AMDGPU) ? getAmdgpuPhdr(Sec) + : (uint32_t)PT_LOAD; + Load = AddHdr(LoadType, NewFlags); + Flags = NewFlags; } - - if (PH->p_flags != Flags) { - // Flags changed. Create a new PT_LOAD. - PH = &Phdrs[++PhdrIdx]; - uint32_t PTType = (Config->EMachine != EM_AMDGPU) ? (uint32_t)PT_LOAD - : getAmdgpuPhdr(Sec); - setPhdr(PH, PTType, Flags, FileOff, VA, 0, Target->PageSize); - } - + // If we meet TLS section then we create TLS header + // and put all TLS sections inside for futher use when + // assign addresses. if (Sec->getFlags() & SHF_TLS) { - if (!TlsPhdr.p_vaddr) - setPhdr(&TlsPhdr, PT_TLS, PF_R, FileOff, VA, 0, Sec->getAlign()); - if (Sec->getType() != SHT_NOBITS) - VA = alignTo(VA, Sec->getAlign()); - uintX_t TVA = alignTo(VA + ThreadBssOffset, Sec->getAlign()); - Sec->setVA(TVA); - TlsPhdr.p_memsz += Sec->getSize(); - if (Sec->getType() == SHT_NOBITS) { - ThreadBssOffset = TVA - VA + Sec->getSize(); - } else { - TlsPhdr.p_filesz += Sec->getSize(); - VA += Sec->getSize(); - } - TlsPhdr.p_align = std::max(TlsPhdr.p_align, Sec->getAlign()); - } else { - VA = alignTo(VA, Sec->getAlign()); - Sec->setVA(VA); - VA += Sec->getSize(); - if (InRelRo) - updateRelro(PH, &GnuRelroPhdr, VA); + if (!TlsHdr) + TlsHdr = std::make_unique(PT_TLS, PF_R); + TlsHdr->OutSections.push_back(Sec); } } - - FileOff = alignTo(FileOff, Sec->getAlign()); - Sec->setFileOffset(FileOff); - if (Sec->getType() != SHT_NOBITS) - FileOff += Sec->getSize(); - if (needsPhdr(Sec)) { - PH->p_filesz = FileOff - PH->p_offset; - PH->p_memsz = VA - PH->p_vaddr; - } + Load->OutSections.push_back(Sec); } - if (TlsPhdr.p_vaddr) { - // The TLS pointer goes after PT_TLS. At least glibc will align it, - // so round up the size to make sure the offsets are correct. - TlsPhdr.p_memsz = alignTo(TlsPhdr.p_memsz, TlsPhdr.p_align); - Phdrs[++PhdrIdx] = TlsPhdr; - Out::TlsPhdr = &Phdrs[PhdrIdx]; + // TLS header. + if (TlsHdr) { + Map.TlsPhdrId = Map.PhdrList.size(); + Map.PhdrList.push_back(std::move(TlsHdr)); } // Add an entry for .dynamic. if (isOutputDynamic()) { - Elf_Phdr *PH = &Phdrs[++PhdrIdx]; - PH->p_type = PT_DYNAMIC; - copyPhdr(PH, Out::Dynamic); + Phdr *Hdr = AddHdr(PT_DYNAMIC, toPhdrFlags(Out::Dynamic->getFlags())); + Hdr->OutSections.push_back(Out::Dynamic); } + // PT_GNU_RELRO includes all sections that should be marked as + // read-only by dynamic linker after proccessing relocations. if (HasRelro) { - Elf_Phdr *PH = &Phdrs[++PhdrIdx]; - *PH = GnuRelroPhdr; + Map.RelroPhdrId = Map.PhdrList.size(); + AddHdr(PT_GNU_RELRO, PF_R); } + // PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr. if (Out::EhFrameHdr->Live) { - Elf_Phdr *PH = &Phdrs[++PhdrIdx]; - PH->p_type = PT_GNU_EH_FRAME; - copyPhdr(PH, Out::EhFrameHdr); + Phdr *Hdr = AddHdr(PT_GNU_EH_FRAME, toPhdrFlags(Out::EhFrameHdr->getFlags())); + Hdr->OutSections.push_back(Out::EhFrameHdr); } // PT_GNU_STACK is a special section to tell the loader to make the // pages for the stack non-executable. - if (!Config->ZExecStack) { - Elf_Phdr *PH = &Phdrs[++PhdrIdx]; - PH->p_type = PT_GNU_STACK; - PH->p_flags = PF_R | PF_W; - } + if (!Config->ZExecStack) + AddHdr(PT_GNU_STACK, PF_R | PF_W); - // Fix up PT_INTERP as we now know the address of .interp section. - if (Interp) { - Interp->p_type = PT_INTERP; - copyPhdr(Interp, Out::Interp); + return Map; +} + +static bool isLoadPhdr(unsigned H) { + return H == PT_LOAD || H == PT_AMDGPU_HSA_LOAD_CODE_AGENT || + H == PT_AMDGPU_HSA_LOAD_GLOBAL_PROGRAM; +} + +// Visits all headers in PhdrMap and assigns the adresses to +// the output sections. Also creates common and special headers. +template void Writer::assignAddresses() { + // We know the final amount of program headers here. + PhdrMap Map = scanHeaders(); + Phdrs.resize(Map.PhdrList.size()); + + bool RelroAligned = false; + uintX_t ThreadBssOffset = 0; + size_t PhdrSize = sizeof(Elf_Phdr) * Phdrs.size(); + uintX_t VA = Target->getVAStart() + sizeof(Elf_Ehdr) + PhdrSize; + uintX_t FileOff = sizeof(Elf_Ehdr) + PhdrSize; + + // Set up PT_PHDR right after ELF file header. + setPhdr(&Phdrs[0], 0, 0, FileOff - PhdrSize, VA - PhdrSize, PhdrSize, 8); + + // Main pass, here all load segments are processed and + // final addresses are assigned to all sections. + // Flags and header types are not assigned during pass. + // Them are assigned once for all headers at second pass. + for (size_t I = 0, E = Map.PhdrList.size(); I != E; ++I) { + std::unique_ptr &PHdr = Map.PhdrList[I]; + if (!isLoadPhdr(PHdr->Type)) + continue; + + Elf_Phdr *PH = &Phdrs[I]; + // New PT_LOAD is usually created when access flags were changed. + // If it is the very first load then it is special case, + // it contains elf header and program headers. + // Otherwise we need to align VA and FileOff to new memory page + // and create a new load. + const bool FirstLoad = (Map.FirstLoadId == I); + if (!FirstLoad) { + VA = alignTo(VA, Target->PageSize); + FileOff = alignTo(FileOff, Target->PageSize); + } + uintX_t Offset = FirstLoad ? 0 : FileOff; + uintX_t LoadVA = FirstLoad ? Target->getVAStart() : VA; + uintX_t LoadSize = FirstLoad ? FileOff : 0; + setPhdr(PH, 0, 0, Offset, LoadVA, LoadSize, Target->PageSize); + + for (OutputSectionBase *Sec : PHdr->OutSections) { + bool InRelRo = HasRelro && (PHdr->Flags & PF_W) && isRelroSection(Sec); + bool FirstNonRelRo = HasRelro && Phdrs[Map.RelroPhdrId].p_type && + needsPhdr(Sec) && !RelroAligned && !InRelRo; + // If this is a first non relro section after relro sequence then + // we want to align to page boundaries. We do this because we dont want + // any other sections except relro were marked as read only by dynamic + // loader. Them should be placed on a different memory page for that. + if (FirstNonRelRo) { + VA = alignTo(VA, Target->PageSize); + FileOff = alignTo(FileOff, Target->PageSize); + RelroAligned = true; + } + + if (needsPhdr(Sec)) { + bool IsTls = Sec->getFlags() & SHF_TLS; + // If we found TLS section we want to fill phdr parameters early + // because later we will not have its VA and FileOff. + if (IsTls && !Phdrs[Map.TlsPhdrId].p_vaddr) + setPhdr(&Phdrs[Map.TlsPhdrId], 0, 0, FileOff, VA, 0, 0); + + // Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is + // responsible for allocating space for them, not the PT_LOAD that + // contains the TLS initialization image. + if (!IsTls || Sec->getType() != SHT_NOBITS) + VA = alignTo(VA, Sec->getAlign()); + + uintX_t TVA = alignTo(VA + ThreadBssOffset, Sec->getAlign()); + Sec->setVA(IsTls ? TVA : VA); + + if (IsTls && Sec->getType() == SHT_NOBITS) + ThreadBssOffset = TVA - VA + Sec->getSize(); + else + VA += Sec->getSize(); + + // If we are in relro then we need to update + // the GnuRelroPhdr header. + if (InRelRo) + updateRelro(PH, &Phdrs[Map.RelroPhdrId], VA); + } + + FileOff = alignTo(FileOff, Sec->getAlign()); + Sec->setFileOffset(FileOff); + if (Sec->getType() != SHT_NOBITS) + FileOff += Sec->getSize(); + if (needsPhdr(Sec)) { + PH->p_filesz = FileOff - PH->p_offset; + PH->p_memsz = VA - PH->p_vaddr; + } + } } + // Assign phdrs values in according to sections parameters attached. + // Each segment can contain zero, one or multiple sections assigned. + // Not just load segments but any others as well. That can be achieved usually + // with linker script. finalizeHeaders modifies each segment parameters and size + // in the way to cover all sections inside. + for (size_t I = 0, E = Map.PhdrList.size(); I != E; ++I) + finalizeHeaders(&Phdrs[I], *Map.PhdrList[I].get()); + // Add space for section headers. SectionHeaderOff = alignTo(FileOff, sizeof(uintX_t)); FileSize = SectionHeaderOff + getNumSections() * sizeof(Elf_Shdr); @@ -1212,35 +1295,35 @@ ElfSym::End.st_value = VA; } -// Returns the number of PHDR entries. -template int Writer::getPhdrsNum() const { - bool Tls = false; - int I = 2; // 2 for PT_PHDR and first PT_LOAD - if (needsInterpSection()) - ++I; - if (isOutputDynamic()) - ++I; - if (!Config->ZExecStack) - ++I; - uintX_t Last = PF_R; - for (OutputSectionBase *Sec : OutputSections) { - if (!needsPhdr(Sec)) - continue; - if (Sec->getFlags() & SHF_TLS) - Tls = true; - uintX_t Flags = toPhdrFlags(Sec->getFlags()); - if (Last != Flags) { - Last = Flags; - ++I; +template +void Writer::finalizeHeaders(Elf_Phdr *PH, const Phdr &PHdr) { + // We already handled PT_LOAD and PT_PHDR and do not proccess them. + if (!isLoadPhdr(PHdr.Type) && PHdr.Type != PT_PHDR) { + for (OutputSectionBase *Sec : PHdr.OutSections) { + // Initially we initialize the header with parameters + // of first output section attached. + if (PH->p_memsz == 0 && PHdr.Type != PT_TLS) { + copyPhdr(PH, Sec); + continue; + } + // Here we extending the header size to cover all sections inside it. + if (Sec->getType() != SHT_NOBITS) + PH->p_filesz += Sec->getSize(); + PH->p_memsz += Sec->getSize(); + PH->p_align = std::max(PH->p_align, Sec->getAlign()); } } - if (Tls) - ++I; - if (HasRelro) - ++I; - if (Out::EhFrameHdr->Live) - ++I; - return I; + + // Set type and flags. + PH->p_type = PHdr.Type; + PH->p_flags = PHdr.Flags; + + // The TLS pointer goes after PT_TLS. At least glibc will align it, + // so round up the size to make sure the offsets are correct. + if (PHdr.Type == PT_TLS) { + PH->p_memsz = alignTo(PH->p_memsz, PH->p_align); + Out::TlsPhdr = PH; + } } static uint32_t getELFFlags() {