xref: /aosp_15_r20/system/unwinding/libunwindstack/tools/strip.py (revision eb293b8f56ee8303637c5595cfcdeef8039e85c6)
1*eb293b8fSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*eb293b8fSAndroid Build Coastguard Worker#
3*eb293b8fSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project
4*eb293b8fSAndroid Build Coastguard Worker#
5*eb293b8fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*eb293b8fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*eb293b8fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*eb293b8fSAndroid Build Coastguard Worker#
9*eb293b8fSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*eb293b8fSAndroid Build Coastguard Worker#
11*eb293b8fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*eb293b8fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*eb293b8fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*eb293b8fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*eb293b8fSAndroid Build Coastguard Worker# limitations under the License.
16*eb293b8fSAndroid Build Coastguard Worker#
17*eb293b8fSAndroid Build Coastguard Worker
18*eb293b8fSAndroid Build Coastguard Workerimport argparse, subprocess, re, os, glob, array, gzip
19*eb293b8fSAndroid Build Coastguard Worker
20*eb293b8fSAndroid Build Coastguard WorkerDESCRIPTION = "This tool reduces ELF size using stripping and compression"
21*eb293b8fSAndroid Build Coastguard Worker
22*eb293b8fSAndroid Build Coastguard WorkerSTRIP_SECTIONS = [".text", ".rodata"]
23*eb293b8fSAndroid Build Coastguard Worker
24*eb293b8fSAndroid Build Coastguard WorkerREADELF_FORMAT = """
25*eb293b8fSAndroid Build Coastguard Worker  \s+(?P<index>[0-9\[\] ]+)
26*eb293b8fSAndroid Build Coastguard Worker  \s+(?P<name>[a-z_.]+)
27*eb293b8fSAndroid Build Coastguard Worker  \s+(?P<type>[A-Z_]+)
28*eb293b8fSAndroid Build Coastguard Worker  \s+(?P<address>[0-9a-f]+)
29*eb293b8fSAndroid Build Coastguard Worker  \s+(?P<offset>[0-9a-f]+)
30*eb293b8fSAndroid Build Coastguard Worker  \s+(?P<size>[0-9a-f]+)
31*eb293b8fSAndroid Build Coastguard Worker"""
32*eb293b8fSAndroid Build Coastguard Worker
33*eb293b8fSAndroid Build Coastguard Workerdef strip(path):
34*eb293b8fSAndroid Build Coastguard Worker  proc = subprocess.run(["readelf", "--file-header", "--sections", path],
35*eb293b8fSAndroid Build Coastguard Worker                        stdout=subprocess.PIPE, universal_newlines=True)
36*eb293b8fSAndroid Build Coastguard Worker  assert(proc.returncode == 0)  # readelf command failed
37*eb293b8fSAndroid Build Coastguard Worker  sections = {m["name"] : m for m in re.finditer(READELF_FORMAT, proc.stdout, re.VERBOSE)}
38*eb293b8fSAndroid Build Coastguard Worker  for name in STRIP_SECTIONS:
39*eb293b8fSAndroid Build Coastguard Worker    if name == ".text" and os.path.basename(path) in ["vdso", "vdso.so", "libc.so"]:
40*eb293b8fSAndroid Build Coastguard Worker      continue  # Stripping these libraries breaks signal handler unwinding.
41*eb293b8fSAndroid Build Coastguard Worker    section = sections.get(name)
42*eb293b8fSAndroid Build Coastguard Worker    if not section:
43*eb293b8fSAndroid Build Coastguard Worker      print("Warning: {} not found in {}".format(name, path))
44*eb293b8fSAndroid Build Coastguard Worker    if section and section["type"] != "NOBITS":
45*eb293b8fSAndroid Build Coastguard Worker      offset, size = int(section["offset"], 16), int(section["size"], 16) & ~1
46*eb293b8fSAndroid Build Coastguard Worker      with open(path, "r+b") as f:
47*eb293b8fSAndroid Build Coastguard Worker        f.seek(offset)
48*eb293b8fSAndroid Build Coastguard Worker        data = array.array('H')  # 16-bit unsigned integer array.
49*eb293b8fSAndroid Build Coastguard Worker        data.frombytes(f.read(size))
50*eb293b8fSAndroid Build Coastguard Worker        # Preserve top bits for thumb so that we can still determine instruction size.
51*eb293b8fSAndroid Build Coastguard Worker        is_thumb = (name == ".text" and re.search("Machine:\s+ARM", proc.stdout))
52*eb293b8fSAndroid Build Coastguard Worker        for i in range(len(data)):
53*eb293b8fSAndroid Build Coastguard Worker          data[i] = 0xffff if is_thumb and (data[i] & 0xe000) == 0xe000 else 0
54*eb293b8fSAndroid Build Coastguard Worker        f.seek(offset)
55*eb293b8fSAndroid Build Coastguard Worker        f.write(data.tobytes())
56*eb293b8fSAndroid Build Coastguard Worker
57*eb293b8fSAndroid Build Coastguard Worker  # gzip-compress the file to take advantage of the zeroed sections.
58*eb293b8fSAndroid Build Coastguard Worker  with open(path, 'rb') as src, gzip.open(path + ".gz", 'wb') as dst:
59*eb293b8fSAndroid Build Coastguard Worker    dst.write(src.read())
60*eb293b8fSAndroid Build Coastguard Worker  os.remove(path)
61*eb293b8fSAndroid Build Coastguard Worker
62*eb293b8fSAndroid Build Coastguard Workerdef main():
63*eb293b8fSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description=DESCRIPTION)
64*eb293b8fSAndroid Build Coastguard Worker  parser.add_argument('target', nargs='+', help="ELF file or whole directory to strip")
65*eb293b8fSAndroid Build Coastguard Worker  args = parser.parse_args()
66*eb293b8fSAndroid Build Coastguard Worker
67*eb293b8fSAndroid Build Coastguard Worker  for path in args.target:
68*eb293b8fSAndroid Build Coastguard Worker    if os.path.isdir(path):
69*eb293b8fSAndroid Build Coastguard Worker      for path in glob.glob(os.path.join(path, "**/*"), recursive=True):
70*eb293b8fSAndroid Build Coastguard Worker        if os.path.isfile(path) and open(path, "rb").read(4) == b"\x7FELF":
71*eb293b8fSAndroid Build Coastguard Worker          strip(path)
72*eb293b8fSAndroid Build Coastguard Worker    else:
73*eb293b8fSAndroid Build Coastguard Worker      strip(path)
74*eb293b8fSAndroid Build Coastguard Worker
75*eb293b8fSAndroid Build Coastguard Workerif __name__ == '__main__':
76*eb293b8fSAndroid Build Coastguard Worker  main()
77