// ksymoops.cc v1.7 -- A simple filter to resolve symbols in Linux Oops-logs // Copyright (C) 1995 Greg McGary // compile like so: g++ -o ksymoops ksymoops.cc -liostream // Update to binutils 2.8 and handling of header text on oops lines by // Keith Owens ////////////////////////////////////////////////////////////////////////////// // 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 2, or (at your option) // any later version. // 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. // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, write to the // Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // This is a simple filter to resolve EIP and call-trace symbols from // a Linux kernel "Oops" log. Supply the symbol-map file name as a // command-line argument, and redirect the oops-log into stdin. Out // will come the EIP and call-trace in symbolic form. ////////////////////////////////////////////////////////////////////////////// // BUGS: // * Only resolves operands of jump and call instructions. #include #include #include #include #include #include #include #include inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); } const int code_size = 20; ////////////////////////////////////////////////////////////////////////////// class KSym { friend class NameList; private: unsigned long address_; char* name_; long offset_; long extent_; private: istream& scan(istream&); ostream& print(ostream&) const; void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; } public: friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); } friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); } }; istream& KSym::scan(istream& is) { is >> ::hex >> address_; char type; is >> type; char name[128]; is >> name; name_ = new char [strlen(name)+1]; strcpy(name_, name); offset_ = 0; return is; } ostream& KSym::print(ostream& os) const { os << ::hex << address_ + offset_ << ' ' << '<' << name_; if (offset_) os << '+' << ::hex << offset_ << '/' << ::hex << extent_; return os << '>'; } ////////////////////////////////////////////////////////////////////////////// class NameList { private: // Caution: Fixed Allocation! // This should suffice for awhile since 1.1.86 has only 2482 symbols. KSym ksyms_0_[8000]; int cardinality_; public: NameList() : cardinality_(0) { } private: istream& scan(istream&); public: int valid() { return (cardinality_ > 0); } KSym* find(unsigned long address); void decode(unsigned char* code, long eip_addr); public: friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); } }; KSym* NameList::find(unsigned long address) { if (!valid()) return 0; KSym* start = ksyms_0_; KSym* end = &ksyms_0_[cardinality_ - 1]; if (address < start->address_ || address >= end->address_) return 0; KSym* mid; while (start <= end) { mid = &start[(end - start) / 2]; if (mid->address_ < address) start = mid + 1; else if (mid->address_ > address) end = mid - 1; else return mid; } while (mid->address_ > address) --mid; mid->offset_ = address - mid->address_; if (mid->offset_ > mid->extent_) clog << "Oops! " << *mid << endl; return mid; } void NameList::decode(unsigned char* code, long eip_addr) { /* This is a hack to avoid using gcc. We create an object file by concatenating objfile_head, the twenty bytes of code, and objfile_tail. */ unsigned char objfile_head[] = { 0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; unsigned char objfile_tail[] = { 0x00, 0x90, 0x90, 0x90, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 'g', 'c', 'c', '2', '_', 'c', 'o', 'm', 'p', 'i', 'l', 'e', 'd', '.', '\0', '_', 'E', 'I', 'P', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; char const* objdump_command = "objdump -d oops_decode.o"; char const* objfile_name = &objdump_command[11]; ofstream objfile_stream(objfile_name); objfile_stream.write(objfile_head, sizeof(objfile_head)); objfile_stream.write(code, code_size); objfile_stream.write(objfile_tail, sizeof(objfile_tail)); objfile_stream.close(); FILE* objdump_FILE = popen(objdump_command, "r"); if (objdump_FILE == 0) { clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl; return; } char buf[1024]; int lines = 0; int eip_seen = 0; long offset; while (fgets(buf, sizeof(buf), objdump_FILE)) { if (strlen(buf) < 14) continue; if (eip_seen && buf[4] == ':') { // assume objdump from binutils 2.8..., reformat to old style offset = strtol(buf, 0, 16); char newbuf[sizeof(buf)]; memset(newbuf, '\0', sizeof(newbuf)); ostrstream ost(newbuf, sizeof(newbuf)); ost.width(8); ost << offset; ost << " <_EIP+" << offset << ">: " << &buf[6] << ends; strcpy(buf, newbuf); } if (!strnequ(&buf[9], "<_EIP", 5)) continue; eip_seen = 1; if (strstr(buf, " is out of bounds")) break; lines++; cout << "Code: "; if (!valid()) { cout << buf; continue; } offset = strtol(buf, 0, 16); char* bp_0 = strchr(buf, '>'); KSym* ksym = find(eip_addr + offset); if (bp_0) bp_0 += 2; else bp_0 = strchr(buf, ':'); if (ksym) cout << *ksym << ' '; char *bp_1 = strstr(bp_0, "\t"); // objdump from binutils 2.8... if (bp_1) ++bp_1; else bp_1 = bp_0; char *bp = bp_1; while (!isspace(*bp)) bp++; while (isspace(*bp)) bp++; if (!isxdigit(*bp)) { cout << bp_0; } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn long rel_addr = strtol(bp, 0, 16); ksym = find(eip_addr + rel_addr); if (ksym) { *bp++ = '\0'; cout << bp_0 << *ksym << endl; } else cout << bp_0; } else { cout << bp_0; } } if (!lines) clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl; pclose(objdump_FILE); unlink(objfile_name); } istream& NameList::scan(istream& is) { KSym* ksyms = ksyms_0_; int cardinality = 0; while (!is.eof()) { is >> *ksyms; if (cardinality++ > 0) ksyms[-1].set_extent(*ksyms); ksyms++; } cardinality_ = --cardinality; return is; } ////////////////////////////////////////////////////////////////////////////// char const* program_name; void usage() { clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl; exit(1); } int main(int argc, char** argv) { char c; char *oops_column = NULL; program_name = (argc--, *argv++); NameList names; if (argc > 1) usage(); else if (argc == 1) { char const* map_file_name = (argc--, *argv++); ifstream map(map_file_name); if (map.bad()) clog << program_name << ": Can't open `" << map_file_name << "'" << endl; else { map >> names; cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl; } } if (!names.valid()) cout << "No symbol map. I'll only show you disassembled code." << endl; cout << endl; char buffer[1024]; while (1) { long eip_addr; cin.get(buffer, sizeof(buffer)); if (cin.eof()) break; cin.get(c); /* swallow newline */ if (strstr(buffer, "EIP:") && names.valid()) { oops_column = strstr(buffer, "EIP:"); if (sscanf(oops_column+13, "[<%x>]", &eip_addr) != 1) { cout << "Cannot read eip address from EIP: line. Is this a valid oops file?" << endl; exit(1); } cout << ">>EIP: "; KSym* ksym = names.find(eip_addr); if (ksym) cout << *ksym << endl; else cout << ::hex << eip_addr << " cannot be resolved" << endl; } else if (oops_column && strstr(oops_column, "[<") && names.valid()) { unsigned long address; while (strstr(oops_column, "[<")) { char *p = oops_column; while (1) { while (*p && *p++ != '[') ; if (sscanf(p, "<%x>]", &address) != 1) break; cout << "Trace: "; KSym* ksym = names.find(address); if (ksym) cout << *ksym; else cout << ::hex << address; cout << endl; } cin.get(buffer, sizeof(buffer)); if (cin.eof()) break; cin.get(c); /* swallow newline */ } } if (oops_column && strnequ(oops_column, "Code:", 5)) { unsigned char code[code_size]; unsigned char* cp = code; unsigned char* end = &code[code_size]; char *p = oops_column + 5; int c; memset(code, '\0', sizeof(code)); while (*p && cp < end) { while (*p == ' ') ++p; if (sscanf(p, "%x", &c) != 1) break; *cp++ = c; while (*p && *p++ != ' ') ; } names.decode(code, eip_addr); } } cout << flush; return 0; }