forked from t-crest/patmos-misc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
patmos-dwarfdump
executable file
·208 lines (177 loc) · 7.19 KB
/
patmos-dwarfdump
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/usr/bin/python
#----------------------------------------------------------------------
# This script reads DWARF debug info from an ELF file and
# prints out function names and line number infos for every
# address that is given as an input.
#
# This script is designed to be used with objdump or similar tools.
# To annotate the objdump output with line infos, use
#
# objdump -d <binary> | patmos-dwarfdump <binary>
#
# Author: Stefan Hepp <stefan@stefant.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#----------------------------------------------------------------------
from __future__ import print_function
import sys
import os
import argparse
import re
from libs.IntervalTree import IntervalTree
# We do not care about closed stdout pipe, just abort
import signal
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
signal.signal(signal.SIGINT, signal.SIG_DFL)
# Import pyelftools stuff
sys.path.append(os.path.join(os.path.dirname(__file__), 'pyelftools'))
from elftools.common.py3compat import itervalues, maxint, bytes2str
from elftools.elf.elffile import ELFFile
class IntervalInfo:
def __init__(self, start, end, info):
self.start = start
self.end = end
self.info = info
def get_begin(self):
return self.start
def get_end(self):
return self.end
def get_info(self):
return self.info
def decode_funcname(funcinfo, address):
if funcinfo:
intervals = funcinfo.search(address)
if len(intervals) > 0:
return intervals[0].get_info()
return None
def decode_file_line(lineinfo, address):
if lineinfo:
intervals = lineinfo.search(address)
if len(intervals) > 0:
return intervals[0].get_info()
return None, None
def load_lineinfo(dwarfinfo):
# Load all line infos and all function names into lists
# Lines contains tuples of startaddr, endaddress, filename, line
lines = []
# Functions contains tuples of lowpc, highpc, function
functions = []
for CU in dwarfinfo.iter_CUs():
# First, look at line programs to find the file/line map
lineprog = dwarfinfo.line_program_for_CU(CU)
prevstate = None
for entry in lineprog.get_entries():
# We're interested in those entries where a new state is assigned
state = entry.state
if state is None: continue
if prevstate and prevstate.address <= state.address and not prevstate.end_sequence:
file_entry = lineprog['file_entry'][prevstate.file - 1]
file_name = bytes2str(file_entry.name)
if file_entry.dir_index == 0:
# current directory
# TODO get directory of source file and prepend it
filename = './%s' % (file_name)
elif file_name[0] == '/':
# This ssems to be a hack.. can we detect somehow else if
# this is an absolute path, and is include_rirectory the
# correct path if it is not absolute?
filename = file_name
else:
filename = '%s/%s' % (
bytes2str(lineprog['include_directory'][file_entry.dir_index - 1]),
file_name)
info = filename, prevstate.line
lines.append( IntervalInfo(prevstate.address, state.address - 1, info) )
prevstate = state
# Go over all DIEs in the DWARF information. Note that
# this simplifies things by disregarding subprograms that may have
# split address ranges.
for DIE in CU.iter_DIEs():
try:
if DIE.tag == 'DW_TAG_subprogram':
lowpc = DIE.attributes['DW_AT_low_pc'].value
highpc = DIE.attributes['DW_AT_high_pc'].value
function = DIE.attributes['DW_AT_name'].value
# Skip entries that have not been relocated
if not lowpc and not highpc: continue
info = bytes2str(function)
functions.append( IntervalInfo( lowpc, highpc, info) )
except KeyError:
continue
linetree = None
functree = None
if lines:
linetree = IntervalTree(lines)
if functions:
functree = IntervalTree(functions)
return linetree, functree
def print_lineinfos(lineinfo, functioninfo, input, printAllLines, printInput = True):
# Try to match first column as hex PC
ppc = re.compile("([a-fA-F0-9]+)[ \t\n]")
# Try to match objdump format
opc = re.compile(" +([a-f0-9]+):")
# Try to match stacktraces
spc = re.compile(" +at 0x([a-f0-9]+) ")
lastLineNr = None
lastFilename = None
lastFunction = None
for line in input:
# Check if we have a PC at the beginning of the line
PC = None
isStacktrace = False
for r in [ppc, opc, spc]:
m = r.match(line)
if m:
isStacktrace = (r == spc)
PC = int(m.group(1), 16)
break
if PC is None:
if printInput: print(line, end="")
continue
function = decode_funcname(functioninfo, PC)
filename, linenr = decode_file_line(lineinfo, PC)
if isStacktrace:
print(line, end="")
print(' %s:%s' % (filename, linenr))
elif printAllLines:
print('%s:%s, %s():' % (filename, linenr, function))
else:
if function and lastFunction != function:
print("%s():" % function)
if (filename or linenr) and (lastLineNr != linenr or lastFilename != filename):
print("%s:%s" % (filename, linenr))
lastFunction = function
lastFilename = filename
lastLineNr = linenr
if printInput and not isStacktrace: print(line, end="")
def load_dwarfinfo(filename):
elffile = ELFFile(filename)
if not elffile.has_dwarf_info():
print(filename.name + ': ELF file has no DWARF info!')
sys.exit(1)
# TODO support Patmos relocation types, enable resolving relocations here
dwarfinfo = elffile.get_dwarf_info(False)
return dwarfinfo
parser = argparse.ArgumentParser(description='Display ELF debug infos.')
parser.add_argument('elffile', type=file, help='ELF file containing debug infos')
parser.add_argument('-a', '--all', action='store_true', help='Print line numbers for all found PCs, not only when it changes')
parser.add_argument('-l', '--lines', help='Display line numbers for the given input file (defaults to stdin)')
args = parser.parse_args()
# Load the ELF file
dwarfinfo = load_dwarfinfo(args.elffile)
# Load all dwarf line number and function infos into a lookup table
lineinfo, funcinfo = load_lineinfo(dwarfinfo)
if args.lines and args.lines != "-":
input = None
try:
input = open(args.lines, 'r')
except:
print("Could not open input file " + args.lines + ": " + e)
sys.exit(1)
print_lineinfos(lineinfo, funcinfo, input, args.all)
input.close()
else:
print_lineinfos(lineinfo, funcinfo, sys.stdin, args.all)