summaryrefslogtreecommitdiff
path: root/sys-devel/mold/files/mold-2.0.0-DT_RELR-dependency.patch
blob: 267afde230d2fc3617c2ad31b80ec58314c6b211 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
https://bugs.gentoo.org/911591
https://bugzilla.mozilla.org/show_bug.cgi?id=1847697
https://github.com/rui314/mold/issues/653#event-10041847648
https://github.com/rui314/mold/commit/f467ad1add2ab6e381e0e458f026df197e63d487

From f467ad1add2ab6e381e0e458f026df197e63d487 Mon Sep 17 00:00:00 2001
From: Rui Ueyama <ruiu@bluewhale.systems>
Date: Wed, 9 Aug 2023 11:40:09 +0900
Subject: [PATCH] Create a symbol version dependency to GLIBC_ABI_DT_RELR

Fixes https://github.com/rui314/mold/issues/653
---
 elf/cmdline.cc                     |  2 ++
 elf/mold.h                         |  1 +
 elf/output-chunks.cc               | 44 +++++++++++++++++++++++++-----
 test/elf/z-pack-relative-relocs.sh | 16 +++++++++++
 4 files changed, 56 insertions(+), 7 deletions(-)
 create mode 100755 test/elf/z-pack-relative-relocs.sh

diff --git a/elf/cmdline.cc b/elf/cmdline.cc
index c568ce086..82a0e6869 100644
--- a/elf/cmdline.cc
+++ b/elf/cmdline.cc
@@ -875,8 +875,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
       ctx.arg.z_nodefaultlib = true;
     } else if (read_z_flag("pack-relative-relocs")) {
       ctx.arg.pack_dyn_relocs_relr = true;
+      ctx.arg.z_pack_relative_relocs = true;
     } else if (read_z_flag("nopack-relative-relocs")) {
       ctx.arg.pack_dyn_relocs_relr = false;
+      ctx.arg.z_pack_relative_relocs = false;
     } else if (read_z_flag("separate-loadable-segments")) {
       z_separate_code = SEPARATE_LOADABLE_SEGMENTS;
     } else if (read_z_flag("separate-code")) {
diff --git a/elf/mold.h b/elf/mold.h
index e5532211c..3a027f1e9 100644
--- a/elf/mold.h
+++ b/elf/mold.h
@@ -1831,6 +1831,7 @@ struct Context {
     bool z_nodefaultlib = false;
     bool z_now = false;
     bool z_origin = false;
+    bool z_pack_relative_relocs = false;
     bool z_relro = true;
     bool z_sectionheader = true;
     bool z_shstk = false;
diff --git a/elf/output-chunks.cc b/elf/output-chunks.cc
index 726a4da2b..3896a2991 100644
--- a/elf/output-chunks.cc
+++ b/elf/output-chunks.cc
@@ -2373,12 +2373,13 @@ void VerneedSection<E>::construct(Context<E> &ctx) {
            std::tuple(((SharedFile<E> *)b->file)->soname, b->ver_idx);
   });
 
-  // Resize of .gnu.version
+  // Resize .gnu.version
   ctx.versym->contents.resize(ctx.dynsym->symbols.size(), 1);
   ctx.versym->contents[0] = 0;
 
   // Allocate a large enough buffer for .gnu.version_r.
-  contents.resize((sizeof(ElfVerneed<E>) + sizeof(ElfVernaux<E>)) * syms.size());
+  contents.resize((sizeof(ElfVerneed<E>) + sizeof(ElfVernaux<E>)) *
+                  (syms.size() + 1));
 
   // Fill .gnu.version_r.
   u8 *buf = (u8 *)&contents[0];
@@ -2394,14 +2395,14 @@ void VerneedSection<E>::construct(Context<E> &ctx) {
       verneed->vn_next = ptr - (u8 *)verneed;
 
     verneed = (ElfVerneed<E> *)ptr;
-    ptr += sizeof(*verneed);
+    ptr += sizeof(ElfVerneed<E>);
     verneed->vn_version = 1;
     verneed->vn_file = ctx.dynstr->find_string(((SharedFile<E> *)file)->soname);
     verneed->vn_aux = sizeof(ElfVerneed<E>);
     aux = nullptr;
   };
 
-  auto add_entry = [&](Symbol<E> *sym) {
+  auto add_entry = [&](std::string_view verstr) {
     verneed->vn_cnt++;
 
     if (aux)
@@ -2409,23 +2410,52 @@ void VerneedSection<E>::construct(Context<E> &ctx) {
     aux = (ElfVernaux<E> *)ptr;
     ptr += sizeof(*aux);
 
-    std::string_view verstr = sym->get_version();
     aux->vna_hash = elf_hash(verstr);
     aux->vna_other = ++veridx;
     aux->vna_name = ctx.dynstr->add_string(verstr);
   };
 
+  // Create version entries.
   for (i64 i = 0; i < syms.size(); i++) {
     if (i == 0 || syms[i - 1]->file != syms[i]->file) {
       start_group(syms[i]->file);
-      add_entry(syms[i]);
+      add_entry(syms[i]->get_version());
     } else if (syms[i - 1]->ver_idx != syms[i]->ver_idx) {
-      add_entry(syms[i]);
+      add_entry(syms[i]->get_version());
     }
 
     ctx.versym->contents[syms[i]->get_dynsym_idx(ctx)] = veridx;
   }
 
+  if (ctx.arg.z_pack_relative_relocs) {
+    // If `-z pack-relative-relocs` is specified, we'll create a .relr.dyn
+    // section and store base relocation records to that section instead of
+    // to the usual .rela.dyn section.
+    //
+    // .relr.dyn is relatively new feature and not supported by glibc until
+    // 2.38 which was released in 2022. Executables built with `-z
+    // pack-relative-relocs` don't work and usually crash immediately on
+    // startup if libc doesn't support it.
+    //
+    // In the following code, we'll add a dependency to a dummy version name
+    // "GLIBC_ABI_DT_RELR" so that executables built with the option failed
+    // with a more friendly "version `GLIBC_ABI_DT_RELR' not found" error
+    // message. glibc 2.38 or later knows about this dummy version name and
+    // simply ignores it.
+    auto find_glibc2 = [&]() -> InputFile<E> * {
+      for (Symbol<E> *sym : syms)
+        if (((SharedFile<E> *)sym->file)->soname.starts_with("libc.so.") &&
+            sym->get_version().starts_with("GLIBC_2."))
+          return sym->file;
+      return nullptr;
+    };
+
+    if (InputFile<E> *file = find_glibc2()) {
+      start_group(file);
+      add_entry("GLIBC_ABI_DT_RELR");
+    }
+  }
+
   // Resize .gnu.version_r to fit to its contents.
   contents.resize(ptr - buf);
 }
diff --git a/test/elf/z-pack-relative-relocs.sh b/test/elf/z-pack-relative-relocs.sh
new file mode 100755
index 000000000..e09d441e7
--- /dev/null
+++ b/test/elf/z-pack-relative-relocs.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+. $(dirname $0)/common.inc
+
+cat <<EOF | $CC -o $t/a.o -fPIC -c -xc -
+#include <stdio.h>
+int main() {
+  printf("Hello world\n");
+}
+EOF
+
+$CC -B. -o $t/exe $t/a.o -pie -Wl,-z,pack-relative-relocs
+
+readelf -W -V $t/exe > $t/log
+grep -Fq GLIBC_2. $t/log || skip
+
+grep -q GLIBC_ABI_DT_RELR $t/log