Phdrs_cmds

Reference reference

phdrsCommands (empty for most binary)

In LinkerScript:

// lld/ELF/LinkerScript.h

class LinkerScript final{
  
  // PHDRS command list.
  std::vector<PhdrsCommand> phdrsCommands;

};

In LLD Code

updated in two ways;

  • based on the linker script if exists;
  • by the linker itself if no linker script is given (true for binary and relocatable outputs);

The driven function to update the program header commands, in void Writer<ELFT>::finalizeSections()

// lld/ELF/Writer.cpp

// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::finalizeSections() {
 ...
 if (!config->relocatable && !config->oFormatBinary) {
    for (Partition &part : partitions) {
      part.phdrs = script->hasPhdrsCommands() ? script->createPhdrs()
                                              : createPhdrs(part);
      if (config->emachine == EM_ARM) {
        // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
        addPhdrForSection(part, SHT_ARM_EXIDX, PT_ARM_EXIDX, PF_R);
      }
      ...
    }
  ...

Update based on scripts:

// lld/ELF/ScriptParser.cpp
void ScriptParser::readPhdrs() {
  expect("{");

  while (!errorCount() && !consume("}")) {
    PhdrsCommand cmd;
    cmd.name = next();
    cmd.type = readPhdrType();

    while (!errorCount() && !consume(";")) {
      if (consume("FILEHDR"))
        cmd.hasFilehdr = true;
      else if (consume("PHDRS"))
        cmd.hasPhdrs = true;
      else if (consume("AT"))
        cmd.lmaExpr = readParenExpr();
      else if (consume("FLAGS"))
        cmd.flags = readParenExpr()().getValue();
      else
        setError("unexpected header attribute: " + next());
    }

    script->phdrsCommands.push_back(cmd);
  }
}

Update by linker itself:

Used here:

  • To adjust sections after sorting

    // lld/ELF/LinkerScript.cpp
    
    void LinkerScript::adjustSectionsAfterSorting() {
    // Try and find an appropriate memory region to assign offsets in.
    for (BaseCommand *base : sectionCommands) {
    if (auto *sec = dyn_cast<OutputSection>(base)) {
      if (!sec->lmaRegionName.empty()) {
        if (MemoryRegion *m = memoryRegions.lookup(sec->lmaRegionName))
          sec->lmaRegion = m;
        else
          error("memory region '" + sec->lmaRegionName + "' not declared");
      }
      sec->memRegion = findMemoryRegion(sec);
    }
    }
    
    // If output section command doesn't specify any segments,
    // and we haven't previously assigned any section to segment,
    // then we simply assign section to the very first load segment.
    // Below is an example of such linker script:
    // PHDRS { seg PT_LOAD; }
    // SECTIONS { .aaa : { *(.aaa) } }
    std::vector<StringRef> defPhdrs;
    auto firstPtLoad = llvm::find_if(phdrsCommands, [](const PhdrsCommand &cmd) {
    return cmd.type == PT_LOAD;
    });
    if (firstPtLoad != phdrsCommands.end())
    defPhdrs.push_back(firstPtLoad->name);
    
    // Walk the commands and propagate the program headers to commands that don't
    // explicitly specify them.
    for (BaseCommand *base : sectionCommands) {
    auto *sec = dyn_cast<OutputSection>(base);
    if (!sec)
      continue;
    
    if (sec->phdrs.empty()) {
      // To match the bfd linker script behaviour, only propagate program
      // headers to sections that are allocated.
      if (sec->flags & SHF_ALLOC)
        sec->phdrs = defPhdrs;
    } else {
      defPhdrs = sec->phdrs;
    }
    }
    }
    
  • To allocate headers???

    // lld/ELF/LinkerScrpt.cpp
    
    // When the SECTIONS command is used, try to find an address for the file and
    // program headers output sections, which can be added to the first PT_LOAD
    // segment when program headers are created.
    //
    // We check if the headers fit below the first allocated section. If there isn't
    // enough space for these sections, we'll remove them from the PT_LOAD segment,
    // and we'll also remove the PT_PHDR segment.
    void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
    uint64_t min = std::numeric_limits<uint64_t>::max();
    for (OutputSection *sec : outputSections)
    if (sec->flags & SHF_ALLOC)
      min = std::min<uint64_t>(min, sec->addr);
    
    auto it = llvm::find_if(
      phdrs, [](const PhdrEntry *e) { return e->p_type == PT_LOAD; });
    if (it == phdrs.end())
    return;
    PhdrEntry *firstPTLoad = *it;
    
    bool hasExplicitHeaders =
      llvm::any_of(phdrsCommands, [](const PhdrsCommand &cmd) {
        return cmd.hasPhdrs || cmd.hasFilehdr;
      });
    bool paged = !config->omagic && !config->nmagic;
    uint64_t headerSize = getHeaderSize();
    if ((paged || hasExplicitHeaders) &&
      headerSize <= min - computeBase(min, hasExplicitHeaders)) {
    min = alignDown(min - headerSize, config->maxPageSize);
    Out::elfHeader->addr = min;
    Out::programHeaders->addr = min + Out::elfHeader->size;
    return;
    }
    
    // Error if we were explicitly asked to allocate headers.
    if (hasExplicitHeaders)
    error("could not allocate headers");
    
    Out::elfHeader->ptLoad = nullptr;
    Out::programHeaders->ptLoad = nullptr;
    firstPTLoad->firstSec = findFirstSection(firstPTLoad);
    
    llvm::erase_if(phdrs,
                 [](const PhdrEntry *e) { return e->p_type == PT_PHDR; });
    }
    
  • To create program header entries:

    // lld/ELF/LinkerScript.cpp
    
    // Creates program headers as instructed by PHDRS linker script command.
    std::vector<PhdrEntry *> LinkerScript::createPhdrs() {
    std::vector<PhdrEntry *> ret;
    
    // Process PHDRS and FILEHDR keywords because they are not
    // real output sections and cannot be added in the following loop.
    for (const PhdrsCommand &cmd : phdrsCommands) {
    PhdrEntry *phdr = make<PhdrEntry>(cmd.type, cmd.flags ? *cmd.flags : PF_R);
    
    if (cmd.hasFilehdr)
      phdr->add(Out::elfHeader);
    if (cmd.hasPhdrs)
      phdr->add(Out::programHeaders);
    
    if (cmd.lmaExpr) {
      phdr->p_paddr = cmd.lmaExpr().getValue();
      phdr->hasLMA = true;
    }
    ret.push_back(phdr);
    }
    
    // Add output sections to program headers.
    for (OutputSection *sec : outputSections) {
    // Assign headers specified by linker script
    for (size_t id : getPhdrIndices(sec)) {
      ret[id]->add(sec);
      if (!phdrsCommands[id].flags.hasValue())
        ret[id]->p_flags |= sec->getPhdrFlags();
    }
    }
    return ret;
    }
    
  • To check whether have .interp section

    // lld/ELF/LinkerScript.cpp
    
    // Returns true if we should emit an .interp section.
    //
    // We usually do. But if PHDRS commands are given, and
    // no PT_INTERP is there, there's no place to emit an
    // .interp, so we don't do that in that case.
    bool LinkerScript::needsInterpSection() {
    if (phdrsCommands.empty())
    return true;
    for (PhdrsCommand &cmd : phdrsCommands)
    if (cmd.type == PT_INTERP)
      return true;
    return false;
    }
    
  • To get the indices of ELF headers containing specific section:

    // lld/ELF/LinkerScript.cpp
    
    // Returns indices of ELF headers containing specific section. Each index is a
    // zero based number of ELF header listed within PHDRS {} script block.
    std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
    std::vector<size_t> ret;
    
    for (StringRef s : cmd->phdrs) {
    if (Optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
      ret.push_back(*idx);
    else if (s != "NONE")
      error(cmd->location + ": program header '" + s +
            "' is not listed in PHDRS");
    }
    return ret;
    }
    
    // 
    
    // Returns the index of the segment named Name.
    static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> vec,
                                     StringRef name) {
    for (size_t i = 0; i < vec.size(); ++i)
    if (vec[i].name == name)
      return i;
    return None;
    }
    
  • To check whether the phdr command exists

    // lld/ELF/LinkerScript.h
    // class LinkerScript
    bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
    
Created Aug 3, 2020 // Last Updated May 18, 2021

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?