xref: /aosp_15_r20/build/make/tools/check_elf_file.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*9e94795aSAndroid Build Coastguard Worker#
3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project
4*9e94795aSAndroid Build Coastguard Worker#
5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*9e94795aSAndroid Build Coastguard Worker#
9*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*9e94795aSAndroid Build Coastguard Worker#
11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Worker"""ELF file checker.
18*9e94795aSAndroid Build Coastguard Worker
19*9e94795aSAndroid Build Coastguard WorkerThis command ensures all undefined symbols in an ELF file can be resolved to
20*9e94795aSAndroid Build Coastguard Workerglobal (or weak) symbols defined in shared objects specified in DT_NEEDED
21*9e94795aSAndroid Build Coastguard Workerentries.
22*9e94795aSAndroid Build Coastguard Worker"""
23*9e94795aSAndroid Build Coastguard Worker
24*9e94795aSAndroid Build Coastguard Workerfrom __future__ import print_function
25*9e94795aSAndroid Build Coastguard Worker
26*9e94795aSAndroid Build Coastguard Workerimport argparse
27*9e94795aSAndroid Build Coastguard Workerimport collections
28*9e94795aSAndroid Build Coastguard Workerimport os
29*9e94795aSAndroid Build Coastguard Workerimport os.path
30*9e94795aSAndroid Build Coastguard Workerimport re
31*9e94795aSAndroid Build Coastguard Workerimport struct
32*9e94795aSAndroid Build Coastguard Workerimport subprocess
33*9e94795aSAndroid Build Coastguard Workerimport sys
34*9e94795aSAndroid Build Coastguard Worker
35*9e94795aSAndroid Build Coastguard Worker
36*9e94795aSAndroid Build Coastguard Worker_ELF_MAGIC = b'\x7fELF'
37*9e94795aSAndroid Build Coastguard Worker
38*9e94795aSAndroid Build Coastguard Worker
39*9e94795aSAndroid Build Coastguard Worker# Known machines
40*9e94795aSAndroid Build Coastguard Worker_EM_386 = 3
41*9e94795aSAndroid Build Coastguard Worker_EM_ARM = 40
42*9e94795aSAndroid Build Coastguard Worker_EM_X86_64 = 62
43*9e94795aSAndroid Build Coastguard Worker_EM_AARCH64 = 183
44*9e94795aSAndroid Build Coastguard Worker
45*9e94795aSAndroid Build Coastguard Worker_KNOWN_MACHINES = {_EM_386, _EM_ARM, _EM_X86_64, _EM_AARCH64}
46*9e94795aSAndroid Build Coastguard Worker
47*9e94795aSAndroid Build Coastguard Worker
48*9e94795aSAndroid Build Coastguard Worker# ELF header struct
49*9e94795aSAndroid Build Coastguard Worker_ELF_HEADER_STRUCT = (
50*9e94795aSAndroid Build Coastguard Worker  ('ei_magic', '4s'),
51*9e94795aSAndroid Build Coastguard Worker  ('ei_class', 'B'),
52*9e94795aSAndroid Build Coastguard Worker  ('ei_data', 'B'),
53*9e94795aSAndroid Build Coastguard Worker  ('ei_version', 'B'),
54*9e94795aSAndroid Build Coastguard Worker  ('ei_osabi', 'B'),
55*9e94795aSAndroid Build Coastguard Worker  ('ei_pad', '8s'),
56*9e94795aSAndroid Build Coastguard Worker  ('e_type', 'H'),
57*9e94795aSAndroid Build Coastguard Worker  ('e_machine', 'H'),
58*9e94795aSAndroid Build Coastguard Worker  ('e_version', 'I'),
59*9e94795aSAndroid Build Coastguard Worker)
60*9e94795aSAndroid Build Coastguard Worker
61*9e94795aSAndroid Build Coastguard Worker_ELF_HEADER_STRUCT_FMT = ''.join(_fmt for _, _fmt in _ELF_HEADER_STRUCT)
62*9e94795aSAndroid Build Coastguard Worker
63*9e94795aSAndroid Build Coastguard Worker
64*9e94795aSAndroid Build Coastguard WorkerELFHeader = collections.namedtuple(
65*9e94795aSAndroid Build Coastguard Worker  'ELFHeader', [_name for _name, _ in _ELF_HEADER_STRUCT])
66*9e94795aSAndroid Build Coastguard Worker
67*9e94795aSAndroid Build Coastguard Worker
68*9e94795aSAndroid Build Coastguard WorkerELF = collections.namedtuple(
69*9e94795aSAndroid Build Coastguard Worker  'ELF',
70*9e94795aSAndroid Build Coastguard Worker  ('alignments', 'dt_soname', 'dt_needed', 'imported', 'exported', 'header'))
71*9e94795aSAndroid Build Coastguard Worker
72*9e94795aSAndroid Build Coastguard Worker
73*9e94795aSAndroid Build Coastguard Workerdef _get_os_name():
74*9e94795aSAndroid Build Coastguard Worker  """Get the host OS name."""
75*9e94795aSAndroid Build Coastguard Worker  if sys.platform.startswith('linux'):
76*9e94795aSAndroid Build Coastguard Worker    return 'linux'
77*9e94795aSAndroid Build Coastguard Worker  if sys.platform.startswith('darwin'):
78*9e94795aSAndroid Build Coastguard Worker    return 'darwin'
79*9e94795aSAndroid Build Coastguard Worker  raise ValueError(sys.platform + ' is not supported')
80*9e94795aSAndroid Build Coastguard Worker
81*9e94795aSAndroid Build Coastguard Worker
82*9e94795aSAndroid Build Coastguard Workerdef _get_build_top():
83*9e94795aSAndroid Build Coastguard Worker  """Find the build top of the source tree ($ANDROID_BUILD_TOP)."""
84*9e94795aSAndroid Build Coastguard Worker  prev_path = None
85*9e94795aSAndroid Build Coastguard Worker  curr_path = os.path.abspath(os.getcwd())
86*9e94795aSAndroid Build Coastguard Worker  while prev_path != curr_path:
87*9e94795aSAndroid Build Coastguard Worker    if os.path.exists(os.path.join(curr_path, '.repo')):
88*9e94795aSAndroid Build Coastguard Worker      return curr_path
89*9e94795aSAndroid Build Coastguard Worker    prev_path = curr_path
90*9e94795aSAndroid Build Coastguard Worker    curr_path = os.path.dirname(curr_path)
91*9e94795aSAndroid Build Coastguard Worker  return None
92*9e94795aSAndroid Build Coastguard Worker
93*9e94795aSAndroid Build Coastguard Worker
94*9e94795aSAndroid Build Coastguard Workerdef _select_latest_llvm_version(versions):
95*9e94795aSAndroid Build Coastguard Worker  """Select the latest LLVM prebuilts version from a set of versions."""
96*9e94795aSAndroid Build Coastguard Worker  pattern = re.compile('clang-r([0-9]+)([a-z]?)')
97*9e94795aSAndroid Build Coastguard Worker  found_rev = 0
98*9e94795aSAndroid Build Coastguard Worker  found_ver = None
99*9e94795aSAndroid Build Coastguard Worker  for curr_ver in versions:
100*9e94795aSAndroid Build Coastguard Worker    match = pattern.match(curr_ver)
101*9e94795aSAndroid Build Coastguard Worker    if not match:
102*9e94795aSAndroid Build Coastguard Worker      continue
103*9e94795aSAndroid Build Coastguard Worker    curr_rev = int(match.group(1))
104*9e94795aSAndroid Build Coastguard Worker    if not found_ver or curr_rev > found_rev or (
105*9e94795aSAndroid Build Coastguard Worker        curr_rev == found_rev and curr_ver > found_ver):
106*9e94795aSAndroid Build Coastguard Worker      found_rev = curr_rev
107*9e94795aSAndroid Build Coastguard Worker      found_ver = curr_ver
108*9e94795aSAndroid Build Coastguard Worker  return found_ver
109*9e94795aSAndroid Build Coastguard Worker
110*9e94795aSAndroid Build Coastguard Worker
111*9e94795aSAndroid Build Coastguard Workerdef _get_latest_llvm_version(llvm_dir):
112*9e94795aSAndroid Build Coastguard Worker  """Find the latest LLVM prebuilts version from `llvm_dir`."""
113*9e94795aSAndroid Build Coastguard Worker  return _select_latest_llvm_version(os.listdir(llvm_dir))
114*9e94795aSAndroid Build Coastguard Worker
115*9e94795aSAndroid Build Coastguard Worker
116*9e94795aSAndroid Build Coastguard Workerdef _get_llvm_dir():
117*9e94795aSAndroid Build Coastguard Worker  """Find the path to LLVM prebuilts."""
118*9e94795aSAndroid Build Coastguard Worker  build_top = _get_build_top()
119*9e94795aSAndroid Build Coastguard Worker
120*9e94795aSAndroid Build Coastguard Worker  llvm_prebuilts_base = os.environ.get('LLVM_PREBUILTS_BASE')
121*9e94795aSAndroid Build Coastguard Worker  if not llvm_prebuilts_base:
122*9e94795aSAndroid Build Coastguard Worker    llvm_prebuilts_base = os.path.join('prebuilts', 'clang', 'host')
123*9e94795aSAndroid Build Coastguard Worker
124*9e94795aSAndroid Build Coastguard Worker  llvm_dir = os.path.join(
125*9e94795aSAndroid Build Coastguard Worker    build_top, llvm_prebuilts_base, _get_os_name() + '-x86')
126*9e94795aSAndroid Build Coastguard Worker
127*9e94795aSAndroid Build Coastguard Worker  if not os.path.exists(llvm_dir):
128*9e94795aSAndroid Build Coastguard Worker    return None
129*9e94795aSAndroid Build Coastguard Worker
130*9e94795aSAndroid Build Coastguard Worker  llvm_prebuilts_version = os.environ.get('LLVM_PREBUILTS_VERSION')
131*9e94795aSAndroid Build Coastguard Worker  if not llvm_prebuilts_version:
132*9e94795aSAndroid Build Coastguard Worker    llvm_prebuilts_version = _get_latest_llvm_version(llvm_dir)
133*9e94795aSAndroid Build Coastguard Worker
134*9e94795aSAndroid Build Coastguard Worker  llvm_dir = os.path.join(llvm_dir, llvm_prebuilts_version)
135*9e94795aSAndroid Build Coastguard Worker
136*9e94795aSAndroid Build Coastguard Worker  if not os.path.exists(llvm_dir):
137*9e94795aSAndroid Build Coastguard Worker    return None
138*9e94795aSAndroid Build Coastguard Worker
139*9e94795aSAndroid Build Coastguard Worker  return llvm_dir
140*9e94795aSAndroid Build Coastguard Worker
141*9e94795aSAndroid Build Coastguard Worker
142*9e94795aSAndroid Build Coastguard Workerdef _get_llvm_readobj():
143*9e94795aSAndroid Build Coastguard Worker  """Find the path to llvm-readobj executable."""
144*9e94795aSAndroid Build Coastguard Worker  llvm_dir = _get_llvm_dir()
145*9e94795aSAndroid Build Coastguard Worker  llvm_readobj = os.path.join(llvm_dir, 'bin', 'llvm-readobj')
146*9e94795aSAndroid Build Coastguard Worker  return llvm_readobj if os.path.exists(llvm_readobj) else 'llvm-readobj'
147*9e94795aSAndroid Build Coastguard Worker
148*9e94795aSAndroid Build Coastguard Worker
149*9e94795aSAndroid Build Coastguard Workerclass ELFError(ValueError):
150*9e94795aSAndroid Build Coastguard Worker  """Generic ELF parse error"""
151*9e94795aSAndroid Build Coastguard Worker  pass
152*9e94795aSAndroid Build Coastguard Worker
153*9e94795aSAndroid Build Coastguard Worker
154*9e94795aSAndroid Build Coastguard Workerclass ELFInvalidMagicError(ELFError):
155*9e94795aSAndroid Build Coastguard Worker  """Invalid ELF magic word error"""
156*9e94795aSAndroid Build Coastguard Worker  def __init__(self):
157*9e94795aSAndroid Build Coastguard Worker    super(ELFInvalidMagicError, self).__init__('bad ELF magic')
158*9e94795aSAndroid Build Coastguard Worker
159*9e94795aSAndroid Build Coastguard Worker
160*9e94795aSAndroid Build Coastguard Workerclass ELFParser(object):
161*9e94795aSAndroid Build Coastguard Worker  """ELF file parser"""
162*9e94795aSAndroid Build Coastguard Worker
163*9e94795aSAndroid Build Coastguard Worker  @classmethod
164*9e94795aSAndroid Build Coastguard Worker  def _read_elf_header(cls, elf_file_path):
165*9e94795aSAndroid Build Coastguard Worker    """Read the ELF magic word from the beginning of the file."""
166*9e94795aSAndroid Build Coastguard Worker    with open(elf_file_path, 'rb') as elf_file:
167*9e94795aSAndroid Build Coastguard Worker      buf = elf_file.read(struct.calcsize(_ELF_HEADER_STRUCT_FMT))
168*9e94795aSAndroid Build Coastguard Worker      try:
169*9e94795aSAndroid Build Coastguard Worker        return ELFHeader(*struct.unpack(_ELF_HEADER_STRUCT_FMT, buf))
170*9e94795aSAndroid Build Coastguard Worker      except struct.error:
171*9e94795aSAndroid Build Coastguard Worker        return None
172*9e94795aSAndroid Build Coastguard Worker
173*9e94795aSAndroid Build Coastguard Worker
174*9e94795aSAndroid Build Coastguard Worker  @classmethod
175*9e94795aSAndroid Build Coastguard Worker  def open(cls, elf_file_path, llvm_readobj):
176*9e94795aSAndroid Build Coastguard Worker    """Open and parse the ELF file."""
177*9e94795aSAndroid Build Coastguard Worker    # Parse the ELF header to check the magic word.
178*9e94795aSAndroid Build Coastguard Worker    header = cls._read_elf_header(elf_file_path)
179*9e94795aSAndroid Build Coastguard Worker    if not header or header.ei_magic != _ELF_MAGIC:
180*9e94795aSAndroid Build Coastguard Worker      raise ELFInvalidMagicError()
181*9e94795aSAndroid Build Coastguard Worker
182*9e94795aSAndroid Build Coastguard Worker    # Run llvm-readobj and parse the output.
183*9e94795aSAndroid Build Coastguard Worker    return cls._read_llvm_readobj(elf_file_path, header, llvm_readobj)
184*9e94795aSAndroid Build Coastguard Worker
185*9e94795aSAndroid Build Coastguard Worker
186*9e94795aSAndroid Build Coastguard Worker  @classmethod
187*9e94795aSAndroid Build Coastguard Worker  def _find_prefix(cls, pattern, lines_it):
188*9e94795aSAndroid Build Coastguard Worker    """Iterate `lines_it` until finding a string that starts with `pattern`."""
189*9e94795aSAndroid Build Coastguard Worker    for line in lines_it:
190*9e94795aSAndroid Build Coastguard Worker      if line.startswith(pattern):
191*9e94795aSAndroid Build Coastguard Worker        return True
192*9e94795aSAndroid Build Coastguard Worker    return False
193*9e94795aSAndroid Build Coastguard Worker
194*9e94795aSAndroid Build Coastguard Worker
195*9e94795aSAndroid Build Coastguard Worker  @classmethod
196*9e94795aSAndroid Build Coastguard Worker  def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):
197*9e94795aSAndroid Build Coastguard Worker    """Run llvm-readobj and parse the output."""
198*9e94795aSAndroid Build Coastguard Worker    cmd = [llvm_readobj, '--program-headers', '--dynamic-table',
199*9e94795aSAndroid Build Coastguard Worker           '--dyn-symbols', elf_file_path]
200*9e94795aSAndroid Build Coastguard Worker    out = subprocess.check_output(cmd, text=True)
201*9e94795aSAndroid Build Coastguard Worker    lines = out.splitlines()
202*9e94795aSAndroid Build Coastguard Worker    return cls._parse_llvm_readobj(elf_file_path, header, lines)
203*9e94795aSAndroid Build Coastguard Worker
204*9e94795aSAndroid Build Coastguard Worker
205*9e94795aSAndroid Build Coastguard Worker  @classmethod
206*9e94795aSAndroid Build Coastguard Worker  def _parse_llvm_readobj(cls, elf_file_path, header, lines):
207*9e94795aSAndroid Build Coastguard Worker    """Parse the output of llvm-readobj."""
208*9e94795aSAndroid Build Coastguard Worker    lines_it = iter(lines)
209*9e94795aSAndroid Build Coastguard Worker    alignments = cls._parse_program_headers(lines_it)
210*9e94795aSAndroid Build Coastguard Worker    dt_soname, dt_needed = cls._parse_dynamic_table(elf_file_path, lines_it)
211*9e94795aSAndroid Build Coastguard Worker    imported, exported = cls._parse_dynamic_symbols(lines_it)
212*9e94795aSAndroid Build Coastguard Worker    return ELF(alignments, dt_soname, dt_needed, imported, exported, header)
213*9e94795aSAndroid Build Coastguard Worker
214*9e94795aSAndroid Build Coastguard Worker
215*9e94795aSAndroid Build Coastguard Worker  _PROGRAM_HEADERS_START_PATTERN = 'ProgramHeaders ['
216*9e94795aSAndroid Build Coastguard Worker  _PROGRAM_HEADERS_END_PATTERN = ']'
217*9e94795aSAndroid Build Coastguard Worker  _PROGRAM_HEADER_START_PATTERN = 'ProgramHeader {'
218*9e94795aSAndroid Build Coastguard Worker  _PROGRAM_HEADER_TYPE_PATTERN = re.compile('^\\s+Type:\\s+(.*)$')
219*9e94795aSAndroid Build Coastguard Worker  _PROGRAM_HEADER_ALIGN_PATTERN = re.compile('^\\s+Alignment:\\s+(.*)$')
220*9e94795aSAndroid Build Coastguard Worker  _PROGRAM_HEADER_END_PATTERN = '}'
221*9e94795aSAndroid Build Coastguard Worker
222*9e94795aSAndroid Build Coastguard Worker
223*9e94795aSAndroid Build Coastguard Worker  @classmethod
224*9e94795aSAndroid Build Coastguard Worker  def _parse_program_headers(cls, lines_it):
225*9e94795aSAndroid Build Coastguard Worker    """Parse the dynamic table section."""
226*9e94795aSAndroid Build Coastguard Worker    alignments = []
227*9e94795aSAndroid Build Coastguard Worker
228*9e94795aSAndroid Build Coastguard Worker    if not cls._find_prefix(cls._PROGRAM_HEADERS_START_PATTERN, lines_it):
229*9e94795aSAndroid Build Coastguard Worker      raise ELFError()
230*9e94795aSAndroid Build Coastguard Worker
231*9e94795aSAndroid Build Coastguard Worker    for line in lines_it:
232*9e94795aSAndroid Build Coastguard Worker      # Parse each program header
233*9e94795aSAndroid Build Coastguard Worker      if line.strip() == cls._PROGRAM_HEADER_START_PATTERN:
234*9e94795aSAndroid Build Coastguard Worker        p_align = None
235*9e94795aSAndroid Build Coastguard Worker        p_type = None
236*9e94795aSAndroid Build Coastguard Worker        for line in lines_it:
237*9e94795aSAndroid Build Coastguard Worker          if line.strip() == cls._PROGRAM_HEADER_END_PATTERN:
238*9e94795aSAndroid Build Coastguard Worker            if not p_align:
239*9e94795aSAndroid Build Coastguard Worker              raise ELFError("Could not parse alignment from program header!")
240*9e94795aSAndroid Build Coastguard Worker            if not p_type:
241*9e94795aSAndroid Build Coastguard Worker              raise ELFError("Could not parse type from program header!")
242*9e94795aSAndroid Build Coastguard Worker
243*9e94795aSAndroid Build Coastguard Worker            if p_type.startswith("PT_LOAD "):
244*9e94795aSAndroid Build Coastguard Worker              alignments.append(int(p_align))
245*9e94795aSAndroid Build Coastguard Worker            break
246*9e94795aSAndroid Build Coastguard Worker
247*9e94795aSAndroid Build Coastguard Worker          match = cls._PROGRAM_HEADER_TYPE_PATTERN.match(line)
248*9e94795aSAndroid Build Coastguard Worker          if match:
249*9e94795aSAndroid Build Coastguard Worker            p_type = match.group(1)
250*9e94795aSAndroid Build Coastguard Worker
251*9e94795aSAndroid Build Coastguard Worker          match = cls._PROGRAM_HEADER_ALIGN_PATTERN.match(line)
252*9e94795aSAndroid Build Coastguard Worker          if match:
253*9e94795aSAndroid Build Coastguard Worker            p_align = match.group(1)
254*9e94795aSAndroid Build Coastguard Worker
255*9e94795aSAndroid Build Coastguard Worker      if line == cls._PROGRAM_HEADERS_END_PATTERN:
256*9e94795aSAndroid Build Coastguard Worker        break
257*9e94795aSAndroid Build Coastguard Worker
258*9e94795aSAndroid Build Coastguard Worker    return alignments
259*9e94795aSAndroid Build Coastguard Worker
260*9e94795aSAndroid Build Coastguard Worker
261*9e94795aSAndroid Build Coastguard Worker  _DYNAMIC_SECTION_START_PATTERN = 'DynamicSection ['
262*9e94795aSAndroid Build Coastguard Worker
263*9e94795aSAndroid Build Coastguard Worker  _DYNAMIC_SECTION_NEEDED_PATTERN = re.compile(
264*9e94795aSAndroid Build Coastguard Worker    '^  0x[0-9a-fA-F]+\\s+NEEDED\\s+Shared library: \\[(.*)\\]$')
265*9e94795aSAndroid Build Coastguard Worker
266*9e94795aSAndroid Build Coastguard Worker  _DYNAMIC_SECTION_SONAME_PATTERN = re.compile(
267*9e94795aSAndroid Build Coastguard Worker    '^  0x[0-9a-fA-F]+\\s+SONAME\\s+Library soname: \\[(.*)\\]$')
268*9e94795aSAndroid Build Coastguard Worker
269*9e94795aSAndroid Build Coastguard Worker  _DYNAMIC_SECTION_END_PATTERN = ']'
270*9e94795aSAndroid Build Coastguard Worker
271*9e94795aSAndroid Build Coastguard Worker
272*9e94795aSAndroid Build Coastguard Worker  @classmethod
273*9e94795aSAndroid Build Coastguard Worker  def _parse_dynamic_table(cls, elf_file_path, lines_it):
274*9e94795aSAndroid Build Coastguard Worker    """Parse the dynamic table section."""
275*9e94795aSAndroid Build Coastguard Worker    dt_soname = os.path.basename(elf_file_path)
276*9e94795aSAndroid Build Coastguard Worker    dt_needed = []
277*9e94795aSAndroid Build Coastguard Worker
278*9e94795aSAndroid Build Coastguard Worker    dynamic = cls._find_prefix(cls._DYNAMIC_SECTION_START_PATTERN, lines_it)
279*9e94795aSAndroid Build Coastguard Worker    if not dynamic:
280*9e94795aSAndroid Build Coastguard Worker      return (dt_soname, dt_needed)
281*9e94795aSAndroid Build Coastguard Worker
282*9e94795aSAndroid Build Coastguard Worker    for line in lines_it:
283*9e94795aSAndroid Build Coastguard Worker      if line == cls._DYNAMIC_SECTION_END_PATTERN:
284*9e94795aSAndroid Build Coastguard Worker        break
285*9e94795aSAndroid Build Coastguard Worker
286*9e94795aSAndroid Build Coastguard Worker      match = cls._DYNAMIC_SECTION_NEEDED_PATTERN.match(line)
287*9e94795aSAndroid Build Coastguard Worker      if match:
288*9e94795aSAndroid Build Coastguard Worker        dt_needed.append(match.group(1))
289*9e94795aSAndroid Build Coastguard Worker        continue
290*9e94795aSAndroid Build Coastguard Worker
291*9e94795aSAndroid Build Coastguard Worker      match = cls._DYNAMIC_SECTION_SONAME_PATTERN.match(line)
292*9e94795aSAndroid Build Coastguard Worker      if match:
293*9e94795aSAndroid Build Coastguard Worker        dt_soname = match.group(1)
294*9e94795aSAndroid Build Coastguard Worker        continue
295*9e94795aSAndroid Build Coastguard Worker
296*9e94795aSAndroid Build Coastguard Worker    return (dt_soname, dt_needed)
297*9e94795aSAndroid Build Coastguard Worker
298*9e94795aSAndroid Build Coastguard Worker
299*9e94795aSAndroid Build Coastguard Worker  _DYNAMIC_SYMBOLS_START_PATTERN = 'DynamicSymbols ['
300*9e94795aSAndroid Build Coastguard Worker  _DYNAMIC_SYMBOLS_END_PATTERN = ']'
301*9e94795aSAndroid Build Coastguard Worker
302*9e94795aSAndroid Build Coastguard Worker  _SYMBOL_ENTRY_START_PATTERN = '  Symbol {'
303*9e94795aSAndroid Build Coastguard Worker  _SYMBOL_ENTRY_PATTERN = re.compile('^    ([A-Za-z0-9_]+): (.*)$')
304*9e94795aSAndroid Build Coastguard Worker  _SYMBOL_ENTRY_PAREN_PATTERN = re.compile(
305*9e94795aSAndroid Build Coastguard Worker    '\\s+\\((?:(?:\\d+)|(?:0x[0-9a-fA-F]+))\\)$')
306*9e94795aSAndroid Build Coastguard Worker  _SYMBOL_ENTRY_END_PATTERN = '  }'
307*9e94795aSAndroid Build Coastguard Worker
308*9e94795aSAndroid Build Coastguard Worker
309*9e94795aSAndroid Build Coastguard Worker  @staticmethod
310*9e94795aSAndroid Build Coastguard Worker  def _parse_symbol_name(name_with_version):
311*9e94795aSAndroid Build Coastguard Worker    """Split `name_with_version` into name and version. This function may split
312*9e94795aSAndroid Build Coastguard Worker    at last occurrence of `@@` or `@`."""
313*9e94795aSAndroid Build Coastguard Worker    pos = name_with_version.rfind('@')
314*9e94795aSAndroid Build Coastguard Worker    if pos == -1:
315*9e94795aSAndroid Build Coastguard Worker      name = name_with_version
316*9e94795aSAndroid Build Coastguard Worker      version = ''
317*9e94795aSAndroid Build Coastguard Worker    else:
318*9e94795aSAndroid Build Coastguard Worker      if pos > 0 and name_with_version[pos - 1] == '@':
319*9e94795aSAndroid Build Coastguard Worker        name = name_with_version[0:pos - 1]
320*9e94795aSAndroid Build Coastguard Worker      else:
321*9e94795aSAndroid Build Coastguard Worker        name = name_with_version[0:pos]
322*9e94795aSAndroid Build Coastguard Worker      version = name_with_version[pos + 1:]
323*9e94795aSAndroid Build Coastguard Worker    return (name, version)
324*9e94795aSAndroid Build Coastguard Worker
325*9e94795aSAndroid Build Coastguard Worker
326*9e94795aSAndroid Build Coastguard Worker  @classmethod
327*9e94795aSAndroid Build Coastguard Worker  def _parse_dynamic_symbols(cls, lines_it):
328*9e94795aSAndroid Build Coastguard Worker    """Parse dynamic symbol table and collect imported and exported symbols."""
329*9e94795aSAndroid Build Coastguard Worker    imported = collections.defaultdict(set)
330*9e94795aSAndroid Build Coastguard Worker    exported = collections.defaultdict(set)
331*9e94795aSAndroid Build Coastguard Worker
332*9e94795aSAndroid Build Coastguard Worker    for symbol in cls._parse_dynamic_symbols_internal(lines_it):
333*9e94795aSAndroid Build Coastguard Worker      name, version = cls._parse_symbol_name(symbol['Name'])
334*9e94795aSAndroid Build Coastguard Worker      if name:
335*9e94795aSAndroid Build Coastguard Worker        if symbol['Section'] == 'Undefined':
336*9e94795aSAndroid Build Coastguard Worker          if symbol['Binding'] != 'Weak':
337*9e94795aSAndroid Build Coastguard Worker            imported[name].add(version)
338*9e94795aSAndroid Build Coastguard Worker        else:
339*9e94795aSAndroid Build Coastguard Worker          if symbol['Binding'] != 'Local':
340*9e94795aSAndroid Build Coastguard Worker            exported[name].add(version)
341*9e94795aSAndroid Build Coastguard Worker
342*9e94795aSAndroid Build Coastguard Worker    # Freeze the returned imported/exported dict.
343*9e94795aSAndroid Build Coastguard Worker    return (dict(imported), dict(exported))
344*9e94795aSAndroid Build Coastguard Worker
345*9e94795aSAndroid Build Coastguard Worker
346*9e94795aSAndroid Build Coastguard Worker  @classmethod
347*9e94795aSAndroid Build Coastguard Worker  def _parse_dynamic_symbols_internal(cls, lines_it):
348*9e94795aSAndroid Build Coastguard Worker    """Parse symbols entries and yield each symbols."""
349*9e94795aSAndroid Build Coastguard Worker
350*9e94795aSAndroid Build Coastguard Worker    if not cls._find_prefix(cls._DYNAMIC_SYMBOLS_START_PATTERN, lines_it):
351*9e94795aSAndroid Build Coastguard Worker      return
352*9e94795aSAndroid Build Coastguard Worker
353*9e94795aSAndroid Build Coastguard Worker    for line in lines_it:
354*9e94795aSAndroid Build Coastguard Worker      if line == cls._DYNAMIC_SYMBOLS_END_PATTERN:
355*9e94795aSAndroid Build Coastguard Worker        return
356*9e94795aSAndroid Build Coastguard Worker
357*9e94795aSAndroid Build Coastguard Worker      if line == cls._SYMBOL_ENTRY_START_PATTERN:
358*9e94795aSAndroid Build Coastguard Worker        symbol = {}
359*9e94795aSAndroid Build Coastguard Worker        continue
360*9e94795aSAndroid Build Coastguard Worker
361*9e94795aSAndroid Build Coastguard Worker      if line == cls._SYMBOL_ENTRY_END_PATTERN:
362*9e94795aSAndroid Build Coastguard Worker        yield symbol
363*9e94795aSAndroid Build Coastguard Worker        symbol = None
364*9e94795aSAndroid Build Coastguard Worker        continue
365*9e94795aSAndroid Build Coastguard Worker
366*9e94795aSAndroid Build Coastguard Worker      match = cls._SYMBOL_ENTRY_PATTERN.match(line)
367*9e94795aSAndroid Build Coastguard Worker      if match:
368*9e94795aSAndroid Build Coastguard Worker        key = match.group(1)
369*9e94795aSAndroid Build Coastguard Worker        value = cls._SYMBOL_ENTRY_PAREN_PATTERN.sub('', match.group(2))
370*9e94795aSAndroid Build Coastguard Worker        symbol[key] = value
371*9e94795aSAndroid Build Coastguard Worker        continue
372*9e94795aSAndroid Build Coastguard Worker
373*9e94795aSAndroid Build Coastguard Worker
374*9e94795aSAndroid Build Coastguard Workerclass Checker(object):
375*9e94795aSAndroid Build Coastguard Worker  """ELF file checker that checks DT_SONAME, DT_NEEDED, and symbols."""
376*9e94795aSAndroid Build Coastguard Worker
377*9e94795aSAndroid Build Coastguard Worker  def __init__(self, llvm_readobj):
378*9e94795aSAndroid Build Coastguard Worker    self._file_path = ''
379*9e94795aSAndroid Build Coastguard Worker    self._file_under_test = None
380*9e94795aSAndroid Build Coastguard Worker    self._shared_libs = []
381*9e94795aSAndroid Build Coastguard Worker
382*9e94795aSAndroid Build Coastguard Worker    self._llvm_readobj = llvm_readobj
383*9e94795aSAndroid Build Coastguard Worker
384*9e94795aSAndroid Build Coastguard Worker
385*9e94795aSAndroid Build Coastguard Worker  if sys.stderr.isatty():
386*9e94795aSAndroid Build Coastguard Worker    _ERROR_TAG = '\033[0;1;31merror:\033[m'  # Red error
387*9e94795aSAndroid Build Coastguard Worker    _NOTE_TAG = '\033[0;1;30mnote:\033[m'  # Black note
388*9e94795aSAndroid Build Coastguard Worker  else:
389*9e94795aSAndroid Build Coastguard Worker    _ERROR_TAG = 'error:'  # Red error
390*9e94795aSAndroid Build Coastguard Worker    _NOTE_TAG = 'note:'  # Black note
391*9e94795aSAndroid Build Coastguard Worker
392*9e94795aSAndroid Build Coastguard Worker
393*9e94795aSAndroid Build Coastguard Worker  def _error(self, *args):
394*9e94795aSAndroid Build Coastguard Worker    """Emit an error to stderr."""
395*9e94795aSAndroid Build Coastguard Worker    print(self._file_path + ': ' + self._ERROR_TAG, *args, file=sys.stderr)
396*9e94795aSAndroid Build Coastguard Worker
397*9e94795aSAndroid Build Coastguard Worker
398*9e94795aSAndroid Build Coastguard Worker  def _note(self, *args):
399*9e94795aSAndroid Build Coastguard Worker    """Emit a note to stderr."""
400*9e94795aSAndroid Build Coastguard Worker    print(self._file_path + ': ' + self._NOTE_TAG, *args, file=sys.stderr)
401*9e94795aSAndroid Build Coastguard Worker
402*9e94795aSAndroid Build Coastguard Worker
403*9e94795aSAndroid Build Coastguard Worker  def _load_elf_file(self, path, skip_bad_elf_magic):
404*9e94795aSAndroid Build Coastguard Worker    """Load an ELF file from the `path`."""
405*9e94795aSAndroid Build Coastguard Worker    try:
406*9e94795aSAndroid Build Coastguard Worker      return ELFParser.open(path, self._llvm_readobj)
407*9e94795aSAndroid Build Coastguard Worker    except (IOError, OSError):
408*9e94795aSAndroid Build Coastguard Worker      self._error('Failed to open "{}".'.format(path))
409*9e94795aSAndroid Build Coastguard Worker      sys.exit(2)
410*9e94795aSAndroid Build Coastguard Worker    except ELFInvalidMagicError:
411*9e94795aSAndroid Build Coastguard Worker      if skip_bad_elf_magic:
412*9e94795aSAndroid Build Coastguard Worker        sys.exit(0)
413*9e94795aSAndroid Build Coastguard Worker      else:
414*9e94795aSAndroid Build Coastguard Worker        self._error('File "{}" must have a valid ELF magic word.'.format(path))
415*9e94795aSAndroid Build Coastguard Worker        sys.exit(2)
416*9e94795aSAndroid Build Coastguard Worker    except:
417*9e94795aSAndroid Build Coastguard Worker      self._error('An unknown error occurred while opening "{}".'.format(path))
418*9e94795aSAndroid Build Coastguard Worker      raise
419*9e94795aSAndroid Build Coastguard Worker
420*9e94795aSAndroid Build Coastguard Worker
421*9e94795aSAndroid Build Coastguard Worker  def load_file_under_test(self, path, skip_bad_elf_magic,
422*9e94795aSAndroid Build Coastguard Worker                           skip_unknown_elf_machine):
423*9e94795aSAndroid Build Coastguard Worker    """Load file-under-test (either an executable or a shared lib)."""
424*9e94795aSAndroid Build Coastguard Worker    self._file_path = path
425*9e94795aSAndroid Build Coastguard Worker    self._file_under_test = self._load_elf_file(path, skip_bad_elf_magic)
426*9e94795aSAndroid Build Coastguard Worker
427*9e94795aSAndroid Build Coastguard Worker    if skip_unknown_elf_machine and \
428*9e94795aSAndroid Build Coastguard Worker        self._file_under_test.header.e_machine not in _KNOWN_MACHINES:
429*9e94795aSAndroid Build Coastguard Worker      sys.exit(0)
430*9e94795aSAndroid Build Coastguard Worker
431*9e94795aSAndroid Build Coastguard Worker
432*9e94795aSAndroid Build Coastguard Worker  def load_shared_libs(self, shared_lib_paths):
433*9e94795aSAndroid Build Coastguard Worker    """Load shared libraries."""
434*9e94795aSAndroid Build Coastguard Worker    for path in shared_lib_paths:
435*9e94795aSAndroid Build Coastguard Worker      self._shared_libs.append(self._load_elf_file(path, False))
436*9e94795aSAndroid Build Coastguard Worker
437*9e94795aSAndroid Build Coastguard Worker
438*9e94795aSAndroid Build Coastguard Worker  def check_dt_soname(self, soname):
439*9e94795aSAndroid Build Coastguard Worker    """Check whether DT_SONAME matches installation file name."""
440*9e94795aSAndroid Build Coastguard Worker    if self._file_under_test.dt_soname != soname:
441*9e94795aSAndroid Build Coastguard Worker      self._error('DT_SONAME "{}" must be equal to the file name "{}".'
442*9e94795aSAndroid Build Coastguard Worker                  .format(self._file_under_test.dt_soname, soname))
443*9e94795aSAndroid Build Coastguard Worker      sys.exit(2)
444*9e94795aSAndroid Build Coastguard Worker
445*9e94795aSAndroid Build Coastguard Worker
446*9e94795aSAndroid Build Coastguard Worker  def check_dt_needed(self, system_shared_lib_names):
447*9e94795aSAndroid Build Coastguard Worker    """Check whether all DT_NEEDED entries are specified in the build
448*9e94795aSAndroid Build Coastguard Worker    system."""
449*9e94795aSAndroid Build Coastguard Worker
450*9e94795aSAndroid Build Coastguard Worker    missing_shared_libs = False
451*9e94795aSAndroid Build Coastguard Worker
452*9e94795aSAndroid Build Coastguard Worker    # Collect the DT_SONAMEs from shared libs specified in the build system.
453*9e94795aSAndroid Build Coastguard Worker    specified_sonames = {lib.dt_soname for lib in self._shared_libs}
454*9e94795aSAndroid Build Coastguard Worker
455*9e94795aSAndroid Build Coastguard Worker    # Chech whether all DT_NEEDED entries are specified.
456*9e94795aSAndroid Build Coastguard Worker    for lib in self._file_under_test.dt_needed:
457*9e94795aSAndroid Build Coastguard Worker      if lib not in specified_sonames:
458*9e94795aSAndroid Build Coastguard Worker        self._error(f'DT_NEEDED "{lib}" is not specified in shared_libs.')
459*9e94795aSAndroid Build Coastguard Worker        missing_shared_libs = True
460*9e94795aSAndroid Build Coastguard Worker
461*9e94795aSAndroid Build Coastguard Worker    if missing_shared_libs:
462*9e94795aSAndroid Build Coastguard Worker      dt_needed = sorted(set(self._file_under_test.dt_needed))
463*9e94795aSAndroid Build Coastguard Worker      modules = [re.sub('\\.so$', '', lib) for lib in dt_needed]
464*9e94795aSAndroid Build Coastguard Worker
465*9e94795aSAndroid Build Coastguard Worker      # Remove system shared libraries from the suggestion since they are added
466*9e94795aSAndroid Build Coastguard Worker      # by default.
467*9e94795aSAndroid Build Coastguard Worker      modules = [name for name in modules
468*9e94795aSAndroid Build Coastguard Worker                 if name not in system_shared_lib_names]
469*9e94795aSAndroid Build Coastguard Worker
470*9e94795aSAndroid Build Coastguard Worker      self._note()
471*9e94795aSAndroid Build Coastguard Worker      self._note('Fix suggestions:')
472*9e94795aSAndroid Build Coastguard Worker      self._note(
473*9e94795aSAndroid Build Coastguard Worker        '  Android.bp: shared_libs: [' +
474*9e94795aSAndroid Build Coastguard Worker        ', '.join('"' + module + '"' for module in modules) + '],')
475*9e94795aSAndroid Build Coastguard Worker      self._note(
476*9e94795aSAndroid Build Coastguard Worker        '  Android.mk: LOCAL_SHARED_LIBRARIES := ' + ' '.join(modules))
477*9e94795aSAndroid Build Coastguard Worker
478*9e94795aSAndroid Build Coastguard Worker      self._note()
479*9e94795aSAndroid Build Coastguard Worker      self._note('If the fix above doesn\'t work, bypass this check with:')
480*9e94795aSAndroid Build Coastguard Worker      self._note('  Android.bp: check_elf_files: false,')
481*9e94795aSAndroid Build Coastguard Worker      self._note('  Android.mk: LOCAL_CHECK_ELF_FILES := false')
482*9e94795aSAndroid Build Coastguard Worker
483*9e94795aSAndroid Build Coastguard Worker      sys.exit(2)
484*9e94795aSAndroid Build Coastguard Worker
485*9e94795aSAndroid Build Coastguard Worker  def check_max_page_size(self, max_page_size):
486*9e94795aSAndroid Build Coastguard Worker    for alignment in self._file_under_test.alignments:
487*9e94795aSAndroid Build Coastguard Worker      if alignment % max_page_size != 0:
488*9e94795aSAndroid Build Coastguard Worker        self._error(f'Load segment has alignment {alignment} but '
489*9e94795aSAndroid Build Coastguard Worker                    f'{max_page_size} required.')
490*9e94795aSAndroid Build Coastguard Worker        self._note()
491*9e94795aSAndroid Build Coastguard Worker        self._note('Fix suggestions:')
492*9e94795aSAndroid Build Coastguard Worker        self._note(f'  use linker flag "-Wl,-z,max-page-size={max_page_size}" '
493*9e94795aSAndroid Build Coastguard Worker                   f'when compiling this lib')
494*9e94795aSAndroid Build Coastguard Worker        self._note()
495*9e94795aSAndroid Build Coastguard Worker        self._note('If the fix above doesn\'t work, bypass this check with:')
496*9e94795aSAndroid Build Coastguard Worker        self._note('  Android.bp: ignore_max_page_size: true,')
497*9e94795aSAndroid Build Coastguard Worker        self._note('  Android.mk: LOCAL_IGNORE_MAX_PAGE_SIZE := true')
498*9e94795aSAndroid Build Coastguard Worker        self._note('  Device mk: PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE := false')
499*9e94795aSAndroid Build Coastguard Worker
500*9e94795aSAndroid Build Coastguard Worker        # TODO: instead of exiting immediately, we may want to collect the
501*9e94795aSAndroid Build Coastguard Worker        # errors from all checks and emit them at once
502*9e94795aSAndroid Build Coastguard Worker        sys.exit(2)
503*9e94795aSAndroid Build Coastguard Worker
504*9e94795aSAndroid Build Coastguard Worker  @staticmethod
505*9e94795aSAndroid Build Coastguard Worker  def _find_symbol(lib, name, version):
506*9e94795aSAndroid Build Coastguard Worker    """Check whether the symbol name and version matches a definition in
507*9e94795aSAndroid Build Coastguard Worker    lib."""
508*9e94795aSAndroid Build Coastguard Worker    try:
509*9e94795aSAndroid Build Coastguard Worker      lib_sym_vers = lib.exported[name]
510*9e94795aSAndroid Build Coastguard Worker    except KeyError:
511*9e94795aSAndroid Build Coastguard Worker      return False
512*9e94795aSAndroid Build Coastguard Worker    if version == '':  # Symbol version is not requested
513*9e94795aSAndroid Build Coastguard Worker      return True
514*9e94795aSAndroid Build Coastguard Worker    return version in lib_sym_vers
515*9e94795aSAndroid Build Coastguard Worker
516*9e94795aSAndroid Build Coastguard Worker
517*9e94795aSAndroid Build Coastguard Worker  @classmethod
518*9e94795aSAndroid Build Coastguard Worker  def _find_symbol_from_libs(cls, libs, name, version):
519*9e94795aSAndroid Build Coastguard Worker    """Check whether the symbol name and version is defined in one of the
520*9e94795aSAndroid Build Coastguard Worker    shared libraries in libs."""
521*9e94795aSAndroid Build Coastguard Worker    for lib in libs:
522*9e94795aSAndroid Build Coastguard Worker      if cls._find_symbol(lib, name, version):
523*9e94795aSAndroid Build Coastguard Worker        return lib
524*9e94795aSAndroid Build Coastguard Worker    return None
525*9e94795aSAndroid Build Coastguard Worker
526*9e94795aSAndroid Build Coastguard Worker
527*9e94795aSAndroid Build Coastguard Worker  def check_symbols(self):
528*9e94795aSAndroid Build Coastguard Worker    """Check whether all undefined symbols are resolved to a definition."""
529*9e94795aSAndroid Build Coastguard Worker    all_elf_files = [self._file_under_test] + self._shared_libs
530*9e94795aSAndroid Build Coastguard Worker    missing_symbols = []
531*9e94795aSAndroid Build Coastguard Worker    for sym, imported_vers in self._file_under_test.imported.items():
532*9e94795aSAndroid Build Coastguard Worker      for imported_ver in imported_vers:
533*9e94795aSAndroid Build Coastguard Worker        lib = self._find_symbol_from_libs(all_elf_files, sym, imported_ver)
534*9e94795aSAndroid Build Coastguard Worker        if not lib:
535*9e94795aSAndroid Build Coastguard Worker          missing_symbols.append((sym, imported_ver))
536*9e94795aSAndroid Build Coastguard Worker
537*9e94795aSAndroid Build Coastguard Worker    if missing_symbols:
538*9e94795aSAndroid Build Coastguard Worker      for sym, ver in sorted(missing_symbols):
539*9e94795aSAndroid Build Coastguard Worker        if ver:
540*9e94795aSAndroid Build Coastguard Worker          sym += '@' + ver
541*9e94795aSAndroid Build Coastguard Worker        self._error(f'Unresolved symbol: {sym}')
542*9e94795aSAndroid Build Coastguard Worker
543*9e94795aSAndroid Build Coastguard Worker      self._note()
544*9e94795aSAndroid Build Coastguard Worker      self._note('Some dependencies might be changed, thus the symbol(s) '
545*9e94795aSAndroid Build Coastguard Worker                 'above cannot be resolved.')
546*9e94795aSAndroid Build Coastguard Worker      self._note(f'Please re-build the prebuilt file: "{self._file_path}".')
547*9e94795aSAndroid Build Coastguard Worker
548*9e94795aSAndroid Build Coastguard Worker      self._note()
549*9e94795aSAndroid Build Coastguard Worker      self._note('If this is a new prebuilt file and it is designed to have '
550*9e94795aSAndroid Build Coastguard Worker                 'unresolved symbols, add one of the following properties:')
551*9e94795aSAndroid Build Coastguard Worker      self._note('  Android.bp: allow_undefined_symbols: true,')
552*9e94795aSAndroid Build Coastguard Worker      self._note('  Android.mk: LOCAL_ALLOW_UNDEFINED_SYMBOLS := true')
553*9e94795aSAndroid Build Coastguard Worker
554*9e94795aSAndroid Build Coastguard Worker      sys.exit(2)
555*9e94795aSAndroid Build Coastguard Worker
556*9e94795aSAndroid Build Coastguard Worker
557*9e94795aSAndroid Build Coastguard Workerdef _parse_args():
558*9e94795aSAndroid Build Coastguard Worker  """Parse command line options."""
559*9e94795aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
560*9e94795aSAndroid Build Coastguard Worker
561*9e94795aSAndroid Build Coastguard Worker  # Input file
562*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('file',
563*9e94795aSAndroid Build Coastguard Worker                      help='Path to the input file to be checked')
564*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--soname',
565*9e94795aSAndroid Build Coastguard Worker                      help='Shared object name of the input file')
566*9e94795aSAndroid Build Coastguard Worker
567*9e94795aSAndroid Build Coastguard Worker  # Shared library dependencies
568*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--shared-lib', action='append', default=[],
569*9e94795aSAndroid Build Coastguard Worker                      help='Path to shared library dependencies')
570*9e94795aSAndroid Build Coastguard Worker
571*9e94795aSAndroid Build Coastguard Worker  # System Shared library names
572*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--system-shared-lib', action='append', default=[],
573*9e94795aSAndroid Build Coastguard Worker                      help='System shared libraries to be hidden from fix '
574*9e94795aSAndroid Build Coastguard Worker                      'suggestions')
575*9e94795aSAndroid Build Coastguard Worker
576*9e94795aSAndroid Build Coastguard Worker  # Check options
577*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--skip-bad-elf-magic', action='store_true',
578*9e94795aSAndroid Build Coastguard Worker                      help='Ignore the input file without the ELF magic word')
579*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--skip-unknown-elf-machine', action='store_true',
580*9e94795aSAndroid Build Coastguard Worker                      help='Ignore the input file with unknown machine ID')
581*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--allow-undefined-symbols', action='store_true',
582*9e94795aSAndroid Build Coastguard Worker                      help='Ignore unresolved undefined symbols')
583*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--max-page-size', action='store', type=int,
584*9e94795aSAndroid Build Coastguard Worker                      help='Required page size alignment support')
585*9e94795aSAndroid Build Coastguard Worker
586*9e94795aSAndroid Build Coastguard Worker  # Other options
587*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--llvm-readobj',
588*9e94795aSAndroid Build Coastguard Worker                      help='Path to the llvm-readobj executable')
589*9e94795aSAndroid Build Coastguard Worker
590*9e94795aSAndroid Build Coastguard Worker  return parser.parse_args()
591*9e94795aSAndroid Build Coastguard Worker
592*9e94795aSAndroid Build Coastguard Worker
593*9e94795aSAndroid Build Coastguard Workerdef main():
594*9e94795aSAndroid Build Coastguard Worker  """Main function"""
595*9e94795aSAndroid Build Coastguard Worker  args = _parse_args()
596*9e94795aSAndroid Build Coastguard Worker
597*9e94795aSAndroid Build Coastguard Worker  llvm_readobj = args.llvm_readobj
598*9e94795aSAndroid Build Coastguard Worker  if not llvm_readobj:
599*9e94795aSAndroid Build Coastguard Worker    llvm_readobj = _get_llvm_readobj()
600*9e94795aSAndroid Build Coastguard Worker
601*9e94795aSAndroid Build Coastguard Worker  # Load ELF files
602*9e94795aSAndroid Build Coastguard Worker  checker = Checker(llvm_readobj)
603*9e94795aSAndroid Build Coastguard Worker  checker.load_file_under_test(
604*9e94795aSAndroid Build Coastguard Worker    args.file, args.skip_bad_elf_magic, args.skip_unknown_elf_machine)
605*9e94795aSAndroid Build Coastguard Worker  checker.load_shared_libs(args.shared_lib)
606*9e94795aSAndroid Build Coastguard Worker
607*9e94795aSAndroid Build Coastguard Worker  # Run checks
608*9e94795aSAndroid Build Coastguard Worker  if args.soname:
609*9e94795aSAndroid Build Coastguard Worker    checker.check_dt_soname(args.soname)
610*9e94795aSAndroid Build Coastguard Worker
611*9e94795aSAndroid Build Coastguard Worker  checker.check_dt_needed(args.system_shared_lib)
612*9e94795aSAndroid Build Coastguard Worker
613*9e94795aSAndroid Build Coastguard Worker  if args.max_page_size:
614*9e94795aSAndroid Build Coastguard Worker    checker.check_max_page_size(args.max_page_size)
615*9e94795aSAndroid Build Coastguard Worker
616*9e94795aSAndroid Build Coastguard Worker  if not args.allow_undefined_symbols:
617*9e94795aSAndroid Build Coastguard Worker    checker.check_symbols()
618*9e94795aSAndroid Build Coastguard Worker
619*9e94795aSAndroid Build Coastguard Worker
620*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__':
621*9e94795aSAndroid Build Coastguard Worker  main()
622