📄 oops.c
字号:
/* oops.c. Oops processing for ksymoops. Copyright 1999 Keith Owens <kaos@ocs.com.au>. Released under the GNU Public Licence, Version 2. */#include "ksymoops.h"#include <ctype.h>#include <errno.h>#include <malloc.h>#include <memory.h>#include <stdlib.h>#include <string.h>#include <unistd.h>/* Error detected by bfd */static void Oops_bfd_perror(const char *msg){ static char const procname[] = "Oops_bfd_perror"; bfd_error_type err = bfd_get_error(); ERROR("%s %s", msg, bfd_errmsg(err));}/* Open the ksymoops binary so we have an input bfd to copy data from. */static void Oops_open_input_bfd(char **me, bfd **ibfd, const OPTIONS *options){ char **matches, **match; static char const procname[] = "Oops_open_input_bfd"; *me = find_fullpath(prefix); if (!*ibfd && !(*ibfd = bfd_openr(*me, NULL))) { Oops_bfd_perror(*me); FATAL("%s", "giving up"); } if (!bfd_check_format_matches(*ibfd, bfd_object, &matches)) { Oops_bfd_perror(*me); if (bfd_get_error() == bfd_error_file_ambiguously_recognized) { printf("%s Matching formats:", procname); match = matches; while (*match) printf(" %s", *match++); printf("\n"); free(matches); } FATAL("%s", "giving up"); }}/* If target or architecture is not already set, default to the same as * ksymoops. */static void Oops_set_default_ta(const char *me, const bfd *ibfd, OPTIONS *options){ static char const procname[] = "Oops_set_default_ta"; const char *bt; const bfd_arch_info_type *bai; int t = 0, a = 0; if (!options->target) { bt = bfd_get_target(ibfd); /* Bah, undocumented bfd function */ if (!bt) { Oops_bfd_perror(me); FATAL("cannot get bfd_target for %s:", me); } options->target = bt; t = 1; } if (!options->architecture) { /* bfd_get_arch_info should really take (const bfd *) */ bai = bfd_get_arch_info((bfd *)ibfd); if (!bai) { Oops_bfd_perror(me); FATAL("cannot get bfd_arch_info for %s:", me); } options->architecture = bai->printable_name; a = 1; } if (t || a) { printf("Using defaults from ksymoops"); if (t) printf(" -t %s", options->target); if (a) printf(" -a %s", options->architecture); printf("\n"); }}/* Write the code values to a file using bfd. */static int Oops_write_bfd_data(const bfd *ibfd, bfd *obfd, const char *code, int size){ asection *isec, *osec; asymbol *osym; /* bfd_get_section_by_name should really take (const bfd *,) */ if (!(isec = bfd_get_section_by_name((bfd *)ibfd, ".text"))) { Oops_bfd_perror("get_section"); return(0); } if (!bfd_set_start_address(obfd, 0)) { Oops_bfd_perror("set_start_address"); return(0); } if (!(osec = bfd_make_section(obfd, ".text"))) { Oops_bfd_perror("make_section"); return(0); } if (!bfd_set_section_flags(obfd, osec, bfd_get_section_flags(ibfd, isec))) { Oops_bfd_perror("set_section_flags"); return(0); } if (!bfd_set_section_alignment(obfd, osec, bfd_get_section_alignment(ibfd, isec))) { Oops_bfd_perror("set_section_alignment"); return(0); } osec->output_section = osec; if (!(osym = bfd_make_empty_symbol(obfd))) { Oops_bfd_perror("make_empty_symbol"); return(0); } osym->name = "_XXX"; osym->section = osec; osym->flags = BSF_GLOBAL; osym->value = 0; if (!bfd_set_symtab(obfd, &osym, 1)) { Oops_bfd_perror("set_symtab"); return(0); } if (!bfd_set_section_size(obfd, osec, size)) { Oops_bfd_perror("set_section_size"); return(0); } if (!bfd_set_section_vma(obfd, osec, 0)) { Oops_bfd_perror("set_section_vma"); return(0); } if (!bfd_set_section_contents(obfd, osec, (PTR) code, 0, size)) { Oops_bfd_perror("set_section_contents"); return(0); } if (!bfd_close(obfd)) { Oops_bfd_perror("close(obfd)"); return(0); } return 1;}/* Write the Oops code to a temporary file with suitable header and trailer. */static char *Oops_code_to_file(const char *code, int size, const bfd *ibfd, OPTIONS *options){ char *file, *tmpdir; int fd; bfd *obfd; const bfd_arch_info_type *bai; static const char temp_suffix[] = "/ksymoops.XXXXXX"; static char const procname[] = "Oops_code_to_file"; /* Security fix, use mkstemp and honour TMPDIR */ if (!(tmpdir = getenv("TMPDIR")) || !*tmpdir) {#ifdef P_tmpdir tmpdir = P_tmpdir;#else tmpdir = "/tmp";#endif } file = malloc(strlen(tmpdir) + sizeof(temp_suffix)); if (!file) malloc_error(procname); strcpy(file, tmpdir); strcat(file, temp_suffix); if ((fd = mkstemp(file)) < 0) { ERROR("Unable to open mkstemp file '%s'\n", file); perror(prefix); return(NULL); } close(fd); /* Set the target on the output file */ if (!(obfd = bfd_openw(file, options->target))) { Oops_bfd_perror(file); if (bfd_get_error() == bfd_error_wrong_format) printf("Sorry, looks like your binutils cannot " "handle target %s\n", options->target); return(NULL); } bfd_set_format(obfd, bfd_object); /* Set the architecture on the output file */ if (!(bai = bfd_scan_arch(options->architecture))) { Oops_bfd_perror("scan_arch for specified architecture"); if (bfd_get_error() == bfd_error_wrong_format) printf("Sorry, looks like your binutils cannot " "handle the specified architecture\n"); return(NULL); } bfd_set_arch_info(obfd, bai); options->address_bits = bfd_arch_bits_per_address(obfd); if (!Oops_write_bfd_data(ibfd, obfd, code, size)) return(NULL); return(file);}/* Run objdump against the binary Oops code */static FILE *Oops_objdump(const char *file, const OPTIONS *options){ char *cmd; FILE *f; int cmd_strlen; static char const objdump_options[] = "-dhf --target="; static char const procname[] = "Oops_objdump"; /* remember to leave space for spaces */ cmd_strlen = strlen(path_objdump)+1+strlen(objdump_options)+strlen(options->target)+1+strlen(file)+1; cmd = malloc(cmd_strlen); if (!cmd) malloc_error(procname); strcpy(cmd, path_objdump); strcat(cmd, " "); strcat(cmd, objdump_options); strcat(cmd, options->target); strcat(cmd, " "); strcat(cmd, file); DEBUG(2, "command '%s'", cmd); f = popen_local(cmd, procname); free(cmd); return(f);}/* Forward references. */static const char *Oops_arch_to_eip(const OPTIONS *options);static void Oops_decode_one_add(SYMBOL_SET *ss, const addr_t address, const char *line1, const char type, const OPTIONS *options);static addr_t Oops_truncate_address(addr_t address, const OPTIONS *options);/* Process one code line from objdump, ignore everything else */static void Oops_decode_one(SYMBOL_SET *ss, const char *line, addr_t eip, int adjust, const char type, const OPTIONS *options){ int i; addr_t address, eip_relative; char *line2, *map, **string = NULL; static regex_t re_Oops_objdump; static regmatch_t *re_Oops_objdump_pmatch; static char const procname[] = "Oops_decode_one"; /* objdump output. Optional whitespace, hex digits, optional * ' <_XXX+offset>', ':'. The '+offset' after _XXX is also optional. * Older binutils output 'xxxxxxxx <_XXX+offset>:', newer versions do * '00000000 <_XXX>:' first followed by ' xx:' lines. * * Just to complicate things even more, objdump recognises jmp, call, * etc., converts the code to something like this :- * " f: e8 32 34 00 00 call 3446 <_XXX+0x3446>" * Recognise this and append the eip adjusted address, followed by the * map_address text for that address. * * With any luck, objdump will take care of all such references which * makes this routine architecture insensitive. No need to test for * i386 jmp, call or m68k swl etc. */ RE_COMPILE(&re_Oops_objdump, "^ *" "([0-9a-fA-F]+)" /* 1 */ "( <_XXX[^>]*>)?" /* 2 */ ":" "(" /* 3 */ ".* +<_XXX\\+0?x?([0-9a-fA-F]+)> *$" /* 4 */ ")?" ".*" , REG_NEWLINE|REG_EXTENDED|REG_ICASE, &re_Oops_objdump_pmatch); i = regexec(&re_Oops_objdump, line, re_Oops_objdump.re_nsub+1, re_Oops_objdump_pmatch, 0); DEBUG(4, "regexec %d", i); if (i != 0) return; re_strings(&re_Oops_objdump, line, re_Oops_objdump_pmatch, &string); address = hexstring(string[1]); if (errno) { ERROR("Invalid hex value in objdump line, treated as zero - '%s'\n" " objdump line '%s'", string[1], line); perror(prefix); address = 0; } address = Oops_truncate_address(address + eip + adjust, options); if (string[4]) { /* EIP relative data to be adjusted. */ eip_relative = hexstring(string[4]); if (errno) { ERROR("Invalid hex value in objdump line, treated as zero - '%s'\n" "objdump line '%s'", string[4], line); perror(prefix); eip_relative = 0; } eip_relative = Oops_truncate_address(eip_relative + eip + adjust, options); map = map_address(&ss_merged, eip_relative, options); /* new text is original line, eip_relative in hex, map text */ i = strlen(line)+1+2*sizeof(eip_relative)+1+strlen(map)+1; line2 = malloc(i); if (!line2) malloc_error(procname); snprintf(line2, i, "%s %s %s", line, format_address(eip_relative, options), map); Oops_decode_one_add(ss, address, line2, type, options); free(line2); } else Oops_decode_one_add(ss, address, line, type, options); /* as is */ re_strings_free(&re_Oops_objdump, &string);}/* Maximum number of code bytes to process. It needs to be a multiple of 2 for * endianess swapping (-e). Sparc and alpha dump 36 bytes so use 64. */#define CODE_SIZE 64/* Extract the hex values from the Code: line and convert to binary. * Strange as it seems, this is actually architecture independent, nothing in * this proc cares who created the Code: line. */static int Oops_code_values(const unsigned char* code_text, unsigned char *code, int *adjust, const OPTIONS *options){ int byte = 0, byte_prev, len, ret = 1; U16 u16; U32 u32; U64 u64, value; char **string = NULL; const char *p; static regex_t re_Oops_code_value; static regmatch_t *re_Oops_code_value_pmatch; static const char procname[] = "Oops_code_values"; /* Given by re_Oops_code: code_text is a message (e.g. "general protection") * or one or more hex fields separated by spaces. Some architectures * bracket the current instruction with '<' and '>', others use '(' and ')'. * The first character is nonblank. */ if (!isxdigit(*code_text)) { WARNING("%s", "Code looks like message, not hex digits. " "No disassembly attempted."); return(0); } memset(code, '\0', CODE_SIZE); p = code_text; *adjust = 0; /* EIP points to code byte 0 */ /* Code values. Hex values separated by white space. On some systems, the * current instruction is bracketed in '<' and '>' or '(' and ')'. */ RE_COMPILE(&re_Oops_code_value, "^" "(" /* 1 */ "([<(]?)" /* 2 */ "([0-9a-fA-F]+)" /* 3 */ "[)>]?" " *" ")" "|(Bad .*)" /* 4 */ , REG_NEWLINE|REG_EXTENDED|REG_ICASE, &re_Oops_code_value_pmatch); while (regexec(&re_Oops_code_value, p, re_Oops_code_value.re_nsub+1, re_Oops_code_value_pmatch, 0) == 0) { re_strings(&re_Oops_code_value, p, re_Oops_code_value_pmatch, &string); if (string[4] && *(string[4])) { ret = 0; break; /* Bad EIP value, no code bytes */ } if (byte >= CODE_SIZE) break; value = hexstring(string[3]); if (errno) { ERROR("Invalid hex value in code_value line, treated as zero - " "'%s'\ncode_value line '%s'", string[3], code_text); perror(prefix); value = 0; } if (string[2] && *(string[2])) *adjust = -byte; /* this byte is EIP */ /* On some architectures Code: is a stream of bytes, on some it is a * stream of shorts, on some it is a stream of ints. Consistent we're * not! */ len = strlen(string[3]); byte_prev = byte; if (byte+len/2 > CODE_SIZE) { WARNING("extra values in Code line, ignored - '%s'", string[3]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -