Linux: consolidate dup2 implementation
[glibc.git] / scripts / tst-elf-edit.py
blob0e19ce1e7392f3cadbbc5766cab17415a722e231
1 #!/usr/bin/python3
2 # ELF editor for load align tests.
3 # Copyright (C) 2022 Free Software Foundation, Inc.
4 # Copyright The GNU Toolchain Authors.
5 # This file is part of the GNU C Library.
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.
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.
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/>.
21 import argparse
22 import os
23 import sys
24 import struct
26 EI_NIDENT=16
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'
37 EI_CLASS=4
38 ELFCLASSNONE=b'0'
39 ELFCLASS32=b'\x01'
40 ELFCLASS64=b'\x02'
42 EI_DATA=5
43 ELFDATA2LSB=b'\x01'
44 ELFDATA2MSB=b'\x02'
46 ET_EXEC=2
47 ET_DYN=3
49 PT_LOAD=1
50 PT_TLS=7
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)
58 class Elf_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)
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]
82 class Elf_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)
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]
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)
140 def error(msg):
141 print(msg, file=sys.stderr)
142 sys.exit(1)
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)
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
159 def elf_edit(f, opts):
160 ei_nident_fmt = 'c' * EI_NIDENT
161 ei_nident_len = struct.calcsize(ei_nident_fmt)
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)
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))
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]))
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]))
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))
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
204 if opts.maximize_tls_size and not maximize_tls_size_done:
205 error('{}: TLS maximum size was not updated'.format(f.name))
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
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)
225 if __name__ == '__main__':
226 main(sys.argv[1:])