summaryrefslogtreecommitdiff
path: root/debian/patches/Adjust-DT_MIPS_RLD_MAP_REL-dynamic-section-entry-if-prese.patch
blob: ffee4b9338fe9ff2788ba0ca81cf3fbdfc552622 (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
From: "Ivan A. Melnikov" <iv@altlinux.org>
Date: Thu, 22 Aug 2019 15:30:11 +0400
Subject: Adjust DT_MIPS_RLD_MAP_REL dynamic section entry if present

`patchelf --set-rpath` corrupted executables on mips32el: the dynamic
liker crushed with Segmentation fault when loading any executable with
RPATH added that way.

The problem was around the MIPS-specific mechanism of setting up the
debug map pointer. When DT_MIPS_RLD_MAP_REL entry in the dynamic section
is not zero, it holds the relative address of __RLD_MAP -- an offset
relative to this dynamic section entry. Dynamic linker puts the
pointer to the `r_debug` structure there.

When patchelf updates the executable RPATH, it moves the .dynamic
section both in the binary and in memory, while __RLD_MAP is not moved
in memory, since it belongs to special .rld_map section that has type
PROGBITS. So, the offset stored in DT_MIPS_RLD_MAP_REL entry is not
valid anymore and should be updated.

This commit adds the necessary update.

Here we also import DT_MIPS_RLD_MAP_REL definition in elf.h form
glibc commit a2057c984e4314c3740f04cf54e36c824e4c8f32.

Refs: #82
Signed-off-by: Ivan A. Melnikov <iv@altlinux.org>

DT_MIPS_RLD_MAP_REL
---
 src/elf.h       |  6 +++++-
 src/patchelf.cc | 12 ++++++++++--
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/elf.h b/src/elf.h
index 87cb141..e4e167c 100644
--- a/src/elf.h
+++ b/src/elf.h
@@ -1640,7 +1640,11 @@ typedef struct
    PLT is writable.  For a non-writable PLT, this is omitted or has a zero
    value.  */
 #define DT_MIPS_RWPLT        0x70000034
-#define DT_MIPS_NUM	     0x35
+/* An alternative description of the classic MIPS RLD_MAP that is usable
+   in a PIE as it stores a relative offset from the address of the tag
+   rather than an absolute address.  */
+#define DT_MIPS_RLD_MAP_REL  0x70000035
+#define DT_MIPS_NUM         0x36
 
 /* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry.  */
 
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 965686a..d8ea097 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -949,9 +949,9 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
        (e.g., those produced by klibc's klcc). */
     Elf_Shdr * shdrDynamic = findSection2(".dynamic");
     if (shdrDynamic) {
-        Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic->sh_offset));
+        Elf_Dyn * dyn_table = (Elf_Dyn *) (contents + rdi(shdrDynamic->sh_offset));
         unsigned int d_tag;
-        for ( ; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++)
+        for (Elf_Dyn * dyn = dyn_table; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++)
             if (d_tag == DT_STRTAB)
                 dyn->d_un.d_ptr = findSection(".dynstr").sh_addr;
             else if (d_tag == DT_STRSZ)
@@ -990,6 +990,14 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
                 dyn->d_un.d_ptr = findSection(".gnu.version_r").sh_addr;
             else if (d_tag == DT_VERSYM)
                 dyn->d_un.d_ptr = findSection(".gnu.version").sh_addr;
+            else if (d_tag == DT_MIPS_RLD_MAP_REL) {
+                /* the MIPS_RLD_MAP_REL tag stores the offset to the debug
+                   pointer, relative to the address of the tag */
+                debug("Updating DT_MIPS_RLD_MAP_REL");
+                Elf_Addr rtld_map_addr = findSection(".rld_map").sh_addr;
+                auto dyn_offset = ((char*)dyn) - ((char*)dyn_table);
+                dyn->d_un.d_ptr = rtld_map_addr + dyn_offset - shdrDynamic->sh_addr;
+            }
     }