xref: /aosp_15_r20/external/angle/build/chromeos/embed_sections.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*8975f5c5SAndroid Build Coastguard Worker#
3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2024 The Chromium Authors
4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
6*8975f5c5SAndroid Build Coastguard Worker'''
7*8975f5c5SAndroid Build Coastguard WorkerEmbed the address and size of elf sections into the pre-defined symbols.
8*8975f5c5SAndroid Build Coastguard Worker
9*8975f5c5SAndroid Build Coastguard WorkerThe embedded values are used for performance optimization by mlock(2)ing the
10*8975f5c5SAndroid Build Coastguard Workersections on ChromeOS. See chromeos/ash/components/memory/memory.cc for details.
11*8975f5c5SAndroid Build Coastguard Worker'''
12*8975f5c5SAndroid Build Coastguard Worker
13*8975f5c5SAndroid Build Coastguard Workerimport argparse
14*8975f5c5SAndroid Build Coastguard Workerimport os
15*8975f5c5SAndroid Build Coastguard Workerimport subprocess
16*8975f5c5SAndroid Build Coastguard Workerimport sys
17*8975f5c5SAndroid Build Coastguard Workerimport shutil
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard Workerllvm_readelf = os.path.join(
20*8975f5c5SAndroid Build Coastguard Worker    os.path.dirname(sys.argv[0]), '..', '..', 'third_party', 'llvm-build',
21*8975f5c5SAndroid Build Coastguard Worker    'Release+Asserts', 'bin', 'llvm-readelf')
22*8975f5c5SAndroid Build Coastguard Worker
23*8975f5c5SAndroid Build Coastguard WorkerTARGET_SECTIONS = {
24*8975f5c5SAndroid Build Coastguard Worker    '.rodata': {
25*8975f5c5SAndroid Build Coastguard Worker        'addr': 'kRodataAddr',
26*8975f5c5SAndroid Build Coastguard Worker        'size': 'kRodataSize'
27*8975f5c5SAndroid Build Coastguard Worker    },
28*8975f5c5SAndroid Build Coastguard Worker    '.text.hot': {
29*8975f5c5SAndroid Build Coastguard Worker        'addr': 'kTextHotAddr',
30*8975f5c5SAndroid Build Coastguard Worker        'size': 'kTextHotSize'
31*8975f5c5SAndroid Build Coastguard Worker    },
32*8975f5c5SAndroid Build Coastguard Worker}
33*8975f5c5SAndroid Build Coastguard Worker
34*8975f5c5SAndroid Build Coastguard Worker
35*8975f5c5SAndroid Build Coastguard Workerdef parse_endianess(objdump_result):
36*8975f5c5SAndroid Build Coastguard Worker  for line in objdump_result.splitlines():
37*8975f5c5SAndroid Build Coastguard Worker    line = line.strip()
38*8975f5c5SAndroid Build Coastguard Worker    if line.startswith('Data:'):
39*8975f5c5SAndroid Build Coastguard Worker      if '1' in line:
40*8975f5c5SAndroid Build Coastguard Worker        return 'big'
41*8975f5c5SAndroid Build Coastguard Worker      if '2' in line:
42*8975f5c5SAndroid Build Coastguard Worker        return 'little'
43*8975f5c5SAndroid Build Coastguard Worker  raise ValueError('No endian found')
44*8975f5c5SAndroid Build Coastguard Worker
45*8975f5c5SAndroid Build Coastguard Worker
46*8975f5c5SAndroid Build Coastguard Workerdef assert_elf_type(objdump_result):
47*8975f5c5SAndroid Build Coastguard Worker  for line in objdump_result.splitlines():
48*8975f5c5SAndroid Build Coastguard Worker    line = line.strip()
49*8975f5c5SAndroid Build Coastguard Worker    if line.startswith('Class:'):
50*8975f5c5SAndroid Build Coastguard Worker      if 'ELF64' in line or 'ELF32' in line:
51*8975f5c5SAndroid Build Coastguard Worker        return
52*8975f5c5SAndroid Build Coastguard Worker      raise ValueError('Class is not ELF64 nor ELF32: ' + line)
53*8975f5c5SAndroid Build Coastguard Worker  raise ValueError('No class found')
54*8975f5c5SAndroid Build Coastguard Worker
55*8975f5c5SAndroid Build Coastguard Worker
56*8975f5c5SAndroid Build Coastguard Workerdef parse_section_info(objdump_result, section_name):
57*8975f5c5SAndroid Build Coastguard Worker  for line in objdump_result.splitlines():
58*8975f5c5SAndroid Build Coastguard Worker    row = line.strip().split()
59*8975f5c5SAndroid Build Coastguard Worker    if len(row) < 2:
60*8975f5c5SAndroid Build Coastguard Worker      continue
61*8975f5c5SAndroid Build Coastguard Worker    if row[1] == section_name:
62*8975f5c5SAndroid Build Coastguard Worker      # 3: Address, 4: Offset, 5: Size
63*8975f5c5SAndroid Build Coastguard Worker      return (int(row[3], base=16), int(row[4], base=16), int(row[5], base=16))
64*8975f5c5SAndroid Build Coastguard Worker  return (0, 0, 0)
65*8975f5c5SAndroid Build Coastguard Worker
66*8975f5c5SAndroid Build Coastguard Worker
67*8975f5c5SAndroid Build Coastguard Workerdef create_symbol_map(binary_input, objdump_result):
68*8975f5c5SAndroid Build Coastguard Worker  (rodata_section_addr, rodata_section_offset,
69*8975f5c5SAndroid Build Coastguard Worker   rodata_section_size) = parse_section_info(objdump_result, '.rodata')
70*8975f5c5SAndroid Build Coastguard Worker
71*8975f5c5SAndroid Build Coastguard Worker  command = [llvm_readelf, '--symbols', binary_input]
72*8975f5c5SAndroid Build Coastguard Worker  with subprocess.Popen(command, stdout=subprocess.PIPE, text=True) as process:
73*8975f5c5SAndroid Build Coastguard Worker
74*8975f5c5SAndroid Build Coastguard Worker    variable_names = [
75*8975f5c5SAndroid Build Coastguard Worker        var for maps in TARGET_SECTIONS.values() for var in maps.values()
76*8975f5c5SAndroid Build Coastguard Worker    ]
77*8975f5c5SAndroid Build Coastguard Worker    result = {}
78*8975f5c5SAndroid Build Coastguard Worker    while len(result) < len(variable_names):
79*8975f5c5SAndroid Build Coastguard Worker      line = process.stdout.readline()
80*8975f5c5SAndroid Build Coastguard Worker      if not line:
81*8975f5c5SAndroid Build Coastguard Worker        break
82*8975f5c5SAndroid Build Coastguard Worker      for var in variable_names:
83*8975f5c5SAndroid Build Coastguard Worker        if var in line:
84*8975f5c5SAndroid Build Coastguard Worker          row = line.strip().split()
85*8975f5c5SAndroid Build Coastguard Worker          if row[2] == '8':
86*8975f5c5SAndroid Build Coastguard Worker            size = 8
87*8975f5c5SAndroid Build Coastguard Worker          elif row[2] == '4':
88*8975f5c5SAndroid Build Coastguard Worker            size = 4
89*8975f5c5SAndroid Build Coastguard Worker          else:
90*8975f5c5SAndroid Build Coastguard Worker            raise ValueError('variable size is not 8 or 4: ' + line)
91*8975f5c5SAndroid Build Coastguard Worker          addr = int(row[1], base=16)
92*8975f5c5SAndroid Build Coastguard Worker          rodata_section_end_addr = rodata_section_addr + rodata_section_size
93*8975f5c5SAndroid Build Coastguard Worker          if addr < rodata_section_addr or addr >= rodata_section_end_addr:
94*8975f5c5SAndroid Build Coastguard Worker            raise ValueError(var + ' is not in .rodata section')
95*8975f5c5SAndroid Build Coastguard Worker          offset = addr - rodata_section_addr + rodata_section_offset
96*8975f5c5SAndroid Build Coastguard Worker          result[var] = (offset, size)
97*8975f5c5SAndroid Build Coastguard Worker
98*8975f5c5SAndroid Build Coastguard Worker  return result
99*8975f5c5SAndroid Build Coastguard Worker
100*8975f5c5SAndroid Build Coastguard Worker
101*8975f5c5SAndroid Build Coastguard Workerdef overwrite_variable(file, symbol_map, endianess, section_name, var_type,
102*8975f5c5SAndroid Build Coastguard Worker                       value):
103*8975f5c5SAndroid Build Coastguard Worker  (var_offset, var_size) = symbol_map[TARGET_SECTIONS[section_name][var_type]]
104*8975f5c5SAndroid Build Coastguard Worker  file.seek(var_offset)
105*8975f5c5SAndroid Build Coastguard Worker  if file.write(
106*8975f5c5SAndroid Build Coastguard Worker      value.to_bytes(length=var_size, byteorder=endianess,
107*8975f5c5SAndroid Build Coastguard Worker                     signed=False)) != var_size:
108*8975f5c5SAndroid Build Coastguard Worker    raise ValueError('failed to write value to file')
109*8975f5c5SAndroid Build Coastguard Worker
110*8975f5c5SAndroid Build Coastguard Worker
111*8975f5c5SAndroid Build Coastguard Workerdef main():
112*8975f5c5SAndroid Build Coastguard Worker  argparser = argparse.ArgumentParser(
113*8975f5c5SAndroid Build Coastguard Worker      description='embed sections informataion into binary.')
114*8975f5c5SAndroid Build Coastguard Worker
115*8975f5c5SAndroid Build Coastguard Worker  argparser.add_argument('--binary-input', help='exe file path.')
116*8975f5c5SAndroid Build Coastguard Worker  argparser.add_argument('--binary-output', help='embedded file path.')
117*8975f5c5SAndroid Build Coastguard Worker  args = argparser.parse_args()
118*8975f5c5SAndroid Build Coastguard Worker
119*8975f5c5SAndroid Build Coastguard Worker  objdump_result = subprocess.run([llvm_readelf, '-e', args.binary_input],
120*8975f5c5SAndroid Build Coastguard Worker                                  stdout=subprocess.PIPE,
121*8975f5c5SAndroid Build Coastguard Worker                                  check=True,
122*8975f5c5SAndroid Build Coastguard Worker                                  text=True).stdout
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker  assert_elf_type(objdump_result)
125*8975f5c5SAndroid Build Coastguard Worker
126*8975f5c5SAndroid Build Coastguard Worker  symbol_map = create_symbol_map(args.binary_input, objdump_result)
127*8975f5c5SAndroid Build Coastguard Worker  if len(symbol_map) != len(set(symbol_map.values())):
128*8975f5c5SAndroid Build Coastguard Worker    raise ValueError(f'symbol_map overlaps: {symbol_map}')
129*8975f5c5SAndroid Build Coastguard Worker
130*8975f5c5SAndroid Build Coastguard Worker  endianess = parse_endianess(objdump_result)
131*8975f5c5SAndroid Build Coastguard Worker
132*8975f5c5SAndroid Build Coastguard Worker  shutil.copyfile(args.binary_input, args.binary_output)
133*8975f5c5SAndroid Build Coastguard Worker
134*8975f5c5SAndroid Build Coastguard Worker  with open(args.binary_output, 'r+b') as file:
135*8975f5c5SAndroid Build Coastguard Worker    for section_name in TARGET_SECTIONS:
136*8975f5c5SAndroid Build Coastguard Worker      (addr, _, size) = parse_section_info(objdump_result, section_name)
137*8975f5c5SAndroid Build Coastguard Worker      overwrite_variable(file, symbol_map, endianess, section_name, 'addr',
138*8975f5c5SAndroid Build Coastguard Worker                         addr)
139*8975f5c5SAndroid Build Coastguard Worker      overwrite_variable(file, symbol_map, endianess, section_name, 'size',
140*8975f5c5SAndroid Build Coastguard Worker                         size)
141*8975f5c5SAndroid Build Coastguard Worker
142*8975f5c5SAndroid Build Coastguard Worker  objdump_result_after = subprocess.run(
143*8975f5c5SAndroid Build Coastguard Worker      [llvm_readelf, '-e', args.binary_output],
144*8975f5c5SAndroid Build Coastguard Worker      stdout=subprocess.PIPE,
145*8975f5c5SAndroid Build Coastguard Worker      check=True,
146*8975f5c5SAndroid Build Coastguard Worker      text=True).stdout
147*8975f5c5SAndroid Build Coastguard Worker  if objdump_result_after != objdump_result:
148*8975f5c5SAndroid Build Coastguard Worker    raise ValueError('realelf result has changed')
149*8975f5c5SAndroid Build Coastguard Worker
150*8975f5c5SAndroid Build Coastguard Worker  return 0
151*8975f5c5SAndroid Build Coastguard Worker
152*8975f5c5SAndroid Build Coastguard Worker
153*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
154*8975f5c5SAndroid Build Coastguard Worker  sys.exit(main())
155