Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 3 additions & 3 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,9 +629,9 @@ static bool isRelroSection(Ctx &ctx, const OutputSection *sec) {
// magic section names.
StringRef s = sec->name;

bool abiAgnostic = s == ".data.rel.ro" || s == ".bss.rel.ro" ||
s == ".ctors" || s == ".dtors" || s == ".jcr" ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposal also mentions adding ".hot" to the end of section names, so maybe it would be better to do something like this:
s.starts_with(".data.rel.ro") || s.starts_with(".bss.rel.ro" || s == ".ctors" ...

Do we also need to slightly revisit the behaviour of LinkerScript::getOutputSectionName to not merge these segments when a linker script is not provided? I'll note that there is an option which keeps some .text-prefixed sections separated, which we might want to extend to data?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for taking a look and sharing the thoughts @lenary ! I replied inline. Let me know your thoughts and I'd be glad to follow up!

The proposal also mentions adding ".hot" to the end of section names

yeah, for the prototype in the rfc, .rodata is indeed partitioned into .rodata.hot, rodata.unlikely and .rodata, and most of the prototyping work are (and will be) kept (e.g., in the codegen pass and asmprinter).

However to have fewer elf sections (4 more instead of 8 more) after partitioning 4 sections, the implementation changed to place cold data in <section>.unlikely and keeping the rest in <section>, and there is no <section>.hot in the eventual executable. Both .hot and .unlikely suffixes are kept in intermediate object files though (more on this on linker-script vs keep-data-section-prefix discussion)

so maybe it would be better to do something like this:: s.starts_with(".data.rel.ro") || s.starts_with(".bss.rel.ro")

The updated change handles both .hot and .unlikely suffix for .data.rel.ro only. The motivation to leave out other relro sections is that the amount of accesses from those sections are very trivial (e.g., 0.002% dTLB miss is from .bss.rel.ro, with 3e-7 accesses). We can get simpler binary with fewer partitioned elf sections, and it doesn't really miss performance opportunities.

Do we also need to slightly revisit the behaviour of LinkerScript::getOutputSectionName to not merge these segments when a linker script is not provided? I'll note that there is an option which keeps some .text-prefixed sections separated, which we might want to extend to data?

For text it'd be

if (ctx.arg.zKeepTextSectionPrefix)
for (StringRef v : {".text.hot", ".text.unknown", ".text.unlikely",
".text.startup", ".text.exit", ".text.split"})
if (isSectionPrefix(v.substr(5), s->name.substr(5)))
return v;
. There isn't an immediate use case to revisit the behavior in upstream lld for the binaries I work on. But I think it's sensible to have a similar functionality for data. Are you interested in extending
if (ctx.arg.zKeepTextSectionPrefix)
for (StringRef v : {".text.hot", ".text.unknown", ".text.unlikely",
".text.startup", ".text.exit", ".text.split"})
if (isSectionPrefix(v.substr(5), s->name.substr(5)))
return v;
for data or having a use case?

fwiw, one upside about using linker script is that it provides flexible section mapping and section ordering besides grouping.

  • By section mapping, symbols in the intermediate object file have .hot, .unlikely or no suffix , and linker script can map intermediate unlikely to executable <section>.unlikely, and map .hot and no-suffix ones to executable <section>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The motivation to leave out other relro sections is that the amount of accesses from those sections are very trivial

Ok, sounds good, I'm happy to ignore .bss.rel.ro.

I think there are three use-cases that are relevant:

  1. A user that doesn't care about this feature, but might have input files containing .data.rel.ro.unlikely or .data.rel.ro.<something> (maybe from a static library they don't control). They don't use a linker script or provide any custom options to the linker. In this case, all the .data.rel.ro.* input sections should be merged, I think like they currently are.

  2. A user that uses this feature, but doesn't want to really care about layout. They would use your -z keep-data-section-prefix option, which would put the .data.rel.ro.unlikely and .data.rel.ro input sections into separate output sections in the final executable.

  3. A user that uses this feature and deeply cares about exactly where these sections are placed, so uses a linker script to manually place .data.rel.ro and .data.rel.ro.unlikely. So yes they get to care about both address assignment and grouping as you have pointed out.

I expect (but cannot verify) that there would be no problem having lots of .data.rel.ro.* (output) sections in an ELF executable, but I do realise we want to keep to just one RELRO segment (program header), and likely just one .relro_padding.

These usecases effectively echo the same situation for .text.hot and .text.unlikely - if you don't care, you do nothing and get one .text section, if you care a little you can use a flag (and the flag tries to do something reasonable: several .text.* sections, but still only one executable segment), and if you care a lot you can use a linker script to assign addresses for your .text.* sections, and potentially change the section->segment mapping too.

These same 3 usecases should apply equivalently for .rodata and .data (and suffixed versions of those), which can use the same flag you've added here. Maybe that work is in a separate patch?

It's not entirely clear to me in the code how to achieve this, but I'm surprised that your handling of the option is in this function rather than in LinkerScript::getOutputSectionName, like we do for .text - but I also understand that .data.rel.ro needs a bit more processing than .text for assigning into segments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the summary! They capture the different use cases pretty well.

These same 3 usecases should apply equivalently for .rodata and .data (and suffixed versions of those), which can use the same flag you've added here. Maybe that work is in a separate patch?
It's not entirely clear to me in the code how to achieve this, but I'm surprised that your handling of the option is in this function rather than in LinkerScript::getOutputSectionName, like we do for .text - but I also understand that .data.rel.ro needs a bit more processing than .text for assigning into segments.

It's correct that LinkerScript::getOutputSectionName should be the place to implement section mapping if linker script is not adopted (e.g., for use case 2).

To clarify a little bit, this change aims to support placing .data.rel.ro.unlikely after .data.rel.ro section but doesn't implement the data section mapping. Before this change, lld will fail with error section: .relro_padding is not contiguous with other relro sections when .data.rel.ro.unlikely is placed after .data.rel.ro section, and lld/test/ELF/keep-text-section-prefix.s L19-20 has a test case.

I revived a commit as #148985 to implement the section mapping, and I plan to send it out as a separate change after adding a regression test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've sent out #148985 for review. Both this PR and #148985 introduces -z,keep-data-section-prefix option, and the relevant code will be merged once (depending on which one gets merged first).

s == ".eh_frame" || s == ".fini_array" ||
bool abiAgnostic = s == ".data.rel.ro" || s == ".data.rel.ro.unlikely" ||
s == ".bss.rel.ro" || s == ".ctors" || s == ".dtors" ||
s == ".jcr" || s == ".eh_frame" || s == ".fini_array" ||
s == ".init_array" || s == ".preinit_array";

bool abiSpecific =
Expand Down
50 changes: 50 additions & 0 deletions lld/test/ELF/relro-data-unlikely.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# REQUIRES: x86
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep-text-section-prefix.s

(Make the filename more generic in case this feature applies to .data.unlikely in the future)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o

# RUN: echo "SECTIONS { \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a few extra files. We prefer split-file for such tests. Many tests use a.o b.o out as relocatable and executable file names. Consider -o a.o ld.lld -z keep-data-section-prefix -T a.lds a.o -o out

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

# RUN: .data.rel.ro : { .data.rel.ro } \
# RUN: .data.rel.ro.unlikely : { *(.data.rel.ro.unlikely) } \
# RUN: } INSERT AFTER .text " > %t.script

# RUN: ld.lld --script=%t.script %t.o -o %t
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=SEG %s
# RUN: llvm-readelf -S %t | FileCheck %s

# There are 2 RW PT_LOAD segments. p_offset p_vaddr p_paddr p_filesz of the first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use ## for non-RUN-non-CHECK comments to make them stand out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. I'll keep this in mind for future changes.

# should match PT_GNU_RELRO.
# The .data.rel.ro.unlikely section is in PT_GNU_RELRO segment.

# Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
# SEG: LOAD 0x0001c8 0x00000000002011c8 0x00000000002011c8 0x000001 0x000001 R E 0x1000
# SEG-NEXT: LOAD 0x0001c9 0x00000000002021c9 0x00000000002021c9 0x001001 0x001e37 RW 0x1000
# SEG-NEXT: LOAD 0x0011ca 0x00000000002041ca 0x00000000002041ca 0x000001 0x000002 RW 0x1000
# SEG-NEXT: GNU_RELRO 0x0001c9 0x00000000002021c9 0x00000000002021c9 0x001001 0x001e37 R 0x1
# SEG-NEXT: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x0

# SEG: .text
# SEG-NEXT: .data.rel.ro .data.rel.ro.unlikely .relro_padding
# SEG-NEXT: .data .bss

# [Nr] Name Type Address Off Size
# CHECK: .data.rel.ro PROGBITS 00000000002021c9 0001c9 000001
# CHECK-NEXT: .data.rel.ro.unlikely PROGBITS 00000000002021ca 0001ca 001000
# CHECK-NEXT: .relro_padding NOBITS 00000000002031ca 0011ca 000e36
# CHECK-NEXT: .data PROGBITS 00000000002041ca 0011ca 000001
# CHECK-NEXT: .bss NOBITS 00000000002041cb 0011cb 000001

.globl _start
_start:
ret


.section .data.rel.ro, "aw"
.space 1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add .data.rel.ro.hot ``.data.rel.ro.split` to demonstrate the behavior

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

.section .data.rel.ro.unlikely, "aw"
.space 4096

.section .data, "aw"
.space 1

.section .bss, "aw"
.space 1
Loading