diff options
| -rw-r--r-- | mesonbuild/scripts/depfixer.py | 79 |
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] |
