Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -44,10 +44,11 @@ // This enum is used to implement linker script SECTIONS command. // https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS enum SectionsCommandKind { - AssignmentKind, + AssignmentKind, // . = expr or = expr OutputSectionKind, InputSectionKind, - AssertKind + AssertKind, // ASSERT(expr) + BytesDataKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr) }; struct BaseCommand { @@ -138,6 +139,15 @@ Expr Expression; }; +struct BytesDataCommand : BaseCommand { + BytesDataCommand(uint64_t Data, unsigned Size) + : BaseCommand(BytesDataKind), Data(Data), Size(Size) {} + static bool classof(const BaseCommand *C); + uint64_t Data; + unsigned Offset; + unsigned Size; +}; + struct PhdrsCommand { StringRef Name; unsigned Type; @@ -193,6 +203,7 @@ bool ignoreInterpSection(); ArrayRef getFiller(StringRef Name); + void writeDataBytes(StringRef Name, uint8_t *Buf); Expr getLma(StringRef Name); bool shouldKeep(InputSectionBase *S); void assignOffsets(OutputSectionCommand *Cmd); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -38,6 +38,7 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -94,6 +95,10 @@ return C->Kind == AssertKind; } +bool BytesDataCommand::classof(const BaseCommand *C) { + return C->Kind == BytesDataKind; +} + template static bool isDiscarded(InputSectionBase *S) { return !S || !S->Live; } @@ -405,6 +410,7 @@ } template void LinkerScript::process(BaseCommand &Base) { + // This handles the assignments to symbol or to a location counter (.) if (auto *AssignCmd = dyn_cast(&Base)) { if (AssignCmd->Name == ".") { // Update to location counter means update to section size. @@ -415,6 +421,18 @@ assignSectionSymbol(AssignCmd, CurOutSec, Dot - CurOutSec->getVA()); return; } + // It handles the bytes data command, which places some byte value into output + // section and moves the location counter in according to value data size. + if (auto *DataCmd = dyn_cast(&Base)) { + DataCmd->Offset = Dot - CurOutSec->getVA(); + Dot += DataCmd->Size; + CurOutSec->setSize(Dot - CurOutSec->getVA()); + return; + } + + // It handles single input section description command, + // calculates and assigns the offsets for each section and also + // updates the output section size. auto &ICmd = cast(Base); for (InputSectionData *ID : ICmd.Sections) { auto *IB = static_cast *>(ID); @@ -691,6 +709,43 @@ return {}; } +template +static OutputSectionCommand *findOutputSection(StringRef Name) { + for (const std::unique_ptr &Base : ScriptConfig->Commands) + if (auto *Cmd = dyn_cast(Base.get())) + if (Cmd->Name == Name) + return Cmd; + return nullptr; +} + +template +void LinkerScript::writeDataBytes(StringRef Name, uint8_t *Buf) { + const endianness E = ELFT::TargetEndianness; + + if (OutputSectionCommand *Cmd = findOutputSection(Name)) { + for (const std::unique_ptr &Base2 : Cmd->Commands) { + if (auto *DataCmd = dyn_cast(Base2.get())) { + // If the object file format of the output file has an explicit + // endianness, the value will be stored in that endianness. + switch (DataCmd->Size) { + case 1: + Buf[DataCmd->Offset] = (uint8_t)DataCmd->Data; + break; + case 2: + write16(&Buf[DataCmd->Offset], (uint16_t)DataCmd->Data); + break; + case 4: + write32(&Buf[DataCmd->Offset], (uint32_t)DataCmd->Data); + break; + case 8: + write64(&Buf[DataCmd->Offset], DataCmd->Data); + break; + } + } + } + } +} + template Expr LinkerScript::getLma(StringRef Name) { for (const std::unique_ptr &Base : Opt.Commands) if (auto *Cmd = dyn_cast(Base.get())) @@ -813,6 +868,7 @@ void readVersionScriptCommand(); SymbolAssignment *readAssignment(StringRef Name); + BytesDataCommand *readBytesDataCommand(StringRef Tok); std::vector readFill(); OutputSectionCommand *readOutputSectionDescription(StringRef OutSec); std::vector readOutputSectionFiller(StringRef Tok); @@ -1259,6 +1315,8 @@ StringRef Tok = next(); if (SymbolAssignment *Assignment = readProvideOrAssignment(Tok, false)) Cmd->Commands.emplace_back(Assignment); + else if (BytesDataCommand *Data = readBytesDataCommand(Tok)) + Cmd->Commands.emplace_back(Data); else if (Tok == "FILL") Cmd->Filler = readFill(); else if (Tok == "SORT") @@ -1448,6 +1506,25 @@ return true; } +BytesDataCommand *ScriptParser::readBytesDataCommand(StringRef Tok) { + unsigned Size = StringSwitch(Tok) + .Case("BYTE", 1) + .Case("SHORT", 2) + .Case("LONG", 4) + .Case("QUAD", 8) + .Default(-1); + if (Size == (unsigned)-1) + return nullptr; + + expect("("); + uint64_t Val = 0; + StringRef S = next(); + if (!readInteger(S, Val)) + setError("unexpected value: " + S); + expect(")"); + return new BytesDataCommand(Val, Size); +} + Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -1005,6 +1005,9 @@ for (InputSection *C : Sections) C->writeTo(Buf); } + // Linker scripts may have BYTE()-family commands with which you + // can write arbitrary bytes to the output. Process them if any. + Script::X->writeDataBytes(this->Name, Buf); } template Index: test/ELF/linkerscript/data-commands.s =================================================================== --- test/ELF/linkerscript/data-commands.s +++ test/ELF/linkerscript/data-commands.s @@ -0,0 +1,34 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o + +# RUN: echo "SECTIONS \ +# RUN: { \ +# RUN: .foo : { \ +# RUN: *(.foo.1) \ +# RUN: BYTE(0x11) \ +# RUN: *(.foo.2) \ +# RUN: SHORT(0x1122) \ +# RUN: *(.foo.3) \ +# RUN: LONG(0x11223344) \ +# RUN: *(.foo.4) \ +# RUN: QUAD(0x1122334455667788) \ +# RUN: } \ +# RUN: }" > %t.script +# RUN: ld.lld -o %t --script %t.script %t.o +# RUN: llvm-objdump -s %t | FileCheck %s + +# CHECK: Contents of section .foo: +# CHECK-NEXT: 00e8 ff11ff22 11ff4433 2211ff88 77665544 +# CHECK-NEXT: 00f8 332211 + +.section .foo.1, "a" + .byte 0xFF + +.section .foo.2, "a" + .byte 0xFF + +.section .foo.3, "a" + .byte 0xFF + +.section .foo.4, "a" + .byte 0xFF