Skip to content

Conversation

@pskrgag
Copy link
Contributor

@pskrgag pskrgag commented May 15, 2025

LinkerScript::allocateHeaders() checks if PHDR segment is located below minimal virtual address provided in linker script. However user could pass section bases via -T command line argument, which may lead to unsorted (or overlapping) segments in resulting elf.

To fix it, check PHDR segment when sectionStartMap is not empty, which contains bases passed via -T argument

Closes #138584

@llvmbot
Copy link
Member

llvmbot commented May 15, 2025

@llvm/pr-subscribers-lld

Author: Pavel Skripkin (pskrgag)

Changes

LinkerScript::allocateHeaders() checks if PHDR segment is located below minimal virtual address provided in linker script. However user could pass section bases via -T command line argument, which may lead to unsorted (or overlapping) segments in resulting elf.

To fix it, check PHDR segment when sectionStartMap is not empty, which contains bases passed via -T argument

Closes #138584


Full diff: https://github.com/llvm/llvm-project/pull/140126.diff

3 Files Affected:

  • (modified) lld/ELF/Writer.cpp (+1-1)
  • (modified) lld/test/ELF/sectionstart.s (+24)
  • (modified) lld/test/ELF/ttext-tdata-tbss.s (-2)
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 28b24f90716b8..a11bc76a0fd18 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -331,7 +331,7 @@ template <class ELFT> void Writer<ELFT>::run() {
   for (OutputSection *sec : ctx.outputSections)
     sec->maybeCompress<ELFT>(ctx);
 
-  if (ctx.script->hasSectionsCommand)
+  if (ctx.script->hasSectionsCommand || !ctx.arg.sectionStartMap.empty())
     ctx.script->allocateHeaders(ctx.mainPart->phdrs);
 
   // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
diff --git a/lld/test/ELF/sectionstart.s b/lld/test/ELF/sectionstart.s
index d694c9375fd80..8eb50263fe6aa 100644
--- a/lld/test/ELF/sectionstart.s
+++ b/lld/test/ELF/sectionstart.s
@@ -11,6 +11,30 @@
 # CHECK-NEXT:    2 .data         00000004 0000000000110000 DATA
 # CHECK-NEXT:    3 .bss          00000004 0000000000200000 BSS
 
+## Check that PHDRS are allocated below .text if .text is below default
+## base for non-pie case
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -Ttext=0x3000 -o %t
+# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CHECK-TEXT %s
+
+# CHECK-TEXT: Program Headers:
+# CHECK-TEXT-NEXT:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# CHECK-TEXT-NEXT:  PHDR 0x000040 0x0000000000002040 0x0000000000002040 0x000118 0x000118 R 0x8
+# CHECK-TEXT-NEXT:  LOAD 0x000000 0x0000000000002000 0x0000000000002000 0x000158 0x000158 R 0x1000
+# CHECK-TEXT-NEXT:  LOAD 0x001000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 R E 0x1000
+
+## Check that PHDRS are deleted if they don't fit
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -Ttext=0x0 -o %t
+# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CHECK-TEXT-ZERO %s
+
+# CHECK-TEXT-ZERO: Program Headers:
+# CHECK-TEXT-ZERO-NEXT:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# CHECK-TEXT-ZERO-NEXT:  LOAD 0x001000 0x0000000000000000 0x0000000000000000 0x000001 0x000001 R E 0x1000
+# CHECK-TEXT-ZERO-NEXT:  LOAD 0x001001 0x0000000000001001 0x0000000000001001 0x000004 0x000008 RW 0x1000
+
 ## The same, but dropped "0x" prefix.
 # RUN: ld.lld %t.o --section-start .text=100000 \
 # RUN:   --section-start .data=110000 --section-start .bss=0x200000 -o %t1
diff --git a/lld/test/ELF/ttext-tdata-tbss.s b/lld/test/ELF/ttext-tdata-tbss.s
index c8254d6969293..01b6e51718b99 100644
--- a/lld/test/ELF/ttext-tdata-tbss.s
+++ b/lld/test/ELF/ttext-tdata-tbss.s
@@ -24,8 +24,6 @@
 # USER1-NEXT: .rodata PROGBITS 0000000000009008 002008 000008
 # USER1-NEXT: .aw     PROGBITS 000000000000a010 002010 000008
 # USER1:      Type
-# USER1-NEXT: PHDR 0x000040 0x0000000000200040
-# USER1-NEXT: LOAD 0x000000 0x0000000000200000
 # USER1-NEXT: LOAD 0x001000 0x0000000000000000
 
 ## Specify --image-base to make program headers look normal.

@llvmbot
Copy link
Member

llvmbot commented May 15, 2025

@llvm/pr-subscribers-lld-elf

Author: Pavel Skripkin (pskrgag)

Changes

LinkerScript::allocateHeaders() checks if PHDR segment is located below minimal virtual address provided in linker script. However user could pass section bases via -T command line argument, which may lead to unsorted (or overlapping) segments in resulting elf.

To fix it, check PHDR segment when sectionStartMap is not empty, which contains bases passed via -T argument

Closes #138584


Full diff: https://github.com/llvm/llvm-project/pull/140126.diff

3 Files Affected:

  • (modified) lld/ELF/Writer.cpp (+1-1)
  • (modified) lld/test/ELF/sectionstart.s (+24)
  • (modified) lld/test/ELF/ttext-tdata-tbss.s (-2)
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 28b24f90716b8..a11bc76a0fd18 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -331,7 +331,7 @@ template <class ELFT> void Writer<ELFT>::run() {
   for (OutputSection *sec : ctx.outputSections)
     sec->maybeCompress<ELFT>(ctx);
 
-  if (ctx.script->hasSectionsCommand)
+  if (ctx.script->hasSectionsCommand || !ctx.arg.sectionStartMap.empty())
     ctx.script->allocateHeaders(ctx.mainPart->phdrs);
 
   // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
diff --git a/lld/test/ELF/sectionstart.s b/lld/test/ELF/sectionstart.s
index d694c9375fd80..8eb50263fe6aa 100644
--- a/lld/test/ELF/sectionstart.s
+++ b/lld/test/ELF/sectionstart.s
@@ -11,6 +11,30 @@
 # CHECK-NEXT:    2 .data         00000004 0000000000110000 DATA
 # CHECK-NEXT:    3 .bss          00000004 0000000000200000 BSS
 
+## Check that PHDRS are allocated below .text if .text is below default
+## base for non-pie case
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -Ttext=0x3000 -o %t
+# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CHECK-TEXT %s
+
+# CHECK-TEXT: Program Headers:
+# CHECK-TEXT-NEXT:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# CHECK-TEXT-NEXT:  PHDR 0x000040 0x0000000000002040 0x0000000000002040 0x000118 0x000118 R 0x8
+# CHECK-TEXT-NEXT:  LOAD 0x000000 0x0000000000002000 0x0000000000002000 0x000158 0x000158 R 0x1000
+# CHECK-TEXT-NEXT:  LOAD 0x001000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 R E 0x1000
+
+## Check that PHDRS are deleted if they don't fit
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -Ttext=0x0 -o %t
+# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CHECK-TEXT-ZERO %s
+
+# CHECK-TEXT-ZERO: Program Headers:
+# CHECK-TEXT-ZERO-NEXT:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# CHECK-TEXT-ZERO-NEXT:  LOAD 0x001000 0x0000000000000000 0x0000000000000000 0x000001 0x000001 R E 0x1000
+# CHECK-TEXT-ZERO-NEXT:  LOAD 0x001001 0x0000000000001001 0x0000000000001001 0x000004 0x000008 RW 0x1000
+
 ## The same, but dropped "0x" prefix.
 # RUN: ld.lld %t.o --section-start .text=100000 \
 # RUN:   --section-start .data=110000 --section-start .bss=0x200000 -o %t1
diff --git a/lld/test/ELF/ttext-tdata-tbss.s b/lld/test/ELF/ttext-tdata-tbss.s
index c8254d6969293..01b6e51718b99 100644
--- a/lld/test/ELF/ttext-tdata-tbss.s
+++ b/lld/test/ELF/ttext-tdata-tbss.s
@@ -24,8 +24,6 @@
 # USER1-NEXT: .rodata PROGBITS 0000000000009008 002008 000008
 # USER1-NEXT: .aw     PROGBITS 000000000000a010 002010 000008
 # USER1:      Type
-# USER1-NEXT: PHDR 0x000040 0x0000000000200040
-# USER1-NEXT: LOAD 0x000000 0x0000000000200000
 # USER1-NEXT: LOAD 0x001000 0x0000000000000000
 
 ## Specify --image-base to make program headers look normal.

sec->maybeCompress<ELFT>(ctx);

if (ctx.script->hasSectionsCommand)
if (ctx.script->hasSectionsCommand || !ctx.arg.sectionStartMap.empty())
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should call allocateHeaders for the additional condition.

The user might specify --section-start to increase the address of a section. It should not change ELF header/program headers 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.

Ok, thanks for review! Closing pr in favor of #140187

@pskrgag pskrgag closed this May 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[lld] Unsorted loadable program headers with -Ttext= argument

3 participants