Skip to content

[ELF] Add --default-script/-dT #89327

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 2 commits into from
Apr 19, 2024

Conversation

MaskRay
Copy link
Member

@MaskRay MaskRay commented Apr 18, 2024

GNU ld added --default-script (alias: -dT) in 2007. The option specifies
a default script that is processed if --script/-T is not specified. -dT
can be used to override GNU ld's internal linker script, but only when
the application does not specify -T.
In addition, dynamorio's CMakeLists.txt may use -dT.

The implementation is simple and the feature can be useful to dabble
with different section layouts.

Created using spr 1.3.5-bogner
@llvmbot
Copy link
Member

llvmbot commented Apr 18, 2024

@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-elf

Author: Fangrui Song (MaskRay)

Changes

GNU ld added --default-script (alias: -dT) in 2007. The option specifies
a default script that is processed if --script/-T is not specified. Some
users use this to override GNU ld's internal linker script, but only
when the application does not specify -T.
In addition, dynamorio's CMakeLists.txt may use -dT.

The implementation is simple and the feature can be useful to dabble
with different section layouts.


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

5 Files Affected:

  • (modified) lld/ELF/Driver.cpp (+13-3)
  • (modified) lld/ELF/DriverUtils.cpp (+1)
  • (modified) lld/ELF/Options.td (+3)
  • (added) lld/test/ELF/linkerscript/default-script.s (+63)
  • (modified) lld/test/ELF/reproduce.s (+3-1)
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 5fffdc51f34ddf..a5b47f020f8726 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1856,8 +1856,9 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
   std::vector<std::tuple<bool, bool, bool>> stack;
 
   // Iterate over argv to process input files and positional arguments.
+  std::optional<MemoryBufferRef> defaultScript;
   InputFile::isInGroup = false;
-  bool hasInput = false;
+  bool hasInput = false, hasScript = false;
   for (auto *arg : args) {
     switch (arg->getOption().getID()) {
     case OPT_library:
@@ -1879,9 +1880,16 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
       break;
     }
     case OPT_script:
+    case OPT_default_script:
       if (std::optional<std::string> path = searchScript(arg->getValue())) {
-        if (std::optional<MemoryBufferRef> mb = readFile(*path))
-          readLinkerScript(*mb);
+        if (std::optional<MemoryBufferRef> mb = readFile(*path)) {
+          if (arg->getOption().matches(OPT_default_script)) {
+            defaultScript = mb;
+          } else {
+            readLinkerScript(*mb);
+            hasScript = true;
+          }
+        }
         break;
       }
       error(Twine("cannot find linker script ") + arg->getValue());
@@ -1961,6 +1969,8 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
     }
   }
 
+  if (defaultScript && !hasScript)
+    readLinkerScript(*defaultScript);
   if (files.empty() && !hasInput && errorCount() == 0)
     error("no input files");
 }
diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index de8ffab0e21249..ac74604408152d 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -186,6 +186,7 @@ std::string elf::createResponseFile(const opt::InputArgList &args) {
       os << arg->getSpelling() << quote(rewritePath(arg->getValue())) << "\n";
       break;
     case OPT_call_graph_ordering_file:
+    case OPT_default_script:
     case OPT_dynamic_list:
     case OPT_export_dynamic_symbol_list:
     case OPT_just_symbols:
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index d470646ed0eeef..72eaf157a181cf 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -157,6 +157,8 @@ defm debug_names: BB<"debug-names",
     "Generate a merged .debug_names section",
     "Do not generate a merged .debug_names section (default)">;
 
+defm default_script: EEq<"default-script", "In the absence of --script, read this default linker script">;
+
 defm demangle: B<"demangle",
     "Demangle symbol names (default)",
     "Do not demangle symbol names">;
@@ -555,6 +557,7 @@ HelpText<"Format diagnostics for Visual Studio compatibility">;
 def package_metadata: JJ<"package-metadata=">, HelpText<"Emit package metadata note">;
 
 // Aliases
+def: Separate<["-"], "dT">, Alias<default_script>, HelpText<"Alias for --default-script">;
 def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
 def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
 def: F<"dy">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
diff --git a/lld/test/ELF/linkerscript/default-script.s b/lld/test/ELF/linkerscript/default-script.s
new file mode 100644
index 00000000000000..f5650dabfdd865
--- /dev/null
+++ b/lld/test/ELF/linkerscript/default-script.s
@@ -0,0 +1,63 @@
+# REQUIRES: x86
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+# RUN: ld.lld --default-script=def.t b.t -T a.t a.o -o out
+# RUN: llvm-readelf -Ss out | FileCheck %s
+
+# CHECK:      Name
+# CHECK:      .foo2
+# CHECK-NEXT: .foo0
+# CHECK-NEXT: .foo1
+# CHECK:      1: 000000000000000c     0 NOTYPE  GLOBAL DEFAULT     4 _start
+# CHECK-NEXT: 2: 000000000000002a     0 NOTYPE  GLOBAL DEFAULT   ABS b
+# CHECK-NEXT: 3: 000000000000002a     0 NOTYPE  GLOBAL DEFAULT   ABS a
+# CHECK-EMPTY:
+
+## In the absence of --script options, the default linker script is read.
+# RUN: ld.lld --default-script def.t b.t a.o -o out1
+# RUN: llvm-readelf -Ss out1 | FileCheck %s --check-prefix=CHECK1
+# RUN: ld.lld -dT def.t b.t a.o -o out1a && cmp out1 out1a
+## The last -dT wins.
+# RUN: ld.lld -dT a.t -dT def.t b.t a.o -o out1a && cmp out1 out1a
+
+# RUN: mkdir d && cp def.t d/default.t
+# RUN: ld.lld -L d -dT default.t b.t a.o -o out1a && cmp out1 out1a
+
+# CHECK1:      Name
+# CHECK1:      .foo2
+# CHECK1-NEXT: .foo1
+# CHECK1-NEXT: .foo0
+# CHECK1:      1: 000000000000000c     0 NOTYPE  GLOBAL DEFAULT     4 _start
+# CHECK1-NEXT: 2: 000000000000002a     0 NOTYPE  GLOBAL DEFAULT   ABS b
+# CHECK1-NEXT: 3: 000000000000002a     0 NOTYPE  GLOBAL DEFAULT   ABS def
+# CHECK1-EMPTY:
+
+# RUN: not ld.lld --default-script not-exist.t b.t -T a.t a.o 2>&1 | FileCheck %s --check-prefix=ERR
+# ERR: error: cannot find linker script not-exist.t
+
+#--- a.s
+.globl _start
+_start:
+
+.section .foo0,"a"; .long 0
+.section .foo1,"a"; .long 0
+.section .foo2,"a"; .long 0
+
+#--- a.t
+a = 42;
+SECTIONS {
+  .foo2 : {}
+  .foo0 : {}
+  .foo1 : {}
+}
+
+#--- b.t
+b = 42;
+
+#--- def.t
+def = 42;
+SECTIONS {
+  .foo2 : {}
+  .foo1 : {}
+  .foo0 : {}
+}
diff --git a/lld/test/ELF/reproduce.s b/lld/test/ELF/reproduce.s
index 314c08b4f7cdd5..8818a9e35f4039 100644
--- a/lld/test/ELF/reproduce.s
+++ b/lld/test/ELF/reproduce.s
@@ -34,10 +34,11 @@
 # RUN: cp dyn dyn2
 # RUN: echo > file
 # RUN: echo > file2
+# RUN: echo > file3
 # RUN: echo "_start" > order
 # RUN: mkdir "sysroot with spaces"
 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o 'foo bar'
-# RUN: ld.lld --reproduce repro3.tar 'foo bar' -L"foo bar" -Lfile -Tfile2 \
+# RUN: ld.lld --reproduce repro3.tar 'foo bar' -L"foo bar" -Lfile -Tfile2 -dT file3 \
 # RUN:   --dynamic-list dyn --export-dynamic-symbol-list dyn2 -rpath file --script=file --symbol-ordering-file order \
 # RUN:   --sysroot "sysroot with spaces" --sysroot="sysroot with spaces" \
 # RUN:   --version-script ver --dynamic-linker "some unusual/path" -soname 'foo bar' \
@@ -48,6 +49,7 @@
 # RSP3-NEXT: -L "[[BASEDIR:.+]]/foo bar"
 # RSP3-NEXT: -L [[BASEDIR]]/file
 # RSP3-NEXT: --script [[BASEDIR]]/file2
+# RSP3-NEXT: --default-script [[BASEDIR]]/file3
 # RSP3-NEXT: --dynamic-list [[BASEDIR]]/dyn
 # RSP3-NEXT: --export-dynamic-symbol-list [[BASEDIR]]/dyn2
 # RSP3-NEXT: -rpath [[BASEDIR]]/file

Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

Code changes LGTM, I think it will be worth adding the option to the man page too.

Created using spr 1.3.5-bogner
@MaskRay MaskRay merged commit f02a27d into main Apr 19, 2024
@MaskRay MaskRay deleted the users/MaskRay/spr/elf-add-default-script-dt branch April 19, 2024 16:09
aniplcc pushed a commit to aniplcc/llvm-project that referenced this pull request Apr 21, 2024
GNU ld added --default-script (alias: -dT) in 2007. The option specifies
a default script that is processed if --script/-T is not specified. -dT
can be used to override GNU ld's internal linker script, but only when
the application does not specify -T.
In addition, dynamorio's CMakeLists.txt may use -dT.

The implementation is simple and the feature can be useful to dabble
with different section layouts.

Pull Request: llvm#89327
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.

3 participants