1#!/usr/local/bin/python
2#
3# Copyright 2004 John-Mark Gurney
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD$
28
29import sys
30import os
31import popen2
32import re
33
34gdb_cmd = 'kgdb %(p)s/kernel.debug %(core)s | tee /tmp/gdb.log'
35#GDB regex
36filenamere = re.compile(r'filename\s+=\s+0x[0-9a-f]+\s("(?P<fn>[^"]+)"|(?P<error><[^>]*>))', re.M)
37addressre = re.compile(r'address\s+=\s+(?P<ad>0x[0-9a-f]+)', re.M)
38nextre = re.compile(r'tqe_next\s+=\s+(?P<ad>0x[0-9a-f]+)', re.M)
39printre = re.compile(r'\$\d+\s+=\s+')
40
41#Paths to search for ko's/debugs
42kld_debug_paths = []
43
44if len(sys.argv[1:]) < 2:
45	print 'Usage: prog <kerncomp> <core> [<paths>]'
46	sys.exit(1)
47
48#Get the base modules path
49pfs = sys.argv[1].split('/')
50try:
51	i = 0
52	while 1:
53		i = i + pfs[i:].index('sys') + 1
54except:
55	pass
56
57if i == -1:
58	sys.stderr.write("No sys dir in kernel source path: %s\n" % sys.argv[1])
59	sys.exit(0)
60
61kld_debug_paths.append('/'.join(pfs[:i] + ['modules']))
62kld_debug_paths.append(sys.argv[1])
63#kld_debug_paths.append(sys.argv[3:])
64gdb_cmd = gdb_cmd % {'p': sys.argv[1], 'core': sys.argv[2] }
65
66#Start gdb
67gdb = popen2.popen4(gdb_cmd)
68
69def searchfor(inp, re, j = 0, l = None):
70	"""searchfor(inp, re, j, l):  Searches for regex re in inp.  It will
71automatically add more lines.  If j is set, the lines will be joined together.
72l can provide a starting line to help search against.  Return value is a
73tuple of the last line, and the match if any."""
74	ret = None
75	if not l:
76		l = inp.readline()
77	ret = re.search(l)
78	while l and not ret:
79		if j:
80			l += inp.readline()
81		else:
82			l = inp.readline()
83		ret = re.search(l)
84
85	return (l, ret)
86
87def get_addresses(inp, out):
88	"""get_addresses(inp, out):  It will search for addresses from gdb.
89inp and out, are the gdb input and output respectively.  Return value is
90a list of tuples.  The tuples contain the filename and the address the
91filename was loaded."""
92	addr = []
93	nxad = 1
94	while nxad:
95		if nxad == 1:
96			out.write("print linker_files.tqh_first[0]\n")
97		else:
98			out.write("print *(struct linker_file *)%d\n" % nxad)
99		out.flush()
100		l = searchfor(inp, printre)[0]
101		l, fn = searchfor(inp, filenamere, 1, l)
102		if not fn.group('fn'):
103			sys.stderr.write("got error: %s\n" % fn.group('error'))
104			nxad = 0
105		else:
106			l, ad = searchfor(inp, addressre, 1, l)
107			l, nx = searchfor(inp, nextre, 1, l)
108			addr.append((fn.group('fn'), long(ad.group('ad'), 16)))
109			nxad = long(nx.group('ad'), 16)
110
111	return addr
112
113#Get the addresses
114addr = get_addresses(gdb[0], gdb[1])
115
116#Pass through the resulting addresses, skipping the kernel.
117for i in addr[1:]:
118	for j in kld_debug_paths:
119		#Try .debug first.
120		p = popen2.popen4('find %s -type f -name "%s.debug"' % (j, i[0]))[0].read().strip()
121		if p:
122			break
123		#Try just .ko if .debug wasn't found.
124		p = popen2.popen4('find %s -type f -name "%s"' % (j, i[0]))[0].read().strip()
125		if p:
126			break
127
128	if not p:
129		#Tell our user that we couldn't find it.
130		a = i[1]
131		sys.stderr.write("Can't find module: %s (addr: %d + header)\n" % (i[0], a))
132		print '#add-symbol-file <file>', a, '#add header'
133		continue
134
135	#j = popen2.popen4('objdump --section-headers /boot/kernel/%s | grep "\.text"' % i[0])[0].read().strip().split()
136	#Output the necessary information
137	j = popen2.popen4('objdump --section-headers "%s" | grep "\.text"' % p)[0].read().strip().split()
138	try:
139		a = int(j[5], 16)
140		print 'add-symbol-file', p, i[1] + a
141	except IndexError:
142		sys.stderr.write('Bad file: %s, address: %d\n' % (i[0], i[1]))
143