1#
2# Copyright (C) 2017 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17from vts.utils.python.library import elf_parser
18
19
20class ArError(Exception):
21    """The exception raised by the functions in this module."""
22    pass
23
24
25def _IterateArchive(archive_path):
26    """A generator yielding the offsets of the ELF objects in an archive.
27
28    An archive file is a magic string followed by an array of file members.
29    Each file member consists of a header and the file content. The header
30    begins on a 2-byte boundary and is defined by the following structure. All
31    fields are ASCII strings padded with space characters.
32
33    BYTES FIELD
34     0~15 file name
35    16~27 modification time
36    28~33 user id
37    34~39 group id
38    40~47 permission
39    48~57 size
40    58~59 magic bytes
41
42    Args:
43        archive_path: The path to the archive file.
44
45    Yields:
46        An integer, the offset of the ELF object.
47
48    Raises:
49        ArError if the file is not a valid archive.
50    """
51    ar = None
52    try:
53        ar = open(archive_path, "rb")
54        if ar.read(8) != "!<arch>\n":
55            raise ArError("Wrong magic string.")
56        while True:
57            header = ar.read(60)
58            if len(header) != 60:
59                break
60            obj_name = header[0:16].rstrip(" ")
61            obj_size = int(header[48:58].rstrip(" "))
62            obj_offset = ar.tell()
63            # skip string tables
64            if obj_name not in ("/", "//", "/SYM64/"):
65                yield obj_offset
66            obj_offset += obj_size
67            ar.seek(obj_offset + obj_offset % 2)
68    except IOError as e:
69        raise ArError(e)
70    finally:
71        if ar:
72            ar.close()
73
74
75def ListGlobalSymbols(archive_path):
76    """Lists global symbols in an ELF archive.
77
78    Args:
79        archive_path: The path to the archive file.
80
81    Returns:
82        A List of strings, the global symbols in the archive.
83
84    Raises:
85        ArError if fails to load the archive.
86        elf_parser.ElfError if fails to load any library in the archive.
87    """
88    symbols = []
89    for offset in _IterateArchive(archive_path):
90        with elf_parser.ElfParser(archive_path, offset) as parser:
91            symbols.extend(parser.ListGlobalSymbols())
92    return symbols
93