blob: ec2472c4cf2bd8a8ae28f65093078670ad235f47 [file] [log] [blame]
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helpers that interact with the "readelf" tool."""
import argparse
import logging
import os
import re
import subprocess
import archive_util
import models
import path_util
def BuildIdFromElf(elf_path):
"""Returns the Build ID for the given binary."""
args = [path_util.GetReadElfPath(), '-n', elf_path]
stdout = subprocess.check_output(args, encoding='ascii')
match = re.search(r'Build ID: (\w+)', stdout)
assert match, 'Build ID not found from running: ' + ' '.join(args)
return match.group(1)
def ArchFromElf(elf_path):
"""Returns the GN architecture for the given binary."""
args = [path_util.GetReadElfPath(), '-h', elf_path]
stdout = subprocess.check_output(args, encoding='ascii')
machine = re.search(r'Machine:\s*(.+)', stdout).group(1)
if machine == 'Intel 80386':
return 'x86'
if machine == 'Advanced Micro Devices X86-64':
return 'x64'
if machine == 'ARM':
return 'arm'
if machine == 'AArch64':
return 'arm64'
return machine
def SectionInfoFromElf(elf_path):
"""Finds the address and size of all ELF sections
Merges custom sections (those without a "." prefix) into their proceeding
sections.
Returns:
A dict of section_name->(start_address, size).
"""
args = [path_util.GetReadElfPath(), '-S', '--wide', elf_path]
stdout = subprocess.check_output(args, encoding='ascii')
section_ranges = {}
# Matches [ 2] .hash HASH 00000000006681f0 0001f0 003154 04 A 3 0 8
prev_section_name = None
for match in re.finditer(r'\[[\s\d]+\] (.*)$', stdout, re.MULTILINE):
items = match.group(1).split()
section_name = items[0]
# The first line looks like:
# [ 0] NULL 00000000 000000 000000 00 0 0 0
if section_name == 'NULL':
continue
assert not section_name.startswith('.debug'), (
'Should not section sizes of an unstripped binary.')
# Stop if we hit any partitions.
if section_name.endswith('_partition'):
break
section_type = items[1]
if section_type == 'NOBITS':
# Ensure we don't count BSS as real size.
assert section_name in models.BSS_SECTIONS, (
'BSS_SECTIONS out of date: ' + section_name)
section_range = (int(items[2], 16), int(items[4], 16))
# E.g. Merge user-defined sections. e.g.: malloc_hook, protected_memory.
if not section_name.startswith('.'):
logging.info('Merged %s into %s', section_name, prev_section_name)
archive_util.ExtendSectionRangeAdjacent(section_ranges, prev_section_name,
section_range[0],
section_range[1])
else:
section_ranges[items[0]] = section_range
prev_section_name = section_name
return section_ranges
def CollectRelocationAddresses(elf_path):
"""Returns the list of addresses that are targets for relative relocations."""
cmd = [path_util.GetReadElfPath(), '--relocs', elf_path]
ret = subprocess.check_output(cmd, encoding='ascii').splitlines()
# Grab first column from (sample output) '02de6d5c 00000017 R_ARM_RELATIVE'
return [int(l.split(maxsplit=1)[0], 16) for l in ret if 'R_ARM_RELATIVE' in l]
def main():
parser = argparse.ArgumentParser()
parser.add_argument('elf_path', type=os.path.realpath)
args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG,
format='%(levelname).1s %(relativeCreated)6d %(message)s')
# Other functions in this file have test entrypoints in object_analyzer.py.
section_ranges = SectionInfoFromElf(args.elf_path)
for name, (addr, size) in section_ranges.items():
print(f'{name:20} {addr:20x}\t{size}')
if __name__ == '__main__':
main()