diff --git a/linux-ramdump-parser-v2/gdbmi.py b/linux-ramdump-parser-v2/gdbmi.py index 7301bef2d47323d3564910733753911b8374f175..25bc112144d048c05fb1e67ba727399be80c5ca6 100755 --- a/linux-ramdump-parser-v2/gdbmi.py +++ b/linux-ramdump-parser-v2/gdbmi.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +# Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 and @@ -12,6 +12,8 @@ import sys import re import subprocess +import module_table +from print_out import print_out_str GDB_SENTINEL = '(gdb) ' GDB_DATA_LINE = '~' @@ -62,6 +64,7 @@ class GdbMI(object): self.kaslr_offset = kaslr_offset self._cache = {} self._gdbmi = None + self.mod_table = None def open(self): """Open the connection to the ``gdbmi`` backend. Not needed if using @@ -95,6 +98,14 @@ class GdbMI(object): if line == GDB_SENTINEL: break + def setup_module_table(self, module_table): + self.mod_table = module_table + d = {"\\":"\\\\"} + for mod_tbl_ent in self.mod_table.module_table: + load_mod_sym_cmd = 'add-symbol-file %s 0x%x' % (mod_tbl_ent.get_sym_path(), mod_tbl_ent.module_offset) + gdb_cmd = ''.join(d.get(c, c) for c in load_mod_sym_cmd) + self._run(gdb_cmd) + def _run(self, cmd, skip_cache=False, save_in_cache=True): """Runs a gdb command and returns a GdbMIResult of the result. Results are cached (unless skip_cache=True) for quick future lookups. diff --git a/linux-ramdump-parser-v2/module_table.py b/linux-ramdump-parser-v2/module_table.py new file mode 100644 index 0000000000000000000000000000000000000000..0242e6330ab2f82c0d75d34e72353470ed06153f --- /dev/null +++ b/linux-ramdump-parser-v2/module_table.py @@ -0,0 +1,65 @@ +# Copyright (c) 2017, The Linux Foundation. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 and +# only version 2 as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import os +from print_out import print_out_str + +class module_table_entry(): + + def __init__(self): + self.name = '' + self.module_offset = 0 + self.sym_lookup_table = [] + self.sym_path = '' + + def num_symbols(self): + return len(self.sym_lookup_table) + + def set_sym_path(self, sym_path): + if os.path.isfile(sym_path): + self.sym_path = sym_path + return True + else: + print_out_str('sym_path: ' + sym_path + ' not valid or file doesn\'t exist') + return False + + def get_sym_path(self): + return self.sym_path + +class module_table_class(): + + def __init__(self): + self.module_table = [] + self.sym_path = '' + + def add_entry(self, new_entry): + self.module_table.append(new_entry) + + def num_modules(self): + return len(self.module_table) + + def setup_sym_path(self, sym_path): + if sym_path is None: + print_out_str('sym_path: not specified!') + self.sym_path = '' + return False + elif not os.path.exists(sym_path): + print_out_str('sym_path: ' + sym_path + ' not valid or directory doesn\'t exist') + self.sym_path = '' + return False + else: + self.sym_path = sym_path + return True + + def sym_path_exists(self): + return self.sym_path != '' + + diff --git a/linux-ramdump-parser-v2/ramdump.py b/linux-ramdump-parser-v2/ramdump.py index cbef95da42e3b1c2729425a0abd3571ca586e4f9..343f40f33fea9f6975031aeb8b514af6c52df192 100644 --- a/linux-ramdump-parser-v2/ramdump.py +++ b/linux-ramdump-parser-v2/ramdump.py @@ -29,6 +29,7 @@ from mmu import Armv7MMU, Armv7LPAEMMU, Armv8MMU import parser_util import minidump_util from importlib import import_module +import module_table FP = 11 SP = 13 @@ -551,6 +552,8 @@ class RamDump(): print "Oops, missing required library for minidump. Check README" sys.exit(1) self.ram_addr = options.ram_addr + self.module_table = module_table.module_table_class() + self.module_table.setup_sym_path(options.sym_path) if options.ram_addr is not None: # TODO sanity check to make sure the memory regions don't overlap @@ -713,6 +716,9 @@ class RamDump(): print_out_str('!!! Some features may be disabled!') self.unwind = self.Unwinder(self) + if self.module_table.sym_path_exists(): + self.setup_module_symbols() + self.gdbmi.setup_module_table(self.module_table) def __del__(self): self.gdbmi.close() @@ -1029,8 +1035,18 @@ class RamDump(): 'task.config /opt/t32/demo/arm/kernel/linux/linux.t32\n'.encode('ascii', 'ignore')) startup_script.write( 'menu.reprogram /opt/t32/demo/arm/kernel/linux/linux.men\n'.encode('ascii', 'ignore')) + + for mod_tbl_ent in self.module_table.module_table: + mod_sym_path = mod_tbl_ent.get_sym_path() + if mod_sym_path != '': + where = os.path.abspath(mod_sym_path) + where += ' 0x{0:x}'.format(mod_tbl_ent.module_offset) + dloadelf = 'data.load.elf {} /nocode /noclear\n'.format(where) + startup_script.write(dloadelf.encode('ascii', 'ignore')) + if not self.minidump: startup_script.write('task.dtask\n'.encode('ascii', 'ignore')) + startup_script.write( 'v.v %ASCII %STRING linux_banner\n'.encode('ascii', 'ignore')) if os.path.exists(out_path + '/regs_panic.cmm'): @@ -1221,6 +1237,54 @@ class RamDump(): s[2].rstrip())) stream.close() + def retrieve_modules(self): + mod_list = self.address_of('modules') + next_offset = self.field_offset('struct list_head', 'next') + list_offset = self.field_offset('struct module', 'list') + name_offset = self.field_offset('struct module', 'name') + + if self.kernel_version > (4, 4, 0): + module_core_offset = self.field_offset('struct module', 'core_layout.base') + else: + module_core_offset = self.field_offset('struct module', 'module_core') + + next_list_ent = self.read_pointer(mod_list + next_offset) + while next_list_ent != mod_list: + mod_tbl_ent = module_table.module_table_entry() + module = next_list_ent - list_offset + name_ptr = module + name_offset + mod_tbl_ent.name = self.read_cstring(name_ptr) + mod_tbl_ent.module_offset = self.read_pointer(module + module_core_offset) + self.module_table.add_entry(mod_tbl_ent) + next_list_ent = self.read_pointer(next_list_ent + next_offset) + + def parse_symbols_of_one_module(self, mod_tbl_ent, sym_path): + if not mod_tbl_ent.set_sym_path( os.path.join(sym_path, mod_tbl_ent.name + '.ko') ): + return + stream = os.popen(self.nm_path + ' -n ' + mod_tbl_ent.get_sym_path()) + symbols = stream.readlines() + + for line in symbols: + s = line.split(' ') + if len(s) == 3: + mod_tbl_ent.sym_lookup_table.append( ( int(s[0], 16) + mod_tbl_ent.module_offset, s[2].rstrip() ) ) + stream.close() + + def parse_module_symbols(self): + for mod_tbl_ent in self.module_table.module_table: + self.parse_symbols_of_one_module(mod_tbl_ent, self.module_table.sym_path) + + def add_symbols_to_global_lookup_table(self): + for mod_tbl_ent in self.module_table.module_table: + for sym in mod_tbl_ent.sym_lookup_table: + self.lookup_table.append(sym) + self.lookup_table.sort() + + def setup_module_symbols(self): + self.retrieve_modules() + self.parse_module_symbols(); + self.add_symbols_to_global_lookup_table() + def address_of(self, symbol): """Returns the address of a symbol. @@ -1302,11 +1366,6 @@ class RamDump(): if (addr is None): return ('(Invalid address)', 0x0) - # modules are not supported so just print out an address - # instead of a confusing symbol - if (addr < self.modules_end): - return ('(No symbol for address {0:x})'.format(addr), 0x0) - low = 0 high = len(self.lookup_table) # Python now complains about division producing floats diff --git a/linux-ramdump-parser-v2/ramparse.py b/linux-ramdump-parser-v2/ramparse.py index 89a6257a7e216c4b2dd790207d231a5c93986759..10ecfdc438aa9e63d118c7412328be26a5f25248 100644 --- a/linux-ramdump-parser-v2/ramparse.py +++ b/linux-ramdump-parser-v2/ramparse.py @@ -1,6 +1,6 @@ #!/usr/bin/env python2 -# Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +# Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 and @@ -156,6 +156,7 @@ if __name__ == '__main__': help='Parse minidump') parser.add_option('', '--ram-elf', dest='ram_elf_addr', help='pass ap_minidump.elf generated by crashscope') + parser.add_option('', '--sym_path', dest='sym_path', help='symbol path to all loadable modules') for p in parser_util.get_parsers(): parser.add_option(p.shortopt or '',