1// ksymoops.cc v1.7 -- A simple filter to resolve symbols in Linux Oops-logs 2// Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com> 3// compile like so: g++ -o ksymoops ksymoops.cc -liostream 4 5////////////////////////////////////////////////////////////////////////////// 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 2, or (at your option) 10// 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; see the file COPYING. If not, write to the 19// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 21// This is a simple filter to resolve EIP and call-trace symbols from 22// a Linux kernel "Oops" log. Supply the symbol-map file name as a 23// command-line argument, and redirect the oops-log into stdin. Out 24// will come the EIP and call-trace in symbolic form. 25 26////////////////////////////////////////////////////////////////////////////// 27 28// BUGS: 29// * Doesn't deal with line-prefixes prepended by syslog--strip 30// these off first, before submitting to ksymoops. 31// * Only resolves operands of jump and call instructions. 32 33#include <fstream.h> 34#include <strstream.h> 35#include <iomanip.h> 36#include <stdio.h> 37#include <string.h> 38#include <stdlib.h> 39#include <unistd.h> 40#include <ctype.h> 41 42inline int strequ(char const* x, char const* y) { return (::strcmp(x, y) == 0); } 43inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); } 44 45const int code_size = 20; 46 47////////////////////////////////////////////////////////////////////////////// 48 49class KSym 50{ 51 friend class NameList; 52 53 private: 54 unsigned long address_; 55 char* name_; 56 long offset_; 57 long extent_; 58 59 private: 60 istream& scan(istream&); 61 ostream& print(ostream&) const; 62 void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; } 63 64 public: 65 friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); } 66 friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); } 67}; 68 69istream& 70KSym::scan(istream& is) 71{ 72 is >> ::hex >> address_; 73 char type; 74 is >> type; 75 char name[128]; 76 is >> name; 77 name_ = new char [strlen(name)+1]; 78 strcpy(name_, name); 79 offset_ = 0; 80 return is; 81} 82 83ostream& 84KSym::print(ostream& os) const 85{ 86 os << ::hex << address_ + offset_ << ' ' << '<' << name_; 87 if (offset_) 88 os << '+' << ::hex << offset_ << '/' << ::hex << extent_; 89 return os << '>'; 90} 91 92////////////////////////////////////////////////////////////////////////////// 93 94class NameList 95{ 96 private: 97 // Caution: Fixed Allocation! 98 // This should suffice for awhile since 1.1.86 has only 2482 symbols. 99 KSym ksyms_0_[8000]; 100 int cardinality_; 101 102 public: 103 NameList() : cardinality_(0) { } 104 105 private: 106 istream& scan(istream&); 107 108 public: 109 int valid() { return (cardinality_ > 0); } 110 111 KSym* find(unsigned long address); 112 void decode(unsigned char* code, long eip_addr); 113 114 public: 115 friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); } 116}; 117 118KSym* 119NameList::find(unsigned long address) 120{ 121 if (!valid()) 122 return 0; 123 KSym* start = ksyms_0_; 124 KSym* end = &ksyms_0_[cardinality_ - 1]; 125 if (address < start->address_ || address >= end->address_) 126 return 0; 127 128 KSym* mid; 129 while (start <= end) { 130 mid = &start[(end - start) / 2]; 131 if (mid->address_ < address) 132 start = mid + 1; 133 else if (mid->address_ > address) 134 end = mid - 1; 135 else 136 return mid; 137 } 138 while (mid->address_ > address) 139 --mid; 140 mid->offset_ = address - mid->address_; 141 if (mid->offset_ > mid->extent_) 142 clog << "Oops! " << *mid << endl; 143 return mid; 144} 145 146void 147NameList::decode(unsigned char* code, long eip_addr) 148{ 149 /* This is a hack to avoid using gcc. We create an object file by 150 concatenating objfile_head, the twenty bytes of code, and 151 objfile_tail. */ 152 unsigned char objfile_head[] = { 153 0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 157 }; 158 unsigned char objfile_tail[] = { 159 0x00, 0x90, 0x90, 0x90, 160 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 161 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 162 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 164 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 165 'g', 'c', 'c', '2', '_', 'c', 'o', 'm', 166 'p', 'i', 'l', 'e', 'd', '.', '\0', '_', 167 'E', 'I', 'P', '\0', '\0', '\0', '\0', '\0', 168 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 169 '\0', '\0', '\0', '\0', '\0', '\0' 170 }; 171 char const* objdump_command = "objdump -d oops_decode.o"; 172 char const* objfile_name = &objdump_command[11]; 173 ofstream objfile_stream(objfile_name); 174 175 objfile_stream.write(objfile_head, sizeof(objfile_head)); 176 objfile_stream.write(code, code_size); 177 objfile_stream.write(objfile_tail, sizeof(objfile_tail)); 178 objfile_stream.close(); 179 180 FILE* objdump_FILE = popen(objdump_command, "r"); 181 if (objdump_FILE == 0) { 182 clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl; 183 return; 184 } 185 186 char buf[1024]; 187 int lines = 0; 188 int eip_seen = 0; 189 long offset; 190 while (fgets(buf, sizeof(buf), objdump_FILE)) { 191 if (eip_seen && buf[4] == ':') { 192 // assume objdump from binutils 2.8..., reformat to old style 193 offset = strtol(buf, 0, 16); 194 char newbuf[sizeof(buf)]; 195 memset(newbuf, '\0', sizeof(newbuf)); 196 ostrstream ost(newbuf, sizeof(newbuf)); 197 ost.width(8); 198 ost << offset; 199 ost << " <_EIP+" << offset << ">: " << &buf[6] << ends; 200 strcpy(buf, newbuf); 201 } 202 if (!strnequ(&buf[9], "<_EIP", 5)) 203 continue; 204 eip_seen = 1; 205 if (strstr(buf, " is out of bounds")) 206 break; 207 lines++; 208 cout << "Code: "; 209 if (!valid()) { 210 cout << buf; 211 continue; 212 } 213 offset = strtol(buf, 0, 16); 214 char* bp_0 = strchr(buf, '>'); 215 KSym* ksym = find(eip_addr + offset); 216 if (bp_0) 217 bp_0 += 2; 218 else 219 bp_0 = strchr(buf, ':'); 220 if (ksym) 221 cout << *ksym << ' '; 222 char *bp_1 = strstr(bp_0, "\t"); // objdump from binutils 2.8... 223 if (bp_1) 224 ++bp_1; 225 else 226 bp_1 = bp_0; 227 char *bp = bp_1; 228 while (!isspace(*bp)) 229 bp++; 230 while (isspace(*bp)) 231 bp++; 232 if (!isxdigit(*bp)) { 233 cout << bp_0; 234 } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn 235 long rel_addr = strtol(bp, 0, 16); 236 ksym = find(eip_addr + rel_addr); 237 if (ksym) { 238 *bp++ = '\0'; 239 cout << bp_0 << *ksym << endl; 240 } else 241 cout << bp_0; 242 } else { 243 cout << bp_0; 244 } 245 } 246 if (!lines) 247 clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl; 248 pclose(objdump_FILE); 249 unlink(objfile_name); 250} 251 252istream& 253NameList::scan(istream& is) 254{ 255 KSym* ksyms = ksyms_0_; 256 int cardinality = 0; 257 while (!is.eof()) { 258 is >> *ksyms; 259 if (cardinality++ > 0) 260 ksyms[-1].set_extent(*ksyms); 261 ksyms++; 262 } 263 cardinality_ = --cardinality; 264 return is; 265} 266 267////////////////////////////////////////////////////////////////////////////// 268 269char const* program_name; 270 271void 272usage() 273{ 274 clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl; 275 exit(1); 276} 277 278int 279main(int argc, char** argv) 280{ 281 char c; 282 program_name = (argc--, *argv++); 283 284 NameList names; 285 if (argc > 1) 286 usage(); 287 else if (argc == 1) { 288 char const* map_file_name = (argc--, *argv++); 289 ifstream map(map_file_name); 290 if (map.bad()) 291 clog << program_name << ": Can't open `" << map_file_name << "'" << endl; 292 else { 293 map >> names; 294 cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl; 295 } 296 } 297 if (!names.valid()) 298 cout << "No symbol map. I'll only show you disassembled code." << endl; 299 cout << endl; 300 301 char buffer[1024]; 302 while (!cin.eof()) 303 { 304 long eip_addr; 305 cin >> buffer; 306 if (strequ(buffer, "EIP:") && names.valid()) { 307 cin >> ::hex >> eip_addr; 308 cin >> c >> c >> c; 309 cin >> ::hex >> eip_addr; 310 cin >> c >> c >> buffer; 311 if (!strequ(buffer, "EFLAGS:")) { 312 clog << "Please strip the line-prefixes and rerun " << program_name << endl; 313 exit(1); 314 } 315 KSym* ksym = names.find(eip_addr); 316 if (ksym) 317 cout << ">>EIP: " << *ksym << endl; 318 } else if (strequ(buffer, "Trace:") && names.valid()) { 319 unsigned long address; 320 while ((cin >> buffer) && 321 (sscanf(buffer, " [<%x>]", &address) == 1) && 322 address > 0xc) { 323 cout << "Trace: "; 324 KSym* ksym = names.find(address); 325 if (ksym) 326 cout << *ksym; 327 else 328 cout << ::hex << address; 329 cout << endl; 330 } 331 cout << endl; 332 } 333 if (strequ(buffer, "ode:") || strequ(buffer, "Code:")) { 334 // The 'C' might have been consumed as a hex number 335 unsigned char code[code_size]; 336 unsigned char* cp = code; 337 unsigned char* end = &code[code_size]; 338 while (cp < end) { 339 int c; 340 cin >> ::hex >> c; 341 *cp++ = c; 342 } 343 names.decode(code, eip_addr); 344 } 345 } 346 cout << flush; 347 348 return 0; 349} 350

