(root)/
glibc-2.38/
scripts/
tst-elf-edit.py
       1  #!/usr/bin/python3
       2  # ELF editor for load align tests.
       3  # Copyright (C) 2022-2023 Free Software Foundation, Inc.
       4  # Copyright The GNU Toolchain Authors.
       5  # This file is part of the GNU C Library.
       6  #
       7  # The GNU C Library is free software; you can redistribute it and/or
       8  # modify it under the terms of the GNU Lesser General Public
       9  # License as published by the Free Software Foundation; either
      10  # version 2.1 of the License, or (at your option) any later version.
      11  #
      12  # The GNU C Library is distributed in the hope that it will be useful,
      13  # but WITHOUT ANY WARRANTY; without even the implied warranty of
      14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15  # Lesser General Public License for more details.
      16  #
      17  # You should have received a copy of the GNU Lesser General Public
      18  # License along with the GNU C Library; if not, see
      19  # <https://www.gnu.org/licenses/>.
      20  
      21  import argparse
      22  import os
      23  import sys
      24  import struct
      25  
      26  EI_NIDENT=16
      27  
      28  EI_MAG0=0
      29  ELFMAG0=b'\x7f'
      30  EI_MAG1=1
      31  ELFMAG1=b'E'
      32  EI_MAG2=2
      33  ELFMAG2=b'L'
      34  EI_MAG3=3
      35  ELFMAG3=b'F'
      36  
      37  EI_CLASS=4
      38  ELFCLASSNONE=b'0'
      39  ELFCLASS32=b'\x01'
      40  ELFCLASS64=b'\x02'
      41  
      42  EI_DATA=5
      43  ELFDATA2LSB=b'\x01'
      44  ELFDATA2MSB=b'\x02'
      45  
      46  ET_EXEC=2
      47  ET_DYN=3
      48  
      49  PT_LOAD=1
      50  PT_TLS=7
      51  
      52  def elf_types_fmts(e_ident):
      53      endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>'
      54      addr = 'I' if e_ident[EI_CLASS] == ELFCLASS32 else 'Q'
      55      off = 'I' if e_ident[EI_CLASS] == ELFCLASS32 else 'Q'
      56      return (endian, addr, off)
      57  
      58  class ESC[4;38;5;81mElf_Ehdr:
      59      def __init__(self, e_ident):
      60          endian, addr, off = elf_types_fmts(e_ident)
      61          self.fmt = '{0}HHI{1}{2}{2}IHHHHHH'.format(endian, addr, off)
      62          self.len = struct.calcsize(self.fmt)
      63  
      64      def read(self, f):
      65          buf = f.read(self.len)
      66          if not buf:
      67              error('{}: header too small'.format(f.name))
      68          data = struct.unpack(self.fmt, buf)
      69          self.e_type = data[0]
      70          self.e_machine = data[1]
      71          self.e_version = data[2]
      72          self.e_entry = data[3]
      73          self.e_phoff = data[4]
      74          self.e_shoff = data[5]
      75          self.e_flags = data[6]
      76          self.e_ehsize = data[7]
      77          self.e_phentsize= data[8]
      78          self.e_phnum = data[9]
      79          self.e_shstrndx = data[10]
      80  
      81  
      82  class ESC[4;38;5;81mElf_Phdr:
      83      def __init__(self, e_ident):
      84          endian, addr, off = elf_types_fmts(e_ident)
      85          self.ei_class = e_ident[EI_CLASS]
      86          if self.ei_class == ELFCLASS32:
      87              self.fmt = '{0}I{2}{1}{1}IIII'.format(endian, addr, off)
      88          else:
      89              self.fmt = '{0}II{2}{1}{1}QQQ'.format(endian, addr, off)
      90          self.len = struct.calcsize(self.fmt)
      91  
      92      def read(self, f):
      93          buf = f.read(self.len)
      94          if len(buf) < self.len:
      95              error('{}: program header too small'.format(f.name))
      96          data = struct.unpack(self.fmt, buf)
      97          if self.ei_class == ELFCLASS32:
      98              self.p_type = data[0]
      99              self.p_offset = data[1]
     100              self.p_vaddr = data[2]
     101              self.p_paddr = data[3]
     102              self.p_filesz = data[4]
     103              self.p_memsz = data[5]
     104              self.p_flags = data[6]
     105              self.p_align = data[7]
     106          else:
     107              self.p_type = data[0]
     108              self.p_flags = data[1]
     109              self.p_offset = data[2]
     110              self.p_vaddr = data[3]
     111              self.p_paddr = data[4]
     112              self.p_filesz = data[5]
     113              self.p_memsz = data[6]
     114              self.p_align = data[7]
     115  
     116      def write(self, f):
     117          if self.ei_class == ELFCLASS32:
     118              data = struct.pack(self.fmt,
     119                                 self.p_type,
     120                                 self.p_offset,
     121                                 self.p_vaddr,
     122                                 self.p_paddr,
     123                                 self.p_filesz,
     124                                 self.p_memsz,
     125                                 self.p_flags,
     126                                 self.p_align)
     127          else:
     128              data = struct.pack(self.fmt,
     129                                 self.p_type,
     130                                 self.p_flags,
     131                                 self.p_offset,
     132                                 self.p_vaddr,
     133                                 self.p_paddr,
     134                                 self.p_filesz,
     135                                 self.p_memsz,
     136                                 self.p_align)
     137          f.write(data)
     138  
     139  
     140  def error(msg):
     141      print(msg, file=sys.stderr)
     142      sys.exit(1)
     143  
     144  
     145  def elf_edit_align(phdr, align):
     146      if align == 'half':
     147          phdr.p_align = phdr.p_align >> 1
     148      else:
     149          phdr.p_align = int(align)
     150  
     151  def elf_edit_maximize_tls_size(phdr, elfclass):
     152      if elfclass == ELFCLASS32:
     153          # It is possible that the kernel can allocate half of the
     154          # address space, so use something larger.
     155          phdr.p_memsz = 0xfff00000
     156      else:
     157          phdr.p_memsz = 1 << 63
     158  
     159  def elf_edit(f, opts):
     160      ei_nident_fmt = 'c' * EI_NIDENT
     161      ei_nident_len = struct.calcsize(ei_nident_fmt)
     162  
     163      data = f.read(ei_nident_len)
     164      if len(data) < ei_nident_len:
     165        error('{}: e_nident too small'.format(f.name))
     166      e_ident = struct.unpack(ei_nident_fmt, data)
     167  
     168      if e_ident[EI_MAG0] != ELFMAG0 \
     169         or e_ident[EI_MAG1] != ELFMAG1 \
     170         or e_ident[EI_MAG2] != ELFMAG2 \
     171         or e_ident[EI_MAG3] != ELFMAG3:
     172        error('{}: bad ELF header'.format(f.name))
     173  
     174      if e_ident[EI_CLASS] != ELFCLASS32 \
     175         and e_ident[EI_CLASS] != ELFCLASS64:
     176        error('{}: unsupported ELF class: {}'.format(f.name, e_ident[EI_CLASS]))
     177  
     178      if e_ident[EI_DATA] != ELFDATA2LSB \
     179         and e_ident[EI_DATA] != ELFDATA2MSB: \
     180        error('{}: unsupported ELF data: {}'.format(f.name, e_ident[EI_DATA]))
     181  
     182      ehdr = Elf_Ehdr(e_ident)
     183      ehdr.read(f)
     184      if ehdr.e_type not in (ET_EXEC, ET_DYN):
     185         error('{}: not an executable or shared library'.format(f.name))
     186  
     187      phdr = Elf_Phdr(e_ident)
     188      maximize_tls_size_done = False
     189      for i in range(0, ehdr.e_phnum):
     190          f.seek(ehdr.e_phoff + i * phdr.len)
     191          phdr.read(f)
     192          if phdr.p_type == PT_LOAD and opts.align is not None:
     193              elf_edit_align(phdr, opts.align)
     194              f.seek(ehdr.e_phoff + i * phdr.len)
     195              phdr.write(f)
     196              break
     197          if phdr.p_type == PT_TLS and opts.maximize_tls_size:
     198              elf_edit_maximize_tls_size(phdr, e_ident[EI_CLASS])
     199              f.seek(ehdr.e_phoff + i * phdr.len)
     200              phdr.write(f)
     201              maximize_tls_size_done = True
     202              break
     203  
     204      if opts.maximize_tls_size and not maximize_tls_size_done:
     205          error('{}: TLS maximum size was not updated'.format(f.name))
     206  
     207  def get_parser():
     208      parser = argparse.ArgumentParser(description=__doc__)
     209      parser.add_argument('-a', dest='align',
     210                          help='How to set the LOAD alignment')
     211      parser.add_argument('--maximize-tls-size', action='store_true',
     212                          help='Set maximum PT_TLS size')
     213      parser.add_argument('output',
     214                          help='ELF file to edit')
     215      return parser
     216  
     217  
     218  def main(argv):
     219      parser = get_parser()
     220      opts = parser.parse_args(argv)
     221      with open(opts.output, 'r+b') as fout:
     222         elf_edit(fout, opts)
     223  
     224  
     225  if __name__ == '__main__':
     226      main(sys.argv[1:])