1*7594170eSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*7594170eSAndroid Build Coastguard Worker# 3*7594170eSAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project 4*7594170eSAndroid Build Coastguard Worker# 5*7594170eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*7594170eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*7594170eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*7594170eSAndroid Build Coastguard Worker# 9*7594170eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*7594170eSAndroid Build Coastguard Worker# 11*7594170eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*7594170eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*7594170eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*7594170eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*7594170eSAndroid Build Coastguard Worker# limitations under the License.""" 16*7594170eSAndroid Build Coastguard Worker 17*7594170eSAndroid Build Coastguard Workerimport pathlib 18*7594170eSAndroid Build Coastguard Workerimport re 19*7594170eSAndroid Build Coastguard Workerimport subprocess 20*7594170eSAndroid Build Coastguard Workerfrom diffs.diff import Diff, ExtractInfo 21*7594170eSAndroid Build Coastguard Worker 22*7594170eSAndroid Build Coastguard Workerclass _Symbol: 23*7594170eSAndroid Build Coastguard Worker """Data structure to hold a symbol as specified by nm 24*7594170eSAndroid Build Coastguard Worker 25*7594170eSAndroid Build Coastguard Worker Equality of symbols is based on their name and attributes. 26*7594170eSAndroid Build Coastguard Worker 27*7594170eSAndroid Build Coastguard Worker The self._addr property is excluded from comparisons in this class 28*7594170eSAndroid Build Coastguard Worker because the location of a symbol in one binary is not a useful 29*7594170eSAndroid Build Coastguard Worker difference from another binary. 30*7594170eSAndroid Build Coastguard Worker """ 31*7594170eSAndroid Build Coastguard Worker def __init__(self, name, addr, attr): 32*7594170eSAndroid Build Coastguard Worker self.name = name 33*7594170eSAndroid Build Coastguard Worker self._addr = addr 34*7594170eSAndroid Build Coastguard Worker self.attr = attr 35*7594170eSAndroid Build Coastguard Worker def __hash__(self): 36*7594170eSAndroid Build Coastguard Worker return (self.name + self.attr).__hash__() 37*7594170eSAndroid Build Coastguard Worker def __eq__(self, other): 38*7594170eSAndroid Build Coastguard Worker return self.name == other.name and self.attr == other.attr 39*7594170eSAndroid Build Coastguard Worker def __repr__(self): 40*7594170eSAndroid Build Coastguard Worker return f"{self.name}{{{self.attr}}}" 41*7594170eSAndroid Build Coastguard Worker 42*7594170eSAndroid Build Coastguard Worker 43*7594170eSAndroid Build Coastguard Workerclass NmSymbolDiff(Diff): 44*7594170eSAndroid Build Coastguard Worker """Compares symbols the symbol table output by nm 45*7594170eSAndroid Build Coastguard Worker 46*7594170eSAndroid Build Coastguard Worker Example nm output: 47*7594170eSAndroid Build Coastguard Worker 0000000000000140 t GetExceptionSummary 48*7594170eSAndroid Build Coastguard Worker U ExpandableStringInitialize 49*7594170eSAndroid Build Coastguard Worker U ExpandableStringRelease 50*7594170eSAndroid Build Coastguard Worker 0000000000000cf0 T jniCreateString 51*7594170eSAndroid Build Coastguard Worker 52*7594170eSAndroid Build Coastguard Worker The first column is the address of the symbol in the binary, the second 53*7594170eSAndroid Build Coastguard Worker column is an attribute associated with the symbol (see man nm for details), 54*7594170eSAndroid Build Coastguard Worker and the last column is the demangled symbol name. 55*7594170eSAndroid Build Coastguard Worker """ 56*7594170eSAndroid Build Coastguard Worker _nm_re = re.compile(r"^(\w+)?\s+([a-zA-Z])\s(\S+)$") 57*7594170eSAndroid Build Coastguard Worker 58*7594170eSAndroid Build Coastguard Worker def __init__(self, tool: ExtractInfo, tool_name: str): 59*7594170eSAndroid Build Coastguard Worker self.tool = tool 60*7594170eSAndroid Build Coastguard Worker self.tool_name = tool_name 61*7594170eSAndroid Build Coastguard Worker 62*7594170eSAndroid Build Coastguard Worker def _read_symbols(nm_output): 63*7594170eSAndroid Build Coastguard Worker symbols = set() 64*7594170eSAndroid Build Coastguard Worker for line in nm_output: 65*7594170eSAndroid Build Coastguard Worker match = NmSymbolDiff._nm_re.match(line) 66*7594170eSAndroid Build Coastguard Worker if match: 67*7594170eSAndroid Build Coastguard Worker symbols.add(_Symbol(match.group(3), match.group(1), match.group(2))) 68*7594170eSAndroid Build Coastguard Worker return symbols 69*7594170eSAndroid Build Coastguard Worker 70*7594170eSAndroid Build Coastguard Worker def diff(self, left_path: pathlib.Path, right_path: pathlib.Path) -> list[str]: 71*7594170eSAndroid Build Coastguard Worker left_nm = subprocess.run(["nm", left_path], capture_output=True, encoding="utf-8").stdout.splitlines() 72*7594170eSAndroid Build Coastguard Worker right_nm = subprocess.run(["nm", right_path], capture_output=True, encoding="utf-8").stdout.splitlines() 73*7594170eSAndroid Build Coastguard Worker left_symbols = NmSymbolDiff._read_symbols(left_nm) 74*7594170eSAndroid Build Coastguard Worker right_symbols = NmSymbolDiff._read_symbols(right_nm) 75*7594170eSAndroid Build Coastguard Worker 76*7594170eSAndroid Build Coastguard Worker left_only = [] 77*7594170eSAndroid Build Coastguard Worker for s in left_symbols: 78*7594170eSAndroid Build Coastguard Worker if s not in right_symbols: 79*7594170eSAndroid Build Coastguard Worker left_only.append(s) 80*7594170eSAndroid Build Coastguard Worker right_only = [] 81*7594170eSAndroid Build Coastguard Worker for s in right_symbols: 82*7594170eSAndroid Build Coastguard Worker if s not in left_symbols: 83*7594170eSAndroid Build Coastguard Worker right_only.append(s) 84*7594170eSAndroid Build Coastguard Worker 85*7594170eSAndroid Build Coastguard Worker errors = [] 86*7594170eSAndroid Build Coastguard Worker if left_only: 87*7594170eSAndroid Build Coastguard Worker errors.append(f"symbols in {left_path} not in {right_path}:") 88*7594170eSAndroid Build Coastguard Worker errors.extend("\t" + str(s) for s in left_only) 89*7594170eSAndroid Build Coastguard Worker if right_only: 90*7594170eSAndroid Build Coastguard Worker errors.append(f"symbols in {right_path} not in {left_path}:") 91*7594170eSAndroid Build Coastguard Worker errors.extend("\t" + str(s) for s in right_only) 92*7594170eSAndroid Build Coastguard Worker 93*7594170eSAndroid Build Coastguard Worker return errors 94