Skip to content

ELF: Introduce --randomize-section-padding option. #117653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ struct Config {
bool printGcSections;
bool printIcfSections;
bool printMemoryUsage;
std::optional<uint64_t> randomizeSectionPadding;
bool rejectMismatch;
bool relax;
bool relaxGP;
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
ctx.arg.searchPaths = args::getStrings(args, OPT_library_path);
ctx.arg.sectionStartMap = getSectionStartMap(ctx, args);
ctx.arg.shared = args.hasArg(OPT_shared);
if (args.hasArg(OPT_randomize_section_padding))
ctx.arg.randomizeSectionPadding =
args::getInteger(args, OPT_randomize_section_padding, 0);
ctx.arg.singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true);
ctx.arg.soName = args.getLastArgValue(OPT_soname);
ctx.arg.sortSection = getSortSection(ctx, args);
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,9 @@ defm section_start: Eq<"section-start", "Set address of section">,

def shared: F<"shared">, HelpText<"Build a shared object">;

def randomize_section_padding: JJ<"randomize-section-padding=">,
HelpText<"Randomly insert padding between input sections using given seed">;

defm soname: Eq<"soname", "Set DT_SONAME">;

defm sort_section:
Expand Down
4 changes: 2 additions & 2 deletions lld/ELF/OutputSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ class OutputSection final : public SectionBase {
void sortInitFini();
void sortCtorsDtors();

std::array<uint8_t, 4> getFiller(Ctx &);

// Used for implementation of --compress-debug-sections and
// --compress-sections.
CompressedData compressed;

private:
SmallVector<InputSection *, 0> storage;

std::array<uint8_t, 4> getFiller(Ctx &);
};

