Index: llvm/include/llvm/ObjectYAML/yaml2obj.h =================================================================== --- llvm/include/llvm/ObjectYAML/yaml2obj.h +++ llvm/include/llvm/ObjectYAML/yaml2obj.h @@ -47,14 +47,15 @@ using ErrorHandler = llvm::function_ref; bool yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); -bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); +bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH, + uint64_t MaxSize); bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool convertYAML(Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, - unsigned DocNum = 1); + unsigned DocNum = 1, uint64_t MaxSize = UINT64_MAX); /// Convenience function for tests. std::unique_ptr Index: llvm/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -34,30 +34,88 @@ // This class is used to build up a contiguous binary blob while keeping // track of an offset in the output (which notionally begins at // `InitialOffset`). +// The blob might be limited to an arbitrary size. All attempts to write a +// data are ignored and the error condition is remembered when the limit is +// reached. Such an approach allows us to simplify the code by delaying errors +// reporting and doing it at a convenient time. namespace { class ContiguousBlobAccumulator { const uint64_t InitialOffset; + const uint64_t MaxSize; + SmallVector Buf; raw_svector_ostream OS; + bool ReachedLimit; + + bool checkLimit(uint64_t Size) { + if (!ReachedLimit && getOffset() + Size <= MaxSize) + return true; + ReachedLimit = true; + return false; + } public: - ContiguousBlobAccumulator(uint64_t InitialOffset_) - : InitialOffset(InitialOffset_), Buf(), OS(Buf) {} + ContiguousBlobAccumulator(uint64_t BaseOffset, uint64_t SizeLimit) + : InitialOffset(BaseOffset), MaxSize(SizeLimit), OS(Buf), + ReachedLimit(false) {} + uint64_t tell() const { return OS.tell(); } uint64_t getOffset() const { return InitialOffset + OS.tell(); } - raw_ostream &getOS() { return OS; } + void writeBlobToStream(raw_ostream &Out) const { Out << OS.str(); } + bool hasReachedLimit() const { return ReachedLimit; } /// \returns The new offset. uint64_t padToAlignment(unsigned Align) { - if (Align == 0) - Align = 1; uint64_t CurrentOffset = getOffset(); - uint64_t AlignedOffset = alignTo(CurrentOffset, Align); - OS.write_zeros(AlignedOffset - CurrentOffset); - return AlignedOffset; // == CurrentOffset; + if (ReachedLimit) + return CurrentOffset; + + uint64_t AlignedOffset = alignTo(CurrentOffset, Align == 0 ? 1 : Align); + uint64_t PaddingSize = AlignedOffset - CurrentOffset; + if (!checkLimit(PaddingSize)) + return CurrentOffset; + + writeZeros(PaddingSize); + return AlignedOffset; + } + + raw_ostream *getRawOS(uint64_t Size) { + if (checkLimit(Size)) + return &OS; + return nullptr; + } + + void writeAsBinary(const yaml::BinaryRef &Bin, uint64_t N = UINT64_MAX) { + if (!checkLimit(Bin.binary_size())) + return; + Bin.writeAsBinary(OS, N); + } + + void writeZeros(uint64_t Num) { + if (checkLimit(Num)) + OS.write_zeros(Num); + } + + void write(const char *Ptr, size_t Size) { + if (checkLimit(Size)) + OS.write(Ptr, Size); + } + + void write(unsigned char C) { + if (checkLimit(1)) + OS.write(C); + } + + unsigned writeULEB128(uint64_t Val) { + if (!checkLimit(sizeof(uint64_t))) + return 0; + return encodeULEB128(Val, OS); } - void writeBlobToStream(raw_ostream &Out) { Out << OS.str(); } + template void write(T Val, support::endianness E) { + if (checkLimit(sizeof(T))) + support::endian::write(OS, Val, E); + } }; // Used to keep track of section and symbol names, so that in the YAML file @@ -165,7 +223,7 @@ ArrayRef SHeaders); void finalizeStrings(); - void writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream &OS); + void writeELFHeader(raw_ostream &OS, uint64_t SHOff); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, ContiguousBlobAccumulator &CBA); @@ -236,7 +294,7 @@ public: static bool writeELF(raw_ostream &OS, ELFYAML::Object &Doc, - yaml::ErrorHandler EH); + yaml::ErrorHandler EH, uint64_t MaxSize); }; } // end anonymous namespace @@ -305,7 +363,7 @@ } template -void ELFState::writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream &OS) { +void ELFState::writeELFHeader(raw_ostream &OS, uint64_t SHOff) { using namespace llvm::ELF; Elf_Ehdr Header; @@ -331,10 +389,6 @@ Header.e_shentsize = Doc.Header.SHEntSize ? (uint16_t)*Doc.Header.SHEntSize : sizeof(Elf_Shdr); - // Align the start of the section header table, which is written after all - // other sections to the end of the file. - uint64_t SHOff = - alignToOffset(CBA, sizeof(typename ELFT::uint), /*Offset=*/None); if (Doc.Header.SHOff) Header.e_shoff = *Doc.Header.SHOff; @@ -664,19 +718,19 @@ return Symbols.size(); } -static uint64_t writeContent(raw_ostream &OS, +static uint64_t writeContent(ContiguousBlobAccumulator &CBA, const Optional &Content, const Optional &Size) { size_t ContentSize = 0; if (Content) { - Content->writeAsBinary(OS); + CBA.writeAsBinary(*Content); ContentSize = Content->binary_size(); } if (!Size) return ContentSize; - OS.write_zeros(*Size - ContentSize); + CBA.writeZeros(*Size - ContentSize); return *Size; } @@ -788,18 +842,17 @@ assignSectionAddress(SHeader, YAMLSec); SHeader.sh_offset = alignToOffset(CBA, SHeader.sh_addralign, /*Offset=*/None); - raw_ostream &OS = CBA.getOS(); if (RawSec && (RawSec->Content || RawSec->Size)) { assert(Symbols.empty()); - SHeader.sh_size = writeContent(OS, RawSec->Content, RawSec->Size); + SHeader.sh_size = writeContent(CBA, RawSec->Content, RawSec->Size); return; } std::vector Syms = toELFSymbols(Symbols, IsStatic ? DotStrtab : DotDynstr); - writeArrayData(OS, makeArrayRef(Syms)); - SHeader.sh_size = arrayDataSize(makeArrayRef(Syms)); + SHeader.sh_size = Syms.size() * sizeof(Elf_Sym); + CBA.write((const char *)Syms.data(), SHeader.sh_size); } template @@ -816,12 +869,12 @@ dyn_cast_or_null(YAMLSec); SHeader.sh_offset = alignToOffset(CBA, SHeader.sh_addralign, /*Offset=*/None); - raw_ostream &OS = CBA.getOS(); if (RawSec && (RawSec->Content || RawSec->Size)) { - SHeader.sh_size = writeContent(OS, RawSec->Content, RawSec->Size); + SHeader.sh_size = writeContent(CBA, RawSec->Content, RawSec->Size); } else { - STB.write(OS); + if (raw_ostream *OS = CBA.getRawOS(STB.getSize())) + STB.write(*OS); SHeader.sh_size = STB.getSize(); } @@ -846,18 +899,31 @@ template uint64_t emitDWARF(typename ELFT::Shdr &SHeader, StringRef Name, - const DWARFYAML::Data &DWARF, raw_ostream &OS) { - uint64_t BeginOffset = OS.tell(); + const DWARFYAML::Data &DWARF, + ContiguousBlobAccumulator &CBA) { + // We are unable to predict the size of a debug data, so we request 0 bytes to + // write. This should always return us an output stream unless CBA is already + // in a error state. + raw_ostream *OS = CBA.getRawOS(0); + if (!OS) + return 0; + + uint64_t BeginOffset = CBA.tell(); if (Name == ".debug_str") - DWARFYAML::emitDebugStr(OS, DWARF); + DWARFYAML::emitDebugStr(*OS, DWARF); else if (Name == ".debug_aranges") - DWARFYAML::emitDebugAranges(OS, DWARF); + DWARFYAML::emitDebugAranges(*OS, DWARF); else if (Name == ".debug_ranges") - DWARFYAML::emitDebugRanges(OS, DWARF); + DWARFYAML::emitDebugRanges(*OS, DWARF); else llvm_unreachable("unexpected emitDWARF() call"); - return OS.tell() - BeginOffset; + // Now, when we wrote the debug data, we request an output stream again. + // This forces CBA to recheck the available data size and triggers + // a corresponding internal error state if the limit was reached. + CBA.getRawOS(0); + + return CBA.tell() - BeginOffset; } template @@ -879,9 +945,9 @@ "' contents in the 'DWARF' entry and the 'Content' " "or 'Size' in the 'Sections' entry at the same time"); else - SHeader.sh_size = emitDWARF(SHeader, Name, *Doc.DWARF, CBA.getOS()); + SHeader.sh_size = emitDWARF(SHeader, Name, *Doc.DWARF, CBA); } else if (RawSec) - SHeader.sh_size = writeContent(CBA.getOS(), RawSec->Content, RawSec->Size); + SHeader.sh_size = writeContent(CBA, RawSec->Content, RawSec->Size); else llvm_unreachable("debug sections can only be initialized via the 'DWARF' " "entry or a RawContentSection"); @@ -991,7 +1057,7 @@ void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, ContiguousBlobAccumulator &CBA) { - SHeader.sh_size = writeContent(CBA.getOS(), Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); if (Section.EntSize) SHeader.sh_entsize = *Section.EntSize; @@ -1031,7 +1097,6 @@ if (!Section.RelocatableSec.empty()) SHeader.sh_info = toSectionIndex(Section.RelocatableSec, Section.Name); - raw_ostream &OS = CBA.getOS(); for (const auto &Rel : Section.Relocations) { unsigned SymIdx = Rel.Symbol ? toSymbolIndex(*Rel.Symbol, Section.Name, Section.Link == ".dynsym") @@ -1042,13 +1107,13 @@ REntry.r_offset = Rel.Offset; REntry.r_addend = Rel.Addend; REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); - OS.write((const char *)&REntry, sizeof(REntry)); + CBA.write((const char *)&REntry, sizeof(REntry)); } else { Elf_Rel REntry; zero(REntry); REntry.r_offset = Rel.Offset; REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); - OS.write((const char *)&REntry, sizeof(REntry)); + CBA.write((const char *)&REntry, sizeof(REntry)); } } } @@ -1060,9 +1125,8 @@ SHeader.sh_entsize = Section.EntSize ? uint64_t(*Section.EntSize) : sizeof(Elf_Relr); - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1073,7 +1137,7 @@ if (!ELFT::Is64Bits && E > UINT32_MAX) reportError(Section.Name + ": the value is too large for 32-bits: 0x" + Twine::utohexstr(E)); - support::endian::write(OS, E, ELFT::TargetEndianness); + CBA.write(E, ELFT::TargetEndianness); } SHeader.sh_size = sizeof(uintX_t) * Section.Entries->size(); @@ -1084,7 +1148,7 @@ Elf_Shdr &SHeader, const ELFYAML::SymtabShndxSection &Shndx, ContiguousBlobAccumulator &CBA) { for (uint32_t E : Shndx.Entries) - support::endian::write(CBA.getOS(), E, ELFT::TargetEndianness); + CBA.write(E, ELFT::TargetEndianness); SHeader.sh_entsize = Shndx.EntSize ? (uint64_t)*Shndx.EntSize : 4; SHeader.sh_size = Shndx.Entries.size() * SHeader.sh_entsize; @@ -1109,14 +1173,13 @@ SHeader.sh_info = toSymbolIndex(*Section.Signature, Section.Name, /*IsDynamic=*/false); - raw_ostream &OS = CBA.getOS(); for (const ELFYAML::SectionOrType &Member : Section.Members) { unsigned int SectionIndex = 0; if (Member.sectionNameOrType == "GRP_COMDAT") SectionIndex = llvm::ELF::GRP_COMDAT; else SectionIndex = toSectionIndex(Member.sectionNameOrType, Section.Name); - support::endian::write(OS, SectionIndex, ELFT::TargetEndianness); + CBA.write(SectionIndex, ELFT::TargetEndianness); } } @@ -1124,9 +1187,8 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::SymverSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); for (uint16_t Version : Section.Entries) - support::endian::write(OS, Version, ELFT::TargetEndianness); + CBA.write(Version, ELFT::TargetEndianness); SHeader.sh_entsize = Section.EntSize ? (uint64_t)*Section.EntSize : 2; SHeader.sh_size = Section.Entries.size() * SHeader.sh_entsize; @@ -1136,15 +1198,14 @@ void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::StackSizesSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } for (const ELFYAML::StackSizeEntry &E : *Section.Entries) { - support::endian::write(OS, E.Address, ELFT::TargetEndianness); - SHeader.sh_size += sizeof(uintX_t) + encodeULEB128(E.Size, OS); + CBA.write(E.Address, ELFT::TargetEndianness); + SHeader.sh_size += sizeof(uintX_t) + CBA.writeULEB128(E.Size); } } @@ -1152,9 +1213,8 @@ void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1162,10 +1222,10 @@ return; for (const ELFYAML::LinkerOption &LO : *Section.Options) { - OS.write(LO.Key.data(), LO.Key.size()); - OS.write('\0'); - OS.write(LO.Value.data(), LO.Value.size()); - OS.write('\0'); + CBA.write(LO.Key.data(), LO.Key.size()); + CBA.write('\0'); + CBA.write(LO.Value.data(), LO.Value.size()); + CBA.write('\0'); SHeader.sh_size += (LO.Key.size() + LO.Value.size() + 2); } } @@ -1174,9 +1234,8 @@ void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::DependentLibrariesSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1184,8 +1243,8 @@ return; for (StringRef Lib : *Section.Libs) { - OS.write(Lib.data(), Lib.size()); - OS.write('\0'); + CBA.write(Lib.data(), Lib.size()); + CBA.write('\0'); SHeader.sh_size += Lib.size() + 1; } } @@ -1210,7 +1269,7 @@ AlignedOffset = alignTo(CurrentOffset, std::max(Align, (uint64_t)1)); } - CBA.getOS().write_zeros(AlignedOffset - CurrentOffset); + CBA.writeZeros(AlignedOffset - CurrentOffset); return AlignedOffset; } @@ -1228,9 +1287,8 @@ SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1241,9 +1299,9 @@ unsigned From = toSymbolIndex(E.From, Section.Name, /*IsDynamic=*/false); unsigned To = toSymbolIndex(E.To, Section.Name, /*IsDynamic=*/false); - support::endian::write(OS, From, ELFT::TargetEndianness); - support::endian::write(OS, To, ELFT::TargetEndianness); - support::endian::write(OS, E.Weight, ELFT::TargetEndianness); + CBA.write(From, ELFT::TargetEndianness); + CBA.write(To, ELFT::TargetEndianness); + CBA.write(E.Weight, ELFT::TargetEndianness); SHeader.sh_size += 16; } } @@ -1257,23 +1315,22 @@ SN2I.lookup(".dynsym", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } - support::endian::write( - OS, Section.NBucket.getValueOr(llvm::yaml::Hex64(Section.Bucket->size())), + CBA.write( + Section.NBucket.getValueOr(llvm::yaml::Hex64(Section.Bucket->size())), ELFT::TargetEndianness); - support::endian::write( - OS, Section.NChain.getValueOr(llvm::yaml::Hex64(Section.Chain->size())), + CBA.write( + Section.NChain.getValueOr(llvm::yaml::Hex64(Section.Chain->size())), ELFT::TargetEndianness); for (uint32_t Val : *Section.Bucket) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); for (uint32_t Val : *Section.Chain) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); SHeader.sh_size = (2 + Section.Bucket->size() + Section.Chain->size()) * 4; } @@ -1287,9 +1344,8 @@ SHeader.sh_info = Section.Info; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1312,7 +1368,7 @@ else VerDef.vd_next = sizeof(Elf_Verdef) + E.VerNames.size() * sizeof(Elf_Verdaux); - OS.write((const char *)&VerDef, sizeof(Elf_Verdef)); + CBA.write((const char *)&VerDef, sizeof(Elf_Verdef)); for (size_t J = 0; J < E.VerNames.size(); ++J, ++AuxCnt) { Elf_Verdaux VernAux; @@ -1321,7 +1377,7 @@ VernAux.vda_next = 0; else VernAux.vda_next = sizeof(Elf_Verdaux); - OS.write((const char *)&VernAux, sizeof(Elf_Verdaux)); + CBA.write((const char *)&VernAux, sizeof(Elf_Verdaux)); } } @@ -1338,9 +1394,8 @@ SHeader.sh_info = Section.Info; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1361,7 +1416,7 @@ sizeof(Elf_Verneed) + VE.AuxV.size() * sizeof(Elf_Vernaux); VerNeed.vn_cnt = VE.AuxV.size(); VerNeed.vn_aux = sizeof(Elf_Verneed); - OS.write((const char *)&VerNeed, sizeof(Elf_Verneed)); + CBA.write((const char *)&VerNeed, sizeof(Elf_Verneed)); for (size_t J = 0; J < VE.AuxV.size(); ++J, ++AuxCnt) { const ELFYAML::VernauxEntry &VAuxE = VE.AuxV[J]; @@ -1375,7 +1430,7 @@ VernAux.vna_next = 0; else VernAux.vna_next = sizeof(Elf_Vernaux); - OS.write((const char *)&VernAux, sizeof(Elf_Vernaux)); + CBA.write((const char *)&VernAux, sizeof(Elf_Vernaux)); } } @@ -1406,7 +1461,7 @@ Flags.ases = Section.ASEs; Flags.flags1 = Section.Flags1; Flags.flags2 = Section.Flags2; - CBA.getOS().write((const char *)&Flags, sizeof(Flags)); + CBA.write((const char *)&Flags, sizeof(Flags)); } template @@ -1430,13 +1485,12 @@ else SHeader.sh_entsize = sizeof(Elf_Dyn); - raw_ostream &OS = CBA.getOS(); for (const ELFYAML::DynamicEntry &DE : Section.Entries) { - support::endian::write(OS, DE.Tag, ELFT::TargetEndianness); - support::endian::write(OS, DE.Val, ELFT::TargetEndianness); + CBA.write(DE.Tag, ELFT::TargetEndianness); + CBA.write(DE.Val, ELFT::TargetEndianness); } if (Section.Content) - Section.Content->writeAsBinary(OS); + CBA.writeAsBinary(*Section.Content); } template @@ -1448,62 +1502,57 @@ SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } for (StringRef Sym : *Section.Symbols) - SHeader.sh_size += encodeULEB128( - toSymbolIndex(Sym, Section.Name, /*IsDynamic=*/false), OS); + SHeader.sh_size += + CBA.writeULEB128(toSymbolIndex(Sym, Section.Name, /*IsDynamic=*/false)); } template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::NoteSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); - uint64_t Offset = OS.tell(); + uint64_t Offset = CBA.tell(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } for (const ELFYAML::NoteEntry &NE : *Section.Notes) { // Write name size. if (NE.Name.empty()) - support::endian::write(OS, 0, ELFT::TargetEndianness); + CBA.write(0, ELFT::TargetEndianness); else - support::endian::write(OS, NE.Name.size() + 1, - ELFT::TargetEndianness); + CBA.write(NE.Name.size() + 1, ELFT::TargetEndianness); // Write description size. if (NE.Desc.binary_size() == 0) - support::endian::write(OS, 0, ELFT::TargetEndianness); + CBA.write(0, ELFT::TargetEndianness); else - support::endian::write(OS, NE.Desc.binary_size(), - ELFT::TargetEndianness); + CBA.write(NE.Desc.binary_size(), ELFT::TargetEndianness); // Write type. - support::endian::write(OS, NE.Type, ELFT::TargetEndianness); + CBA.write(NE.Type, ELFT::TargetEndianness); // Write name, null terminator and padding. if (!NE.Name.empty()) { - support::endian::write(OS, arrayRefFromStringRef(NE.Name), - ELFT::TargetEndianness); - support::endian::write(OS, 0, ELFT::TargetEndianness); + CBA.write(NE.Name.data(), NE.Name.size()); + CBA.write('\0'); CBA.padToAlignment(4); } // Write description and padding. if (NE.Desc.binary_size() != 0) { - NE.Desc.writeAsBinary(OS); + CBA.writeAsBinary(NE.Desc); CBA.padToAlignment(4); } } - SHeader.sh_size = OS.tell() - Offset; + SHeader.sh_size = CBA.tell() - Offset; } template @@ -1515,9 +1564,8 @@ SN2I.lookup(".dynsym", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1526,42 +1574,35 @@ // be used to override this field, which is useful for producing broken // objects. if (Section.Header->NBuckets) - support::endian::write(OS, *Section.Header->NBuckets, - ELFT::TargetEndianness); + CBA.write(*Section.Header->NBuckets, ELFT::TargetEndianness); else - support::endian::write(OS, Section.HashBuckets->size(), - ELFT::TargetEndianness); + CBA.write(Section.HashBuckets->size(), ELFT::TargetEndianness); // Write the index of the first symbol in the dynamic symbol table accessible // via the hash table. - support::endian::write(OS, Section.Header->SymNdx, - ELFT::TargetEndianness); + CBA.write(Section.Header->SymNdx, ELFT::TargetEndianness); // Write the number of words in the Bloom filter. As above, the "MaskWords" // property can be used to set this field to any value. if (Section.Header->MaskWords) - support::endian::write(OS, *Section.Header->MaskWords, - ELFT::TargetEndianness); + CBA.write(*Section.Header->MaskWords, ELFT::TargetEndianness); else - support::endian::write(OS, Section.BloomFilter->size(), - ELFT::TargetEndianness); + CBA.write(Section.BloomFilter->size(), ELFT::TargetEndianness); // Write the shift constant used by the Bloom filter. - support::endian::write(OS, Section.Header->Shift2, - ELFT::TargetEndianness); + CBA.write(Section.Header->Shift2, ELFT::TargetEndianness); // We've finished writing the header. Now write the Bloom filter. for (llvm::yaml::Hex64 Val : *Section.BloomFilter) - support::endian::write(OS, Val, - ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); // Write an array of hash buckets. for (llvm::yaml::Hex32 Val : *Section.HashBuckets) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); // Write an array of hash values. for (llvm::yaml::Hex32 Val : *Section.HashValues) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); SHeader.sh_size = 16 /*Header size*/ + Section.BloomFilter->size() * sizeof(typename ELFT::uint) + @@ -1572,18 +1613,17 @@ template void ELFState::writeFill(ELFYAML::Fill &Fill, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); size_t PatternSize = Fill.Pattern ? Fill.Pattern->binary_size() : 0; if (!PatternSize) { - OS.write_zeros(Fill.Size); + CBA.writeZeros(Fill.Size); return; } // Fill the content with the specified pattern. uint64_t Written = 0; for (; Written + PatternSize <= Fill.Size; Written += PatternSize) - Fill.Pattern->writeAsBinary(OS); - Fill.Pattern->writeAsBinary(OS, Fill.Size - Written); + CBA.writeAsBinary(*Fill.Pattern); + CBA.writeAsBinary(*Fill.Pattern, Fill.Size - Written); } template @@ -1705,7 +1745,7 @@ template bool ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc, - yaml::ErrorHandler EH) { + yaml::ErrorHandler EH, uint64_t MaxSize) { ELFState State(Doc, EH); if (State.HasError) return false; @@ -1728,7 +1768,11 @@ // things to `OS`. const size_t SectionContentBeginOffset = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * Doc.ProgramHeaders.size(); - ContiguousBlobAccumulator CBA(SectionContentBeginOffset); + // It is quite easy to accidentally create output with yaml2obj that is larger + // than intended, for example, due to an issue in the YAML description. + // We limit the maximum allowed output size, but also provide a command line + // option to drop this limitation. + ContiguousBlobAccumulator CBA(SectionContentBeginOffset, MaxSize); std::vector SHeaders; State.initSectionHeaders(SHeaders, CBA); @@ -1736,10 +1780,19 @@ // Now we can decide segment offsets. State.setProgramHeaderLayout(PHeaders, SHeaders); + // Align the start of the section header table, which is written after all + // other sections to the end of the file. + uint64_t SHOff = + State.alignToOffset(CBA, sizeof(typename ELFT::uint), /*Offset=*/None); + if (CBA.hasReachedLimit() || + SHOff + arrayDataSize(makeArrayRef(SHeaders)) > MaxSize) + State.reportError("the output size produced reached the limit. Consider " + "using the --max-size option"); + if (State.HasError) return false; - State.writeELFHeader(CBA, OS); + State.writeELFHeader(OS, SHOff); writeArrayData(OS, makeArrayRef(PHeaders)); CBA.writeBlobToStream(OS); writeArrayData(OS, makeArrayRef(SHeaders)); @@ -1749,17 +1802,18 @@ namespace llvm { namespace yaml { -bool yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { +bool yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH, + uint64_t MaxSize) { bool IsLE = Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); bool Is64Bit = Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64); if (Is64Bit) { if (IsLE) - return ELFState::writeELF(Out, Doc, EH); - return ELFState::writeELF(Out, Doc, EH); + return ELFState::writeELF(Out, Doc, EH, MaxSize); + return ELFState::writeELF(Out, Doc, EH, MaxSize); } if (IsLE) - return ELFState::writeELF(Out, Doc, EH); - return ELFState::writeELF(Out, Doc, EH); + return ELFState::writeELF(Out, Doc, EH, MaxSize); + return ELFState::writeELF(Out, Doc, EH, MaxSize); } } // namespace yaml Index: llvm/lib/ObjectYAML/yaml2obj.cpp =================================================================== --- llvm/lib/ObjectYAML/yaml2obj.cpp +++ llvm/lib/ObjectYAML/yaml2obj.cpp @@ -19,7 +19,7 @@ namespace yaml { bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, - unsigned DocNum) { + unsigned DocNum, uint64_t MaxSize) { unsigned CurDocNum = 0; do { if (++CurDocNum != DocNum) @@ -33,7 +33,7 @@ } if (Doc.Elf) - return yaml2elf(*Doc.Elf, Out, ErrHandler); + return yaml2elf(*Doc.Elf, Out, ErrHandler, MaxSize); if (Doc.Coff) return yaml2coff(*Doc.Coff, Out, ErrHandler); if (Doc.MachO || Doc.FatMachO) Index: llvm/test/tools/yaml2obj/ELF/output-limit.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/ELF/output-limit.yaml @@ -0,0 +1,54 @@ +## Check that yaml2obj limits the output size by default to 10 Mb. +## Check it is possible to change this limit using the +## --max-size command line option. + +## One of the often cases to reach the limit is to create a section with a +## large portion of data. Check this case is handled properly. + +## 0x9FFEC0 = 0xA00000 (10 Mb) - sizeof(Elf_Ehdr) - sizeof(Elf_Shdr) * 4. +# RUN: yaml2obj %s -DSIZE=0x9FFEC0 --docnum=1 -o /dev/null 2>&1 +# RUN: not yaml2obj %s -DSIZE=0x9FFEC1 --docnum=1 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR + +# ERROR: error: the output size produced reached the limit. Consider using the --max-size option + +## We use 0xA00008 instead of 0xA00001 here because the section header table +## offset is aligned to 8 bytes, so we need to request 7 more bytes for it. +# RUN: yaml2obj %s -DSIZE=0x9FFEC1 --docnum=1 --max-size=0xA00008 -o /dev/null + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .section + Type: SHT_PROGBITS + Size: [[SIZE]] + - Name: .strtab + Type: SHT_PROGBITS + Size: 0x0 + - Name: .shstrtab + Type: SHT_PROGBITS + Size: 0x0 + +## Another possible case is when an alignment gap inserted +## is too large because of overaligning. Check it is also handled properly. +## Also check that we can drop the limit with the use of --max-size=0. +# RUN: not yaml2obj %s --docnum=2 -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR +# RUN: yaml2obj --max-size=0 %s --docnum=2 -o /dev/null + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + - Name: .bar + Type: SHT_PROGBITS + AddressAlign: 0xA00100 + Size: 0x0 Index: llvm/tools/yaml2obj/yaml2obj.cpp =================================================================== --- llvm/tools/yaml2obj/yaml2obj.cpp +++ llvm/tools/yaml2obj/yaml2obj.cpp @@ -44,6 +44,11 @@ cl::desc("Read specified document from input (default = 1)"), cl::cat(Cat)); +static cl::opt MaxSize( + "max-size", cl::init(10 * 1024 * 1024), + cl::desc( + "Sets the maximum allowed output size (0 means no limit) [ELF only]")); + cl::opt OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"), cl::Prefix, cl::cat(Cat)); @@ -115,7 +120,9 @@ if (!Buffer) return 1; yaml::Input YIn(*Buffer); - if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum)) + + if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum, + MaxSize == 0 ? UINT64_MAX : MaxSize)) return 1; Out->keep();