[LLD][ELF] Allow merging XO and RX sections, and add --[no-]xosegment flag (#132412)

Following from the discussion in #132224, this seems like the best
approach to deal with a mix of XO and RX output sections in the same
binary. This change will also simplify the implementation of the
PURECODE section flag for AArch64.

To control this behaviour, the `--[no-]xosegment` flag is added to LLD
(similarly to `--[no-]rosegment`), which determines whether to allow
merging XO and RX sections in the same segment. The default value is
`--no-xosegment`, which is a breaking change compared to the previous
behaviour.

Release notes are also added, since this will be a breaking change.
This commit is contained in:
Csanád Hajdú 2025-04-08 08:47:51 +02:00 committed by GitHub
parent 0d19efa9d5
commit 2c1bdd4a08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 138 additions and 8 deletions

View File

@ -341,6 +341,7 @@ struct Config {
llvm::DenseSet<llvm::StringRef> saveTempsArgs;
llvm::SmallVector<std::pair<llvm::GlobPattern, uint32_t>, 0> shuffleSections;
bool singleRoRx;
bool singleXoRx;
bool shared;
bool symbolic;
bool isStatic = false;

View File

@ -1485,6 +1485,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
ctx.arg.randomizeSectionPadding =
args::getInteger(args, OPT_randomize_section_padding, 0);
ctx.arg.singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true);
ctx.arg.singleXoRx = !args.hasFlag(OPT_xosegment, OPT_no_xosegment, false);
ctx.arg.soName = args.getLastArgValue(OPT_soname);
ctx.arg.sortSection = getSortSection(ctx, args);
ctx.arg.splitStackAdjustSize =

View File

@ -432,6 +432,10 @@ defm rosegment: BB<"rosegment",
"Put read-only non-executable sections in their own segment (default)",
"Do not put read-only non-executable sections in their own segment">;
defm xosegment: BB<"xosegment",
"Put execute-only sections in their own segment",
"Do not put execute-only sections in their own segment (default)">;
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;

View File

@ -2379,10 +2379,16 @@ Writer<ELFT>::createPhdrs(Partition &part) {
// so when hasSectionsCommand, since we cannot introduce the extra alignment
// needed to create a new LOAD)
uint64_t newFlags = computeFlags(ctx, sec->getPhdrFlags());
// When --no-rosegment is specified, RO and RX sections are compatible.
uint32_t incompatible = flags ^ newFlags;
if (ctx.arg.singleRoRx && !(newFlags & PF_W))
incompatible &= ~PF_X;
uint64_t incompatible = flags ^ newFlags;
if (!(newFlags & PF_W)) {
// When --no-rosegment is specified, RO and RX sections are compatible.
if (ctx.arg.singleRoRx)
incompatible &= ~PF_X;
// When --no-xosegment is specified (the default), XO and RX sections are
// compatible.
if (ctx.arg.singleXoRx)
incompatible &= ~PF_R;
}
if (incompatible)
load = nullptr;

View File

@ -42,8 +42,16 @@ ELF Improvements
* Linker script ``OVERLAY`` descriptions now support virtual memory regions
(e.g. ``>region``) and ``NOCROSSREFS``.
* Added ``--xosegment`` and ``--no-xosegment`` flags to control whether to place
executable-only and readable-executable sections in the same segment. The
default value is ``--no-xosegment``.
(`#132412 <https://github.com/llvm/llvm-project/pull/132412>`_)
Breaking changes
----------------
* Executable-only and readable-executable sections are now allowed to be placed
in the same segment by default. Pass ``--xosegment`` to lld in order to get
the old behavior back.
COFF Improvements
-----------------

View File

@ -0,0 +1,55 @@
// REQUIRES: aarch64
// RUN: rm -rf %t && split-file %s %t && cd %t
// RUN: llvm-mc -filetype=obj -triple=aarch64 start.s -o start.o
// RUN: llvm-mc -filetype=obj -triple=aarch64 xo.s -o xo.o
// RUN: llvm-mc -filetype=obj -triple=aarch64 rx.s -o rx.o
// RUN: ld.lld start.o xo.o -o xo
// RUN: ld.lld start.o rx.o -o rx-default
// RUN: ld.lld --xosegment start.o rx.o -o rx-xosegment
// RUN: ld.lld --no-xosegment start.o rx.o -o rx-no-xosegment
// RUN: llvm-readelf -l xo | FileCheck --check-prefix=CHECK-XO %s
// RUN: llvm-readelf -l rx-default | FileCheck --check-prefix=CHECK-MERGED %s
// RUN: llvm-readelf -l rx-xosegment | FileCheck --check-prefix=CHECK-SEPARATE %s
// RUN: llvm-readelf -l rx-no-xosegment | FileCheck --check-prefix=CHECK-MERGED %s
// CHECK-XO: PHDR
// CHECK-XO-NEXT: LOAD
// CHECK-XO-NEXT: LOAD 0x000120 0x0000000000210120 0x0000000000210120 0x00000c 0x00000c E 0x10000
/// Index should match the index of the LOAD segment above.
// CHECK-XO: 02 .text .foo
// CHECK-MERGED: PHDR
// CHECK-MERGED-NEXT: LOAD
// CHECK-MERGED-NEXT: LOAD 0x000120 0x0000000000210120 0x0000000000210120 0x00000c 0x00000c R E 0x10000
/// Index should match the index of the LOAD segment above.
// CHECK-MERGED: 02 .text .foo
// CHECK-SEPARATE: PHDR
// CHECK-SEPARATE-NEXT: LOAD
// CHECK-SEPARATE-NEXT: LOAD 0x000158 0x0000000000210158 0x0000000000210158 0x000008 0x000008 E 0x10000
// CHECK-SEPARATE-NEXT: LOAD 0x000160 0x0000000000220160 0x0000000000220160 0x000004 0x000004 R E 0x10000
/// Index should match the index of the LOAD segment above.
// CHECK-SEPARATE: 02 .text
// CHECK-SEPARATE: 03 .foo
//--- start.s
.section .text,"axy",@progbits,unique,0
.global _start
_start:
bl foo
ret
//--- xo.s
.section .foo,"axy",@progbits,unique,0
.global foo
foo:
ret
//--- rx.s
/// Ensure that the implicitly-created .text section has the SHF_AARCH64_PURECODE flag.
.section .text,"axy",@progbits,unique,0
.section .foo,"ax",@progbits,unique,0
.global foo
foo:
ret

View File

@ -1,12 +1,12 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
// RUN: ld.lld %t.o -o %t.so -shared
// RUN: ld.lld --xosegment %t.o -o %t.so -shared
// RUN: llvm-readelf -l %t.so | FileCheck --implicit-check-not=LOAD %s
// RUN: echo ".section .foo,\"ax\"; ret" > %t.s
// RUN: llvm-mc -filetype=obj -triple=aarch64 %t.s -o %t2.o
// RUN: ld.lld %t.o %t2.o -o %t.so -shared
// RUN: ld.lld --xosegment %t.o %t2.o -o %t.so -shared
// RUN: llvm-readelf -l %t.so | FileCheck --check-prefix=DIFF --implicit-check-not=LOAD %s
// CHECK: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x10000

View File

@ -0,0 +1,55 @@
// REQUIRES: arm
// RUN: rm -rf %t && split-file %s %t && cd %t
// RUN: llvm-mc -filetype=obj -triple=armv7 start.s -o start.o
// RUN: llvm-mc -filetype=obj -triple=armv7 xo.s -o xo.o
// RUN: llvm-mc -filetype=obj -triple=armv7 rx.s -o rx.o
// RUN: ld.lld start.o xo.o -o xo
// RUN: ld.lld start.o rx.o -o rx-default
// RUN: ld.lld --xosegment start.o rx.o -o rx-xosegment
// RUN: ld.lld --no-xosegment start.o rx.o -o rx-no-xosegment
// RUN: llvm-readelf -l xo | FileCheck --check-prefix=CHECK-XO %s
// RUN: llvm-readelf -l rx-default | FileCheck --check-prefix=CHECK-MERGED %s
// RUN: llvm-readelf -l rx-xosegment | FileCheck --check-prefix=CHECK-SEPARATE %s
// RUN: llvm-readelf -l rx-no-xosegment | FileCheck --check-prefix=CHECK-MERGED %s
// CHECK-XO: PHDR
// CHECK-XO-NEXT: LOAD
// CHECK-XO-NEXT: LOAD 0x0000b4 0x000200b4 0x000200b4 0x0000c 0x0000c E 0x10000
/// Index should match the index of the LOAD segment above.
// CHECK-XO: 02 .text .foo
// CHECK-MERGED: PHDR
// CHECK-MERGED-NEXT: LOAD
// CHECK-MERGED-NEXT: LOAD 0x0000b4 0x000200b4 0x000200b4 0x0000c 0x0000c R E 0x10000
/// Index should match the index of the LOAD segment above.
// CHECK-MERGED: 02 .text .foo
// CHECK-SEPARATE: PHDR
// CHECK-SEPARATE-NEXT: LOAD
// CHECK-SEPARATE-NEXT: LOAD 0x0000d4 0x000200d4 0x000200d4 0x00008 0x00008 E 0x10000
// CHECK-SEPARATE-NEXT: LOAD 0x0000dc 0x000300dc 0x000300dc 0x00004 0x00004 R E 0x10000
/// Index should match the index of the LOAD segment above.
// CHECK-SEPARATE: 02 .text
// CHECK-SEPARATE: 03 .foo
//--- start.s
.section .text,"axy",%progbits,unique,0
.global _start
_start:
bl foo
bx lr
//--- xo.s
.section .foo,"axy",%progbits,unique,0
.global foo
foo:
bx lr
//--- rx.s
/// Ensure that the implicitly-created .text section has the SHF_ARM_PURECODE flag.
.section .text,"axy",%progbits,unique,0
.section .foo,"ax",%progbits,unique,0
.global foo
foo:
bx lr

View File

@ -1,13 +1,13 @@
// REQUIRES: arm
// RUN: llvm-mc -filetype=obj -triple=armv7-pc-linux %s -o %t.o
// RUN: ld.lld %t.o -o %t.so -shared
// RUN: ld.lld --xosegment %t.o -o %t.so -shared
// RUN: llvm-readelf -l %t.so | FileCheck --implicit-check-not=LOAD %s
// RUN: echo ".section .foo,\"ax\"; \
// RUN: bx lr" > %t.s
// RUN: llvm-mc -filetype=obj -triple=armv7-pc-linux %t.s -o %t2.o
// RUN: ld.lld %t.o %t2.o -o %t.so -shared
// RUN: ld.lld --xosegment %t.o %t2.o -o %t.so -shared
// RUN: llvm-readelf -l %t.so | FileCheck --check-prefix=DIFF --implicit-check-not=LOAD %s
// CHECK: LOAD 0x000000 0x00000000 0x00000000 0x0016d 0x0016d R 0x10000