| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- #!/usr/bin/python3
- # ELF editor for load align tests.
- # Copyright (C) 2022-2026 Free Software Foundation, Inc.
- # Copyright The GNU Toolchain Authors.
- # This file is part of the GNU C Library.
- #
- # The GNU C Library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
- # License as published by the Free Software Foundation; either
- # version 2.1 of the License, or (at your option) any later version.
- #
- # The GNU C Library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public
- # License along with the GNU C Library; if not, see
- # <https://www.gnu.org/licenses/>.
- import argparse
- import os
- import sys
- import struct
- EI_NIDENT=16
- EI_MAG0=0
- ELFMAG0=b'\x7f'
- EI_MAG1=1
- ELFMAG1=b'E'
- EI_MAG2=2
- ELFMAG2=b'L'
- EI_MAG3=3
- ELFMAG3=b'F'
- EI_CLASS=4
- ELFCLASSNONE=b'0'
- ELFCLASS32=b'\x01'
- ELFCLASS64=b'\x02'
- EI_DATA=5
- ELFDATA2LSB=b'\x01'
- ELFDATA2MSB=b'\x02'
- ET_EXEC=2
- ET_DYN=3
- PT_LOAD=1
- PT_TLS=7
- def elf_types_fmts(e_ident):
- endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>'
- addr = 'I' if e_ident[EI_CLASS] == ELFCLASS32 else 'Q'
- off = 'I' if e_ident[EI_CLASS] == ELFCLASS32 else 'Q'
- return (endian, addr, off)
- class Elf_Ehdr:
- def __init__(self, e_ident):
- endian, addr, off = elf_types_fmts(e_ident)
- self.fmt = '{0}HHI{1}{2}{2}IHHHHHH'.format(endian, addr, off)
- self.len = struct.calcsize(self.fmt)
- def read(self, f):
- buf = f.read(self.len)
- if not buf:
- error('{}: header too small'.format(f.name))
- data = struct.unpack(self.fmt, buf)
- self.e_type = data[0]
- self.e_machine = data[1]
- self.e_version = data[2]
- self.e_entry = data[3]
- self.e_phoff = data[4]
- self.e_shoff = data[5]
- self.e_flags = data[6]
- self.e_ehsize = data[7]
- self.e_phentsize= data[8]
- self.e_phnum = data[9]
- self.e_shstrndx = data[10]
- class Elf_Phdr:
- def __init__(self, e_ident):
- endian, addr, off = elf_types_fmts(e_ident)
- self.ei_class = e_ident[EI_CLASS]
- if self.ei_class == ELFCLASS32:
- self.fmt = '{0}I{2}{1}{1}IIII'.format(endian, addr, off)
- else:
- self.fmt = '{0}II{2}{1}{1}QQQ'.format(endian, addr, off)
- self.len = struct.calcsize(self.fmt)
- def read(self, f):
- buf = f.read(self.len)
- if len(buf) < self.len:
- error('{}: program header too small'.format(f.name))
- data = struct.unpack(self.fmt, buf)
- if self.ei_class == ELFCLASS32:
- self.p_type = data[0]
- self.p_offset = data[1]
- self.p_vaddr = data[2]
- self.p_paddr = data[3]
- self.p_filesz = data[4]
- self.p_memsz = data[5]
- self.p_flags = data[6]
- self.p_align = data[7]
- else:
- self.p_type = data[0]
- self.p_flags = data[1]
- self.p_offset = data[2]
- self.p_vaddr = data[3]
- self.p_paddr = data[4]
- self.p_filesz = data[5]
- self.p_memsz = data[6]
- self.p_align = data[7]
- def write(self, f):
- if self.ei_class == ELFCLASS32:
- data = struct.pack(self.fmt,
- self.p_type,
- self.p_offset,
- self.p_vaddr,
- self.p_paddr,
- self.p_filesz,
- self.p_memsz,
- self.p_flags,
- self.p_align)
- else:
- data = struct.pack(self.fmt,
- self.p_type,
- self.p_flags,
- self.p_offset,
- self.p_vaddr,
- self.p_paddr,
- self.p_filesz,
- self.p_memsz,
- self.p_align)
- f.write(data)
- def error(msg):
- print(msg, file=sys.stderr)
- sys.exit(1)
- def elf_edit_align(phdr, align):
- if align == 'half':
- phdr.p_align = phdr.p_align >> 1
- else:
- phdr.p_align = int(align)
- def elf_edit_maximize_tls_size(phdr, elfclass):
- if elfclass == ELFCLASS32:
- # It is possible that the kernel can allocate half of the
- # address space, so use something larger.
- phdr.p_memsz = 0xfff00000
- else:
- phdr.p_memsz = 1 << 63
- def elf_edit(f, opts):
- ei_nident_fmt = 'c' * EI_NIDENT
- ei_nident_len = struct.calcsize(ei_nident_fmt)
- data = f.read(ei_nident_len)
- if len(data) < ei_nident_len:
- error('{}: e_nident too small'.format(f.name))
- e_ident = struct.unpack(ei_nident_fmt, data)
- if e_ident[EI_MAG0] != ELFMAG0 \
- or e_ident[EI_MAG1] != ELFMAG1 \
- or e_ident[EI_MAG2] != ELFMAG2 \
- or e_ident[EI_MAG3] != ELFMAG3:
- error('{}: bad ELF header'.format(f.name))
- if e_ident[EI_CLASS] != ELFCLASS32 \
- and e_ident[EI_CLASS] != ELFCLASS64:
- error('{}: unsupported ELF class: {}'.format(f.name, e_ident[EI_CLASS]))
- if e_ident[EI_DATA] != ELFDATA2LSB \
- and e_ident[EI_DATA] != ELFDATA2MSB: \
- error('{}: unsupported ELF data: {}'.format(f.name, e_ident[EI_DATA]))
- ehdr = Elf_Ehdr(e_ident)
- ehdr.read(f)
- if ehdr.e_type not in (ET_EXEC, ET_DYN):
- error('{}: not an executable or shared library'.format(f.name))
- phdr = Elf_Phdr(e_ident)
- maximize_tls_size_done = False
- for i in range(0, ehdr.e_phnum):
- f.seek(ehdr.e_phoff + i * phdr.len)
- phdr.read(f)
- if phdr.p_type == PT_LOAD and opts.align is not None:
- elf_edit_align(phdr, opts.align)
- f.seek(ehdr.e_phoff + i * phdr.len)
- phdr.write(f)
- break
- if phdr.p_type == PT_TLS and opts.maximize_tls_size:
- elf_edit_maximize_tls_size(phdr, e_ident[EI_CLASS])
- f.seek(ehdr.e_phoff + i * phdr.len)
- phdr.write(f)
- maximize_tls_size_done = True
- break
- if opts.maximize_tls_size and not maximize_tls_size_done:
- error('{}: TLS maximum size was not updated'.format(f.name))
- def get_parser():
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('-a', dest='align',
- help='How to set the LOAD alignment')
- parser.add_argument('--maximize-tls-size', action='store_true',
- help='Set maximum PT_TLS size')
- parser.add_argument('output',
- help='ELF file to edit')
- return parser
- def main(argv):
- parser = get_parser()
- opts = parser.parse_args(argv)
- with open(opts.output, 'r+b') as fout:
- elf_edit(fout, opts)
- if __name__ == '__main__':
- main(sys.argv[1:])
|