Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -28,6 +28,7 @@ struct SectionPiece; template class DefinedRegular; +template class MergeSyntheticSection; template class ObjectFile; template class OutputSection; class OutputSectionBase; @@ -130,6 +131,8 @@ // Returns the size of this section (even if this is a common or BSS.) size_t getSize() const; + OutputSectionBase *getOutputSection() const; + ObjectFile *getFile() const { return File; } llvm::object::ELFFile getObj() const { return File->getObj(); } uintX_t getOffset(const DefinedRegular &Sym) const; @@ -206,6 +209,11 @@ SectionPiece *getSectionPiece(uintX_t Offset); const SectionPiece *getSectionPiece(uintX_t Offset) const; + // MergeInputSections are aggregated to a synthetic input sections, + // and then added to an OutputSection. This pointer points to a + // synthetic MergeSyntheticSection which this section belongs to. + MergeSyntheticSection *MergeSec = nullptr; + private: void splitStrings(ArrayRef A, size_t Size); void splitNonStrings(ArrayRef A, size_t Size); Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -80,7 +80,8 @@ // If it is not a mergeable section, overwrite the flag so that the flag // is consistent with the class. This inconsistency could occur when // string merging is disabled using -O0 flag. - if (!Config->Relocatable && !isa>(this)) + if (!Config->Relocatable && !isa>(this) && + !isa>(this)) this->Flags &= ~(SHF_MERGE | SHF_STRINGS); } @@ -122,11 +123,19 @@ // identify the start of the output .eh_frame. return Offset; case Merge: - return cast>(this)->getOffset(Offset); + const MergeInputSection *MS = cast>(this); + return (MS->MergeSec ? MS->MergeSec->OutSecOff : 0 ) + MS->getOffset(Offset); } llvm_unreachable("invalid section kind"); } +template +OutputSectionBase *InputSectionBase::getOutputSection() const { + if (auto *MS = dyn_cast>(this)) + return MS->MergeSec ? MS->MergeSec->OutSec : nullptr; + return OutSec; +} + // Uncompress section contents. Note that this function is called // from parallel_for_each, so it must be thread-safe. template void InputSectionBase::uncompress() { Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -129,30 +129,6 @@ uint8_t *Loc = nullptr; }; -template -class MergeOutputSection final : public OutputSectionBase { - typedef typename ELFT::uint uintX_t; - -public: - MergeOutputSection(StringRef Name, uint32_t Type, uintX_t Flags, - uintX_t Alignment); - void addSection(InputSectionData *S) override; - void writeTo(uint8_t *Buf) override; - void finalize() override; - bool shouldTailMerge() const; - Kind getKind() const override { return Merge; } - static bool classof(const OutputSectionBase *B) { - return B->getKind() == Merge; - } - -private: - void finalizeTailMerge(); - void finalizeNoTailMerge(); - - llvm::StringTableBuilder Builder; - std::vector *> Sections; -}; - struct CieRecord { EhSectionPiece *Piece = nullptr; std::vector FdePieces; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -479,71 +479,6 @@ } template -MergeOutputSection::MergeOutputSection(StringRef Name, uint32_t Type, - uintX_t Flags, uintX_t Alignment) - : OutputSectionBase(Name, Type, Flags), - Builder(StringTableBuilder::RAW, Alignment) {} - -template void MergeOutputSection::writeTo(uint8_t *Buf) { - Builder.write(Buf); -} - -template -void MergeOutputSection::addSection(InputSectionData *C) { - auto *Sec = cast>(C); - Sec->OutSec = this; - this->updateAlignment(Sec->Alignment); - this->Entsize = Sec->Entsize; - Sections.push_back(Sec); -} - -template bool MergeOutputSection::shouldTailMerge() const { - return (this->Flags & SHF_STRINGS) && Config->Optimize >= 2; -} - -template void MergeOutputSection::finalizeTailMerge() { - // Add all string pieces to the string table builder to create section - // contents. - for (MergeInputSection *Sec : Sections) - for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) - if (Sec->Pieces[I].Live) - Builder.add(Sec->getData(I)); - - // Fix the string table content. After this, the contents will never change. - Builder.finalize(); - this->Size = Builder.getSize(); - - // finalize() fixed tail-optimized strings, so we can now get - // offsets of strings. Get an offset for each string and save it - // to a corresponding StringPiece for easy access. - for (MergeInputSection *Sec : Sections) - for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) - if (Sec->Pieces[I].Live) - Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I)); -} - -template void MergeOutputSection::finalizeNoTailMerge() { - // Add all string pieces to the string table builder to create section - // contents. Because we are not tail-optimizing, offsets of strings are - // fixed when they are added to the builder (string table builder contains - // a hash table from strings to offsets). - for (MergeInputSection *Sec : Sections) - for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) - if (Sec->Pieces[I].Live) - Sec->Pieces[I].OutputOff = Builder.add(Sec->getData(I)); - - Builder.finalizeInOrder(); - this->Size = Builder.getSize(); -} - -template void MergeOutputSection::finalize() { - if (shouldTailMerge()) - finalizeTailMerge(); - else - finalizeNoTailMerge(); -} - -template static typename ELFT::uint getOutFlags(InputSectionBase *S) { return S->Flags & ~SHF_GROUP & ~SHF_COMPRESSED; } @@ -602,24 +537,15 @@ // // Given the above issues, we instead merge sections by name and error on // incompatible types and flags. - // - // The exception being SHF_MERGE, where we create different output sections - // for each alignment. This makes each output section simple. In case of - // relocatable object generation we do not try to perform merging and treat - // SHF_MERGE sections as regular ones, but also create different output - // sections for them to allow merging at final linking stage. - // - // Fortunately, creating symbols in the middle of a merge section is not - // supported by bfd or gold, so the SHF_MERGE exception should not cause - // problems with most linker scripts. typedef typename ELFT::uint uintX_t; - uintX_t Flags = C->Flags & (SHF_MERGE | SHF_STRINGS); uintX_t Alignment = 0; - if (isa>(C) || - (Config->Relocatable && (C->Flags & SHF_MERGE))) + uintX_t Flags = 0; + if (Config->Relocatable && (C->Flags & SHF_MERGE)) { Alignment = std::max(C->Alignment, C->Entsize); + Flags = C->Flags & (SHF_MERGE | SHF_STRINGS); + } return SectionKey{OutsecName, Flags, Alignment}; } @@ -663,17 +589,11 @@ } uint32_t Type = C->Type; - switch (C->kind()) { - case InputSectionBase::Regular: - case InputSectionBase::Synthetic: - Sec = make>(Key.Name, Type, Flags); - break; - case InputSectionBase::EHFrame: + if (C->kind() == InputSectionBase::EHFrame) return {Out::EhFrame, false}; - case InputSectionBase::Merge: - Sec = make>(Key.Name, Type, Flags, Key.Alignment); - break; - } + Sec = make>(Key.Name, Type, Flags); + if (Flags & SHF_MERGE) + Sec->Entsize = C->Entsize; return {Sec, true}; } @@ -713,11 +633,6 @@ template class EhOutputSection; template class EhOutputSection; -template class MergeOutputSection; -template class MergeOutputSection; -template class MergeOutputSection; -template class MergeOutputSection; - template class OutputSectionFactory; template class OutputSectionFactory; template class OutputSectionFactory; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -63,7 +63,8 @@ Offset += Addend; Addend = 0; } - uintX_t VA = (IS->OutSec ? IS->OutSec->Addr : 0) + IS->getOffset(Offset); + const OutputSectionBase *OutSec = IS->getOutputSection(); + uintX_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset); if (D.isTls() && !Config->Relocatable) { if (!Out::TlsPhdr) fatal(toString(D.File) + Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -39,7 +39,7 @@ public: SyntheticSection(uintX_t Flags, uint32_t Type, uintX_t Addralign, StringRef Name) - : InputSection(Flags, Type, Addralign, ArrayRef(), Name, + : InputSection(Flags, Type, Addralign, {}, Name, InputSectionData::Synthetic) { this->Live = true; } @@ -629,6 +629,32 @@ bool empty() const override; }; +// MergeSyntheticSection is a class that allows us to put mergeable sections +// with different attributes in a single output sections. To do that +// we put them into MergeSyntheticSection synthetic input sections which are +// attached to regular output sections. +template +class MergeSyntheticSection final : public SyntheticSection { + typedef typename ELFT::uint uintX_t; + +public: + MergeSyntheticSection(StringRef Name, uint32_t Type, uintX_t Flags, + uintX_t Alignment); + void addSection(MergeInputSection *MS); + void writeTo(uint8_t *Buf) override; + void finalize() override; + bool shouldTailMerge() const; + size_t getSize() const override; + +private: + void finalizeTailMerge(); + void finalizeNoTailMerge(); + + bool Finalized = false; + llvm::StringTableBuilder Builder; + std::vector *> Sections; +}; + // .MIPS.abiflags section. template class MipsAbiFlagsSection final : public SyntheticSection { Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1152,7 +1152,7 @@ ESym->st_shndx = SHN_ABS; ESym->st_value = Body.Value; } else { - const OutputSectionBase *OutSec = Section->OutSec; + const OutputSectionBase *OutSec = Section->getOutputSection(); ESym->st_shndx = OutSec->SectionIndex; ESym->st_value = OutSec->Addr + Section->getOffset(Body); } @@ -1218,7 +1218,7 @@ case SymbolBody::DefinedRegularKind: { auto &D = cast>(*Sym); if (D.Section) - return D.Section->OutSec; + return D.Section->getOutputSection(); break; } case SymbolBody::DefinedCommonKind: @@ -1872,6 +1872,81 @@ } template +MergeSyntheticSection::MergeSyntheticSection(StringRef Name, + uint32_t Type, uintX_t Flags, + uintX_t Alignment) + : SyntheticSection(Flags, Type, Alignment, Name), + Builder(StringTableBuilder::RAW, Alignment) { + this->Entsize = Alignment; +} + +template +void MergeSyntheticSection::addSection(MergeInputSection *MS) { + assert(!Finalized); + MS->MergeSec = this; + Sections.push_back(MS); + this->Entsize = MS->Entsize; +} + +template void MergeSyntheticSection::writeTo(uint8_t *Buf) { + Builder.write(Buf); +} + +template +bool MergeSyntheticSection::shouldTailMerge() const { + return (this->Flags & SHF_STRINGS) && Config->Optimize >= 2; +} + +template void MergeSyntheticSection::finalizeTailMerge() { + // Add all string pieces to the string table builder to create section + // contents. + for (MergeInputSection *Sec : Sections) + for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) + if (Sec->Pieces[I].Live) + Builder.add(Sec->getData(I)); + + // Fix the string table content. After this, the contents will never change. + Builder.finalize(); + + // finalize() fixed tail-optimized strings, so we can now get + // offsets of strings. Get an offset for each string and save it + // to a corresponding StringPiece for easy access. + for (MergeInputSection *Sec : Sections) + for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) + if (Sec->Pieces[I].Live) + Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I)); +} + +template void MergeSyntheticSection::finalizeNoTailMerge() { + // Add all string pieces to the string table builder to create section + // contents. Because we are not tail-optimizing, offsets of strings are + // fixed when they are added to the builder (string table builder contains + // a hash table from strings to offsets). + for (MergeInputSection *Sec : Sections) + for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) + if (Sec->Pieces[I].Live) + Sec->Pieces[I].OutputOff = Builder.add(Sec->getData(I)); + + Builder.finalizeInOrder(); +} + +template void MergeSyntheticSection::finalize() { + if (Finalized) + return; + Finalized = true; + if (shouldTailMerge()) + finalizeTailMerge(); + else + finalizeNoTailMerge(); +} + +template size_t MergeSyntheticSection::getSize() const { + // We should finalize string builder to know the size. + const_cast *>(this)->finalize(); + return Builder.getSize(); +} + +template MipsRldMapSection::MipsRldMapSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, sizeof(typename ELFT::uint), ".rld_map") {} @@ -2037,6 +2112,11 @@ template class elf::VersionDefinitionSection; template class elf::VersionDefinitionSection; +template class elf::MergeSyntheticSection; +template class elf::MergeSyntheticSection; +template class elf::MergeSyntheticSection; +template class elf::MergeSyntheticSection; + template class elf::MipsRldMapSection; template class elf::MipsRldMapSection; template class elf::MipsRldMapSection; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -148,11 +148,58 @@ Phdrs.erase(I, Phdrs.end()); } +template +static typename ELFT::uint getOutFlags(InputSectionBase *S) { + return S->Flags & ~SHF_GROUP & ~SHF_COMPRESSED; +} + +// This function scans over V and creates mergeable synthetic sections. It +// removes MergeInputSections from array and adds new synthetic ones to the end. +template static void combineMergableSections() { + typedef typename ELFT::uint uintX_t; + + std::vector *> MergeSections; + for (InputSectionBase *&S : Symtab::X->Sections) { + MergeInputSection *MS = dyn_cast>(S); + if (!MS) + continue; + + // We do not want to handle sections that are not alive, so just remove + // them instead of trying to merge. + if (!MS->Live) + continue; + + StringRef OutsecName = getOutputSectionName(MS->Name); + uintX_t Flags = getOutFlags(MS); + uintX_t Alignment = std::max(MS->Alignment, MS->Entsize); + + auto I = + llvm::find_if(MergeSections, [=](MergeSyntheticSection *Sec) { + return Sec->Name == OutsecName && Sec->Flags == Flags && + Sec->Alignment == Alignment; + }); + if (I == MergeSections.end()) { + MergeSyntheticSection *Syn = make>( + OutsecName, MS->Type, Flags, Alignment); + MergeSections.push_back(Syn); + I = std::prev(MergeSections.end()); + S = Syn; + } else { + S = nullptr; + } + (*I)->addSection(MS); + } + + std::vector *> &V = Symtab::X->Sections; + V.erase(std::remove(V.begin(), V.end(), nullptr), V.end()); +} + // The main function of the writer. template void Writer::run() { // Create linker-synthesized sections such as .got or .plt. // Such sections are of type input section. createSyntheticSections(); + combineMergableSections(); // We need to create some reserved symbols such as _end. Create them. if (!Config->Relocatable) Index: test/ELF/linkerscript/alternate-sections.s =================================================================== --- test/ELF/linkerscript/alternate-sections.s +++ test/ELF/linkerscript/alternate-sections.s @@ -4,25 +4,27 @@ # RUN: ld.lld -o %t --script %t.script %t.o -shared # RUN: llvm-readobj -s -section-data %t | FileCheck %s -# This test shows an oddity in lld. When a linker script alternates among -# different types of output section in the same command, the sections are -# reordered. -# In this test we go from regular, to merge and back to regular. The reason -# for the reordering is that we need two create two output sections and -# one cannot be in the middle of another. -# If this ever becomes a problem, some options would be: -# * Adding an extra layer in between input section and output sections (Chunk). -# With that this example would have 3 chunks, but only one output section. -# This would unfortunately complicate the non-script case too. -# * Just create three output sections. -# * If having three output sections causes problem, have linkerscript specific -# code to write the section table and section indexes. That way we could -# keep 3 sections internally but not expose that. - -# CHECK: Name: abc -# CHECK: 0000: 01000000 00000000 02000000 00000000 | -# CHECK: Name: abc -# CHECK: 0000: 61626331 323300 |abc123.| +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: abc +# CHECK-NEXT: Type: SHT_PROGBIT +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_MERGE +# CHECK-NEXT: SHF_STRINGS +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: +# CHECK-NEXT: Link: +# CHECK-NEXT: Info: +# CHECK-NEXT: AddressAlignment: +# CHECK-NEXT: EntrySize: +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 01000000 00000000 61626331 32330002 |........abc123..| +# CHECK-NEXT: 0010: 00000000 000000 |.......| +# CHECK-NEXT: ) +# CHECK-NEXT: } .section foo, "a" .quad 1 Index: test/ELF/linkerscript/merge-sections-syms.s =================================================================== --- /dev/null +++ test/ELF/linkerscript/merge-sections-syms.s @@ -0,0 +1,49 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o + +# RUN: echo "SECTIONS { \ +# RUN: . = SIZEOF_HEADERS; \ +# RUN: .rodata : { *(.aaa) *(.bbb) A = .; *(.ccc) B = .; } \ +# RUN: }" > %t.script +# RUN: ld.lld -o %t.so --script %t.script %t.o -shared +# RUN: llvm-readobj --dyn-symbols %t.so | FileCheck %s + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: A +# CHECK-NEXT: Value: 0x195 +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: B +# CHECK-NEXT: Value: 0x196 +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: +# CHECK-NEXT: } +# CHECK-NEXT: ] + + +.section .aaa,"a" +.byte 11 + +.section .bbb,"aMS",@progbits,1 +.asciz "foo" + +.section .ccc,"a" +.byte 33 Index: test/ELF/linkerscript/merge-sections.s =================================================================== --- test/ELF/linkerscript/merge-sections.s +++ test/ELF/linkerscript/merge-sections.s @@ -17,61 +17,19 @@ # CHECK-NEXT: ] # CHECK-NEXT: Address: 0x[[ADDR1:.*]] # CHECK-NEXT: Offset: 0x[[ADDR1]] -# CHECK-NEXT: Size: 4 -# CHECK-NEXT: Link: 0 -# CHECK-NEXT: Info: 0 -# CHECK-NEXT: AddressAlignment: 1 -# CHECK-NEXT: EntrySize: 1 -# CHECK-NEXT: } -# CHECK-NEXT: Section { -# CHECK-NEXT: Index: -# CHECK-NEXT: Name: .foo -# CHECK-NEXT: Type: SHT_PROGBITS -# CHECK-NEXT: Flags [ -# CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: SHF_MERGE -# CHECK-NEXT: ] -# CHECK-NEXT: Address: 0x -# CHECK-NEXT: Offset: 0x -# CHECK-NEXT: Size: 1 -# CHECK-NEXT: Link: 0 -# CHECK-NEXT: Info: 0 -# CHECK-NEXT: AddressAlignment: 1 -# CHECK-NEXT: EntrySize: 1 -# CHECK-NEXT: } -# CHECK-NEXT: Section { -# CHECK-NEXT: Index: -# CHECK-NEXT: Name: .foo -# CHECK-NEXT: Type: SHT_PROGBITS -# CHECK-NEXT: Flags [ -# CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: SHF_MERGE -# CHECK-NEXT: ] -# CHECK-NEXT: Address: 0x -# CHECK-NEXT: Offset: 0x -# CHECK-NEXT: Size: 2 +# CHECK-NEXT: Size: 14 # CHECK-NEXT: Link: 0 # CHECK-NEXT: Info: 0 # CHECK-NEXT: AddressAlignment: 2 -# CHECK-NEXT: EntrySize: 2 +# CHECK-NEXT: EntrySize: 1 # CHECK-NEXT: } -# CHECK-NEXT: Section { -# CHECK-NEXT: Index: -# CHECK-NEXT: Name: -# CHECK-NEXT: Type: -# CHECK-NEXT: Flags [ -# CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: SHF_EXECINSTR -# CHECK-NEXT: ] -# CHECK-NEXT: Address: 0x[[ADDR2:.*]] -# CHECK-NEXT: Offset: 0x[[ADDR2]] - # CHECK: Name: begin # CHECK-NEXT: Value: 0x[[ADDR1]] # CHECK: Name: end -# CHECK-NEXT: Value: 0x[[ADDR2]] +# 0x19E = begin + sizeof(.foo) = 0x190 + 0xE +# CHECK-NEXT: Value: 0x19E .section .foo.1a,"aMS",@progbits,1 .asciz "foo" Index: test/ELF/map-file.s =================================================================== --- test/ELF/map-file.s +++ test/ELF/map-file.s @@ -48,6 +48,7 @@ // CHECK-NEXT: 0000000000202000 0000000000000004 4 .bss // CHECK-NEXT: 0000000000202000 0000000000000004 4 COMMON // CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment +// CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment // CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab // CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab // CHECK-NEXT: 0000000000000000 0000000000000039 1 .shstrtab Index: test/ELF/merge-string-align.s =================================================================== --- test/ELF/merge-string-align.s +++ test/ELF/merge-string-align.s @@ -34,10 +34,10 @@ // CHECK-NEXT: 0010: 62617200 |bar.| // CHECK-NEXT: ) - .section .rodata.str1.1,"aMS",@progbits,1 + .section .rodata2,"aMS",@progbits,1 .asciz "foo" -// CHECK: Name: .rodata +// CHECK: Name: .rodata2 // CHECK-NEXT: Type: SHT_PROGBITS // CHECK-NEXT: Flags [ // CHECK-NEXT: SHF_ALLOC Index: test/ELF/no-merge.s =================================================================== --- test/ELF/no-merge.s +++ test/ELF/no-merge.s @@ -2,18 +2,12 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o # RUN: echo "SECTIONS { .data : {*(.data.*)} }" > %t0.script # RUN: ld.lld %t.o -o %t0.out --script %t0.script -# RUN: llvm-objdump -s %t0.out | FileCheck %s --check-prefix=OPT -# OPT: Contents of section .data: -# OPT-NEXT: 0000 01 -# OPT-NEXT: Contents of section .data: -# OPT-NEXT: 0001 6100 -# OPT-NEXT: Contents of section .data: -# OPT-NEXT: 0003 03 +# RUN: llvm-objdump -s %t0.out | FileCheck %s # RUN: ld.lld -O0 %t.o -o %t1.out --script %t0.script -# RUN: llvm-objdump -s %t1.out | FileCheck %s --check-prefix=NOOPT -# NOOPT: Contents of section .data: -# NOOPT-NEXT: 0000 01610003 +# RUN: llvm-objdump -s %t1.out | FileCheck %s +# CHECK: Contents of section .data: +# CHECK-NEXT: 0000 01610003 .section .data.aw,"aw",@progbits .byte 1