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