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
29from __future__ import print_function
30import sys
31import os
32import popen2
33import re
34
35gdb_cmd = 'kgdb %(p)s/kernel.debug %(core)s | tee /tmp/gdb.log'
36#GDB regex
37filenamere = re.compile(r'filename\s+=\s+0x[0-9a-f]+\s("(?P<fn>[^"]+)"|(?P<error><[^>]*>))', re.M)
38addressre = re.compile(r'address\s+=\s+(?P<ad>0x[0-9a-f]+)', re.M)
39nextre = re.compile(r'tqe_next\s+=\s+(?P<ad>0x[0-9a-f]+)', re.M)
40printre = re.compile(r'\$\d+\s+=\s+')
41
42#Paths to search for ko's/debugs
43kld_debug_paths = []
44
45if len(sys.argv[1:]) < 2:
46	print('Usage: prog <kerncomp> <core> [<paths>]')
47	sys.exit(1)
48
49#Get the base modules path
50pfs = sys.argv[1].split('/')
51try:
52	i = 0
53	while 1:
54		i = i + pfs[i:].index('sys') + 1
55except:
56	pass
57
58if i == -1:
59	sys.stderr.write("No sys dir in kernel source path: %s\n" % sys.argv[1])
60	sys.exit(0)
61
62kld_debug_paths.append('/'.join(pfs[:i] + ['modules']))
63kld_debug_paths.append(sys.argv[1])
64#kld_debug_paths.append(sys.argv[3:])
65gdb_cmd = gdb_cmd % {'p': sys.argv[1], 'core': sys.argv[2] }
66
67#Start gdb
68gdb = popen2.popen4(gdb_cmd)
69
70def searchfor(inp, re, j = 0, l = None):
71	"""searchfor(inp, re, j, l):  Searches for regex re in inp.  It will
72automatically add more lines.  If j is set, the lines will be joined together.
73l can provide a starting line to help search against.  Return value is a
74tuple of the last line, and the match if any."""
75	ret = None
76	if not l:
77		l = inp.readline()
78	ret = re.search(l)
79	while l and not ret:
80		if j:
81			l += inp.readline()
82		else:
83			l = inp.readline()
84		ret = re.search(l)
85
86	return (l, ret)
87
88def get_addresses(inp, out):
89	"""get_addresses(inp, out):  It will search for addresses from gdb.
90inp and out, are the gdb input and output respectively.  Return value is
91a list of tuples.  The tuples contain the filename and the address the
92filename was loaded."""
93	addr = []
94	nxad = 1
95	while nxad:
96		if nxad == 1:
97			out.write("print linker_files.tqh_first[0]\n")
98		else:
99			out.write("print *(struct linker_file *)%d\n" % nxad)
100		out.flush()
101		l = searchfor(inp, printre)[0]
102		l, fn = searchfor(inp, filenamere, 1, l)
103		if not fn.group('fn'):
104			sys.stderr.write("got error: %s\n" % fn.group('error'))
105			nxad = 0
106		else:
107			l, ad = searchfor(inp, addressre, 1, l)
108			l, nx = searchfor(inp, nextre, 1, l)
109			addr.append((fn.group('fn'), long(ad.group('ad'), 16)))
110			nxad = long(nx.group('ad'), 16)
111
112	return addr
113
114#Get the addresses
115addr = get_addresses(gdb[0], gdb[1])
116
117#Pass through the resulting addresses, skipping the kernel.
118for i in addr[1:]:
119	for j in kld_debug_paths:
120		#Try .debug first.
121		p = popen2.popen4('find %s -type f -name "%s.debug"' % (j, i[0]))[0].read().strip()
122		if p:
123			break
124		#Try just .ko if .debug wasn't found.
125		p = popen2.popen4('find %s -type f -name "%s"' % (j, i[0]))[0].read().strip()
126		if p:
127			break
128
129	if not p:
130		#Tell our user that we couldn't find it.
131		a = i[1]
132		sys.stderr.write("Can't find module: %s (addr: %d + header)\n" % (i[0], a))
133		print('#add-symbol-file <file>', a, '#add header')
134		continue
135
136	#j = popen2.popen4('objdump --section-headers /boot/kernel/%s | grep "\.text"' % i[0])[0].read().strip().split()
137	#Output the necessary information
138	j = popen2.popen4('objdump --section-headers "%s" | grep "\.text"' % p)[0].read().strip().split()
139	try:
140		a = int(j[5], 16)
141		print('add-symbol-file', p, i[1] + a)
142	except IndexError:
143		sys.stderr.write('Bad file: %s, address: %d\n' % (i[0], i[1]))
144