summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/scripts/depfixer.py79
1 files changed, 79 insertions, 0 deletions
diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py
index db9c97d98..8adc35c4a 100644
--- a/mesonbuild/scripts/depfixer.py
+++ b/mesonbuild/scripts/depfixer.py
@@ -31,8 +31,12 @@ class DataSizes:
p = '<'
else:
p = '>'
+ self.Char = p + 'c'
+ self.CharSize = 1
self.Half = p + 'h'
self.HalfSize = 2
+ self.Section = p + 'h'
+ self.SectionSize = 2
self.Word = p + 'I'
self.WordSize = 4
self.Sword = p + 'i'
@@ -71,6 +75,24 @@ class DynamicEntry(DataSizes):
ofile.write(struct.pack(self.Sword, self.d_tag))
ofile.write(struct.pack(self.Word, self.val))
+class DynsymEntry(DataSizes):
+ def __init__(self, ifile: T.BinaryIO, ptrsize: int, is_le: bool) -> None:
+ super().__init__(ptrsize, is_le)
+ is_64 = ptrsize == 64
+ self.st_name = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+ if is_64:
+ self.st_info = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_other = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_shndx = struct.unpack(self.Section, ifile.read(self.SectionSize))[0]
+ self.st_value = struct.unpack(self.Addr, ifile.read(self.AddrSize))[0]
+ self.st_size = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0]
+ else:
+ self.st_value = struct.unpack(self.Addr, ifile.read(self.AddrSize))[0]
+ self.st_size = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+ self.st_info = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_other = struct.unpack(self.Char, ifile.read(self.CharSize))[0]
+ self.st_shndx = struct.unpack(self.Section, ifile.read(self.SectionSize))[0]
+
class SectionHeader(DataSizes):
def __init__(self, ifile: T.BinaryIO, ptrsize: int, is_le: bool) -> None:
super().__init__(ptrsize, is_le)
@@ -115,6 +137,8 @@ class Elf(DataSizes):
self.verbose = verbose
self.sections: T.List[SectionHeader] = []
self.dynamic: T.List[DynamicEntry] = []
+ self.dynsym: T.List[DynsymEntry] = []
+ self.dynsym_strings: T.List[str] = []
self.open_bf(bfile)
try:
(self.ptrsize, self.is_le) = self.detect_elf_type()
@@ -122,6 +146,8 @@ class Elf(DataSizes):
self.parse_header()
self.parse_sections()
self.parse_dynamic()
+ self.parse_dynsym()
+ self.parse_dynsym_strings()
except (struct.error, RuntimeError):
self.close_bf()
raise
@@ -232,6 +258,23 @@ class Elf(DataSizes):
if e.d_tag == 0:
break
+ def parse_dynsym(self) -> None:
+ sec = self.find_section(b'.dynsym')
+ if sec is None:
+ return
+ self.bf.seek(sec.sh_offset)
+ for i in range(sec.sh_size // sec.sh_entsize):
+ e = DynsymEntry(self.bf, self.ptrsize, self.is_le)
+ self.dynsym.append(e)
+
+ def parse_dynsym_strings(self) -> None:
+ sec = self.find_section(b'.dynstr')
+ if sec is None:
+ return
+ for i in self.dynsym:
+ self.bf.seek(sec.sh_offset + i.st_name)
+ self.dynsym_strings.append(self.read_str().decode())
+
@generate_list
def get_section_names(self) -> T.Generator[str, None, None]:
section_names = self.sections[self.e_shstrndx]
@@ -353,12 +396,48 @@ class Elf(DataSizes):
self.bf.write(new_rpath)
self.bf.write(b'\0')
+ def clean_rpath_entry_string(self, entrynum: int) -> None:
+ # Get the rpath string
+ offset = self.get_entry_offset(entrynum)
+ self.bf.seek(offset)
+ rpath_string = self.read_str().decode()
+ reused_str = ''
+
+ # Inspect the dyn strings and check if our rpath string
+ # ends with one of them.
+ # This is to handle a subtle optimization of the linker
+ # where one of the dyn function name offset in the dynstr
+ # table might be set at the an offset of the rpath string.
+ # Example:
+ #
+ # rpath offset = 1314 string = /usr/lib/foo
+ # dym function offset = 1322 string = foo
+ #
+ # In the following case, the dym function string offset is
+ # placed at the offset +10 of the rpath.
+ # To correctly clear the rpath entry AND keep normal
+ # functionality of this optimization (and the binary),
+ # parse the maximum string we can remove from the rpath entry.
+ #
+ # Since strings MUST be null terminated, we can always check
+ # if the rpath string ends with the dyn function string and
+ # calculate what we can actually remove accordingly.
+ for dynsym_string in self.dynsym_strings:
+ if rpath_string.endswith(dynsym_string):
+ if len(dynsym_string) > len(reused_str):
+ reused_str = dynsym_string
+
+ # Seek back to start of string
+ self.bf.seek(offset)
+ self.bf.write(b'X' * (len(rpath_string) - len(reused_str)))
+
def remove_rpath_entry(self, entrynum: int) -> None:
sec = self.find_section(b'.dynamic')
if sec is None:
return None
for (i, entry) in enumerate(self.dynamic):
if entry.d_tag == entrynum:
+ self.clean_rpath_entry_string(entrynum)
rpentry = self.dynamic[i]
rpentry.d_tag = 0
self.dynamic = self.dynamic[:i] + self.dynamic[i + 1:] + [rpentry]