]>
Commit | Line | Data |
---|---|---|
a5419fb5 MC |
1 | #!/usr/bin/python3 |
2 | ## | |
3 | ## This file is part of the sigrok-util project. | |
4 | ## | |
5 | ## Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se> | |
6 | ## | |
7 | ## This program is free software; you can redistribute it and/or modify | |
8 | ## it under the terms of the GNU General Public License as published by | |
9 | ## the Free Software Foundation; either version 3 of the License, or | |
10 | ## (at your option) any later version. | |
11 | ## | |
12 | ## This program is distributed in the hope that it will be useful, | |
13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | ## GNU General Public License for more details. | |
16 | ## | |
17 | ## You should have received a copy of the GNU General Public License | |
18 | ## along with this program; if not, see <http://www.gnu.org/licenses/>. | |
19 | ## | |
20 | ||
21 | import struct | |
22 | ||
23 | class elf: | |
24 | ||
25 | def read_struct(this, struct_fmt, struct_fields): | |
26 | fmt = this.elf_endianprefix+str.translate(struct_fmt, this.elf_format); | |
27 | fields = struct.unpack(fmt, this.file.read(struct.calcsize(fmt))) | |
28 | return dict(zip(struct_fields, fields)) | |
29 | ||
30 | def read_ehdr(this): | |
31 | return this.read_struct('16sHHWNNNWHHHHHH', | |
32 | ('e_ident', 'e_type', 'e_machine', 'e_version', | |
33 | 'e_entry', 'e_phoff', 'e_shoff', 'e_flags', | |
34 | 'e_ehsize', 'e_phentsize', 'e_phnum', | |
35 | 'e_shentsize', 'e_shnum', 'e_shstrndx')) | |
36 | ||
37 | def read_shdr(this): | |
38 | return this.read_struct('WWNNNNWWNN', | |
39 | ('sh_name', 'sh_type', 'sh_flags', 'sh_addr', | |
40 | 'sh_offset', 'sh_size', 'sh_link', 'sh_info', | |
41 | 'sh_addralign', 'sh_entsize')) | |
42 | ||
43 | def read_section(this, shdr): | |
44 | this.file.seek(shdr['sh_offset']) | |
45 | return this.file.read(shdr['sh_size']) | |
46 | ||
47 | def get_name(this, name, strtab=None): | |
48 | strtab = strtab or this.strtab | |
49 | nul = strtab.find(b'\0', name) | |
50 | if nul < 0: | |
51 | return bytes.decode(strtab[name:]) | |
52 | else: | |
53 | return bytes.decode(strtab[name:nul]) | |
54 | ||
55 | def find_section(this, name): | |
56 | for section in this.shdrs: | |
57 | if this.get_name(section['sh_name']) == name: | |
58 | return section | |
59 | raise KeyError(name) | |
60 | ||
61 | def parse_symbol(this): | |
62 | if this.elf_wordsize == 64: | |
63 | return this.read_struct('WBBHNX', | |
64 | ('st_name', 'st_info', 'st_other', | |
65 | 'st_shndx', 'st_value', 'st_size')) | |
66 | else: | |
67 | return this.read_struct('WNWBBH', | |
68 | ('st_name', 'st_value', 'st_size', | |
69 | 'st_info', 'st_other', 'st_shndx')) | |
70 | ||
1b0c6828 MC |
71 | def parse_rela(this): |
72 | return this.read_struct('NNn', ('r_offset', 'r_info', 'r_addend')) | |
73 | ||
74 | def parse_rel(this): | |
75 | return this.read_struct('NN', ('r_offset', 'r_info')) | |
76 | ||
77 | def fixup_reloc(this, reloc): | |
78 | if not 'r_addend' in reloc: | |
79 | reloc['r_addend'] = 0 | |
80 | if this.elf_wordsize == 64: | |
81 | reloc['r_sym'] = reloc['r_info'] >> 32 | |
82 | reloc['r_type'] = reloc['r_info'] & 0xffffffff | |
83 | else: | |
84 | reloc['r_sym'] = reloc['r_info'] >> 8 | |
85 | reloc['r_type'] = reloc['r_info'] & 0xff | |
86 | return reloc | |
87 | ||
a5419fb5 MC |
88 | def parse_symbols(this, symsecname, strsecname): |
89 | try: | |
90 | symsechdr = this.find_section(symsecname) | |
91 | strsechdr = this.find_section(strsecname) | |
92 | except KeyError: | |
93 | return {} | |
94 | strsec = this.read_section(strsechdr) | |
95 | this.file.seek(symsechdr['sh_offset']) | |
1b0c6828 | 96 | syms = [dict(this.parse_symbol(),number=i) for i in |
a5419fb5 MC |
97 | range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])] |
98 | return {this.get_name(sym['st_name'], strsec): sym for sym in syms} | |
99 | ||
1b0c6828 MC |
100 | def parse_relocs(this, section): |
101 | this.file.seek(section['sh_offset']) | |
102 | if section['sh_type'] == 4: | |
103 | relocs = [this.fixup_reloc(this.parse_rela()) for i in | |
104 | range(0, section['sh_size'] // section['sh_entsize'])] | |
105 | else: | |
106 | relocs = [this.fixup_reloc(this.parse_rel()) for i in | |
107 | range(0, section['sh_size'] // section['sh_entsize'])] | |
108 | return relocs | |
109 | ||
a5419fb5 MC |
110 | def address_to_offset(this, addr): |
111 | for section in this.shdrs: | |
112 | if (section['sh_addr'] <= addr and | |
113 | section['sh_addr']+section['sh_size'] > addr): | |
114 | return section['sh_offset']+(addr-section['sh_addr']) | |
115 | raise IndexError('address out of range') | |
116 | ||
117 | def load_symbol(this, sym): | |
118 | this.file.seek(this.address_to_offset(sym['st_value'])) | |
119 | return this.file.read(sym['st_size']) | |
120 | ||
121 | def __init__(this, filename): | |
122 | this.file = open(filename, 'rb') | |
123 | magic = this.file.read(16) | |
124 | ||
125 | if magic[:4] != b'\x7fELF': | |
126 | raise Exception("ELF signature not found") | |
127 | ||
128 | if magic[4] == 1: | |
129 | this.elf_wordsize = 32 | |
130 | nativeint = 'Ii' | |
131 | elif magic[4] == 2: | |
132 | this.elf_wordsize = 64 | |
133 | nativeint = 'Qq' | |
134 | else: | |
135 | raise Exception("Invalid ELF file class") | |
136 | ||
137 | if magic[5] == 1: | |
138 | this.elf_endianprefix = '<' | |
139 | elif magic[5] == 2: | |
140 | this.elf_endianprefix = '>' | |
141 | else: | |
142 | raise Exception("Invalid ELF data encoding") | |
143 | ||
144 | this.elf_format = str.maketrans('HWwXxNn', 'HIiQq'+nativeint) | |
145 | ||
146 | this.file.seek(0) | |
147 | this.ehdr = this.read_ehdr() | |
148 | this.file.seek(this.ehdr['e_shoff']) | |
149 | this.shdrs = [this.read_shdr() for i in range(this.ehdr['e_shnum'])] | |
150 | ||
151 | this.strtab = this.read_section(this.shdrs[this.ehdr['e_shstrndx']]) | |
152 | ||
153 | this.symtab = this.parse_symbols('.symtab', '.strtab') | |
154 | this.dynsym = this.parse_symbols('.dynsym', '.dynstr') | |
155 | ||
1b0c6828 MC |
156 | this.relocs = {} |
157 | for section in this.shdrs: | |
158 | if section['sh_type'] == 4 or section['sh_type'] == 9: | |
159 | rels = {} | |
160 | symsec = this.shdrs[section['sh_link']] | |
161 | if this.get_name(symsec['sh_name']) == '.symtab': | |
162 | rels['symbols'] = this.symtab | |
163 | elif this.get_name(symsec['sh_name']) == '.dynsym': | |
164 | rels['symbols'] = this.dynsym | |
165 | rels['relocs'] = this.parse_relocs(section) | |
166 | this.relocs[this.get_name(section['sh_name'])] = rels | |
167 | ||
a5419fb5 MC |
168 | def __del__(this): |
169 | try: | |
170 | this.file.close() | |
171 | except AttributeError: | |
172 | pass |