struct OutputDesc final : SectionCommand {
Expand Down
15 changes: 15 additions & 0 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2753,6 +2753,21 @@ RelroPaddingSection::RelroPaddingSection(Ctx &ctx)
: SyntheticSection(ctx, ".relro_padding", SHT_NOBITS, SHF_ALLOC | SHF_WRITE,
1) {}

RandomizePaddingSection::RandomizePaddingSection(Ctx &ctx, uint64_t size,
OutputSection *parent)
: SyntheticSection(ctx, ".randomize_padding", SHT_PROGBITS, SHF_ALLOC, 1),
size(size) {
this->parent = parent;
}

void RandomizePaddingSection::writeTo(uint8_t *buf) {
std::array<uint8_t, 4> filler = getParent()->getFiller(ctx);
uint8_t *end = buf + size;
for (; buf + 4 <= end; buf += 4)
memcpy(buf, &filler[0], 4);
memcpy(buf, &filler[0], end - buf);
}

// The string hash function for .gdb_index.
static uint32_t computeGdbHash(StringRef s) {
uint32_t h = 0;
Expand Down
9 changes: 9 additions & 0 deletions lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,15 @@ class RelroPaddingSection final : public SyntheticSection {
void writeTo(uint8_t *buf) override {}
};

class RandomizePaddingSection final : public SyntheticSection {
uint64_t size;

public:
RandomizePaddingSection(Ctx &ctx, uint64_t size, OutputSection *parent);
size_t getSize() const override { return size; }
void writeTo(uint8_t *buf) override;
};

// Used by the merged DWARF32 .debug_names (a per-module index). If we
// move to DWARF64, most of this data will need to be re-sized.
class DebugNamesBaseSection : public SyntheticSection {
Expand Down
36 changes: 36 additions & 0 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,39 @@ static void finalizeSynthetic(Ctx &ctx, SyntheticSection *sec) {
}
}

static bool canInsertPadding(OutputSection *sec) {
StringRef s = sec->name;
return s == ".bss" || s == ".data" || s == ".data.rel.ro" || s == ".rodata" ||
s.starts_with(".text");
}

static void randomizeSectionPadding(Ctx &ctx) {
std::mt19937 g(*ctx.arg.randomizeSectionPadding);
PhdrEntry *curPtLoad = nullptr;
for (OutputSection *os : ctx.outputSections) {
if (!canInsertPadding(os))
continue;
for (SectionCommand *bc : os->commands) {
if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
SmallVector<InputSection *, 0> tmp;
if (os->ptLoad != curPtLoad) {
tmp.push_back(make<RandomizePaddingSection>(
ctx, g() % ctx.arg.maxPageSize, os));
curPtLoad = os->ptLoad;
}
for (InputSection *isec : isd->sections) {
// Probability of inserting padding is 1 in 16.
if (g() % 16 == 0)
tmp.push_back(
make<RandomizePaddingSection>(ctx, isec->addralign, os));
tmp.push_back(isec);
}
isd->sections = std::move(tmp);
}
}
}
}

// We need to generate and finalize the content that depends on the address of
// InputSections. As the generation of the content may also alter InputSection
// addresses we must converge to a fixed point. We do that here. See the comment
Expand All @@ -1470,6 +1503,9 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
if (ctx.arg.emachine == EM_HEXAGON)
hexagonTLSSymbolUpdate(ctx);

if (ctx.arg.randomizeSectionPadding)
randomizeSectionPadding(ctx);

uint32_t pass = 0, assignPasses = 0;
for (;;) {
bool changed = ctx.target->needsThunks
Expand Down
9 changes: 9 additions & 0 deletions lld/docs/ld.lld.1
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ and
.It Fl -pop-state
Restore the states saved by
.Fl -push-state.
.It Fl -randomize-section-padding Ns = Ns Ar seed
Randomly insert padding between input sections using the given seed.
Padding is inserted into output sections with names matching the following patterns:
.Cm .bss ,
.Cm .data ,
.Cm .data.rel.ro ,
.Cm .rodata
and
.Cm .text* .
.It Fl --relax-gp
Enable global pointer relaxation for RISC-V.
.It Fl -relocatable , Fl r
Expand Down
88 changes: 88 additions & 0 deletions lld/test/ELF/randomize-section-padding.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o

## --randomize-section-padding= inserts segment offset padding and pre-section
## padding, and does not affect flags. Segment offset padding is only inserted
## when PT_LOAD changes, as shown by .bss size (.data and .bss share a PT_LOAD).

# RUN: ld.lld --randomize-section-padding=6 %t.o -o %t.out
# RUN: llvm-readelf -sS -x .rodata -x .text -x .data %t.out | FileCheck --check-prefix=PAD6 %s

# PAD6: .rodata PROGBITS 0000000000200158 000158 000b8d 00 A 0 0 1
# PAD6: .text PROGBITS 0000000000201ce8 000ce8 000270 00 AX 0 0 4
# PAD6: .data PROGBITS 0000000000202f58 000f58 000941 00 WA 0 0 1
# PAD6: .bss NOBITS 0000000000203899 001899 000003 00 WA 0 0 1

# PAD6: 0000000000203899 0 NOTYPE LOCAL DEFAULT 4 a
# PAD6: 000000000020389a 0 NOTYPE LOCAL DEFAULT 4 b
# PAD6: 000000000020389b 0 NOTYPE LOCAL DEFAULT 4 c

# PAD6: Hex dump of section '.rodata':
# PAD6: 0x00200cd8 00000000 00000000 00000102 03
# PAD6: Hex dump of section '.text':
# PAD6: 0x00201f48 cccccccc cccccccc cccccccc 0405cc06
# PAD6: Hex dump of section '.data':
# PAD6: 0x00203888 00000000 00000000 00000000 00000708
# PAD6: 0x00203898 09

## Size of segment offset padding and location of pre-section padding is
## dependent on the seed.

# RUN: ld.lld --randomize-section-padding=46 %t.o -o %t.out
# RUN: llvm-readelf -sS -x .rodata -x .text -x .data %t.out | FileCheck --check-prefix=PAD46 %s

# PAD46: .rodata PROGBITS 0000000000200158 000158 000cc0 00 A 0 0 1
# PAD46: .text PROGBITS 0000000000201e18 000e18 0009bf 00 AX 0 0 4
# PAD46: .data PROGBITS 00000000002037d7 0017d7 000540 00 WA 0 0 1
# PAD46: .bss NOBITS 0000000000203d17 001d17 000004 00 WA 0 0 1

# PAD46: 0000000000203d17 0 NOTYPE LOCAL DEFAULT 4 a
# PAD46: 0000000000203d18 0 NOTYPE LOCAL DEFAULT 4 b
# PAD46: 0000000000203d1a 0 NOTYPE LOCAL DEFAULT 4 c

# PAD46: Hex dump of section '.rodata':
# PAD46: 0x00200e08 00000000 00000000 00000000 00010203
# PAD46: Hex dump of section '.text':
# PAD46: 0x002027c8 cccccccc cccccccc cccccccc 040506
# PAD46: Hex dump of section '.data':
# PAD46: 0x00203d07 00000000 00000000 00000000 07000809

.section .rodata.a,"a",@progbits
.byte 1

.section .rodata.b,"a",@progbits
.byte 2

.section .rodata.c,"a",@progbits
.byte 3

.section .text.a,"ax",@progbits
.byte 4

.section .text.b,"ax",@progbits
.byte 5

.section .text.c,"ax",@progbits
.byte 6

.section .data.a,"aw",@progbits
.byte 7

.section .data.b,"aw",@progbits
.byte 8

.section .data.c,"aw",@progbits
.byte 9

.section .bss.a,"a",@nobits
a:
.zero 1

.section .bss.b,"a",@nobits
b:
.zero 1

.section .bss.c,"a",@nobits
c:
.zero 1

Loading