aboutsummaryrefslogtreecommitdiff
path: root/parse_elfnote.py
blob: 5f84b72a7509df6304291c57dc97cedc74cef792 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import argparse
import logging
import subprocess
import sys
from ctypes import c_char
from ctypes import c_int
from ctypes import Structure

SEC_NAME = '.note.android.ident'
ABI_VENDOR = 'Android'
NDK_RESERVED_SIZE = 64


def logger():
    """Returns the module logger."""
    return logging.getLogger(__name__)


class AbiTag(Structure):
    _fields_ = [('namesz', c_int),
                ('descsz', c_int),
                ('type', c_int),
                ('name', c_char * len(ABI_VENDOR)),
                ('android_api', c_int),
                ('ndk_version', c_char * NDK_RESERVED_SIZE),
                ('ndk_build_number', c_char * NDK_RESERVED_SIZE)]


# Get the offset to a section from the output of readelf
def get_section_pos(sec_name, file_path):
    cmd = ['readelf', '--sections', '-W', file_path]
    output = subprocess.check_output(cmd)
    lines = output.split('\n')
    for line in lines:
        logger().debug('Checking line for "%s": %s', sec_name, line)
        # Looking for a line like the following (all whitespace of unknown
        # width).
        #
        #   [ 8] .note.android.ident NOTE 00000000 0000ec 000098 00 A 0 0 4
        #
        # The only column that might have internal whitespace is the first one.
        # Since we don't care about it, remove the head of the string until the
        # closing bracket, then split.
        if sec_name not in line:
            continue
        if ']' not in line:
            continue
        line = line[line.index(']') + 1:].strip()

        sections = line.split()
        if len(sections) < 5 or sec_name not in sections[0]:
            logger().debug('Did not find "%s" in %s', sec_name, sections[0])
            sys.exit('Failed to get offset of {}'.format(sec_name))
        addr = int(sections[2], 16)
        off = int(sections[3], 16)
        return addr + off
    sys.exit('Failed to find section: {}'.format(sec_name))


def print_info(tag):
    print '----------ABI INFO----------'
    print 'ABI_NOTETYPE: {}'.format(tag.type)
    print 'ABI_VENDOR: {}'.format(tag.name)
    print 'ABI_ANDROID_API: {}'.format(tag.android_api)
    print 'ABI_NDK_VERSION: {}'.format(tag.ndk_version)
    print 'ABI_NDK_BUILD_NUMBER: {}'.format(tag.ndk_build_number)


def parse_args():
    """Parses command line arguments."""
    parser = argparse.ArgumentParser()
    parser.add_argument('file_path',
                        help="path of the ELF file with embedded ABI tags")
    parser.add_argument(
        '-v', '--verbose', dest='verbosity', action='count', default=0,
        help='Increase logging verbosity.')
    return parser.parse_args()


def main():
    args = parse_args()
    if args.verbosity == 1:
        logging.basicConfig(level=logging.INFO)
    elif args.verbosity >= 2:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig()

    file_path = args.file_path

    with open(file_path, "rb") as obj_file:
        pos = get_section_pos(SEC_NAME, file_path)
        obj_file.seek(pos)
        tag = AbiTag()
        obj_file.readinto(tag)
        print_info(tag)


if __name__ == '__main__':
    main()