📄 ksym.c
字号:
} } if(0){bad_alloc: fprintf(stderr, "Warning: not enough memory available\n"); } if(0){bad_parse: fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n"); }quiet_goodbye: idx_room = 0; if(ksyms_data) free(ksyms_data) , ksyms_data = NULL; ksyms_room = 0; if(ksyms_index) free(ksyms_index) , ksyms_index = NULL; ksyms_count = 0; return 0;}/*********************************/#define VCNT 16static int sysmap_mmap(const char *restrict const filename, message_fn message) { struct stat sbuf; char *endp; int fd; char Version[32]; fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); if(fd<0) return 0; if(fstat(fd, &sbuf) < 0) goto bad_open; if(!S_ISREG(sbuf.st_mode)) goto bad_open; if(sbuf.st_size < 5000) goto bad_open; /* if way too small */ /* Would be shared read-only, but we want '\0' after each name. */ endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); sysmap_data = endp; while(*endp==' '){ /* damn Alpha machine types */ if(strncmp(endp," w ", 19)) goto bad_parse; endp += 19; endp = strchr(endp,'\n'); if(!endp) goto bad_parse; /* no newline */ if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse; endp++; } if(sysmap_data == (caddr_t) -1) goto bad_open; close(fd); fd = -1; sprintf(Version, "Version_%d", linux_version_code); sysmap_room = 512; for(;;){ void *vp; sysmap_room *= 2; vp = realloc(sysmap_index, sizeof(symb)*sysmap_room); if(!vp) goto bad_alloc; sysmap_index = vp; for(;;){ char *vstart; if(endp - sysmap_data >= sbuf.st_size){ /* if we reached the end */ int i = VCNT; /* check VCNT times to verify this file */ if(*Version) goto bad_version; if(!ksyms_index) return 1; /* if can not verify, assume success */ while(i--){#if 1 const symb *findme; const symb *map_symb; /* Choose VCNT entries from /proc/ksyms to test */ findme = ksyms_index + (ksyms_count*i/VCNT); /* Search for them in the System.map */ map_symb = search(findme->addr, sysmap_index, sysmap_count); if(map_symb){ if(map_symb->addr != findme->addr) continue; /* backup to first matching address */ while (map_symb != sysmap_index){ if (map_symb->addr != (map_symb-1)->addr) break; map_symb--; } /* search for name in symbols with same address */ while (map_symb != (sysmap_index+sysmap_count)){ if (map_symb->addr != findme->addr) break; if (!strcmp(map_symb->name,findme->name)) goto good_match; map_symb++; } map_symb--; /* backup to last symbol with matching address */ message("{%s} {%s}\n",map_symb->name,findme->name); goto bad_match; }good_match:;#endif } return 1; /* success */ } sysmap_index[sysmap_count].addr = STRTOUKL(endp, &endp, 16); if(*endp != ' ') goto bad_parse; endp++; if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse; endp++; if(*endp != ' ') goto bad_parse; endp++; vstart = endp; endp = strchr(endp,'\n'); if(!endp) goto bad_parse; /* no newline */ *endp = '\0'; ++endp; vstart = chop_version(vstart); sysmap_index[sysmap_count].name = vstart; if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0'; if(++sysmap_count >= sysmap_room) break; /* need more space */ } } if(0){bad_match: message("Warning: %s does not match kernel data.\n", filename); } if(0){bad_version: message("Warning: %s has an incorrect kernel version.\n", filename); } if(0){bad_alloc: message("Warning: not enough memory available\n"); } if(0){bad_parse: message("Warning: %s not parseable as a System.map\n", filename); } if(0){bad_open: message("Warning: %s could not be opened as a System.map\n", filename); } sysmap_room=0; sysmap_count=0; if(sysmap_index) free(sysmap_index); sysmap_index = NULL; if(fd>=0) close(fd); if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1); sysmap_data = NULL; return 0;}/*********************************/static void read_and_parse(void){ static time_t stamp; /* after data gets old, load /proc/ksyms again */ if(time(NULL) != stamp){ read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room); parse_ksyms(); memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */ stamp = time(NULL); }}/*********************************/static void default_message(const char *restrict format, ...) __attribute__((format(printf,1,2)));static void default_message(const char *restrict format, ...) { va_list arg; va_start (arg, format); vfprintf (stderr, format, arg); va_end (arg);}/*********************************/static int use_wchan_file;int open_psdb_message(const char *restrict override, message_fn message) { static const char *sysmap_paths[] = { "/boot/System.map-%s", "/boot/System.map", "/lib/modules/%s/System.map", "/usr/src/linux/System.map", "/System.map", NULL }; struct stat sbuf; struct utsname uts; char path[128]; const char **fmt = sysmap_paths; const char *sm;#ifdef SYSMAP_FILENAME /* debug feature */ override = SYSMAP_FILENAME;#endif // first allow for a user-selected System.map file if( (sm=override) || (sm=getenv("PS_SYSMAP")) || (sm=getenv("PS_SYSTEM_MAP")) ){ if(!have_privs){ read_and_parse(); if(sysmap_mmap(sm, message)) return 0; } /* failure is better than ignoring the user & using bad data */ return -1; /* ought to return "Namelist not found." */ } // next try the Linux 2.5.xx method if(!stat("/proc/self/wchan", &sbuf)){ use_wchan_file = 1; // hack return 0; } // finally, search for the System.map file uname(&uts); path[sizeof path - 1] = '\0'; do{ int did_ksyms = 0; snprintf(path, sizeof path - 1, *fmt, uts.release); if(!stat(path, &sbuf)){ if (did_ksyms++) read_and_parse(); if (sysmap_mmap(path, message)) return 0; } }while(*++fmt); /* TODO: Without System.map, no need to keep ksyms loaded. */ return -1;}/***************************************/int open_psdb(const char *restrict override) { return open_psdb_message(override, default_message);}/***************************************/static const char * read_wchan_file(unsigned pid){ static char buf[64]; const char *ret = buf; ssize_t num; int fd; snprintf(buf, sizeof buf, "/proc/%d/wchan", pid); fd = open(buf, O_RDONLY); if(fd==-1) return "?"; num = read(fd, buf, sizeof buf - 1); close(fd); if(num<1) return "?"; // allow for "0" buf[num] = '\0'; if(buf[0]=='0' && buf[1]=='\0') return "-"; // would skip over numbers if they existed -- but no // lame ppc64 has a '.' in front of every name if(*ret=='.') ret++; switch(*ret){ case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; case '_': while(*ret=='_') ret++; break; } return ret;}/***************************************/static const symb fail = { .name = "?" };static const char dash[] = "-";static const char star[] = "*";#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk *//* return pointer to temporary static buffer with function name */const char * lookup_wchan(unsigned KLONG address, unsigned pid) { const symb *mod_symb; const symb *map_symb; const symb *good_symb; const char *ret; unsigned hash; // can't cache it due to a race condition :-( if(use_wchan_file) return read_wchan_file(pid); if(!address) return dash; if(!~address) return star; read_and_parse(); hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */ if(hashtable[hash].addr == address) return hashtable[hash].name; mod_symb = search(address, ksyms_index, ksyms_count); if(!mod_symb) mod_symb = &fail; map_symb = search(address, sysmap_index, sysmap_count); if(!map_symb) map_symb = &fail; /* which result is closest? */ good_symb = (mod_symb->addr > map_symb->addr) ? mod_symb : map_symb ; if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail; /* good_symb->name has the data, but needs to be trimmed */ ret = good_symb->name; // lame ppc64 has a '.' in front of every name if(*ret=='.') ret++; switch(*ret){ case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; case '_': while(*ret=='_') ret++; break; } /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */ /* cache name after abbreviation */ hashtable[hash].addr = address; hashtable[hash].name = ret; return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -