tst-elf-edit.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #!/usr/bin/python3
  2. # ELF editor for load align tests.
  3. # Copyright (C) 2022-2026 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. import argparse
  21. import os
  22. import sys
  23. import struct
  24. EI_NIDENT=16
  25. EI_MAG0=0
  26. ELFMAG0=b'\x7f'
  27. EI_MAG1=1
  28. ELFMAG1=b'E'
  29. EI_MAG2=2
  30. ELFMAG2=b'L'
  31. EI_MAG3=3
  32. ELFMAG3=b'F'
  33. EI_CLASS=4
  34. ELFCLASSNONE=b'0'
  35. ELFCLASS32=b'\x01'
  36. ELFCLASS64=b'\x02'
  37. EI_DATA=5
  38. ELFDATA2LSB=b'\x01'
  39. ELFDATA2MSB=b'\x02'
  40. ET_EXEC=2
  41. ET_DYN=3
  42. PT_LOAD=1
  43. PT_TLS=7
  44. def elf_types_fmts(e_ident):
  45. endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>'
  46. addr = 'I' if e_ident[EI_CLASS] == ELFCLASS32 else 'Q'
  47. off = 'I' if e_ident[EI_CLASS] == ELFCLASS32 else 'Q'
  48. return (endian, addr, off)
  49. class Elf_Ehdr:
  50. def __init__(self, e_ident):
  51. endian, addr, off = elf_types_fmts(e_ident)
  52. self.fmt = '{0}HHI{1}{2}{2}IHHHHHH'.format(endian, addr, off)
  53. self.len = struct.calcsize(self.fmt)
  54. def read(self, f):
  55. buf = f.read(self.len)
  56. if not buf:
  57. error('{}: header too small'.format(f.name))
  58. data = struct.unpack(self.fmt, buf)
  59. self.e_type = data[0]
  60. self.e_machine = data[1]
  61. self.e_version = data[2]
  62. self.e_entry = data[3]
  63. self.e_phoff = data[4]
  64. self.e_shoff = data[5]
  65. self.e_flags = data[6]
  66. self.e_ehsize = data[7]
  67. self.e_phentsize= data[8]
  68. self.e_phnum = data[9]
  69. self.e_shstrndx = data[10]
  70. class Elf_Phdr:
  71. def __init__(self, e_ident):
  72. endian, addr, off = elf_types_fmts(e_ident)
  73. self.ei_class = e_ident[EI_CLASS]
  74. if self.ei_class == ELFCLASS32:
  75. self.fmt = '{0}I{2}{1}{1}IIII'.format(endian, addr, off)
  76. else:
  77. self.fmt = '{0}II{2}{1}{1}QQQ'.format(endian, addr, off)
  78. self.len = struct.calcsize(self.fmt)
  79. def read(self, f):
  80. buf = f.read(self.len)
  81. if len(buf) < self.len:
  82. error('{}: program header too small'.format(f.name))
  83. data = struct.unpack(self.fmt, buf)
  84. if self.ei_class == ELFCLASS32:
  85. self.p_type = data[0]
  86. self.p_offset = data[1]
  87. self.p_vaddr = data[2]
  88. self.p_paddr = data[3]
  89. self.p_filesz = data[4]
  90. self.p_memsz = data[5]
  91. self.p_flags = data[6]
  92. self.p_align = data[7]
  93. else:
  94. self.p_type = data[0]
  95. self.p_flags = data[1]
  96. self.p_offset = data[2]
  97. self.p_vaddr = data[3]
  98. self.p_paddr = data[4]
  99. self.p_filesz = data[5]
  100. self.p_memsz = data[6]
  101. self.p_align = data[7]
  102. def write(self, f):
  103. if self.ei_class == ELFCLASS32:
  104. data = struct.pack(self.fmt,
  105. self.p_type,
  106. self.p_offset,
  107. self.p_vaddr,
  108. self.p_paddr,
  109. self.p_filesz,
  110. self.p_memsz,
  111. self.p_flags,
  112. self.p_align)
  113. else:
  114. data = struct.pack(self.fmt,
  115. self.p_type,
  116. self.p_flags,
  117. self.p_offset,
  118. self.p_vaddr,
  119. self.p_paddr,
  120. self.p_filesz,
  121. self.p_memsz,
  122. self.p_align)
  123. f.write(data)
  124. def error(msg):
  125. print(msg, file=sys.stderr)
  126. sys.exit(1)
  127. def elf_edit_align(phdr, align):
  128. if align == 'half':
  129. phdr.p_align = phdr.p_align >> 1
  130. else:
  131. phdr.p_align = int(align)
  132. def elf_edit_maximize_tls_size(phdr, elfclass):
  133. if elfclass == ELFCLASS32:
  134. # It is possible that the kernel can allocate half of the
  135. # address space, so use something larger.
  136. phdr.p_memsz = 0xfff00000
  137. else:
  138. phdr.p_memsz = 1 << 63
  139. def elf_edit(f, opts):
  140. ei_nident_fmt = 'c' * EI_NIDENT
  141. ei_nident_len = struct.calcsize(ei_nident_fmt)
  142. data = f.read(ei_nident_len)
  143. if len(data) < ei_nident_len:
  144. error('{}: e_nident too small'.format(f.name))
  145. e_ident = struct.unpack(ei_nident_fmt, data)
  146. if e_ident[EI_MAG0] != ELFMAG0 \
  147. or e_ident[EI_MAG1] != ELFMAG1 \
  148. or e_ident[EI_MAG2] != ELFMAG2 \
  149. or e_ident[EI_MAG3] != ELFMAG3:
  150. error('{}: bad ELF header'.format(f.name))
  151. if e_ident[EI_CLASS] != ELFCLASS32 \
  152. and e_ident[EI_CLASS] != ELFCLASS64:
  153. error('{}: unsupported ELF class: {}'.format(f.name, e_ident[EI_CLASS]))
  154. if e_ident[EI_DATA] != ELFDATA2LSB \
  155. and e_ident[EI_DATA] != ELFDATA2MSB: \
  156. error('{}: unsupported ELF data: {}'.format(f.name, e_ident[EI_DATA]))
  157. ehdr = Elf_Ehdr(e_ident)
  158. ehdr.read(f)
  159. if ehdr.e_type not in (ET_EXEC, ET_DYN):
  160. error('{}: not an executable or shared library'.format(f.name))
  161. phdr = Elf_Phdr(e_ident)
  162. maximize_tls_size_done = False
  163. for i in range(0, ehdr.e_phnum):
  164. f.seek(ehdr.e_phoff + i * phdr.len)
  165. phdr.read(f)
  166. if phdr.p_type == PT_LOAD and opts.align is not None:
  167. elf_edit_align(phdr, opts.align)
  168. f.seek(ehdr.e_phoff + i * phdr.len)
  169. phdr.write(f)
  170. break
  171. if phdr.p_type == PT_TLS and opts.maximize_tls_size:
  172. elf_edit_maximize_tls_size(phdr, e_ident[EI_CLASS])
  173. f.seek(ehdr.e_phoff + i * phdr.len)
  174. phdr.write(f)
  175. maximize_tls_size_done = True
  176. break
  177. if opts.maximize_tls_size and not maximize_tls_size_done:
  178. error('{}: TLS maximum size was not updated'.format(f.name))
  179. def get_parser():
  180. parser = argparse.ArgumentParser(description=__doc__)
  181. parser.add_argument('-a', dest='align',
  182. help='How to set the LOAD alignment')
  183. parser.add_argument('--maximize-tls-size', action='store_true',
  184. help='Set maximum PT_TLS size')
  185. parser.add_argument('output',
  186. help='ELF file to edit')
  187. return parser
  188. def main(argv):
  189. parser = get_parser()
  190. opts = parser.parse_args(argv)
  191. with open(opts.output, 'r+b') as fout:
  192. elf_edit(fout, opts)
  193. if __name__ == '__main__':
  194. main(sys.argv[1:])