diff options
| -rw-r--r-- | lld/ELF/InputSection.cpp | 3 | ||||
| -rw-r--r-- | lld/ELF/InputSection.h | 2 | ||||
| -rw-r--r-- | lld/ELF/LinkerScript.cpp | 273 | ||||
| -rw-r--r-- | lld/ELF/LinkerScript.h | 4 | ||||
| -rw-r--r-- | lld/ELF/OutputSections.cpp | 2 | ||||
| -rw-r--r-- | lld/ELF/Symbols.cpp | 2 | ||||
| -rw-r--r-- | lld/ELF/Writer.cpp | 27 | ||||
| -rw-r--r-- | lld/test/ELF/linkerscript/linkerscript-symbols-synthetic.s | 38 | 
8 files changed, 256 insertions, 95 deletions
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index a61cf56a7c3..c7d6ac4e703 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -71,6 +71,7 @@ template <class ELFT>  typename ELFT::uint InputSectionBase<ELFT>::getOffset(uintX_t Offset) const {    switch (SectionKind) {    case Regular: +  case Layout:      return cast<InputSection<ELFT>>(this)->OutSecOff + Offset;    case EHFrame:      // The file crtbeginT.o has relocations pointing to the start of an empty @@ -129,7 +130,7 @@ InputSection<ELFT>::InputSection(elf::ObjectFile<ELFT> *F,  template <class ELFT>  bool InputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) { -  return S->SectionKind == Base::Regular; +  return S->SectionKind == Base::Regular || S->SectionKind == Base::Layout;  }  template <class ELFT> diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index ddb9ce0befa..d21d773fda8 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -48,7 +48,7 @@ protected:    SmallVector<char, 0> Uncompressed;  public: -  enum Kind { Regular, EHFrame, Merge, MipsReginfo, MipsOptions }; +  enum Kind { Regular, EHFrame, Merge, MipsReginfo, MipsOptions, Layout };    Kind SectionKind;    InputSectionBase() : Repl(this) {} diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 2d64af33b03..ab2ce70840e 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -43,6 +43,36 @@ using namespace lld::elf;  ScriptConfiguration *elf::ScriptConfig; +template <class ELFT> +static Symbol *addSymbolToSymtabAux(StringRef Name, uint8_t StOther) { +  return Symtab<ELFT>::X->addRegular(Name, STB_GLOBAL, StOther); +} + +template <class ELFT> +static Symbol *addSymbolToSymtabAux(StringRef Name, typename ELFT::uint Value, +                                    OutputSectionBase<ELFT> *Section) { +  return Symtab<ELFT>::X->addSynthetic(Name, Section, Value); +} + +template <class ELFT, class... ArgsT> +static bool addSymbolToSymtab(SymbolAssignment &Cmd, ArgsT... Args) { +  if (Cmd.Name == ".") +    return false; + +  // If a symbol was in PROVIDE(), define it only when it is an +  // undefined symbol. +  SymbolBody *B = Symtab<ELFT>::X->find(Cmd.Name); +  if (Cmd.Provide && !(B && B->isUndefined())) +    return false; + +  Symbol *Sym = +      addSymbolToSymtabAux<ELFT>(Cmd.Name, std::forward<ArgsT>(Args)...); + +  Sym->Visibility = Cmd.Hidden ? STV_HIDDEN : STV_DEFAULT; +  Cmd.Sym = Sym->body(); +  return true; +} +  bool SymbolAssignment::classof(const BaseCommand *C) {    return C->Kind == AssignmentKind;  } @@ -78,21 +108,6 @@ static bool match(ArrayRef<StringRef> Patterns, StringRef S) {    return false;  } -// Create a vector of (<output section name>, <input section description>). -template <class ELFT> -std::vector<std::pair<StringRef, const InputSectionDescription *>> -LinkerScript<ELFT>::getSectionMap() { -  std::vector<std::pair<StringRef, const InputSectionDescription *>> Ret; - -  for (const std::unique_ptr<BaseCommand> &Base1 : Opt.Commands) -    if (auto *Cmd1 = dyn_cast<OutputSectionCommand>(Base1.get())) -      for (const std::unique_ptr<BaseCommand> &Base2 : Cmd1->Commands) -        if (auto *Cmd2 = dyn_cast<InputSectionDescription>(Base2.get())) -          Ret.emplace_back(Cmd1->Name, Cmd2); - -  return Ret; -} -  static bool fileMatches(const InputSectionDescription *Desc,                          StringRef Filename) {    if (!globMatch(Desc->FilePattern, Filename)) @@ -121,18 +136,101 @@ LinkerScript<ELFT>::getInputSections(const InputSectionDescription *I) {    return Ret;  } -// Add input section to output section. If there is no output section yet, -// then create it and add to output section list. +namespace { +template <class ELFT> class LayoutInputSection : public InputSection<ELFT> { +public: +  LayoutInputSection(SymbolAssignment *Cmd); +  static bool classof(const InputSectionBase<ELFT> *S); +  SymbolAssignment *Cmd; + +private: +  typename ELFT::Shdr Hdr; +}; + +// Helper class, which builds output section list, also +// creating symbol sections, when needed +template <class ELFT> class OutputSectionBuilder { +public: +  OutputSectionBuilder(OutputSectionFactory<ELFT> &F, +                       std::vector<OutputSectionBase<ELFT> *> *Out) +      : Factory(F), OutputSections(Out) {} + +  void addSection(StringRef OutputName, InputSectionBase<ELFT> *I); +  void addSymbol(SymbolAssignment *Cmd) { +    PendingSymbols.emplace_back(new LayoutInputSection<ELFT>(Cmd)); +  } +  void flushSymbols(); +  void flushSection(); +  void finalize(); + +private: +  OutputSectionFactory<ELFT> &Factory; +  std::vector<OutputSectionBase<ELFT> *> *OutputSections; +  OutputSectionBase<ELFT> *Current = nullptr; +  std::vector<std::unique_ptr<LayoutInputSection<ELFT>>> PendingSymbols; +  static std::vector<std::unique_ptr<LayoutInputSection<ELFT>>> OwningSections; +}; + +template <class ELFT> +std::vector<std::unique_ptr<LayoutInputSection<ELFT>>> +    OutputSectionBuilder<ELFT>::OwningSections; +} + +template <class T> static T *zero(T *Val) { +  memset(Val, 0, sizeof(*Val)); +  return Val; +} +  template <class ELFT> -static void addSection(OutputSectionFactory<ELFT> &Factory, -                       std::vector<OutputSectionBase<ELFT> *> &Out, -                       InputSectionBase<ELFT> *C, StringRef Name) { -  OutputSectionBase<ELFT> *Sec; +LayoutInputSection<ELFT>::LayoutInputSection(SymbolAssignment *Cmd) +    : InputSection<ELFT>(nullptr, zero(&Hdr)), Cmd(Cmd) { +  this->Live = true; +  this->SectionKind = InputSectionBase<ELFT>::Layout; +  Hdr.sh_type = SHT_NOBITS; +} + +template <class ELFT> +bool LayoutInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) { +  return S->SectionKind == InputSectionBase<ELFT>::Layout; +} + +template <class ELFT> +void OutputSectionBuilder<ELFT>::addSection(StringRef OutputName, +                                            InputSectionBase<ELFT> *C) {    bool IsNew; -  std::tie(Sec, IsNew) = Factory.create(C, Name); +  std::tie(Current, IsNew) = Factory.create(C, OutputName);    if (IsNew) -    Out.push_back(Sec); -  Sec->addSection(C); +    OutputSections->push_back(Current); +  flushSymbols(); +  Current->addSection(C); +} + +template <class ELFT> void OutputSectionBuilder<ELFT>::flushSymbols() { +  for (std::unique_ptr<LayoutInputSection<ELFT>> &I : PendingSymbols) +    if (I->Cmd->Name == "." || addSymbolToSymtab<ELFT>(*I->Cmd, 0, Current)) { +      // Only regular output sections are supported. +      if (dyn_cast_or_null<OutputSection<ELFT>>(Current)) { +        Current->addSection(I.get()); +        OwningSections.push_back(std::move(I)); +      } +    } + +  PendingSymbols.clear(); +} + +template <class ELFT> void OutputSectionBuilder<ELFT>::flushSection() { +  flushSymbols(); +  Current = nullptr; +} + +template <class ELFT> void OutputSectionBuilder<ELFT>::finalize() { +  // Assign offsets to all sections which don't contain symbols +  for (OutputSectionBase<ELFT> *S : *OutputSections) +    if (llvm::find_if(OwningSections, +                      [&](std::unique_ptr<LayoutInputSection<ELFT>> &L) { +                        return L->OutSec == S; +                      }) == OwningSections.end()) +      S->assignOffsets();  }  template <class ELFT> @@ -160,40 +258,51 @@ getComparator(SortKind K) {  template <class ELFT>  void LinkerScript<ELFT>::createSections(      OutputSectionFactory<ELFT> &Factory) { -  for (auto &P : getSectionMap()) { -    StringRef OutputName = P.first; -    const InputSectionDescription *Cmd = P.second; -    std::vector<InputSectionBase<ELFT> *> Sections = getInputSections(Cmd); +  OutputSectionBuilder<ELFT> Builder(Factory, OutputSections); +  auto Add = [&](StringRef OutputName, const InputSectionDescription *Cmd) { +    std::vector<InputSectionBase<ELFT> *> Sections = getInputSections(Cmd);      if (OutputName == "/DISCARD/") {        for (InputSectionBase<ELFT> *S : Sections) {          S->Live = false;          reportDiscarded(S);        } -      continue; +      return;      } -      if (Cmd->SortInner)        std::stable_sort(Sections.begin(), Sections.end(),                         getComparator<ELFT>(Cmd->SortInner));      if (Cmd->SortOuter)        std::stable_sort(Sections.begin(), Sections.end(),                         getComparator<ELFT>(Cmd->SortOuter)); -      for (InputSectionBase<ELFT> *S : Sections) -      addSection(Factory, *OutputSections, S, OutputName); -  } +      Builder.addSection(OutputName, S); +  }; + +  for (const std::unique_ptr<BaseCommand> &Base1 : Opt.Commands) +    if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base1.get())) { +      for (const std::unique_ptr<BaseCommand> &Base2 : Cmd->Commands) +        if (auto *Assignment = dyn_cast<SymbolAssignment>(Base2.get())) +          Builder.addSymbol(Assignment); +        else +          Add(Cmd->Name, cast<InputSectionDescription>(Base2.get())); + +      Builder.flushSection(); +    } else if (auto *Cmd2 = dyn_cast<SymbolAssignment>(Base1.get())) { +      addSymbolToSymtab<ELFT>(*Cmd2, STV_DEFAULT); +    }    // Add all other input sections, which are not listed in script.    for (const std::unique_ptr<ObjectFile<ELFT>> &F :         Symtab<ELFT>::X->getObjectFiles())      for (InputSectionBase<ELFT> *S : F->getSections())        if (!isDiscarded(S) && !S->OutSec) -        addSection(Factory, *OutputSections, S, getOutputSectionName(S)); +        Builder.addSection(getOutputSectionName(S), S);    // Remove from the output all the sections which did not meet    // the optional constraints.    filter(); +  Builder.finalize();  }  template <class R, class T> @@ -224,6 +333,35 @@ template <class ELFT> void LinkerScript<ELFT>::filter() {    }  } +template <class ELFT> void assignOffsets(OutputSectionBase<ELFT> *Sec) { +  // Non-zero size means we have assigned offsets earlier in +  // OutputSectionBuilder<ELFT>::finalize +  auto *OutSec = dyn_cast<OutputSection<ELFT>>(Sec); +  if (Sec->getSize() || !OutSec) +    return; + +  typedef typename ELFT::uint uintX_t; +  uintX_t Off = 0; + +  for (InputSection<ELFT> *I : OutSec->Sections) { +    if (auto *L = dyn_cast<LayoutInputSection<ELFT>>(I)) { +      uintX_t Value = L->Cmd->Expression(Sec->getVA() + Off) - Sec->getVA(); +      if (L->Cmd->Name == ".") +        Off = Value; +      else +        cast<DefinedSynthetic<ELFT>>(L->Cmd->Sym)->Value = Value; +    } else { +      Off = alignTo(Off, I->Alignment); +      I->OutSecOff = Off; +      Off += I->getSize(); +    } +    // Update section size inside for-loop, so that SIZEOF  +    // works correctly in the case below: +    // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) } +    Sec->setSize(Off); +  } +} +  template <class ELFT> void LinkerScript<ELFT>::assignAddresses() {    ArrayRef<OutputSectionBase<ELFT> *> Sections = *OutputSections;    // Orphan sections are sections present in the input files which @@ -275,6 +413,7 @@ template <class ELFT> void LinkerScript<ELFT>::assignAddresses() {          uintX_t TVA = Dot + ThreadBssOffset;          TVA = alignTo(TVA, Sec->getAlignment());          Sec->setVA(TVA); +        assignOffsets(Sec);          ThreadBssOffset = TVA - Dot + Sec->getSize();          continue;        } @@ -282,6 +421,7 @@ template <class ELFT> void LinkerScript<ELFT>::assignAddresses() {        if (Sec->getFlags() & SHF_ALLOC) {          Dot = alignTo(Dot, Sec->getAlignment());          Sec->setVA(Dot); +        assignOffsets(Sec);          MinVA = std::min(MinVA, Dot);          Dot += Sec->getSize();          continue; @@ -395,28 +535,6 @@ int LinkerScript<ELFT>::compareSections(StringRef A, StringRef B) {    return I < J ? -1 : 1;  } -// Add symbols defined by linker scripts. -template <class ELFT> void LinkerScript<ELFT>::addScriptedSymbols() { -  for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands) { -    auto *Cmd = dyn_cast<SymbolAssignment>(Base.get()); -    if (!Cmd || Cmd->Name == ".") -      continue; - -    // If a symbol was in PROVIDE(), define it only when it is an -    // undefined symbol. -    SymbolBody *B = Symtab<ELFT>::X->find(Cmd->Name); -    if (Cmd->Provide && !(B && B->isUndefined())) -      continue; - -    // Define an absolute symbol. The symbol value will be assigned later. -    // (At this point, we don't know the final address yet.) -    Symbol *Sym = Symtab<ELFT>::X->addUndefined(Cmd->Name); -    replaceBody<DefinedRegular<ELFT>>(Sym, Cmd->Name, STV_DEFAULT); -    Sym->Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT; -    Cmd->Sym = Sym->body(); -  } -} -  template <class ELFT> bool LinkerScript<ELFT>::hasPhdrsCommands() {    return !Opt.PhdrsCommands.empty();  } @@ -499,6 +617,7 @@ private:    unsigned readPhdrType();    SortKind readSortKind();    SymbolAssignment *readProvide(bool Hidden); +  SymbolAssignment *readProvideOrAssignment(StringRef Tok);    Expr readAlign();    void readSort();    Expr readAssert(); @@ -688,18 +807,12 @@ void ScriptParser::readSections() {    expect("{");    while (!Error && !skip("}")) {      StringRef Tok = next(); -    BaseCommand *Cmd; -    if (peek() == "=" || peek() == "+=") { -      Cmd = readAssignment(Tok); -      expect(";"); -    } else if (Tok == "PROVIDE") { -      Cmd = readProvide(false); -    } else if (Tok == "PROVIDE_HIDDEN") { -      Cmd = readProvide(true); -    } else if (Tok == "ASSERT") { -      Cmd = new AssertCommand(readAssert()); -    } else { -      Cmd = readOutputSectionDescription(Tok); +    BaseCommand *Cmd = readProvideOrAssignment(Tok); +    if (!Cmd) { +      if (Tok == "ASSERT") +        Cmd = new AssertCommand(readAssert()); +      else +        Cmd = readOutputSectionDescription(Tok);      }      Opt.Commands.emplace_back(Cmd);    } @@ -836,11 +949,14 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {        Cmd->Commands.emplace_back(readInputSectionDescription());        continue;      } -    if (skip("SORT")) { + +    StringRef Tok = next(); +    if (SymbolAssignment *Assignment = readProvideOrAssignment(Tok)) +      Cmd->Commands.emplace_back(Assignment); +    else if (Tok == "SORT")        readSort(); -      continue; -    } -    setError("unknown command " + peek()); +    else +      setError("unknown command " + Tok);    }    Cmd->Phdrs = readOutputSectionPhdrs();    Cmd->Filler = readOutputSectionFiller(); @@ -877,6 +993,19 @@ SymbolAssignment *ScriptParser::readProvide(bool Hidden) {    return Cmd;  } +SymbolAssignment *ScriptParser::readProvideOrAssignment(StringRef Tok) { +  SymbolAssignment *Cmd = nullptr; +  if (peek() == "=" || peek() == "+=") { +    Cmd = readAssignment(Tok); +    expect(";"); +  } else if (Tok == "PROVIDE") { +    Cmd = readProvide(false); +  } else if (Tok == "PROVIDE_HIDDEN") { +    Cmd = readProvide(true); +  } +  return Cmd; +} +  static uint64_t getSymbolValue(StringRef S, uint64_t Dot) {    if (S == ".")      return Dot; diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index 324b2702cbd..c394224c2c4 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -146,7 +146,6 @@ public:    bool shouldKeep(InputSectionBase<ELFT> *S);    void assignAddresses();    int compareSections(StringRef A, StringRef B); -  void addScriptedSymbols();    bool hasPhdrsCommands();    uintX_t getOutputSectionSize(StringRef Name);    uintX_t getSizeOfHeaders(); @@ -154,9 +153,6 @@ public:    std::vector<OutputSectionBase<ELFT> *> *OutputSections;  private: -  std::vector<std::pair<StringRef, const InputSectionDescription *>> -  getSectionMap(); -    std::vector<InputSectionBase<ELFT> *>    getInputSections(const InputSectionDescription *); diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 7ea281c22a0..3485737a4ce 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -1762,6 +1762,8 @@ OutputSectionFactory<ELFT>::create(InputSectionBase<ELFT> *C,    case InputSectionBase<ELFT>::MipsOptions:      Sec = new MipsOptionsOutputSection<ELFT>();      break; +  case InputSectionBase<ELFT>::Layout: +    llvm_unreachable("Invalid section type");    }    OwningSections.emplace_back(Sec);    return {Sec, true}; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index b1056f8d508..0d63281580f 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -58,7 +58,7 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,        Offset += Addend;        Addend = 0;      } -    uintX_t VA = SC->OutSec->getVA() + SC->getOffset(Offset); +    uintX_t VA = (SC->OutSec ? SC->OutSec->getVA() : 0) + SC->getOffset(Offset);      if (D.isTls())        return VA - Out<ELFT>::TlsPhdr->p_vaddr;      return VA; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 2f421275d29..ac716c5ec6b 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -238,6 +238,9 @@ template <class ELFT> void Writer<ELFT>::run() {      copyLocalSymbols();    addReservedSymbols(); +  if (Target->NeedsThunks) +    forEachRelSec(createThunks<ELFT>); +    CommonInputSection<ELFT> Common(getCommonSymbols<ELFT>());    CommonInputSection<ELFT>::X = &Common; @@ -659,6 +662,14 @@ template <class ELFT> void Writer<ELFT>::createSections() {        Sec->addSection(C);      }    } + +  sortInitFini(findSection(".init_array")); +  sortInitFini(findSection(".fini_array")); +  sortCtorsDtors(findSection(".ctors")); +  sortCtorsDtors(findSection(".dtors")); + +  for (OutputSectionBase<ELFT> *Sec : OutputSections) +    Sec->assignOffsets();  }  // Create output section objects and add them to OutputSections. @@ -667,12 +678,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {    Out<ELFT>::InitArray = findSection(".init_array");    Out<ELFT>::FiniArray = findSection(".fini_array"); -  // Sort section contents for __attribute__((init_priority(N)). -  sortInitFini(Out<ELFT>::InitArray); -  sortInitFini(Out<ELFT>::FiniArray); -  sortCtorsDtors(findSection(".ctors")); -  sortCtorsDtors(findSection(".dtors")); -    // The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop    // symbols for sections, so that the runtime can get the start and end    // addresses of each section by section name. Add such symbols. @@ -692,21 +697,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {    // Define __rel[a]_iplt_{start,end} symbols if needed.    addRelIpltSymbols(); -  // Add scripted symbols with zero values now. -  // Real values will be assigned later -  Script<ELFT>::X->addScriptedSymbols(); -    if (!Out<ELFT>::EhFrame->empty()) {      OutputSections.push_back(Out<ELFT>::EhFrame);      Out<ELFT>::EhFrame->finalize();    } -  if (Target->NeedsThunks) -    forEachRelSec(createThunks<ELFT>); - -  for (OutputSectionBase<ELFT> *Sec : OutputSections) -    Sec->assignOffsets(); -    // Scan relocations. This must be done after every symbol is declared so that    // we can correctly decide if a dynamic relocation is needed.    forEachRelSec(scanRelocations<ELFT>); diff --git a/lld/test/ELF/linkerscript/linkerscript-symbols-synthetic.s b/lld/test/ELF/linkerscript/linkerscript-symbols-synthetic.s new file mode 100644 index 00000000000..f1d27a7acd7 --- /dev/null +++ b/lld/test/ELF/linkerscript/linkerscript-symbols-synthetic.s @@ -0,0 +1,38 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +# Simple symbol assignment within input section list. The '.' symbol +# is not location counter but offset from the beginning of output +# section .foo +# RUN: echo "SECTIONS { \ +# RUN:          .foo : { \ +# RUN:              begin_foo = .; \ +# RUN:              *(.foo) \ +# RUN:              end_foo = .; \ +# RUN:              size_foo_1 = SIZEOF(.foo); \ +# RUN:              . = ALIGN(0x1000); \ +# RUN:              begin_bar = .; \ +# RUN:              *(.bar) \ +# RUN:              end_bar = .; \ +# RUN:              size_foo_2 = SIZEOF(.foo); \  +# RUN:            } \ +# RUN:            size_foo_3 = SIZEOF(.foo); }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=SIMPLE %s +# SIMPLE:      0000000000000120         .foo    00000000 begin_foo +# SIMPLE-NEXT: 0000000000000128         .foo    00000000 end_foo +# SIMPLE-NEXT: 0000000000000008         .foo    00000000 size_foo_1 +# SIMPLE-NEXT: 0000000000001000         .foo    00000000 begin_bar +# SIMPLE-NEXT: 0000000000001004         .foo    00000000 end_bar +# SIMPLE-NEXT: 0000000000000ee4         .foo    00000000 size_foo_2 +# SIMPLE-NEXT: 0000000000000ee4         *ABS*   00000000 size_foo_3 + +.global _start +_start: + nop + +.section .foo,"a" + .quad 0 + +.section .bar,"a" + .long 0  | 

