// SPDX-License-Identifier: GPL-3.0-or-later #include "rcc/MC/ELFObjectWriter.h" #include #include #include #include #include #include #include #include #include #include #include namespace rcc::mc { namespace { constexpr std::uint32_t SHTNull = 0; constexpr std::uint32_t SHTProgBits = 1; constexpr std::uint32_t SHTSymTab = 2; constexpr std::uint32_t SHTStrTab = 3; constexpr std::uint32_t SHTRela = 4; constexpr std::uint32_t SHTNoBits = 8; constexpr std::uint32_t SHTRel = 9; constexpr std::uint32_t SHTGroup = 17; constexpr std::uint32_t SHTSymTabShndx = 18; constexpr std::uint16_t SHNUndefined = 0; constexpr std::uint32_t SHNLoReserve = 0xff00U; constexpr std::uint16_t SHNCommon = 0xfff2U; constexpr std::uint32_t GRPComdat = 1; constexpr std::uint64_t SHFWrite = 0x1; constexpr std::uint64_t SHFAlloc = 0x2; constexpr std::uint64_t SHFExecInstr = 0x4; constexpr std::uint64_t SHFMerge = 0x10; constexpr std::uint64_t SHFStrings = 0x20; constexpr std::uint64_t SHFInfoLink = 0x40; constexpr std::uint64_t SHFGroup = 0x200; constexpr std::uint64_t SHFTLS = 0x400; constexpr std::uint64_t SHFGNURetain = 0x200000; class StringTable final { public: StringTable() { Bytes.push_back(0); } [[nodiscard]] Expected add(std::string_view Value) { const auto Existing = Offsets.find(std::string(Value)); if (Existing != Offsets.end()) return Existing->second; constexpr std::size_t Maximum = std::numeric_limits::max(); if (Value.size() > Maximum - 1U || Bytes.size() > Maximum - Value.size() - 1U) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF string table exceeds 4 GiB"); } const auto Offset = static_cast(Bytes.size()); Bytes.insert(Bytes.end(), Value.begin(), Value.end()); Bytes.push_back(0); Offsets.emplace(std::string(Value), Offset); return Offset; } [[nodiscard]] const std::vector &bytes() const noexcept { return Bytes; } private: std::vector Bytes; std::unordered_map Offsets; }; struct OutputSymbol final { std::uint32_t Name = 0; std::uint8_t Info = 0; std::uint8_t Other = 0; std::uint16_t Section = SHNUndefined; std::uint64_t Value = 0; std::uint64_t Size = 0; const MCSymbol *Identity = nullptr; }; struct OutputSection final { std::string Name; std::uint32_t NameOffset = 0; std::uint32_t Type = SHTProgBits; std::uint64_t Flags = 0; std::uint64_t Alignment = 1; std::uint64_t EntrySize = 0; std::uint32_t Link = 0; std::uint32_t Info = 0; std::vector Contents; std::uint64_t AddressSize = 0; std::uint64_t FileOffset = 0; const MCSection *Input = nullptr; std::optional RelocationTarget; MCSymbolRef GroupSignature; }; [[nodiscard]] bool isPowerOfTwo(std::uint64_t Value) noexcept { return Value != 0 && (Value & (Value - 1U)) == 0; } [[nodiscard]] bool isWriterManagedSectionType(std::uint32_t Type) noexcept { switch (Type) { case SHTNull: case SHTSymTab: case SHTStrTab: case SHTRela: case SHTNoBits: case SHTRel: case SHTGroup: case SHTSymTabShndx: return true; default: return false; } } [[nodiscard]] Expected checkedAdd(std::uint64_t Left, std::uint64_t Right, std::string_view Message) { std::uint64_t Result = 0; if (__builtin_add_overflow(Left, Right, &Result)) return makeUnexpected(ErrorCode::ObjectWritingFailed, std::string(Message)); return Result; } [[nodiscard]] Expected alignTo(std::uint64_t Value, std::uint64_t Alignment) { if (!isPowerOfTwo(Alignment)) return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF alignment is not a power of two"); auto Adjusted = checkedAdd(Value, Alignment - 1U, "ELF file offset overflows"); if (!Adjusted) return std::unexpected(Adjusted.error()); return *Adjusted & ~(Alignment - 1U); } void appendU16(std::vector &Output, std::uint16_t Value) { Output.push_back(static_cast(Value)); Output.push_back(static_cast(Value >> 8U)); } void appendU32(std::vector &Output, std::uint32_t Value) { for (std::uint32_t Shift = 0; Shift < 32; Shift += 8) Output.push_back(static_cast(Value >> Shift)); } void appendU64(std::vector &Output, std::uint64_t Value) { for (std::uint32_t Shift = 0; Shift < 64; Shift += 8) Output.push_back(static_cast(Value >> Shift)); } void writeU64(std::vector &Output, std::size_t Offset, std::uint64_t Value) { for (std::uint32_t Shift = 0; Shift < 64; Shift += 8) Output[Offset + Shift / 8U] = static_cast(Value >> Shift); } [[nodiscard]] std::uint64_t sectionFlags(MCSectionFlags Flags) noexcept { std::uint64_t Result = 0; if (Flags.has(MCSectionFlag::Writable)) Result |= SHFWrite; if (Flags.has(MCSectionFlag::Alloc)) Result |= SHFAlloc; if (Flags.has(MCSectionFlag::Executable)) Result |= SHFExecInstr; if (Flags.has(MCSectionFlag::Merge)) Result |= SHFMerge; if (Flags.has(MCSectionFlag::Strings)) Result |= SHFStrings; if (Flags.has(MCSectionFlag::Group)) Result |= SHFGroup; if (Flags.has(MCSectionFlag::TLS)) Result |= SHFTLS; if (Flags.has(MCSectionFlag::Retain)) Result |= SHFGNURetain; return Result; } [[nodiscard]] std::uint8_t symbolBinding(MCObjectSymbolBinding Binding) noexcept { switch (Binding) { case MCObjectSymbolBinding::Local: return 0; case MCObjectSymbolBinding::Global: return 1; case MCObjectSymbolBinding::Weak: return 2; } return 0; } [[nodiscard]] std::uint8_t symbolType(MCObjectSymbolType Type) noexcept { switch (Type) { case MCObjectSymbolType::None: return 0; case MCObjectSymbolType::Object: return 1; case MCObjectSymbolType::Function: return 2; case MCObjectSymbolType::Section: return 3; case MCObjectSymbolType::TLSObject: return 6; } return 0; } [[nodiscard]] std::uint8_t symbolVisibility(MCObjectSymbolVisibility Visibility) noexcept { switch (Visibility) { case MCObjectSymbolVisibility::Default: return 0; case MCObjectSymbolVisibility::Hidden: return 2; case MCObjectSymbolVisibility::Protected: return 3; } return 0; } [[nodiscard]] Expected appendAtOffset(std::vector &Output, std::uint64_t Offset, std::span Contents) { if (Offset > std::numeric_limits::max() || Contents.size() > std::numeric_limits::max() - static_cast(Offset)) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF output exceeds the host limit"); } const std::size_t Position = static_cast(Offset); if (Output.size() > Position) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF output sections overlap in file layout"); } if (Output.size() < Position) Output.resize(Position); Output.insert(Output.end(), Contents.begin(), Contents.end()); return {}; } } // namespace Expected> ELF64LEObjectWriter::writeObject(const MCAssembledObject &Object) const { if (Target.Machine == 0) return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF target machine is not configured"); constexpr std::size_t ReservedOutputSections = 4; constexpr std::size_t MaximumInputAndExtraSections = SHNLoReserve - ReservedOutputSections - 1U; if (Object.Sections.size() > MaximumInputAndExtraSections || Target.ExtraSections.size() > MaximumInputAndExtraSections - Object.Sections.size()) { return makeUnexpected(ErrorCode::Unsupported, "ELF extended section numbering is not implemented"); } std::vector Sections; Sections.push_back(OutputSection{.Name = {}, .NameOffset = 0, .Type = SHTNull, .Flags = 0, .Alignment = 0, .EntrySize = 0, .Link = 0, .Info = 0, .Contents = {}, .AddressSize = 0, .FileOffset = 0, .Input = nullptr, .RelocationTarget = std::nullopt, .GroupSignature = {}}); const auto RequireSectionCapacity = [&Sections](std::size_t Additional) -> Expected { if (Sections.size() >= SHNLoReserve || Additional >= SHNLoReserve - Sections.size()) { return makeUnexpected(ErrorCode::Unsupported, "ELF extended section numbering is not implemented"); } return {}; }; auto InputCapacity = RequireSectionCapacity(Object.Sections.size()); if (!InputCapacity) return std::unexpected(InputCapacity.error()); std::vector InputSectionIndices(Object.Sections.size()); std::unordered_map ExistingSections; for (std::size_t Index = 0; Index < Object.Sections.size(); ++Index) { const MCObjectSection &Input = Object.Sections[Index]; if (!Input.Section) return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input section is null"); if (!ExistingSections.emplace(Input.Section.get(), true).second) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input contains a duplicate section identity"); } const MCSectionDesc &Description = Input.Section->description(); auto DescriptionVerification = Description.verify(); if (!DescriptionVerification) return std::unexpected(DescriptionVerification.error()); const bool NoBits = Description.Kind == MCSectionKind::BSS || Description.Kind == MCSectionKind::TLSBSS; if (!isPowerOfTwo(Input.Alignment) || Input.Alignment < Description.Alignment) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input section has an invalid effective alignment"); } if ((!NoBits && Input.AddressSize != Input.Contents.size()) || (NoBits && !Input.Contents.empty())) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input section contents disagree with its address size"); } if (NoBits && !Input.Relocations.empty()) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF NOBITS section cannot contain relocations"); } const auto OutputIndex = static_cast(Sections.size()); InputSectionIndices[Index] = OutputIndex; Sections.push_back( OutputSection{.Name = Description.Name, .NameOffset = 0, .Type = NoBits ? SHTNoBits : SHTProgBits, .Flags = sectionFlags(Description.Flags), .Alignment = Input.Alignment, .EntrySize = Description.EntrySize, .Link = 0, .Info = 0, .Contents = NoBits ? std::vector{} : Input.Contents, .AddressSize = Input.AddressSize, .FileOffset = 0, .Input = Input.Section.get(), .RelocationTarget = std::nullopt, .GroupSignature = Description.Group}); } for (const ELFExtraSection &Extra : Target.ExtraSections) { if (Extra.Name.empty() || isWriterManagedSectionType(Extra.Type) || !isPowerOfTwo(Extra.Alignment) || (Extra.Flags & (SHFInfoLink | SHFGroup)) != 0 || (Extra.EntrySize != 0 && Extra.Contents.size() % Extra.EntrySize != 0)) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF extra section has an invalid description"); } auto ExtraCapacity = RequireSectionCapacity(1); if (!ExtraCapacity) return std::unexpected(ExtraCapacity.error()); Sections.push_back(OutputSection{.Name = Extra.Name, .NameOffset = 0, .Type = Extra.Type, .Flags = Extra.Flags, .Alignment = Extra.Alignment, .EntrySize = Extra.EntrySize, .Link = 0, .Info = 0, .Contents = Extra.Contents, .AddressSize = Extra.Contents.size(), .FileOffset = 0, .Input = nullptr, .RelocationTarget = std::nullopt, .GroupSignature = {}}); } StringTable SymbolNames; std::vector LocalSymbols; std::vector GlobalSymbols; std::unordered_map ExistingSymbols; for (const MCObjectSymbol &Input : Object.Symbols) { if (!Input.Symbol) return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input symbol is null"); if (!ExistingSymbols.emplace(Input.Symbol.get(), true).second) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input contains a duplicate symbol identity"); } auto Name = SymbolNames.add(Input.Symbol->name()); if (!Name) return std::unexpected(Name.error()); std::uint16_t Section = SHNUndefined; std::uint64_t Value = Input.Value; if (Input.CommonAlignment != 0) { if (!isPowerOfTwo(Input.CommonAlignment) || Input.Size == 0 || Input.SectionIndex || Input.Binding == MCObjectSymbolBinding::Local || Input.Value != Input.CommonAlignment) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF common symbol has an invalid definition"); } Section = SHNCommon; Value = Input.CommonAlignment; } else if (Input.SectionIndex) { if (*Input.SectionIndex >= InputSectionIndices.size()) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF symbol references an invalid section"); } const std::uint32_t Mapped = InputSectionIndices[*Input.SectionIndex]; if (Mapped >= SHNLoReserve) { return makeUnexpected(ErrorCode::Unsupported, "ELF extended section indices are not implemented"); } Section = static_cast(Mapped); const std::uint64_t SectionSize = Object.Sections[*Input.SectionIndex].AddressSize; if (Input.Value > SectionSize || Input.Size > SectionSize - Input.Value) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF symbol range exceeds its section"); } } else if (Input.Value != 0) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF undefined symbol has a nonzero value"); } OutputSymbol Symbol{.Name = *Name, .Info = static_cast((symbolBinding(Input.Binding) << 4U) | symbolType(Input.Type)), .Other = symbolVisibility(Input.Visibility), .Section = Section, .Value = Value, .Size = Input.Size, .Identity = Input.Symbol.get()}; if (Input.Binding == MCObjectSymbolBinding::Local) LocalSymbols.push_back(Symbol); else GlobalSymbols.push_back(Symbol); } for (const OutputSection &Section : Sections) { if (!Section.GroupSignature || ExistingSymbols.contains(Section.GroupSignature.get())) continue; auto Name = SymbolNames.add(Section.GroupSignature->name()); if (!Name) return std::unexpected(Name.error()); GlobalSymbols.push_back(OutputSymbol{.Name = *Name, .Info = static_cast(1U << 4U), .Other = 0, .Section = SHNUndefined, .Value = 0, .Size = 0, .Identity = Section.GroupSignature.get()}); ExistingSymbols.emplace(Section.GroupSignature.get(), true); } std::vector Symbols; Symbols.push_back(OutputSymbol{}); for (std::size_t Index = 1; Index < Sections.size(); ++Index) { Symbols.push_back(OutputSymbol{.Name = 0, .Info = static_cast(3), .Other = 0, .Section = static_cast(Index), .Value = 0, .Size = 0, .Identity = nullptr}); } constexpr std::size_t MaximumSymbolCount = std::numeric_limits::max(); if (LocalSymbols.size() > MaximumSymbolCount - Symbols.size()) { return makeUnexpected(ErrorCode::Unsupported, "ELF symbol index exceeds 32 bits"); } Symbols.insert(Symbols.end(), LocalSymbols.begin(), LocalSymbols.end()); const std::uint32_t FirstGlobal = static_cast(Symbols.size()); if (GlobalSymbols.size() > MaximumSymbolCount - Symbols.size()) { return makeUnexpected(ErrorCode::Unsupported, "ELF symbol index exceeds 32 bits"); } Symbols.insert(Symbols.end(), GlobalSymbols.begin(), GlobalSymbols.end()); std::unordered_map SymbolIndices; for (std::size_t Index = 0; Index < Symbols.size(); ++Index) { if (Symbols[Index].Identity) SymbolIndices.emplace(Symbols[Index].Identity, static_cast(Index)); } for (std::size_t InputIndex = 0; InputIndex < Object.Sections.size(); ++InputIndex) { const MCObjectSection &Input = Object.Sections[InputIndex]; if (Input.Relocations.empty()) continue; if (Input.Relocations.size() > std::numeric_limits::max() / 24U) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF relocation section exceeds the host limit"); } std::vector Contents; Contents.reserve(Input.Relocations.size() * 24U); for (const MCObjectRelocation &Relocation : Input.Relocations) { const auto Symbol = SymbolIndices.find(Relocation.Symbol.get()); if (!Relocation.Symbol || Symbol == SymbolIndices.end()) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF relocation references an unknown symbol"); } if (Relocation.Type == 0 || Relocation.Offset >= Input.AddressSize) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF relocation has an invalid type or section offset"); } appendU64(Contents, Relocation.Offset); appendU64(Contents, (static_cast(Symbol->second) << 32U) | Relocation.Type); appendU64(Contents, static_cast(Relocation.Addend)); } const std::uint32_t TargetSection = InputSectionIndices[InputIndex]; const MCSymbolRef GroupSignature = Sections[TargetSection].GroupSignature; auto RelocationCapacity = RequireSectionCapacity(1); if (!RelocationCapacity) return std::unexpected(RelocationCapacity.error()); Sections.push_back(OutputSection{.Name = ".rela" + std::string(Input.Section->name()), .NameOffset = 0, .Type = SHTRela, .Flags = SHFInfoLink | (GroupSignature ? SHFGroup : 0), .Alignment = 8, .EntrySize = 24, .Link = 0, .Info = TargetSection, .Contents = std::move(Contents), .AddressSize = 0, .FileOffset = 0, .Input = nullptr, .RelocationTarget = InputIndex, .GroupSignature = GroupSignature}); } struct PendingGroup final { MCSymbolRef Signature; std::vector Members; }; std::vector Groups; for (std::size_t SectionIndex = 1; SectionIndex < Sections.size(); ++SectionIndex) { if (!Sections[SectionIndex].GroupSignature) continue; const auto Existing = std::ranges::find_if(Groups, [&](const PendingGroup &Group) { return Group.Signature == Sections[SectionIndex].GroupSignature; }); if (Existing == Groups.end()) { Groups.push_back(PendingGroup{ .Signature = Sections[SectionIndex].GroupSignature, .Members = {static_cast(SectionIndex)}, }); } else { Existing->Members.push_back(static_cast(SectionIndex)); } } for (const PendingGroup &Group : Groups) { const auto Signature = SymbolIndices.find(Group.Signature.get()); if (Signature == SymbolIndices.end() || (Symbols[Signature->second].Info >> 4U) == 0) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF COMDAT group requires a nonlocal signature symbol"); } std::vector Contents; appendU32(Contents, GRPComdat); for (const std::uint32_t Member : Group.Members) appendU32(Contents, Member); auto GroupCapacity = RequireSectionCapacity(1); if (!GroupCapacity) return std::unexpected(GroupCapacity.error()); Sections.push_back(OutputSection{.Name = ".group", .NameOffset = 0, .Type = SHTGroup, .Flags = 0, .Alignment = 4, .EntrySize = 4, .Link = 0, .Info = Signature->second, .Contents = std::move(Contents), .AddressSize = 0, .FileOffset = 0, .Input = nullptr, .RelocationTarget = std::nullopt, .GroupSignature = {}}); } auto MetadataCapacity = RequireSectionCapacity(3); if (!MetadataCapacity) return std::unexpected(MetadataCapacity.error()); const std::uint32_t SymTabIndex = static_cast(Sections.size()); const std::uint32_t StrTabIndex = SymTabIndex + 1U; for (OutputSection &Section : Sections) { if (Section.Type == SHTGroup || Section.Type == SHTRela) Section.Link = SymTabIndex; } std::vector SymbolContents; if (Symbols.size() > std::numeric_limits::max() / 24U) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF symbol table exceeds the host limit"); } SymbolContents.reserve(Symbols.size() * 24U); for (const OutputSymbol &Symbol : Symbols) { appendU32(SymbolContents, Symbol.Name); SymbolContents.push_back(Symbol.Info); SymbolContents.push_back(Symbol.Other); appendU16(SymbolContents, Symbol.Section); appendU64(SymbolContents, Symbol.Value); appendU64(SymbolContents, Symbol.Size); } Sections.push_back(OutputSection{.Name = ".symtab", .NameOffset = 0, .Type = SHTSymTab, .Flags = 0, .Alignment = 8, .EntrySize = 24, .Link = StrTabIndex, .Info = FirstGlobal, .Contents = std::move(SymbolContents), .AddressSize = 0, .FileOffset = 0, .Input = nullptr, .RelocationTarget = std::nullopt, .GroupSignature = {}}); Sections.push_back(OutputSection{.Name = ".strtab", .NameOffset = 0, .Type = SHTStrTab, .Flags = 0, .Alignment = 1, .EntrySize = 0, .Link = 0, .Info = 0, .Contents = SymbolNames.bytes(), .AddressSize = 0, .FileOffset = 0, .Input = nullptr, .RelocationTarget = std::nullopt, .GroupSignature = {}}); StringTable SectionNames; for (OutputSection &Section : Sections) { auto Name = SectionNames.add(Section.Name); if (!Name) return std::unexpected(Name.error()); Section.NameOffset = *Name; } const std::uint32_t ShStrTabIndex = static_cast(Sections.size()); auto ShStrName = SectionNames.add(".shstrtab"); if (!ShStrName) return std::unexpected(ShStrName.error()); Sections.push_back(OutputSection{.Name = ".shstrtab", .NameOffset = *ShStrName, .Type = SHTStrTab, .Flags = 0, .Alignment = 1, .EntrySize = 0, .Link = 0, .Info = 0, .Contents = SectionNames.bytes(), .AddressSize = 0, .FileOffset = 0, .Input = nullptr, .RelocationTarget = std::nullopt, .GroupSignature = {}}); if (Sections.size() >= SHNLoReserve) { return makeUnexpected(ErrorCode::Unsupported, "ELF extended section numbering is not implemented"); } std::uint64_t FileOffset = 64; for (std::size_t Index = 1; Index < Sections.size(); ++Index) { OutputSection &Section = Sections[Index]; auto Aligned = alignTo(FileOffset, Section.Alignment); if (!Aligned) return std::unexpected(Aligned.error()); Section.FileOffset = *Aligned; if (Section.Type == SHTNoBits) continue; auto End = checkedAdd(*Aligned, Section.Contents.size(), "ELF section file range overflows"); if (!End) return std::unexpected(End.error()); FileOffset = *End; } auto SectionHeaderOffset = alignTo(FileOffset, 8); if (!SectionHeaderOffset) return std::unexpected(SectionHeaderOffset.error()); if (Sections.size() > std::numeric_limits::max() / 64U) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF section header table exceeds the host limit"); } auto End = checkedAdd(*SectionHeaderOffset, static_cast(Sections.size()) * 64U, "ELF section header table overflows"); if (!End || *End > std::numeric_limits::max()) { return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF output exceeds the host limit"); } std::vector Output(64, 0); Output[0] = 0x7f; Output[1] = 'E'; Output[2] = 'L'; Output[3] = 'F'; Output[4] = 2; Output[5] = 1; Output[6] = 1; Output[7] = Target.OSABI; Output[8] = Target.ABIVersion; Output[16] = 1; Output[18] = static_cast(Target.Machine); Output[19] = static_cast(Target.Machine >> 8U); Output[20] = 1; writeU64(Output, 40, *SectionHeaderOffset); Output[48] = static_cast(Target.Flags | Object.Flags); Output[49] = static_cast((Target.Flags | Object.Flags) >> 8U); Output[50] = static_cast((Target.Flags | Object.Flags) >> 16U); Output[51] = static_cast((Target.Flags | Object.Flags) >> 24U); Output[52] = 64; Output[58] = 64; Output[60] = static_cast(Sections.size()); Output[61] = static_cast(Sections.size() >> 8U); Output[62] = static_cast(ShStrTabIndex); Output[63] = static_cast(ShStrTabIndex >> 8U); for (std::size_t Index = 1; Index < Sections.size(); ++Index) { if (Sections[Index].Type == SHTNoBits) continue; auto Appended = appendAtOffset(Output, Sections[Index].FileOffset, Sections[Index].Contents); if (!Appended) return std::unexpected(Appended.error()); } if (Output.size() < *SectionHeaderOffset) Output.resize(static_cast(*SectionHeaderOffset)); for (const OutputSection &Section : Sections) { appendU32(Output, Section.NameOffset); appendU32(Output, Section.Type); appendU64(Output, Section.Flags); appendU64(Output, 0); appendU64(Output, Section.FileOffset); appendU64(Output, Section.Type == SHTNoBits ? Section.AddressSize : Section.Contents.size()); appendU32(Output, Section.Link); appendU32(Output, Section.Info); appendU64(Output, Section.Alignment); appendU64(Output, Section.EntrySize); } return Output; } } // namespace rcc::mc