mirror of
https://github.com/crash-utility/crash
synced 2025-02-23 08:56:48 +00:00
per-program and per-map data for the "bpf -p ID" and "bpf -m ID" options, containing data items shown by the "bpftool prog list" and "bpftool map list" options; new "bpf -P" and "bpf -M" options have been added that dump the extra data for all loaded programs or tasks. (anderson@redhat.com)
13072 lines
368 KiB
C
13072 lines
368 KiB
C
/* symbols.c - core analysis suite
|
|
*
|
|
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
|
* Copyright (C) 2002-2018 David Anderson
|
|
* Copyright (C) 2002-2018 Red Hat, Inc. 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 as published by
|
|
* the Free Software Foundation; either version 2 of the License, 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.
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <elf.h>
|
|
#ifdef GDB_7_6
|
|
#define __CONFIG_H__ 1
|
|
#include "config.h"
|
|
#endif
|
|
#include "bfd.h"
|
|
|
|
static void store_symbols(bfd *, int, void *, long, unsigned int);
|
|
static void store_sysmap_symbols(void);
|
|
static ulong relocate(ulong, char *, int);
|
|
static int relocate_force(ulong, char *);
|
|
static void kaslr_init(void);
|
|
static ulong symbol_value_from_proc_kallsyms(char *);
|
|
static void strip_module_symbol_end(char *s);
|
|
static int compare_syms(const void *, const void *);
|
|
static int compare_mods(const void *, const void *);
|
|
static int compare_prios(const void *v1, const void *v2);
|
|
static int compare_size_name(const void *, const void *);
|
|
struct type_request;
|
|
static void append_struct_symbol (struct type_request *, struct gnu_request *);
|
|
static void request_types(ulong, ulong, char *);
|
|
static asection *get_kernel_section(char *);
|
|
static char * get_section(ulong vaddr, char *buf);
|
|
static void symbol_dump(ulong, char *);
|
|
static void check_for_dups(struct load_module *);
|
|
static struct syment *kallsyms_module_symbol(struct load_module *, symbol_info *);
|
|
static int kallsyms_module_function_size(struct syment *, struct load_module *, ulong *);
|
|
static void store_load_module_symbols \
|
|
(bfd *, int, void *, long, uint, ulong, char *);
|
|
static int load_module_index(struct syment *);
|
|
static void section_header_info(bfd *, asection *, void *);
|
|
static void store_section_data(struct load_module *, bfd *, asection *);
|
|
static void calculate_load_order_v1(struct load_module *, bfd *);
|
|
static void calculate_load_order_v2(struct load_module *, bfd *, int,
|
|
void *, long, unsigned int);
|
|
static void check_insmod_builtin(struct load_module *, int, ulong *);
|
|
static int is_insmod_builtin(struct load_module *, struct syment *);
|
|
struct load_module;
|
|
static int add_symbol_file(struct load_module *);
|
|
static int add_symbol_file_kallsyms(struct load_module *, struct gnu_request *);
|
|
static void find_mod_etext(struct load_module *);
|
|
static long rodata_search(ulong *, ulong);
|
|
static int ascii_long(ulong word);
|
|
static int is_bfd_format(char *);
|
|
static int is_binary_stripped(char *);
|
|
static int namespace_ctl(int, struct symbol_namespace *, void *, void *);
|
|
static void symval_hash_init(void);
|
|
static struct syment *symval_hash_search(ulong);
|
|
static void symname_hash_init(void);
|
|
static void symname_hash_install(struct syment *);
|
|
static struct syment *symname_hash_search(char *);
|
|
static void gnu_qsort(bfd *, void *, long, unsigned int, asymbol *, asymbol *);
|
|
static int check_gnu_debuglink(bfd *);
|
|
static int separate_debug_file_exists(const char *, unsigned long, int *);
|
|
static int store_module_kallsyms_v1(struct load_module *, int, int, char *);
|
|
static int store_module_kallsyms_v2(struct load_module *, int, int, char *);
|
|
static void datatype_error(void **, char *, char *, char *, int);
|
|
static char *get_thisfile(void);
|
|
struct elf_common;
|
|
static void Elf32_Sym_to_common(Elf32_Sym *, struct elf_common *);
|
|
static void Elf64_Sym_to_common(Elf64_Sym *, struct elf_common *);
|
|
static void cmd_datatype_common(ulong);
|
|
static void do_datatype_addr(struct datatype_member *, ulong, int,
|
|
ulong, char **, int);
|
|
static void process_gdb_output(char *, unsigned, const char *, int);
|
|
static char *expr_type_name(const char *);
|
|
static int display_per_cpu_info(struct syment *, int, char *);
|
|
static struct load_module *get_module_percpu_sym_owner(struct syment *);
|
|
static int is_percpu_symbol(struct syment *);
|
|
static void dump_percpu_symbols(struct load_module *);
|
|
static void print_struct_with_dereference(ulong, struct datatype_member *, ulong);
|
|
static int dereference_pointer(ulong, struct datatype_member *, ulong);
|
|
|
|
#define KERNEL_SECTIONS (void *)(1)
|
|
#define MODULE_SECTIONS (void *)(2)
|
|
#define VERIFY_SECTIONS (void *)(3)
|
|
|
|
#define EV_DWARFEXTRACT 101010101
|
|
|
|
#define PARSE_FOR_DATA (1)
|
|
#define PARSE_FOR_DECLARATION (2)
|
|
static void parse_for_member(struct datatype_member *, ulong);
|
|
static int show_member_offset(FILE *, struct datatype_member *, char *);
|
|
struct struct_elem;
|
|
static void free_structure(struct struct_elem *);
|
|
static unsigned char is_right_brace(const char *);
|
|
static struct struct_elem *find_node(struct struct_elem *, char *);
|
|
static void dump_node(struct struct_elem *, char *, unsigned char, unsigned char);
|
|
|
|
|
|
/*
|
|
* structure/union printing stuff
|
|
*/
|
|
|
|
#define UINT8 (0x1)
|
|
#define INT8 (0x2)
|
|
#define UINT16 (0x4)
|
|
#define INT16 (0x8)
|
|
#define UINT32 (0x10)
|
|
#define INT32 (0x20)
|
|
#define UINT64 (0x40)
|
|
#define INT64 (0x80)
|
|
#define POINTER (0x100)
|
|
#define FUNCTION (0x200)
|
|
#define UNION_REQUEST (0x400)
|
|
#define STRUCT_REQUEST (0x800)
|
|
#define ARRAY (0x1000)
|
|
#define ENUM (0x2000)
|
|
#define TYPEDEF (0x4000)
|
|
#define STRUCT_VERBOSE (0x8000)
|
|
#define SHOW_OFFSET (0x10000)
|
|
#define IN_UNION (0x20000)
|
|
#define IN_STRUCT (0x40000)
|
|
#define DATATYPE_QUERY (0x80000)
|
|
#define ANON_MEMBER_QUERY (0x100000)
|
|
#define SHOW_RAW_DATA (0x200000)
|
|
#define DEREF_POINTERS (0x400000)
|
|
|
|
#define INTEGER_TYPE (UINT8|INT8|UINT16|INT16|UINT32|INT32|UINT64|INT64)
|
|
|
|
#define INITIAL_INDENT (4)
|
|
#define INDENT_INCR (2)
|
|
|
|
static void whatis_datatype(char *, ulong, FILE *);
|
|
static void whatis_variable(struct syment *);
|
|
static void print_struct(char *, ulong);
|
|
static void print_union(char *, ulong);
|
|
static void dump_datatype_member(FILE *, struct datatype_member *);
|
|
static void dump_datatype_flags(ulong, FILE *);
|
|
static long anon_member_offset(char *, char *);
|
|
static int gdb_whatis(char *);
|
|
static void do_datatype_declaration(struct datatype_member *, ulong);
|
|
static int member_to_datatype(char *, struct datatype_member *, ulong);
|
|
|
|
#define DEBUGINFO_ERROR_MESSAGE1 \
|
|
"the use of a System.map file requires that the accompanying namelist\nargument is a kernel file built with the -g CFLAG. The namelist argument\nsupplied in this case is a debuginfo file, which must be accompanied by the\nkernel file from which it was derived.\n"
|
|
|
|
#define DEBUGINFO_ERROR_MESSAGE2 \
|
|
"The namelist argument supplied in this case is a debuginfo file,\nwhich must be accompanied by the kernel file from which it was derived.\n"
|
|
|
|
/*
|
|
* This routine scours the namelist for kernel text and data symbols,
|
|
* sorts, and stores, them in a static table for quick reference.
|
|
*/
|
|
void
|
|
symtab_init(void)
|
|
{
|
|
char **matching;
|
|
long symcount;
|
|
void *minisyms;
|
|
unsigned int size;
|
|
asymbol *sort_x;
|
|
asymbol *sort_y;
|
|
|
|
if ((st->bfd = bfd_openr(pc->namelist, NULL)) == NULL)
|
|
error(FATAL, "cannot open object file: %s\n", pc->namelist);
|
|
|
|
if (!bfd_check_format_matches(st->bfd, bfd_object, &matching))
|
|
error(FATAL, "cannot determine object file format: %s\n",
|
|
pc->namelist);
|
|
/*
|
|
* Check whether the namelist is a kerntypes file built by
|
|
* dwarfextract, which places a magic number in e_version.
|
|
*/
|
|
if (file_elf_version(pc->namelist) == EV_DWARFEXTRACT)
|
|
pc->flags |= KERNTYPES;
|
|
|
|
if (pc->flags & SYSMAP) {
|
|
bfd_map_over_sections(st->bfd, section_header_info,
|
|
VERIFY_SECTIONS);
|
|
if ((st->flags & (NO_SEC_LOAD|NO_SEC_CONTENTS)) ==
|
|
(NO_SEC_LOAD|NO_SEC_CONTENTS)) {
|
|
error(INFO, "%s: no text and data contents\n",
|
|
pc->namelist);
|
|
error(FATAL, pc->flags & SYSMAP_ARG ?
|
|
DEBUGINFO_ERROR_MESSAGE1 :
|
|
DEBUGINFO_ERROR_MESSAGE2);
|
|
}
|
|
store_sysmap_symbols();
|
|
return;
|
|
} else if (LKCD_KERNTYPES())
|
|
error(FATAL, "%s: use of kerntypes requires a system map\n",
|
|
pc->namelist);
|
|
|
|
/*
|
|
* Pull a bait-and-switch on st->bfd if we've got a separate
|
|
* .gnu_debuglink file that matches the CRC. Not done for kerntypes.
|
|
*/
|
|
if (!(LKCD_KERNTYPES()) &&
|
|
!(bfd_get_file_flags(st->bfd) & HAS_SYMS)) {
|
|
if (!check_gnu_debuglink(st->bfd))
|
|
no_debugging_data(FATAL);
|
|
}
|
|
|
|
/*
|
|
* Gather references to the kernel sections.
|
|
*/
|
|
if ((st->sections = (struct sec *)
|
|
malloc(st->bfd->section_count * sizeof(struct sec *))) == NULL)
|
|
error(FATAL, "symbol table section array malloc: %s\n",
|
|
strerror(errno));
|
|
BZERO(st->sections, st->bfd->section_count * sizeof(struct sec *));
|
|
st->first_section_start = st->last_section_end = 0;
|
|
|
|
bfd_map_over_sections(st->bfd, section_header_info, KERNEL_SECTIONS);
|
|
if ((st->flags & (NO_SEC_LOAD|NO_SEC_CONTENTS)) ==
|
|
(NO_SEC_LOAD|NO_SEC_CONTENTS)) {
|
|
if (!pc->namelist_debug && !pc->debuginfo_file) {
|
|
error(INFO, "%s: no text and data contents\n",
|
|
pc->namelist);
|
|
error(FATAL, DEBUGINFO_ERROR_MESSAGE2);
|
|
}
|
|
}
|
|
|
|
symcount = bfd_read_minisymbols(st->bfd, FALSE, &minisyms, &size);
|
|
|
|
if (symcount <= 0)
|
|
no_debugging_data(FATAL);
|
|
|
|
sort_x = bfd_make_empty_symbol(st->bfd);
|
|
sort_y = bfd_make_empty_symbol(st->bfd);
|
|
if (sort_x == NULL || sort_y == NULL)
|
|
error(FATAL, "bfd_make_empty_symbol() failed\n");
|
|
|
|
kaslr_init();
|
|
|
|
gnu_qsort(st->bfd, minisyms, symcount, size, sort_x, sort_y);
|
|
|
|
store_symbols(st->bfd, FALSE, minisyms, symcount, size);
|
|
|
|
free(minisyms);
|
|
|
|
symname_hash_init();
|
|
symval_hash_init();
|
|
}
|
|
|
|
/*
|
|
* Adapted from gdb's get_debug_link_info()
|
|
*
|
|
* Look in: current directory
|
|
* basename-of-namelist/.debug directory
|
|
* /usr/lib/debug/boot (since we know it's a Red Hat kernel)
|
|
*/
|
|
static int
|
|
check_gnu_debuglink(bfd *bfd)
|
|
{
|
|
int i, exists, found;
|
|
asection *sect;
|
|
bfd_size_type debuglink_size;
|
|
char *contents;
|
|
int crc_offset;
|
|
unsigned long crc32;
|
|
char *dirname;
|
|
char *namelist_debug;
|
|
char **matching;
|
|
|
|
sect = bfd_get_section_by_name(bfd, ".gnu_debuglink");
|
|
if (!sect) {
|
|
error(INFO, "%s: no .gnu_debuglink section\n", pc->namelist);
|
|
return FALSE;
|
|
}
|
|
|
|
debuglink_size = bfd_section_size(bfd, sect);
|
|
|
|
contents = GETBUF(debuglink_size);
|
|
|
|
bfd_get_section_contents(bfd, sect, contents,
|
|
(file_ptr)0, (bfd_size_type)debuglink_size);
|
|
|
|
crc_offset = strlen (contents) + 1;
|
|
crc_offset = (crc_offset + 3) & ~3;
|
|
|
|
crc32 = bfd_get_32(bfd, (bfd_byte *)(contents + crc_offset));
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(NOTE, "gnu_debuglink file: %s\ncrc32: %lx\n",
|
|
contents, crc32);
|
|
|
|
if ((pc->debuginfo_file = (char *)
|
|
malloc(((strlen(pc->namelist) + strlen("/.debug/") +
|
|
+ strlen(".debug") + strlen(" /usr/lib/debug/boot/ "))*10)
|
|
+ strlen(pc->namelist_debug ? pc->namelist_debug : " "))) == NULL)
|
|
error(FATAL, "debuginfo file name malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
dirname = GETBUF(strlen(pc->namelist)+1);
|
|
strcpy(dirname, pc->namelist);
|
|
|
|
for (i = strlen(dirname)-1; i >= 0; i--)
|
|
{
|
|
if (dirname[i] == '/')
|
|
break;
|
|
}
|
|
dirname[i+1] = NULLCHAR;
|
|
if (!strlen(dirname))
|
|
sprintf(dirname, ".");
|
|
|
|
namelist_debug = NULL;
|
|
if (pc->namelist_debug) {
|
|
sprintf(pc->debuginfo_file, "%s", pc->namelist_debug);
|
|
if (separate_debug_file_exists(pc->debuginfo_file,
|
|
crc32, &exists)) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: CRC matches\n",
|
|
pc->debuginfo_file);
|
|
st->flags |= CRC_MATCHES;
|
|
goto reset_bfd;
|
|
} else {
|
|
if ((st->flags & FORCE_DEBUGINFO) && exists) {
|
|
error(WARNING,
|
|
"%s:\n CRC value does not match\n\n",
|
|
pc->debuginfo_file);
|
|
goto reset_bfd;
|
|
} else
|
|
error(INFO, "%s:\n CRC value does not match\n\n",
|
|
pc->debuginfo_file);
|
|
namelist_debug = pc->namelist_debug;
|
|
pc->namelist_debug = NULL;
|
|
}
|
|
}
|
|
|
|
found = 0;
|
|
sprintf(pc->debuginfo_file, "%s/%s", dirname, contents);
|
|
if (separate_debug_file_exists(pc->debuginfo_file, crc32, &exists)) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: CRC matches\n", pc->debuginfo_file);
|
|
st->flags |= CRC_MATCHES;
|
|
goto reset_bfd;
|
|
} else {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: %s\n", pc->debuginfo_file, exists ?
|
|
"CRC does not match" : "not readable/found");
|
|
if (exists) {
|
|
error(INFO, "%s: CRC does not match\n\n",
|
|
pc->debuginfo_file);
|
|
found++;
|
|
}
|
|
}
|
|
|
|
sprintf(pc->debuginfo_file, "%s/.debug/%s", dirname, contents);
|
|
if (separate_debug_file_exists(pc->debuginfo_file, crc32, &exists)) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: CRC matches\n", pc->debuginfo_file);
|
|
st->flags |= CRC_MATCHES;
|
|
goto reset_bfd;
|
|
} else {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: %s\n", pc->debuginfo_file, exists ?
|
|
"CRC does not match" : "not readable/found");
|
|
if (exists) {
|
|
error(INFO, "%s: CRC does not match\n\n",
|
|
pc->debuginfo_file);
|
|
found++;
|
|
}
|
|
}
|
|
|
|
sprintf(pc->debuginfo_file, "/usr/lib/debug/boot/%s", contents);
|
|
if (separate_debug_file_exists(pc->debuginfo_file, crc32, &exists)) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: CRC matches\n", pc->debuginfo_file);
|
|
st->flags |= CRC_MATCHES;
|
|
goto reset_bfd;
|
|
} else {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: %s\n", pc->debuginfo_file, exists ?
|
|
"CRC does not match" : "not readable/found");
|
|
if (exists) {
|
|
error(INFO, "%s: CRC does not match\n\n",
|
|
pc->debuginfo_file);
|
|
found++;
|
|
}
|
|
}
|
|
|
|
if (!found && namelist_debug) {
|
|
error(INFO,
|
|
"%s:\n use of -f option may suffice, or may fail miserably\n",
|
|
namelist_debug);
|
|
}
|
|
|
|
if (!found && !namelist_debug) {
|
|
no_debugging_data(INFO);
|
|
error(INFO, "%s: debuginfo file not found\n", contents);
|
|
error(FATAL,
|
|
"either install the appropriate kernel debuginfo package, or\n copy %s to this machine", contents);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
reset_bfd:
|
|
|
|
if ((st->bfd = bfd_openr(pc->debuginfo_file, NULL)) == NULL)
|
|
error(FATAL, "cannot open object file: %s\n",
|
|
pc->debuginfo_file);
|
|
|
|
if (!bfd_check_format_matches(st->bfd, bfd_object, &matching))
|
|
error(FATAL, "cannot determine object file format: %s\n",
|
|
pc->debuginfo_file);
|
|
|
|
FREEBUF(contents);
|
|
FREEBUF(dirname);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Based upon gdb's separate_debug_file_exists().
|
|
*/
|
|
static int
|
|
separate_debug_file_exists(const char *name, unsigned long crc, int *exists)
|
|
{
|
|
unsigned long file_crc = 0;
|
|
int fd;
|
|
char buffer[8*1024];
|
|
size_t count;
|
|
|
|
fd = open(name, O_RDONLY);
|
|
if (fd < 0) {
|
|
*exists = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
*exists = TRUE;
|
|
while ((count = read(fd, buffer, sizeof(buffer))) > 0)
|
|
#ifdef GDB_5_3
|
|
file_crc = calc_crc32(file_crc, buffer, count);
|
|
#else
|
|
#ifdef GDB_7_6
|
|
file_crc = bfd_calc_gnu_debuglink_crc32(file_crc,
|
|
(unsigned char *)buffer, count);
|
|
#else
|
|
file_crc = gnu_debuglink_crc32(file_crc,
|
|
(unsigned char *)buffer, count);
|
|
#endif
|
|
#endif
|
|
|
|
close (fd);
|
|
|
|
return crc == file_crc;
|
|
}
|
|
|
|
/*
|
|
* Callback for gdb to use a specified vmlinux.debug file.
|
|
*/
|
|
char *
|
|
check_specified_kernel_debug_file()
|
|
{
|
|
if (pc->flags & GDB_INIT)
|
|
return NULL;
|
|
|
|
return (pc->namelist_debug ? pc->namelist_debug : NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Common bailout/warning routine when running against non-debug kernels.
|
|
*
|
|
* INFO: used when this routine should return.
|
|
* FATAL: kills function if runtime, or kills program if during init.
|
|
* WARNING: called by gdb_session_init() only, in an attempt to at least
|
|
* get by with built-in debug data; if not possible the program
|
|
* is killed.
|
|
*/
|
|
void
|
|
no_debugging_data(int error_type)
|
|
{
|
|
switch (error_type)
|
|
{
|
|
case INFO:
|
|
error(INFO, "%s: no debugging data available\n", pc->namelist);
|
|
break;
|
|
|
|
case FATAL:
|
|
error(FATAL, "%s%s: no debugging data available\n",
|
|
pc->flags & RUNTIME ? "" : "\n", pc->namelist);
|
|
clean_exit(1);
|
|
|
|
case WARNING:
|
|
error(FATAL, "\n%s: no debugging data available\n",
|
|
pc->namelist);
|
|
clean_exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the address space formerly used as init-time text. While there
|
|
* get the boundaries of the kernel .rodata section so that it won't
|
|
* be confused with text.
|
|
*
|
|
* This is done indirectly by the call-back to section_header_info().
|
|
*/
|
|
void
|
|
get_text_init_space(void)
|
|
{
|
|
asection *section = NULL;
|
|
|
|
if (pc->flags & SYSMAP)
|
|
return;
|
|
|
|
if (machine_type("ARM"))
|
|
section = get_kernel_section(".init");
|
|
if (!section && !(section = get_kernel_section(".text.init")))
|
|
section = get_kernel_section(".init.text");
|
|
if (!section) {
|
|
error(WARNING, "cannot determine text init space\n");
|
|
return;
|
|
}
|
|
|
|
kt->stext_init = (ulong)bfd_get_section_vma(st->bfd, section);
|
|
kt->etext_init = kt->stext_init +
|
|
(ulong)bfd_section_size(st->bfd, section);
|
|
|
|
if (kt->relocate) {
|
|
kt->stext_init -= kt->relocate;
|
|
kt->etext_init -= kt->relocate;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Strip gcc-generated cloned text symbol name endings.
|
|
*/
|
|
static char *
|
|
strip_symbol_end(const char *name, char *buf)
|
|
{
|
|
char *p;
|
|
|
|
if (st->flags & NO_STRIP)
|
|
return (char *)name;
|
|
|
|
if ((p = strstr(name, ".isra."))) {
|
|
if (buf) {
|
|
strcpy(buf, name);
|
|
buf[p-name] = NULLCHAR;
|
|
return buf;
|
|
} else {
|
|
*p = NULLCHAR;
|
|
return (char *)name;
|
|
}
|
|
}
|
|
|
|
if ((p = strstr(name, ".part."))) {
|
|
if (buf) {
|
|
strcpy(buf, name);
|
|
buf[p-name] = NULLCHAR;
|
|
return buf;
|
|
} else {
|
|
*p = NULLCHAR;
|
|
return (char *)name;
|
|
}
|
|
}
|
|
|
|
return (char *)name;
|
|
}
|
|
|
|
/*
|
|
* Gather the relevant information from the dumpfile or live system
|
|
* and determine whether to derive the KASLR offset.
|
|
*
|
|
* Setting st->_stext_vmlinux to UNINITIALIZED will trigger the
|
|
* search for "_stext" from the vmlinux file during the initial
|
|
* symbol sort operation.
|
|
*
|
|
* Setting RELOC_AUTO will ensure that derive_kaslr_offset() is
|
|
* called after the sorting operation has captured the vmlinux
|
|
* file's "_stext" symbol value -- which it will compare to the
|
|
* relocated "_stext" value found in either a dumpfile's vmcoreinfo
|
|
* or in /proc/kallsyms on a live system.
|
|
*
|
|
* Setting KASLR_CHECK will trigger a search for "module_load_offset"
|
|
* during the initial symbol sort operation, and if found, will
|
|
* set (RELOC_AUTO|KASLR). On live systems, the search is done
|
|
* here by checking /proc/kallsyms.
|
|
*/
|
|
static void
|
|
kaslr_init(void)
|
|
{
|
|
char *string;
|
|
|
|
if ((!machine_type("X86_64") && !machine_type("ARM64") && !machine_type("X86")) ||
|
|
(kt->flags & RELOC_SET))
|
|
return;
|
|
|
|
/*
|
|
* --kaslr=auto
|
|
*/
|
|
if ((kt->flags2 & (RELOC_AUTO|KASLR)) == (RELOC_AUTO|KASLR))
|
|
st->_stext_vmlinux = UNINITIALIZED;
|
|
|
|
if (ACTIVE() && /* Linux 3.15 */
|
|
(symbol_value_from_proc_kallsyms("module_load_offset") != BADVAL)) {
|
|
kt->flags2 |= (RELOC_AUTO|KASLR);
|
|
st->_stext_vmlinux = UNINITIALIZED;
|
|
}
|
|
|
|
if (QEMU_MEM_DUMP_NO_VMCOREINFO()) {
|
|
if (KDUMP_DUMPFILE() && kdump_kaslr_check()) {
|
|
kt->flags2 |= KASLR_CHECK;
|
|
} else if (DISKDUMP_DUMPFILE() && diskdump_kaslr_check()) {
|
|
kt->flags2 |= KASLR_CHECK;
|
|
}
|
|
} else if (KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()) {
|
|
if ((string = pc->read_vmcoreinfo("SYMBOL(_stext)"))) {
|
|
kt->vmcoreinfo._stext_SYMBOL =
|
|
htol(string, RETURN_ON_ERROR, NULL);
|
|
free(string);
|
|
}
|
|
|
|
/* Linux 3.14 */
|
|
if ((string = pc->read_vmcoreinfo("KERNELOFFSET"))) {
|
|
free(string);
|
|
kt->flags2 |= KASLR_CHECK;
|
|
st->_stext_vmlinux = UNINITIALIZED;
|
|
}
|
|
}
|
|
|
|
if (SADUMP_DUMPFILE() || VMSS_DUMPFILE())
|
|
kt->flags2 |= KASLR_CHECK;
|
|
}
|
|
|
|
/*
|
|
* Derives the kernel aslr offset by comparing the _stext symbol from the
|
|
* the vmcoreinfo in the dump file to the _stext symbol in the vmlinux file.
|
|
*/
|
|
static void
|
|
derive_kaslr_offset(bfd *abfd, int dynamic, bfd_byte *start, bfd_byte *end,
|
|
unsigned int size, asymbol *store)
|
|
{
|
|
unsigned long relocate;
|
|
ulong _stext_relocated;
|
|
|
|
if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) {
|
|
ulong kaslr_offset = 0;
|
|
ulong phys_base = 0;
|
|
|
|
calc_kaslr_offset(&kaslr_offset, &phys_base);
|
|
|
|
if (kaslr_offset) {
|
|
kt->relocate = kaslr_offset * -1;
|
|
kt->flags |= RELOC_SET;
|
|
}
|
|
|
|
if (phys_base) {
|
|
if (SADUMP_DUMPFILE())
|
|
sadump_set_phys_base(phys_base);
|
|
else if (KDUMP_DUMPFILE())
|
|
kdump_set_phys_base(phys_base);
|
|
else if (DISKDUMP_DUMPFILE())
|
|
diskdump_set_phys_base(phys_base);
|
|
else if (VMSS_DUMPFILE())
|
|
vmware_vmss_set_phys_base(phys_base);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (ACTIVE()) {
|
|
_stext_relocated = symbol_value_from_proc_kallsyms("_stext");
|
|
if (_stext_relocated == BADVAL)
|
|
return;
|
|
} else {
|
|
_stext_relocated = kt->vmcoreinfo._stext_SYMBOL;
|
|
if (_stext_relocated == 0)
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* To avoid mistaking an mismatched kernel version with
|
|
* a kaslr offset, we make sure that the offset is
|
|
* aligned by 0x1000, as it always will be for kaslr.
|
|
*/
|
|
if (st->_stext_vmlinux && (st->_stext_vmlinux != UNINITIALIZED)) {
|
|
relocate = st->_stext_vmlinux - _stext_relocated;
|
|
if (relocate && !(relocate & 0xfff)) {
|
|
kt->relocate = relocate;
|
|
kt->flags |= RELOC_SET;
|
|
}
|
|
}
|
|
|
|
if (CRASHDEBUG(1) && (kt->flags & RELOC_SET)) {
|
|
fprintf(fp, "KASLR:\n");
|
|
fprintf(fp, " _stext from %s: %lx\n",
|
|
basename(pc->namelist), st->_stext_vmlinux);
|
|
fprintf(fp, " _stext from %s: %lx\n",
|
|
ACTIVE() ? "/proc/kallsyms" : "vmcoreinfo",
|
|
_stext_relocated);
|
|
fprintf(fp, " relocate: %lx (%ldMB)\n",
|
|
kt->relocate * -1, (kt->relocate * -1) >> 20);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store the symbols gathered by symtab_init(). The symbols are stored
|
|
* in increasing numerical order.
|
|
*/
|
|
static void
|
|
store_symbols(bfd *abfd, int dynamic, void *minisyms, long symcount,
|
|
unsigned int size)
|
|
{
|
|
asymbol *store;
|
|
asymbol *sym;
|
|
bfd_byte *from, *fromend;
|
|
symbol_info syminfo;
|
|
struct syment *sp;
|
|
char buf[BUFSIZE];
|
|
char *name;
|
|
int first;
|
|
|
|
if ((store = bfd_make_empty_symbol(abfd)) == NULL)
|
|
error(FATAL, "bfd_make_empty_symbol() failed\n");
|
|
|
|
if ((st->symtable = (struct syment *)
|
|
calloc(symcount, sizeof(struct syment))) == NULL)
|
|
error(FATAL, "symbol table syment space malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if (!namespace_ctl(NAMESPACE_INIT, &st->kernel_namespace,
|
|
(void *)symcount, NULL))
|
|
error(FATAL, "symbol table namespace malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
st->syment_size = symcount * sizeof(struct syment);
|
|
|
|
st->symcnt = 0;
|
|
sp = st->symtable;
|
|
|
|
first = 0;
|
|
from = (bfd_byte *) minisyms;
|
|
fromend = from + symcount * size;
|
|
|
|
if (machine_type("X86")) {
|
|
if (kt->flags2 & KASLR) {
|
|
if ((kt->flags2 & RELOC_AUTO) && !(kt->flags & RELOC_SET))
|
|
derive_kaslr_offset(abfd, dynamic, from,
|
|
fromend, size, store);
|
|
} else if (!(kt->flags & RELOC_SET))
|
|
kt->flags |= RELOC_FORCE;
|
|
} else if (machine_type("X86_64") || machine_type("ARM64")) {
|
|
if ((kt->flags2 & RELOC_AUTO) && !(kt->flags & RELOC_SET))
|
|
derive_kaslr_offset(abfd, dynamic, from,
|
|
fromend, size, store);
|
|
} else
|
|
kt->flags &= ~RELOC_SET;
|
|
|
|
for (; from < fromend; from += size)
|
|
{
|
|
if ((sym = bfd_minisymbol_to_symbol(abfd, dynamic, from, store))
|
|
== NULL)
|
|
error(FATAL, "bfd_minisymbol_to_symbol() failed\n");
|
|
|
|
bfd_get_symbol_info(abfd, sym, &syminfo);
|
|
name = strip_symbol_end(syminfo.name, buf);
|
|
|
|
if (machdep->verify_symbol(name, syminfo.value,
|
|
syminfo.type)) {
|
|
if (kt->flags & (RELOC_SET|RELOC_FORCE))
|
|
sp->value = relocate(syminfo.value,
|
|
(char *)syminfo.name, !(first++));
|
|
else
|
|
sp->value = syminfo.value;
|
|
sp->type = syminfo.type;
|
|
namespace_ctl(NAMESPACE_INSTALL, &st->kernel_namespace,
|
|
sp, name);
|
|
sp++;
|
|
st->symcnt++;
|
|
}
|
|
}
|
|
|
|
st->symend = &st->symtable[st->symcnt];
|
|
|
|
st->flags |= KERNEL_SYMS;
|
|
|
|
namespace_ctl(NAMESPACE_COMPLETE, &st->kernel_namespace,
|
|
st->symtable, st->symend);
|
|
}
|
|
|
|
/*
|
|
* Store the symbols from the designated System.map. The symbols are stored
|
|
* in increasing numerical order.
|
|
*/
|
|
static void
|
|
store_sysmap_symbols(void)
|
|
{
|
|
int c, first;
|
|
long symcount;
|
|
char buf[BUFSIZE];
|
|
char name[BUFSIZE];
|
|
FILE *map;
|
|
char *mapitems[MAXARGS];
|
|
struct syment *sp, syment;
|
|
|
|
if ((map = fopen(pc->system_map, "r")) == NULL)
|
|
error(FATAL, "cannot open %s\n", pc->system_map);
|
|
|
|
symcount = 0;
|
|
while (fgets(buf, BUFSIZE, map))
|
|
symcount++;
|
|
|
|
if ((st->symtable = (struct syment *)
|
|
calloc(symcount, sizeof(struct syment))) == NULL)
|
|
error(FATAL, "symbol table syment space malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if (!namespace_ctl(NAMESPACE_INIT, &st->kernel_namespace,
|
|
(void *)symcount, NULL))
|
|
error(FATAL, "symbol table namespace malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if (!machine_type("X86") && !machine_type("X86_64") &&
|
|
!machine_type("ARM64"))
|
|
kt->flags &= ~RELOC_SET;
|
|
|
|
first = 0;
|
|
st->syment_size = symcount * sizeof(struct syment);
|
|
st->symcnt = 0;
|
|
sp = st->symtable;
|
|
|
|
rewind(map);
|
|
|
|
while (fgets(buf, BUFSIZE, map)) {
|
|
if ((c = parse_line(buf, mapitems)) != 3)
|
|
continue;
|
|
|
|
syment.value = htol(mapitems[0], FAULT_ON_ERROR, NULL);
|
|
syment.type = mapitems[1][0];
|
|
syment.name = mapitems[2];
|
|
|
|
strcpy(name, syment.name);
|
|
strip_symbol_end(name, NULL);
|
|
|
|
if (machdep->verify_symbol(name, syment.value,
|
|
syment.type)) {
|
|
if (kt->flags & RELOC_SET)
|
|
sp->value = relocate(syment.value,
|
|
syment.name, !(first++));
|
|
else
|
|
sp->value = syment.value;
|
|
sp->type = syment.type;
|
|
namespace_ctl(NAMESPACE_INSTALL, &st->kernel_namespace,
|
|
sp, name);
|
|
sp++;
|
|
st->symcnt++;
|
|
}
|
|
}
|
|
|
|
fclose(map);
|
|
|
|
st->symend = &st->symtable[st->symcnt];
|
|
|
|
st->flags |= KERNEL_SYMS;
|
|
|
|
namespace_ctl(NAMESPACE_COMPLETE, &st->kernel_namespace,
|
|
st->symtable, st->symend);
|
|
|
|
symname_hash_init();
|
|
symval_hash_init();
|
|
}
|
|
|
|
/*
|
|
* Handle x86/arm64 kernels configured such that the vmlinux symbols
|
|
* are not as loaded into the kernel (not unity-mapped).
|
|
*/
|
|
static ulong
|
|
relocate(ulong symval, char *symname, int first_symbol)
|
|
{
|
|
if (XEN_HYPER_MODE()) {
|
|
kt->flags &= ~(RELOC_SET|RELOC_FORCE);
|
|
return symval;
|
|
}
|
|
|
|
switch (kt->flags & (RELOC_SET|RELOC_FORCE))
|
|
{
|
|
case RELOC_SET:
|
|
break;
|
|
|
|
case RELOC_FORCE:
|
|
if (first_symbol && !relocate_force(symval, symname))
|
|
kt->flags &= ~RELOC_FORCE;
|
|
break;
|
|
}
|
|
|
|
if (machine_type("X86_64")) {
|
|
/*
|
|
* There are some symbols which are outside of any section
|
|
* either because they are offsets or because they are absolute
|
|
* addresses. These should not be relocated.
|
|
*/
|
|
if (symval >= st->first_section_start &&
|
|
symval <= st->last_section_end) {
|
|
return symval - kt->relocate;
|
|
} else {
|
|
return symval;
|
|
}
|
|
} else
|
|
return symval - kt->relocate;
|
|
}
|
|
|
|
/*
|
|
* If no --reloc argument was passed, try to figure it out
|
|
* by comparing the first vmlinux kernel symbol with the
|
|
* first /proc/kallsyms symbol. (should be "_text")
|
|
*
|
|
* Live system only (at least for now).
|
|
*/
|
|
static int
|
|
relocate_force(ulong symval, char *symname)
|
|
{
|
|
int count, found;
|
|
FILE *kp;
|
|
char buf[BUFSIZE];
|
|
char *kallsyms[MAXARGS];
|
|
ulong kallsym;
|
|
|
|
if (!ACTIVE() || !file_exists("/proc/kallsyms", NULL)) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp,
|
|
"cannot determine relocation value: %s\n",
|
|
!ACTIVE() ? "not a live system" :
|
|
"/proc/kallsyms does not exist");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((kp = fopen("/proc/kallsyms", "r")) == NULL) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp,
|
|
"cannot open /proc/kallsyms to determine relocation\n");
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp,
|
|
"relocate from: %s\n"
|
|
" %s @ %lx\n"
|
|
"relocate to: /proc/kallsyms\n",
|
|
pc->namelist, symname, symval);
|
|
|
|
found = FALSE;
|
|
count = kallsym = 0;
|
|
|
|
while (!found && fgets(buf, BUFSIZE, kp) &&
|
|
(parse_line(buf, kallsyms) == 3) &&
|
|
hexadecimal(kallsyms[0], 0)) {
|
|
|
|
if (STREQ(kallsyms[2], symname)) {
|
|
kallsym = htol(kallsyms[0], RETURN_ON_ERROR, NULL);
|
|
found = TRUE;
|
|
}
|
|
|
|
count++;
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp,
|
|
" %s @ %s %s\n",
|
|
kallsyms[2], kallsyms[0],
|
|
STREQ(kallsyms[2], symname) ?
|
|
"(match!)" : "");
|
|
}
|
|
fclose(kp);
|
|
|
|
/*
|
|
* If the symbols match and have different values,
|
|
* force the relocation.
|
|
*/
|
|
if (found) {
|
|
if (symval != kallsym) {
|
|
kt->relocate = symval - kallsym;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp,
|
|
"cannot determine relocation value from"
|
|
" %d symbols in /proc/kallsyms\n", count);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get a symbol value from /proc/kallsyms.
|
|
*/
|
|
static ulong
|
|
symbol_value_from_proc_kallsyms(char *symname)
|
|
{
|
|
FILE *kp;
|
|
char buf[BUFSIZE];
|
|
char *kallsyms[MAXARGS];
|
|
ulong kallsym;
|
|
int found;
|
|
|
|
if (!file_exists("/proc/kallsyms", NULL)) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "cannot determine value of %s: "
|
|
"/proc/kallsyms does not exist\n\n", symname);
|
|
return BADVAL;
|
|
}
|
|
|
|
if ((kp = fopen("/proc/kallsyms", "r")) == NULL) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "cannot determine value of %s: "
|
|
"cannot open /proc/kallsyms\n\n", symname);
|
|
return BADVAL;
|
|
}
|
|
|
|
found = FALSE;
|
|
while (!found && fgets(buf, BUFSIZE, kp) &&
|
|
(parse_line(buf, kallsyms) == 3)) {
|
|
if (hexadecimal(kallsyms[0], 0) &&
|
|
STREQ(kallsyms[2], symname)) {
|
|
kallsym = htol(kallsyms[0], RETURN_ON_ERROR, NULL);
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
fclose(kp);
|
|
|
|
return(found ? kallsym : BADVAL);
|
|
}
|
|
|
|
/*
|
|
* Install all static kernel symbol values into the symval_hash.
|
|
*/
|
|
static void
|
|
symval_hash_init(void)
|
|
{
|
|
int index;
|
|
struct syment *sp, *sph;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
index = SYMVAL_HASH_INDEX(sp->value);
|
|
|
|
if (st->symval_hash[index].val_hash_head == NULL) {
|
|
st->symval_hash[index].val_hash_head = sp;
|
|
st->symval_hash[index].val_hash_last = sp;
|
|
continue;
|
|
}
|
|
|
|
sph = st->symval_hash[index].val_hash_head;
|
|
while (sph->val_hash_next)
|
|
sph = sph->val_hash_next;
|
|
|
|
sph->val_hash_next = sp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Static kernel symbol value search
|
|
*/
|
|
static struct syment *
|
|
symval_hash_search(ulong value)
|
|
{
|
|
int index;
|
|
struct syment *sp, *splo;
|
|
|
|
index = SYMVAL_HASH_INDEX(value);
|
|
|
|
if (!st->symval_hash[index].val_hash_head)
|
|
return NULL;
|
|
|
|
st->val_hash_searches += 1;
|
|
st->val_hash_iterations += 1;
|
|
|
|
if (st->symval_hash[index].val_hash_last->value <= value)
|
|
sp = st->symval_hash[index].val_hash_last;
|
|
else
|
|
sp = st->symval_hash[index].val_hash_head;
|
|
|
|
for (splo = NULL; sp; sp = sp->val_hash_next) {
|
|
if (sp->value == value) {
|
|
st->symval_hash[index].val_hash_last = sp;
|
|
return sp;
|
|
}
|
|
|
|
if (sp->value > value)
|
|
break;
|
|
|
|
st->val_hash_iterations += 1;
|
|
|
|
splo = sp;
|
|
}
|
|
|
|
if (splo)
|
|
st->symval_hash[index].val_hash_last = splo;
|
|
|
|
return splo;
|
|
}
|
|
|
|
/*
|
|
* Store all kernel static symbols into the symname_hash.
|
|
*/
|
|
static void
|
|
symname_hash_init(void)
|
|
{
|
|
struct syment *sp;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++)
|
|
symname_hash_install(sp);
|
|
|
|
if ((sp = symbol_search("__per_cpu_start")))
|
|
st->__per_cpu_start = sp->value;
|
|
if ((sp = symbol_search("__per_cpu_end")))
|
|
st->__per_cpu_end = sp->value;
|
|
}
|
|
|
|
/*
|
|
* Install a single static kernel symbol into the symname_hash.
|
|
*/
|
|
static void
|
|
symname_hash_install(struct syment *spn)
|
|
{
|
|
struct syment *sp;
|
|
int index;
|
|
|
|
index = SYMNAME_HASH_INDEX(spn->name);
|
|
spn->cnt = 1;
|
|
|
|
if ((sp = st->symname_hash[index]) == NULL)
|
|
st->symname_hash[index] = spn;
|
|
else {
|
|
while (sp) {
|
|
if (STREQ(sp->name, spn->name)) {
|
|
sp->cnt++;
|
|
spn->cnt++;
|
|
}
|
|
if (sp->name_hash_next)
|
|
sp = sp->name_hash_next;
|
|
else {
|
|
sp->name_hash_next = spn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Static kernel symbol value search
|
|
*/
|
|
static struct syment *
|
|
symname_hash_search(char *name)
|
|
{
|
|
struct syment *sp;
|
|
|
|
sp = st->symname_hash[SYMNAME_HASH_INDEX(name)];
|
|
|
|
while (sp) {
|
|
if (STREQ(sp->name, name))
|
|
return sp;
|
|
sp = sp->name_hash_next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Output for sym -[lL] command.
|
|
*/
|
|
|
|
#define MODULE_PSEUDO_SYMBOL(sp) \
|
|
((STRNEQ((sp)->name, "_MODULE_START_") || STRNEQ((sp)->name, "_MODULE_END_")) || \
|
|
(STRNEQ((sp)->name, "_MODULE_INIT_START_") || STRNEQ((sp)->name, "_MODULE_INIT_END_")) || \
|
|
(STRNEQ((sp)->name, "_MODULE_SECTION_")))
|
|
|
|
#define MODULE_START(sp) (STRNEQ((sp)->name, "_MODULE_START_"))
|
|
#define MODULE_END(sp) (STRNEQ((sp)->name, "_MODULE_END_"))
|
|
#define MODULE_INIT_START(sp) (STRNEQ((sp)->name, "_MODULE_INIT_START_"))
|
|
#define MODULE_INIT_END(sp) (STRNEQ((sp)->name, "_MODULE_INIT_END_"))
|
|
#define MODULE_SECTION_START(sp) (STRNEQ((sp)->name, "_MODULE_SECTION_START"))
|
|
#define MODULE_SECTION_END(sp) (STRNEQ((sp)->name, "_MODULE_SECTION_END"))
|
|
|
|
static void
|
|
symbol_dump(ulong flags, char *module)
|
|
{
|
|
int i, start, percpu_syms;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
char *p1, *p2;;
|
|
|
|
#define TBD 1
|
|
#define DISPLAYED 2
|
|
|
|
if (flags & KERNEL_SYMS) {
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
show_symbol(sp, 0, SHOW_RADIX());
|
|
if (received_SIGINT() || output_closed())
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!(flags & MODULE_SYMS))
|
|
return;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
|
|
lm = &st->load_modules[i];
|
|
if (module && !STREQ(module, lm->mod_name))
|
|
continue;
|
|
|
|
if (received_SIGINT() || output_closed())
|
|
return;
|
|
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
percpu_syms = 0;
|
|
|
|
for (start = FALSE; sp <= sp_end; sp++) {
|
|
|
|
if (IN_MODULE_PERCPU(sp->value, lm)) {
|
|
if (percpu_syms == DISPLAYED)
|
|
continue;
|
|
if (!start) {
|
|
percpu_syms = TBD;
|
|
continue;
|
|
}
|
|
dump_percpu_symbols(lm);
|
|
percpu_syms = DISPLAYED;
|
|
}
|
|
|
|
if (MODULE_PSEUDO_SYMBOL(sp)) {
|
|
if (MODULE_SECTION_START(sp)) {
|
|
p1 = sp->name +
|
|
strlen("_MODULE_SECTION_START ");
|
|
p2 = "section start";
|
|
} else if (MODULE_SECTION_END(sp)) {
|
|
p1 = sp->name +
|
|
strlen("_MODULE_SECTION_END ");
|
|
p2 = "section end";
|
|
} else if (MODULE_START(sp)) {
|
|
p1 = "MODULE START";
|
|
p2 = sp->name+strlen("_MODULE_START_");
|
|
start = TRUE;
|
|
} else {
|
|
p1 = "MODULE END";
|
|
p2 = sp->name+strlen("_MODULE_END_");
|
|
if (MODULE_PERCPU_SYMS_LOADED(lm) &&
|
|
!percpu_syms) {
|
|
dump_percpu_symbols(lm);
|
|
percpu_syms = DISPLAYED;
|
|
}
|
|
}
|
|
fprintf(fp, "%lx %s: %s\n", sp->value, p1, p2);
|
|
|
|
if (percpu_syms == TBD) {
|
|
dump_percpu_symbols(lm);
|
|
percpu_syms = DISPLAYED;
|
|
}
|
|
} else
|
|
show_symbol(sp, 0, SHOW_RADIX());
|
|
}
|
|
|
|
if (lm->mod_init_symtable) {
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp <= sp_end; sp++) {
|
|
if (MODULE_PSEUDO_SYMBOL(sp)) {
|
|
if (MODULE_INIT_START(sp)) {
|
|
p1 = "MODULE INIT START";
|
|
p2 = sp->name+strlen("_MODULE_INIT_START_");
|
|
} else {
|
|
p1 = "MODULE INIT END";
|
|
p2 = sp->name+strlen("_MODULE_INIT_END_");
|
|
}
|
|
fprintf(fp, "%lx %s: %s\n", sp->value, p1, p2);
|
|
} else
|
|
show_symbol(sp, 0, SHOW_RADIX());
|
|
}
|
|
}
|
|
}
|
|
#undef TBD
|
|
#undef DISPLAYED
|
|
}
|
|
|
|
static void
|
|
dump_percpu_symbols(struct load_module *lm)
|
|
{
|
|
struct syment *sp, *sp_end;
|
|
|
|
if (MODULE_PERCPU_SYMS_LOADED(lm)) {
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
for ( ; sp <= sp_end; sp++) {
|
|
if (IN_MODULE_PERCPU(sp->value, lm))
|
|
show_symbol(sp, 0, SHOW_RADIX());
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get a pointer to the desired asection.
|
|
*/
|
|
static asection *
|
|
get_kernel_section(char *name)
|
|
{
|
|
int i;
|
|
asection **sec;
|
|
|
|
sec = (asection **)st->sections;
|
|
for (i = 0; i < st->bfd->section_count; i++, sec++) {
|
|
if (STREQ(name, (*sec)->name))
|
|
return(*sec);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Walk through the current set of symbols and check for duplicates.
|
|
*/
|
|
static void
|
|
check_for_dups(struct load_module *lm)
|
|
{
|
|
struct syment *sp, *sp_end;
|
|
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp <= sp_end; sp++) {
|
|
if (symbol_name_count(sp->name) > 1)
|
|
error(NOTE, "%s: duplicate symbol name: %s\n",
|
|
lm->mod_name, sp->name);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Store the externally declared symbols for all modules in the system.
|
|
* allowing for dynamic loading of symbols from individual mod object files
|
|
* during runtime.
|
|
*/
|
|
|
|
struct module_symbol {
|
|
unsigned long value;
|
|
const char *name;
|
|
};
|
|
|
|
void
|
|
store_module_symbols_v1(ulong total, int mods_installed)
|
|
{
|
|
int i, m;
|
|
ulong mod, mod_next, mod_name;
|
|
uint nsyms;
|
|
ulong syms, size_of_struct;
|
|
long strbuflen, size;
|
|
int mcnt, lm_mcnt;
|
|
struct module_symbol *modsym;
|
|
struct load_module *lm;
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE*2];
|
|
char name[BUFSIZE];
|
|
char rodata[BUFSIZE*2];
|
|
char *strbuf, *modbuf, *modsymbuf;
|
|
struct syment *sp;
|
|
ulong first, last;
|
|
|
|
st->mods_installed = mods_installed;
|
|
|
|
if (!st->mods_installed) {
|
|
st->flags &= ~MODULE_SYMS;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If we've been here before, free up everything and start over.
|
|
*/
|
|
if (st->flags & MODULE_SYMS) {
|
|
error(FATAL,
|
|
"re-initialization of module symbols not implemented yet!\n");
|
|
}
|
|
|
|
if ((st->ext_module_symtable = (struct syment *)
|
|
calloc(total, sizeof(struct syment))) == NULL)
|
|
error(FATAL, "module syment space malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if (!namespace_ctl(NAMESPACE_INIT, &st->ext_module_namespace,
|
|
(void *)total, NULL))
|
|
error(FATAL, "module namespace malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if ((st->load_modules = (struct load_module *)calloc
|
|
(st->mods_installed, sizeof(struct load_module))) == NULL)
|
|
error(FATAL, "load_module array malloc: %s\n", strerror(errno));
|
|
|
|
modbuf = GETBUF(SIZE(module));
|
|
modsymbuf = NULL;
|
|
m = mcnt = mod_next = 0;
|
|
|
|
for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
|
|
|
|
readmem(mod, KVADDR, modbuf, SIZE(module),
|
|
"module buffer", FAULT_ON_ERROR);
|
|
|
|
nsyms = UINT(modbuf + OFFSET(module_nsyms));
|
|
syms = ULONG(modbuf + OFFSET(module_syms));
|
|
size = LONG(modbuf + OFFSET(module_size));
|
|
mod_name = ULONG(modbuf + OFFSET(module_name));
|
|
size_of_struct = ULONG(modbuf +
|
|
OFFSET(module_size_of_struct));
|
|
|
|
if (!read_string(mod_name, name, BUFSIZE-1))
|
|
sprintf(name, "(unknown module)");
|
|
|
|
sprintf(rodata, "__insmod_%s_S.rodata", name);
|
|
|
|
lm = &st->load_modules[m++];
|
|
BZERO(lm, sizeof(struct load_module));
|
|
lm->mod_base = lm->module_struct = mod;
|
|
lm->mod_size = size;
|
|
lm->mod_size_of_struct = size_of_struct;
|
|
if (strlen(name) < MAX_MOD_NAME)
|
|
strcpy(lm->mod_name, name);
|
|
else {
|
|
error(INFO,
|
|
"module name greater than MAX_MOD_NAME: %s\n",
|
|
name);
|
|
BCOPY(name, lm->mod_name, MAX_MOD_NAME-1);
|
|
}
|
|
|
|
lm->mod_flags = MOD_EXT_SYMS;
|
|
lm->mod_ext_symcnt = mcnt;
|
|
lm->mod_etext_guess = 0;
|
|
|
|
st->ext_module_symtable[mcnt].value = mod;
|
|
st->ext_module_symtable[mcnt].type = 'm';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
sprintf(buf2, "%s%s", "_MODULE_START_", name);
|
|
namespace_ctl(NAMESPACE_INSTALL, &st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf2);
|
|
lm_mcnt = mcnt;
|
|
mcnt++;
|
|
|
|
if (nsyms) {
|
|
modsymbuf = GETBUF(sizeof(struct module_symbol)*nsyms);
|
|
readmem((ulong)syms, KVADDR, modsymbuf,
|
|
nsyms * sizeof(struct module_symbol),
|
|
"module symbols", FAULT_ON_ERROR);
|
|
}
|
|
|
|
for (i = first = last = 0; i < nsyms; i++) {
|
|
modsym = (struct module_symbol *)
|
|
(modsymbuf + (i * sizeof(struct module_symbol)));
|
|
if (!first
|
|
|| first > (ulong)modsym->name)
|
|
first = (ulong)modsym->name;
|
|
if ((ulong)modsym->name > last)
|
|
last = (ulong)modsym->name;
|
|
}
|
|
|
|
if (last > first) {
|
|
strbuflen = (last-first) + BUFSIZE;
|
|
if ((first + strbuflen) >=
|
|
(lm->mod_base + lm->mod_size)) {
|
|
strbuflen = (lm->mod_base + lm->mod_size) -
|
|
first;
|
|
|
|
}
|
|
strbuf = GETBUF(strbuflen);
|
|
|
|
if (!readmem(first, KVADDR, strbuf, strbuflen,
|
|
"module symbol strings", RETURN_ON_ERROR)) {
|
|
FREEBUF(strbuf);
|
|
strbuf = NULL;
|
|
}
|
|
} else
|
|
strbuf = NULL;
|
|
|
|
for (i = 0; i < nsyms; i++) {
|
|
|
|
modsym = (struct module_symbol *)
|
|
(modsymbuf + (i * sizeof(struct module_symbol)));
|
|
|
|
BZERO(buf1, BUFSIZE);
|
|
|
|
if (strbuf)
|
|
strcpy(buf1,
|
|
&strbuf[(ulong)modsym->name - first]);
|
|
else
|
|
read_string((ulong)modsym->name, buf1,
|
|
BUFSIZE-1);
|
|
|
|
if (strlen(buf1)) {
|
|
st->ext_module_symtable[mcnt].value =
|
|
modsym->value;
|
|
st->ext_module_symtable[mcnt].type = '?';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
strip_module_symbol_end(buf1);
|
|
strip_symbol_end(buf1, NULL);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf1);
|
|
|
|
if (strstr(buf1, rodata))
|
|
lm->mod_etext_guess = modsym->value;
|
|
|
|
sprintf(buf2, "__insmod_%s_O/", lm->mod_name);
|
|
if (strstr(buf1, buf2) &&
|
|
!strstr(buf1, "modules"))
|
|
lm->mod_flags |= MOD_INITRD;
|
|
mcnt++;
|
|
}
|
|
}
|
|
|
|
if (modsymbuf) {
|
|
FREEBUF(modsymbuf);
|
|
modsymbuf = NULL;
|
|
}
|
|
|
|
if (strbuf)
|
|
FREEBUF(strbuf);
|
|
|
|
/*
|
|
* If the module was compiled with kallsyms, add them in.
|
|
*/
|
|
switch (kt->flags & (KALLSYMS_V1|KALLSYMS_V2))
|
|
{
|
|
case KALLSYMS_V1:
|
|
mcnt += store_module_kallsyms_v1(lm, lm_mcnt,
|
|
mcnt, modbuf);
|
|
break;
|
|
case KALLSYMS_V2: /* impossible, I hope... */
|
|
mcnt += store_module_kallsyms_v2(lm, lm_mcnt,
|
|
mcnt, modbuf);
|
|
break;
|
|
}
|
|
|
|
st->ext_module_symtable[mcnt].value = mod + size;
|
|
st->ext_module_symtable[mcnt].type = 'm';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
sprintf(buf2, "%s%s", "_MODULE_END_", name);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf2);
|
|
mcnt++;
|
|
|
|
lm->mod_ext_symcnt = mcnt - lm->mod_ext_symcnt;
|
|
|
|
if (!lm->mod_etext_guess)
|
|
find_mod_etext(lm);
|
|
|
|
NEXT_MODULE(mod_next, modbuf);
|
|
}
|
|
|
|
FREEBUF(modbuf);
|
|
|
|
st->ext_module_symcnt = mcnt;
|
|
st->ext_module_symend = &st->ext_module_symtable[mcnt];
|
|
|
|
namespace_ctl(NAMESPACE_COMPLETE, &st->ext_module_namespace,
|
|
st->ext_module_symtable, st->ext_module_symend);
|
|
|
|
qsort(st->ext_module_symtable, mcnt, sizeof(struct syment),
|
|
compare_syms);
|
|
|
|
qsort(st->load_modules, m, sizeof(struct load_module), compare_mods);
|
|
|
|
for (m = 0; m < st->mods_installed; m++) {
|
|
lm = &st->load_modules[m];
|
|
sprintf(buf1, "_MODULE_START_%s", lm->mod_name);
|
|
sprintf(buf2, "_MODULE_END_%s", lm->mod_name);
|
|
|
|
for (sp = st->ext_module_symtable;
|
|
sp < st->ext_module_symend; sp++) {
|
|
if (STREQ(sp->name, buf1)) {
|
|
lm->mod_ext_symtable = sp;
|
|
lm->mod_symtable = sp;
|
|
}
|
|
if (STREQ(sp->name, buf2)) {
|
|
lm->mod_ext_symend = sp;
|
|
lm->mod_symend = sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
st->flags |= MODULE_SYMS;
|
|
|
|
if (symbol_query("__insmod_", NULL, NULL))
|
|
st->flags |= INSMOD_BUILTIN;
|
|
}
|
|
|
|
struct kernel_symbol
|
|
{
|
|
unsigned long value;
|
|
const char *name;
|
|
};
|
|
|
|
void
|
|
store_module_symbols_v2(ulong total, int mods_installed)
|
|
{
|
|
|
|
int i, m;
|
|
ulong mod, mod_next;
|
|
char *mod_name;
|
|
uint nsyms, ngplsyms;
|
|
ulong syms, gpl_syms;
|
|
ulong nksyms;
|
|
long strbuflen;
|
|
ulong size;
|
|
int mcnt, lm_mcnt;
|
|
struct kernel_symbol *modsym;
|
|
struct load_module *lm;
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE];
|
|
char *strbuf, *modbuf, *modsymbuf;
|
|
struct syment *sp;
|
|
ulong first, last;
|
|
|
|
st->mods_installed = mods_installed;
|
|
|
|
if (!st->mods_installed) {
|
|
st->flags &= ~MODULE_SYMS;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If we've been here before, free up everything and start over.
|
|
*/
|
|
if (st->flags & MODULE_SYMS) {
|
|
error(FATAL,
|
|
"re-initialization of module symbols not implemented yet!\n");
|
|
}
|
|
|
|
if ((st->ext_module_symtable = (struct syment *)
|
|
calloc(total, sizeof(struct syment))) == NULL)
|
|
error(FATAL, "v2 module syment space malloc (%ld symbols): %s\n",
|
|
total, strerror(errno));
|
|
|
|
if (!namespace_ctl(NAMESPACE_INIT, &st->ext_module_namespace,
|
|
(void *)total, NULL))
|
|
error(FATAL, "module namespace malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if ((st->load_modules = (struct load_module *)calloc
|
|
(st->mods_installed, sizeof(struct load_module))) == NULL)
|
|
error(FATAL, "load_module array malloc: %s\n", strerror(errno));
|
|
|
|
modbuf = GETBUF(SIZE(module));
|
|
modsymbuf = NULL;
|
|
m = mcnt = mod_next = 0;
|
|
|
|
for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
|
|
|
|
readmem(mod, KVADDR, modbuf, SIZE(module),
|
|
"module buffer", FAULT_ON_ERROR);
|
|
|
|
syms = ULONG(modbuf + OFFSET(module_syms));
|
|
gpl_syms = ULONG(modbuf + OFFSET(module_gpl_syms));
|
|
nsyms = UINT(modbuf + OFFSET(module_num_syms));
|
|
ngplsyms = UINT(modbuf + OFFSET(module_num_gpl_syms));
|
|
|
|
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
|
|
nksyms = UINT(modbuf + OFFSET(module_num_symtab));
|
|
size = UINT(modbuf + OFFSET(module_core_size));
|
|
} else {
|
|
nksyms = ULONG(modbuf + OFFSET(module_num_symtab));
|
|
size = ULONG(modbuf + OFFSET(module_core_size));
|
|
}
|
|
|
|
mod_name = modbuf + OFFSET(module_name);
|
|
|
|
lm = &st->load_modules[m++];
|
|
BZERO(lm, sizeof(struct load_module));
|
|
lm->mod_base = ULONG(modbuf + OFFSET(module_module_core));
|
|
lm->module_struct = mod;
|
|
lm->mod_size = size;
|
|
if (strlen(mod_name) < MAX_MOD_NAME)
|
|
strcpy(lm->mod_name, mod_name);
|
|
else {
|
|
error(INFO,
|
|
"module name greater than MAX_MOD_NAME: %s\n",
|
|
mod_name);
|
|
strncpy(lm->mod_name, mod_name, MAX_MOD_NAME-1);
|
|
}
|
|
if (CRASHDEBUG(3))
|
|
fprintf(fp,
|
|
"%lx (%lx): %s syms: %d gplsyms: %d ksyms: %ld\n",
|
|
mod, lm->mod_base, lm->mod_name, nsyms,
|
|
ngplsyms, nksyms);
|
|
lm->mod_flags = MOD_EXT_SYMS;
|
|
lm->mod_ext_symcnt = mcnt;
|
|
lm->mod_init_module_ptr = ULONG(modbuf +
|
|
OFFSET(module_module_init));
|
|
if (VALID_MEMBER(module_percpu))
|
|
lm->mod_percpu = ULONG(modbuf + OFFSET(module_percpu));
|
|
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
|
|
lm->mod_etext_guess = lm->mod_base +
|
|
UINT(modbuf + OFFSET(module_core_text_size));
|
|
lm->mod_init_size =
|
|
UINT(modbuf + OFFSET(module_init_size));
|
|
lm->mod_init_text_size =
|
|
UINT(modbuf + OFFSET(module_init_text_size));
|
|
} else {
|
|
lm->mod_etext_guess = lm->mod_base +
|
|
ULONG(modbuf + OFFSET(module_core_text_size));
|
|
lm->mod_init_size =
|
|
ULONG(modbuf + OFFSET(module_init_size));
|
|
lm->mod_init_text_size =
|
|
ULONG(modbuf + OFFSET(module_init_text_size));
|
|
}
|
|
lm->mod_text_start = lm->mod_base;
|
|
|
|
st->ext_module_symtable[mcnt].value = lm->mod_base;
|
|
st->ext_module_symtable[mcnt].type = 'm';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
sprintf(buf2, "%s%s", "_MODULE_START_", mod_name);
|
|
namespace_ctl(NAMESPACE_INSTALL, &st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf2);
|
|
lm_mcnt = mcnt;
|
|
mcnt++;
|
|
|
|
if (lm->mod_init_size > 0) {
|
|
st->ext_module_symtable[mcnt].value = lm->mod_init_module_ptr;
|
|
st->ext_module_symtable[mcnt].type = 'm';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
sprintf(buf3, "%s%s", "_MODULE_INIT_START_", mod_name);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf3);
|
|
lm_mcnt = mcnt;
|
|
mcnt++;
|
|
lm->mod_flags |= MOD_INIT;
|
|
}
|
|
|
|
|
|
if (nsyms && !IN_MODULE(syms, lm)) {
|
|
error(WARNING,
|
|
"[%s] module.syms outside of module "
|
|
"address space (%lx)\n\n",
|
|
lm->mod_name, syms);
|
|
nsyms = 0;
|
|
}
|
|
|
|
if (nsyms) {
|
|
modsymbuf = GETBUF(sizeof(struct kernel_symbol)*nsyms);
|
|
readmem((ulong)syms, KVADDR, modsymbuf,
|
|
nsyms * sizeof(struct kernel_symbol),
|
|
"module symbols", FAULT_ON_ERROR);
|
|
}
|
|
|
|
for (i = first = last = 0; i < nsyms; i++) {
|
|
modsym = (struct kernel_symbol *)
|
|
(modsymbuf + (i * sizeof(struct kernel_symbol)));
|
|
if (!first
|
|
|| first > (ulong)modsym->name)
|
|
first = (ulong)modsym->name;
|
|
if ((ulong)modsym->name > last)
|
|
last = (ulong)modsym->name;
|
|
}
|
|
|
|
if (last > first) {
|
|
strbuflen = (last-first) + BUFSIZE;
|
|
if ((first + strbuflen) >=
|
|
(lm->mod_base + lm->mod_size)) {
|
|
strbuflen = (lm->mod_base + lm->mod_size) -
|
|
first;
|
|
|
|
}
|
|
strbuf = GETBUF(strbuflen);
|
|
|
|
if (!readmem(first, KVADDR, strbuf, strbuflen,
|
|
"module symbol strings", RETURN_ON_ERROR)) {
|
|
FREEBUF(strbuf);
|
|
strbuf = NULL;
|
|
}
|
|
} else
|
|
strbuf = NULL;
|
|
|
|
|
|
for (i = 0; i < nsyms; i++) {
|
|
|
|
modsym = (struct kernel_symbol *)
|
|
(modsymbuf + (i * sizeof(struct kernel_symbol)));
|
|
|
|
BZERO(buf1, BUFSIZE);
|
|
|
|
if (strbuf)
|
|
strcpy(buf1,
|
|
&strbuf[(ulong)modsym->name - first]);
|
|
else
|
|
read_string((ulong)modsym->name, buf1,
|
|
BUFSIZE-1);
|
|
|
|
if (strlen(buf1)) {
|
|
st->ext_module_symtable[mcnt].value =
|
|
modsym->value;
|
|
st->ext_module_symtable[mcnt].type = '?';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
strip_module_symbol_end(buf1);
|
|
strip_symbol_end(buf1, NULL);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf1);
|
|
|
|
mcnt++;
|
|
}
|
|
}
|
|
|
|
if (modsymbuf) {
|
|
FREEBUF(modsymbuf);
|
|
modsymbuf = NULL;
|
|
}
|
|
|
|
if (strbuf)
|
|
FREEBUF(strbuf);
|
|
|
|
if (ngplsyms) {
|
|
modsymbuf = GETBUF(sizeof(struct kernel_symbol) *
|
|
ngplsyms);
|
|
readmem((ulong)gpl_syms, KVADDR, modsymbuf,
|
|
ngplsyms * sizeof(struct kernel_symbol),
|
|
"module gpl symbols", FAULT_ON_ERROR);
|
|
}
|
|
|
|
for (i = first = last = 0; i < ngplsyms; i++) {
|
|
modsym = (struct kernel_symbol *)
|
|
(modsymbuf + (i * sizeof(struct kernel_symbol)));
|
|
if (!first
|
|
|| first > (ulong)modsym->name)
|
|
first = (ulong)modsym->name;
|
|
if ((ulong)modsym->name > last)
|
|
last = (ulong)modsym->name;
|
|
}
|
|
|
|
if (last > first) {
|
|
strbuflen = (last-first) + BUFSIZE;
|
|
if ((first + strbuflen) >=
|
|
(lm->mod_base + lm->mod_size)) {
|
|
strbuflen = (lm->mod_base + lm->mod_size) -
|
|
first;
|
|
|
|
}
|
|
strbuf = GETBUF(strbuflen);
|
|
|
|
if (!readmem(first, KVADDR, strbuf, strbuflen,
|
|
"module gpl symbol strings", RETURN_ON_ERROR)) {
|
|
FREEBUF(strbuf);
|
|
strbuf = NULL;
|
|
}
|
|
} else
|
|
strbuf = NULL;
|
|
|
|
for (i = 0; i < ngplsyms; i++) {
|
|
|
|
modsym = (struct kernel_symbol *)
|
|
(modsymbuf + (i * sizeof(struct kernel_symbol)));
|
|
|
|
BZERO(buf1, BUFSIZE);
|
|
|
|
if (strbuf)
|
|
strcpy(buf1,
|
|
&strbuf[(ulong)modsym->name - first]);
|
|
else
|
|
read_string((ulong)modsym->name, buf1,
|
|
BUFSIZE-1);
|
|
|
|
if (strlen(buf1)) {
|
|
st->ext_module_symtable[mcnt].value =
|
|
modsym->value;
|
|
st->ext_module_symtable[mcnt].type = '?';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
strip_module_symbol_end(buf1);
|
|
strip_symbol_end(buf1, NULL);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf1);
|
|
|
|
mcnt++;
|
|
}
|
|
}
|
|
|
|
if (modsymbuf) {
|
|
FREEBUF(modsymbuf);
|
|
modsymbuf = NULL;
|
|
}
|
|
|
|
if (strbuf)
|
|
FREEBUF(strbuf);
|
|
|
|
/*
|
|
* If the module was compiled with kallsyms, add them in.
|
|
*/
|
|
switch (kt->flags & (KALLSYMS_V1|KALLSYMS_V2))
|
|
{
|
|
case KALLSYMS_V1: /* impossible, I hope... */
|
|
mcnt += store_module_kallsyms_v1(lm, lm_mcnt,
|
|
mcnt, modbuf);
|
|
break;
|
|
case KALLSYMS_V2:
|
|
mcnt += store_module_kallsyms_v2(lm, lm_mcnt,
|
|
mcnt, modbuf);
|
|
break;
|
|
}
|
|
|
|
st->ext_module_symtable[mcnt].value = lm->mod_base + size;
|
|
st->ext_module_symtable[mcnt].type = 'm';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
sprintf(buf2, "%s%s", "_MODULE_END_", mod_name);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf2);
|
|
mcnt++;
|
|
|
|
if (lm->mod_init_size > 0) {
|
|
st->ext_module_symtable[mcnt].value = lm->mod_init_module_ptr + lm->mod_init_size;
|
|
st->ext_module_symtable[mcnt].type = 'm';
|
|
st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
|
sprintf(buf4, "%s%s", "_MODULE_INIT_END_", mod_name);
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt], buf4);
|
|
mcnt++;
|
|
}
|
|
|
|
lm->mod_ext_symcnt = mcnt - lm->mod_ext_symcnt;
|
|
|
|
if (!lm->mod_etext_guess)
|
|
find_mod_etext(lm);
|
|
|
|
NEXT_MODULE(mod_next, modbuf);
|
|
}
|
|
|
|
FREEBUF(modbuf);
|
|
|
|
st->ext_module_symcnt = mcnt;
|
|
st->ext_module_symend = &st->ext_module_symtable[mcnt];
|
|
|
|
namespace_ctl(NAMESPACE_COMPLETE, &st->ext_module_namespace,
|
|
st->ext_module_symtable, st->ext_module_symend);
|
|
|
|
qsort(st->ext_module_symtable, mcnt, sizeof(struct syment),
|
|
compare_syms);
|
|
|
|
qsort(st->load_modules, m, sizeof(struct load_module), compare_mods);
|
|
|
|
for (m = 0; m < st->mods_installed; m++) {
|
|
lm = &st->load_modules[m];
|
|
sprintf(buf1, "_MODULE_START_%s", lm->mod_name);
|
|
sprintf(buf2, "_MODULE_END_%s", lm->mod_name);
|
|
sprintf(buf3, "_MODULE_INIT_START_%s", lm->mod_name);
|
|
sprintf(buf4, "_MODULE_INIT_END_%s", lm->mod_name);
|
|
|
|
for (sp = st->ext_module_symtable;
|
|
sp < st->ext_module_symend; sp++) {
|
|
if (STREQ(sp->name, buf1)) {
|
|
lm->mod_ext_symtable = sp;
|
|
lm->mod_symtable = sp;
|
|
}
|
|
if (STREQ(sp->name, buf2)) {
|
|
lm->mod_ext_symend = sp;
|
|
lm->mod_symend = sp;
|
|
}
|
|
if (STREQ(sp->name, buf3)) {
|
|
lm->mod_init_symtable = sp;
|
|
}
|
|
if (STREQ(sp->name, buf4)) {
|
|
lm->mod_init_symend = sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
st->flags |= MODULE_SYMS;
|
|
|
|
if (symbol_query("__insmod_", NULL, NULL))
|
|
st->flags |= INSMOD_BUILTIN;
|
|
|
|
if (mcnt > total)
|
|
error(FATAL, "store_module_symbols_v2: total: %ld mcnt: %d\n",
|
|
total, mcnt);
|
|
}
|
|
|
|
/*
|
|
* Get the module's kallsyms list if it was compiled in.
|
|
*/
|
|
static int
|
|
store_module_kallsyms_v1(struct load_module *lm, int start, int curr,
|
|
char *modbuf)
|
|
{
|
|
int i, j;
|
|
struct syment *sp;
|
|
ulong kallsyms_header;
|
|
char *module_buf;
|
|
char *header_buf;
|
|
uint symbols;
|
|
ulong name_off;
|
|
ulong sec_name_off;
|
|
ulong section_off;
|
|
ulong symptr;
|
|
ulong symbol_addr;
|
|
ulong stringptr;
|
|
ulong sectionptr;
|
|
char *nameptr;
|
|
char *secnameptr;
|
|
ulong secptr;
|
|
char type;
|
|
int mcnt;
|
|
int mcnt_idx;
|
|
int found;
|
|
struct symbol_namespace *ns;
|
|
|
|
if (!(kt->flags & KALLSYMS_V1))
|
|
return 0;
|
|
|
|
kallsyms_header = ULONG(modbuf + OFFSET(module_kallsyms_start));
|
|
if (!kallsyms_header)
|
|
return 0;
|
|
|
|
mcnt = 0;
|
|
mcnt_idx = curr;
|
|
module_buf = GETBUF(ULONG(modbuf + OFFSET(module_size)));
|
|
ns = &st->ext_module_namespace;
|
|
|
|
if (!readmem(lm->mod_base, KVADDR, module_buf, lm->mod_size,
|
|
"module (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
|
error(WARNING,"cannot access module kallsyms\n");
|
|
FREEBUF(module_buf);
|
|
return 0;
|
|
}
|
|
|
|
#define IN_MODULE_BUF_V1(x) \
|
|
(((x) >= module_buf) && ((x) < (module_buf + lm->mod_size)))
|
|
|
|
header_buf = module_buf + (kallsyms_header - lm->mod_base);
|
|
symbols = UINT(header_buf + OFFSET(kallsyms_header_symbols));
|
|
// sections = UINT(header_buf + OFFSET(kallsyms_header_sections));
|
|
|
|
if (CRASHDEBUG(7))
|
|
fprintf(fp, "kallsyms: module: %s\n", lm->mod_name);
|
|
|
|
symptr = (ulong)(header_buf +
|
|
ULONG(header_buf + OFFSET(kallsyms_header_symbol_off)));
|
|
stringptr = (ulong)(header_buf +
|
|
ULONG(header_buf + OFFSET(kallsyms_header_string_off)));
|
|
sectionptr = (ulong)(header_buf +
|
|
ULONG(header_buf + OFFSET(kallsyms_header_section_off)));
|
|
|
|
for (i = 0; i < symbols; i++, symptr += SIZE(kallsyms_symbol)) {
|
|
symbol_addr = ULONG(symptr+OFFSET(kallsyms_symbol_symbol_addr));
|
|
name_off = ULONG(symptr+OFFSET(kallsyms_symbol_name_off));
|
|
section_off = ULONG(symptr+OFFSET(kallsyms_symbol_section_off));
|
|
nameptr = (char *)(stringptr + name_off);
|
|
secptr = (ulong)(sectionptr + section_off);
|
|
sec_name_off = ULONG(secptr+OFFSET(kallsyms_section_name_off));
|
|
secnameptr = (char *)(stringptr + sec_name_off);
|
|
|
|
if (!IN_MODULE_BUF_V1(nameptr)) {
|
|
if (CRASHDEBUG(7))
|
|
error(INFO,
|
|
"%s: invalid nameptr: %lx (stringptr: %lx + name_off: %lx)\n",
|
|
lm->mod_name, nameptr,
|
|
stringptr, name_off);
|
|
continue;
|
|
}
|
|
if (!IN_MODULE_BUF_V1(secnameptr)) {
|
|
if (CRASHDEBUG(7))
|
|
error(INFO,
|
|
"%s: invalid secnameptr: %lx (stringptr: %lx + sec_name_off: %lx)\n",
|
|
lm->mod_name, secnameptr,
|
|
stringptr, sec_name_off);
|
|
continue;
|
|
}
|
|
|
|
if (!STREQ(nameptr, secnameptr)) {
|
|
if (STREQ(secnameptr, ".text"))
|
|
type = 't';
|
|
else if (STREQ(secnameptr, ".data"))
|
|
type = 'd';
|
|
else if (STREQ(secnameptr, ".bss"))
|
|
type = 'b';
|
|
else if (STREQ(secnameptr, ".rodata"))
|
|
type = 'd';
|
|
else
|
|
continue;
|
|
|
|
strip_module_symbol_end(nameptr);
|
|
strip_symbol_end(nameptr, NULL);
|
|
|
|
if (CRASHDEBUG(7))
|
|
fprintf(fp," symbol: %lx \"%s\" section: %s\n",
|
|
symbol_addr, nameptr, secnameptr);
|
|
|
|
for (found = 0, j = start; j < curr; j++) {
|
|
sp = &st->ext_module_symtable[j];
|
|
if ((sp->value == symbol_addr) &&
|
|
STREQ(nameptr,
|
|
&ns->address[(ulong)sp->name])) {
|
|
if (CRASHDEBUG(7))
|
|
fprintf(fp,
|
|
"current symbol \"%s\" at %lx of type (%c)\n",
|
|
&ns->address[(ulong)sp->name],
|
|
sp->value, sp->type);
|
|
if (sp->type == '?')
|
|
sp->type = type;
|
|
found++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
continue;
|
|
|
|
st->ext_module_symtable[mcnt_idx].value = symbol_addr;
|
|
st->ext_module_symtable[mcnt_idx].type = type;
|
|
st->ext_module_symtable[mcnt_idx].flags |= MODULE_SYMBOL;
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt_idx++], nameptr);
|
|
mcnt++;
|
|
}
|
|
}
|
|
|
|
lm->mod_flags |= MOD_KALLSYMS;
|
|
|
|
FREEBUF(module_buf);
|
|
return mcnt;
|
|
}
|
|
|
|
/*
|
|
* Translate either an Elf32_Sym or Elf64_Sym to an elf_common structure
|
|
* for more convenient use by store_module_kallsyms_v2().
|
|
*/
|
|
|
|
struct elf_common {
|
|
ulong st_name;
|
|
ulong st_value;
|
|
ulong st_shndx;
|
|
unsigned char st_info;
|
|
ulong st_size;
|
|
};
|
|
|
|
static void
|
|
Elf32_Sym_to_common(Elf32_Sym *e32, struct elf_common *ec)
|
|
{
|
|
ec->st_name = (ulong)e32->st_name;
|
|
ec->st_value = (ulong)e32->st_value;
|
|
ec->st_shndx = (ulong)e32->st_shndx;
|
|
ec->st_info = e32->st_info;
|
|
ec->st_size = (ulong)e32->st_size;
|
|
}
|
|
|
|
static void
|
|
Elf64_Sym_to_common(Elf64_Sym *e64, struct elf_common *ec)
|
|
{
|
|
ec->st_name = (ulong)e64->st_name;
|
|
ec->st_value = (ulong)e64->st_value;
|
|
ec->st_shndx = (ulong)e64->st_shndx;
|
|
ec->st_info = e64->st_info;
|
|
ec->st_size = (ulong)e64->st_size;
|
|
}
|
|
|
|
static int
|
|
store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
|
char *modbuf)
|
|
{
|
|
int i, j, found;
|
|
struct elf_common elf_common, *ec;
|
|
ulong nksyms, ksymtab, kstrtab;
|
|
char *module_buf, *ptr, *locsymtab, *locstrtab, *nameptr;
|
|
struct syment *sp;
|
|
struct symbol_namespace *ns;
|
|
int mcnt;
|
|
int mcnt_idx;
|
|
char *module_buf_init = NULL;
|
|
|
|
if (!(kt->flags & KALLSYMS_V2))
|
|
return 0;
|
|
|
|
mcnt = 0;
|
|
BZERO(&elf_common, sizeof(struct elf_common));
|
|
mcnt_idx = curr;
|
|
ns = &st->ext_module_namespace;
|
|
ec = &elf_common;
|
|
|
|
module_buf = GETBUF(lm->mod_size);
|
|
|
|
if (!readmem(lm->mod_base, KVADDR, module_buf, lm->mod_size,
|
|
"module (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
|
error(WARNING,"cannot access module kallsyms\n");
|
|
FREEBUF(module_buf);
|
|
return 0;
|
|
}
|
|
|
|
if (lm->mod_init_size > 0) {
|
|
module_buf_init = GETBUF(lm->mod_init_size);
|
|
|
|
if (!readmem(lm->mod_init_module_ptr, KVADDR, module_buf_init, lm->mod_init_size,
|
|
"module init (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
|
error(WARNING,"cannot access module init kallsyms\n");
|
|
FREEBUF(module_buf_init);
|
|
}
|
|
}
|
|
|
|
if (THIS_KERNEL_VERSION >= LINUX(2,6,27))
|
|
nksyms = UINT(modbuf + OFFSET(module_num_symtab));
|
|
else
|
|
nksyms = ULONG(modbuf + OFFSET(module_num_symtab));
|
|
|
|
ksymtab = ULONG(modbuf + OFFSET(module_symtab));
|
|
if (!IN_MODULE(ksymtab, lm) && !IN_MODULE_INIT(ksymtab, lm)) {
|
|
error(WARNING,
|
|
"%s: module.symtab outside of module address space\n",
|
|
lm->mod_name);
|
|
FREEBUF(module_buf);
|
|
if (module_buf_init)
|
|
FREEBUF(module_buf_init);
|
|
return 0;
|
|
}
|
|
if (IN_MODULE(ksymtab, lm))
|
|
locsymtab = module_buf + (ksymtab - lm->mod_base);
|
|
else
|
|
locsymtab = module_buf_init + (ksymtab - lm->mod_init_module_ptr);
|
|
|
|
kstrtab = ULONG(modbuf + OFFSET(module_strtab));
|
|
if (!IN_MODULE(kstrtab, lm) && !IN_MODULE_INIT(kstrtab, lm)) {
|
|
error(WARNING,
|
|
"%s: module.strtab outside of module address space\n",
|
|
lm->mod_name);
|
|
FREEBUF(module_buf);
|
|
if (module_buf_init)
|
|
FREEBUF(module_buf_init);
|
|
return 0;
|
|
}
|
|
if (IN_MODULE(kstrtab, lm))
|
|
locstrtab = module_buf + (kstrtab - lm->mod_base);
|
|
else
|
|
locstrtab = module_buf_init + (kstrtab - lm->mod_init_module_ptr);
|
|
|
|
for (i = 1; i < nksyms; i++) { /* ELF starts real symbols at 1 */
|
|
switch (BITS())
|
|
{
|
|
case 32:
|
|
ptr = locsymtab + (i * sizeof(Elf32_Sym));
|
|
Elf32_Sym_to_common((Elf32_Sym *)ptr, ec);
|
|
break;
|
|
case 64:
|
|
ptr = locsymtab + (i * sizeof(Elf64_Sym));
|
|
Elf64_Sym_to_common((Elf64_Sym *)ptr, ec);
|
|
break;
|
|
}
|
|
|
|
if (((ec->st_value < lm->mod_base) ||
|
|
(ec->st_value > (lm->mod_base + lm->mod_size))) &&
|
|
((ec->st_value < lm->mod_init_module_ptr) ||
|
|
(ec->st_value > (lm->mod_init_module_ptr + lm->mod_init_size))))
|
|
continue;
|
|
|
|
if (ec->st_shndx == SHN_UNDEF)
|
|
continue;
|
|
|
|
if (!IN_MODULE(kstrtab + ec->st_name, lm) && !IN_MODULE_INIT(kstrtab + ec->st_name, lm)) {
|
|
if (CRASHDEBUG(3)) {
|
|
error(WARNING,
|
|
"%s: bad st_name index: %lx -> %lx\n "
|
|
" st_value: %lx st_shndx: %ld st_info: %c\n",
|
|
lm->mod_name,
|
|
ec->st_name, (kstrtab + ec->st_name),
|
|
ec->st_value, ec->st_shndx,
|
|
ec->st_info);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
nameptr = locstrtab + ec->st_name;
|
|
if (*nameptr == '\0')
|
|
continue;
|
|
|
|
/*
|
|
* On ARM/ARM64 we have linker mapping symbols like '$a'
|
|
* or '$x' for ARM64, and '$d'.
|
|
* Make sure that these don't end up into our symbol list.
|
|
*/
|
|
if ((machine_type("ARM") || machine_type("ARM64")) &&
|
|
!machdep->verify_symbol(nameptr, ec->st_value, ec->st_info))
|
|
continue;
|
|
|
|
if (CRASHDEBUG(7))
|
|
fprintf(fp,
|
|
"%s: st_name: %ld st_value: %lx st_shndx: %ld st_info: %c\n",
|
|
nameptr, ec->st_name, ec->st_value,
|
|
ec->st_shndx, ec->st_info);
|
|
|
|
strip_symbol_end(nameptr, NULL);
|
|
|
|
for (found = 0, j = start; j < curr; j++) {
|
|
sp = &st->ext_module_symtable[j];
|
|
if ((sp->value == ec->st_value) &&
|
|
STREQ(nameptr, &ns->address[(ulong)sp->name])) {
|
|
if (CRASHDEBUG(7))
|
|
fprintf(fp,
|
|
"current symbol \"%s\" at %lx of type (%c)\n",
|
|
&ns->address[(ulong)sp->name],
|
|
sp->value, sp->type);
|
|
if (sp->type == '?')
|
|
sp->type = ec->st_info;
|
|
found++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
continue;
|
|
|
|
st->ext_module_symtable[mcnt_idx].value = ec->st_value;
|
|
st->ext_module_symtable[mcnt_idx].type = ec->st_info;
|
|
st->ext_module_symtable[mcnt_idx].flags |= MODULE_SYMBOL;
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&st->ext_module_namespace,
|
|
&st->ext_module_symtable[mcnt_idx++], nameptr);
|
|
mcnt++;
|
|
}
|
|
|
|
lm->mod_flags |= MOD_KALLSYMS;
|
|
FREEBUF(module_buf);
|
|
if (module_buf_init)
|
|
FREEBUF(module_buf_init);
|
|
|
|
return mcnt;
|
|
}
|
|
|
|
/*
|
|
* Strip the kernel clutter tagged on the end of an exported module symbol.
|
|
*/
|
|
static void
|
|
strip_module_symbol_end(char *buf)
|
|
{
|
|
char *p1, *lastR;
|
|
|
|
if (!(lastR = strrchr(buf, 'R')))
|
|
return;
|
|
|
|
if (((p1 = lastR-1) < buf) || (*p1 != '_'))
|
|
return;
|
|
|
|
if ((kt->flags & SMP) && STRNEQ(p1, "_Rsmp_")) {
|
|
*p1 = NULLCHAR;
|
|
return;
|
|
}
|
|
|
|
if (!hexadecimal(lastR+1, 0))
|
|
return;
|
|
|
|
*p1 = NULLCHAR;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the lowest or highest module virtual address.
|
|
*/
|
|
ulong
|
|
lowest_module_address(void)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
ulong low, lowest;
|
|
|
|
if (!st->mods_installed)
|
|
return 0;
|
|
|
|
lowest = (ulong)(-1);
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
low = lm->mod_base;
|
|
if (low < lowest)
|
|
lowest = low;
|
|
}
|
|
|
|
return lowest;
|
|
}
|
|
|
|
ulong
|
|
highest_module_address(void)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
ulong high, highest;
|
|
|
|
highest = 0;
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
high = lm->mod_base + lm->mod_size;
|
|
if (high > highest)
|
|
highest = high;
|
|
}
|
|
|
|
return highest;
|
|
}
|
|
|
|
|
|
/*
|
|
* Look through a string for bogus kernel clutter of an exported
|
|
* module symbol. In the case of LM_P_FILTER, shift the string left
|
|
* as appropriate to get rid of the extra stuff. In the case of
|
|
* LM_DIS_FILTER, translation of the previous address is done first,
|
|
* and its results are stuffed into the string. In both cases,
|
|
* this routine is recursive to catch multiple instances.
|
|
*/
|
|
|
|
#define SMP_CLUTTER (strlen("_Rsmp_"))
|
|
#define UP_CLUTTER (strlen("_R"))
|
|
#define CLUTTER_IDLEN (8)
|
|
|
|
char *
|
|
load_module_filter(char *s, int type)
|
|
{
|
|
char *arglist[MAXARGS];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
int clen, last;
|
|
int prev;
|
|
char *pstart, *p1, *p2, *smp, *pend, *colon;
|
|
ulong vaddr;
|
|
ulong offset;
|
|
struct syment *sp;
|
|
int argc;
|
|
|
|
switch (type)
|
|
{
|
|
case LM_P_FILTER:
|
|
if (!(pstart = strstr(s, "_R")))
|
|
return s;
|
|
|
|
smp = strstr(s, "_Rsmp_");
|
|
pend = &s[strlen(s)];
|
|
|
|
p2 = pstart + (smp ? SMP_CLUTTER : UP_CLUTTER);
|
|
|
|
if ((p2 >= pend) || !hexadecimal(p2, CLUTTER_IDLEN))
|
|
return s;
|
|
|
|
clen = smp ?
|
|
SMP_CLUTTER+CLUTTER_IDLEN : UP_CLUTTER+CLUTTER_IDLEN;
|
|
|
|
if (bracketed(s, pstart, clen)) { /* hack it out for now */
|
|
pstart--;
|
|
shift_string_left(pstart, clen+2);
|
|
if (*pstart == ',')
|
|
shift_string_left(pstart-1, 1);
|
|
} else
|
|
shift_string_left(pstart, clen);
|
|
|
|
return (load_module_filter(s, type)); /* catch multiples */
|
|
|
|
case LM_DIS_FILTER:
|
|
strip_beginning_whitespace(s);
|
|
strcpy(buf1, s);
|
|
argc = parse_line(buf1, arglist);
|
|
|
|
if (argc < 2)
|
|
return s;
|
|
|
|
/*
|
|
* Fix up the first half of the disassembly expression,
|
|
* that is, the address and symbol to the left of the
|
|
* colon.
|
|
*/
|
|
colon = NULL;
|
|
|
|
if (hexadecimal(arglist[0], VADDR_PRLEN+2) &&
|
|
bracketed(arglist[1], &arglist[1][1], 0) &&
|
|
(colon = strstr(s, ":"))) {
|
|
strcpy(buf2, colon+2);
|
|
|
|
vaddr = htol(arglist[0], FAULT_ON_ERROR, NULL);
|
|
if ((sp = value_search(vaddr, &offset))) {
|
|
if (offset)
|
|
sprintf(s, "%s <%s+%ld>:\t%s",
|
|
arglist[0], sp->name, offset, buf2);
|
|
else
|
|
sprintf(s, "%s <%s>:\t%s",
|
|
arglist[0], sp->name, buf2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now work on the second part -- if it exists.
|
|
* Find a virtual address followed by a bracked symbol
|
|
* at the end of the line.
|
|
*/
|
|
|
|
if (colon) {
|
|
strcpy(buf1, s);
|
|
argc = parse_line(buf1, arglist);
|
|
colon = strstr(s, ":");
|
|
}
|
|
|
|
last = argc-1;
|
|
prev = argc-2;
|
|
|
|
if (bracketed(arglist[last], &arglist[last][1], 0) &&
|
|
hexadecimal(arglist[prev], VADDR_PRLEN+2)) {
|
|
|
|
vaddr = htol(arglist[prev], FAULT_ON_ERROR, NULL);
|
|
p1 = strstr(s, arglist[last]);
|
|
|
|
if ((sp = value_search(vaddr, &offset)) &&
|
|
!(colon && (p1 < colon))) {
|
|
if (offset)
|
|
sprintf(p1, "<%s+%ld>\n",
|
|
sp->name, offset);
|
|
else
|
|
sprintf(p1, "<%s>\n", sp->name);
|
|
}
|
|
}
|
|
|
|
pend = &s[strlen(s)-3];
|
|
if (STREQ(pend, ":\t\n"))
|
|
LASTCHAR(s) = NULLCHAR;
|
|
|
|
return s;
|
|
|
|
default:
|
|
return NULL; /* can't get here */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle the various commands for controlling symbol string space:
|
|
*
|
|
* NAMESPACE_INIT: Allocates an estimated size for the string space.
|
|
* NAMESPACE_REUSE: Resets appropriate fields to allow a previously
|
|
* allocated module string buffer to be reused.
|
|
* NAMESPACE_FREE: Frees (module) string space.
|
|
* NAMESPACE_INSTALL: Copies a symbol name string into the next available
|
|
* buffer space. If the string cannot be squeezed in,
|
|
* the whole string space is reallocated, which may
|
|
* change its starting address. For that reason, the
|
|
* buffer index is temporarily stored in the sp->name
|
|
* field, which NAMESPACE_COMPLETE later transforms into
|
|
* the proper address when the buffer is set.
|
|
* NAMESPACE_COMPLETE: Reallocs a completed string buffer to the exact
|
|
* size that is required, and then calculates and stores
|
|
* the proper addresses into the name fields of the
|
|
* passed-in syment array.
|
|
*/
|
|
|
|
#define AVERAGE_SYMBOL_SIZE (16)
|
|
|
|
static int
|
|
namespace_ctl(int cmd, struct symbol_namespace *ns, void *nsarg1, void *nsarg2)
|
|
{
|
|
char *addr;
|
|
struct syment *sp, *sp_end;
|
|
char *name;
|
|
long cnt;
|
|
int len;
|
|
|
|
switch (cmd)
|
|
{
|
|
case NAMESPACE_INIT:
|
|
cnt = (long)nsarg1;
|
|
if ((addr = calloc(cnt, AVERAGE_SYMBOL_SIZE)) == NULL)
|
|
return FALSE;
|
|
ns->address = addr;
|
|
ns->index = 0;
|
|
ns->cnt = 0;
|
|
ns->size = cnt * AVERAGE_SYMBOL_SIZE;
|
|
return TRUE;
|
|
|
|
case NAMESPACE_REUSE:
|
|
ns->index = 0;
|
|
ns->cnt = 0;
|
|
return TRUE;
|
|
|
|
case NAMESPACE_FREE:
|
|
if (!ns->address)
|
|
error(FATAL,
|
|
"attempt to free unallocated module namespace\n");
|
|
free(ns->address);
|
|
ns->address = 0;
|
|
ns->index = 0;
|
|
ns->size = 0;
|
|
ns->cnt = 0;
|
|
return TRUE;
|
|
|
|
case NAMESPACE_INSTALL:
|
|
sp = (struct syment *)nsarg1;
|
|
name = (char *)nsarg2;
|
|
len = strlen(name)+1;
|
|
if ((ns->index + len) >= ns->size) {
|
|
if (!(addr = realloc(ns->address, ns->size*2)))
|
|
error(FATAL, "symbol name space malloc: %s\n",
|
|
strerror(errno));
|
|
ns->address = addr;
|
|
ns->size *= 2;
|
|
}
|
|
sp->name = (char *)ns->index;
|
|
BCOPY(name, &ns->address[ns->index], len);
|
|
ns->index += len;
|
|
ns->cnt++;
|
|
return TRUE;
|
|
|
|
case NAMESPACE_COMPLETE:
|
|
sp = (struct syment *)nsarg1;
|
|
sp_end = (struct syment *)nsarg2;
|
|
if (ns->index < (ns->size-1)) {
|
|
if ((addr = realloc(ns->address, ns->index+1))) {
|
|
ns->address = addr;
|
|
ns->size = ns->index+1;
|
|
}
|
|
}
|
|
for ( ; sp < sp_end; sp++)
|
|
sp->name = ns->address + (long)sp->name;
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE; /* can't get here */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* These comparison functions must return an integer less than,
|
|
* equal to, or greater than zero if the first argument is
|
|
* considered to be respectively less than, equal to, or
|
|
* greater than the second. If two members compare as equal,
|
|
* their order in the sorted array is undefined.
|
|
*/
|
|
|
|
static int
|
|
compare_syms(const void *v1, const void *v2)
|
|
{
|
|
struct syment *s1, *s2;
|
|
char sn1[BUFSIZE], sn2[BUFSIZE];
|
|
|
|
s1 = (struct syment *)v1;
|
|
s2 = (struct syment *)v2;
|
|
|
|
if (s1->value == s2->value) {
|
|
if (STRNEQ(s1->name, "__insmod"))
|
|
return -1;
|
|
if (STRNEQ(s2->name, "__insmod"))
|
|
return 1;
|
|
if (STRNEQ(s2->name, "_MODULE_START_"))
|
|
return 1;
|
|
/* Get pseudo section name. */
|
|
if (MODULE_SECTION_START(s1))
|
|
sscanf(s1->name, "_MODULE_SECTION_START [%s]", sn1);
|
|
else if (MODULE_SECTION_END(s1))
|
|
sscanf(s1->name, "_MODULE_SECTION_END [%s]", sn1);
|
|
|
|
if (MODULE_SECTION_START(s2))
|
|
sscanf(s2->name, "_MODULE_SECTION_START [%s]", sn2);
|
|
else if (MODULE_SECTION_END(s2))
|
|
sscanf(s2->name, "_MODULE_SECTION_END [%s]", sn2);
|
|
|
|
/*
|
|
* Sort pseudo symbols in mind section.
|
|
* The same values must be sorted like examples.
|
|
* - exp1
|
|
* c9046000 MODULE START: sctp
|
|
* c9046000 [.data]: section start
|
|
* c9046000 (D) sctp_timer_events
|
|
*
|
|
* - exp2
|
|
* c9046c68 [.bss]: section end
|
|
* c9046c68 MODULE END: sctp
|
|
*
|
|
* - exp3
|
|
* c90e9b44 [.text]: section end
|
|
* c90e9b44 [.exit.text]: section start
|
|
* c90e9b44 (T) cleanup_module
|
|
* c90e9b44 (t) sctp_exit
|
|
* c90e9c81 [.exit.text]: section end
|
|
*/
|
|
if (MODULE_SECTION_END(s1)) {
|
|
if (!MODULE_PSEUDO_SYMBOL(s2) || MODULE_END(s2))
|
|
return -1;
|
|
else if (MODULE_SECTION_START(s2) && !STREQ(sn1, sn2))
|
|
return -1;
|
|
return 1;
|
|
}
|
|
if (MODULE_SECTION_END(s2)) {
|
|
if (MODULE_END(s1) || !MODULE_PSEUDO_SYMBOL(s1))
|
|
return 1;
|
|
else if (MODULE_SECTION_START(s1) && STREQ(sn1, sn2))
|
|
return 1;
|
|
return -1;
|
|
}
|
|
if (MODULE_SECTION_START(s2)) {
|
|
if (MODULE_START(s1))
|
|
return -1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return (s1->value < s2->value ? -1 :
|
|
s1->value == s2->value ? 0 : 1);
|
|
}
|
|
|
|
static int
|
|
compare_mods(const void *v1, const void *v2)
|
|
{
|
|
struct load_module *lm1, *lm2;
|
|
|
|
lm1 = (struct load_module *)v1;
|
|
lm2 = (struct load_module *)v2;
|
|
|
|
return (lm1->mod_base < lm2->mod_base ? -1 :
|
|
lm1->mod_base == lm2->mod_base ? 0 : 1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether a value falls into a text-type (SEC_CODE) section.
|
|
* If it's a module address, and symbols are not loaded, we're forced
|
|
* to use our "mod_etext_guess" value.
|
|
*/
|
|
int
|
|
is_kernel_text(ulong value)
|
|
{
|
|
int i, s;
|
|
asection **sec, *section;
|
|
struct load_module *lm;
|
|
ulong start, end;
|
|
struct syment *sp;
|
|
|
|
start = 0;
|
|
|
|
if (pc->flags & SYSMAP) {
|
|
if ((sp = value_search(value, NULL)) && is_symbol_text(sp))
|
|
return TRUE;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if (!is_symbol_text(sp))
|
|
continue;
|
|
if ((value >= sp->value) && (value < kt->etext))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
} else {
|
|
sec = (asection **)st->sections;
|
|
for (i = 0; i < st->bfd->section_count; i++, sec++) {
|
|
section = *sec;
|
|
if (section->flags & SEC_CODE) {
|
|
start = (ulong)bfd_get_section_vma(st->bfd,
|
|
section);
|
|
end = start + (ulong)bfd_section_size(st->bfd,
|
|
section);
|
|
|
|
if ((value >= start) && (value < end))
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((sp = value_search(value, NULL)) && is_symbol_text(sp))
|
|
return TRUE;
|
|
|
|
if (NO_MODULES())
|
|
return FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
|
|
if (!IN_MODULE(value, lm) && !IN_MODULE_INIT(value, lm))
|
|
continue;
|
|
|
|
if (lm->mod_flags & MOD_LOAD_SYMS) {
|
|
for (s = (lm->mod_sections-1); s >= 0; s--) {
|
|
if (!(lm->mod_section_data[s].flags & SEC_CODE))
|
|
continue;
|
|
|
|
start = lm->mod_base +
|
|
lm->mod_section_data[s].offset;
|
|
end = start + lm->mod_section_data[s].size;
|
|
|
|
if ((value >= start) && (value < end))
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
switch (kt->flags & (KMOD_V1|KMOD_V2))
|
|
{
|
|
case KMOD_V1:
|
|
start = lm->mod_base + lm->mod_size_of_struct;
|
|
break;
|
|
case KMOD_V2:
|
|
if (IN_MODULE(value, lm))
|
|
start = lm->mod_base;
|
|
else
|
|
start = lm->mod_init_module_ptr;
|
|
break;
|
|
}
|
|
end = lm->mod_etext_guess;
|
|
if (IN_MODULE_INIT(value, lm) && end < lm->mod_init_module_ptr + lm->mod_init_size)
|
|
end = lm->mod_init_module_ptr + lm->mod_init_size;
|
|
|
|
if ((value >= start) && (value < end))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Detemine whether an address is offset into a text function, i.e., not
|
|
* the starting address of the function.
|
|
*/
|
|
int
|
|
is_kernel_text_offset(ulong value)
|
|
{
|
|
struct syment *sp;
|
|
ulong offset;
|
|
|
|
if (!is_kernel_text(value))
|
|
return FALSE;
|
|
|
|
if (!(sp = value_search(value, &offset)))
|
|
return FALSE;
|
|
|
|
return(offset ? TRUE : FALSE);
|
|
}
|
|
|
|
int
|
|
is_symbol_text(struct syment *sp)
|
|
{
|
|
return ((sp->type == 'T') || (sp->type == 't'));
|
|
}
|
|
|
|
/*
|
|
* Check whether an address is most likely kernel data.
|
|
*
|
|
* TBD: This should be refined to recognize module text/data.
|
|
*/
|
|
int
|
|
is_kernel_data(ulong value)
|
|
{
|
|
return(IS_KVADDR(value) &&
|
|
!is_kernel_text(value) && !IS_MODULE_VADDR(value));
|
|
}
|
|
|
|
/*
|
|
* Check whether the closest symbol to a value is rodata.
|
|
*/
|
|
int
|
|
is_rodata(ulong value, struct syment **spp)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if (!(sp = value_search(value, NULL)))
|
|
return FALSE;
|
|
|
|
if ((sp->type == 'r') || (sp->type == 'R')) {
|
|
if (spp)
|
|
*spp = sp;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* For a given kernel virtual address, request that gdb return
|
|
* the address range of the containing function. For module
|
|
* text addresses, its debuginfo data must be loaded.
|
|
*/
|
|
int
|
|
get_text_function_range(ulong vaddr, ulong *low, ulong *high)
|
|
{
|
|
struct syment *sp;
|
|
struct gnu_request gnu_request, *req = &gnu_request;
|
|
struct load_module *lm;
|
|
ulong size;
|
|
|
|
if (!(sp = value_search(vaddr, NULL)))
|
|
return FALSE;
|
|
|
|
if (module_symbol(vaddr, NULL, &lm, NULL, 0)) {
|
|
if (kallsyms_module_function_size(sp, lm, &size)) {
|
|
*low = sp->value;
|
|
*high = sp->value + size;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BZERO(req, sizeof(struct gnu_request));
|
|
req->command = GNU_GET_FUNCTION_RANGE;
|
|
req->pc = sp->value;
|
|
req->name = sp->name;
|
|
gdb_interface(req);
|
|
if (req->flags & GNU_COMMAND_FAILED)
|
|
return FALSE;
|
|
|
|
if ((vaddr < req->addr) || (vaddr >= req->addr2))
|
|
return FALSE;
|
|
|
|
*low = req->addr;
|
|
*high = req->addr2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Get the text size of a module function from kallsyms.
|
|
*/
|
|
static int
|
|
kallsyms_module_function_size(struct syment *sp, struct load_module *lm, ulong *size)
|
|
{
|
|
int i;
|
|
ulong nksyms, ksymtab, st_size;
|
|
char *ptr, *module_buf, *module_buf_init, *modbuf, *locsymtab;
|
|
struct elf_common elf_common, *ec;
|
|
|
|
if (!(lm->mod_flags & MOD_KALLSYMS) || !(kt->flags & KALLSYMS_V2))
|
|
return FALSE;
|
|
|
|
module_buf = GETBUF(lm->mod_size);
|
|
modbuf = module_buf + (lm->module_struct - lm->mod_base);
|
|
|
|
if (!readmem(lm->mod_base, KVADDR, module_buf, lm->mod_size,
|
|
"module (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
|
FREEBUF(module_buf);
|
|
return FALSE;
|
|
}
|
|
|
|
if (lm->mod_init_size > 0) {
|
|
module_buf_init = GETBUF(lm->mod_init_size);
|
|
if (!readmem(lm->mod_init_module_ptr, KVADDR, module_buf_init,
|
|
lm->mod_init_size, "module init (kallsyms)",
|
|
RETURN_ON_ERROR|QUIET)) {
|
|
FREEBUF(module_buf_init);
|
|
module_buf_init = NULL;
|
|
}
|
|
} else
|
|
module_buf_init = NULL;
|
|
|
|
if (THIS_KERNEL_VERSION >= LINUX(2,6,27))
|
|
nksyms = UINT(modbuf + OFFSET(module_num_symtab));
|
|
else
|
|
nksyms = ULONG(modbuf + OFFSET(module_num_symtab));
|
|
|
|
ksymtab = ULONG(modbuf + OFFSET(module_symtab));
|
|
if (!IN_MODULE(ksymtab, lm) && !IN_MODULE_INIT(ksymtab, lm)) {
|
|
FREEBUF(module_buf);
|
|
if (module_buf_init)
|
|
FREEBUF(module_buf_init);
|
|
return FALSE;
|
|
}
|
|
|
|
if (IN_MODULE(ksymtab, lm))
|
|
locsymtab = module_buf + (ksymtab - lm->mod_base);
|
|
else
|
|
locsymtab = module_buf_init + (ksymtab - lm->mod_init_module_ptr);
|
|
|
|
st_size = 0;
|
|
ec = &elf_common;
|
|
BZERO(&elf_common, sizeof(struct elf_common));
|
|
|
|
for (i = 1; i < nksyms; i++) { /* ELF starts real symbols at 1 */
|
|
switch (BITS())
|
|
{
|
|
case 32:
|
|
ptr = locsymtab + (i * sizeof(Elf32_Sym));
|
|
Elf32_Sym_to_common((Elf32_Sym *)ptr, ec);
|
|
break;
|
|
case 64:
|
|
ptr = locsymtab + (i * sizeof(Elf64_Sym));
|
|
Elf64_Sym_to_common((Elf64_Sym *)ptr, ec);
|
|
break;
|
|
}
|
|
|
|
if (sp->value == ec->st_value) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "kallsyms_module_function_size: "
|
|
"st_value: %lx st_size: %ld\n",
|
|
ec->st_value, ec->st_size);
|
|
st_size = ec->st_size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (module_buf_init)
|
|
FREEBUF(module_buf_init);
|
|
FREEBUF(module_buf);
|
|
|
|
if (st_size) {
|
|
*size = st_size;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* "help -s" output
|
|
*/
|
|
void
|
|
dump_symbol_table(void)
|
|
{
|
|
int i, s, cnt, tot;
|
|
struct load_module *lm;
|
|
struct syment *sp;
|
|
struct downsized *ds;
|
|
int others;
|
|
asection **sec;
|
|
|
|
fprintf(fp, " flags: %lx%s(", st->flags,
|
|
count_bits_long(st->flags) > 3 ? "\n " : " ");
|
|
others = 0;
|
|
if (st->flags & KERNEL_SYMS)
|
|
fprintf(fp, "%sKERNEL_SYMS", others++ ? "|" : "");
|
|
if (st->flags & MODULE_SYMS)
|
|
fprintf(fp, "%sMODULE_SYMS", others++ ? "|" : "");
|
|
if (st->flags & LOAD_MODULE_SYMS)
|
|
fprintf(fp, "%sLOAD_MODULE_SYMS",
|
|
others++ ? "|" : "");
|
|
if (st->flags & INSMOD_BUILTIN)
|
|
fprintf(fp, "%sINSMOD_BUILTIN", others++ ? "|" : "");
|
|
if (st->flags & GDB_SYMS_PATCHED)
|
|
fprintf(fp, "%sGDB_SYMS_PATCHED", others++ ? "|" : "");
|
|
if (st->flags & NO_SEC_LOAD)
|
|
fprintf(fp, "%sNO_SEC_LOAD", others++ ? "|" : "");
|
|
if (st->flags & NO_SEC_CONTENTS)
|
|
fprintf(fp, "%sNO_SEC_CONTENTS", others++ ? "|" : "");
|
|
if (st->flags & FORCE_DEBUGINFO)
|
|
fprintf(fp, "%sFORCE_DEBUGINFO", others++ ? "|" : "");
|
|
if (st->flags & CRC_MATCHES)
|
|
fprintf(fp, "%sCRC_MATCHES", others++ ? "|" : "");
|
|
if (st->flags & ADD_SYMBOL_FILE)
|
|
fprintf(fp, "%sADD_SYMBOL_FILE", others++ ? "|" : "");
|
|
if (st->flags & USE_OLD_ADD_SYM)
|
|
fprintf(fp, "%sUSE_OLD_ADD_SYM", others++ ? "|" : "");
|
|
if (st->flags & PERCPU_SYMS)
|
|
fprintf(fp, "%sPERCPU_SYMS", others++ ? "|" : "");
|
|
if (st->flags & MODSECT_V1)
|
|
fprintf(fp, "%sMODSECT_V1", others++ ? "|" : "");
|
|
if (st->flags & MODSECT_V2)
|
|
fprintf(fp, "%sMODSECT_V2", others++ ? "|" : "");
|
|
if (st->flags & MODSECT_V3)
|
|
fprintf(fp, "%sMODSECT_V3", others++ ? "|" : "");
|
|
if (st->flags & MODSECT_UNKNOWN)
|
|
fprintf(fp, "%sMODSECT_UNKNOWN", others++ ? "|" : "");
|
|
if (st->flags & NO_STRIP)
|
|
fprintf(fp, "%sNO_STRIP", others++ ? "|" : "");
|
|
fprintf(fp, ")\n");
|
|
|
|
fprintf(fp, " bfd: %lx\n", (ulong)st->bfd);
|
|
fprintf(fp, " symtable: %lx\n", (ulong)st->symtable);
|
|
fprintf(fp, " symend: %lx\n", (ulong)st->symend);
|
|
fprintf(fp, " symcnt: %ld\n", st->symcnt);
|
|
fprintf(fp, " syment_size: %ld\n", st->syment_size);
|
|
fprintf(fp, " first_ksymbol: ");
|
|
if (st->first_ksymbol) {
|
|
fprintf(fp, "%lx (%s)\n",
|
|
st->first_ksymbol,
|
|
st->flags & KERNEL_SYMS ?
|
|
value_symbol(st->first_ksymbol) : "");
|
|
} else
|
|
fprintf(fp, "(unused)\n");
|
|
if (st->__per_cpu_start || st->__per_cpu_end) {
|
|
fprintf(fp, " __per_cpu_start: %lx\n", st->__per_cpu_start);
|
|
fprintf(fp, " __per_cpu_end: %lx\n", st->__per_cpu_end);
|
|
} else {
|
|
fprintf(fp, " __per_cpu_start: (unused)\n");
|
|
fprintf(fp, " __per_cpu_end: (unused)\n");
|
|
}
|
|
|
|
fprintf(fp, " first_section_start: %lx\n", st->first_section_start);
|
|
fprintf(fp, " last_section_end: %lx\n", st->last_section_end);
|
|
|
|
fprintf(fp, " _stext_vmlinux: %lx ", st->_stext_vmlinux);
|
|
if (st->_stext_vmlinux == UNINITIALIZED)
|
|
fprintf(fp, "(UNINITIALIZED)\n");
|
|
else if (st->_stext_vmlinux == 0)
|
|
fprintf(fp, "(unused)\n");
|
|
else
|
|
fprintf(fp, "\n");
|
|
|
|
if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) {
|
|
fprintf(fp, "divide_error_vmlinux: %lx\n", st->divide_error_vmlinux);
|
|
fprintf(fp, " idt_table_vmlinux: %lx\n", st->idt_table_vmlinux);
|
|
fprintf(fp, "saved_command_line_vmlinux: %lx\n", st->saved_command_line_vmlinux);
|
|
fprintf(fp, " pti_init_vmlinux: %lx\n", st->pti_init_vmlinux);
|
|
fprintf(fp, " kaiser_init_vmlinux: %lx\n", st->kaiser_init_vmlinux);
|
|
} else {
|
|
fprintf(fp, "divide_error_vmlinux: (unused)\n");
|
|
fprintf(fp, " idt_table_vmlinux: (unused)\n");
|
|
fprintf(fp, "saved_command_line_vmlinux: (unused)\n");
|
|
fprintf(fp, " pti_init_vmlinux: (unused)\n");
|
|
fprintf(fp, " kaiser_init_vmlinux: (unused)\n");
|
|
}
|
|
|
|
fprintf(fp, " symval_hash[%d]: %lx\n", SYMVAL_HASH,
|
|
(ulong)&st->symval_hash[0]);
|
|
|
|
if (CRASHDEBUG(1)) {
|
|
fprintf(fp, " ");
|
|
for (i = 0; i < SYMVAL_HASH; i++) {
|
|
fprintf(fp, " [%3d]: ", i);
|
|
|
|
sp = st->symval_hash[i].val_hash_head;
|
|
if (!sp) {
|
|
fprintf(fp, " 0 ");
|
|
} else {
|
|
cnt = 1;
|
|
while ((sp = sp->val_hash_next))
|
|
cnt++;
|
|
|
|
fprintf(fp, "%3d ", cnt);
|
|
}
|
|
|
|
if (i && (((i+1)%6)== 0))
|
|
fprintf(fp, "\n ");
|
|
}
|
|
}
|
|
|
|
fprintf(fp, "%s val_hash_searches: %.0f\n",
|
|
CRASHDEBUG(1) ? "\n" : "", st->val_hash_searches);
|
|
fprintf(fp, " val_hash_iterations: %.0f (avg: %.1f)\n",
|
|
st->val_hash_iterations,
|
|
st->val_hash_iterations/st->val_hash_searches);
|
|
|
|
fprintf(fp, " symname_hash[%d]: %lx\n", SYMNAME_HASH,
|
|
(ulong)&st->symname_hash[0]);
|
|
|
|
if (CRASHDEBUG(1)) {
|
|
fprintf(fp, " ");
|
|
for (i = tot = 0; i < SYMNAME_HASH; i++) {
|
|
fprintf(fp, "[%3d]: ", i);
|
|
if ((sp = st->symname_hash[i]) == NULL)
|
|
fprintf(fp, "%3d ", 0);
|
|
else {
|
|
cnt = 1;
|
|
while (sp->name_hash_next) {
|
|
cnt++;
|
|
sp = sp->name_hash_next;
|
|
}
|
|
fprintf(fp, "%3d ", cnt);
|
|
tot += cnt;
|
|
}
|
|
if (i && (((i+1) % 6) == 0))
|
|
fprintf(fp, "\n ");
|
|
}
|
|
if (SYMNAME_HASH % 6)
|
|
fprintf(fp, "\n");
|
|
}
|
|
fprintf(fp, " symbol_namespace: ");
|
|
fprintf(fp, "address: %lx ", (ulong)st->kernel_namespace.address);
|
|
fprintf(fp, "index: %ld ", st->kernel_namespace.index);
|
|
fprintf(fp, "size: %ld ", (ulong)st->kernel_namespace.size);
|
|
fprintf(fp, "cnt: %ld\n", st->kernel_namespace.cnt);
|
|
fprintf(fp, " ext_module_symtable: %lx\n",
|
|
(ulong)st->ext_module_symtable);
|
|
fprintf(fp, " ext_module_symend: %lx\n",
|
|
(ulong)st->ext_module_symend);
|
|
fprintf(fp, " ext_module_symcnt: %ld\n",
|
|
(ulong)st->ext_module_symcnt);
|
|
fprintf(fp, "ext_module_namespace: ");
|
|
fprintf(fp, "address: %lx ",
|
|
(ulong)st->ext_module_namespace.address);
|
|
fprintf(fp, "index: %ld ",
|
|
st->ext_module_namespace.index);
|
|
fprintf(fp, "size: %ld ",
|
|
(ulong)st->ext_module_namespace.size);
|
|
fprintf(fp, "cnt: %ld\n",
|
|
st->ext_module_namespace.cnt);
|
|
|
|
fprintf(fp, " mods_installed: %d\n", st->mods_installed);
|
|
fprintf(fp, " current: %lx\n", (ulong)st->current);
|
|
fprintf(fp, " load_modules: %lx\n", (ulong)st->load_modules);
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
others = 0;
|
|
|
|
fprintf(fp, "\n mod_base: %lx\n", lm->mod_base);
|
|
fprintf(fp, " module_struct: %lx\n", lm->module_struct);
|
|
fprintf(fp, " mod_name: %s\n", lm->mod_name);
|
|
fprintf(fp, " mod_size: %ld\n", lm->mod_size);
|
|
fprintf(fp, " mod_namelist: %s\n", lm->mod_namelist);
|
|
fprintf(fp, " mod_flags: %lx (", lm->mod_flags);
|
|
if (lm->mod_flags & MOD_EXT_SYMS)
|
|
fprintf(fp, "%sMOD_EXT_SYMS", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_LOAD_SYMS)
|
|
fprintf(fp, "%sMOD_LOAD_SYMS", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_REMOTE)
|
|
fprintf(fp, "%sMOD_REMOTE", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_KALLSYMS)
|
|
fprintf(fp, "%sMOD_KALLSYMS", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_INITRD)
|
|
fprintf(fp, "%sMOD_INITRD", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_NOPATCH)
|
|
fprintf(fp, "%sMOD_NOPATCH", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_INIT)
|
|
fprintf(fp, "%sMOD_INIT", others++ ? "|" : "");
|
|
if (lm->mod_flags & MOD_DO_READNOW)
|
|
fprintf(fp, "%sMOD_DO_READNOW", others++ ? "|" : "");
|
|
fprintf(fp, ")\n");
|
|
|
|
fprintf(fp, " mod_symtable: %lx\n",
|
|
(ulong)lm->mod_symtable);
|
|
fprintf(fp, " mod_symend: %lx\n",
|
|
(ulong)lm->mod_symend);
|
|
fprintf(fp, " mod_init_symtable: %lx\n",
|
|
(ulong)lm->mod_init_symtable);
|
|
fprintf(fp, " mod_init_symend: %lx\n",
|
|
(ulong)lm->mod_init_symend);
|
|
|
|
fprintf(fp, " mod_ext_symcnt: %ld\n",
|
|
lm->mod_ext_symcnt);
|
|
fprintf(fp, " mod_ext_symtable: %lx\n",
|
|
(ulong)lm->mod_ext_symtable);
|
|
fprintf(fp, " mod_ext_symend: %lx\n",
|
|
(ulong)lm->mod_ext_symend);
|
|
|
|
fprintf(fp, " mod_load_symcnt: %ld\n",
|
|
lm->mod_load_symcnt);
|
|
fprintf(fp, " mod_load_symtable: %lx\n",
|
|
(ulong)lm->mod_load_symtable);
|
|
fprintf(fp, " mod_load_symend: %lx\n",
|
|
(ulong)lm->mod_load_symend);
|
|
|
|
fprintf(fp, " mod_load_namespace: ");
|
|
fprintf(fp, "address: %lx ",
|
|
(ulong)lm->mod_load_namespace.address);
|
|
fprintf(fp, "index: %ld ",
|
|
lm->mod_load_namespace.index);
|
|
fprintf(fp, "size: %ld ",
|
|
(ulong)lm->mod_load_namespace.size);
|
|
fprintf(fp, "cnt: %ld\n",
|
|
lm->mod_load_namespace.cnt);
|
|
|
|
fprintf(fp, " mod_symalloc: %ld\n", lm->mod_symalloc);
|
|
fprintf(fp, " mod_size_of_struct: %ld (%lx)\n",
|
|
lm->mod_size_of_struct, lm->mod_size_of_struct);
|
|
fprintf(fp, " mod_text_start: %lx (%lx)\n",
|
|
lm->mod_text_start,
|
|
lm->mod_text_start ?
|
|
lm->mod_text_start - lm->mod_base : 0);
|
|
fprintf(fp, " mod_etext_guess: %lx (%lx)\n",
|
|
lm->mod_etext_guess,
|
|
lm->mod_etext_guess ?
|
|
lm->mod_etext_guess - lm->mod_base : 0);
|
|
fprintf(fp, " mod_rodata_start: %lx (%lx)\n",
|
|
lm->mod_rodata_start,
|
|
lm->mod_rodata_start ?
|
|
lm->mod_rodata_start - lm->mod_base : 0);
|
|
fprintf(fp, " mod_data_start: %lx (%lx)\n",
|
|
lm->mod_data_start,
|
|
lm->mod_data_start ?
|
|
lm->mod_data_start - lm->mod_base : 0);
|
|
fprintf(fp, " mod_bss_start: %lx (%lx)\n",
|
|
lm->mod_bss_start,
|
|
lm->mod_bss_start ?
|
|
lm->mod_bss_start - lm->mod_base : 0);
|
|
fprintf(fp, " mod_init_size: %ld\n",
|
|
lm->mod_init_size);
|
|
fprintf(fp, " mod_init_text_size: %ld\n",
|
|
lm->mod_init_text_size);
|
|
fprintf(fp, " mod_init_module_ptr: %lx\n",
|
|
lm->mod_init_module_ptr);
|
|
if (lm->mod_percpu_size) {
|
|
fprintf(fp, " mod_percpu_size: %lx\n",
|
|
lm->mod_percpu_size);
|
|
fprintf(fp, " mod_percpu: %lx - %lx\n",
|
|
lm->mod_percpu,
|
|
lm->mod_percpu + lm->mod_percpu_size);
|
|
} else {
|
|
if (lm->mod_percpu) {
|
|
fprintf(fp,
|
|
" mod_percpu_size: (not loaded)\n");
|
|
fprintf(fp,
|
|
" mod_percpu: %lx - (unknown)\n",
|
|
lm->mod_percpu);
|
|
} else {
|
|
fprintf(fp,
|
|
" mod_percpu_size: (not used)\n");
|
|
fprintf(fp,
|
|
" mod_percpu: (not used)\n");
|
|
}
|
|
}
|
|
|
|
fprintf(fp, " mod_sections: %d\n", lm->mod_sections);
|
|
fprintf(fp, " mod_section_data: %lx %s\n",
|
|
(ulong)lm->mod_section_data,
|
|
lm->mod_section_data ? "" : "(not allocated)");
|
|
|
|
|
|
for (s = 0; s < lm->mod_sections; s++) {
|
|
fprintf(fp,
|
|
" %12s prio: %x flags: %05x offset: %-8lx size: %lx\n",
|
|
lm->mod_section_data[s].name,
|
|
lm->mod_section_data[s].priority,
|
|
lm->mod_section_data[s].flags,
|
|
lm->mod_section_data[s].offset,
|
|
lm->mod_section_data[s].size);
|
|
}
|
|
|
|
fprintf(fp, " loaded_objfile: %lx\n", (ulong)lm->loaded_objfile);
|
|
|
|
if (CRASHDEBUG(1)) {
|
|
for (sp = lm->mod_load_symtable;
|
|
sp < lm->mod_load_symend; sp++) {
|
|
fprintf(fp, " %lx %s\n",
|
|
sp->value, sp->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
fprintf(fp, "\n");
|
|
fprintf(fp, " dwarf_eh_frame_file_offset: %llx\n",
|
|
(unsigned long long)st->dwarf_eh_frame_file_offset);
|
|
fprintf(fp, " dwarf_eh_frame_size: %ld\n", st->dwarf_eh_frame_size);
|
|
|
|
fprintf(fp, "dwarf_debug_frame_file_offset: %llx\n",
|
|
(unsigned long long)st->dwarf_debug_frame_file_offset);
|
|
fprintf(fp, " dwarf_debug_frame_size: %ld\n", st->dwarf_debug_frame_size);
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
sec = (asection **)st->sections;
|
|
fprintf(fp, " sections: %s\n", sec ? "" : "(not in use)");
|
|
for (i = 0; sec && (i < st->bfd->section_count); i++, sec++) {
|
|
asection *section;
|
|
|
|
section = *sec;
|
|
fprintf(fp, "%25s vma: %.*lx size: %ld\n",
|
|
section->name, VADDR_PRLEN,
|
|
(ulong)bfd_get_section_vma(st->bfd, section),
|
|
(ulong)bfd_section_size(st->bfd, section));
|
|
}
|
|
fprintf(fp, "\n downsized: ");
|
|
if (st->downsized.name) {
|
|
for (ds = &st->downsized, cnt = 0; ds->name; ds = ds->next)
|
|
fprintf(fp, "%s%s", cnt++ ? ", " : "", ds->name);
|
|
fprintf(fp, "\n");
|
|
} else
|
|
fprintf(fp, "(none)\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Determine whether a file is in ELF format by checking the magic number
|
|
* in the first EI_NIDENT characters of the file; if those match, check
|
|
* whether the file is a known BFD format.
|
|
*/
|
|
int
|
|
is_elf_file(char *s)
|
|
{
|
|
int fd;
|
|
char magic[EI_NIDENT];
|
|
|
|
if ((fd = open(s, O_RDONLY)) < 0) {
|
|
error(INFO, "%s: %s\n", s, strerror(errno));
|
|
return FALSE;
|
|
}
|
|
if (read(fd, magic, EI_NIDENT) != EI_NIDENT) {
|
|
/* error(INFO, "%s: %s\n", s, strerror(errno)); */
|
|
close(fd);
|
|
return FALSE;
|
|
}
|
|
close(fd);
|
|
|
|
magic[EI_CLASS] = NULLCHAR;
|
|
|
|
if (!STREQ(magic, ELFMAG))
|
|
return FALSE;
|
|
|
|
return(is_bfd_format(s));
|
|
}
|
|
|
|
/*
|
|
* Verify a vmlinux file, issuing a warning for processor and endianness
|
|
* mismatches.
|
|
*/
|
|
int
|
|
is_kernel(char *file)
|
|
{
|
|
int fd, swap;
|
|
char eheader[BUFSIZE];
|
|
Elf32_Ehdr *elf32;
|
|
Elf64_Ehdr *elf64;
|
|
|
|
if ((fd = open(file, O_RDONLY)) < 0) {
|
|
error(INFO, "%s: %s\n", file, strerror(errno));
|
|
return FALSE;
|
|
}
|
|
if (read(fd, eheader, BUFSIZE) != BUFSIZE) {
|
|
/* error(INFO, "%s: %s\n", file, strerror(errno)); */
|
|
close(fd);
|
|
return FALSE;
|
|
}
|
|
close(fd);
|
|
|
|
if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT)
|
|
return FALSE;
|
|
|
|
elf32 = (Elf32_Ehdr *)&eheader[0];
|
|
elf64 = (Elf64_Ehdr *)&eheader[0];
|
|
|
|
swap = (((eheader[EI_DATA] == ELFDATA2LSB) &&
|
|
(__BYTE_ORDER == __BIG_ENDIAN)) ||
|
|
((eheader[EI_DATA] == ELFDATA2MSB) &&
|
|
(__BYTE_ORDER == __LITTLE_ENDIAN)));
|
|
|
|
if ((elf32->e_ident[EI_CLASS] == ELFCLASS32) &&
|
|
(swap16(elf32->e_type, swap) == ET_EXEC) &&
|
|
(swap32(elf32->e_version, swap) == EV_CURRENT)) {
|
|
switch (swap16(elf32->e_machine, swap))
|
|
{
|
|
case EM_386:
|
|
if (machine_type_mismatch(file, "X86", NULL, 0)) {
|
|
if (machine_type("X86_64")) {
|
|
/*
|
|
* Since is_bfd_format() returns TRUE
|
|
* in this case, just bail out here.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
goto bailout;
|
|
}
|
|
break;
|
|
|
|
case EM_S390:
|
|
if (machine_type_mismatch(file, "S390", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_ARM:
|
|
if (machine_type_mismatch(file, "ARM", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_PPC:
|
|
if (machine_type_mismatch(file, "PPC", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_MIPS:
|
|
if (machine_type_mismatch(file, "MIPS", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_SPARCV9:
|
|
if (machine_type_mismatch(file, "SPARC64", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
default:
|
|
if (machine_type_mismatch(file, "(unknown)", NULL, 0))
|
|
goto bailout;
|
|
}
|
|
|
|
if (endian_mismatch(file, elf32->e_ident[EI_DATA], 0))
|
|
goto bailout;
|
|
|
|
} else if ((elf64->e_ident[EI_CLASS] == ELFCLASS64) &&
|
|
((swap16(elf64->e_type, swap) == ET_EXEC) ||
|
|
(swap16(elf64->e_type, swap) == ET_DYN)) &&
|
|
(swap32(elf64->e_version, swap) == EV_CURRENT)) {
|
|
switch (swap16(elf64->e_machine, swap))
|
|
{
|
|
case EM_IA_64:
|
|
if (machine_type_mismatch(file, "IA64", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_PPC64:
|
|
if (machine_type_mismatch(file, "PPC64", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_X86_64:
|
|
if (machine_type_mismatch(file, "X86_64", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_386:
|
|
if (machine_type_mismatch(file, "X86", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_S390:
|
|
if (machine_type_mismatch(file, "S390X", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
case EM_AARCH64:
|
|
if (machine_type_mismatch(file, "ARM64", NULL, 0))
|
|
goto bailout;
|
|
break;
|
|
|
|
default:
|
|
if (machine_type_mismatch(file, "(unknown)", NULL, 0))
|
|
goto bailout;
|
|
}
|
|
|
|
if (endian_mismatch(file, elf64->e_ident[EI_DATA], 0))
|
|
goto bailout;
|
|
} else
|
|
return FALSE;
|
|
|
|
bailout:
|
|
return(is_bfd_format(file));
|
|
}
|
|
|
|
int
|
|
is_compressed_kernel(char *file, char **tmp)
|
|
{
|
|
int len, type, fd;
|
|
char *tmpdir, *tempname;
|
|
unsigned char header[BUFSIZE];
|
|
char command[BUFSIZE];
|
|
char message[BUFSIZE];
|
|
|
|
#define GZIP (1)
|
|
#define BZIP2 (2)
|
|
|
|
#define FNAME (1 << 3)
|
|
|
|
if ((fd = open(file, O_RDONLY)) < 0)
|
|
return FALSE;
|
|
|
|
if (read(fd, header, BUFSIZE) != BUFSIZE) {
|
|
close(fd);
|
|
return FALSE;
|
|
}
|
|
close(fd);
|
|
|
|
type = 0;
|
|
|
|
if ((header[0] == 0x1f) && (header[1] == 0x8b) && (header[2] == 8)) {
|
|
if (!(header[3] & FNAME)) {
|
|
if (!(st->flags & FORCE_DEBUGINFO)) {
|
|
error(INFO, "%s: "
|
|
"original filename unknown\n",
|
|
file);
|
|
error(CONT,
|
|
"Use \"-f %s\" on command line to prevent this message.\n\n",
|
|
file);
|
|
}
|
|
} else if (!STRNEQ((char *)&header[10], "vmlinux") &&
|
|
!(st->flags & FORCE_DEBUGINFO)) {
|
|
error(INFO, "%s: compressed file name does not "
|
|
"start with \"vmlinux\"\n", &header[10]);
|
|
error(CONT,
|
|
"Use \"-f %s\" on command line to override.\n\n",
|
|
file);
|
|
return FALSE;
|
|
}
|
|
type = GZIP;
|
|
}
|
|
|
|
if ((header[0] == 'B') && (header[1] == 'Z') && (header[2] == 'h')) {
|
|
if (!STRNEQ(basename(file), "vmlinux") &&
|
|
!(st->flags & FORCE_DEBUGINFO)) {
|
|
error(INFO, "%s: compressed file name does not start "
|
|
"with \"vmlinux\"\n", file);
|
|
error(CONT,
|
|
"Use \"-f %s\" on command line to override.\n\n",
|
|
file);
|
|
return FALSE;
|
|
}
|
|
type = BZIP2;
|
|
}
|
|
|
|
if (!type)
|
|
return FALSE;
|
|
|
|
if (!(tmpdir = getenv("TMPDIR")))
|
|
tmpdir = "/var/tmp";
|
|
len = strlen(tmpdir) + strlen(basename(file)) +
|
|
strlen("_XXXXXX") + 2;
|
|
if (!(tempname = (char *)malloc(len)))
|
|
return FALSE;
|
|
sprintf(tempname, "%s/%s_XXXXXX", tmpdir, basename(file));
|
|
|
|
fd = mkstemp(tempname);
|
|
if (fd < 0) {
|
|
perror("mkstemp");
|
|
free(tempname);
|
|
return FALSE;
|
|
}
|
|
pc->cleanup = tempname;
|
|
|
|
sprintf(message, "uncompressing %s", file);
|
|
please_wait(message);
|
|
switch (type)
|
|
{
|
|
case GZIP:
|
|
sprintf(command, "%s -c %s > %s",
|
|
file_exists("/bin/gunzip", NULL) ?
|
|
"/bin/gunzip" : "/usr/bin/gunzip",
|
|
file, tempname);
|
|
break;
|
|
case BZIP2:
|
|
sprintf(command, "%s -c %s > %s",
|
|
file_exists("/bin/bunzip2", NULL) ?
|
|
"/bin/bunzip2" : "/usr/bin/bunzip2",
|
|
file, tempname);
|
|
break;
|
|
}
|
|
if (system(command) < 0) {
|
|
please_wait_done();
|
|
error(INFO, "%s of %s failed\n",
|
|
type == GZIP ? "gunzip" : "bunzip2", file);
|
|
free(tempname);
|
|
return FALSE;
|
|
}
|
|
please_wait_done();
|
|
|
|
if (is_bfd_format(tempname) && is_kernel(tempname)) {
|
|
*tmp = tempname;
|
|
return TRUE;
|
|
}
|
|
|
|
unlink(tempname);
|
|
close(fd);
|
|
free(tempname);
|
|
pc->cleanup = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
is_shared_object(char *file)
|
|
{
|
|
int fd, swap;
|
|
char eheader[BUFSIZE];
|
|
Elf32_Ehdr *elf32;
|
|
Elf64_Ehdr *elf64;
|
|
|
|
if (is_directory(file))
|
|
return FALSE;
|
|
|
|
if ((fd = open(file, O_RDONLY)) < 0)
|
|
return FALSE;
|
|
|
|
if (read(fd, eheader, BUFSIZE) != BUFSIZE) {
|
|
close(fd);
|
|
return FALSE;
|
|
}
|
|
close(fd);
|
|
|
|
if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT)
|
|
return FALSE;
|
|
|
|
elf32 = (Elf32_Ehdr *)&eheader[0];
|
|
elf64 = (Elf64_Ehdr *)&eheader[0];
|
|
|
|
swap = (((eheader[EI_DATA] == ELFDATA2LSB) &&
|
|
(__BYTE_ORDER == __BIG_ENDIAN)) ||
|
|
((eheader[EI_DATA] == ELFDATA2MSB) &&
|
|
(__BYTE_ORDER == __LITTLE_ENDIAN)));
|
|
|
|
if ((elf32->e_ident[EI_CLASS] == ELFCLASS32) &&
|
|
(swap16(elf32->e_type, swap) == ET_DYN)) {
|
|
switch (swap16(elf32->e_machine, swap))
|
|
{
|
|
case EM_386:
|
|
if (machine_type("X86") || machine_type("ARM") ||
|
|
machine_type("MIPS"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_S390:
|
|
if (machine_type("S390"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_ARM:
|
|
if (machine_type("ARM"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_MIPS:
|
|
if (machine_type("MIPS"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_PPC:
|
|
if (machine_type("PPC"))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "%s: machine type mismatch: %d\n",
|
|
file, swap16(elf32->e_machine, swap));
|
|
|
|
return FALSE;
|
|
|
|
} else if ((elf64->e_ident[EI_CLASS] == ELFCLASS64) &&
|
|
(swap16(elf64->e_type, swap) == ET_DYN)) {
|
|
switch (swap16(elf64->e_machine, swap))
|
|
{
|
|
case EM_IA_64:
|
|
if (machine_type("IA64"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_PPC64:
|
|
if (machine_type("PPC64"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_X86_64:
|
|
if (machine_type("X86_64") || machine_type("ARM64"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_S390:
|
|
if (machine_type("S390X"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_AARCH64:
|
|
if (machine_type("ARM64"))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_SPARCV9:
|
|
if (machine_type("SPARC64"))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "%s: machine type mismatch: %d\n",
|
|
file, swap16(elf32->e_machine, swap));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Given a choice between two namelists, pick the one for gdb to use.
|
|
* For now, just check get their stats and check their sizes; the larger
|
|
* one presumably has debug data.
|
|
*/
|
|
int
|
|
select_namelist(char *new)
|
|
{
|
|
struct stat stat1, stat2;
|
|
char *namep;
|
|
|
|
if (pc->server_namelist) {
|
|
pc->namelist_debug = new;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!file_exists(pc->namelist, &stat1) ||
|
|
!file_exists(new, &stat2)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (stat1.st_size > stat2.st_size) {
|
|
pc->namelist_debug = pc->namelist;
|
|
if (pc->namelist_orig) {
|
|
namep = pc->namelist_debug_orig;
|
|
pc->namelist_debug_orig = pc->namelist_orig;
|
|
pc->namelist_orig = namep;
|
|
}
|
|
pc->namelist = new;
|
|
} else if (stat2.st_size > stat1.st_size)
|
|
pc->namelist_debug = new;
|
|
else {
|
|
error(INFO, "cannot distinguish %s and %s\n",
|
|
pc->namelist, new);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Make a sweep of a non-dump, non-ELF file to guess whether it's a
|
|
* legitimate System.map file.
|
|
*/
|
|
int
|
|
is_system_map(char *s)
|
|
{
|
|
int i, lines, retval;
|
|
char *mapitems[MAXARGS];
|
|
char buf[16384];
|
|
FILE *map;
|
|
|
|
/*
|
|
* First simulate what "file" does by verifying that the first 16K
|
|
* bytes are ascii data.
|
|
*/
|
|
if ((map = fopen(s, "r")) == NULL) {
|
|
error(INFO, "cannot open %s\n", s);
|
|
return FALSE;
|
|
}
|
|
|
|
retval = FALSE;
|
|
if (fread(buf, sizeof(char), 16384, map) != (16384*sizeof(char))) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "%s: cannot read 16K\n", s);
|
|
goto not_system_map;
|
|
}
|
|
|
|
for (i = 0; i < 16384; i++) {
|
|
if (!ascii(buf[i]))
|
|
goto not_system_map;
|
|
}
|
|
|
|
rewind(map);
|
|
|
|
for (lines = 0; lines < 100; lines++) {
|
|
if (!fgets(buf, BUFSIZE, map))
|
|
goto not_system_map;
|
|
if (parse_line(buf, mapitems) != 3)
|
|
goto not_system_map;
|
|
if ((strlen(mapitems[0]) > MAX_HEXADDR_STRLEN) ||
|
|
!hexadecimal(mapitems[0], 0) || (strlen(mapitems[1]) > 1))
|
|
goto not_system_map;
|
|
}
|
|
|
|
if ((pc->flags & SYSMAP) && !same_file("/boot/System.map", s))
|
|
error(INFO, "overriding /boot/System.map with %s\n", s);
|
|
|
|
retval = TRUE;
|
|
|
|
not_system_map:
|
|
|
|
fclose(map);
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Check whether a file is a known BFD format.
|
|
*/
|
|
static int
|
|
is_bfd_format(char *filename)
|
|
{
|
|
#ifdef GDB_5_3
|
|
struct _bfd *bfd;
|
|
#else
|
|
struct bfd *bfd;
|
|
#endif
|
|
char **matching;
|
|
|
|
if ((bfd = bfd_openr(filename, NULL)) == NULL)
|
|
return FALSE;
|
|
|
|
if (!bfd_check_format_matches(bfd, bfd_object, &matching)) {
|
|
bfd_close(bfd);
|
|
return FALSE;
|
|
}
|
|
|
|
bfd_close(bfd);
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
is_binary_stripped(char *filename)
|
|
{
|
|
#ifdef GDB_5_3
|
|
struct _bfd *bfd;
|
|
#else
|
|
struct bfd *bfd;
|
|
#endif
|
|
int number_of_symbols;
|
|
|
|
if ((bfd = bfd_openr(filename, NULL)) == NULL) {
|
|
error(INFO, "cannot open ELF file: %s\n", filename);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bfd_check_format(bfd, bfd_object)) {
|
|
error(INFO, "invalid ELF file: %s\n", filename);
|
|
bfd_close(bfd);
|
|
return FALSE;
|
|
}
|
|
|
|
number_of_symbols = bfd_canonicalize_symtab(bfd, NULL);
|
|
|
|
bfd_close(bfd);
|
|
|
|
return (number_of_symbols == 0);
|
|
}
|
|
|
|
/*
|
|
* This command may be used to:
|
|
*
|
|
* 1. Translate a symbol to its value.
|
|
* 2. Translate a value to it symbol.
|
|
* 3. List all stored symbols.
|
|
* 4. Query for symbols containing a string.
|
|
* 5. Show the next and previous symbols.
|
|
*/
|
|
void
|
|
cmd_sym(void)
|
|
{
|
|
int c;
|
|
struct syment *sp, *spp, *spn;
|
|
ulong value, show_flags;
|
|
ulong offset;
|
|
int next, prev, multiples, others;
|
|
char *name;
|
|
int errflag;
|
|
char buf[BUFSIZE];
|
|
|
|
next = prev = others = 0;
|
|
show_flags = SHOW_LINENUM | SHOW_RADIX();
|
|
|
|
while ((c = getopt(argcnt, args, "lLQ:q:npsMm:")) != EOF) {
|
|
switch(c)
|
|
{
|
|
case 'n':
|
|
next++;
|
|
break;
|
|
|
|
case 'p':
|
|
prev++;
|
|
break;
|
|
|
|
case 'Q':
|
|
fprintf(fp, "%d found ",
|
|
symbol_query(optarg, NULL, &sp));
|
|
if (sp)
|
|
fprintf(fp, "(%s)", sp->name);
|
|
fprintf(fp, "\n");
|
|
others++;
|
|
break;
|
|
|
|
case 'q':
|
|
if (!symbol_query(optarg, "", NULL))
|
|
fprintf(fp, "(none found)\n");
|
|
others++;
|
|
break;
|
|
|
|
case 'm':
|
|
symbol_dump(MODULE_SYMS, optarg);
|
|
others++;
|
|
break;
|
|
|
|
case 'M':
|
|
symbol_dump(MODULE_SYMS, NULL);
|
|
others++;
|
|
break;
|
|
|
|
case 'L': /* obsolete */
|
|
case 'l':
|
|
symbol_dump(KERNEL_SYMS|MODULE_SYMS, NULL);
|
|
others++;
|
|
break;
|
|
|
|
case 's':
|
|
show_flags &= ~SHOW_LINENUM;
|
|
show_flags |= SHOW_SECTION;
|
|
break;
|
|
|
|
default:
|
|
argerrs++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argerrs)
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
if (args[optind]) {
|
|
do {
|
|
name = NULL;
|
|
multiples = 0;
|
|
sp = NULL;
|
|
show_flags &= ~SHOW_MODULE;
|
|
|
|
if (clean_arg() &&
|
|
(!symbol_exists(args[optind]) && hexadecimal(args[optind], 0))) {
|
|
errflag = 0;
|
|
value = htol(args[optind], RETURN_ON_ERROR,
|
|
&errflag);
|
|
if (errflag || !in_ksymbol_range(value)) {
|
|
error(INFO, "invalid address: %s\n",
|
|
args[optind]);
|
|
} else if ((sp = value_search(value, &offset))){
|
|
name = sp->name;
|
|
if (module_symbol(sp->value, NULL, NULL,
|
|
NULL, 0))
|
|
show_flags |= SHOW_MODULE;
|
|
if (prev &&
|
|
(spp = prev_symbol(NULL, sp)))
|
|
show_symbol(spp, 0, show_flags);
|
|
|
|
show_symbol(sp, offset, show_flags);
|
|
}
|
|
else if (module_symbol(value, &sp,
|
|
NULL, buf, *gdb_output_radix)) {
|
|
name = buf;
|
|
|
|
if (prev && sp &&
|
|
(spp = prev_symbol(NULL, sp)))
|
|
show_symbol(spp, 0, show_flags);
|
|
|
|
fprintf(fp, "%lx (?) %s\n",
|
|
value, buf);
|
|
} else
|
|
fprintf(fp, "symbol not found: %s\n",
|
|
args[optind]);
|
|
} else {
|
|
if ((sp = symbol_search(args[optind]))) {
|
|
multiples = symbol_name_count(sp->name);
|
|
do_multiples:
|
|
if (module_symbol(sp->value, NULL, NULL,
|
|
NULL, 0))
|
|
show_flags |= SHOW_MODULE;
|
|
name = sp->name;
|
|
if (prev &&
|
|
(spp = prev_symbol(NULL, sp)))
|
|
show_symbol(spp, 0, show_flags);
|
|
|
|
show_symbol(sp, 0, show_flags);
|
|
}
|
|
else {
|
|
fprintf(fp, "symbol not found: %s\n",
|
|
args[optind]);
|
|
fprintf(fp, "possible alternatives:\n");
|
|
if (!symbol_query(args[optind], " ",
|
|
NULL))
|
|
fprintf(fp, " (none found)\n");
|
|
}
|
|
}
|
|
|
|
if (name && next && (spn = next_symbol(NULL, sp)))
|
|
show_symbol(spn, 0, show_flags);
|
|
|
|
if (multiples > 1) {
|
|
if ((sp = symbol_search_next(name, sp)))
|
|
goto do_multiples;
|
|
}
|
|
|
|
optind++;
|
|
} while(args[optind]);
|
|
}
|
|
else if (!others)
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
}
|
|
|
|
/*
|
|
* Common symbol display for cmd_sym().
|
|
*/
|
|
void
|
|
show_symbol(struct syment *sp, ulong offset, ulong show_flags)
|
|
{
|
|
char buf[BUFSIZE];
|
|
char *p1;
|
|
ulong radix;
|
|
struct load_module *lm;
|
|
|
|
lm = NULL;
|
|
if (CRASHDEBUG(1))
|
|
show_flags |= SHOW_LINENUM;
|
|
|
|
switch (show_flags & (SHOW_HEX_OFFS|SHOW_DEC_OFFS))
|
|
{
|
|
case SHOW_DEC_OFFS:
|
|
radix = 10;
|
|
break;
|
|
|
|
default:
|
|
case SHOW_HEX_OFFS:
|
|
radix = 16;
|
|
break;
|
|
}
|
|
|
|
if (MODULE_START(sp)) {
|
|
p1 = sp->name + strlen("_MODULE_START_");
|
|
fprintf(fp, "%lx (%c) (%s module)", sp->value, sp->type, p1);
|
|
if (offset)
|
|
fprintf(fp, (radix == 16) ? "+0x%lx" : "+%ld",
|
|
offset);
|
|
fprintf(fp, "\n");
|
|
return;
|
|
} else if (show_flags & SHOW_MODULE)
|
|
module_symbol(sp->value, NULL, &lm, NULL, 0);
|
|
|
|
if (offset)
|
|
fprintf(fp, (radix == 16) ?
|
|
"%lx (%c) %s+0x%lx" : "%lx (%c) %s+%ld",
|
|
sp->value+offset, sp->type, sp->name, offset);
|
|
else
|
|
fprintf(fp, "%lx (%c) %s", sp->value, sp->type, sp->name);
|
|
|
|
if (lm)
|
|
fprintf(fp, " [%s]", lm->mod_name);
|
|
|
|
if (is_kernel_text(sp->value+offset) &&
|
|
(show_flags & SHOW_LINENUM))
|
|
fprintf(fp, " %s",
|
|
get_line_number(sp->value+offset, buf, TRUE));
|
|
|
|
if (show_flags & SHOW_SECTION)
|
|
fprintf(fp, " [%s]", get_section(sp->value+offset, buf));
|
|
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
/*
|
|
* Use the gdb_interface to get a line number associated with a
|
|
* text address -- but first check whether the address gets past
|
|
* any machine-dependent line_number_hooks reference.
|
|
*/
|
|
char *
|
|
get_line_number(ulong addr, char *buf, int reserved)
|
|
{
|
|
char *p;
|
|
struct gnu_request request, *req;
|
|
struct line_number_hook *lnh;
|
|
struct syment *sp;
|
|
char bldbuf[BUFSIZE], *name;
|
|
struct load_module *lm;
|
|
|
|
buf[0] = NULLCHAR;
|
|
lm = NULL;
|
|
|
|
if (NO_LINE_NUMBERS() || !is_kernel_text(addr))
|
|
return(buf);
|
|
|
|
if (module_symbol(addr, NULL, &lm, NULL, 0)) {
|
|
if (!(lm->mod_flags & MOD_LOAD_SYMS))
|
|
return(buf);
|
|
} else if (kt->flags2 & KASLR)
|
|
addr -= (kt->relocate * -1);
|
|
|
|
if ((lnh = machdep->line_number_hooks)) {
|
|
name = closest_symbol(addr);
|
|
while (lnh->func) {
|
|
if (STREQ(name, lnh->func)) {
|
|
sprintf(buf, "%s/%s",
|
|
get_build_directory(bldbuf) ?
|
|
bldbuf : "..", *(lnh->file));
|
|
break;
|
|
}
|
|
lnh++;
|
|
}
|
|
}
|
|
|
|
if (!strlen(buf)) {
|
|
req = &request;
|
|
BZERO(req, sizeof(struct gnu_request));
|
|
req->command = GNU_GET_LINE_NUMBER;
|
|
req->addr = addr;
|
|
req->buf = buf;
|
|
if (lm && lm->loaded_objfile)
|
|
req->lm = lm;
|
|
if ((sp = value_search(addr, NULL)))
|
|
req->name = sp->name;
|
|
gdb_interface(req);
|
|
}
|
|
|
|
while ((p = strstr(buf, "//")))
|
|
shift_string_left(p+1, 1);
|
|
|
|
return(buf);
|
|
}
|
|
|
|
static char *
|
|
get_section(ulong vaddr, char *buf)
|
|
{
|
|
int i;
|
|
asection **sec;
|
|
asection *section;
|
|
ulong start, end;
|
|
struct load_module *lm;
|
|
|
|
buf[0] = NULLCHAR;
|
|
|
|
if (module_symbol(vaddr, NULL, &lm, NULL, *gdb_output_radix)) {
|
|
if (lm->mod_flags & MOD_LOAD_SYMS) {
|
|
for (i = (lm->mod_sections-1); i >= 0; i--) {
|
|
start = lm->mod_base +
|
|
lm->mod_section_data[i].offset;
|
|
end = start + lm->mod_section_data[i].size;
|
|
|
|
if ((vaddr >= start) && (vaddr < end)) {
|
|
strcpy(buf,
|
|
lm->mod_section_data[i].name);
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
sprintf(buf, "in %s module", lm->mod_name);
|
|
} else {
|
|
sec = (asection **)st->sections;
|
|
for (i = 0; i < st->bfd->section_count; i++, sec++) {
|
|
section = *sec;
|
|
start = (ulong)bfd_get_section_vma(st->bfd, section);
|
|
end = start + (ulong)bfd_section_size(st->bfd, section);
|
|
|
|
if ((vaddr >= start) && (vaddr < end)) {
|
|
strcpy(buf, bfd_get_section_name(st->bfd,
|
|
section));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Get the kernel build directory.
|
|
*/
|
|
char *
|
|
get_build_directory(char *buf)
|
|
{
|
|
char *p;
|
|
|
|
if (symbol_exists("schedule"))
|
|
get_line_number(symbol_value("schedule"), buf, FALSE);
|
|
else if (symbol_exists("do_schedule"))
|
|
get_line_number(symbol_value("do_schedule"), buf, FALSE);
|
|
else
|
|
return NULL;
|
|
if ((p = strstr(buf, "/kernel/")))
|
|
*p = NULLCHAR;
|
|
else
|
|
return(NULL);
|
|
|
|
return buf;
|
|
}
|
|
/*
|
|
* Search for all symbols containing a string.
|
|
*/
|
|
int
|
|
symbol_query(char *s, char *print_pad, struct syment **spp)
|
|
{
|
|
int i;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
int cnt, search_init;
|
|
|
|
cnt = 0;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if (strstr(sp->name, s)) {
|
|
if (print_pad) {
|
|
if (strlen(print_pad))
|
|
fprintf(fp, "%s", print_pad);
|
|
show_symbol(sp, 0, SHOW_RADIX());
|
|
}
|
|
if (spp)
|
|
*spp = sp;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
search_init = FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_INIT)
|
|
search_init = TRUE;
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (MODULE_START(sp))
|
|
continue;
|
|
|
|
if (strstr(sp->name, s)) {
|
|
if (print_pad) {
|
|
if (strlen(print_pad))
|
|
fprintf(fp, "%s", print_pad);
|
|
show_symbol(sp, 0,
|
|
SHOW_RADIX()|SHOW_MODULE);
|
|
}
|
|
if (spp)
|
|
*spp = sp;
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!search_init)
|
|
return(cnt);
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_symtable)
|
|
continue;
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (MODULE_START(sp))
|
|
continue;
|
|
|
|
if (strstr(sp->name, s)) {
|
|
if (print_pad) {
|
|
if (strlen(print_pad))
|
|
fprintf(fp, "%s", print_pad);
|
|
show_symbol(sp, 0,
|
|
SHOW_RADIX()|SHOW_MODULE);
|
|
}
|
|
if (spp)
|
|
*spp = sp;
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(cnt);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the syment of a symbol.
|
|
*/
|
|
struct syment *
|
|
symbol_search(char *s)
|
|
{
|
|
int i;
|
|
struct syment *sp_hashed, *sp, *sp_end;
|
|
struct load_module *lm;
|
|
int pseudos, search_init;
|
|
|
|
sp_hashed = symname_hash_search(s);
|
|
|
|
for (sp = sp_hashed ? sp_hashed : st->symtable; sp < st->symend; sp++) {
|
|
if (STREQ(s, sp->name))
|
|
return(sp);
|
|
}
|
|
|
|
pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_"));
|
|
search_init = FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_INIT)
|
|
search_init = TRUE;
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp <= sp_end; sp++) {
|
|
if (!pseudos && MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
if (STREQ(s, sp->name))
|
|
return(sp);
|
|
}
|
|
}
|
|
|
|
if (!search_init)
|
|
return((struct syment *)NULL);
|
|
|
|
pseudos = (strstr(s, "_MODULE_INIT_START_") || strstr(s, "_MODULE_INIT_END_"));
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_symtable)
|
|
continue;
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (!pseudos && MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
|
|
if (STREQ(s, sp->name))
|
|
return(sp);
|
|
}
|
|
}
|
|
|
|
return((struct syment *)NULL);
|
|
}
|
|
|
|
/*
|
|
* Count the number of instances of a symbol name.
|
|
*/
|
|
int
|
|
symbol_name_count(char *s)
|
|
{
|
|
int i;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
int count, pseudos, search_init;
|
|
|
|
count = 0;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if (STREQ(s, sp->name)) {
|
|
count = sp->cnt;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_"));
|
|
search_init = FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_INIT)
|
|
search_init = TRUE;
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (!pseudos && MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
|
|
if (STREQ(s, sp->name))
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (!search_init)
|
|
return(count);
|
|
|
|
pseudos = (strstr(s, "_MODULE_INIT_START_") || strstr(s, "_MODULE_INIT_END_"));
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_symtable)
|
|
continue;
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (!pseudos && MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
|
|
if (STREQ(s, sp->name))
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return(count);
|
|
}
|
|
|
|
/*
|
|
* Return the syment of the next symbol with the same name of the input symbol.
|
|
*/
|
|
struct syment *
|
|
symbol_search_next(char *s, struct syment *spstart)
|
|
{
|
|
int i;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
int found_start;
|
|
int pseudos, search_init;
|
|
|
|
found_start = FALSE;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if (sp == spstart) {
|
|
found_start = TRUE;
|
|
continue;
|
|
} else if (!found_start)
|
|
continue;
|
|
|
|
if (strcmp(s, sp->name) == 0) {
|
|
return(sp);
|
|
}
|
|
}
|
|
|
|
pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_"));
|
|
search_init = FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_INIT)
|
|
search_init = TRUE;
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (!pseudos && MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
|
|
if (sp == spstart) {
|
|
found_start = TRUE;
|
|
continue;
|
|
} else if (!found_start)
|
|
continue;
|
|
|
|
if (STREQ(s, sp->name))
|
|
return(sp);
|
|
}
|
|
}
|
|
|
|
if (!search_init)
|
|
return((struct syment *)NULL);
|
|
|
|
pseudos = (strstr(s, "_MODULE_INIT_START_") || strstr(s, "_MODULE_INIT_END_"));
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_symtable)
|
|
continue;
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (!pseudos && MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
|
|
if (sp == spstart) {
|
|
found_start = TRUE;
|
|
continue;
|
|
} else if (!found_start)
|
|
continue;
|
|
|
|
if (STREQ(s, sp->name))
|
|
return(sp);
|
|
}
|
|
}
|
|
|
|
return((struct syment *)NULL);
|
|
}
|
|
|
|
/*
|
|
* Determine whether an address falls within the kernel's, or any module's,
|
|
* address space.
|
|
*/
|
|
int
|
|
in_ksymbol_range(ulong value)
|
|
{
|
|
if ((value >= st->symtable[0].value) &&
|
|
(value <= st->symtable[st->symcnt-1].value)) {
|
|
if ((st->flags & PERCPU_SYMS) && (value < st->first_ksymbol))
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
if (module_symbol(value, NULL, NULL, NULL, *gdb_output_radix))
|
|
return TRUE;
|
|
|
|
if (machdep->value_to_symbol(value, NULL))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Determine whether an address falls within any module's address space.
|
|
* If syment or load_module pointers are passed, send them back.
|
|
* If a pointer to a name buffer is passed, stuff it with the particulars.
|
|
*/
|
|
int
|
|
module_symbol(ulong value,
|
|
struct syment **spp,
|
|
struct load_module **lmp,
|
|
char *name,
|
|
ulong radix)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
struct syment *sp;
|
|
char buf[BUFSIZE];
|
|
ulong offs, offset;
|
|
ulong base, end;
|
|
|
|
if (NO_MODULES())
|
|
return FALSE;
|
|
|
|
if (!radix)
|
|
radix = *gdb_output_radix;
|
|
if ((radix != 10) && (radix != 16))
|
|
radix = 16;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
|
|
if (IN_MODULE(value, lm)) {
|
|
base = lm->mod_base;
|
|
end = lm->mod_base + lm->mod_size;
|
|
} else if (IN_MODULE_INIT(value, lm)) {
|
|
base = lm->mod_init_module_ptr;
|
|
end = lm->mod_init_module_ptr + lm->mod_init_size;
|
|
} else if (IN_MODULE_PERCPU(value, lm)) {
|
|
base = lm->mod_percpu;
|
|
end = lm->mod_percpu + lm->mod_percpu_size;
|
|
} else
|
|
continue;
|
|
|
|
if ((value >= base) && (value < end)) {
|
|
if (lmp)
|
|
*lmp = lm;
|
|
|
|
if (name) {
|
|
offs = value - base;
|
|
if ((sp = value_search(value, &offset))) {
|
|
if (offset)
|
|
sprintf(buf, radix == 16 ?
|
|
"%s+0x%lx" : "%s+%ld",
|
|
sp->name, offset);
|
|
else
|
|
sprintf(buf, "%s", sp->name);
|
|
strcpy(name, buf);
|
|
if (spp)
|
|
*spp = sp;
|
|
return TRUE;
|
|
}
|
|
|
|
sprintf(name, "(%s module)", lm->mod_name);
|
|
|
|
if (offs) {
|
|
sprintf(buf, radix == 16 ?
|
|
"+0x%lx" : "+%ld", offs);
|
|
strcat(name, buf);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
struct syment *
|
|
value_search_module(ulong value, ulong *offset)
|
|
{
|
|
int i;
|
|
struct syment *sp, *sp_end, *spnext, *splast;
|
|
struct load_module *lm;
|
|
int search_init_sections, search_init;
|
|
|
|
search_init = FALSE;
|
|
search_init_sections = 0;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
if (st->load_modules[i].mod_flags & MOD_INIT)
|
|
search_init_sections++;
|
|
}
|
|
|
|
retry:
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
|
|
if (search_init) {
|
|
if (lm->mod_init_symtable) {
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
} else
|
|
continue;
|
|
} else {
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
}
|
|
|
|
if (sp->value > value) /* invalid -- between modules */
|
|
break;
|
|
|
|
/*
|
|
* splast will contain the last module symbol encountered.
|
|
* Note: "__insmod_"-type symbols will be set in splast only
|
|
* when they have unique values.
|
|
*/
|
|
splast = NULL;
|
|
for ( ; sp <= sp_end; sp++) {
|
|
if (machine_type("ARM64") &&
|
|
IN_MODULE_PERCPU(sp->value, lm) &&
|
|
!IN_MODULE_PERCPU(value, lm))
|
|
continue;
|
|
|
|
if (value == sp->value) {
|
|
if (MODULE_END(sp) || MODULE_INIT_END(sp))
|
|
break;
|
|
|
|
if (MODULE_PSEUDO_SYMBOL(sp)) {
|
|
spnext = sp + 1;
|
|
if (MODULE_PSEUDO_SYMBOL(spnext))
|
|
continue;
|
|
if (spnext->value == value)
|
|
sp = spnext;
|
|
}
|
|
if (is_insmod_builtin(lm, sp)) {
|
|
spnext = sp+1;
|
|
if ((spnext < sp_end) &&
|
|
(value == spnext->value))
|
|
sp = spnext;
|
|
}
|
|
if (sp->name[0] == '.') {
|
|
spnext = sp+1;
|
|
if (spnext->value == value)
|
|
sp = spnext;
|
|
}
|
|
if (offset)
|
|
*offset = 0;
|
|
return((struct syment *)sp);
|
|
}
|
|
|
|
if (sp->value > value) {
|
|
sp = splast ? splast : sp - 1;
|
|
if (offset)
|
|
*offset = value - sp->value;
|
|
return(sp);
|
|
}
|
|
|
|
if (!MODULE_PSEUDO_SYMBOL(sp)) {
|
|
if (is_insmod_builtin(lm, sp)) {
|
|
if (!splast ||
|
|
(sp->value > splast->value))
|
|
splast = sp;
|
|
|
|
} else
|
|
splast = sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (search_init_sections) {
|
|
if (!search_init) {
|
|
search_init = TRUE;
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
return((struct syment *)NULL);
|
|
}
|
|
|
|
/*
|
|
* Return the syment of the symbol closest to the value, along with
|
|
* the offset from the symbol value if requested.
|
|
*/
|
|
struct syment *
|
|
value_search(ulong value, ulong *offset)
|
|
{
|
|
struct syment *sp, *spnext;
|
|
|
|
if (!in_ksymbol_range(value))
|
|
return((struct syment *)NULL);
|
|
|
|
if ((sp = machdep->value_to_symbol(value, offset)))
|
|
return sp;
|
|
|
|
if (IS_VMALLOC_ADDR(value))
|
|
goto check_modules;
|
|
|
|
if ((sp = symval_hash_search(value)) == NULL)
|
|
sp = st->symtable;
|
|
|
|
for ( ; sp < st->symend; sp++) {
|
|
if (value == sp->value) {
|
|
#if !defined(GDB_5_3) && !defined(GDB_6_0) && !defined(GDB_6_1)
|
|
if (STRNEQ(sp->name, ".text.")) {
|
|
spnext = sp+1;
|
|
if (spnext->value == value)
|
|
sp = spnext;
|
|
}
|
|
#endif
|
|
if (offset)
|
|
*offset = 0;
|
|
|
|
/*
|
|
* Avoid "SyS" and "compat_SyS" kernel syscall
|
|
* aliases by returning the real symbol name,
|
|
* which is the next symbol in the list.
|
|
*/
|
|
if ((STRNEQ(sp->name, "SyS_") ||
|
|
STRNEQ(sp->name, "compat_SyS_")) &&
|
|
((spnext = sp+1) < st->symend) &&
|
|
(spnext->value == value))
|
|
sp = spnext;
|
|
|
|
/*
|
|
* If any of the special text region starting address
|
|
* delimiters declared in vmlinux.lds.S match the
|
|
* first "real" text symbol in the region, return
|
|
* that (next) one instead.
|
|
*/
|
|
if (strstr_rightmost(sp->name, "_text_start") &&
|
|
((spnext = sp+1) < st->symend) &&
|
|
(spnext->value == value))
|
|
sp = spnext;
|
|
|
|
return((struct syment *)sp);
|
|
}
|
|
if (sp->value > value) {
|
|
if (offset)
|
|
*offset = value - ((sp-1)->value);
|
|
return((struct syment *)(sp-1));
|
|
}
|
|
}
|
|
|
|
check_modules:
|
|
sp = value_search_module(value, offset);
|
|
|
|
return sp;
|
|
}
|
|
|
|
ulong
|
|
highest_bss_symbol(void)
|
|
{
|
|
struct syment *sp;
|
|
ulong highest = 0;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if ((sp->type == 'b') || (sp->type == 'B')) {
|
|
if (sp->value > highest)
|
|
highest = sp->value;
|
|
}
|
|
}
|
|
return highest;
|
|
}
|
|
|
|
/*
|
|
* Search for a value only within the base kernel's symbols,
|
|
* also avoiding the machdep->value_to_symbol() call, which will
|
|
* most likely be the prime consumer of this call.
|
|
*/
|
|
struct syment *
|
|
value_search_base_kernel(ulong value, ulong *offset)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if (value < st->symtable[0].value)
|
|
return((struct syment *)NULL);
|
|
|
|
if ((sp = symval_hash_search(value)) == NULL)
|
|
sp = st->symtable;
|
|
|
|
for ( ; sp < st->symend; sp++) {
|
|
if (value == sp->value) {
|
|
if (offset)
|
|
*offset = 0;
|
|
return((struct syment *)sp);
|
|
}
|
|
if (sp->value > value) {
|
|
if (offset)
|
|
*offset = value - ((sp-1)->value);
|
|
return((struct syment *)(sp-1));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we go off the end, just use the last symbol plus offset.
|
|
*/
|
|
sp = st->symend;
|
|
if (offset)
|
|
*offset = value - ((sp-1)->value);
|
|
return((struct syment *)(sp-1));
|
|
}
|
|
|
|
/*
|
|
* Allow platforms to assign symbols to their own special values.
|
|
*/
|
|
struct syment *
|
|
generic_machdep_value_to_symbol(ulong value, ulong *offset)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* For a given value, format a string containing the nearest symbol name
|
|
* plus the offset if appropriate. Display the offset in the specified
|
|
* radix (10 or 16) -- if it's 0, set it to the current pc->output_radix.
|
|
*/
|
|
char *
|
|
value_to_symstr(ulong value, char *buf, ulong radix)
|
|
{
|
|
struct syment *sp;
|
|
ulong offset;
|
|
char *p1, locbuf[BUFSIZE];
|
|
struct load_module *lm;
|
|
|
|
sp = NULL;
|
|
offset = 0;
|
|
buf[0] = NULLCHAR;
|
|
|
|
if (!radix)
|
|
radix = *gdb_output_radix;
|
|
if ((radix != 10) && (radix != 16))
|
|
radix = 16;
|
|
|
|
if ((sp = value_search(value, &offset))) {
|
|
if (offset)
|
|
sprintf(buf, radix == 16 ? "%s+0x%lx" : "%s+%ld",
|
|
sp->name, offset);
|
|
else
|
|
sprintf(buf, "%s", sp->name);
|
|
}
|
|
|
|
if (module_symbol(value, NULL, NULL, locbuf, *gdb_output_radix)) {
|
|
if (sp) {
|
|
if (STRNEQ(locbuf, "_MODULE_START_"))
|
|
shift_string_left(locbuf,
|
|
strlen("_MODULE_START_"));
|
|
if ((p1 = strstr(locbuf, "+")))
|
|
*p1 = NULLCHAR;
|
|
|
|
if (offset) {
|
|
if (is_module_name(locbuf, NULL, &lm) &&
|
|
(value < lm->mod_text_start))
|
|
sprintf(buf, radix == 16 ?
|
|
"(%s module)+0x%lx" :
|
|
"(%s module)+%ld",
|
|
locbuf, offset);
|
|
else
|
|
sprintf(buf, radix == 16 ?
|
|
"%s+0x%lx" : "%s+%ld",
|
|
locbuf, offset);
|
|
} else {
|
|
if (is_module_name(locbuf, NULL, &lm) &&
|
|
(value < lm->mod_text_start))
|
|
sprintf(buf, "(%s)", locbuf);
|
|
else
|
|
sprintf(buf, "%s", locbuf);
|
|
}
|
|
} else
|
|
sprintf(buf, "%s", locbuf);
|
|
}
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/*
|
|
* For a given value, return the closest (lower-in-value) symbol name.
|
|
*/
|
|
char *
|
|
closest_symbol(ulong value)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if ((sp = value_search(value, NULL)))
|
|
return(sp->name);
|
|
else
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Same as above, but return the closest (lower-in-value) symbol value.
|
|
*/
|
|
ulong
|
|
closest_symbol_value(ulong value)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if ((sp = value_search(value, NULL)))
|
|
return(sp->value);
|
|
else
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* For a given symbol, return a pointer to the next (higher) symbol's syment.
|
|
* Either a symbol name or syment pointer may be passed as an argument.
|
|
*/
|
|
struct syment *
|
|
next_symbol(char *symbol, struct syment *sp_in)
|
|
{
|
|
int i;
|
|
int found, search_init;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
char buf[BUFSIZE], *p1;
|
|
|
|
if (!symbol && !sp_in)
|
|
error(FATAL, "next_symbol: two NULL args!\n");
|
|
|
|
if (sp_in) {
|
|
found = FALSE;
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if (sp == sp_in)
|
|
found = TRUE;
|
|
else if (found) {
|
|
if (sp->value > sp_in->value)
|
|
return sp;
|
|
}
|
|
}
|
|
|
|
search_init = FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_INIT)
|
|
search_init = TRUE;
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
if (sp == sp_in)
|
|
found = TRUE;
|
|
else if (found) {
|
|
if ((sp->value == sp_in->value) &&
|
|
is_insmod_builtin(lm, sp))
|
|
continue;
|
|
return sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; search_init && (i < st->mods_installed); i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_symtable)
|
|
continue;
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
if (sp == sp_in)
|
|
found = TRUE;
|
|
else if (found)
|
|
return sp;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Deal with a few special cases...
|
|
*/
|
|
if (strstr(symbol, " module)")) {
|
|
sprintf(buf, "_MODULE_START_");
|
|
strcat(buf, &symbol[1]);
|
|
p1 = strstr(buf, " module)");
|
|
*p1 = NULLCHAR;
|
|
symbol = buf;
|
|
}
|
|
|
|
if (STREQ(symbol, "_end")) {
|
|
if (!st->mods_installed)
|
|
return NULL;
|
|
|
|
lm = &st->load_modules[0];
|
|
|
|
return lm->mod_symtable;
|
|
}
|
|
|
|
if ((sp = symbol_search(symbol))) {
|
|
sp++;
|
|
if (MODULE_END(sp)) {
|
|
sp--;
|
|
i = load_module_index(sp);
|
|
if ((i+1) == st->mods_installed)
|
|
return NULL;
|
|
|
|
lm = &st->load_modules[i+1];
|
|
|
|
sp = lm->mod_symtable;
|
|
}
|
|
return sp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* For a given symbol, return a pointer to the previous (lower) symbol's syment.
|
|
* Either a symbol name or syment pointer may be passed as an argument.
|
|
*/
|
|
struct syment *
|
|
prev_symbol(char *symbol, struct syment *sp_in)
|
|
{
|
|
int i, search_init;
|
|
struct syment *sp, *sp_end, *sp_prev;
|
|
char buf[BUFSIZE], *p1;
|
|
struct load_module *lm;
|
|
|
|
if (!symbol && !sp_in)
|
|
error(FATAL, "prev_symbol: two NULL args!\n");
|
|
|
|
if (sp_in) {
|
|
sp_prev = NULL;
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if (sp == sp_in)
|
|
return sp_prev;
|
|
sp_prev = sp;
|
|
}
|
|
|
|
search_init = FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_INIT)
|
|
search_init = TRUE;
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
if (sp == sp_in)
|
|
return sp_prev;
|
|
|
|
if (is_insmod_builtin(lm, sp)) {
|
|
if (sp->value > sp_prev->value)
|
|
sp_prev = sp;
|
|
} else
|
|
sp_prev = sp;
|
|
}
|
|
}
|
|
|
|
for (i = 0; search_init && (i < st->mods_installed); i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_symtable)
|
|
continue;
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (MODULE_PSEUDO_SYMBOL(sp))
|
|
continue;
|
|
if (sp == sp_in)
|
|
return sp_prev;
|
|
|
|
sp_prev = sp;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (strstr(symbol, " module)")) {
|
|
sprintf(buf, "_MODULE_START_");
|
|
strcat(buf, &symbol[1]);
|
|
p1 = strstr(buf, " module)");
|
|
*p1 = NULLCHAR;
|
|
symbol = buf;
|
|
}
|
|
|
|
if ((sp = symbol_search(symbol))) {
|
|
if (sp == st->symtable)
|
|
return((struct syment *)NULL);
|
|
|
|
if (module_symbol(sp->value, NULL, NULL, NULL, 0)) {
|
|
if (MODULE_START(sp)) {
|
|
i = load_module_index(sp);
|
|
if (i == 0)
|
|
sp = symbol_search("_end");
|
|
else {
|
|
lm = &st->load_modules[i-1];
|
|
sp = lm->mod_symend;
|
|
sp--;
|
|
}
|
|
} else
|
|
sp--;
|
|
} else
|
|
sp--;
|
|
|
|
return sp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the specified amount of data from the given symbol's value.
|
|
*/
|
|
void
|
|
get_symbol_data(char *symbol, long size, void *local)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if ((sp = symbol_search(symbol)))
|
|
readmem(sp->value, KVADDR, local,
|
|
size, symbol, FAULT_ON_ERROR);
|
|
else
|
|
error(FATAL, "cannot resolve: \"%s\"\n", symbol);
|
|
}
|
|
|
|
/*
|
|
* Same as above, but allow for failure.
|
|
*/
|
|
int
|
|
try_get_symbol_data(char *symbol, long size, void *local)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if ((sp = symbol_search(symbol)) &&
|
|
readmem(sp->value, KVADDR, local,
|
|
size, symbol, RETURN_ON_ERROR|QUIET))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Return the value of a given symbol.
|
|
*/
|
|
ulong
|
|
symbol_value(char *symbol)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if (!(sp = symbol_search(symbol)))
|
|
error(FATAL, "cannot resolve \"%s\"\n", symbol);
|
|
|
|
return(sp->value);
|
|
}
|
|
|
|
/*
|
|
* Return the value of a symbol from a specific module.
|
|
*/
|
|
ulong
|
|
symbol_value_module(char *symbol, char *module)
|
|
{
|
|
int i;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
|
|
if (!STREQ(module, lm->mod_name))
|
|
continue;
|
|
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (STREQ(symbol, sp->name))
|
|
return(sp->value);
|
|
}
|
|
|
|
if (lm->mod_init_symtable) {
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (STREQ(symbol, sp->name))
|
|
return(sp->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return the symbol name of a given value, with no allowance for offsets.
|
|
* Returns NULL on failure to allow for testing of a value.
|
|
*/
|
|
char *
|
|
value_symbol(ulong value)
|
|
{
|
|
struct syment *sp;
|
|
ulong offset;
|
|
|
|
if ((sp = value_search(value, &offset))) {
|
|
if (offset)
|
|
return NULL;
|
|
else
|
|
return sp->name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Determine whether a symbol exists.
|
|
*/
|
|
int
|
|
symbol_exists(char *symbol)
|
|
{
|
|
int i;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
|
|
if ((sp = symname_hash_search(symbol)))
|
|
return TRUE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (STREQ(symbol, sp->name))
|
|
return(TRUE);
|
|
}
|
|
|
|
if (lm->mod_init_symtable) {
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (STREQ(symbol, sp->name))
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Determine whether a per-cpu symbol exists.
|
|
|
|
* The old-style per-cpu symbol names were pre-pended with
|
|
* "per_cpu__", whereas the new-style ones (as of 2.6.34)
|
|
* are not. This function allows the symbol argument to
|
|
* use either the old- or new-sytle format, and find either
|
|
* type.
|
|
*/
|
|
struct syment *
|
|
per_cpu_symbol_search(char *symbol)
|
|
{
|
|
struct syment *sp;
|
|
char old[BUFSIZE];
|
|
char *new;
|
|
|
|
if (STRNEQ(symbol, "per_cpu__")) {
|
|
if ((sp = symbol_search(symbol)))
|
|
return sp;
|
|
new = symbol + strlen("per_cpu__");
|
|
if ((sp = symbol_search(new))) {
|
|
if ((sp->type == 'V') || (is_percpu_symbol(sp)))
|
|
return sp;
|
|
if ((sp->type == 'd') &&
|
|
(st->__per_cpu_start == st->__per_cpu_end))
|
|
return sp;
|
|
}
|
|
} else {
|
|
if ((sp = symbol_search(symbol))) {
|
|
if ((sp->type == 'V') || (is_percpu_symbol(sp)))
|
|
return sp;
|
|
}
|
|
|
|
sprintf(old, "per_cpu__%s", symbol);
|
|
if ((sp = symbol_search(old)))
|
|
return sp;
|
|
}
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "per_cpu_symbol_search(%s): NULL\n", symbol);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Determine whether a static kernel symbol exists.
|
|
*/
|
|
int
|
|
kernel_symbol_exists(char *symbol)
|
|
{
|
|
struct syment *sp;
|
|
|
|
if ((sp = symname_hash_search(symbol)))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Similar to above, but return the syment of the kernel symbol.
|
|
*/
|
|
struct syment *
|
|
kernel_symbol_search(char *symbol)
|
|
{
|
|
return symname_hash_search(symbol);
|
|
}
|
|
|
|
/*
|
|
* Return the number of instances of a symbol name along with pointers to
|
|
* their syment structures.
|
|
*/
|
|
int
|
|
get_syment_array(char *symbol, struct syment **sp_array, int max)
|
|
{
|
|
int i, cnt;
|
|
struct syment *sp, *sp_end;
|
|
struct load_module *lm;
|
|
|
|
cnt = 0;
|
|
|
|
for (sp = st->symtable; sp < st->symend; sp++) {
|
|
if ((*symbol == *(sp->name)) && STREQ(symbol, sp->name)) {
|
|
if (!sp_array)
|
|
return sp->cnt;
|
|
if (max) {
|
|
if (cnt == max) {
|
|
error(INFO,
|
|
"symbol count overflow (%s)\n",
|
|
symbol);
|
|
return cnt;
|
|
} else
|
|
sp_array[cnt] = sp;
|
|
}
|
|
if (sp->cnt == 1)
|
|
return 1;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
sp = lm->mod_symtable;
|
|
sp_end = lm->mod_symend;
|
|
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (STREQ(symbol, sp->name)) {
|
|
if (max && (cnt < max))
|
|
sp_array[cnt] = sp;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
if (lm->mod_init_symtable) {
|
|
sp = lm->mod_init_symtable;
|
|
sp_end = lm->mod_init_symend;
|
|
for ( ; sp < sp_end; sp++) {
|
|
if (STREQ(symbol, sp->name)) {
|
|
if (max && (cnt < max))
|
|
sp_array[cnt] = sp;
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/*
|
|
* Perform any datatype-related initializations here.
|
|
*/
|
|
void
|
|
datatype_init(void)
|
|
{
|
|
BNEG(&offset_table, sizeof(offset_table));
|
|
BNEG(&size_table, sizeof(size_table));
|
|
BZERO(&array_table, sizeof(array_table));
|
|
}
|
|
|
|
/*
|
|
* This function is called through the following macros:
|
|
*
|
|
* #define STRUCT_SIZE(X) datatype_info((X), NULL, NULL)
|
|
* #define UNION_SIZE(X) datatype_info((X), NULL, NULL)
|
|
* #define DATATYPE_SIZE(X) datatype_info((X)->name, NULL, (X))
|
|
* #define MEMBER_OFFSET(X,Y) datatype_info((X), (Y), NULL)
|
|
* #define STRUCT_EXISTS(X) (datatype_info((X), NULL, NULL) >= 0)
|
|
* #define MEMBER_EXISTS(X,Y) (datatype_info((X), (Y), NULL) >= 0)
|
|
* #define MEMBER_SIZE(X,Y) datatype_info((X), (Y), MEMBER_SIZE_REQUEST)
|
|
* #define MEMBER_TYPE(X,Y) datatype_info((X), (Y), MEMBER_TYPE_REQUEST)
|
|
* #define ANON_MEMBER_OFFSET(X,Y) datatype_info((X), (Y), ANON_MEMBER_OFFSET_REQUEST)
|
|
*
|
|
* to determine structure or union sizes, or member offsets.
|
|
*/
|
|
long
|
|
datatype_info(char *name, char *member, struct datatype_member *dm)
|
|
{
|
|
struct gnu_request *req;
|
|
long offset, size, member_size;
|
|
int member_typecode;
|
|
ulong type_found;
|
|
char buf[BUFSIZE];
|
|
|
|
if (dm == ANON_MEMBER_OFFSET_REQUEST)
|
|
return anon_member_offset(name, member);
|
|
|
|
strcpy(buf, name);
|
|
|
|
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
|
|
req->command = GNU_GET_DATATYPE;
|
|
req->flags |= GNU_RETURN_ON_ERROR;
|
|
req->name = buf;
|
|
req->member = member;
|
|
req->fp = pc->nullfp;
|
|
|
|
gdb_interface(req);
|
|
if (req->flags & GNU_COMMAND_FAILED) {
|
|
FREEBUF(req);
|
|
return -1;
|
|
}
|
|
|
|
if (!req->typecode) {
|
|
sprintf(buf, "struct %s", name);
|
|
gdb_interface(req);
|
|
}
|
|
|
|
if (!req->typecode) {
|
|
sprintf(buf, "union %s", name);
|
|
gdb_interface(req);
|
|
}
|
|
|
|
member_typecode = TYPE_CODE_UNDEF;
|
|
member_size = 0;
|
|
type_found = 0;
|
|
|
|
if (CRASHDEBUG(2)) {
|
|
if (req->typecode) {
|
|
console("name: %s ", req->name);
|
|
if (member)
|
|
console("member: %s ", req->member);
|
|
console("typecode: %d%s ", req->typecode,
|
|
req->is_typedef ? " (TYPEDEF)" : "");
|
|
console("length: %ld ", req->length);
|
|
console("member_offset: %ld\n", req->member_offset);
|
|
}
|
|
else
|
|
console("%s: unknown\n", name);
|
|
}
|
|
|
|
switch (req->typecode)
|
|
{
|
|
case TYPE_CODE_STRUCT:
|
|
type_found = STRUCT_REQUEST;
|
|
size = req->length;
|
|
if (req->member_offset >= 0) {
|
|
offset = req->member_offset/BITS_PER_BYTE;
|
|
member_size = req->member_length;
|
|
member_typecode = req->member_typecode;
|
|
} else {
|
|
offset = -1;
|
|
member_size = 0;
|
|
member_typecode = TYPE_CODE_UNDEF;
|
|
}
|
|
break;
|
|
|
|
case TYPE_CODE_UNION:
|
|
type_found = UNION_REQUEST;
|
|
size = req->length;
|
|
if (req->member_offset >= 0) {
|
|
offset = req->member_offset/BITS_PER_BYTE;
|
|
member_size = req->member_length;
|
|
member_typecode = req->member_typecode;
|
|
} else {
|
|
offset = -1;
|
|
member_size = 0;
|
|
member_typecode = TYPE_CODE_UNDEF;
|
|
}
|
|
break;
|
|
|
|
case TYPE_CODE_RANGE:
|
|
case TYPE_CODE_INT:
|
|
size = req->length;
|
|
offset = 0;
|
|
switch (size)
|
|
{
|
|
case SIZEOF_64BIT:
|
|
type_found = INT64;
|
|
break;
|
|
case SIZEOF_32BIT:
|
|
type_found = INT32;
|
|
break;
|
|
case SIZEOF_16BIT:
|
|
type_found = INT16;
|
|
break;
|
|
case SIZEOF_8BIT:
|
|
type_found = INT8;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TYPE_CODE_PTR:
|
|
size = req->length;
|
|
offset = 0;
|
|
type_found = POINTER;
|
|
break;
|
|
|
|
case TYPE_CODE_FUNC:
|
|
size = req->length;
|
|
offset = 0;
|
|
type_found = FUNCTION;
|
|
break;
|
|
|
|
case TYPE_CODE_ARRAY:
|
|
size = req->length;
|
|
offset = 0;
|
|
type_found = ARRAY;
|
|
break;
|
|
|
|
case TYPE_CODE_ENUM:
|
|
size = req->length;
|
|
offset = 0;
|
|
type_found = ENUM;
|
|
break;
|
|
|
|
default:
|
|
type_found = 0;
|
|
size = -1;
|
|
offset = -1;
|
|
break;
|
|
}
|
|
|
|
FREEBUF(req);
|
|
|
|
if (dm && (dm != MEMBER_SIZE_REQUEST) && (dm != MEMBER_TYPE_REQUEST) &&
|
|
(dm != STRUCT_SIZE_REQUEST)) {
|
|
dm->type = type_found;
|
|
dm->size = size;
|
|
dm->member_size = member_size;
|
|
dm->member_typecode = member_typecode;
|
|
dm->member_offset = offset;
|
|
if (req->is_typedef) {
|
|
dm->flags |= TYPEDEF;
|
|
}
|
|
if (req->tagname) {
|
|
dm->tagname = req->tagname;
|
|
dm->value = req->value;
|
|
}
|
|
}
|
|
|
|
if (!type_found)
|
|
return -1;
|
|
|
|
if (dm == MEMBER_SIZE_REQUEST)
|
|
return member_size;
|
|
else if (dm == MEMBER_TYPE_REQUEST)
|
|
return member_typecode;
|
|
else if (dm == STRUCT_SIZE_REQUEST) {
|
|
if ((req->typecode == TYPE_CODE_STRUCT) ||
|
|
(req->typecode == TYPE_CODE_UNION) ||
|
|
req->is_typedef)
|
|
return size;
|
|
else
|
|
return -1;
|
|
} else if (member) {
|
|
if ((req->typecode == TYPE_CODE_STRUCT) ||
|
|
(req->typecode == TYPE_CODE_UNION))
|
|
return offset;
|
|
else
|
|
return -1;
|
|
} else
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* Determine the offset of a member in an anonymous union
|
|
* in a structure or union.
|
|
*/
|
|
static long
|
|
anon_member_offset(char *name, char *member)
|
|
{
|
|
char buf[BUFSIZE];
|
|
ulong value;
|
|
int type;
|
|
|
|
value = -1;
|
|
type = STRUCT_REQUEST;
|
|
sprintf(buf, "printf \"%%p\", &((struct %s *)0x0)->%s", name, member);
|
|
open_tmpfile2();
|
|
retry:
|
|
if (gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) {
|
|
rewind(pc->tmpfile2);
|
|
if (fgets(buf, BUFSIZE, pc->tmpfile2)) {
|
|
if (hexadecimal(buf, 0))
|
|
value = htol(buf, RETURN_ON_ERROR|QUIET, NULL);
|
|
else if (STRNEQ(buf, "(nil)"))
|
|
value = 0;
|
|
}
|
|
}
|
|
|
|
if ((value == -1) && (type == STRUCT_REQUEST)) {
|
|
type = UNION_REQUEST;
|
|
sprintf(buf, "printf \"%%p\", &((union %s *)0x0)->%s", name, member);
|
|
rewind(pc->tmpfile2);
|
|
goto retry;
|
|
}
|
|
|
|
close_tmpfile2();
|
|
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* Get the basic type info for a symbol. Let the caller pass in the
|
|
* gnu_request structure to have access to the full response; in either
|
|
* case, return the type code. The member field can be used for structures
|
|
* with no type names, and if there, the member data will be filled in
|
|
* as well.
|
|
*/
|
|
int
|
|
get_symbol_type(char *name, char *member, struct gnu_request *caller_req)
|
|
{
|
|
struct gnu_request *req;
|
|
int typecode;
|
|
|
|
if (!caller_req)
|
|
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
|
|
else {
|
|
req = caller_req;
|
|
BZERO(req, sizeof(struct gnu_request));
|
|
}
|
|
|
|
req->command = GNU_GET_SYMBOL_TYPE;
|
|
req->name = name;
|
|
req->member = member;
|
|
req->flags = GNU_RETURN_ON_ERROR;
|
|
req->fp = pc->nullfp;
|
|
|
|
gdb_interface(req);
|
|
|
|
if (req->flags & GNU_COMMAND_FAILED)
|
|
typecode = TYPE_CODE_UNDEF;
|
|
else if (member) {
|
|
if (req->member_offset >= 0)
|
|
typecode = req->member_typecode;
|
|
else
|
|
typecode = TYPE_CODE_UNDEF;
|
|
} else
|
|
typecode = req->typecode;
|
|
|
|
if (!caller_req)
|
|
FREEBUF(req);
|
|
|
|
return(typecode);
|
|
}
|
|
|
|
int
|
|
get_symbol_length(char *symbol)
|
|
{
|
|
struct gnu_request *req;
|
|
int len;
|
|
|
|
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
|
|
if (get_symbol_type(symbol, NULL, req) == TYPE_CODE_UNDEF)
|
|
error(FATAL, "cannot determine length of symbol: %s\n",
|
|
symbol);
|
|
|
|
len = (int)req->length;
|
|
FREEBUF(req);
|
|
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* Initialize the caller's restore_radix, and if valid,
|
|
* temporarily override the current output radix.
|
|
*/
|
|
void
|
|
set_temporary_radix(unsigned int radix, unsigned int *restore_radix)
|
|
{
|
|
*restore_radix = *gdb_output_radix;
|
|
|
|
if ((radix == 10) || (radix == 16)) {
|
|
*gdb_output_radix = radix; \
|
|
*gdb_output_format = (*gdb_output_radix == 10) ? 0 : 'x';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore the output radix to the current/default value saved
|
|
* by the caller.
|
|
*/
|
|
void
|
|
restore_current_radix(unsigned int restore_radix)
|
|
{
|
|
if ((restore_radix == 10) || (restore_radix == 16)) {
|
|
*gdb_output_radix = restore_radix;
|
|
*gdb_output_format = (*gdb_output_radix == 10) ? 0 : 'x';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Externally available routine to dump a structure at an address.
|
|
*/
|
|
void
|
|
dump_struct(char *s, ulong addr, unsigned radix)
|
|
{
|
|
unsigned restore_radix;
|
|
long len;
|
|
|
|
restore_radix = 0;
|
|
|
|
if ((len = STRUCT_SIZE(s)) < 0)
|
|
error(FATAL, "invalid structure name: %s\n", s);
|
|
|
|
set_temporary_radix(radix, &restore_radix);
|
|
|
|
print_struct(s, addr);
|
|
|
|
restore_current_radix(restore_radix);
|
|
}
|
|
|
|
/*
|
|
* Externally available routine to dump a structure member, given the
|
|
* base structure address. The input string must be in struct.member format.
|
|
*/
|
|
void
|
|
dump_struct_member(char *s, ulong addr, unsigned radix)
|
|
{
|
|
struct datatype_member datatype_member, *dm;
|
|
unsigned restore_radix;
|
|
char *buf, *p1;
|
|
|
|
restore_radix = 0;
|
|
buf = GETBUF(strlen(s)+1);
|
|
strcpy(buf, s);
|
|
|
|
p1 = strstr(buf, ".");
|
|
*p1 = NULLCHAR;
|
|
p1++;
|
|
|
|
dm = &datatype_member;
|
|
dm->name = buf;
|
|
dm->member = p1;
|
|
|
|
if (!STRUCT_EXISTS(dm->name)) {
|
|
FREEBUF(buf);
|
|
error(FATAL, "invalid structure name: %s\n", dm->name);
|
|
}
|
|
|
|
set_temporary_radix(radix, &restore_radix);
|
|
|
|
open_tmpfile();
|
|
print_struct(dm->name, addr);
|
|
|
|
if (MEMBER_EXISTS(dm->name, dm->member))
|
|
parse_for_member(dm, PARSE_FOR_DATA);
|
|
else
|
|
parse_for_member_extended(dm, PARSE_FOR_DATA);
|
|
|
|
close_tmpfile();
|
|
|
|
restore_current_radix(restore_radix);
|
|
|
|
FREEBUF(buf);
|
|
}
|
|
|
|
|
|
/*
|
|
* Externally available routine to dump a union at an address.
|
|
*/
|
|
void
|
|
dump_union(char *s, ulong addr, unsigned radix)
|
|
{
|
|
unsigned restore_radix;
|
|
long len;
|
|
|
|
restore_radix = 0;
|
|
|
|
if ((len = UNION_SIZE(s)) < 0)
|
|
error(FATAL, "invalid union name: %s\n", s);
|
|
|
|
set_temporary_radix(radix, &restore_radix);
|
|
|
|
print_union(s, addr);
|
|
|
|
restore_current_radix(restore_radix);
|
|
}
|
|
|
|
/*
|
|
* This command displays either a structure definition, or a formatted display
|
|
* of the contents of a structure at a specified address. If no address is
|
|
* specified, the structure size and the file in which the structure is defined
|
|
* are also displayed. A structure member may be appended to the structure
|
|
* name (in a "struct.member" format) in order to limit the scope of the data
|
|
* displayed to that particular member. Structure data is shown in hexadecimal
|
|
* format. The raw data in a structure may be dumped with the -r flag.
|
|
*/
|
|
void
|
|
cmd_struct(void)
|
|
{
|
|
cmd_datatype_common(STRUCT_REQUEST);
|
|
}
|
|
/*
|
|
* This command displays either a union definition, or a formatted display
|
|
* of the contents of a union at a specified address. If no address is
|
|
* specified, the union size and the file in which the union is defined
|
|
* are also displayed. A union member may be appended to the union
|
|
* name (in a "union.member" format) in order to limit the scope of the data
|
|
* displayed to that particular member. Structure data is shown in hexadecimal
|
|
* format. The raw data in a union may be dumped with the -r flag.
|
|
*/
|
|
void
|
|
cmd_union(void)
|
|
{
|
|
cmd_datatype_common(UNION_REQUEST);
|
|
}
|
|
|
|
/*
|
|
* After determining what type of data type follows the *, this routine
|
|
* has the identical functionality as cmd_struct() or cmd_union().
|
|
*/
|
|
void
|
|
cmd_pointer(void)
|
|
{
|
|
cmd_datatype_common(0);
|
|
}
|
|
|
|
static void
|
|
print_struct_with_dereference(ulong addr, struct datatype_member *dm, ulong flags)
|
|
{
|
|
int indent;
|
|
char *p1;
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
struct datatype_member datatype_member, *dm1;
|
|
|
|
dm1 = &datatype_member;
|
|
|
|
open_tmpfile();
|
|
|
|
if (flags & UNION_REQUEST)
|
|
print_union(dm->name, addr);
|
|
else if (flags & STRUCT_REQUEST)
|
|
print_struct(dm->name, addr);
|
|
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
|
|
indent = count_leading_spaces(buf1);
|
|
if ((indent != 2) || strstr(buf1, "{") || strstr(buf1, "}")) {
|
|
print_verbatim(pc->saved_fp, buf1);
|
|
continue;
|
|
}
|
|
|
|
sprintf(buf2, "%s.", dm->name);
|
|
strcpy(buf3, &buf1[2]);
|
|
p1 = strstr(buf3, " =");
|
|
*p1 = NULLCHAR;
|
|
strcat(buf2, buf3);
|
|
|
|
if ((arg_to_datatype(buf2, dm1, RETURN_ON_ERROR) == 2) &&
|
|
dereference_pointer(addr, dm1, flags))
|
|
continue;
|
|
|
|
print_verbatim(pc->saved_fp, buf1);
|
|
}
|
|
|
|
close_tmpfile();
|
|
}
|
|
|
|
|
|
static int
|
|
dereference_pointer(ulong addr, struct datatype_member *dm, ulong flags)
|
|
{
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE*2];
|
|
char *typeptr, *member, *charptr, *voidptr, *p1, *sym;
|
|
int found, ptrptr, funcptr, typedef_is_ptr, use_symbol;
|
|
ulong target, value;
|
|
|
|
found = ptrptr = funcptr = typedef_is_ptr = use_symbol = FALSE;
|
|
member = GETBUF(strlen(dm->member)+4);
|
|
typeptr = charptr = voidptr = NULL;
|
|
|
|
open_tmpfile2();
|
|
whatis_datatype(dm->name, flags, pc->tmpfile2);
|
|
|
|
rewind(pc->tmpfile2);
|
|
while (fgets(buf1, BUFSIZE, pc->tmpfile2)) {
|
|
sprintf(member, " *%s;", dm->member);
|
|
if (strstr(buf1, member) && (buf1[4] != ' ')) {
|
|
typeptr = &buf1[4];
|
|
found++;
|
|
break;
|
|
}
|
|
sprintf(member, "**%s;", dm->member);
|
|
if (strstr(buf1, member) && (buf1[4] != ' ')) {
|
|
typeptr = &buf1[4];
|
|
found++;
|
|
ptrptr = TRUE;
|
|
break;
|
|
}
|
|
sprintf(member, "(*%s)(", dm->member);
|
|
if (strstr(buf1, member) && (buf1[4] != ' ')) {
|
|
typeptr = &buf1[4];
|
|
funcptr = TRUE;
|
|
found++;
|
|
break;
|
|
}
|
|
sprintf(member, " %s;", dm->member);
|
|
if (strstr(buf1, member) && (buf1[4] != ' ')) {
|
|
typeptr = &buf1[4];
|
|
typedef_is_ptr = TRUE;
|
|
strcpy(buf2, typeptr);
|
|
p1 = strstr(buf2, " ");
|
|
*p1 = NULLCHAR;
|
|
if (datatype_exists(buf2) == TYPE_CODE_PTR) {
|
|
found++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
close_tmpfile2();
|
|
FREEBUF(member);
|
|
|
|
if (!found) {
|
|
console("%s.%s: not found!\n", dm->name, dm->member);
|
|
return FALSE;
|
|
}
|
|
|
|
if (funcptr) {
|
|
p1 = strstr(buf1, ";");
|
|
*p1 = NULLCHAR;
|
|
} else if (ptrptr) {
|
|
p1 = strstr(buf1, "**");
|
|
*(p1+2) = NULLCHAR;
|
|
charptr = voidptr = NULL;
|
|
} else if (typedef_is_ptr) {
|
|
p1 = strstr(typeptr, " ");
|
|
*p1 = NULLCHAR;
|
|
} else {
|
|
p1 = strstr(buf1, "*");
|
|
*(p1+1) = NULLCHAR;
|
|
charptr = strstr(&buf1[4], "char *");
|
|
voidptr = strstr(&buf1[4], "void *");
|
|
}
|
|
|
|
console("%s.%s typeptr: %s ",
|
|
dm->name, dm->member,
|
|
typeptr);
|
|
if (charptr)
|
|
console("[char *]");
|
|
else if (voidptr)
|
|
console("[void *]");
|
|
else if (funcptr)
|
|
console("[func *]");
|
|
else if (typedef_is_ptr)
|
|
console("[typedef is ptr]");
|
|
console("\n");
|
|
|
|
if (!readmem(addr + dm->member_offset, KVADDR,
|
|
&target, sizeof(void *), "target address",
|
|
RETURN_ON_ERROR|QUIET)) {
|
|
error(INFO, "cannot access %s.%s %lx\n",
|
|
dm->name, dm->member,
|
|
addr + dm->member_offset);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((sym = value_symbol(target))) {
|
|
switch (get_symbol_type(sym, NULL, NULL))
|
|
{
|
|
case TYPE_CODE_ARRAY:
|
|
case TYPE_CODE_UNION:
|
|
case TYPE_CODE_STRUCT:
|
|
case TYPE_CODE_INT:
|
|
case TYPE_CODE_PTR:
|
|
use_symbol = TRUE;
|
|
console("use_symbol: %s\n", sym);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (funcptr) {
|
|
fprintf(pc->saved_fp, " %s = 0x%lx\n -> ",
|
|
typeptr, target);
|
|
if (sym)
|
|
fprintf(pc->saved_fp, "<%s>\n", sym);
|
|
else if (target)
|
|
fprintf(pc->saved_fp, "(unknown)\n");
|
|
else
|
|
fprintf(pc->saved_fp, "NULL\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if (charptr) {
|
|
fprintf(pc->saved_fp, " %s%s = 0x%lx\n -> ", typeptr, dm->member,
|
|
target);
|
|
if (sym)
|
|
fprintf(pc->saved_fp, "<%s> ", sym);
|
|
if (!target)
|
|
fprintf(pc->saved_fp, "NULL\n");
|
|
else if (!accessible(target) || !read_string(target, buf1, BUFSIZE-1))
|
|
fprintf(pc->saved_fp, "(not accessible)\n");
|
|
else
|
|
fprintf(pc->saved_fp, "\"%s\"\n", buf1);
|
|
return TRUE;
|
|
}
|
|
|
|
if (voidptr && !use_symbol) {
|
|
fprintf(pc->saved_fp, " %s%s = 0x%lx\n -> ", typeptr, dm->member,
|
|
target);
|
|
if (sym)
|
|
fprintf(pc->saved_fp, "<%s>\n", sym);
|
|
else if (!target)
|
|
fprintf(pc->saved_fp, "NULL\n");
|
|
else if (voidptr)
|
|
fprintf(pc->saved_fp, "(unknown target type)\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if (!target || !accessible(target)) {
|
|
fprintf(pc->saved_fp, " %s%s%s = 0x%lx\n -> ", typeptr,
|
|
typedef_is_ptr ? " " : "", dm->member, target);
|
|
if (!target)
|
|
fprintf(pc->saved_fp, "NULL\n");
|
|
else
|
|
fprintf(pc->saved_fp, "(not accessible)\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if (ptrptr) {
|
|
fprintf(pc->saved_fp, " %s%s = 0x%lx\n -> ", typeptr, dm->member,
|
|
target);
|
|
if (sym)
|
|
fprintf(pc->saved_fp, "<%s> ", sym);
|
|
if (!target ||
|
|
!readmem(target, KVADDR, &value, sizeof(void *),
|
|
"target value", RETURN_ON_ERROR|QUIET))
|
|
fprintf(pc->saved_fp, "\n");
|
|
else
|
|
fprintf(pc->saved_fp, "%lx\n", value);
|
|
return TRUE;
|
|
}
|
|
|
|
if (use_symbol)
|
|
sprintf(buf2, "p %s\n", sym);
|
|
else
|
|
sprintf(buf2, "p *((%s)(0x%lx))\n", typeptr, target);
|
|
console("gdb command: %s", buf2);
|
|
|
|
if (!typedef_is_ptr) {
|
|
p1 = strstr(typeptr, "*");
|
|
*(p1-1) = NULLCHAR;
|
|
}
|
|
|
|
if (!datatype_exists(typeptr)) {
|
|
fprintf(pc->saved_fp,
|
|
" %s %s%s = 0x%lx\n -> (%s: no debuginfo data)\n",
|
|
typeptr, typedef_is_ptr ? "" : "*", dm->member, target,
|
|
typeptr);
|
|
return TRUE;
|
|
}
|
|
|
|
open_tmpfile2();
|
|
if (!gdb_pass_through(buf2, pc->tmpfile2, GNU_RETURN_ON_ERROR)) {
|
|
console("gdb request failed: %s\n", buf2);
|
|
close_tmpfile2();
|
|
return FALSE;
|
|
}
|
|
|
|
fprintf(pc->saved_fp, " %s %s%s = 0x%lx\n -> ", typeptr,
|
|
typedef_is_ptr ? "" : "*", dm->member, target);
|
|
|
|
rewind(pc->tmpfile2);
|
|
while (fgets(buf1, BUFSIZE, pc->tmpfile2)) {
|
|
if (buf1[0] == '$') {
|
|
if (sym)
|
|
fprintf(pc->saved_fp, "<%s> ", sym);
|
|
if (typedef_is_ptr || use_symbol) {
|
|
if (strstr(buf1, "(") && strstr(buf1, ")")) {
|
|
fprintf(pc->saved_fp, "\n");
|
|
break;
|
|
}
|
|
}
|
|
p1 = strstr(buf1, "=");
|
|
fprintf(pc->saved_fp, "%s", p1+2);
|
|
} else
|
|
fprintf(pc->saved_fp, " %s", buf1);
|
|
}
|
|
|
|
close_tmpfile2();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
cmd_datatype_common(ulong flags)
|
|
{
|
|
int c;
|
|
ulong addr, aflag;
|
|
char *cpuspec;
|
|
ulong *cpus;
|
|
struct syment *sp;
|
|
ulong list_head_offset;
|
|
int count;
|
|
int argc_members;
|
|
int optind_save;
|
|
unsigned int radix, restore_radix;
|
|
struct datatype_member datatype_member, *dm;
|
|
char *separator;
|
|
char *structname, *members;
|
|
char *memberlist[MAXARGS];
|
|
char *typename;
|
|
|
|
dm = &datatype_member;
|
|
count = 0xdeadbeef;
|
|
aflag = addr = 0;
|
|
list_head_offset = 0;
|
|
argc_members = 0;
|
|
radix = restore_radix = 0;
|
|
separator = members = NULL;
|
|
cpuspec = NULL;
|
|
cpus = NULL;
|
|
|
|
while ((c = getopt(argcnt, args, "pxdhfuc:rvol:")) != EOF) {
|
|
switch (c)
|
|
{
|
|
case 'p':
|
|
flags |= DEREF_POINTERS;
|
|
break;
|
|
|
|
case 'd':
|
|
if (radix == 16)
|
|
error(FATAL,
|
|
"-d and -x are mutually exclusive\n");
|
|
radix = 10;
|
|
break;
|
|
|
|
case 'h':
|
|
case 'x':
|
|
if (radix == 10)
|
|
error(FATAL,
|
|
"-d and -x are mutually exclusive\n");
|
|
radix = 16;
|
|
break;
|
|
|
|
case 'c':
|
|
count = atoi(optarg);
|
|
break;
|
|
|
|
case 'r':
|
|
flags |= SHOW_RAW_DATA;
|
|
break;
|
|
|
|
case 'v':
|
|
flags |= STRUCT_VERBOSE;
|
|
break;
|
|
|
|
case 'o':
|
|
flags |= SHOW_OFFSET;
|
|
break;
|
|
|
|
case 'l':
|
|
if (IS_A_NUMBER(optarg))
|
|
list_head_offset = stol(optarg,
|
|
FAULT_ON_ERROR, NULL);
|
|
else if (arg_to_datatype(optarg,
|
|
dm, RETURN_ON_ERROR) > 1)
|
|
list_head_offset = dm->member_offset;
|
|
else
|
|
error(FATAL, "invalid -l option: %s\n",
|
|
optarg);
|
|
break;
|
|
|
|
case 'f':
|
|
if (!pc->dumpfile)
|
|
error(FATAL,
|
|
"-f option requires a dumpfile\n");
|
|
pc->curcmd_flags |= MEMTYPE_FILEADDR;
|
|
break;
|
|
|
|
case 'u':
|
|
pc->curcmd_flags |= MEMTYPE_UVADDR;
|
|
break;
|
|
|
|
default:
|
|
argerrs++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argerrs || !args[optind])
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
if ((count_chars(args[optind], ',')+1) > MAXARGS)
|
|
error(FATAL, "too many members in comma-separated list!\n");
|
|
|
|
if ((LASTCHAR(args[optind]) == ',') ||
|
|
(LASTCHAR(args[optind]) == '.'))
|
|
error(FATAL, "invalid format: %s\n", args[optind]);
|
|
|
|
optind_save = optind;
|
|
|
|
/*
|
|
* Take care of address and count (array).
|
|
*/
|
|
while (args[++optind]) {
|
|
if (aflag && (count != 0xdeadbeef))
|
|
error(FATAL, "too many arguments!\n");
|
|
|
|
if (!aflag) {
|
|
cpuspec = strchr(args[optind], ':');
|
|
if (cpuspec)
|
|
*cpuspec++ = NULLCHAR;
|
|
}
|
|
|
|
if (clean_arg() && IS_A_NUMBER(args[optind])) {
|
|
if (aflag)
|
|
count = stol(args[optind],
|
|
FAULT_ON_ERROR, NULL);
|
|
else if (cpuspec) {
|
|
if (pc->curcmd_flags & MEMTYPE_FILEADDR)
|
|
error(FATAL, "-f option cannot be used with percpu\n");
|
|
addr = htol(args[optind], FAULT_ON_ERROR, NULL);
|
|
aflag++;
|
|
} else {
|
|
if (pc->curcmd_flags & MEMTYPE_FILEADDR)
|
|
pc->curcmd_private = stoll(args[optind],
|
|
FAULT_ON_ERROR, NULL);
|
|
else if (pc->curcmd_flags & MEMTYPE_UVADDR) {
|
|
addr = htol(args[optind], FAULT_ON_ERROR,
|
|
NULL);
|
|
} else if (!IS_KVADDR(addr = htol(args[optind],
|
|
FAULT_ON_ERROR, NULL)))
|
|
error(FATAL,
|
|
"invalid kernel virtual address: %s\n",
|
|
args[optind]);
|
|
aflag++;
|
|
}
|
|
} else if ((sp = symbol_search(args[optind]))) {
|
|
if (cpuspec && !is_percpu_symbol(sp)) {
|
|
error(WARNING,
|
|
"%s is not percpu; cpuspec ignored.\n",
|
|
sp->name);
|
|
cpuspec = NULL;
|
|
}
|
|
if (cpuspec) {
|
|
if ((typename = expr_type_name(sp->name))) {
|
|
if (LASTCHAR(typename) == '*')
|
|
error(WARNING,
|
|
"percpu symbol \"%s\" is of type pointer\n",
|
|
sp->name);
|
|
FREEBUF(typename);
|
|
}
|
|
}
|
|
addr = sp->value;
|
|
aflag++;
|
|
} else {
|
|
fprintf(fp, "symbol not found: %s\n", args[optind]);
|
|
fprintf(fp, "possible alternatives:\n");
|
|
if (!symbol_query(args[optind], " ", NULL))
|
|
fprintf(fp, " (none found)\n");
|
|
goto freebuf;
|
|
}
|
|
}
|
|
|
|
if (cpuspec) {
|
|
cpus = get_cpumask_buf();
|
|
if (STREQ(cpuspec, ""))
|
|
SET_BIT(cpus, CURRENT_CONTEXT()->processor);
|
|
else
|
|
make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL);
|
|
}
|
|
|
|
optind = optind_save;
|
|
|
|
if (count == 0xdeadbeef)
|
|
count = 1;
|
|
else if (!aflag)
|
|
error(FATAL, "no kernel virtual address argument entered\n");
|
|
|
|
if ((flags & DEREF_POINTERS) && !aflag)
|
|
error(FATAL, "-p option requires address argument\n");
|
|
|
|
if (list_head_offset)
|
|
addr -= list_head_offset;
|
|
|
|
/*
|
|
* Handle struct.member[,member] argument format.
|
|
*/
|
|
if (strstr(args[optind], ".")) {
|
|
structname = GETBUF(strlen(args[optind])+1);
|
|
strcpy(structname, args[optind]);
|
|
separator = strstr(structname, ".");
|
|
|
|
members = GETBUF(strlen(args[optind])+1);
|
|
strcpy(members, separator+1);
|
|
replace_string(members, ",", ' ');
|
|
argc_members = parse_line(members, memberlist);
|
|
} else
|
|
structname = args[optind];
|
|
|
|
if ((arg_to_datatype(structname, dm,
|
|
DATATYPE_QUERY|ANON_MEMBER_QUERY|RETURN_ON_ERROR) < 1))
|
|
error(FATAL, "invalid data structure reference: %s\n", structname);
|
|
|
|
if (! (flags & (STRUCT_REQUEST|UNION_REQUEST)) ) {
|
|
flags |= dm->type;
|
|
if (!(flags & (UNION_REQUEST|STRUCT_REQUEST)))
|
|
error(FATAL, "invalid argument");
|
|
} else if ( (flags &(STRUCT_REQUEST|UNION_REQUEST)) != dm->type) {
|
|
error(FATAL, "data type mismatch: %s is not a %s\n",
|
|
dm->name, flags & UNION_REQUEST ? "union" : "struct");
|
|
}
|
|
|
|
if ((argc_members > 1) && !aflag) {
|
|
error(INFO, flags & SHOW_OFFSET ?
|
|
"-o option not valid with multiple member format\n" :
|
|
"multiple member format not supported in this syntax\n");
|
|
*separator = NULLCHAR;
|
|
argc_members = 0;
|
|
flags |= SHOW_OFFSET;
|
|
}
|
|
|
|
if ((argc_members > 1) && aflag && (flags & SHOW_OFFSET))
|
|
error(FATAL,
|
|
"-o option not valid with multiple member format\n");
|
|
|
|
set_temporary_radix(radix, &restore_radix);
|
|
|
|
/*
|
|
* No address was passed -- dump the structure/member declaration.
|
|
*/
|
|
if (!aflag) {
|
|
if (argc_members &&
|
|
!member_to_datatype(memberlist[0], dm,
|
|
ANON_MEMBER_QUERY))
|
|
error(FATAL, "invalid data structure reference: %s.%s\n",
|
|
dm->name, memberlist[0]);
|
|
do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF));
|
|
} else if (cpus) {
|
|
for (c = 0; c < kt->cpus; c++) {
|
|
ulong cpuaddr;
|
|
|
|
if (!NUM_IN_BITMAP(cpus, c))
|
|
continue;
|
|
|
|
cpuaddr = addr + kt->__per_cpu_offset[c];
|
|
|
|
fprintf(fp, "[%d]: ", c);
|
|
|
|
if (hide_offline_cpu(c)) {
|
|
fprintf(fp, "[OFFLINE]\n");
|
|
continue;
|
|
}
|
|
|
|
fprintf(fp, "%lx\n", cpuaddr);
|
|
do_datatype_addr(dm, cpuaddr , count,
|
|
flags, memberlist, argc_members);
|
|
}
|
|
} else
|
|
do_datatype_addr(dm, addr, count, flags,
|
|
memberlist, argc_members);
|
|
|
|
restore_current_radix(restore_radix);
|
|
|
|
freebuf:
|
|
if (argc_members) {
|
|
FREEBUF(structname);
|
|
FREEBUF(members);
|
|
}
|
|
|
|
if (cpus)
|
|
FREEBUF(cpus);
|
|
}
|
|
|
|
static void
|
|
do_datatype_addr(struct datatype_member *dm, ulong addr, int count,
|
|
ulong flags, char **memberlist, int argc_members)
|
|
{
|
|
int i, c;
|
|
long len = dm->size;
|
|
|
|
if (count < 0) {
|
|
addr -= len * abs(count);
|
|
addr += len;
|
|
}
|
|
|
|
if (pc->curcmd_flags & MEMTYPE_FILEADDR)
|
|
addr = 0; /* unused, but parsed by gdb */
|
|
|
|
for (c = 0; c < abs(count); c++, addr += len, pc->curcmd_private += len) {
|
|
if (c)
|
|
fprintf(fp,"\n");
|
|
|
|
i = 0;
|
|
do {
|
|
if (argc_members) {
|
|
/* This call works fine with fields
|
|
* of the second, third, ... levels.
|
|
* There is no need to fix it
|
|
*/
|
|
if (!member_to_datatype(memberlist[i], dm,
|
|
ANON_MEMBER_QUERY))
|
|
error(FATAL, "invalid data structure reference: %s.%s\n",
|
|
dm->name, memberlist[i]);
|
|
if (flags & SHOW_RAW_DATA)
|
|
error(FATAL,
|
|
"member-specific output not allowed with -r\n");
|
|
}
|
|
|
|
/*
|
|
* Display member addresses or data
|
|
*/
|
|
if (flags & SHOW_OFFSET) {
|
|
dm->vaddr = addr;
|
|
do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF));
|
|
} else if (flags & SHOW_RAW_DATA)
|
|
raw_data_dump(addr, len, flags & STRUCT_VERBOSE);
|
|
else if ((flags & DEREF_POINTERS) && !dm->member) {
|
|
print_struct_with_dereference(addr, dm, flags);
|
|
} else {
|
|
if (dm->member)
|
|
open_tmpfile();
|
|
|
|
if (flags & UNION_REQUEST)
|
|
print_union(dm->name, addr);
|
|
else if (flags & STRUCT_REQUEST)
|
|
print_struct(dm->name, addr);
|
|
|
|
if (dm->member) {
|
|
if (!((flags & DEREF_POINTERS) &&
|
|
dereference_pointer(addr, dm, flags))) {
|
|
if (count_chars(dm->member, '.') || count_chars(dm->member, '['))
|
|
parse_for_member_extended(dm, PARSE_FOR_DATA);
|
|
else
|
|
parse_for_member(dm, PARSE_FOR_DATA);
|
|
}
|
|
close_tmpfile();
|
|
}
|
|
|
|
}
|
|
} while (++i < argc_members);
|
|
}
|
|
}
|
|
|
|
int
|
|
is_string(char *structure, char *member)
|
|
{
|
|
int retval;
|
|
char *t;
|
|
char buf[BUFSIZE];
|
|
|
|
retval = FALSE;
|
|
open_tmpfile();
|
|
whatis_datatype(structure, STRUCT_REQUEST, pc->tmpfile);
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
if (!(t = strstr(buf, "char ")))
|
|
continue;
|
|
t += 5;
|
|
if (*t == '*')
|
|
t++;
|
|
if (t != strstr(t, member))
|
|
continue;
|
|
t += strlen(member);
|
|
if (*t == ';' || *t == '[') {
|
|
retval = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
close_tmpfile();
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* Generic function for dumping data structure declarations, with a small
|
|
* fixup for typedefs, sizes and member offsets.
|
|
*/
|
|
static void
|
|
do_datatype_declaration(struct datatype_member *dm, ulong flags)
|
|
{
|
|
long len;
|
|
char buf[BUFSIZE];
|
|
char *p1, *p2, *multiline;
|
|
FILE *sfp;
|
|
|
|
if (CRASHDEBUG(1))
|
|
dump_datatype_member(fp, dm);
|
|
|
|
if (dm->member && count_chars(dm->member, '.'))
|
|
error(FATAL, "invalid data structure reference: %s.%s\n",
|
|
dm->name, dm->member);
|
|
|
|
open_tmpfile();
|
|
whatis_datatype(dm->name, flags, pc->tmpfile);
|
|
rewind(pc->tmpfile);
|
|
|
|
if (dm->member)
|
|
flags |= SHOW_OFFSET;
|
|
|
|
sfp = pc->saved_fp;
|
|
len = dm->size;
|
|
multiline = NULL;
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
if (STRNEQ(buf, "type = ")) {
|
|
multiline = strstr(buf, "{");
|
|
if (flags & TYPEDEF)
|
|
fprintf(sfp, "typedef ");
|
|
p1 = buf + strlen("type = ");
|
|
if ((p2 = strstr(buf, "(*)()"))) {
|
|
*p2 = NULLCHAR;
|
|
fprintf(sfp, "%s(*%s)();\n",
|
|
p1, dm->name);
|
|
} else if ((p2 = strstr(buf, "()"))) {
|
|
*p2 = NULLCHAR;
|
|
fprintf(sfp, "%s(%s)();\n", p1, dm->name);
|
|
} else if (multiline)
|
|
fprintf(sfp, "%s", p1);
|
|
else
|
|
fprintf(sfp, "%s %s;\n",
|
|
strip_linefeeds(p1), dm->name);
|
|
} else {
|
|
if (multiline && STRNEQ(buf, "}") && (flags & TYPEDEF)){
|
|
if (strstr(buf, "} **()"))
|
|
fprintf(sfp, "} **(%s)();\n", dm->name);
|
|
else
|
|
fprintf(sfp, "%s %s;\n",
|
|
strip_linefeeds(buf), dm->name);
|
|
} else {
|
|
if ((flags & SHOW_OFFSET) && whitespace(buf[0]))
|
|
show_member_offset(sfp, dm, buf);
|
|
else
|
|
fprintf(sfp, "%s", buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!dm->member) {
|
|
switch (*gdb_output_radix)
|
|
{
|
|
default:
|
|
case 10:
|
|
fprintf(sfp, "SIZE: %ld\n", len);
|
|
break;
|
|
case 16:
|
|
fprintf(sfp, "SIZE: 0x%lx\n", len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
close_tmpfile();
|
|
}
|
|
|
|
|
|
/*
|
|
* Take a argument string, which may be in "struct.member" or "union.member"
|
|
* format, figure out whether it's a structure or a union reference, and
|
|
* fill in the appropriate fields of the dataytype_member structure.
|
|
* Return 1 if it's a straight struct or union reference, 2 if it has
|
|
* a legitimate .member attached to it, or 0 if it's bogus.
|
|
*/
|
|
int
|
|
arg_to_datatype(char *s, struct datatype_member *dm, ulong flags)
|
|
{
|
|
char *p1;
|
|
int both;
|
|
|
|
BZERO(dm, sizeof(struct datatype_member));
|
|
both = FALSE;
|
|
|
|
dm->name = s;
|
|
|
|
if (!(p1 = strstr(s, ".")))
|
|
both = FALSE;
|
|
else if (flags & DATATYPE_QUERY) {
|
|
*p1 = NULLCHAR;
|
|
both = FALSE;
|
|
} else {
|
|
if ((p1 == s) || !strlen(p1+1))
|
|
goto datatype_member_fatal;
|
|
*p1 = NULLCHAR;
|
|
if (strstr(p1+1, "."))
|
|
goto datatype_member_fatal;
|
|
|
|
both = TRUE;
|
|
}
|
|
|
|
if ((dm->size = DATATYPE_SIZE(dm)) < 0) {
|
|
if (flags & RETURN_ON_ERROR)
|
|
goto datatype_member_fatal;
|
|
|
|
error(FATAL,
|
|
"cannot handle \"%s\": try \"gdb whatis\" or \"gdb ptype\"\n", s);
|
|
}
|
|
|
|
if (!both)
|
|
return 1;
|
|
|
|
if (member_to_datatype(p1 + 1, dm, flags))
|
|
return 2;
|
|
|
|
datatype_member_fatal:
|
|
|
|
if (flags & RETURN_ON_ERROR) {
|
|
if (both)
|
|
*p1 = '.';
|
|
return 0;
|
|
}
|
|
|
|
if (both) {
|
|
*p1 = '.';
|
|
if (strstr(p1+1, "."))
|
|
error(FATAL, "only one %s member allowed: %s\n",
|
|
(dm->type == STRUCT_REQUEST) ? "struct" :
|
|
((dm->type == UNION_REQUEST) ?
|
|
"union" : "struct/union"), s);
|
|
}
|
|
|
|
return (error(FATAL, "invalid argument: %s\n", s));
|
|
}
|
|
|
|
static int
|
|
member_to_datatype(char *s, struct datatype_member *dm, ulong flags)
|
|
{
|
|
dm->member = s;
|
|
|
|
if ((dm->member_offset = MEMBER_OFFSET(dm->name, s)) >= 0)
|
|
return TRUE;
|
|
|
|
if ((flags & ANON_MEMBER_QUERY) &&
|
|
((dm->member_offset = ANON_MEMBER_OFFSET(dm->name, s)) >= 0))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* debug routine -- not called on purpose by anybody.
|
|
*/
|
|
static void
|
|
dump_datatype_member(FILE *ofp, struct datatype_member *dm)
|
|
{
|
|
int others;
|
|
|
|
others = 0;
|
|
fprintf(ofp, " name: %s\n", dm->name);
|
|
fprintf(ofp, " member: %s\n", dm->member);
|
|
fprintf(ofp, " type: %lx (", dm->type);
|
|
if (dm->type & STRUCT_REQUEST)
|
|
fprintf(ofp, "%sSTRUCT_REQUEST", others++ ? "|" : "");
|
|
if (dm->type & UNION_REQUEST)
|
|
fprintf(fp, "%sUNION_REQUEST", others++ ? "|" : "");
|
|
if (dm->type & INT64)
|
|
fprintf(ofp, "%sINT64", others++ ? "|" : "");
|
|
if (dm->type & INT32)
|
|
fprintf(ofp, "%sINT32", others++ ? "|" : "");
|
|
if (dm->type & INT16)
|
|
fprintf(ofp, "%sINT16", others++ ? "|" : "");
|
|
if (dm->type & INT8)
|
|
fprintf(ofp, "%sINT8", others++ ? "|" : "");
|
|
if (dm->type & POINTER)
|
|
fprintf(ofp, "%sPOINTER", others++ ? "|" : "");
|
|
if (dm->type & FUNCTION)
|
|
fprintf(ofp, "%sFUNCTION", others++ ? "|" : "");
|
|
if (dm->type & ARRAY)
|
|
fprintf(ofp, "%sARRAY", others++ ? "|" : "");
|
|
if (dm->type & ENUM)
|
|
fprintf(ofp, "%sENUM", others++ ? "|" : "");
|
|
if (dm->type & IN_UNION)
|
|
fprintf(ofp, "%sIN_UNION", others++ ? "|" : "");
|
|
if (dm->type & IN_STRUCT)
|
|
fprintf(ofp, "%sIN_STRUCT", others++ ? "|" : "");
|
|
fprintf(ofp, ")\n");
|
|
fprintf(ofp, " size: %ld\n", dm->size);
|
|
fprintf(ofp, " member_offset: %ld\n", dm->member_offset);
|
|
fprintf(ofp, " member_size: %ld\n", dm->member_size);
|
|
fprintf(ofp, "member_typecode: %d\n", dm->member_typecode);
|
|
fprintf(ofp, " flags: %lx ", dm->flags);
|
|
dump_datatype_flags(dm->flags, ofp);
|
|
fprintf(ofp, " tagname: %s\n", dm->tagname);
|
|
fprintf(ofp, " value: %ld\n", dm->value);
|
|
fprintf(ofp, " vaddr: %lx\n", dm->vaddr);
|
|
fprintf(ofp, "\n");
|
|
}
|
|
|
|
struct type_request {
|
|
int cnt; /* current number of entries in types array */
|
|
int idx; /* index to next entry in types array */
|
|
struct type_info { /* dynamically-sized array of collected types */
|
|
char *name;
|
|
ulong size;
|
|
} *types;
|
|
};
|
|
|
|
static int
|
|
compare_size_name(const void *va, const void *vb) {
|
|
struct type_info *a, *b;
|
|
|
|
a = (struct type_info *)va;
|
|
b = (struct type_info *)vb;
|
|
|
|
if (a->size == b->size)
|
|
return strcmp(a->name, b->name);
|
|
else
|
|
return a->size < b->size ? -1 : 1;
|
|
}
|
|
|
|
static void
|
|
append_struct_symbol (struct type_request *treq, struct gnu_request *req)
|
|
{
|
|
int i;
|
|
long s;
|
|
|
|
for (i = 0; i < treq->idx; i++)
|
|
if (treq->types[i].name == req->name)
|
|
break;
|
|
|
|
if (i < treq->idx) // We've already collected this type
|
|
return;
|
|
|
|
if (treq->idx == treq->cnt) {
|
|
s = sizeof(struct type_info) * treq->cnt;
|
|
RESIZEBUF(treq->types, s, s * 3);
|
|
treq->cnt *= 3;
|
|
}
|
|
|
|
treq->types[treq->idx].name = req->name;
|
|
treq->types[treq->idx].size = req->length;
|
|
treq->idx++;
|
|
}
|
|
|
|
static void
|
|
request_types(ulong lowest, ulong highest, char *member_name)
|
|
{
|
|
int i, len;
|
|
char buf[BUFSIZE];
|
|
struct type_request typereq;
|
|
struct gnu_request request = {0};
|
|
|
|
typereq.idx = 0;
|
|
typereq.cnt = 16;
|
|
typereq.types = (void *)GETBUF(16 * sizeof(struct type_info));
|
|
|
|
#if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) || defined(GDB_7_0)
|
|
error(FATAL, "-r option not supported with this version of gdb\n");
|
|
#else
|
|
request.type_name = member_name;
|
|
#endif
|
|
|
|
while (!request.global_iterator.finished) {
|
|
request.command = GNU_GET_NEXT_DATATYPE;
|
|
gdb_interface(&request);
|
|
if (highest &&
|
|
!(lowest <= request.length && request.length <= highest))
|
|
continue;
|
|
|
|
if (member_name) {
|
|
request.command = GNU_LOOKUP_STRUCT_CONTENTS;
|
|
gdb_interface(&request);
|
|
if (!request.value)
|
|
continue;
|
|
}
|
|
|
|
append_struct_symbol(&typereq, &request);
|
|
}
|
|
|
|
qsort(typereq.types, typereq.idx, sizeof(struct type_info), compare_size_name);
|
|
|
|
if (typereq.idx == 0)
|
|
fprintf(fp, "(none found)\n");
|
|
else {
|
|
sprintf(buf, "%ld", typereq.types[typereq.idx-1].size);
|
|
len = MAX(strlen(buf), strlen("SIZE"));
|
|
fprintf(fp, "%s TYPE\n",
|
|
mkstring(buf, len, RJUST, "SIZE"));
|
|
|
|
for (i = 0; i < typereq.idx; i++)
|
|
fprintf(fp, "%s %s\n",
|
|
mkstring(buf, len, RJUST|LONG_DEC,
|
|
MKSTR(typereq.types[i].size)),
|
|
typereq.types[i].name);
|
|
}
|
|
|
|
FREEBUF(typereq.types);
|
|
}
|
|
|
|
/*
|
|
* This command displays the definition of structures, unions, typedefs or
|
|
* text/data symbols:
|
|
*
|
|
* 1. For a structure name, the output is the same as if the "struct"
|
|
* command was used.
|
|
* 2. For a union name, the output is the same as if the "union" command
|
|
* was used.
|
|
* 3. For a typedef name that translates to a structure or union, the output
|
|
* is the same as if the "struct" or "union" command was used.
|
|
* 4. For a typedef name that translates to a primitive datatype, the one-line
|
|
* declaration is displayed.
|
|
* 5. For a kernel symbol name, the output is the same as if the "sym" command
|
|
* was used.
|
|
* 6. If the -r and -m are given, then the structures/unions of specified size
|
|
* and/or contain a member type.
|
|
*/
|
|
void
|
|
cmd_whatis(void)
|
|
{
|
|
int c, do_request;
|
|
struct datatype_member datatype_member, *dm;
|
|
struct syment *sp;
|
|
char buf[BUFSIZE], *pl, *ph, *member;
|
|
long len;
|
|
ulong lowest, highest;
|
|
ulong flags;
|
|
|
|
dm = &datatype_member;
|
|
flags = 0;
|
|
lowest = highest = 0;
|
|
pl = buf;
|
|
member = NULL;
|
|
do_request = FALSE;
|
|
|
|
while ((c = getopt(argcnt, args, "om:r:")) != EOF) {
|
|
switch(c)
|
|
{
|
|
case 'o':
|
|
flags |= SHOW_OFFSET;
|
|
break;
|
|
|
|
case 'm':
|
|
member = optarg;
|
|
do_request = TRUE;
|
|
break;
|
|
|
|
case 'r':
|
|
strncpy(buf, optarg, 15);
|
|
if ((ph = strstr(buf, "-")) != NULL)
|
|
*(ph++) = '\0';
|
|
highest = lowest = stol(pl, FAULT_ON_ERROR, NULL);
|
|
if (ph)
|
|
highest = stol(ph, FAULT_ON_ERROR, NULL);
|
|
do_request = TRUE;
|
|
break;
|
|
|
|
default:
|
|
argerrs++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!argerrs && do_request) {
|
|
request_types(lowest, highest, member);
|
|
return;
|
|
}
|
|
|
|
if (argerrs || !args[optind])
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
if (STREQ(args[optind], "struct") ||
|
|
STREQ(args[optind], "union") ||
|
|
STREQ(args[optind], "enum"))
|
|
optind++;
|
|
else if ((sp = symbol_search(args[optind]))) {
|
|
whatis_variable(sp);
|
|
return;
|
|
}
|
|
|
|
if (!args[optind])
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
if (arg_to_datatype(args[optind], dm, RETURN_ON_ERROR)) {
|
|
if ((len = dm->size) < 0)
|
|
goto whatis_failure;
|
|
|
|
flags |= dm->type;
|
|
|
|
if (dm->type == ENUM) {
|
|
if (dm->tagname)
|
|
fprintf(fp, "%senum%s%s = %ld\n",
|
|
dm->flags & TYPEDEF ? "typedef " : "",
|
|
strlen(dm->tagname) ? " " : "",
|
|
dm->tagname, dm->value);
|
|
else
|
|
dump_enumerator_list(args[optind]);
|
|
|
|
return;
|
|
}
|
|
|
|
do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF));
|
|
} else {
|
|
if (!gdb_whatis(concat_args(buf, 1, FALSE)))
|
|
goto whatis_failure;
|
|
}
|
|
|
|
return;
|
|
|
|
whatis_failure:
|
|
|
|
error(INFO, "cannot resolve: %s\n", concat_args(buf, 1, FALSE));
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
}
|
|
|
|
/*
|
|
* Try gdb's whatis on a command string.
|
|
*/
|
|
static int
|
|
gdb_whatis(char *s)
|
|
{
|
|
char buf[BUFSIZE], *p1;
|
|
|
|
open_tmpfile();
|
|
sprintf(buf, "whatis %s", s);
|
|
if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) {
|
|
close_tmpfile();
|
|
return FALSE;
|
|
}
|
|
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
p1 = buf;
|
|
if (STRNEQ(buf, "type = "))
|
|
p1 += strlen("type = ");
|
|
fprintf(pc->saved_fp, "%s", p1);
|
|
}
|
|
|
|
close_tmpfile();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Given the name of an enum, have gdb dump its enumerator list.
|
|
*/
|
|
int
|
|
dump_enumerator_list(char *e)
|
|
{
|
|
struct gnu_request *req;
|
|
struct datatype_member datatype_member, *dm;
|
|
dm = &datatype_member;
|
|
|
|
if (!arg_to_datatype(e, dm, RETURN_ON_ERROR) ||
|
|
(dm->size < 0) || (dm->type != ENUM) || dm->tagname)
|
|
return FALSE;
|
|
|
|
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
|
|
req->command = GNU_GET_DATATYPE;
|
|
req->name = e;
|
|
req->flags = GNU_PRINT_ENUMERATORS;
|
|
|
|
gdb_interface(req);
|
|
|
|
FREEBUF(req);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Given the name of an enum, return its value.
|
|
*/
|
|
int
|
|
enumerator_value(char *e, long *value)
|
|
{
|
|
struct datatype_member datatype_member, *dm;
|
|
|
|
dm = &datatype_member;
|
|
|
|
if (arg_to_datatype(e, dm, RETURN_ON_ERROR)) {
|
|
if ((dm->size >= 0) &&
|
|
(dm->type == ENUM) && dm->tagname) {
|
|
*value = dm->value;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Verify that a datatype exists, but return on error.
|
|
*/
|
|
int
|
|
datatype_exists(char *s)
|
|
{
|
|
int retval;
|
|
char buf[BUFSIZE], *p;
|
|
struct gnu_request *req;
|
|
|
|
strcpy(buf, s);
|
|
if ((p = strstr(buf, ".")))
|
|
*p = NULLCHAR;
|
|
|
|
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
|
|
req->command = GNU_GET_DATATYPE;
|
|
req->name = buf;
|
|
req->flags = GNU_RETURN_ON_ERROR;
|
|
req->fp = pc->nullfp;
|
|
|
|
gdb_interface(req);
|
|
|
|
retval = req->typecode;
|
|
|
|
FREEBUF(req);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the output radix if requested, and pass it on to gdb.
|
|
*/
|
|
void
|
|
cmd_p(void)
|
|
{
|
|
int c;
|
|
struct syment *sp, *percpu_sp;
|
|
unsigned radix;
|
|
int do_load_module_filter;
|
|
char buf1[BUFSIZE];
|
|
char *cpuspec;
|
|
|
|
do_load_module_filter = radix = 0;
|
|
|
|
while ((c = getopt(argcnt, args, "dhxu")) != EOF) {
|
|
switch(c)
|
|
{
|
|
case 'd':
|
|
if (radix == 16)
|
|
error(FATAL,
|
|
"-d and -x are mutually exclusive\n");
|
|
radix = 10;
|
|
break;
|
|
|
|
case 'h':
|
|
case 'x':
|
|
if (radix == 10)
|
|
error(FATAL,
|
|
"-d and -x are mutually exclusive\n");
|
|
radix = 16;
|
|
break;
|
|
|
|
case 'u':
|
|
pc->curcmd_flags |= MEMTYPE_UVADDR;
|
|
break;
|
|
|
|
default:
|
|
argerrs++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argerrs || !args[optind])
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
cpuspec = strrchr(args[optind], ':');
|
|
if (cpuspec)
|
|
*cpuspec++ = NULLCHAR;
|
|
|
|
sp = NULL;
|
|
if ((sp = symbol_search(args[optind])) && !args[optind+1]) {
|
|
if ((percpu_sp = per_cpu_symbol_search(args[optind])) &&
|
|
display_per_cpu_info(percpu_sp, radix, cpuspec))
|
|
return;
|
|
if (module_symbol(sp->value, NULL, NULL, NULL, *gdb_output_radix))
|
|
do_load_module_filter = TRUE;
|
|
} else if ((percpu_sp = per_cpu_symbol_search(args[optind])) &&
|
|
display_per_cpu_info(percpu_sp, radix, cpuspec))
|
|
return;
|
|
else if (st->flags & LOAD_MODULE_SYMS)
|
|
do_load_module_filter = TRUE;
|
|
|
|
if (cpuspec) {
|
|
if (sp)
|
|
error(WARNING, "%s is not percpu; cpuspec ignored.\n",
|
|
sp->name);
|
|
else
|
|
/* maybe a valid C expression (e.g. ':') */
|
|
*(cpuspec-1) = ':';
|
|
}
|
|
|
|
process_gdb_output(concat_args(buf1, 0, TRUE), radix,
|
|
sp ? sp->name : NULL, do_load_module_filter);
|
|
}
|
|
|
|
static void
|
|
process_gdb_output(char *gdb_request, unsigned radix,
|
|
const char *leader, int do_load_module_filter)
|
|
{
|
|
unsigned restore_radix;
|
|
int success;
|
|
char buf1[BUFSIZE];
|
|
char *p1;
|
|
|
|
if (leader || do_load_module_filter)
|
|
open_tmpfile();
|
|
|
|
set_temporary_radix(radix, &restore_radix);
|
|
|
|
success = gdb_pass_through(gdb_request, NULL, GNU_RETURN_ON_ERROR);
|
|
|
|
if (success && (leader || do_load_module_filter)) {
|
|
int firstline;
|
|
|
|
if (leader) {
|
|
fprintf(pc->saved_fp, "%s = ", leader);
|
|
fflush(pc->saved_fp);
|
|
}
|
|
|
|
firstline = TRUE;
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
|
|
if (firstline &&
|
|
(p1 = strstr(buf1, "{")) &&
|
|
!STRNEQ(p1, "{\n")) {
|
|
*p1 = NULLCHAR;
|
|
fprintf(pc->saved_fp, "%s", buf1);
|
|
fprintf(pc->saved_fp, "\n {");
|
|
print_verbatim(pc->saved_fp, p1+1);
|
|
} else
|
|
print_verbatim(pc->saved_fp, do_load_module_filter ?
|
|
load_module_filter(buf1, LM_P_FILTER) :
|
|
buf1);
|
|
|
|
firstline = FALSE;
|
|
}
|
|
}
|
|
|
|
if (leader || do_load_module_filter)
|
|
close_tmpfile();
|
|
|
|
restore_current_radix(restore_radix);
|
|
|
|
if (!success)
|
|
error(FATAL, "gdb request failed: %s\n", gdb_request);
|
|
}
|
|
|
|
/*
|
|
* Get the type of an expression using gdb's "whatis" command.
|
|
* The returned string is dynamically allocated, and it should
|
|
* be passed to FREEBUF() when no longer needed.
|
|
* Return NULL if the type cannot be determined.
|
|
*/
|
|
static char *
|
|
expr_type_name(const char *expr)
|
|
{
|
|
char buf[BUFSIZE], *p;
|
|
|
|
open_tmpfile();
|
|
sprintf(buf, "whatis %s", expr);
|
|
if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) {
|
|
close_tmpfile();
|
|
return NULL;
|
|
}
|
|
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile) && !STRNEQ(buf, "type = "))
|
|
;
|
|
p = feof(pc->tmpfile) ? NULL : buf + strlen("type = ");
|
|
close_tmpfile();
|
|
|
|
if (p) {
|
|
size_t len = strlen(clean_line(p));
|
|
/* GDB reports unknown types as <...descriptive text...> */
|
|
if (p[0] == '<' && p[len-1] == '>')
|
|
return NULL;
|
|
return strcpy(GETBUF(len + 1), p);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Display the datatype of the per_cpu__xxx symbol and
|
|
* the addresses of each its per-cpu instances.
|
|
*/
|
|
static int
|
|
display_per_cpu_info(struct syment *sp, int radix, char *cpuspec)
|
|
{
|
|
ulong *cpus;
|
|
int c;
|
|
ulong addr;
|
|
char buf[BUFSIZE];
|
|
char leader[sizeof("&per_cpu(") + strlen(sp->name) +
|
|
sizeof(", " STR(UINT_MAX) ")")];
|
|
char *typename;
|
|
int do_load_module_filter;
|
|
|
|
if (((kt->flags & (SMP|PER_CPU_OFF)) != (SMP|PER_CPU_OFF)) ||
|
|
(!is_percpu_symbol(sp)) ||
|
|
!((sp->type == 'd') || (sp->type == 'D') || (sp->type == 'V')))
|
|
return FALSE;
|
|
|
|
if (cpuspec) {
|
|
cpus = get_cpumask_buf();
|
|
if (STREQ(cpuspec, ""))
|
|
SET_BIT(cpus, CURRENT_CONTEXT()->processor);
|
|
else
|
|
make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL);
|
|
} else
|
|
cpus = NULL;
|
|
|
|
typename = expr_type_name(sp->name);
|
|
|
|
if (!cpus) {
|
|
fprintf(fp, "PER-CPU DATA TYPE:\n ");
|
|
if (!typename)
|
|
fprintf(fp, "[undetermined type] %s;\n", sp->name);
|
|
else
|
|
whatis_variable(sp);
|
|
|
|
fprintf(fp, "PER-CPU ADDRESSES:\n");
|
|
}
|
|
|
|
do_load_module_filter =
|
|
module_symbol(sp->value, NULL, NULL, NULL, *gdb_output_radix);
|
|
|
|
for (c = 0; c < kt->cpus; c++) {
|
|
if (hide_offline_cpu(c)) {
|
|
fprintf(fp, "cpu %d is OFFLINE\n", c);
|
|
continue;
|
|
}
|
|
|
|
if (cpus && !NUM_IN_BITMAP(cpus, c))
|
|
continue;
|
|
addr = sp->value + kt->__per_cpu_offset[c];
|
|
if (!cpus)
|
|
fprintf(fp, " [%d]: %lx\n", c, addr);
|
|
else if (typename) {
|
|
snprintf(buf, sizeof buf, "p *(%s*) 0x%lx",
|
|
typename, addr);
|
|
sprintf(leader, "per_cpu(%s, %u)",
|
|
sp->name, c);
|
|
process_gdb_output(buf, radix, leader,
|
|
do_load_module_filter);
|
|
} else {
|
|
snprintf(buf, sizeof buf, "p (void*) 0x%lx", addr);
|
|
sprintf(leader, "&per_cpu(%s, %u)",
|
|
sp->name, c);
|
|
process_gdb_output(buf, radix, leader,
|
|
do_load_module_filter);
|
|
}
|
|
}
|
|
|
|
if (typename)
|
|
FREEBUF(typename);
|
|
if (cpus)
|
|
FREEBUF(cpus);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static struct load_module *
|
|
get_module_percpu_sym_owner(struct syment *sp)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
|
|
if (!IS_MODULE_SYMBOL(sp))
|
|
return NULL;
|
|
|
|
/*
|
|
* Find out percpu symbol owner module.
|
|
* If found out, sp is module's percpu symbol.
|
|
*/
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!MODULE_PERCPU_SYMS_LOADED(lm))
|
|
continue;
|
|
if (IN_MODULE_PERCPU(sp->value, lm))
|
|
return lm;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
is_percpu_symbol(struct syment *sp)
|
|
{
|
|
if (sp->value >= st->__per_cpu_start) {
|
|
if (sp->value < st->__per_cpu_end)
|
|
/* kernel percpu symbol */
|
|
return 1;
|
|
else if (get_module_percpu_sym_owner(sp))
|
|
/* module percpu symbol */
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* As a latch ditch effort before a command is thrown away by exec_command(),
|
|
* args[0] is checked to see whether it's the name of a variable, structure,
|
|
* union, or typedef. If so, args[0] is changed to the appropriate command,
|
|
* i.e., "p", "struct", "union", or "whatis", and the original args are all
|
|
* shifted into the next higer args[] location.
|
|
*/
|
|
int
|
|
is_datatype_command(void)
|
|
{
|
|
int i;
|
|
long len;
|
|
char *command;
|
|
struct datatype_member datatype_member, *dm;
|
|
struct syment *sp;
|
|
char *rdarg;
|
|
char buf[BUFSIZE];
|
|
|
|
if (!args[0])
|
|
return FALSE;
|
|
|
|
strcpy(buf, args[0]);
|
|
dm = &datatype_member;
|
|
|
|
if ((sp = symbol_search(args[0])) && (argcnt == 1)) {
|
|
if (is_gdb_command(FALSE, RETURN_ON_ERROR)) {
|
|
pc->curcmd = pc->program_name;
|
|
error(FATAL,
|
|
"ambiguous command: %s (symbol and gdb command)\n",
|
|
args[0]);
|
|
}
|
|
command = "p";
|
|
} else if (STREQ(args[0], "enum"))
|
|
command = "whatis";
|
|
else if (!datatype_exists(args[0]))
|
|
return FALSE;
|
|
else if (!arg_to_datatype(buf, dm, RETURN_ON_ERROR|DATATYPE_QUERY))
|
|
return FALSE;
|
|
else {
|
|
if (is_gdb_command(FALSE, RETURN_ON_ERROR)) {
|
|
pc->curcmd = pc->program_name;
|
|
error(FATAL,
|
|
"ambiguous command: %s (symbol/data type and gdb command)\n",
|
|
args[0]);
|
|
}
|
|
|
|
if ((sp = symbol_search(args[0])) && (argcnt == 1)) {
|
|
command = "p";
|
|
dm->type = 0;
|
|
} else if ((len = DATATYPE_SIZE(dm)) < 0) {
|
|
return FALSE;
|
|
} else if (sp) {
|
|
command = "p";
|
|
dm->type = 0;
|
|
}
|
|
|
|
switch (dm->type)
|
|
{
|
|
case STRUCT_REQUEST:
|
|
if ((dm->flags & TYPEDEF) && (argcnt == 1))
|
|
command = "whatis";
|
|
else
|
|
command = "struct";
|
|
break;
|
|
|
|
case UNION_REQUEST:
|
|
if ((dm->flags & TYPEDEF) && (argcnt == 1))
|
|
command = "whatis";
|
|
else
|
|
command = "union";
|
|
break;
|
|
|
|
case POINTER:
|
|
command = "whatis";
|
|
break;
|
|
|
|
case ARRAY:
|
|
command = "whatis";
|
|
break;
|
|
|
|
case FUNCTION:
|
|
command = "whatis";
|
|
break;
|
|
|
|
case ENUM:
|
|
command = "whatis";
|
|
break;
|
|
|
|
default:
|
|
if (dm->type & INTEGER_TYPE) {
|
|
switch (dm->type)
|
|
{
|
|
case INT64: rdarg = "-64"; break;
|
|
case INT32: rdarg = "-32"; break;
|
|
case INT16: rdarg = "-16"; break;
|
|
case INT8: rdarg = "-8"; break;
|
|
default: rdarg = NULL; break;
|
|
}
|
|
|
|
if (args[1]) {
|
|
if ((sp = symbol_search(args[1]))) {
|
|
command = "p";
|
|
args[0] = args[1];
|
|
argcnt--;
|
|
} else {
|
|
command = "rd";
|
|
args[0] = rdarg;
|
|
}
|
|
} else
|
|
command = "whatis";
|
|
} else
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = argcnt; i; i--)
|
|
args[i] = args[i-1];
|
|
args[0] = command;
|
|
argcnt++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Given a structure name and an address, have gdb do most of the work.
|
|
*/
|
|
static void
|
|
print_struct(char *s, ulong addr)
|
|
{
|
|
char buf[BUFSIZE];
|
|
|
|
if (is_downsized(s))
|
|
pc->curcmd_flags |= PARTIAL_READ_OK;
|
|
|
|
if (is_typedef(s))
|
|
sprintf(buf, "output *(%s *)0x%lx", s, addr);
|
|
else
|
|
sprintf(buf, "output *(struct %s *)0x%lx", s, addr);
|
|
fprintf(fp, "struct %s ", s);
|
|
gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR);
|
|
fprintf(fp, "\n");
|
|
|
|
pc->curcmd_flags &= ~PARTIAL_READ_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a union name and an address, let gdb do the work.
|
|
*/
|
|
static void
|
|
print_union(char *s, ulong addr)
|
|
{
|
|
char buf[BUFSIZE];
|
|
|
|
if (is_downsized(s))
|
|
pc->curcmd_flags |= PARTIAL_READ_OK;
|
|
|
|
if (is_typedef(s))
|
|
sprintf(buf, "output *(%s *)0x%lx", s, addr);
|
|
else
|
|
sprintf(buf, "output *(union %s *)0x%lx", s, addr);
|
|
fprintf(fp, "union %s ", s);
|
|
gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR);
|
|
|
|
pc->curcmd_flags &= ~PARTIAL_READ_OK;
|
|
}
|
|
|
|
/*
|
|
* Given a structure or union, find its definition in the datatype symbol
|
|
* file, and dump it. If the verbose flags is set, everything from the
|
|
* file is shown; otherwise the bitpos, size and id data is stripped.
|
|
*/
|
|
static void
|
|
whatis_datatype(char *st, ulong flags, FILE *ofp)
|
|
{
|
|
char lookbuf[BUFSIZE];
|
|
|
|
if (flags & TYPEDEF)
|
|
sprintf(lookbuf, "ptype %s", st);
|
|
else if (flags & UNION_REQUEST)
|
|
sprintf(lookbuf, "ptype union %s", st);
|
|
else if (flags & STRUCT_REQUEST)
|
|
sprintf(lookbuf, "ptype struct %s", st);
|
|
else
|
|
return;
|
|
|
|
if (!gdb_pass_through(lookbuf, ofp, GNU_RETURN_ON_ERROR)) {
|
|
/*
|
|
* When a structure is defined using the format:
|
|
*
|
|
* typedef struct {
|
|
* yada yada yada
|
|
* } type_t;
|
|
*
|
|
* gdb says it's a structure and not a typedef. So
|
|
* if the union or struct pass-through fails, it can't
|
|
* hurt to retry it with just "ptype type_t" before
|
|
* giving up.
|
|
*/
|
|
if (flags & (UNION_REQUEST|STRUCT_REQUEST)) {
|
|
sprintf(lookbuf, "ptype %s", st);
|
|
gdb_pass_through(lookbuf, ofp, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan the symbol file for a variable declaration.
|
|
*/
|
|
static void
|
|
whatis_variable(struct syment *sp)
|
|
{
|
|
char *p1;
|
|
char buf[BUFSIZE];
|
|
|
|
open_tmpfile();
|
|
sprintf(buf, "whatis %s", sp->name);
|
|
if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) {
|
|
close_tmpfile();
|
|
error(FATAL, "gdb request failed: whatis %s\n", sp->name);
|
|
}
|
|
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
if (STRNEQ(buf, "type = "))
|
|
break;
|
|
}
|
|
close_tmpfile();
|
|
|
|
clean_line(buf);
|
|
|
|
if ((p1 = strstr(buf, "["))) {
|
|
shift_string_right(p1, strlen(sp->name));
|
|
BCOPY(sp->name, p1, strlen(sp->name));
|
|
p1 = buf + strlen("type = ");
|
|
fprintf(fp, "%s;\n", p1);
|
|
} else if ((p1 = strstr(buf, "("))) {
|
|
if (index(buf, '(') == rindex(buf, '(')) {
|
|
shift_string_right(p1, strlen(sp->name));
|
|
BCOPY(sp->name, p1, strlen(sp->name));
|
|
} else {
|
|
p1 = strstr(buf, ")");
|
|
shift_string_right(p1, strlen(sp->name));
|
|
BCOPY(sp->name, p1, strlen(sp->name));
|
|
}
|
|
p1 = buf + strlen("type = ");
|
|
fprintf(fp, "%s;\n", p1);
|
|
} else {
|
|
p1 = buf + strlen("type = ");
|
|
fprintf(fp, "%s%s%s;\n", p1, LASTCHAR(p1) == '*' ? "":" ",
|
|
sp->name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Determines whether the current structure or union member is a typedef.
|
|
*/
|
|
int
|
|
is_typedef(char *name)
|
|
{
|
|
struct datatype_member datatype_member, *dm;
|
|
|
|
if (!name)
|
|
drop_core("is_typedef() received NULL name string\n");
|
|
|
|
dm = &datatype_member;
|
|
BZERO(dm, sizeof(struct datatype_member));
|
|
dm->name = name;
|
|
return (DATATYPE_SIZE(dm) < 0 ? FALSE : (dm->flags & TYPEDEF));
|
|
}
|
|
|
|
static void
|
|
dump_datatype_flags(ulong flags, FILE *ofp)
|
|
{
|
|
int others;
|
|
|
|
others = 0;
|
|
|
|
fprintf(ofp, "(");
|
|
if (flags & UINT8)
|
|
fprintf(ofp, "%sUINT8", others++ ? "|" : "");
|
|
if (flags & INT8)
|
|
fprintf(ofp, "%sINT8", others++ ? "|" : "");
|
|
if (flags & UINT16)
|
|
fprintf(ofp, "%sUINT16", others++ ? "|" : "");
|
|
if (flags & INT16)
|
|
fprintf(ofp, "%sINT16", others++ ? "|" : "");
|
|
if (flags & UINT32)
|
|
fprintf(ofp, "%sUINT32", others++ ? "|" : "");
|
|
if (flags & INT32)
|
|
fprintf(ofp, "%sINT32", others++ ? "|" : "");
|
|
if (flags & UINT64)
|
|
fprintf(ofp, "%sUINT64", others++ ? "|" : "");
|
|
if (flags & INT64)
|
|
fprintf(ofp, "%sINT64", others++ ? "|" : "");
|
|
if (flags & POINTER)
|
|
fprintf(ofp, "%sPOINTER", others++ ? "|" : "");
|
|
if (flags & FUNCTION)
|
|
fprintf(ofp, "%sFUNCTION", others++ ? "|" : "");
|
|
if (flags & ARRAY)
|
|
fprintf(ofp, "%sARRAY", others++ ? "|" : "");
|
|
if (flags & ENUM)
|
|
fprintf(ofp, "%sENUM", others++ ? "|" : "");
|
|
if (flags & TYPEDEF)
|
|
fprintf(ofp, "%sTYPEDEF", others++ ? "|" : "");
|
|
if (flags & STRUCT_VERBOSE)
|
|
fprintf(ofp, "%sSTRUCT_VERBOSE", others++ ? "|" : "");
|
|
if (flags & SHOW_OFFSET)
|
|
fprintf(ofp, "%sSHOW_OFFSET", others++ ? "|" : "");
|
|
if (flags & DATATYPE_QUERY)
|
|
fprintf(ofp, "%sDATATYPE_QUERY", others++ ? "|" : "");
|
|
if (flags & ANON_MEMBER_QUERY)
|
|
fprintf(ofp, "%sANON_MEMBER_QUERY", others++ ? "|" : "");
|
|
if (flags & SHOW_RAW_DATA)
|
|
fprintf(ofp, "%sSHOW_RAW_DATA", others++ ? "|" : "");
|
|
if (flags & DEREF_POINTERS)
|
|
fprintf(ofp, "%sDEREF_POINTERS", others++ ? "|" : "");
|
|
fprintf(ofp, ")\n");
|
|
}
|
|
|
|
/*
|
|
* When a request is made to print just a member of a structure or union,
|
|
* the whole datatype is dumped to a temporary file, and this routine
|
|
* parses through it for the targeted member.
|
|
*/
|
|
static void
|
|
parse_for_member(struct datatype_member *dm, ulong flag)
|
|
{
|
|
char *s;
|
|
char buf[BUFSIZE];
|
|
char lookfor1[BUFSIZE];
|
|
char lookfor2[BUFSIZE];
|
|
char lookfor3[BUFSIZE];
|
|
char lookfor4[BUFSIZE];
|
|
char lookfor5[BUFSIZE];
|
|
long curpos, last_open_bracket;
|
|
int indent, on, array, embed;
|
|
char *p1;
|
|
|
|
s = dm->member;
|
|
indent = 0;
|
|
array = FALSE;
|
|
on = 0;
|
|
embed = 0;
|
|
rewind(pc->tmpfile);
|
|
|
|
switch (flag)
|
|
{
|
|
case PARSE_FOR_DATA:
|
|
sprintf(lookfor1, " %s ", s);
|
|
sprintf(lookfor2, " %s[", s);
|
|
next_item:
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
if (embed && (count_leading_spaces(buf) == embed))
|
|
embed = 0;
|
|
|
|
if (!on && !embed && strstr(buf, "= {") && !strstr(buf, lookfor1))
|
|
embed = count_leading_spaces(buf);
|
|
|
|
if (embed)
|
|
continue;
|
|
|
|
if (strstr(buf, lookfor1) || strstr(buf, lookfor2)) {
|
|
on++;
|
|
if (strstr(buf, "= {"))
|
|
indent = count_leading_spaces(buf);
|
|
if (strstr(buf, "["))
|
|
array = TRUE;
|
|
}
|
|
|
|
if (on) {
|
|
if ((indent && (on > 1) && (count_leading_spaces(buf) == indent) &&
|
|
!strstr(buf, "}")) || (buf[0] == '}')) {
|
|
break;
|
|
}
|
|
if (!indent) {
|
|
if ((p1 = strstr(buf, ", \n")))
|
|
sprintf(p1, "\n");
|
|
fprintf(pc->saved_fp, "%s", buf);
|
|
break;
|
|
}
|
|
if (strstr(buf, "}") &&
|
|
(count_leading_spaces(buf) == indent)) {
|
|
if ((p1 = strstr(buf, "}, \n")))
|
|
sprintf(p1, "}\n");
|
|
fprintf(pc->saved_fp, "%s", buf);
|
|
break;
|
|
}
|
|
fprintf(pc->saved_fp, "%s", buf);
|
|
on++;
|
|
}
|
|
}
|
|
if (array) {
|
|
on = array = FALSE;
|
|
on = 0;
|
|
goto next_item;
|
|
}
|
|
break;
|
|
|
|
case PARSE_FOR_DECLARATION:
|
|
last_open_bracket = curpos = 0;
|
|
|
|
sprintf(lookfor1, " %s;", s);
|
|
sprintf(lookfor2, "*%s;", s);
|
|
sprintf(lookfor3, " %s[", s);
|
|
sprintf(lookfor4, "*%s[", s);
|
|
sprintf(lookfor5, " %s :", s);
|
|
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
indent = count_leading_spaces(buf);
|
|
|
|
switch (indent)
|
|
{
|
|
case 0:
|
|
curpos = ftell(pc->tmpfile);
|
|
continue;
|
|
|
|
case INITIAL_INDENT:
|
|
if (strstr(buf, "{"))
|
|
last_open_bracket = curpos;
|
|
break;
|
|
|
|
default:
|
|
if (!on && (indent != INITIAL_INDENT))
|
|
continue;
|
|
}
|
|
|
|
if (strstr(buf, lookfor1) ||
|
|
strstr(buf, lookfor2) ||
|
|
strstr(buf, lookfor3) ||
|
|
strstr(buf, lookfor4) ||
|
|
strstr(buf, lookfor5)) {
|
|
if (strstr(buf, "}") && !on) {
|
|
on = TRUE;
|
|
fseek(pc->tmpfile, last_open_bracket,
|
|
SEEK_SET);
|
|
} else {
|
|
print_verbatim(pc->saved_fp, buf);
|
|
if (indent == INITIAL_INDENT)
|
|
break;
|
|
}
|
|
}
|
|
else if (on)
|
|
print_verbatim(pc->saved_fp, buf);
|
|
|
|
curpos = ftell(pc->tmpfile);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct struct_elem {
|
|
char field_name[BUFSIZE];
|
|
unsigned char field_len;
|
|
char value[BUFSIZE];
|
|
unsigned char is_array_root:1;
|
|
|
|
struct struct_elem *parent;
|
|
struct struct_elem *inner;
|
|
struct struct_elem *next;
|
|
struct struct_elem *prev;
|
|
};
|
|
|
|
#define ALLOC_XXX_ELEMENT(xxx, clone_parent) \
|
|
{ \
|
|
if (current == NULL) { \
|
|
error(FATAL, "Internal error while parsing structure %s\n", dm->name); \
|
|
} \
|
|
current->xxx = (struct struct_elem *)GETBUF(sizeof(struct struct_elem)); \
|
|
if (clone_parent) current->xxx->parent = current->parent; \
|
|
else current->xxx->parent = current; \
|
|
current = current->xxx; \
|
|
}
|
|
|
|
#define ALLOC_INNER_ELEMENT { ALLOC_XXX_ELEMENT(inner, 0) }
|
|
#define ALLOC_NEXT_ELEMENT { ALLOC_XXX_ELEMENT(next, 1) }
|
|
|
|
static void
|
|
free_structure(struct struct_elem *p)
|
|
{
|
|
if (p == NULL)
|
|
return;
|
|
free_structure(p->inner);
|
|
free_structure(p->next);
|
|
FREEBUF(p);
|
|
}
|
|
|
|
static unsigned char
|
|
is_right_brace(const char *b)
|
|
{
|
|
unsigned char r = 0;
|
|
for (; *b == ' '; b++);
|
|
if (*b == '}') {
|
|
b++;
|
|
r = 1;
|
|
if (*b == '}') {
|
|
r = 2;
|
|
b++;
|
|
}
|
|
}
|
|
|
|
if (*b == ',')
|
|
b++;
|
|
|
|
if (*b == '\0')
|
|
return r;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static struct struct_elem *
|
|
find_node(struct struct_elem *s, char *n)
|
|
{
|
|
char *p, *b, *e;
|
|
struct struct_elem *t = s;
|
|
unsigned i;
|
|
|
|
if (('\0' == *n) || (s == NULL))
|
|
return s;
|
|
|
|
/* [n .. p) - struct member with index*/
|
|
if ((p = strstr(n, ".")) == NULL)
|
|
p = n + strlen(n);
|
|
|
|
/* [n .. b) - struct member without index*/
|
|
for (b = n; (b < p) && (*b != '['); b++);
|
|
|
|
/* s - is the current level of items [s, s->next, ..., s->...->next] */
|
|
for (; s; s = s->next) {
|
|
if (*s->field_name == '\0')
|
|
continue;
|
|
|
|
/* `field_name` doesn't match */
|
|
if (((b - n) != s->field_len) || memcmp(s->field_name, n, b - n))
|
|
continue;
|
|
|
|
// For case like `pids.node` where pids is an array
|
|
if (s->is_array_root && *b != '[' && *p)
|
|
return NULL;
|
|
|
|
if (*b == '[') { /* Array */
|
|
i = strtol(b + 1, &e, 10);
|
|
/* Check if the current node is array and
|
|
* we've parsed index more or less correctly
|
|
*/
|
|
if (!(s->is_array_root && *e == ']' && (e != b + 1)))
|
|
return NULL;
|
|
|
|
/* Look for the i-th element */
|
|
for (s = s->inner; s && i; s = s->next, i--);
|
|
if (i || (s == NULL))
|
|
return NULL;
|
|
}
|
|
|
|
/* Ok. We've found node, it's - the last member
|
|
* in our search string, let's return it.
|
|
*/
|
|
if ('\0' == *p)
|
|
return s;
|
|
else
|
|
return find_node(s->inner, p + 1);
|
|
}
|
|
|
|
// We haven't found any field.
|
|
// Might happen, we've encountered anonymous structure
|
|
// of union. Lets try every record without `field_name`
|
|
s = t;
|
|
t = NULL;
|
|
for (; s; s = s->next) {
|
|
if (*s->field_name)
|
|
continue;
|
|
t = find_node(s->inner, n);
|
|
if (t)
|
|
break;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
static void
|
|
dump_node(struct struct_elem *p, char *f, unsigned char level, unsigned char is_array)
|
|
{
|
|
unsigned int i;
|
|
if (p == NULL)
|
|
return;
|
|
do {
|
|
#define PUT_INDENTED_STRING(m, ...) { \
|
|
for (i = 0; i++ < 2 + 2 * (m * is_array + level); fprintf(pc->saved_fp, " ")); \
|
|
fprintf(pc->saved_fp, __VA_ARGS__); }
|
|
|
|
if (p->inner) {
|
|
if (*p->field_name) {
|
|
PUT_INDENTED_STRING(1, "%s = %s\n", f ? f : p->field_name,
|
|
p->inner->is_array_root ? "{{" : "{");
|
|
} else {
|
|
if (f) /* For union */
|
|
PUT_INDENTED_STRING(1, "%s = ", f);
|
|
PUT_INDENTED_STRING(1, "%s\n", p->inner->is_array_root ? "{{" : "{");
|
|
}
|
|
dump_node(p->inner, NULL, is_array + level + 1, p->inner->is_array_root);
|
|
PUT_INDENTED_STRING(1, "%s%s\n", p->inner->is_array_root ? "}}" : "}",
|
|
(p->next && !p->next->is_array_root) ? "," : "");
|
|
} else {
|
|
PUT_INDENTED_STRING(1, "%s = %s%s", f ? f : p->field_name,
|
|
p->value, p->next ? ",\n" : "\n");
|
|
}
|
|
if (level) {
|
|
p = p->next;
|
|
if (p && p->is_array_root)
|
|
PUT_INDENTED_STRING(0, "}, {\n");
|
|
}
|
|
} while (p && level);
|
|
}
|
|
|
|
void
|
|
parse_for_member_extended(struct datatype_member *dm,
|
|
ulong __attribute__ ((unused)) flag)
|
|
{
|
|
struct struct_elem *i, *current = NULL, *root = NULL;
|
|
|
|
char buf[BUFSIZE];
|
|
char *p, *p1;
|
|
char *s_e; // structure_element
|
|
unsigned int len;
|
|
unsigned char trailing_comma, braces, found = 0;
|
|
|
|
rewind(pc->tmpfile);
|
|
|
|
root = (struct struct_elem *)GETBUF(sizeof(struct struct_elem));
|
|
current = root;
|
|
ALLOC_INNER_ELEMENT;
|
|
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
len = strlen(buf) - 1;
|
|
for (; buf[len] <= ' '; buf[len--] = '\0');
|
|
if ((trailing_comma = (buf[len] == ',')))
|
|
buf[len--] = '\0';
|
|
|
|
if ((braces = is_right_brace(buf))) {
|
|
for (; braces && current; braces--)
|
|
current = current->parent;
|
|
|
|
if ((current->parent == root) || trailing_comma)
|
|
ALLOC_NEXT_ELEMENT;
|
|
continue;
|
|
}
|
|
|
|
for (p1 = buf; *p1 == ' '; p1++);
|
|
|
|
if ((p = strstr(buf, " = ")) != NULL)
|
|
s_e = p + 3;
|
|
else
|
|
s_e = p1;
|
|
|
|
/*
|
|
* After that we have pointers:
|
|
* foobar = bazzz
|
|
* -----^ ^ ^
|
|
* | ------| |
|
|
* | | |
|
|
* p1 p s_e
|
|
*
|
|
* OR
|
|
*
|
|
* {
|
|
* ^
|
|
* |
|
|
* ---------
|
|
* | |
|
|
* p1 s_e
|
|
*
|
|
* p == NULL
|
|
*
|
|
*
|
|
* p1 - the first non-whitespace symbol in line
|
|
* p - pointer to line ' = '.
|
|
* If not NULL, there is identifier
|
|
* s_e - element of structure (brace / double brace / array separator / scalar)
|
|
*
|
|
*/
|
|
|
|
if (current && p && (p - p1 < BUFSIZE)) {
|
|
strncpy(current->field_name, p1, p - p1);
|
|
current->field_len = p - p1;
|
|
}
|
|
|
|
if ( p && (*s_e != '{' || (*s_e == '{' && buf[len] == '}') )) {
|
|
/* Scalar or one-line array
|
|
* next = 0x0
|
|
* or
|
|
* files = {0x0, 0x0}
|
|
*/
|
|
strcpy(current->value, s_e);
|
|
if (trailing_comma) ALLOC_NEXT_ELEMENT;
|
|
}
|
|
else if ( *s_e == '{' ) {
|
|
ALLOC_INNER_ELEMENT;
|
|
if (*(s_e + 1) == '{') {
|
|
current->parent->is_array_root = 1;
|
|
ALLOC_INNER_ELEMENT;
|
|
}
|
|
}
|
|
else if (strstr(s_e, "}, {")) {
|
|
/* Next array element */
|
|
current = current->parent;
|
|
ALLOC_NEXT_ELEMENT;
|
|
ALLOC_INNER_ELEMENT;
|
|
}
|
|
else if (buf == (p = strstr(buf, "struct "))) {
|
|
p += 7; /* strlen "struct " */
|
|
p1 = strstr(buf, " {");
|
|
strncpy(current->field_name, p, p1 - p);
|
|
ALLOC_INNER_ELEMENT;
|
|
}
|
|
}
|
|
|
|
for (i = root->inner; i; i = i->next) {
|
|
if ((current = find_node(i->inner, dm->member))) {
|
|
dump_node(current, dm->member, 0, 0);
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free_structure(root);
|
|
|
|
if (!found)
|
|
error(INFO, "invalid data structure member reference: %s\n",
|
|
dm->member);
|
|
}
|
|
|
|
/*
|
|
* Dig out a member name from a formatted gdb structure declaration dump,
|
|
* and print its offset from the named structure passed in.
|
|
*/
|
|
static int
|
|
show_member_offset(FILE *ofp, struct datatype_member *dm, char *inbuf)
|
|
{
|
|
int i, c, len;
|
|
long offset;
|
|
char *t1, *target;
|
|
char *arglist[MAXARGS];
|
|
char buf1[BUFSIZE];
|
|
char fmt[BUFSIZE];
|
|
char workbuf[BUFSIZE];
|
|
int end_of_block;
|
|
|
|
if (!STRNEQ(inbuf, " ")) {
|
|
fprintf(ofp, "rejecting: %s", inbuf);
|
|
return FALSE;
|
|
}
|
|
|
|
if (STRNEQ(inbuf, " union {"))
|
|
dm->flags |= IN_UNION;
|
|
if (STRNEQ(inbuf, " struct {"))
|
|
dm->flags |= IN_STRUCT;
|
|
end_of_block = STRNEQ(inbuf, " } ");
|
|
|
|
switch (*gdb_output_radix)
|
|
{
|
|
default:
|
|
case 10:
|
|
sprintf(buf1, "%ld", dm->size);
|
|
break;
|
|
case 16:
|
|
sprintf(buf1, "0x%lx", dm->size);
|
|
}
|
|
len = strlen(buf1) + 4;
|
|
|
|
strcpy(workbuf, inbuf);
|
|
c = parse_line(workbuf, arglist);
|
|
|
|
target = NULL;
|
|
if (strstr(inbuf, ":")) {
|
|
for (i = 0; i < c; i++) {
|
|
if (i && STREQ(arglist[i], ":")) {
|
|
target = arglist[i-1];
|
|
break;
|
|
}
|
|
}
|
|
} else if (c) {
|
|
for (i = 0; i < c; i++) {
|
|
if (STRNEQ(arglist[i], "(*")) {
|
|
target = arglist[i]+2;
|
|
if (!(t1 = strstr(target, ")")))
|
|
continue;
|
|
*t1 = NULLCHAR;
|
|
break;
|
|
}
|
|
}
|
|
if (i == c) {
|
|
target = arglist[c-1];
|
|
if (!strstr(target, ";"))
|
|
target = NULL;
|
|
}
|
|
}
|
|
|
|
if (!target)
|
|
goto do_empty_offset;
|
|
|
|
null_first_space(clean_line(replace_string(target, "*[];()", ' ')));
|
|
|
|
if (strlen(target) == 0)
|
|
goto do_empty_offset;
|
|
|
|
if (dm->member && !STREQ(dm->member, target)) {
|
|
if (end_of_block)
|
|
dm->flags &= ~(IN_UNION|IN_STRUCT);
|
|
return FALSE;
|
|
}
|
|
|
|
offset = MEMBER_OFFSET(dm->name, target);
|
|
|
|
if (offset == -1)
|
|
offset = ANON_MEMBER_OFFSET(dm->name, target);
|
|
|
|
if (offset == -1)
|
|
goto do_empty_offset;
|
|
|
|
if (end_of_block && dm->member) {
|
|
if (dm->vaddr)
|
|
sprintf(buf1, " [%lx]", offset + dm->vaddr);
|
|
else
|
|
sprintf(buf1, *gdb_output_radix == 10 ?
|
|
" [%ld]" : " [0x%lx]", offset);
|
|
sprintf(fmt, "%c%ds", '%', len+1);
|
|
fprintf(ofp, fmt, " ");
|
|
|
|
switch (dm->flags & (IN_UNION|IN_STRUCT))
|
|
{
|
|
case IN_UNION:
|
|
fprintf(ofp, "union {\n");
|
|
break;
|
|
|
|
case IN_STRUCT:
|
|
fprintf(ofp, "struct {\n");
|
|
break;
|
|
}
|
|
dm->flags &= ~(IN_UNION|IN_STRUCT);
|
|
}
|
|
|
|
if (dm->vaddr)
|
|
sprintf(buf1, " [%lx]", offset + dm->vaddr);
|
|
else
|
|
sprintf(buf1, *gdb_output_radix == 10 ? " [%ld]" : " [0x%lx]", offset);
|
|
sprintf(fmt, "%c%ds", '%', len);
|
|
fprintf(ofp, fmt, buf1);
|
|
fprintf(ofp, "%s", &inbuf[3]);
|
|
|
|
return TRUE;
|
|
|
|
do_empty_offset:
|
|
if (end_of_block)
|
|
dm->flags &= ~(IN_UNION|IN_STRUCT);
|
|
|
|
if (dm->member)
|
|
return FALSE;
|
|
|
|
len = strlen(buf1)+1;
|
|
fprintf(ofp, "%s%s", space(len), inbuf);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*
|
|
* Get and store the size of a "known" array. This function is only called
|
|
* once per requested array; after the first time, ARRAY_LENGTH() should be
|
|
* used.
|
|
*
|
|
* For data symbols, get_symbol_type() does the work.
|
|
* For structure member arrays, datatype_info() does the work.
|
|
* For two-dimension arrays, or if the designated function above fails,
|
|
* then just parse "whatis" or "ptype" commands as a last resort.
|
|
*/
|
|
int
|
|
get_array_length(char *s, int *two_dim, long entry_size)
|
|
{
|
|
char copy[BUFSIZE];
|
|
char buf[BUFSIZE];
|
|
char lookfor1[BUFSIZE];
|
|
char lookfor2[BUFSIZE];
|
|
int retval;
|
|
struct datatype_member datatype_member, *dm;
|
|
struct gnu_request gnu_request, *req;
|
|
char *p1, *p2;
|
|
|
|
strcpy(copy, s);
|
|
dm = &datatype_member;
|
|
BZERO(dm, sizeof(struct datatype_member));
|
|
|
|
if ((retval = builtin_array_length(s, 0, two_dim)))
|
|
return retval;
|
|
|
|
/* symbol_search cannot be done with just kernel type information */
|
|
if (!(LKCD_KERNTYPES()) && symbol_search(s)) {
|
|
if (!two_dim) {
|
|
req = &gnu_request;
|
|
if ((get_symbol_type(copy, NULL, req) ==
|
|
TYPE_CODE_ARRAY) && req->target_typecode &&
|
|
req->target_length) {
|
|
retval = req->length / req->target_length;
|
|
goto store_builtin;
|
|
}
|
|
}
|
|
sprintf(buf, "whatis %s", s);
|
|
} else {
|
|
if (arg_to_datatype(copy, dm, RETURN_ON_ERROR)) {
|
|
if (!dm->member)
|
|
goto store_builtin;
|
|
|
|
datatype_info(dm->name, dm->member, dm);
|
|
|
|
switch (dm->type)
|
|
{
|
|
case UNION_REQUEST:
|
|
if (entry_size && dm->member_size &&
|
|
(dm->member_typecode == TYPE_CODE_ARRAY)) {
|
|
retval = dm->member_size/entry_size;
|
|
goto store_builtin;
|
|
}
|
|
sprintf(buf, "ptype union %s", dm->name);
|
|
break;
|
|
case STRUCT_REQUEST:
|
|
if (entry_size && dm->member_size &&
|
|
(dm->member_typecode == TYPE_CODE_ARRAY)) {
|
|
retval = dm->member_size/entry_size;
|
|
goto store_builtin;
|
|
}
|
|
sprintf(buf, "ptype struct %s", dm->name);
|
|
break;
|
|
default:
|
|
goto store_builtin;
|
|
}
|
|
|
|
sprintf(lookfor1, " %s[", dm->member);
|
|
sprintf(lookfor2, "*%s[", dm->member);
|
|
|
|
} else
|
|
goto store_builtin;
|
|
}
|
|
|
|
open_tmpfile2();
|
|
if (two_dim)
|
|
*two_dim = 0;
|
|
|
|
gdb_pass_through(buf, pc->tmpfile2, 0);
|
|
rewind(pc->tmpfile2);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile2)) {
|
|
if (STRNEQ(buf, "type = ") &&
|
|
(p1 = strstr(buf, "[")) &&
|
|
(p2 = strstr(buf, "]")) &&
|
|
(index(buf, '[') == rindex(buf, '['))) {
|
|
*p2 = NULLCHAR;
|
|
p1++;
|
|
if (strlen(p1)) {
|
|
retval = atoi(p1);
|
|
break;
|
|
}
|
|
}
|
|
if (STRNEQ(buf, "type = ") &&
|
|
(count_chars(buf, '[') == 2) &&
|
|
(count_chars(buf, ']') == 2) && two_dim) {
|
|
p1 = strstr(buf, "[");
|
|
p2 = strstr(buf, "]");
|
|
*p2 = NULLCHAR;
|
|
p1++;
|
|
if (strlen(p1))
|
|
*two_dim = atoi(p1);
|
|
else
|
|
break;
|
|
p2++;
|
|
p1 = strstr(p2, "[");
|
|
p2 = strstr(p1, "]");
|
|
p1++;
|
|
if (strlen(p1))
|
|
retval = atoi(p1);
|
|
else {
|
|
retval = 0;
|
|
*two_dim = 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (dm->type &&
|
|
(strstr(buf, lookfor1) || strstr(buf, lookfor2)) &&
|
|
(p1 = strstr(buf, "[")) &&
|
|
(p2 = strstr(buf, "]")) &&
|
|
(index(buf, '[') == rindex(buf, '['))) {
|
|
*p2 = NULLCHAR;
|
|
p1++;
|
|
if (strlen(p1)) {
|
|
retval = atoi(p1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
close_tmpfile2();
|
|
|
|
store_builtin:
|
|
|
|
return (builtin_array_length(s, retval, two_dim));
|
|
}
|
|
|
|
/*
|
|
* Get and store the size of a "known" array.
|
|
* A wrapper for get_array_length(), for cases in which
|
|
* the name of the result to be stored is different from the
|
|
* structure.member to be evaluated.
|
|
*/
|
|
int
|
|
get_array_length_alt(char *name, char *s, int *two_dim, long entry_size)
|
|
{
|
|
int retval;
|
|
|
|
retval = get_array_length(s, two_dim, entry_size);
|
|
if (retval)
|
|
retval = builtin_array_length(name, retval, two_dim);
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Designed for use by non-debug kernels, but used by all.
|
|
*/
|
|
int
|
|
builtin_array_length(char *s, int len, int *two_dim)
|
|
{
|
|
int *lenptr;
|
|
int *dimptr;
|
|
|
|
lenptr = dimptr = NULL;
|
|
|
|
if (STREQ(s, "kmem_cache_s.name"))
|
|
lenptr = &array_table.kmem_cache_s_name;
|
|
else if (STREQ(s, "kmem_cache_s.c_name"))
|
|
lenptr = &array_table.kmem_cache_s_c_name;
|
|
else if (STREQ(s, "kmem_cache_s.array"))
|
|
lenptr = &array_table.kmem_cache_s_array;
|
|
else if (STREQ(s, "kmem_cache.array"))
|
|
lenptr = &array_table.kmem_cache_s_array;
|
|
else if (STREQ(s, "kmem_cache_s.cpudata"))
|
|
lenptr = &array_table.kmem_cache_s_cpudata;
|
|
else if (STREQ(s, "log_buf"))
|
|
lenptr = &array_table.log_buf;
|
|
else if (STREQ(s, "irq_desc") || STREQ(s, "_irq_desc"))
|
|
lenptr = &array_table.irq_desc;
|
|
else if (STREQ(s, "irq_action"))
|
|
lenptr = &array_table.irq_action;
|
|
else if (STREQ(s, "timer_vec.vec"))
|
|
lenptr = &array_table.timer_vec_vec;
|
|
else if (STREQ(s, "timer_vec_root.vec"))
|
|
lenptr = &array_table.timer_vec_root_vec;
|
|
else if (STREQ(s, "tvec_s.vec"))
|
|
lenptr = &array_table.tvec_s_vec;
|
|
else if (STREQ(s, "tvec_root_s.vec"))
|
|
lenptr = &array_table.tvec_root_s_vec;
|
|
else if (STREQ(s, "net_device.name"))
|
|
lenptr = &array_table.net_device_name;
|
|
else if (STREQ(s, "neigh_table.hash_buckets"))
|
|
lenptr = &array_table.neigh_table_hash_buckets;
|
|
else if (STREQ(s, "neighbour.ha"))
|
|
lenptr = &array_table.neighbour_ha;
|
|
else if (STREQ(s, "swap_info"))
|
|
lenptr = &array_table.swap_info;
|
|
else if (STREQ(s, "page_hash_table"))
|
|
lenptr = &array_table.page_hash_table;
|
|
else if (STREQ(s, "pglist_data.node_zones"))
|
|
lenptr = &array_table.pglist_data_node_zones;
|
|
else if (STREQ(s, "zone_struct.free_area"))
|
|
lenptr = &array_table.zone_struct_free_area;
|
|
else if (STREQ(s, "zone.free_area"))
|
|
lenptr = &array_table.zone_free_area;
|
|
else if (STREQ(s, "prio_array.queue"))
|
|
lenptr = &array_table.prio_array_queue;
|
|
else if (STREQ(s, "height_to_maxindex"))
|
|
lenptr = &array_table.height_to_maxindex;
|
|
else if (STREQ(s, "height_to_maxnodes"))
|
|
lenptr = &array_table.height_to_maxnodes;
|
|
else if (STREQ(s, "pid_hash"))
|
|
lenptr = &array_table.pid_hash;
|
|
else if (STREQ(s, "free_area")) {
|
|
lenptr = &array_table.free_area;
|
|
if (two_dim)
|
|
dimptr = &array_table.free_area_DIMENSION;
|
|
} else if (STREQ(s, "kmem_cache.node"))
|
|
lenptr = &array_table.kmem_cache_node;
|
|
else if (STREQ(s, "kmem_cache.cpu_slab"))
|
|
lenptr = &array_table.kmem_cache_cpu_slab;
|
|
else if (STREQ(s, "rt_prio_array.queue"))
|
|
lenptr = &array_table.rt_prio_array_queue;
|
|
else if (STREQ(s, "task_struct.rlim"))
|
|
lenptr = &array_table.task_struct_rlim;
|
|
else if (STREQ(s, "signal_struct.rlim"))
|
|
lenptr = &array_table.signal_struct_rlim;
|
|
|
|
if (!lenptr) /* not stored */
|
|
return(len);
|
|
|
|
if (*lenptr) { /* pre-set */
|
|
if (dimptr && two_dim)
|
|
*two_dim = *dimptr;
|
|
return(*lenptr);
|
|
}
|
|
|
|
if (len) {
|
|
*lenptr = len; /* initialize passed-in value(s) */
|
|
if (dimptr && two_dim)
|
|
*dimptr = *two_dim;
|
|
return(len);
|
|
}
|
|
|
|
return(0); /* in table, but not set yet */
|
|
}
|
|
|
|
/*
|
|
* "help -o" output
|
|
*/
|
|
void
|
|
dump_offset_table(char *spec, ulong makestruct)
|
|
{
|
|
char buf[BUFSIZE], *p1;
|
|
char revname[BUFSIZE];
|
|
struct new_utsname *uts;
|
|
long long data_debug;
|
|
|
|
data_debug = pc->flags & DATADEBUG;
|
|
pc->flags &= ~DATADEBUG;
|
|
uts = NULL;
|
|
|
|
if (makestruct) {
|
|
uts = &kt->utsname;
|
|
sprintf(revname, "%s_%s", pc->machine_type, uts->release);
|
|
p1 = revname + strlen(pc->machine_type);
|
|
while (*p1) {
|
|
if (((*p1 >= '0') && (*p1 <= '9')) ||
|
|
((*p1 >= 'a') && (*p1 <= 'z')) ||
|
|
((*p1 >= 'A') && (*p1 <= 'Z')))
|
|
p1++;
|
|
else
|
|
*p1++ = '_';
|
|
}
|
|
}
|
|
|
|
if (spec || makestruct)
|
|
open_tmpfile();
|
|
|
|
fprintf(fp, " offset_table:\n");
|
|
fprintf(fp, " list_head_next: %ld\n",
|
|
OFFSET(list_head_next));
|
|
fprintf(fp, " list_head_prev: %ld\n",
|
|
OFFSET(list_head_prev));
|
|
fprintf(fp, " task_struct_pid: %ld\n",
|
|
OFFSET(task_struct_pid));
|
|
fprintf(fp, " task_struct_state: %ld\n",
|
|
OFFSET(task_struct_state));
|
|
fprintf(fp, " task_struct_exit_state: %ld\n",
|
|
OFFSET(task_struct_exit_state));
|
|
fprintf(fp, " task_struct_comm: %ld\n",
|
|
OFFSET(task_struct_comm));
|
|
fprintf(fp, " task_struct_mm: %ld\n",
|
|
OFFSET(task_struct_mm));
|
|
fprintf(fp, " task_struct_tss: %ld\n",
|
|
OFFSET(task_struct_tss));
|
|
fprintf(fp, " task_struct_thread: %ld\n",
|
|
OFFSET(task_struct_thread));
|
|
fprintf(fp, " task_struct_active_mm: %ld\n",
|
|
OFFSET(task_struct_active_mm));
|
|
fprintf(fp, " task_struct_tss_eip: %ld\n",
|
|
OFFSET(task_struct_tss_eip));
|
|
fprintf(fp, " task_struct_tss_esp: %ld\n",
|
|
OFFSET(task_struct_tss_esp));
|
|
fprintf(fp, " task_struct_tss_ksp: %ld\n",
|
|
OFFSET(task_struct_tss_ksp));
|
|
fprintf(fp, " task_struct_thread_eip: %ld\n",
|
|
OFFSET(task_struct_thread_eip));
|
|
fprintf(fp, " inactive_task_frame_ret_addr: %ld\n",
|
|
OFFSET(inactive_task_frame_ret_addr));
|
|
fprintf(fp, " task_struct_thread_esp: %ld\n",
|
|
OFFSET(task_struct_thread_esp));
|
|
fprintf(fp, " task_struct_thread_ksp: %ld\n",
|
|
OFFSET(task_struct_thread_ksp));
|
|
fprintf(fp, " task_struct_thread_reg29: %ld\n",
|
|
OFFSET(task_struct_thread_reg29));
|
|
fprintf(fp, " task_struct_thread_reg31: %ld\n",
|
|
OFFSET(task_struct_thread_reg31));
|
|
fprintf(fp, " task_struct_thread_context_fp: %ld\n",
|
|
OFFSET(task_struct_thread_context_fp));
|
|
fprintf(fp, " task_struct_thread_context_sp: %ld\n",
|
|
OFFSET(task_struct_thread_context_sp));
|
|
fprintf(fp, " task_struct_thread_context_pc: %ld\n",
|
|
OFFSET(task_struct_thread_context_pc));
|
|
fprintf(fp, " task_struct_processor: %ld\n",
|
|
OFFSET(task_struct_processor));
|
|
fprintf(fp, " task_struct_p_pptr: %ld\n",
|
|
OFFSET(task_struct_p_pptr));
|
|
fprintf(fp, " task_struct_parent: %ld\n",
|
|
OFFSET(task_struct_parent));
|
|
fprintf(fp, " task_struct_has_cpu: %ld\n",
|
|
OFFSET(task_struct_has_cpu));
|
|
fprintf(fp, " task_struct_cpus_runnable: %ld\n",
|
|
OFFSET(task_struct_cpus_runnable));
|
|
fprintf(fp, " task_struct_next_task: %ld\n",
|
|
OFFSET(task_struct_next_task));
|
|
fprintf(fp, " task_struct_files: %ld\n",
|
|
OFFSET(task_struct_files));
|
|
fprintf(fp, " task_struct_fs: %ld\n",
|
|
OFFSET(task_struct_fs));
|
|
fprintf(fp, " task_struct_pidhash_next: %ld\n",
|
|
OFFSET(task_struct_pidhash_next));
|
|
fprintf(fp, " task_struct_next_run: %ld\n",
|
|
OFFSET(task_struct_next_run));
|
|
fprintf(fp, " task_struct_flags: %ld\n",
|
|
OFFSET(task_struct_flags));
|
|
fprintf(fp, " task_struct_sig: %ld\n",
|
|
OFFSET(task_struct_sig));
|
|
fprintf(fp, " task_struct_signal: %ld\n",
|
|
OFFSET(task_struct_signal));
|
|
fprintf(fp, " task_struct_blocked: %ld\n",
|
|
OFFSET(task_struct_blocked));
|
|
fprintf(fp, " task_struct_sigpending: %ld\n",
|
|
OFFSET(task_struct_sigpending));
|
|
fprintf(fp, " task_struct_pending: %ld\n",
|
|
OFFSET(task_struct_pending));
|
|
fprintf(fp, " task_struct_sigqueue: %ld\n",
|
|
OFFSET(task_struct_sigqueue));
|
|
fprintf(fp, " task_struct_sighand: %ld\n",
|
|
OFFSET(task_struct_sighand));
|
|
fprintf(fp, " task_struct_run_list: %ld\n",
|
|
OFFSET(task_struct_run_list));
|
|
fprintf(fp, " task_struct_pgrp: %ld\n",
|
|
OFFSET(task_struct_pgrp));
|
|
fprintf(fp, " task_struct_tgid: %ld\n",
|
|
OFFSET(task_struct_tgid));
|
|
fprintf(fp, " task_struct_namespace: %ld\n",
|
|
OFFSET(task_struct_namespace));
|
|
fprintf(fp, " task_struct_rss_stat: %ld\n",
|
|
OFFSET(task_struct_rss_stat));
|
|
fprintf(fp, " task_rss_stat_count: %ld\n",
|
|
OFFSET(task_rss_stat_count));
|
|
fprintf(fp, " task_struct_pids: %ld\n",
|
|
OFFSET(task_struct_pids));
|
|
fprintf(fp, " task_struct_last_run: %ld\n",
|
|
OFFSET(task_struct_last_run));
|
|
fprintf(fp, " task_struct_timestamp: %ld\n",
|
|
OFFSET(task_struct_timestamp));
|
|
fprintf(fp, " task_struct_sched_info: %ld\n",
|
|
OFFSET(task_struct_sched_info));
|
|
fprintf(fp, " task_struct_rt: %ld\n",
|
|
OFFSET(task_struct_rt));
|
|
fprintf(fp, " sched_rt_entity_run_list: %ld\n",
|
|
OFFSET(sched_rt_entity_run_list));
|
|
fprintf(fp, " sched_info_last_arrival: %ld\n",
|
|
OFFSET(sched_info_last_arrival));
|
|
fprintf(fp, " task_struct_thread_info: %ld\n",
|
|
OFFSET(task_struct_thread_info));
|
|
fprintf(fp, " task_struct_stack: %ld\n",
|
|
OFFSET(task_struct_stack));
|
|
fprintf(fp, " task_struct_nsproxy: %ld\n",
|
|
OFFSET(task_struct_nsproxy));
|
|
fprintf(fp, " task_struct_rlim: %ld\n",
|
|
OFFSET(task_struct_rlim));
|
|
fprintf(fp, " task_struct_prio: %ld\n",
|
|
OFFSET(task_struct_prio));
|
|
fprintf(fp, " task_struct_on_rq: %ld\n",
|
|
OFFSET(task_struct_on_rq));
|
|
fprintf(fp, " task_struct_policy: %ld\n",
|
|
OFFSET(task_struct_policy));
|
|
|
|
fprintf(fp, " thread_info_task: %ld\n",
|
|
OFFSET(thread_info_task));
|
|
fprintf(fp, " thread_info_cpu: %ld\n",
|
|
OFFSET(thread_info_cpu));
|
|
fprintf(fp, " thread_info_flags: %ld\n",
|
|
OFFSET(thread_info_flags));
|
|
fprintf(fp, " thread_info_previous_esp: %ld\n",
|
|
OFFSET(thread_info_previous_esp));
|
|
|
|
fprintf(fp, " nsproxy_mnt_ns: %ld\n",
|
|
OFFSET(nsproxy_mnt_ns));
|
|
fprintf(fp, " mnt_namespace_root: %ld\n",
|
|
OFFSET(mnt_namespace_root));
|
|
fprintf(fp, " mnt_namespace_list: %ld\n",
|
|
OFFSET(mnt_namespace_list));
|
|
|
|
fprintf(fp, " pid_namespace_idr: %ld\n",
|
|
OFFSET(pid_namespace_idr));
|
|
fprintf(fp, " idr_idr_rt: %ld\n",
|
|
OFFSET(idr_idr_rt));
|
|
fprintf(fp, " pid_link_pid: %ld\n",
|
|
OFFSET(pid_link_pid));
|
|
fprintf(fp, " pid_hash_chain: %ld\n",
|
|
OFFSET(pid_hash_chain));
|
|
|
|
fprintf(fp, " pid_numbers: %ld\n",
|
|
OFFSET(pid_numbers));
|
|
|
|
fprintf(fp, " upid_nr: %ld\n",
|
|
OFFSET(upid_nr));
|
|
fprintf(fp, " upid_ns: %ld\n",
|
|
OFFSET(upid_ns));
|
|
fprintf(fp, " upid_pid_chain: %ld\n",
|
|
OFFSET(upid_pid_chain));
|
|
|
|
fprintf(fp, " pid_tasks: %ld\n",
|
|
OFFSET(pid_tasks));
|
|
|
|
fprintf(fp, " hlist_node_next: %ld\n",
|
|
OFFSET(hlist_node_next));
|
|
fprintf(fp, " hlist_node_pprev: %ld\n",
|
|
OFFSET(hlist_node_pprev));
|
|
fprintf(fp, " pid_pid_chain: %ld\n",
|
|
OFFSET(pid_pid_chain));
|
|
|
|
fprintf(fp, " thread_struct_eip: %ld\n",
|
|
OFFSET(thread_struct_eip));
|
|
fprintf(fp, " thread_struct_esp: %ld\n",
|
|
OFFSET(thread_struct_esp));
|
|
fprintf(fp, " thread_struct_ksp: %ld\n",
|
|
OFFSET(thread_struct_ksp));
|
|
fprintf(fp, " thread_struct_rip: %ld\n",
|
|
OFFSET(thread_struct_rip));
|
|
fprintf(fp, " thread_struct_rsp: %ld\n",
|
|
OFFSET(thread_struct_rsp));
|
|
fprintf(fp, " thread_struct_rsp0: %ld\n",
|
|
OFFSET(thread_struct_rsp0));
|
|
|
|
fprintf(fp, " signal_struct_count: %ld\n",
|
|
OFFSET(signal_struct_count));
|
|
fprintf(fp, " signal_struct_nr_threads: %ld\n",
|
|
OFFSET(signal_struct_nr_threads));
|
|
fprintf(fp, " signal_struct_action: %ld\n",
|
|
OFFSET(signal_struct_action));
|
|
fprintf(fp, " signal_struct_shared_pending: %ld\n",
|
|
OFFSET(signal_struct_shared_pending));
|
|
fprintf(fp, " signal_struct_rlim: %ld\n",
|
|
OFFSET(signal_struct_rlim));
|
|
|
|
fprintf(fp, " task_struct_start_time: %ld\n",
|
|
OFFSET(task_struct_start_time));
|
|
fprintf(fp, " task_struct_times: %ld\n",
|
|
OFFSET(task_struct_times));
|
|
fprintf(fp, " task_struct_cpu: %ld\n",
|
|
OFFSET(task_struct_cpu));
|
|
fprintf(fp, " task_struct_utime: %ld\n",
|
|
OFFSET(task_struct_utime));
|
|
fprintf(fp, " task_struct_stime: %ld\n",
|
|
OFFSET(task_struct_stime));
|
|
|
|
fprintf(fp, " tms_tms_utime: %ld\n",
|
|
OFFSET(tms_tms_utime));
|
|
fprintf(fp, " tms_tms_stime: %ld\n",
|
|
OFFSET(tms_tms_stime));
|
|
|
|
fprintf(fp, " timekeeper_xtime: %ld\n",
|
|
OFFSET(timekeeper_xtime));
|
|
fprintf(fp, " timekeeper_xtime_sec: %ld\n",
|
|
OFFSET(timekeeper_xtime_sec));
|
|
|
|
fprintf(fp, " k_sigaction_sa: %ld\n",
|
|
OFFSET(k_sigaction_sa));
|
|
|
|
fprintf(fp, " sigaction_sa_handler: %ld\n",
|
|
OFFSET(sigaction_sa_handler));
|
|
fprintf(fp, " sigaction_sa_flags: %ld\n",
|
|
OFFSET(sigaction_sa_flags));
|
|
fprintf(fp, " sigaction_sa_mask: %ld\n",
|
|
OFFSET(sigaction_sa_mask));
|
|
|
|
fprintf(fp, " sigpending_head: %ld\n",
|
|
OFFSET(sigpending_head));
|
|
fprintf(fp, " sigpending_signal: %ld\n",
|
|
OFFSET(sigpending_signal));
|
|
fprintf(fp, " sigpending_list: %ld\n",
|
|
OFFSET(sigpending_list));
|
|
|
|
fprintf(fp, " signal_queue_next: %ld\n",
|
|
OFFSET(signal_queue_next));
|
|
fprintf(fp, " signal_queue_info: %ld\n",
|
|
OFFSET(signal_queue_info));
|
|
|
|
fprintf(fp, " sigqueue_next: %ld\n",
|
|
OFFSET(sigqueue_next));
|
|
fprintf(fp, " sigqueue_info: %ld\n",
|
|
OFFSET(sigqueue_info));
|
|
fprintf(fp, " sigqueue_list: %ld\n",
|
|
OFFSET(sigqueue_list));
|
|
|
|
fprintf(fp, " sighand_struct_action: %ld\n",
|
|
OFFSET(sighand_struct_action));
|
|
|
|
fprintf(fp, " siginfo_si_signo: %ld\n",
|
|
OFFSET(siginfo_si_signo));
|
|
|
|
fprintf(fp, " thread_struct_fph: %ld\n",
|
|
OFFSET(thread_struct_fph));
|
|
fprintf(fp, " thread_struct_cr3: %ld\n",
|
|
OFFSET(thread_struct_cr3));
|
|
fprintf(fp, " thread_struct_ptbr: %ld\n",
|
|
OFFSET(thread_struct_ptbr));
|
|
fprintf(fp, " thread_struct_pg_tables: %ld\n",
|
|
OFFSET(thread_struct_pg_tables));
|
|
|
|
fprintf(fp, " switch_stack_r26: %ld\n",
|
|
OFFSET(switch_stack_r26));
|
|
fprintf(fp, " switch_stack_b0: %ld\n",
|
|
OFFSET(switch_stack_b0));
|
|
fprintf(fp, " switch_stack_ar_bspstore: %ld\n",
|
|
OFFSET(switch_stack_ar_bspstore));
|
|
fprintf(fp, " switch_stack_ar_pfs: %ld\n",
|
|
OFFSET(switch_stack_ar_pfs));
|
|
fprintf(fp, " switch_stack_ar_rnat: %ld\n",
|
|
OFFSET(switch_stack_ar_rnat));
|
|
fprintf(fp, " switch_stack_pr: %ld\n",
|
|
OFFSET(switch_stack_pr));
|
|
fprintf(fp, " cpuinfo_ia64_proc_freq: %ld\n",
|
|
OFFSET(cpuinfo_ia64_proc_freq));
|
|
fprintf(fp, " cpuinfo_ia64_unimpl_va_mask: %ld\n",
|
|
OFFSET(cpuinfo_ia64_unimpl_va_mask));
|
|
fprintf(fp, " cpuinfo_ia64_unimpl_pa_mask: %ld\n",
|
|
OFFSET(cpuinfo_ia64_unimpl_pa_mask));
|
|
|
|
fprintf(fp, " device_node_type: %ld\n",
|
|
OFFSET(device_node_type));
|
|
fprintf(fp, " device_node_allnext: %ld\n",
|
|
OFFSET(device_node_allnext));
|
|
fprintf(fp, " device_node_properties: %ld\n",
|
|
OFFSET(device_node_properties));
|
|
fprintf(fp, " property_name: %ld\n",
|
|
OFFSET(property_name));
|
|
fprintf(fp, " property_value: %ld\n",
|
|
OFFSET(property_value));
|
|
fprintf(fp, " property_next: %ld\n",
|
|
OFFSET(property_next));
|
|
fprintf(fp, " machdep_calls_setup_residual: %ld\n",
|
|
OFFSET(machdep_calls_setup_residual));
|
|
fprintf(fp, " RESIDUAL_VitalProductData: %ld\n",
|
|
OFFSET(RESIDUAL_VitalProductData));
|
|
fprintf(fp, " VPD_ProcessorHz: %ld\n",
|
|
OFFSET(VPD_ProcessorHz));
|
|
fprintf(fp, " bd_info_bi_intfreq: %ld\n",
|
|
OFFSET(bd_info_bi_intfreq));
|
|
|
|
fprintf(fp, " hwrpb_struct_cycle_freq: %ld\n",
|
|
OFFSET(hwrpb_struct_cycle_freq));
|
|
fprintf(fp, " hwrpb_struct_processor_offset: %ld\n",
|
|
OFFSET(hwrpb_struct_processor_offset));
|
|
fprintf(fp, " hwrpb_struct_processor_size: %ld\n",
|
|
OFFSET(hwrpb_struct_processor_size));
|
|
fprintf(fp, " percpu_struct_halt_PC: %ld\n",
|
|
OFFSET(percpu_struct_halt_PC));
|
|
fprintf(fp, " percpu_struct_halt_ra: %ld\n",
|
|
OFFSET(percpu_struct_halt_ra));
|
|
fprintf(fp, " percpu_struct_halt_pv: %ld\n",
|
|
OFFSET(percpu_struct_halt_pv));
|
|
|
|
fprintf(fp, " mm_struct_mmap: %ld\n",
|
|
OFFSET(mm_struct_mmap));
|
|
fprintf(fp, " mm_struct_pgd: %ld\n",
|
|
OFFSET(mm_struct_pgd));
|
|
fprintf(fp, " mm_struct_mm_count: %ld\n",
|
|
OFFSET(mm_struct_mm_count));
|
|
fprintf(fp, " mm_struct_rss: %ld\n",
|
|
OFFSET(mm_struct_rss));
|
|
fprintf(fp, " mm_struct_anon_rss: %ld\n",
|
|
OFFSET(mm_struct_anon_rss));
|
|
fprintf(fp, " mm_struct_file_rss: %ld\n",
|
|
OFFSET(mm_struct_file_rss));
|
|
fprintf(fp, " mm_struct_total_vm: %ld\n",
|
|
OFFSET(mm_struct_total_vm));
|
|
fprintf(fp, " mm_struct_start_code: %ld\n",
|
|
OFFSET(mm_struct_start_code));
|
|
fprintf(fp, " mm_struct_arg_start: %ld\n",
|
|
OFFSET(mm_struct_arg_start));
|
|
fprintf(fp, " mm_struct_arg_end: %ld\n",
|
|
OFFSET(mm_struct_arg_end));
|
|
fprintf(fp, " mm_struct_env_start: %ld\n",
|
|
OFFSET(mm_struct_env_start));
|
|
fprintf(fp, " mm_struct_env_end: %ld\n",
|
|
OFFSET(mm_struct_env_end));
|
|
fprintf(fp, " mm_struct_rss_stat: %ld\n",
|
|
OFFSET(mm_struct_rss_stat));
|
|
fprintf(fp, " mm_rss_stat_count: %ld\n",
|
|
OFFSET(mm_rss_stat_count));
|
|
|
|
fprintf(fp, " vm_area_struct_vm_mm: %ld\n",
|
|
OFFSET(vm_area_struct_vm_mm));
|
|
fprintf(fp, " vm_area_struct_vm_next: %ld\n",
|
|
OFFSET(vm_area_struct_vm_next));
|
|
fprintf(fp, " vm_area_struct_vm_start: %ld\n",
|
|
OFFSET(vm_area_struct_vm_start));
|
|
fprintf(fp, " vm_area_struct_vm_end: %ld\n",
|
|
OFFSET(vm_area_struct_vm_end));
|
|
fprintf(fp, " vm_area_struct_vm_flags: %ld\n",
|
|
OFFSET(vm_area_struct_vm_flags));
|
|
|
|
fprintf(fp, " vm_area_struct_vm_file: %ld\n",
|
|
OFFSET(vm_area_struct_vm_file));
|
|
fprintf(fp, " vm_area_struct_vm_offset: %ld\n",
|
|
OFFSET(vm_area_struct_vm_offset));
|
|
fprintf(fp, " vm_area_struct_vm_pgoff: %ld\n",
|
|
OFFSET(vm_area_struct_vm_pgoff));
|
|
|
|
fprintf(fp, " vm_struct_addr: %ld\n",
|
|
OFFSET(vm_struct_addr));
|
|
fprintf(fp, " vm_struct_size: %ld\n",
|
|
OFFSET(vm_struct_size));
|
|
fprintf(fp, " vm_struct_next: %ld\n",
|
|
OFFSET(vm_struct_next));
|
|
|
|
fprintf(fp, " vmap_area_va_start: %ld\n",
|
|
OFFSET(vmap_area_va_start));
|
|
fprintf(fp, " vmap_area_va_end: %ld\n",
|
|
OFFSET(vmap_area_va_end));
|
|
fprintf(fp, " vmap_area_list: %ld\n",
|
|
OFFSET(vmap_area_list));
|
|
fprintf(fp, " vmap_area_vm: %ld\n",
|
|
OFFSET(vmap_area_vm));
|
|
fprintf(fp, " vmap_area_flags: %ld\n",
|
|
OFFSET(vmap_area_flags));
|
|
|
|
fprintf(fp, " module_size_of_struct: %ld\n",
|
|
OFFSET(module_size_of_struct));
|
|
fprintf(fp, " module_next: %ld\n",
|
|
OFFSET(module_next));
|
|
fprintf(fp, " module_name: %ld\n",
|
|
OFFSET(module_name));
|
|
fprintf(fp, " module_syms: %ld\n",
|
|
OFFSET(module_syms));
|
|
fprintf(fp, " module_nsyms: %ld\n",
|
|
OFFSET(module_nsyms));
|
|
fprintf(fp, " module_size: %ld\n",
|
|
OFFSET(module_size));
|
|
fprintf(fp, " module_flags: %ld\n",
|
|
OFFSET(module_flags));
|
|
fprintf(fp, " module_num_syms: %ld\n",
|
|
OFFSET(module_num_syms));
|
|
fprintf(fp, " module_gpl_syms: %ld\n",
|
|
OFFSET(module_gpl_syms));
|
|
fprintf(fp, " module_num_gpl_syms: %ld\n",
|
|
OFFSET(module_num_gpl_syms));
|
|
fprintf(fp, " module_list: %ld\n",
|
|
OFFSET(module_list));
|
|
fprintf(fp, " module_module_core: %ld\n",
|
|
OFFSET(module_module_core));
|
|
fprintf(fp, " module_core_size: %ld\n",
|
|
OFFSET(module_core_size));
|
|
fprintf(fp, " module_core_text_size: %ld\n",
|
|
OFFSET(module_core_text_size));
|
|
fprintf(fp, " module_init_size: %ld\n",
|
|
OFFSET(module_init_size));
|
|
fprintf(fp, " module_init_text_size: %ld\n",
|
|
OFFSET(module_init_text_size));
|
|
fprintf(fp, " module_module_init: %ld\n",
|
|
OFFSET(module_module_init));
|
|
fprintf(fp, " module_num_symtab: %ld\n",
|
|
OFFSET(module_num_symtab));
|
|
fprintf(fp, " module_symtab: %ld\n",
|
|
OFFSET(module_symtab));
|
|
fprintf(fp, " module_strtab: %ld\n",
|
|
OFFSET(module_strtab));
|
|
fprintf(fp, " module_percpu: %ld\n",
|
|
OFFSET(module_percpu));
|
|
|
|
fprintf(fp, " module_sect_attrs: %ld\n",
|
|
OFFSET(module_sect_attrs));
|
|
fprintf(fp, " module_sect_attrs_attrs: %ld\n",
|
|
OFFSET(module_sect_attrs_attrs));
|
|
fprintf(fp, " module_sect_attrs_nsections: %ld\n",
|
|
OFFSET(module_sect_attrs_nsections));
|
|
fprintf(fp, " module_sect_attr_mattr: %ld\n",
|
|
OFFSET(module_sect_attr_mattr));
|
|
fprintf(fp, " module_sect_attr_name: %ld\n",
|
|
OFFSET(module_sect_attr_name));
|
|
fprintf(fp, " module_sect_attr_address: %ld\n",
|
|
OFFSET(module_sect_attr_address));
|
|
fprintf(fp, " attribute_owner: %ld\n",
|
|
OFFSET(attribute_owner));
|
|
fprintf(fp, " module_sect_attr_attr: %ld\n",
|
|
OFFSET(module_sect_attr_attr));
|
|
fprintf(fp, " module_sections_attrs: %ld\n",
|
|
OFFSET(module_sections_attrs));
|
|
fprintf(fp, " module_attribute_attr: %ld\n",
|
|
OFFSET(module_attribute_attr));
|
|
|
|
fprintf(fp, " module_kallsyms_start: %ld\n",
|
|
OFFSET(module_kallsyms_start));
|
|
fprintf(fp, " kallsyms_header_sections: %ld\n",
|
|
OFFSET(kallsyms_header_sections));
|
|
fprintf(fp, " kallsyms_header_section_off: %ld\n",
|
|
OFFSET(kallsyms_header_section_off));
|
|
fprintf(fp, " kallsyms_header_symbols: %ld\n",
|
|
OFFSET(kallsyms_header_symbols));
|
|
fprintf(fp, " kallsyms_header_symbol_off: %ld\n",
|
|
OFFSET(kallsyms_header_symbol_off));
|
|
fprintf(fp, " kallsyms_header_string_off: %ld\n",
|
|
OFFSET(kallsyms_header_string_off));
|
|
fprintf(fp, " kallsyms_symbol_section_off: %ld\n",
|
|
OFFSET(kallsyms_symbol_section_off));
|
|
fprintf(fp, " kallsyms_symbol_symbol_addr: %ld\n",
|
|
OFFSET(kallsyms_symbol_symbol_addr));
|
|
fprintf(fp, " kallsyms_symbol_name_off: %ld\n",
|
|
OFFSET(kallsyms_symbol_name_off));
|
|
fprintf(fp, " kallsyms_section_start: %ld\n",
|
|
OFFSET(kallsyms_section_start));
|
|
fprintf(fp, " kallsyms_section_size: %ld\n",
|
|
OFFSET(kallsyms_section_size));
|
|
fprintf(fp, " kallsyms_section_name_off: %ld\n",
|
|
OFFSET(kallsyms_section_name_off));
|
|
|
|
fprintf(fp, " module_taints: %ld\n",
|
|
OFFSET(module_taints));
|
|
fprintf(fp, " module_license_gplok: %ld\n",
|
|
OFFSET(module_license_gplok));
|
|
fprintf(fp, " module_gpgsig_ok: %ld\n",
|
|
OFFSET(module_gpgsig_ok));
|
|
fprintf(fp, " tnt_bit: %ld\n", OFFSET(tnt_bit));
|
|
fprintf(fp, " tnt_true: %ld\n", OFFSET(tnt_true));
|
|
fprintf(fp, " tnt_false: %ld\n", OFFSET(tnt_false));
|
|
fprintf(fp, " tnt_mod: %ld\n", OFFSET(tnt_mod));
|
|
|
|
fprintf(fp, " page_next: %ld\n", OFFSET(page_next));
|
|
fprintf(fp, " page_prev: %ld\n", OFFSET(page_prev));
|
|
fprintf(fp, " page_next_hash: %ld\n",
|
|
OFFSET(page_next_hash));
|
|
fprintf(fp, " page_list: %ld\n",
|
|
OFFSET(page_list));
|
|
fprintf(fp, " page_list_next: %ld\n",
|
|
OFFSET(page_list_next));
|
|
fprintf(fp, " page_list_prev: %ld\n",
|
|
OFFSET(page_list_prev));
|
|
fprintf(fp, " page_inode: %ld\n",
|
|
OFFSET(page_inode));
|
|
fprintf(fp, " page_offset: %ld\n",
|
|
OFFSET(page_offset));
|
|
fprintf(fp, " page_count: %ld\n",
|
|
OFFSET(page_count));
|
|
fprintf(fp, " page_flags: %ld\n",
|
|
OFFSET(page_flags));
|
|
fprintf(fp, " page_mapping: %ld\n",
|
|
OFFSET(page_mapping));
|
|
fprintf(fp, " page_index: %ld\n",
|
|
OFFSET(page_index));
|
|
fprintf(fp, " page_buffers: %ld\n",
|
|
OFFSET(page_buffers));
|
|
fprintf(fp, " page_lru: %ld\n",
|
|
OFFSET(page_lru));
|
|
fprintf(fp, " page_pte: %ld\n",
|
|
OFFSET(page_pte));
|
|
|
|
fprintf(fp, " page_inuse: %ld\n",
|
|
OFFSET(page_inuse));
|
|
fprintf(fp, " page_objects: %ld\n",
|
|
OFFSET(page_objects));
|
|
fprintf(fp, " page_slab: %ld\n",
|
|
OFFSET(page_slab));
|
|
fprintf(fp, " page_slab_page: %ld\n",
|
|
OFFSET(page_slab_page));
|
|
fprintf(fp, " page_first_page: %ld\n",
|
|
OFFSET(page_first_page));
|
|
fprintf(fp, " page_freelist: %ld\n",
|
|
OFFSET(page_freelist));
|
|
fprintf(fp, " page_s_mem: %ld\n",
|
|
OFFSET(page_s_mem));
|
|
fprintf(fp, " page_active: %ld\n",
|
|
OFFSET(page_active));
|
|
fprintf(fp, " page_compound_head: %ld\n",
|
|
OFFSET(page_compound_head));
|
|
|
|
fprintf(fp, " trace_print_flags_mask: %ld\n",
|
|
OFFSET(trace_print_flags_mask));
|
|
fprintf(fp, " trace_print_flags_name: %ld\n",
|
|
OFFSET(trace_print_flags_name));
|
|
|
|
fprintf(fp, " swap_info_struct_swap_file: %ld\n",
|
|
OFFSET(swap_info_struct_swap_file));
|
|
fprintf(fp, " swap_info_struct_swap_vfsmnt: %ld\n",
|
|
OFFSET(swap_info_struct_swap_vfsmnt));
|
|
fprintf(fp, " swap_info_struct_flags: %ld\n",
|
|
OFFSET(swap_info_struct_flags));
|
|
fprintf(fp, " swap_info_struct_swap_map: %ld\n",
|
|
OFFSET(swap_info_struct_swap_map));
|
|
fprintf(fp, " swap_info_struct_swap_device: %ld\n",
|
|
OFFSET(swap_info_struct_swap_device));
|
|
fprintf(fp, " swap_info_struct_prio: %ld\n",
|
|
OFFSET(swap_info_struct_prio));
|
|
fprintf(fp, " swap_info_struct_max: %ld\n",
|
|
OFFSET(swap_info_struct_max));
|
|
fprintf(fp, " swap_info_struct_pages: %ld\n",
|
|
OFFSET(swap_info_struct_pages));
|
|
fprintf(fp, " swap_info_struct_inuse_pages: %ld\n",
|
|
OFFSET(swap_info_struct_inuse_pages));
|
|
fprintf(fp, "swap_info_struct_old_block_size: %ld\n",
|
|
OFFSET(swap_info_struct_old_block_size));
|
|
fprintf(fp, " block_device_bd_inode: %ld\n",
|
|
OFFSET(block_device_bd_inode));
|
|
fprintf(fp, " block_device_bd_list: %ld\n",
|
|
OFFSET(block_device_bd_list));
|
|
fprintf(fp, " block_device_bd_disk: %ld\n",
|
|
OFFSET(block_device_bd_disk));
|
|
fprintf(fp, " address_space_nrpages: %ld\n",
|
|
OFFSET(address_space_nrpages));
|
|
fprintf(fp, " address_space_page_tree: %ld\n",
|
|
OFFSET(address_space_page_tree));
|
|
fprintf(fp, " gendisk_major: %ld\n",
|
|
OFFSET(gendisk_major));
|
|
fprintf(fp, " gendisk_fops: %ld\n",
|
|
OFFSET(gendisk_fops));
|
|
fprintf(fp, " gendisk_disk_name: %ld\n",
|
|
OFFSET(gendisk_disk_name));
|
|
|
|
fprintf(fp, " irq_desc_t_status: %ld\n",
|
|
OFFSET(irq_desc_t_status));
|
|
fprintf(fp, " irq_desc_t_handler: %ld\n",
|
|
OFFSET(irq_desc_t_handler));
|
|
fprintf(fp, " irq_desc_t_chip: %ld\n",
|
|
OFFSET(irq_desc_t_chip));
|
|
fprintf(fp, " irq_desc_t_action: %ld\n",
|
|
OFFSET(irq_desc_t_action));
|
|
fprintf(fp, " irq_desc_t_depth: %ld\n",
|
|
OFFSET(irq_desc_t_depth));
|
|
|
|
fprintf(fp, " irqdesc_action: %ld\n",
|
|
OFFSET(irqdesc_action));
|
|
fprintf(fp, " irqdesc_ctl: %ld\n",
|
|
OFFSET(irqdesc_ctl));
|
|
fprintf(fp, " irqdesc_level: %ld\n",
|
|
OFFSET(irqdesc_level));
|
|
|
|
fprintf(fp, " irq_desc_t_irq_data: %ld\n",
|
|
OFFSET(irq_desc_t_irq_data));
|
|
fprintf(fp, " irq_desc_t_kstat_irqs: %ld\n",
|
|
OFFSET(irq_desc_t_kstat_irqs));
|
|
fprintf(fp, " irq_desc_t_affinity: %ld\n",
|
|
OFFSET(irq_desc_t_affinity));
|
|
fprintf(fp, " irq_data_chip: %ld\n",
|
|
OFFSET(irq_data_chip));
|
|
fprintf(fp, " irq_data_affinity: %ld\n",
|
|
OFFSET(irq_data_affinity));
|
|
fprintf(fp, " irq_desc_irq_data: %ld\n",
|
|
OFFSET(irq_desc_irq_data));
|
|
fprintf(fp, " kernel_stat_irqs: %ld\n",
|
|
OFFSET(kernel_stat_irqs));
|
|
|
|
fprintf(fp, " irqaction_handler: %ld\n",
|
|
OFFSET(irqaction_handler));
|
|
fprintf(fp, " irqaction_flags: %ld\n",
|
|
OFFSET(irqaction_flags));
|
|
fprintf(fp, " irqaction_mask: %ld\n",
|
|
OFFSET(irqaction_mask));
|
|
fprintf(fp, " irqaction_name: %ld\n",
|
|
OFFSET(irqaction_name));
|
|
fprintf(fp, " irqaction_dev_id: %ld\n",
|
|
OFFSET(irqaction_dev_id));
|
|
fprintf(fp, " irqaction_next: %ld\n",
|
|
OFFSET(irqaction_next));
|
|
|
|
|
|
fprintf(fp, " hw_interrupt_type_typename: %ld\n",
|
|
OFFSET(hw_interrupt_type_typename));
|
|
fprintf(fp, " hw_interrupt_type_startup: %ld\n",
|
|
OFFSET(hw_interrupt_type_startup));
|
|
fprintf(fp, " hw_interrupt_type_shutdown: %ld\n",
|
|
OFFSET(hw_interrupt_type_shutdown));
|
|
fprintf(fp, " hw_interrupt_type_handle: %ld\n",
|
|
OFFSET(hw_interrupt_type_handle));
|
|
fprintf(fp, " hw_interrupt_type_enable: %ld\n",
|
|
OFFSET(hw_interrupt_type_enable));
|
|
fprintf(fp, " hw_interrupt_type_disable: %ld\n",
|
|
OFFSET(hw_interrupt_type_disable));
|
|
fprintf(fp, " hw_interrupt_type_ack: %ld\n",
|
|
OFFSET(hw_interrupt_type_ack));
|
|
fprintf(fp, " hw_interrupt_type_end: %ld\n",
|
|
OFFSET(hw_interrupt_type_end));
|
|
fprintf(fp, "hw_interrupt_type_set_affinity: %ld\n",
|
|
OFFSET(hw_interrupt_type_set_affinity));
|
|
|
|
fprintf(fp, " irq_chip_typename: %ld\n",
|
|
OFFSET(irq_chip_typename));
|
|
fprintf(fp, " irq_chip_startup: %ld\n",
|
|
OFFSET(irq_chip_startup));
|
|
fprintf(fp, " irq_chip_shutdown: %ld\n",
|
|
OFFSET(irq_chip_shutdown));
|
|
fprintf(fp, " irq_chip_enable: %ld\n",
|
|
OFFSET(irq_chip_enable));
|
|
fprintf(fp, " irq_chip_disable: %ld\n",
|
|
OFFSET(irq_chip_disable));
|
|
fprintf(fp, " irq_chip_ack: %ld\n",
|
|
OFFSET(irq_chip_ack));
|
|
fprintf(fp, " irq_chip_mask: %ld\n",
|
|
OFFSET(irq_chip_mask));
|
|
fprintf(fp, " irq_chip_mask_ack: %ld\n",
|
|
OFFSET(irq_chip_mask_ack));
|
|
fprintf(fp, " irq_chip_unmask: %ld\n",
|
|
OFFSET(irq_chip_unmask));
|
|
fprintf(fp, " irq_chip_eoi: %ld\n",
|
|
OFFSET(irq_chip_eoi));
|
|
fprintf(fp, " irq_chip_end: %ld\n",
|
|
OFFSET(irq_chip_end));
|
|
fprintf(fp, " irq_chip_set_affinity: %ld\n",
|
|
OFFSET(irq_chip_set_affinity));
|
|
fprintf(fp, " irq_chip_retrigger: %ld\n",
|
|
OFFSET(irq_chip_retrigger));
|
|
fprintf(fp, " irq_chip_set_type: %ld\n",
|
|
OFFSET(irq_chip_set_type));
|
|
fprintf(fp, " irq_chip_set_wake: %ld\n",
|
|
OFFSET(irq_chip_set_wake));
|
|
|
|
fprintf(fp, "irq_cpustat_t___softirq_active: %ld\n",
|
|
OFFSET(irq_cpustat_t___softirq_active));
|
|
fprintf(fp, " irq_cpustat_t___softirq_mask: %ld\n",
|
|
OFFSET(irq_cpustat_t___softirq_mask));
|
|
|
|
fprintf(fp, " files_struct_fdt: %ld\n",
|
|
OFFSET(files_struct_fdt));
|
|
fprintf(fp, " fdtable_max_fds: %ld\n",
|
|
OFFSET(fdtable_max_fds));
|
|
fprintf(fp, " fdtable_max_fdset: %ld\n",
|
|
OFFSET(fdtable_max_fdset));
|
|
fprintf(fp, " fdtable_open_fds: %ld\n",
|
|
OFFSET(fdtable_open_fds));
|
|
fprintf(fp, " fdtable_fd: %ld\n",
|
|
OFFSET(fdtable_fd));
|
|
fprintf(fp, " files_struct_max_fds: %ld\n",
|
|
OFFSET(files_struct_max_fds));
|
|
fprintf(fp, " files_struct_max_fdset: %ld\n",
|
|
OFFSET(files_struct_max_fdset));
|
|
fprintf(fp, " files_struct_open_fds: %ld\n",
|
|
OFFSET(files_struct_open_fds));
|
|
fprintf(fp, " files_struct_fd: %ld\n",
|
|
OFFSET(files_struct_fd));
|
|
fprintf(fp, " files_struct_open_fds_init: %ld\n",
|
|
OFFSET(files_struct_open_fds_init));
|
|
fprintf(fp, " file_f_dentry: %ld\n",
|
|
OFFSET(file_f_dentry));
|
|
fprintf(fp, " file_f_vfsmnt: %ld\n",
|
|
OFFSET(file_f_vfsmnt));
|
|
fprintf(fp, " file_f_count: %ld\n",
|
|
OFFSET(file_f_count));
|
|
fprintf(fp, " file_f_path: %ld\n",
|
|
OFFSET(file_f_path));
|
|
fprintf(fp, " path_mnt: %ld\n",
|
|
OFFSET(path_mnt));
|
|
fprintf(fp, " path_dentry: %ld\n",
|
|
OFFSET(path_dentry));
|
|
fprintf(fp, " fs_struct_root: %ld\n",
|
|
OFFSET(fs_struct_root));
|
|
fprintf(fp, " fs_struct_pwd: %ld\n",
|
|
OFFSET(fs_struct_pwd));
|
|
fprintf(fp, " fs_struct_rootmnt: %ld\n",
|
|
OFFSET(fs_struct_rootmnt));
|
|
fprintf(fp, " fs_struct_pwdmnt: %ld\n",
|
|
OFFSET(fs_struct_pwdmnt));
|
|
|
|
fprintf(fp, " dentry_d_inode: %ld\n",
|
|
OFFSET(dentry_d_inode));
|
|
fprintf(fp, " dentry_d_parent: %ld\n",
|
|
OFFSET(dentry_d_parent));
|
|
fprintf(fp, " dentry_d_name: %ld\n",
|
|
OFFSET(dentry_d_name));
|
|
fprintf(fp, " dentry_d_iname: %ld\n",
|
|
OFFSET(dentry_d_iname));
|
|
fprintf(fp, " dentry_d_covers: %ld\n",
|
|
OFFSET(dentry_d_covers));
|
|
fprintf(fp, " qstr_len: %ld\n", OFFSET(qstr_len));
|
|
fprintf(fp, " qstr_name: %ld\n", OFFSET(qstr_name));
|
|
fprintf(fp, " inode_i_mode: %ld\n",
|
|
OFFSET(inode_i_mode));
|
|
fprintf(fp, " inode_i_op: %ld\n",
|
|
OFFSET(inode_i_op));
|
|
fprintf(fp, " inode_i_sb: %ld\n",
|
|
OFFSET(inode_i_sb));
|
|
fprintf(fp, " inode_u: %ld\n", OFFSET(inode_u));
|
|
fprintf(fp, " inode_i_flock: %ld\n",
|
|
OFFSET(inode_i_flock));
|
|
fprintf(fp, " inode_i_fop: %ld\n",
|
|
OFFSET(inode_i_fop));
|
|
fprintf(fp, " inode_i_mapping: %ld\n",
|
|
OFFSET(inode_i_mapping));
|
|
|
|
fprintf(fp, " vfsmount_mnt_next: %ld\n",
|
|
OFFSET(vfsmount_mnt_next));
|
|
fprintf(fp, " vfsmount_mnt_devname: %ld\n",
|
|
OFFSET(vfsmount_mnt_devname));
|
|
fprintf(fp, " vfsmount_mnt_dirname: %ld\n",
|
|
OFFSET(vfsmount_mnt_dirname));
|
|
fprintf(fp, " vfsmount_mnt_sb: %ld\n",
|
|
OFFSET(vfsmount_mnt_sb));
|
|
fprintf(fp, " vfsmount_mnt_list: %ld\n",
|
|
OFFSET(vfsmount_mnt_list));
|
|
fprintf(fp, " vfsmount_mnt_mountpoint: %ld\n",
|
|
OFFSET(vfsmount_mnt_mountpoint));
|
|
fprintf(fp, " vfsmount_mnt_parent: %ld\n",
|
|
OFFSET(vfsmount_mnt_parent));
|
|
fprintf(fp, " mount_mnt_parent: %ld\n",
|
|
OFFSET(mount_mnt_parent));
|
|
fprintf(fp, " mount_mnt_mountpoint: %ld\n",
|
|
OFFSET(mount_mnt_mountpoint));
|
|
fprintf(fp, " mount_mnt_list: %ld\n",
|
|
OFFSET(mount_mnt_list));
|
|
fprintf(fp, " mount_mnt_devname: %ld\n",
|
|
OFFSET(mount_mnt_devname));
|
|
fprintf(fp, " mount_mnt: %ld\n",
|
|
OFFSET(mount_mnt));
|
|
fprintf(fp, " namespace_root: %ld\n",
|
|
OFFSET(namespace_root));
|
|
fprintf(fp, " namespace_list: %ld\n",
|
|
OFFSET(namespace_list));
|
|
|
|
fprintf(fp, " super_block_s_dirty: %ld\n",
|
|
OFFSET(super_block_s_dirty));
|
|
fprintf(fp, " super_block_s_type: %ld\n",
|
|
OFFSET(super_block_s_type));
|
|
fprintf(fp, " super_block_s_files: %ld\n",
|
|
OFFSET(super_block_s_files));
|
|
|
|
fprintf(fp, " nlm_file_f_file: %ld\n",
|
|
OFFSET(nlm_file_f_file));
|
|
|
|
fprintf(fp, " file_system_type_name: %ld\n",
|
|
OFFSET(file_system_type_name));
|
|
|
|
fprintf(fp, " file_lock_fl_owner: %ld\n",
|
|
OFFSET(file_lock_fl_owner));
|
|
fprintf(fp, " nlm_host_h_exportent: %ld\n",
|
|
OFFSET(nlm_host_h_exportent));
|
|
fprintf(fp, " svc_client_cl_ident: %ld\n",
|
|
OFFSET(svc_client_cl_ident));
|
|
|
|
fprintf(fp, " kmem_cache_s_c_nextp: %ld\n",
|
|
OFFSET(kmem_cache_s_c_nextp));
|
|
fprintf(fp, " kmem_cache_s_c_name: %ld\n",
|
|
OFFSET(kmem_cache_s_c_name));
|
|
fprintf(fp, " kmem_cache_s_c_num: %ld\n",
|
|
OFFSET(kmem_cache_s_c_num));
|
|
fprintf(fp, " kmem_cache_s_c_org_size: %ld\n",
|
|
OFFSET(kmem_cache_s_c_org_size));
|
|
fprintf(fp, " kmem_cache_s_c_flags: %ld\n",
|
|
OFFSET(kmem_cache_s_c_flags));
|
|
fprintf(fp, " kmem_cache_s_c_offset: %ld\n",
|
|
OFFSET(kmem_cache_s_c_offset));
|
|
fprintf(fp, " kmem_cache_s_c_firstp: %ld\n",
|
|
OFFSET(kmem_cache_s_c_firstp));
|
|
fprintf(fp, " kmem_cache_s_c_gfporder: %ld\n",
|
|
OFFSET(kmem_cache_s_c_gfporder));
|
|
fprintf(fp, " kmem_cache_s_c_magic: %ld\n",
|
|
OFFSET(kmem_cache_s_c_magic));
|
|
fprintf(fp, " kmem_cache_s_c_align: %ld\n",
|
|
OFFSET(kmem_cache_s_c_align));
|
|
|
|
fprintf(fp, " kmem_cache_s_num: %ld\n",
|
|
OFFSET(kmem_cache_s_num));
|
|
fprintf(fp, " kmem_cache_s_next: %ld\n",
|
|
OFFSET(kmem_cache_s_next));
|
|
fprintf(fp, " kmem_cache_s_name: %ld\n",
|
|
OFFSET(kmem_cache_s_name));
|
|
fprintf(fp, " kmem_cache_s_objsize: %ld\n",
|
|
OFFSET(kmem_cache_s_objsize));
|
|
fprintf(fp, " kmem_cache_s_flags: %ld\n",
|
|
OFFSET(kmem_cache_s_flags));
|
|
fprintf(fp, " kmem_cache_s_gfporder: %ld\n",
|
|
OFFSET(kmem_cache_s_gfporder));
|
|
fprintf(fp, " kmem_cache_s_slabs: %ld\n",
|
|
OFFSET(kmem_cache_s_slabs));
|
|
fprintf(fp, " kmem_cache_s_slabs_full: %ld\n",
|
|
OFFSET(kmem_cache_s_slabs_full));
|
|
fprintf(fp, " kmem_cache_s_slabs_partial: %ld\n",
|
|
OFFSET(kmem_cache_s_slabs_partial));
|
|
fprintf(fp, " kmem_cache_s_slabs_free: %ld\n",
|
|
OFFSET(kmem_cache_s_slabs_free));
|
|
fprintf(fp, " kmem_cache_s_cpudata: %ld\n",
|
|
OFFSET(kmem_cache_s_cpudata));
|
|
fprintf(fp, " kmem_cache_s_colour_off: %ld\n",
|
|
OFFSET(kmem_cache_s_colour_off));
|
|
|
|
fprintf(fp, " cpucache_s_avail: %ld\n",
|
|
OFFSET(cpucache_s_avail));
|
|
fprintf(fp, " cpucache_s_limit: %ld\n",
|
|
OFFSET(cpucache_s_limit));
|
|
fprintf(fp, " array_cache_avail: %ld\n",
|
|
OFFSET(array_cache_avail));
|
|
fprintf(fp, " array_cache_limit: %ld\n",
|
|
OFFSET(array_cache_limit));
|
|
|
|
fprintf(fp, " kmem_cache_s_array: %ld\n",
|
|
OFFSET(kmem_cache_s_array));
|
|
fprintf(fp, " kmem_cache_s_lists: %ld\n",
|
|
OFFSET(kmem_cache_s_lists));
|
|
fprintf(fp, " kmem_list3_slabs_partial: %ld\n",
|
|
OFFSET(kmem_list3_slabs_partial));
|
|
fprintf(fp, " kmem_list3_slabs_full: %ld\n",
|
|
OFFSET(kmem_list3_slabs_full));
|
|
fprintf(fp, " kmem_list3_slabs_free: %ld\n",
|
|
OFFSET(kmem_list3_slabs_free));
|
|
fprintf(fp, " kmem_list3_free_objects: %ld\n",
|
|
OFFSET(kmem_list3_free_objects));
|
|
fprintf(fp, " kmem_list3_shared: %ld\n",
|
|
OFFSET(kmem_list3_shared));
|
|
|
|
fprintf(fp, " kmem_slab_s_s_nextp: %ld\n",
|
|
OFFSET(kmem_slab_s_s_nextp));
|
|
fprintf(fp, " kmem_slab_s_s_freep: %ld\n",
|
|
OFFSET(kmem_slab_s_s_freep));
|
|
fprintf(fp, " kmem_slab_s_s_inuse: %ld\n",
|
|
OFFSET(kmem_slab_s_s_inuse));
|
|
fprintf(fp, " kmem_slab_s_s_mem: %ld\n",
|
|
OFFSET(kmem_slab_s_s_mem));
|
|
fprintf(fp, " kmem_slab_s_s_index: %ld\n",
|
|
OFFSET(kmem_slab_s_s_index));
|
|
fprintf(fp, " kmem_slab_s_s_offset: %ld\n",
|
|
OFFSET(kmem_slab_s_s_offset));
|
|
fprintf(fp, " kmem_slab_s_s_magic: %ld\n",
|
|
OFFSET(kmem_slab_s_s_magic));
|
|
|
|
fprintf(fp, " slab_s_list: %ld\n",
|
|
OFFSET(slab_s_list));
|
|
fprintf(fp, " slab_s_s_mem: %ld\n",
|
|
OFFSET(slab_s_s_mem));
|
|
fprintf(fp, " slab_s_inuse: %ld\n",
|
|
OFFSET(slab_s_inuse));
|
|
fprintf(fp, " slab_s_free: %ld\n",
|
|
OFFSET(slab_s_free));
|
|
|
|
fprintf(fp, " slab_list: %ld\n",
|
|
OFFSET(slab_list));
|
|
fprintf(fp, " slab_s_mem: %ld\n",
|
|
OFFSET(slab_s_mem));
|
|
fprintf(fp, " slab_inuse: %ld\n",
|
|
OFFSET(slab_inuse));
|
|
fprintf(fp, " slab_free: %ld\n",
|
|
OFFSET(slab_free));
|
|
|
|
fprintf(fp, " kmem_cache_size: %ld\n",
|
|
OFFSET(kmem_cache_size));
|
|
fprintf(fp, " kmem_cache_objsize: %ld\n",
|
|
OFFSET(kmem_cache_objsize));
|
|
fprintf(fp, " kmem_cache_offset: %ld\n",
|
|
OFFSET(kmem_cache_offset));
|
|
fprintf(fp, " kmem_cache_order: %ld\n",
|
|
OFFSET(kmem_cache_order));
|
|
fprintf(fp, " kmem_cache_local_node: %ld\n",
|
|
OFFSET(kmem_cache_local_node));
|
|
fprintf(fp, " kmem_cache_objects: %ld\n",
|
|
OFFSET(kmem_cache_objects));
|
|
fprintf(fp, " kmem_cache_inuse: %ld\n",
|
|
OFFSET(kmem_cache_inuse));
|
|
fprintf(fp, " kmem_cache_align: %ld\n",
|
|
OFFSET(kmem_cache_align));
|
|
fprintf(fp, " kmem_cache_name: %ld\n",
|
|
OFFSET(kmem_cache_name));
|
|
fprintf(fp, " kmem_cache_list: %ld\n",
|
|
OFFSET(kmem_cache_list));
|
|
fprintf(fp, " kmem_cache_red_left_pad: %ld\n",
|
|
OFFSET(kmem_cache_red_left_pad));
|
|
fprintf(fp, " kmem_cache_node: %ld\n",
|
|
OFFSET(kmem_cache_node));
|
|
fprintf(fp, " kmem_cache_cpu_slab: %ld\n",
|
|
OFFSET(kmem_cache_cpu_slab));
|
|
fprintf(fp, " kmem_cache_cpu_partial: %ld\n",
|
|
OFFSET(kmem_cache_cpu_partial));
|
|
fprintf(fp, " kmem_cache_cpu_cache: %ld\n",
|
|
OFFSET(kmem_cache_cpu_cache));
|
|
fprintf(fp, " kmem_cache_oo: %ld\n",
|
|
OFFSET(kmem_cache_oo));
|
|
fprintf(fp, " kmem_cache_random: %ld\n",
|
|
OFFSET(kmem_cache_random));
|
|
|
|
fprintf(fp, " kmem_cache_node_nr_partial: %ld\n",
|
|
OFFSET(kmem_cache_node_nr_partial));
|
|
fprintf(fp, " kmem_cache_node_nr_slabs: %ld\n",
|
|
OFFSET(kmem_cache_node_nr_slabs));
|
|
fprintf(fp, " kmem_cache_node_partial: %ld\n",
|
|
OFFSET(kmem_cache_node_partial));
|
|
fprintf(fp, " kmem_cache_node_full: %ld\n",
|
|
OFFSET(kmem_cache_node_full));
|
|
fprintf(fp, " kmem_cache_node_total_objects: %ld\n",
|
|
OFFSET(kmem_cache_node_total_objects));
|
|
|
|
fprintf(fp, " kmem_cache_cpu_freelist: %ld\n",
|
|
OFFSET(kmem_cache_cpu_freelist));
|
|
fprintf(fp, " kmem_cache_cpu_page: %ld\n",
|
|
OFFSET(kmem_cache_cpu_page));
|
|
fprintf(fp, " kmem_cache_cpu_node: %ld\n",
|
|
OFFSET(kmem_cache_cpu_node));
|
|
fprintf(fp, " kmem_cache_flags: %ld\n",
|
|
OFFSET(kmem_cache_flags));
|
|
|
|
fprintf(fp, " net_device_next: %ld\n",
|
|
OFFSET(net_device_next));
|
|
fprintf(fp, " net_device_name: %ld\n",
|
|
OFFSET(net_device_name));
|
|
fprintf(fp, " net_device_type: %ld\n",
|
|
OFFSET(net_device_type));
|
|
fprintf(fp, " net_device_addr_len: %ld\n",
|
|
OFFSET(net_device_addr_len));
|
|
fprintf(fp, " net_device_ip_ptr: %ld\n",
|
|
OFFSET(net_device_ip_ptr));
|
|
fprintf(fp, " net_device_dev_list: %ld\n",
|
|
OFFSET(net_device_dev_list));
|
|
fprintf(fp, " net_dev_base_head: %ld\n",
|
|
OFFSET(net_dev_base_head));
|
|
|
|
fprintf(fp, " device_next: %ld\n",
|
|
OFFSET(device_next));
|
|
fprintf(fp, " device_name: %ld\n",
|
|
OFFSET(device_name));
|
|
fprintf(fp, " device_type: %ld\n",
|
|
OFFSET(device_type));
|
|
fprintf(fp, " device_ip_ptr: %ld\n",
|
|
OFFSET(device_ip_ptr));
|
|
fprintf(fp, " device_addr_len: %ld\n",
|
|
OFFSET(device_addr_len));
|
|
|
|
fprintf(fp, " socket_sk: %ld\n", OFFSET(socket_sk));
|
|
fprintf(fp, " sock_daddr: %ld\n",
|
|
OFFSET(sock_daddr));
|
|
fprintf(fp, " sock_rcv_saddr: %ld\n",
|
|
OFFSET(sock_rcv_saddr));
|
|
fprintf(fp, " sock_dport: %ld\n",
|
|
OFFSET(sock_dport));
|
|
fprintf(fp, " sock_sport: %ld\n",
|
|
OFFSET(sock_sport));
|
|
fprintf(fp, " sock_num: %ld\n", OFFSET(sock_num));
|
|
fprintf(fp, " sock_family: %ld\n",
|
|
OFFSET(sock_family));
|
|
fprintf(fp, " sock_type: %ld\n", OFFSET(sock_type));
|
|
|
|
fprintf(fp, " sock_sk_type: %ld\n",
|
|
OFFSET(sock_sk_type));
|
|
fprintf(fp, " sock_common_skc_family: %ld\n",
|
|
OFFSET(sock_common_skc_family));
|
|
fprintf(fp, " socket_alloc_vfs_inode: %ld\n",
|
|
OFFSET(socket_alloc_vfs_inode));
|
|
fprintf(fp, " inet_sock_inet: %ld\n",
|
|
OFFSET(inet_sock_inet));
|
|
fprintf(fp, " inet_opt_daddr: %ld\n",
|
|
OFFSET(inet_opt_daddr));
|
|
fprintf(fp, " inet_opt_rcv_saddr: %ld\n",
|
|
OFFSET(inet_opt_rcv_saddr));
|
|
fprintf(fp, " inet_opt_dport: %ld\n",
|
|
OFFSET(inet_opt_dport));
|
|
fprintf(fp, " inet_opt_sport: %ld\n",
|
|
OFFSET(inet_opt_sport));
|
|
fprintf(fp, " inet_opt_num: %ld\n",
|
|
OFFSET(inet_opt_num));
|
|
|
|
fprintf(fp, " ipv6_pinfo_rcv_saddr: %ld\n",
|
|
OFFSET(ipv6_pinfo_rcv_saddr));
|
|
fprintf(fp, " ipv6_pinfo_daddr: %ld\n",
|
|
OFFSET(ipv6_pinfo_daddr));
|
|
|
|
fprintf(fp, " timer_list_list: %ld\n",
|
|
OFFSET(timer_list_list));
|
|
fprintf(fp, " timer_list_next: %ld\n",
|
|
OFFSET(timer_list_next));
|
|
fprintf(fp, " timer_list_entry: %ld\n",
|
|
OFFSET(timer_list_entry));
|
|
fprintf(fp, " timer_list_expires: %ld\n",
|
|
OFFSET(timer_list_expires));
|
|
fprintf(fp, " timer_list_function: %ld\n",
|
|
OFFSET(timer_list_function));
|
|
fprintf(fp, " timer_vec_root_vec: %ld\n",
|
|
OFFSET(timer_vec_root_vec));
|
|
fprintf(fp, " timer_vec_vec: %ld\n",
|
|
OFFSET(timer_vec_vec));
|
|
fprintf(fp, " tvec_root_s_vec: %ld\n",
|
|
OFFSET(tvec_root_s_vec));
|
|
fprintf(fp, " tvec_s_vec: %ld\n",
|
|
OFFSET(tvec_s_vec));
|
|
fprintf(fp, " tvec_t_base_s_tv1: %ld\n",
|
|
OFFSET(tvec_t_base_s_tv1));
|
|
fprintf(fp, " timer_base_vectors: %ld\n",
|
|
OFFSET(timer_base_vectors));
|
|
|
|
fprintf(fp, " wait_queue_task: %ld\n",
|
|
OFFSET(wait_queue_task));
|
|
fprintf(fp, " wait_queue_next: %ld\n",
|
|
OFFSET(wait_queue_next));
|
|
fprintf(fp, " __wait_queue_task: %ld\n",
|
|
OFFSET(__wait_queue_task));
|
|
fprintf(fp, " __wait_queue_head_task_list: %ld\n",
|
|
OFFSET(__wait_queue_head_task_list));
|
|
fprintf(fp, " __wait_queue_task_list: %ld\n",
|
|
OFFSET(__wait_queue_task_list));
|
|
|
|
fprintf(fp, " pglist_data_node_zones: %ld\n",
|
|
OFFSET(pglist_data_node_zones));
|
|
fprintf(fp, " pglist_data_node_mem_map: %ld\n",
|
|
OFFSET(pglist_data_node_mem_map));
|
|
fprintf(fp, " pglist_data_node_start_paddr: %ld\n",
|
|
OFFSET(pglist_data_node_start_paddr));
|
|
fprintf(fp, " pglist_data_node_start_mapnr: %ld\n",
|
|
OFFSET(pglist_data_node_start_mapnr));
|
|
fprintf(fp, " pglist_data_node_size: %ld\n",
|
|
OFFSET(pglist_data_node_size));
|
|
fprintf(fp, " pglist_data_node_id: %ld\n",
|
|
OFFSET(pglist_data_node_id));
|
|
fprintf(fp, " pglist_data_node_next: %ld\n",
|
|
OFFSET(pglist_data_node_next));
|
|
fprintf(fp, " pglist_data_bdata: %ld\n",
|
|
OFFSET(pglist_data_bdata));
|
|
fprintf(fp, " pglist_data_nr_zones: %ld\n",
|
|
OFFSET(pglist_data_nr_zones));
|
|
fprintf(fp, " pglist_data_node_start_pfn: %ld\n",
|
|
OFFSET(pglist_data_node_start_pfn));
|
|
fprintf(fp, " pglist_data_pgdat_next: %ld\n",
|
|
OFFSET(pglist_data_pgdat_next));
|
|
fprintf(fp, "pglist_data_node_present_pages: %ld\n",
|
|
OFFSET(pglist_data_node_present_pages));
|
|
fprintf(fp, "pglist_data_node_spanned_pages: %ld\n",
|
|
OFFSET(pglist_data_node_spanned_pages));
|
|
|
|
fprintf(fp, " page_cache_bucket_chain: %ld\n",
|
|
OFFSET(page_cache_bucket_chain));
|
|
|
|
fprintf(fp, " zone_struct_free_pages: %ld\n",
|
|
OFFSET(zone_struct_free_pages));
|
|
fprintf(fp, " zone_struct_free_area: %ld\n",
|
|
OFFSET(zone_struct_free_area));
|
|
fprintf(fp, " zone_struct_zone_pgdat: %ld\n",
|
|
OFFSET(zone_struct_zone_pgdat));
|
|
fprintf(fp, " zone_struct_name: %ld\n",
|
|
OFFSET(zone_struct_name));
|
|
fprintf(fp, " zone_struct_size: %ld\n",
|
|
OFFSET(zone_struct_size));
|
|
fprintf(fp, " zone_struct_memsize: %ld\n",
|
|
OFFSET(zone_struct_memsize));
|
|
fprintf(fp, " zone_struct_zone_start_pfn: %ld\n",
|
|
OFFSET(zone_struct_zone_start_pfn));
|
|
fprintf(fp, " zone_struct_zone_start_paddr: %ld\n",
|
|
OFFSET(zone_struct_zone_start_paddr));
|
|
fprintf(fp, " zone_struct_zone_start_mapnr: %ld\n",
|
|
OFFSET(zone_struct_zone_start_mapnr));
|
|
fprintf(fp, " zone_struct_zone_mem_map: %ld\n",
|
|
OFFSET(zone_struct_zone_mem_map));
|
|
fprintf(fp, "zone_struct_inactive_clean_pages: %ld\n",
|
|
OFFSET(zone_struct_inactive_clean_pages));
|
|
fprintf(fp, "zone_struct_inactive_clean_list: %ld\n",
|
|
OFFSET(zone_struct_inactive_clean_list));
|
|
fprintf(fp, "zone_struct_inactive_dirty_pages: %ld\n",
|
|
OFFSET(zone_struct_inactive_dirty_pages));
|
|
fprintf(fp, " zone_struct_active_pages: %ld\n",
|
|
OFFSET(zone_struct_active_pages));
|
|
fprintf(fp, " zone_struct_pages_min: %ld\n",
|
|
OFFSET(zone_struct_pages_min));
|
|
fprintf(fp, " zone_struct_pages_low: %ld\n",
|
|
OFFSET(zone_struct_pages_low));
|
|
fprintf(fp, " zone_struct_pages_high: %ld\n",
|
|
OFFSET(zone_struct_pages_high));
|
|
|
|
fprintf(fp, " zone_free_pages: %ld\n",
|
|
OFFSET(zone_free_pages));
|
|
fprintf(fp, " zone_watermark: %ld\n",
|
|
OFFSET(zone_watermark));
|
|
fprintf(fp, " zone_free_area: %ld\n",
|
|
OFFSET(zone_free_area));
|
|
fprintf(fp, " zone_zone_pgdat: %ld\n",
|
|
OFFSET(zone_zone_pgdat));
|
|
fprintf(fp, " zone_zone_mem_map: %ld\n",
|
|
OFFSET(zone_zone_mem_map));
|
|
fprintf(fp, " zone_name: %ld\n",
|
|
OFFSET(zone_name));
|
|
fprintf(fp, " zone_spanned_pages: %ld\n",
|
|
OFFSET(zone_spanned_pages));
|
|
fprintf(fp, " zone_present_pages: %ld\n",
|
|
OFFSET(zone_present_pages));
|
|
fprintf(fp, " zone_zone_start_pfn: %ld\n",
|
|
OFFSET(zone_zone_start_pfn));
|
|
fprintf(fp, " zone_pages_min: %ld\n",
|
|
OFFSET(zone_pages_min));
|
|
fprintf(fp, " zone_pages_low: %ld\n",
|
|
OFFSET(zone_pages_low));
|
|
fprintf(fp, " zone_pages_high: %ld\n",
|
|
OFFSET(zone_pages_high));
|
|
fprintf(fp, " zone_vm_stat: %ld\n",
|
|
OFFSET(zone_vm_stat));
|
|
fprintf(fp, " zone_nr_active: %ld\n",
|
|
OFFSET(zone_nr_active));
|
|
fprintf(fp, " zone_nr_inactive: %ld\n",
|
|
OFFSET(zone_nr_inactive));
|
|
fprintf(fp, " zone_all_unreclaimable: %ld\n",
|
|
OFFSET(zone_all_unreclaimable));
|
|
fprintf(fp, " zone_flags: %ld\n",
|
|
OFFSET(zone_flags));
|
|
fprintf(fp, " zone_pages_scanned: %ld\n",
|
|
OFFSET(zone_pages_scanned));
|
|
|
|
fprintf(fp, " neighbour_next: %ld\n",
|
|
OFFSET(neighbour_next));
|
|
fprintf(fp, " neighbour_primary_key: %ld\n",
|
|
OFFSET(neighbour_primary_key));
|
|
fprintf(fp, " neighbour_ha: %ld\n",
|
|
OFFSET(neighbour_ha));
|
|
fprintf(fp, " neighbour_dev: %ld\n",
|
|
OFFSET(neighbour_dev));
|
|
fprintf(fp, " neighbour_nud_state: %ld\n",
|
|
OFFSET(neighbour_nud_state));
|
|
fprintf(fp, " neigh_table_hash_buckets: %ld\n",
|
|
OFFSET(neigh_table_hash_buckets));
|
|
fprintf(fp, " neigh_table_hash_mask: %ld\n",
|
|
OFFSET(neigh_table_hash_mask));
|
|
fprintf(fp, " neigh_table_hash_shift: %ld\n",
|
|
OFFSET(neigh_table_hash_shift));
|
|
fprintf(fp, " neigh_table_nht_ptr: %ld\n",
|
|
OFFSET(neigh_table_nht_ptr));
|
|
fprintf(fp, " neigh_table_key_len: %ld\n",
|
|
OFFSET(neigh_table_key_len));
|
|
|
|
fprintf(fp, " in_device_ifa_list: %ld\n",
|
|
OFFSET(in_device_ifa_list));
|
|
fprintf(fp, " in_ifaddr_ifa_next: %ld\n",
|
|
OFFSET(in_ifaddr_ifa_next));
|
|
fprintf(fp, " in_ifaddr_ifa_address: %ld\n",
|
|
OFFSET(in_ifaddr_ifa_address));
|
|
|
|
fprintf(fp, " pci_dev_global_list: %ld\n",
|
|
OFFSET(pci_dev_global_list));
|
|
fprintf(fp, " pci_dev_next: %ld\n",
|
|
OFFSET(pci_dev_next));
|
|
fprintf(fp, " pci_dev_bus: %ld\n",
|
|
OFFSET(pci_dev_bus));
|
|
fprintf(fp, " pci_dev_devfn: %ld\n",
|
|
OFFSET(pci_dev_devfn));
|
|
fprintf(fp, " pci_dev_class: %ld\n",
|
|
OFFSET(pci_dev_class));
|
|
fprintf(fp, " pci_dev_device: %ld\n",
|
|
OFFSET(pci_dev_device));
|
|
fprintf(fp, " pci_dev_vendor: %ld\n",
|
|
OFFSET(pci_dev_vendor));
|
|
fprintf(fp, " pci_bus_number: %ld\n",
|
|
OFFSET(pci_bus_number));
|
|
|
|
|
|
fprintf(fp, " resource_entry_t_from: %ld\n",
|
|
OFFSET(resource_entry_t_from));
|
|
fprintf(fp, " resource_entry_t_num: %ld\n",
|
|
OFFSET(resource_entry_t_num));
|
|
fprintf(fp, " resource_entry_t_name: %ld\n",
|
|
OFFSET(resource_entry_t_name));
|
|
fprintf(fp, " resource_entry_t_next: %ld\n",
|
|
OFFSET(resource_entry_t_next));
|
|
fprintf(fp, " resource_name: %ld\n",
|
|
OFFSET(resource_name));
|
|
fprintf(fp, " resource_start: %ld\n",
|
|
OFFSET(resource_start));
|
|
fprintf(fp, " resource_end: %ld\n",
|
|
OFFSET(resource_end));
|
|
fprintf(fp, " resource_sibling: %ld\n",
|
|
OFFSET(resource_sibling));
|
|
fprintf(fp, " resource_child: %ld\n",
|
|
OFFSET(resource_child));
|
|
|
|
fprintf(fp, " runqueue_curr: %ld\n",
|
|
OFFSET(runqueue_curr));
|
|
fprintf(fp, " runqueue_idle: %ld\n",
|
|
OFFSET(runqueue_idle));
|
|
fprintf(fp, " runqueue_active: %ld\n",
|
|
OFFSET(runqueue_active));
|
|
fprintf(fp, " runqueue_expired: %ld\n",
|
|
OFFSET(runqueue_expired));
|
|
fprintf(fp, " runqueue_arrays: %ld\n",
|
|
OFFSET(runqueue_arrays));
|
|
fprintf(fp, " runqueue_cpu: %ld\n",
|
|
OFFSET(runqueue_cpu));
|
|
fprintf(fp, " cpu_s_idle: %ld\n",
|
|
OFFSET(cpu_s_idle));
|
|
fprintf(fp, " cpu_s_curr: %ld\n",
|
|
OFFSET(cpu_s_curr));
|
|
fprintf(fp, " prio_array_queue: %ld\n",
|
|
OFFSET(prio_array_queue));
|
|
fprintf(fp, " rt_prio_array_queue: %ld\n",
|
|
OFFSET(rt_prio_array_queue));
|
|
fprintf(fp, " prio_array_nr_active: %ld\n",
|
|
OFFSET(prio_array_nr_active));
|
|
fprintf(fp, " pt_regs_regs: %ld\n",
|
|
OFFSET(pt_regs_regs));
|
|
fprintf(fp, " pt_regs_cp0_badvaddr: %ld\n",
|
|
OFFSET(pt_regs_cp0_badvaddr));
|
|
fprintf(fp, " user_regs_struct_ebp: %ld\n",
|
|
OFFSET(user_regs_struct_ebp));
|
|
fprintf(fp, " user_regs_struct_eip: %ld\n",
|
|
OFFSET(user_regs_struct_eip));
|
|
fprintf(fp, " user_regs_struct_esp: %ld\n",
|
|
OFFSET(user_regs_struct_esp));
|
|
fprintf(fp, " user_regs_struct_rip: %ld\n",
|
|
OFFSET(user_regs_struct_rip));
|
|
fprintf(fp, " user_regs_struct_rsp: %ld\n",
|
|
OFFSET(user_regs_struct_rsp));
|
|
fprintf(fp, " user_regs_struct_eflags: %ld\n",
|
|
OFFSET(user_regs_struct_eflags));
|
|
fprintf(fp, " user_regs_struct_cs: %ld\n",
|
|
OFFSET(user_regs_struct_cs));
|
|
fprintf(fp, " user_regs_struct_ss: %ld\n",
|
|
OFFSET(user_regs_struct_ss));
|
|
fprintf(fp, " user_regs_struct_eip: %ld\n",
|
|
OFFSET(user_regs_struct_eip));
|
|
fprintf(fp, " user_regs_struct_rax: %ld\n",
|
|
OFFSET(user_regs_struct_rax));
|
|
fprintf(fp, " user_regs_struct_eax: %ld\n",
|
|
OFFSET(user_regs_struct_eax));
|
|
fprintf(fp, " user_regs_struct_rbx: %ld\n",
|
|
OFFSET(user_regs_struct_rbx));
|
|
fprintf(fp, " user_regs_struct_ebx: %ld\n",
|
|
OFFSET(user_regs_struct_ebx));
|
|
fprintf(fp, " user_regs_struct_rcx: %ld\n",
|
|
OFFSET(user_regs_struct_rcx));
|
|
fprintf(fp, " user_regs_struct_ecx: %ld\n",
|
|
OFFSET(user_regs_struct_ecx));
|
|
fprintf(fp, " user_regs_struct_rdx: %ld\n",
|
|
OFFSET(user_regs_struct_rdx));
|
|
fprintf(fp, " user_regs_struct_edx: %ld\n",
|
|
OFFSET(user_regs_struct_edx));
|
|
fprintf(fp, " user_regs_struct_rsi: %ld\n",
|
|
OFFSET(user_regs_struct_rsi));
|
|
fprintf(fp, " user_regs_struct_esi: %ld\n",
|
|
OFFSET(user_regs_struct_esi));
|
|
fprintf(fp, " user_regs_struct_rdi: %ld\n",
|
|
OFFSET(user_regs_struct_rdi));
|
|
fprintf(fp, " user_regs_struct_edi: %ld\n",
|
|
OFFSET(user_regs_struct_edi));
|
|
fprintf(fp, " user_regs_struct_ds: %ld\n",
|
|
OFFSET(user_regs_struct_ds));
|
|
fprintf(fp, " user_regs_struct_es: %ld\n",
|
|
OFFSET(user_regs_struct_es));
|
|
fprintf(fp, " user_regs_struct_fs: %ld\n",
|
|
OFFSET(user_regs_struct_fs));
|
|
fprintf(fp, " user_regs_struct_gs: %ld\n",
|
|
OFFSET(user_regs_struct_gs));
|
|
fprintf(fp, " user_regs_struct_rbp: %ld\n",
|
|
OFFSET(user_regs_struct_rbp));
|
|
fprintf(fp, " user_regs_struct_r8: %ld\n",
|
|
OFFSET(user_regs_struct_r8));
|
|
fprintf(fp, " user_regs_struct_r9: %ld\n",
|
|
OFFSET(user_regs_struct_r9));
|
|
fprintf(fp, " user_regs_struct_r10: %ld\n",
|
|
OFFSET(user_regs_struct_r10));
|
|
fprintf(fp, " user_regs_struct_r11: %ld\n",
|
|
OFFSET(user_regs_struct_r11));
|
|
fprintf(fp, " user_regs_struct_r12: %ld\n",
|
|
OFFSET(user_regs_struct_r12));
|
|
fprintf(fp, " user_regs_struct_r13: %ld\n",
|
|
OFFSET(user_regs_struct_r13));
|
|
fprintf(fp, " user_regs_struct_r14: %ld\n",
|
|
OFFSET(user_regs_struct_r14));
|
|
fprintf(fp, " user_regs_struct_r15: %ld\n",
|
|
OFFSET(user_regs_struct_r15));
|
|
|
|
fprintf(fp, " e820map_nr_map: %ld\n",
|
|
OFFSET(e820map_nr_map));
|
|
fprintf(fp, " e820entry_addr: %ld\n",
|
|
OFFSET(e820entry_addr));
|
|
fprintf(fp, " e820entry_size: %ld\n",
|
|
OFFSET(e820entry_size));
|
|
fprintf(fp, " e820entry_type: %ld\n",
|
|
OFFSET(e820entry_type));
|
|
|
|
fprintf(fp, " char_device_struct_name: %ld\n",
|
|
OFFSET(char_device_struct_name));
|
|
fprintf(fp, " char_device_struct_next: %ld\n",
|
|
OFFSET(char_device_struct_next));
|
|
fprintf(fp, " char_device_struct_fops: %ld\n",
|
|
OFFSET(char_device_struct_fops));
|
|
fprintf(fp, " char_device_struct_major: %ld\n",
|
|
OFFSET(char_device_struct_major));
|
|
fprintf(fp, " char_device_struct_baseminor: %ld\n",
|
|
OFFSET(char_device_struct_baseminor));
|
|
fprintf(fp, " char_device_struct_cdev: %ld\n",
|
|
OFFSET(char_device_struct_cdev));
|
|
|
|
fprintf(fp, " cdev_ops: %ld\n", OFFSET(cdev_ops));
|
|
|
|
fprintf(fp, " probe_next: %ld\n",
|
|
OFFSET(probe_next));
|
|
fprintf(fp, " probe_dev: %ld\n",
|
|
OFFSET(probe_dev));
|
|
fprintf(fp, " probe_data: %ld\n",
|
|
OFFSET(probe_data));
|
|
fprintf(fp, " kobj_map_probes: %ld\n",
|
|
OFFSET(kobj_map_probes));
|
|
|
|
fprintf(fp, " blk_major_name_next: %ld\n",
|
|
OFFSET(blk_major_name_next));
|
|
fprintf(fp, " blk_major_name_major: %ld\n",
|
|
OFFSET(blk_major_name_major));
|
|
fprintf(fp, " blk_major_name_name: %ld\n",
|
|
OFFSET(blk_major_name_name));
|
|
|
|
fprintf(fp, " radix_tree_root_height: %ld\n",
|
|
OFFSET(radix_tree_root_height));
|
|
fprintf(fp, " radix_tree_root_rnode: %ld\n",
|
|
OFFSET(radix_tree_root_rnode));
|
|
fprintf(fp, " radix_tree_node_slots: %ld\n",
|
|
OFFSET(radix_tree_node_slots));
|
|
fprintf(fp, " radix_tree_node_height: %ld\n",
|
|
OFFSET(radix_tree_node_height));
|
|
fprintf(fp, " radix_tree_node_shift: %ld\n",
|
|
OFFSET(radix_tree_node_shift));
|
|
|
|
fprintf(fp, " rb_root_rb_node: %ld\n",
|
|
OFFSET(rb_root_rb_node));
|
|
fprintf(fp, " rb_node_rb_left: %ld\n",
|
|
OFFSET(rb_node_rb_left));
|
|
fprintf(fp, " rb_node_rb_right: %ld\n",
|
|
OFFSET(rb_node_rb_right));
|
|
|
|
fprintf(fp, " x8664_pda_pcurrent: %ld\n",
|
|
OFFSET(x8664_pda_pcurrent));
|
|
fprintf(fp, " x8664_pda_data_offset: %ld\n",
|
|
OFFSET(x8664_pda_data_offset));
|
|
fprintf(fp, " x8664_pda_kernelstack: %ld\n",
|
|
OFFSET(x8664_pda_kernelstack));
|
|
fprintf(fp, " x8664_pda_irqrsp: %ld\n",
|
|
OFFSET(x8664_pda_irqrsp));
|
|
fprintf(fp, " x8664_pda_cpunumber: %ld\n",
|
|
OFFSET(x8664_pda_cpunumber));
|
|
fprintf(fp, " x8664_pda_irqstackptr: %ld\n",
|
|
OFFSET(x8664_pda_irqstackptr));
|
|
fprintf(fp, " x8664_pda_level4_pgt: %ld\n",
|
|
OFFSET(x8664_pda_level4_pgt));
|
|
fprintf(fp, " x8664_pda_me: %ld\n",
|
|
OFFSET(x8664_pda_me));
|
|
|
|
fprintf(fp, " tss_struct_ist: %ld\n",
|
|
OFFSET(tss_struct_ist));
|
|
fprintf(fp, " mem_section_section_mem_map: %ld\n",
|
|
OFFSET(mem_section_section_mem_map));
|
|
|
|
fprintf(fp, " vcpu_guest_context_user_regs: %ld\n",
|
|
OFFSET(vcpu_guest_context_user_regs));
|
|
fprintf(fp, " cpu_user_regs_eip: %ld\n",
|
|
OFFSET(cpu_user_regs_eip));
|
|
fprintf(fp, " cpu_user_regs_esp: %ld\n",
|
|
OFFSET(cpu_user_regs_esp));
|
|
fprintf(fp, " cpu_user_regs_rip: %ld\n",
|
|
OFFSET(cpu_user_regs_rip));
|
|
fprintf(fp, " cpu_user_regs_rsp: %ld\n",
|
|
OFFSET(cpu_user_regs_rsp));
|
|
fprintf(fp, " unwind_table_core: %ld\n",
|
|
OFFSET(unwind_table_core));
|
|
fprintf(fp, " unwind_table_init: %ld\n",
|
|
OFFSET(unwind_table_init));
|
|
fprintf(fp, " unwind_table_address: %ld\n",
|
|
OFFSET(unwind_table_address));
|
|
fprintf(fp, " unwind_table_size: %ld\n",
|
|
OFFSET(unwind_table_size));
|
|
fprintf(fp, " unwind_table_link: %ld\n",
|
|
OFFSET(unwind_table_link));
|
|
fprintf(fp, " unwind_table_name: %ld\n",
|
|
OFFSET(unwind_table_name));
|
|
|
|
fprintf(fp, " rq_cfs: %ld\n",
|
|
OFFSET(rq_cfs));
|
|
fprintf(fp, " rq_rt: %ld\n",
|
|
OFFSET(rq_rt));
|
|
fprintf(fp, " cfs_rq_curr: %ld\n",
|
|
OFFSET(cfs_rq_curr));
|
|
fprintf(fp, " rq_nr_running: %ld\n",
|
|
OFFSET(rq_nr_running));
|
|
fprintf(fp, " rq_timestamp: %ld\n",
|
|
OFFSET(rq_timestamp));
|
|
fprintf(fp, " task_struct_se: %ld\n",
|
|
OFFSET(task_struct_se));
|
|
fprintf(fp, " sched_entity_run_node: %ld\n",
|
|
OFFSET(sched_entity_run_node));
|
|
fprintf(fp, " sched_entity_cfs_rq: %ld\n",
|
|
OFFSET(sched_entity_cfs_rq));
|
|
fprintf(fp, " sched_entity_my_q: %ld\n",
|
|
OFFSET(sched_entity_my_q));
|
|
fprintf(fp, " sched_entity_on_rq: %ld\n",
|
|
OFFSET(sched_entity_on_rq));
|
|
fprintf(fp, " cfs_rq_nr_running: %ld\n",
|
|
OFFSET(cfs_rq_nr_running));
|
|
fprintf(fp, " cfs_rq_rb_leftmost: %ld\n",
|
|
OFFSET(cfs_rq_rb_leftmost));
|
|
fprintf(fp, " cfs_rq_tasks_timeline: %ld\n",
|
|
OFFSET(cfs_rq_tasks_timeline));
|
|
fprintf(fp, " rt_rq_active: %ld\n",
|
|
OFFSET(rt_rq_active));
|
|
fprintf(fp, " pcpu_info_vcpu: %ld\n",
|
|
OFFSET(pcpu_info_vcpu));
|
|
fprintf(fp, " pcpu_info_idle: %ld\n",
|
|
OFFSET(pcpu_info_idle));
|
|
fprintf(fp, " vcpu_struct_rq: %ld\n",
|
|
OFFSET(vcpu_struct_rq));
|
|
fprintf(fp, " s390_lowcore_psw_save_area: %ld\n",
|
|
OFFSET(s390_lowcore_psw_save_area));
|
|
fprintf(fp, " s390_stack_frame_back_chain: %ld\n",
|
|
OFFSET(s390_stack_frame_back_chain));
|
|
fprintf(fp, " s390_stack_frame_r14: %ld\n",
|
|
OFFSET(s390_stack_frame_r14));
|
|
|
|
fprintf(fp, " cpu_context_save_fp: %ld\n",
|
|
OFFSET(cpu_context_save_fp));
|
|
fprintf(fp, " cpu_context_save_sp: %ld\n",
|
|
OFFSET(cpu_context_save_sp));
|
|
fprintf(fp, " cpu_context_save_pc: %ld\n",
|
|
OFFSET(cpu_context_save_pc));
|
|
fprintf(fp, " elf_prstatus_pr_pid: %ld\n",
|
|
OFFSET(elf_prstatus_pr_pid));
|
|
fprintf(fp, " elf_prstatus_pr_reg: %ld\n",
|
|
OFFSET(elf_prstatus_pr_reg));
|
|
fprintf(fp, " irq_desc_t_name: %ld\n",
|
|
OFFSET(irq_desc_t_name));
|
|
fprintf(fp, " thread_info_cpu_context: %ld\n",
|
|
OFFSET(thread_info_cpu_context));
|
|
fprintf(fp, " unwind_table_list: %ld\n",
|
|
OFFSET(unwind_table_list));
|
|
fprintf(fp, " unwind_table_start: %ld\n",
|
|
OFFSET(unwind_table_start));
|
|
fprintf(fp, " unwind_table_stop: %ld\n",
|
|
OFFSET(unwind_table_stop));
|
|
fprintf(fp, " unwind_table_begin_addr: %ld\n",
|
|
OFFSET(unwind_table_begin_addr));
|
|
fprintf(fp, " unwind_table_end_addr: %ld\n",
|
|
OFFSET(unwind_table_end_addr));
|
|
fprintf(fp, " unwind_idx_addr: %ld\n",
|
|
OFFSET(unwind_idx_addr));
|
|
fprintf(fp, " unwind_idx_insn: %ld\n",
|
|
OFFSET(unwind_idx_insn));
|
|
fprintf(fp, " class_devices: %ld\n",
|
|
OFFSET(class_devices));
|
|
fprintf(fp, " class_p: %ld\n",
|
|
OFFSET(class_p));
|
|
fprintf(fp, " class_private_devices: %ld\n",
|
|
OFFSET(class_private_devices));
|
|
fprintf(fp, " device_knode_class: %ld\n",
|
|
OFFSET(device_knode_class));
|
|
fprintf(fp, " device_node: %ld\n",
|
|
OFFSET(device_node));
|
|
fprintf(fp, " gendisk_dev: %ld\n",
|
|
OFFSET(gendisk_dev));
|
|
fprintf(fp, " gendisk_kobj: %ld\n",
|
|
OFFSET(gendisk_kobj));
|
|
fprintf(fp, " gendisk_part0: %ld\n",
|
|
OFFSET(gendisk_part0));
|
|
fprintf(fp, " gendisk_queue: %ld\n",
|
|
OFFSET(gendisk_queue));
|
|
fprintf(fp, " hd_struct_dev: %ld\n",
|
|
OFFSET(hd_struct_dev));
|
|
fprintf(fp, " klist_k_list: %ld\n",
|
|
OFFSET(klist_k_list));
|
|
fprintf(fp, " klist_node_n_klist: %ld\n",
|
|
OFFSET(klist_node_n_klist));
|
|
fprintf(fp, " klist_node_n_node: %ld\n",
|
|
OFFSET(klist_node_n_node));
|
|
fprintf(fp, " kobject_entry: %ld\n",
|
|
OFFSET(kobject_entry));
|
|
fprintf(fp, " kset_list: %ld\n",
|
|
OFFSET(kset_list));
|
|
fprintf(fp, " request_list_count: %ld\n",
|
|
OFFSET(request_list_count));
|
|
fprintf(fp, " request_queue_in_flight: %ld\n",
|
|
OFFSET(request_queue_in_flight));
|
|
fprintf(fp, " request_queue_rq: %ld\n",
|
|
OFFSET(request_queue_rq));
|
|
fprintf(fp, " request_queue_mq_ops: %ld\n",
|
|
OFFSET(request_queue_mq_ops));
|
|
fprintf(fp, " request_queue_queue_ctx: %ld\n",
|
|
OFFSET(request_queue_queue_ctx));
|
|
fprintf(fp, " blk_mq_ctx_rq_dispatched: %ld\n",
|
|
OFFSET(blk_mq_ctx_rq_dispatched));
|
|
fprintf(fp, " blk_mq_ctx_rq_completed: %ld\n",
|
|
OFFSET(blk_mq_ctx_rq_completed));
|
|
fprintf(fp, " subsys_private_klist_devices: %ld\n",
|
|
OFFSET(subsys_private_klist_devices));
|
|
fprintf(fp, " subsystem_kset: %ld\n",
|
|
OFFSET(subsystem_kset));
|
|
|
|
fprintf(fp, " file_f_op: %ld\n",
|
|
OFFSET(file_f_op));
|
|
fprintf(fp, " file_private_data: %ld\n",
|
|
OFFSET(file_private_data));
|
|
|
|
fprintf(fp, " hstate_order: %ld\n",
|
|
OFFSET(hstate_order));
|
|
fprintf(fp, " hstate_nr_huge_pages: %ld\n",
|
|
OFFSET(hstate_nr_huge_pages));
|
|
fprintf(fp, " hstate_free_huge_pages: %ld\n",
|
|
OFFSET(hstate_free_huge_pages));
|
|
fprintf(fp, " hstate_name: %ld\n",
|
|
OFFSET(hstate_name));
|
|
|
|
fprintf(fp, " hugetlbfs_sb_info_hstate: %ld\n",
|
|
OFFSET(hugetlbfs_sb_info_hstate));
|
|
fprintf(fp, " idr_layer_ary: %ld\n",
|
|
OFFSET(idr_layer_ary));
|
|
fprintf(fp, " idr_layer_layer: %ld\n",
|
|
OFFSET(idr_layer_layer));
|
|
fprintf(fp, " idr_layers: %ld\n",
|
|
OFFSET(idr_layers));
|
|
fprintf(fp, " idr_top: %ld\n",
|
|
OFFSET(idr_top));
|
|
fprintf(fp, " ipc_id_ary_p: %ld\n",
|
|
OFFSET(ipc_id_ary_p));
|
|
fprintf(fp, " ipc_ids_entries: %ld\n",
|
|
OFFSET(ipc_ids_entries));
|
|
fprintf(fp, " ipc_ids_max_id: %ld\n",
|
|
OFFSET(ipc_ids_max_id));
|
|
fprintf(fp, " ipc_ids_ipcs_idr: %ld\n",
|
|
OFFSET(ipc_ids_ipcs_idr));
|
|
fprintf(fp, " ipc_ids_in_use: %ld\n",
|
|
OFFSET(ipc_ids_in_use));
|
|
fprintf(fp, " ipc_namespace_ids: %ld\n",
|
|
OFFSET(ipc_namespace_ids));
|
|
fprintf(fp, " kern_ipc_perm_deleted: %ld\n",
|
|
OFFSET(kern_ipc_perm_deleted));
|
|
fprintf(fp, " kern_ipc_perm_key: %ld\n",
|
|
OFFSET(kern_ipc_perm_key));
|
|
fprintf(fp, " kern_ipc_perm_mode: %ld\n",
|
|
OFFSET(kern_ipc_perm_mode));
|
|
fprintf(fp, " kern_ipc_perm_uid: %ld\n",
|
|
OFFSET(kern_ipc_perm_uid));
|
|
fprintf(fp, " kern_ipc_perm_id: %ld\n",
|
|
OFFSET(kern_ipc_perm_id));
|
|
fprintf(fp, " kern_ipc_perm_seq: %ld\n",
|
|
OFFSET(kern_ipc_perm_seq));
|
|
fprintf(fp, " nsproxy_ipc_ns: %ld\n",
|
|
OFFSET(nsproxy_ipc_ns));
|
|
fprintf(fp, " nsproxy_net_ns: %ld\n",
|
|
OFFSET(nsproxy_net_ns));
|
|
fprintf(fp, " shmem_inode_info_swapped: %ld\n",
|
|
OFFSET(shmem_inode_info_swapped));
|
|
fprintf(fp, " shmem_inode_info_vfs_inode: %ld\n",
|
|
OFFSET(shmem_inode_info_vfs_inode));
|
|
fprintf(fp, " shm_file_data_file: %ld\n",
|
|
OFFSET(shm_file_data_file));
|
|
fprintf(fp, " shmid_kernel_shm_file: %ld\n",
|
|
OFFSET(shmid_kernel_shm_file));
|
|
fprintf(fp, " shmid_kernel_shm_nattch: %ld\n",
|
|
OFFSET(shmid_kernel_shm_nattch));
|
|
fprintf(fp, " shmid_kernel_shm_perm: %ld\n",
|
|
OFFSET(shmid_kernel_shm_perm));
|
|
fprintf(fp, " shmid_kernel_shm_segsz: %ld\n",
|
|
OFFSET(shmid_kernel_shm_segsz));
|
|
fprintf(fp, " shmid_kernel_id: %ld\n",
|
|
OFFSET(shmid_kernel_id));
|
|
fprintf(fp, " sem_array_sem_perm: %ld\n",
|
|
OFFSET(sem_array_sem_perm));
|
|
fprintf(fp, " sem_array_sem_id: %ld\n",
|
|
OFFSET(sem_array_sem_id));
|
|
fprintf(fp, " sem_array_sem_nsems: %ld\n",
|
|
OFFSET(sem_array_sem_nsems));
|
|
fprintf(fp, " msg_queue_q_perm: %ld\n",
|
|
OFFSET(msg_queue_q_perm));
|
|
fprintf(fp, " msg_queue_q_id: %ld\n",
|
|
OFFSET(msg_queue_q_id));
|
|
fprintf(fp, " msg_queue_q_cbytes: %ld\n",
|
|
OFFSET(msg_queue_q_cbytes));
|
|
fprintf(fp, " msg_queue_q_qnum: %ld\n",
|
|
OFFSET(msg_queue_q_qnum));
|
|
fprintf(fp, " super_block_s_fs_info: %ld\n",
|
|
OFFSET(super_block_s_fs_info));
|
|
fprintf(fp, " log_ts_nsec: %ld\n",
|
|
OFFSET(log_ts_nsec));
|
|
fprintf(fp, " log_len: %ld\n",
|
|
OFFSET(log_len));
|
|
fprintf(fp, " log_text_len: %ld\n",
|
|
OFFSET(log_text_len));
|
|
fprintf(fp, " log_dict_len: %ld\n",
|
|
OFFSET(log_dict_len));
|
|
fprintf(fp, " log_level: %ld\n",
|
|
OFFSET(log_level));
|
|
fprintf(fp, " log_flags_level: %ld\n",
|
|
OFFSET(log_flags_level));
|
|
fprintf(fp, " sched_rt_entity_my_q: %ld\n",
|
|
OFFSET(sched_rt_entity_my_q));
|
|
fprintf(fp, " task_group_parent: %ld\n",
|
|
OFFSET(task_group_parent));
|
|
fprintf(fp, " task_group_css: %ld\n",
|
|
OFFSET(task_group_css));
|
|
fprintf(fp, " cgroup_subsys_state_cgroup: %ld\n",
|
|
OFFSET(cgroup_subsys_state_cgroup));
|
|
fprintf(fp, " cgroup_dentry: %ld\n",
|
|
OFFSET(cgroup_dentry));
|
|
fprintf(fp, " cgroup_kn: %ld\n",
|
|
OFFSET(cgroup_kn));
|
|
fprintf(fp, " kernfs_node_name: %ld\n",
|
|
OFFSET(kernfs_node_name));
|
|
fprintf(fp, " kernfs_node_parent: %ld\n",
|
|
OFFSET(kernfs_node_parent));
|
|
fprintf(fp, " task_group_rt_rq: %ld\n",
|
|
OFFSET(task_group_rt_rq));
|
|
fprintf(fp, " rt_rq_tg: %ld\n",
|
|
OFFSET(rt_rq_tg));
|
|
fprintf(fp, " task_group_cfs_rq: %ld\n",
|
|
OFFSET(task_group_cfs_rq));
|
|
fprintf(fp, " cfs_rq_tg: %ld\n",
|
|
OFFSET(cfs_rq_tg));
|
|
fprintf(fp, " task_group_siblings: %ld\n",
|
|
OFFSET(task_group_siblings));
|
|
fprintf(fp, " task_group_children: %ld\n",
|
|
OFFSET(task_group_children));
|
|
fprintf(fp, " task_group_cfs_bandwidth: %ld\n",
|
|
OFFSET(task_group_cfs_bandwidth));
|
|
fprintf(fp, " cfs_rq_throttled: %ld\n",
|
|
OFFSET(cfs_rq_throttled));
|
|
fprintf(fp, " task_group_rt_bandwidth: %ld\n",
|
|
OFFSET(task_group_rt_bandwidth));
|
|
fprintf(fp, " rt_rq_rt_throttled: %ld\n",
|
|
OFFSET(rt_rq_rt_throttled));
|
|
fprintf(fp, " rt_rq_highest_prio: %ld\n",
|
|
OFFSET(rt_rq_highest_prio));
|
|
fprintf(fp, " rt_rq_rt_nr_running: %ld\n",
|
|
OFFSET(rt_rq_rt_nr_running));
|
|
fprintf(fp, " hrtimer_cpu_base_clock_base: %ld\n",
|
|
OFFSET(hrtimer_cpu_base_clock_base));
|
|
fprintf(fp, " hrtimer_clock_base_offset: %ld\n",
|
|
OFFSET(hrtimer_clock_base_offset));
|
|
fprintf(fp, " hrtimer_clock_base_active: %ld\n",
|
|
OFFSET(hrtimer_clock_base_active));
|
|
fprintf(fp, " hrtimer_clock_base_first: %ld\n",
|
|
OFFSET(hrtimer_clock_base_first));
|
|
fprintf(fp, " hrtimer_clock_base_get_time: %ld\n",
|
|
OFFSET(hrtimer_clock_base_get_time));
|
|
fprintf(fp, " hrtimer_base_first: %ld\n",
|
|
OFFSET(hrtimer_base_first));
|
|
fprintf(fp, " hrtimer_base_pending: %ld\n",
|
|
OFFSET(hrtimer_base_pending));
|
|
fprintf(fp, " hrtimer_base_get_time: %ld\n",
|
|
OFFSET(hrtimer_base_get_time));
|
|
fprintf(fp, " hrtimer_node: %ld\n",
|
|
OFFSET(hrtimer_node));
|
|
fprintf(fp, " hrtimer_list: %ld\n",
|
|
OFFSET(hrtimer_list));
|
|
fprintf(fp, " hrtimer_softexpires: %ld\n",
|
|
OFFSET(hrtimer_softexpires));
|
|
fprintf(fp, " hrtimer_expires: %ld\n",
|
|
OFFSET(hrtimer_expires));
|
|
fprintf(fp, " hrtimer_function: %ld\n",
|
|
OFFSET(hrtimer_function));
|
|
fprintf(fp, " timerqueue_head_next: %ld\n",
|
|
OFFSET(timerqueue_head_next));
|
|
fprintf(fp, " timerqueue_node_expires: %ld\n",
|
|
OFFSET(timerqueue_node_expires));
|
|
fprintf(fp, " timerqueue_node_node: %ld\n",
|
|
OFFSET(timerqueue_node_node));
|
|
fprintf(fp, " ktime_t_tv64: %ld\n",
|
|
OFFSET(ktime_t_tv64));
|
|
fprintf(fp, " ktime_t_sec: %ld\n",
|
|
OFFSET(ktime_t_sec));
|
|
fprintf(fp, " ktime_t_nsec: %ld\n",
|
|
OFFSET(ktime_t_nsec));
|
|
fprintf(fp, " atomic_t_counter: %ld\n",
|
|
OFFSET(atomic_t_counter));
|
|
fprintf(fp, " percpu_counter_count: %ld\n",
|
|
OFFSET(percpu_counter_count));
|
|
fprintf(fp, " sk_buff_head_next: %ld\n",
|
|
OFFSET(sk_buff_head_next));
|
|
fprintf(fp, " sk_buff_head_qlen: %ld\n",
|
|
OFFSET(sk_buff_head_qlen));
|
|
fprintf(fp, " sk_buff_next: %ld\n",
|
|
OFFSET(sk_buff_next));
|
|
fprintf(fp, " sk_buff_len: %ld\n",
|
|
OFFSET(sk_buff_len));
|
|
fprintf(fp, " sk_buff_data: %ld\n",
|
|
OFFSET(sk_buff_data));
|
|
fprintf(fp, " nlmsghdr_nlmsg_type: %ld\n",
|
|
OFFSET(nlmsghdr_nlmsg_type));
|
|
fprintf(fp, " module_arch: %ld\n",
|
|
OFFSET(module_arch));
|
|
fprintf(fp, " mod_arch_specific_num_orcs: %ld\n",
|
|
OFFSET(mod_arch_specific_num_orcs));
|
|
fprintf(fp, "mod_arch_specific_orc_unwind_ip: %ld\n",
|
|
OFFSET(mod_arch_specific_orc_unwind_ip));
|
|
fprintf(fp, " mod_arch_specific_orc_unwind: %ld\n",
|
|
OFFSET(mod_arch_specific_orc_unwind));
|
|
fprintf(fp, " bpf_prog_aux: %ld\n",
|
|
OFFSET(bpf_prog_aux));
|
|
fprintf(fp, " bpf_prog_type: %ld\n",
|
|
OFFSET(bpf_prog_type));
|
|
fprintf(fp, " bpf_prog_tag: %ld\n",
|
|
OFFSET(bpf_prog_tag));
|
|
fprintf(fp, " bpf_prog_jited_len: %ld\n",
|
|
OFFSET(bpf_prog_jited_len));
|
|
fprintf(fp, " bpf_prog_bpf_func: %ld\n",
|
|
OFFSET(bpf_prog_bpf_func));
|
|
fprintf(fp, " bpf_prog_len: %ld\n",
|
|
OFFSET(bpf_prog_len));
|
|
fprintf(fp, " bpf_prog_pages: %ld\n",
|
|
OFFSET(bpf_prog_pages));
|
|
fprintf(fp, " bpf_prog_insnsi: %ld\n",
|
|
OFFSET(bpf_prog_insnsi));
|
|
fprintf(fp, " bpf_map_map_flags: %ld\n",
|
|
OFFSET(bpf_map_map_flags));
|
|
fprintf(fp, " bpf_map_map_type: %ld\n",
|
|
OFFSET(bpf_map_map_type));
|
|
fprintf(fp, " bpf_map_pages: %ld\n",
|
|
OFFSET(bpf_map_pages));
|
|
fprintf(fp, " bpf_map_key_size: %ld\n",
|
|
OFFSET(bpf_map_key_size));
|
|
fprintf(fp, " bpf_map_value_size: %ld\n",
|
|
OFFSET(bpf_map_value_size));
|
|
fprintf(fp, " bpf_map_max_entries: %ld\n",
|
|
OFFSET(bpf_map_max_entries));
|
|
fprintf(fp, " bpf_map_name: %ld\n",
|
|
OFFSET(bpf_map_name));
|
|
fprintf(fp, " bpf_map_user: %ld\n",
|
|
OFFSET(bpf_map_user));
|
|
|
|
fprintf(fp, " bpf_prog_aux_used_map_cnt: %ld\n",
|
|
OFFSET(bpf_prog_aux_used_map_cnt));
|
|
fprintf(fp, " bpf_prog_aux_used_maps: %ld\n",
|
|
OFFSET(bpf_prog_aux_used_maps));
|
|
fprintf(fp, " bpf_prog_aux_load_time: %ld\n",
|
|
OFFSET(bpf_prog_aux_load_time));
|
|
fprintf(fp, " bpf_prog_aux_user: %ld\n",
|
|
OFFSET(bpf_prog_aux_user));
|
|
fprintf(fp, " user_struct_uid: %ld\n",
|
|
OFFSET(user_struct_uid));
|
|
|
|
fprintf(fp, "\n size_table:\n");
|
|
fprintf(fp, " page: %ld\n", SIZE(page));
|
|
fprintf(fp, " page_flags: %ld\n", SIZE(page_flags));
|
|
fprintf(fp, " trace_print_flags: %ld\n", SIZE(trace_print_flags));
|
|
fprintf(fp, " free_area_struct: %ld\n",
|
|
SIZE(free_area_struct));
|
|
fprintf(fp, " free_area: %ld\n",
|
|
SIZE(free_area));
|
|
fprintf(fp, " zone_struct: %ld\n", SIZE(zone_struct));
|
|
fprintf(fp, " zone: %ld\n", SIZE(zone));
|
|
fprintf(fp, " kmem_slab_s: %ld\n", SIZE(kmem_slab_s));
|
|
fprintf(fp, " slab_s: %ld\n", SIZE(slab_s));
|
|
fprintf(fp, " slab: %ld\n", SIZE(slab));
|
|
fprintf(fp, " kmem_cache_s: %ld\n",
|
|
SIZE(kmem_cache_s));
|
|
fprintf(fp, " cpucache_s: %ld\n", SIZE(cpucache_s));
|
|
fprintf(fp, " array_cache: %ld\n", SIZE(array_cache));
|
|
fprintf(fp, " kmem_bufctl_t: %ld\n",
|
|
SIZE(kmem_bufctl_t));
|
|
fprintf(fp, " kmem_cache: %ld\n", SIZE(kmem_cache));
|
|
fprintf(fp, " kmem_cache_node: %ld\n", SIZE(kmem_cache_node));
|
|
fprintf(fp, " kmem_cache_cpu: %ld\n", SIZE(kmem_cache_cpu));
|
|
|
|
fprintf(fp, " swap_info_struct: %ld\n",
|
|
SIZE(swap_info_struct));
|
|
fprintf(fp, " vm_area_struct: %ld\n",
|
|
SIZE(vm_area_struct));
|
|
fprintf(fp, " mm_struct: %ld\n", SIZE(mm_struct));
|
|
fprintf(fp, " pglist_data: %ld\n", SIZE(pglist_data));
|
|
fprintf(fp, " page_cache_bucket: %ld\n",
|
|
SIZE(page_cache_bucket));
|
|
fprintf(fp, " pt_regs: %ld\n", SIZE(pt_regs));
|
|
fprintf(fp, " task_struct: %ld\n", SIZE(task_struct));
|
|
fprintf(fp, " task_struct_flags: %ld\n", SIZE(task_struct_flags));
|
|
fprintf(fp, " task_struct_policy: %ld\n", SIZE(task_struct_policy));
|
|
fprintf(fp, " thread_info: %ld\n", SIZE(thread_info));
|
|
fprintf(fp, " softirq_state: %ld\n",
|
|
SIZE(softirq_state));
|
|
fprintf(fp, " softirq_action: %ld\n",
|
|
SIZE(softirq_action));
|
|
|
|
fprintf(fp, " desc_struct: %ld\n", SIZE(desc_struct));
|
|
fprintf(fp, " umode_t: %ld\n", SIZE(umode_t));
|
|
fprintf(fp, " dentry: %ld\n", SIZE(dentry));
|
|
fprintf(fp, " fs_struct: %ld\n", SIZE(fs_struct));
|
|
fprintf(fp, " files_struct: %ld\n",
|
|
SIZE(files_struct));
|
|
fprintf(fp, " fdtable: %ld\n", SIZE(fdtable));
|
|
fprintf(fp, " file: %ld\n", SIZE(file));
|
|
fprintf(fp, " inode: %ld\n", SIZE(inode));
|
|
fprintf(fp, " vfsmount: %ld\n", SIZE(vfsmount));
|
|
fprintf(fp, " mount: %ld\n", SIZE(mount));
|
|
fprintf(fp, " super_block: %ld\n",
|
|
SIZE(super_block));
|
|
fprintf(fp, " irqdesc: %ld\n", SIZE(irqdesc));
|
|
fprintf(fp, " module: %ld\n", SIZE(module));
|
|
fprintf(fp, " module_sect_attr: %ld\n", SIZE(module_sect_attr));
|
|
fprintf(fp, " list_head: %ld\n", SIZE(list_head));
|
|
fprintf(fp, " hlist_head: %ld\n", SIZE(hlist_head));
|
|
fprintf(fp, " hlist_node: %ld\n", SIZE(hlist_node));
|
|
fprintf(fp, " irq_cpustat_t: %ld\n",
|
|
SIZE(irq_cpustat_t));
|
|
fprintf(fp, " cpuinfo_x86: %ld\n", SIZE(cpuinfo_x86));
|
|
fprintf(fp, " cpuinfo_ia64: %ld\n",
|
|
SIZE(cpuinfo_ia64));
|
|
fprintf(fp, " timer_list: %ld\n", SIZE(timer_list));
|
|
fprintf(fp, " timer_vec_root: %ld\n",
|
|
SIZE(timer_vec_root));
|
|
fprintf(fp, " timer_vec: %ld\n", SIZE(timer_vec));
|
|
fprintf(fp, " tvec_root_s: %ld\n",
|
|
SIZE(tvec_root_s));
|
|
fprintf(fp, " tvec_s: %ld\n", SIZE(tvec_s));
|
|
fprintf(fp, " tvec_t_base_s: %ld\n",
|
|
SIZE(tvec_t_base_s));
|
|
|
|
fprintf(fp, " wait_queue: %ld\n", SIZE(wait_queue));
|
|
fprintf(fp, " __wait_queue: %ld\n",
|
|
SIZE(__wait_queue));
|
|
fprintf(fp, " device: %ld\n", SIZE(device));
|
|
fprintf(fp, " net_device: %ld\n", SIZE(net_device));
|
|
|
|
fprintf(fp, " sock: %ld\n", SIZE(sock));
|
|
fprintf(fp, " inet_sock: %ld\n", SIZE(inet_sock));
|
|
fprintf(fp, " socket: %ld\n", SIZE(socket));
|
|
fprintf(fp, " in6_addr: %ld\n", SIZE(in6_addr));
|
|
fprintf(fp, " signal_struct: %ld\n",
|
|
SIZE(signal_struct));
|
|
fprintf(fp, " sigpending_signal: %ld\n",
|
|
SIZE(sigpending_signal));
|
|
fprintf(fp, " signal_queue: %ld\n",
|
|
SIZE(signal_queue));
|
|
fprintf(fp, " sigqueue: %ld\n", SIZE(sigqueue));
|
|
fprintf(fp, " k_sigaction: %ld\n",
|
|
SIZE(k_sigaction));
|
|
fprintf(fp, " sighand_struct: %ld\n",
|
|
SIZE(sighand_struct));
|
|
fprintf(fp, " resource_entry_t: %ld\n",
|
|
SIZE(resource_entry_t));
|
|
fprintf(fp, " resource: %ld\n", SIZE(resource));
|
|
fprintf(fp, " runqueue: %ld\n", SIZE(runqueue));
|
|
fprintf(fp, " irq_desc_t: %ld\n", SIZE(irq_desc_t));
|
|
fprintf(fp, " irq_data: %ld\n", SIZE(irq_data));
|
|
fprintf(fp, " task_union: %ld\n", SIZE(task_union));
|
|
fprintf(fp, " thread_union: %ld\n", SIZE(thread_union));
|
|
fprintf(fp, " prio_array: %ld\n", SIZE(prio_array));
|
|
fprintf(fp, " user_regs_struct: %ld\n",
|
|
SIZE(user_regs_struct));
|
|
fprintf(fp, " switch_stack: %ld\n",
|
|
SIZE(switch_stack));
|
|
fprintf(fp, " vm_area_struct_vm_flags: %ld\n",
|
|
SIZE(vm_area_struct_vm_flags));
|
|
fprintf(fp, " e820map: %ld\n", SIZE(e820map));
|
|
fprintf(fp, " e820entry: %ld\n", SIZE(e820entry));
|
|
fprintf(fp, " cpu_s: %ld\n", SIZE(cpu_s));
|
|
fprintf(fp, " pgd_t: %ld\n", SIZE(pgd_t));
|
|
fprintf(fp, " kallsyms_header: %ld\n",
|
|
SIZE(kallsyms_header));
|
|
fprintf(fp, " kallsyms_symbol: %ld\n",
|
|
SIZE(kallsyms_symbol));
|
|
fprintf(fp, " kallsyms_section: %ld\n",
|
|
SIZE(kallsyms_section));
|
|
fprintf(fp, " block_device: %ld\n",
|
|
SIZE(block_device));
|
|
fprintf(fp, " blk_major_name: %ld\n",
|
|
SIZE(blk_major_name));
|
|
fprintf(fp, " address_space: %ld\n",
|
|
SIZE(address_space));
|
|
fprintf(fp, " gendisk: %ld\n",
|
|
SIZE(gendisk));
|
|
|
|
fprintf(fp, " irq_ctx: %ld\n", SIZE(irq_ctx));
|
|
fprintf(fp, " char_device_struct: %ld\n",
|
|
SIZE(char_device_struct));
|
|
fprintf(fp, " spinlock_t: %ld\n",
|
|
SIZE(spinlock_t));
|
|
|
|
fprintf(fp, " radix_tree_root: %ld\n",
|
|
SIZE(radix_tree_root));
|
|
fprintf(fp, " radix_tree_node: %ld\n",
|
|
SIZE(radix_tree_node));
|
|
|
|
fprintf(fp, " x8664_pda: %ld\n",
|
|
SIZE(x8664_pda));
|
|
fprintf(fp, " ppc64_paca: %ld\n",
|
|
SIZE(ppc64_paca));
|
|
fprintf(fp, " gate_struct: %ld\n",
|
|
SIZE(gate_struct));
|
|
fprintf(fp, " tss_struct: %ld\n",
|
|
SIZE(tss_struct));
|
|
fprintf(fp, " task_struct_start_time: %ld\n",
|
|
SIZE(task_struct_start_time));
|
|
fprintf(fp, " task_struct_utime: %ld\n",
|
|
SIZE(task_struct_utime));
|
|
fprintf(fp, " task_struct_stime: %ld\n",
|
|
SIZE(task_struct_stime));
|
|
fprintf(fp, " cputime_t: %ld\n",
|
|
SIZE(cputime_t));
|
|
fprintf(fp, " mem_section: %ld\n",
|
|
SIZE(mem_section));
|
|
fprintf(fp, " pid_link: %ld\n",
|
|
SIZE(pid_link));
|
|
fprintf(fp, " upid: %ld\n",
|
|
SIZE(upid));
|
|
fprintf(fp, " pid: %ld\n",
|
|
SIZE(pid));
|
|
fprintf(fp, " unwind_table: %ld\n",
|
|
SIZE(unwind_table));
|
|
fprintf(fp, " rlimit: %ld\n",
|
|
SIZE(rlimit));
|
|
fprintf(fp, " cfs_rq: %ld\n",
|
|
SIZE(cfs_rq));
|
|
fprintf(fp, " pcpu_info: %ld\n",
|
|
SIZE(pcpu_info));
|
|
fprintf(fp, " vcpu_struct: %ld\n",
|
|
SIZE(vcpu_struct));
|
|
fprintf(fp, " cdev: %ld\n",
|
|
SIZE(cdev));
|
|
fprintf(fp, " probe: %ld\n",
|
|
SIZE(probe));
|
|
fprintf(fp, " kobj_map: %ld\n",
|
|
SIZE(kobj_map));
|
|
fprintf(fp, " cpu_context_save: %ld\n",
|
|
SIZE(cpu_context_save));
|
|
fprintf(fp, " elf_prstatus: %ld\n",
|
|
SIZE(elf_prstatus));
|
|
fprintf(fp, " note_buf: %ld\n",
|
|
SIZE(note_buf));
|
|
fprintf(fp, " unwind_idx: %ld\n",
|
|
SIZE(unwind_idx));
|
|
fprintf(fp, " s390_stack_frame: %ld\n",
|
|
SIZE(s390_stack_frame));
|
|
fprintf(fp, " percpu_data: %ld\n",
|
|
SIZE(percpu_data));
|
|
fprintf(fp, " sched_entity: %ld\n",
|
|
SIZE(sched_entity));
|
|
fprintf(fp, " kernel_stat: %ld\n",
|
|
SIZE(kernel_stat));
|
|
fprintf(fp, " subsystem: %ld\n",
|
|
SIZE(subsystem));
|
|
fprintf(fp, " class_private: %ld\n",
|
|
SIZE(class_private));
|
|
fprintf(fp, " rq_in_flight: %ld\n",
|
|
SIZE(rq_in_flight));
|
|
fprintf(fp, " class_private_devices: %ld\n",
|
|
SIZE(class_private_devices));
|
|
fprintf(fp, " hstate: %ld\n",
|
|
SIZE(hstate));
|
|
fprintf(fp, " ipc_ids: %ld\n",
|
|
SIZE(ipc_ids));
|
|
fprintf(fp, " shmid_kernel: %ld\n",
|
|
SIZE(shmid_kernel));
|
|
fprintf(fp, " sem_array: %ld\n",
|
|
SIZE(sem_array));
|
|
fprintf(fp, " msg_queue: %ld\n",
|
|
SIZE(msg_queue));
|
|
fprintf(fp, " log: %ld\n",
|
|
SIZE(log));
|
|
fprintf(fp, " log_level: %ld\n",
|
|
SIZE(log_level));
|
|
fprintf(fp, " rt_rq: %ld\n",
|
|
SIZE(rt_rq));
|
|
fprintf(fp, " task_group: %ld\n",
|
|
SIZE(task_group));
|
|
fprintf(fp, " vmap_area: %ld\n",
|
|
SIZE(vmap_area));
|
|
fprintf(fp, " hrtimer_clock_base: %ld\n",
|
|
SIZE(hrtimer_clock_base));
|
|
fprintf(fp, " hrtimer_base: %ld\n",
|
|
SIZE(hrtimer_base));
|
|
fprintf(fp, " timer_base: %ld\n",
|
|
SIZE(timer_base));
|
|
fprintf(fp, " tnt: %ld\n",
|
|
SIZE(tnt));
|
|
fprintf(fp, " taint_flag: %ld\n",
|
|
SIZE(taint_flag));
|
|
fprintf(fp, " nlmsghdr: %ld\n",
|
|
SIZE(nlmsghdr));
|
|
fprintf(fp, " nlmsghdr_nlmsg_type: %ld\n",
|
|
SIZE(nlmsghdr_nlmsg_type));
|
|
fprintf(fp, " sk_buff_head_qlen: %ld\n",
|
|
SIZE(sk_buff_head_qlen));
|
|
fprintf(fp, " sk_buff_len: %ld\n",
|
|
SIZE(sk_buff_len));
|
|
fprintf(fp, " orc_entry: %ld\n",
|
|
SIZE(orc_entry));
|
|
fprintf(fp, " bpf_prog: %ld\n",
|
|
SIZE(bpf_prog));
|
|
fprintf(fp, " bpf_prog_aux: %ld\n",
|
|
SIZE(bpf_prog_aux));
|
|
fprintf(fp, " bpf_map: %ld\n",
|
|
SIZE(bpf_map));
|
|
fprintf(fp, " bpf_insn: %ld\n",
|
|
SIZE(bpf_insn));
|
|
|
|
fprintf(fp, "\n array_table:\n");
|
|
/*
|
|
* Use get_array_length() for those fields not set up at init-time;
|
|
* ARRAY_LENGTH() will work for the rest.
|
|
*/
|
|
fprintf(fp, " kmem_cache_s_name: %d\n",
|
|
ARRAY_LENGTH(kmem_cache_s_name));
|
|
fprintf(fp, " kmem_cache_s_c_name: %d\n",
|
|
ARRAY_LENGTH(kmem_cache_s_c_name));
|
|
fprintf(fp, " kmem_cache_s_array: %d\n",
|
|
ARRAY_LENGTH(kmem_cache_s_array));
|
|
fprintf(fp, " kmem_cache_s_cpudata: %d\n",
|
|
ARRAY_LENGTH(kmem_cache_s_cpudata));
|
|
fprintf(fp, " log_buf: %d\n",
|
|
ARRAY_LENGTH(log_buf));
|
|
fprintf(fp, " irq_desc: %d\n",
|
|
ARRAY_LENGTH(irq_desc));
|
|
fprintf(fp, " irq_action: %d\n",
|
|
ARRAY_LENGTH(irq_action));
|
|
fprintf(fp, " timer_vec_vec: %d\n",
|
|
get_array_length("timer_vec.vec", NULL, SIZE(list_head)));
|
|
fprintf(fp, " timer_vec_root_vec: %d\n",
|
|
get_array_length("timer_vec_root.vec", NULL, SIZE(list_head)));
|
|
fprintf(fp, " tvec_root_s_vec: %d\n",
|
|
get_array_length("tvec_root_s.vec", NULL, SIZE(list_head)));
|
|
fprintf(fp, " tvec_s_vec: %d\n",
|
|
get_array_length("tvec_s.vec", NULL, SIZE(list_head)));
|
|
fprintf(fp, " page_hash_table: %d\n",
|
|
ARRAY_LENGTH(page_hash_table));
|
|
fprintf(fp, " net_device_name: %d\n",
|
|
ARRAY_LENGTH(net_device_name));
|
|
fprintf(fp, " neigh_table_hash_buckets: %d\n",
|
|
get_array_length("neigh_table.hash_buckets", NULL,
|
|
sizeof(void *)));
|
|
fprintf(fp, " neighbour_ha: %d\n",
|
|
get_array_length("neighbour.ha", NULL, sizeof(char)));
|
|
fprintf(fp, " swap_info: %d\n",
|
|
get_array_length("swap_info", NULL, 0));
|
|
fprintf(fp, " pglist_data_node_zones: %d\n",
|
|
ARRAY_LENGTH(pglist_data_node_zones));
|
|
fprintf(fp, " zone_struct_free_area: %d\n",
|
|
ARRAY_LENGTH(zone_struct_free_area));
|
|
fprintf(fp, " zone_free_area: %d\n",
|
|
ARRAY_LENGTH(zone_free_area));
|
|
fprintf(fp, " free_area: %d\n",
|
|
ARRAY_LENGTH(free_area));
|
|
fprintf(fp, " free_area_DIMENSION: %d\n",
|
|
ARRAY_LENGTH(free_area_DIMENSION));
|
|
fprintf(fp, " prio_array_queue: %d\n",
|
|
get_array_length("prio_array.queue", NULL, SIZE(list_head)));
|
|
fprintf(fp, " height_to_maxindex: %d\n",
|
|
ARRAY_LENGTH(height_to_maxindex));
|
|
fprintf(fp, " height_to_maxnodes: %d\n",
|
|
ARRAY_LENGTH(height_to_maxnodes));
|
|
fprintf(fp, " pid_hash: %d\n",
|
|
ARRAY_LENGTH(pid_hash));
|
|
fprintf(fp, " kmem_cache_node: %d\n",
|
|
ARRAY_LENGTH(kmem_cache_node));
|
|
fprintf(fp, " kmem_cache_cpu_slab: %d\n",
|
|
ARRAY_LENGTH(kmem_cache_cpu_slab));
|
|
fprintf(fp, " rt_prio_array_queue: %d\n",
|
|
ARRAY_LENGTH(rt_prio_array_queue));
|
|
fprintf(fp, " task_struct_rlim: %d\n",
|
|
ARRAY_LENGTH(task_struct_rlim));
|
|
fprintf(fp, " signal_struct_rlim: %d\n",
|
|
ARRAY_LENGTH(signal_struct_rlim));
|
|
|
|
if (spec) {
|
|
int in_size_table, in_array_table, arrays, offsets, sizes;
|
|
|
|
in_size_table = in_array_table = arrays = offsets = sizes = 0;
|
|
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
if (strstr(buf, "size_table:"))
|
|
in_size_table = TRUE;
|
|
|
|
if (strstr(buf, "array_table:")) {
|
|
in_array_table = TRUE;
|
|
in_size_table = FALSE;
|
|
}
|
|
|
|
if (strstr(buf, spec)) {
|
|
if (in_size_table) {
|
|
if (!sizes)
|
|
fprintf(pc->saved_fp,
|
|
"%s size_table:\n",
|
|
offsets ? "\n" : "");
|
|
sizes++;
|
|
} else if (in_array_table) {
|
|
if (!arrays)
|
|
fprintf(pc->saved_fp,
|
|
"%s array_table:\n",
|
|
offsets || sizes ?
|
|
"\n" : "");
|
|
arrays++;
|
|
} else {
|
|
if (!offsets)
|
|
fprintf(pc->saved_fp,
|
|
" offset_table:\n");
|
|
offsets++;
|
|
}
|
|
|
|
if (strstr(buf, " size_table:") ||
|
|
strstr(buf, " array_table:") ||
|
|
strstr(buf, " offset_table:"))
|
|
break;
|
|
|
|
fprintf(pc->saved_fp, "%s", buf);
|
|
}
|
|
}
|
|
close_tmpfile();
|
|
}
|
|
|
|
if (makestruct) {
|
|
fprintf(pc->saved_fp,
|
|
"static struct builtin_debug_table %s;\n\n",
|
|
revname);
|
|
|
|
rewind(pc->tmpfile);
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
|
|
if (strstr(buf, " offset_table:\n")) {
|
|
fprintf(pc->saved_fp,
|
|
"static struct offset_table %s_offset_table = {\n",
|
|
revname);
|
|
continue;
|
|
}
|
|
if (strstr(buf, " size_table:\n")) {
|
|
fprintf(pc->saved_fp,
|
|
"static struct size_table %s_size_table = {\n",
|
|
revname);
|
|
continue;
|
|
}
|
|
if (strstr(buf, " array_table:\n")) {
|
|
fprintf(pc->saved_fp,
|
|
"static struct array_table %s_array_table = {\n",
|
|
revname);
|
|
continue;
|
|
}
|
|
if (STREQ(buf, "\n")) {
|
|
fprintf(pc->saved_fp, "};\n\n");
|
|
continue;
|
|
}
|
|
|
|
fprintf(pc->saved_fp, "%s,\n", strip_linefeeds(buf));
|
|
}
|
|
fprintf(pc->saved_fp, "};\n\n");
|
|
|
|
close_tmpfile();
|
|
|
|
fprintf(fp, "static struct builtin_debug_table %s = {\n",
|
|
revname);
|
|
fprintf(fp, " release: \"%s\",\n", uts->release);
|
|
fprintf(fp, " machine_type: \"%s\",\n", pc->machine_type);
|
|
fprintf(fp, " offset_table: &%s_offset_table,\n", revname);
|
|
fprintf(fp, " size_table: &%s_size_table,\n", revname);
|
|
fprintf(fp, " array_table: &%s_array_table,\n", revname);
|
|
fprintf(fp, "};\n\n");
|
|
}
|
|
|
|
pc->flags |= data_debug;
|
|
}
|
|
|
|
|
|
|
|
|
|
#define NUMARGS_CACHE_ENTRIES (100)
|
|
|
|
static struct numargs_cache {
|
|
ulong function;
|
|
int numargs;
|
|
} numargs_cache[NUMARGS_CACHE_ENTRIES] = { {0} };
|
|
|
|
static int numargs_cache_index = 0;
|
|
|
|
int
|
|
get_function_numargs(ulong callpc)
|
|
{
|
|
int i;
|
|
struct numargs_cache *na;
|
|
struct gnu_request *req;
|
|
int retval;
|
|
ulong func;
|
|
|
|
func = closest_symbol_value(callpc);
|
|
|
|
if (!func)
|
|
return -1;
|
|
|
|
for (i = 0; i < NUMARGS_CACHE_ENTRIES; i++) {
|
|
na = &numargs_cache[i];
|
|
if (!na->function) {
|
|
numargs_cache_index = i;
|
|
break;
|
|
}
|
|
|
|
if (na->function == func)
|
|
return na->numargs;
|
|
}
|
|
|
|
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
|
|
req->buf = GETBUF(BUFSIZE);
|
|
|
|
req->command = GNU_FUNCTION_NUMARGS;
|
|
req->flags |= GNU_RETURN_ON_ERROR;
|
|
req->pc = func;
|
|
gdb_interface(req);
|
|
|
|
if (req->flags & GNU_COMMAND_FAILED) {
|
|
retval = -1;
|
|
goto func_done;
|
|
}
|
|
|
|
retval = (int)req->value;
|
|
|
|
func_done:
|
|
|
|
FREEBUF(req->buf);
|
|
FREEBUF(req);
|
|
|
|
numargs_cache_index %= NUMARGS_CACHE_ENTRIES;
|
|
na = &numargs_cache[numargs_cache_index++];
|
|
na->function = func;
|
|
na->numargs = retval;
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* help -c output
|
|
*/
|
|
void
|
|
dump_numargs_cache(void)
|
|
{
|
|
int i;
|
|
struct numargs_cache *na;
|
|
char buf[BUFSIZE];
|
|
|
|
fprintf(fp, "numargs_cache_index: %d\n", numargs_cache_index);
|
|
|
|
for (i = 0; i < NUMARGS_CACHE_ENTRIES; i++) {
|
|
na = &numargs_cache[i];
|
|
|
|
if (!na->function)
|
|
break;
|
|
|
|
fprintf(fp, "%lx (%s): %d\n",
|
|
na->function,
|
|
value_to_symstr(na->function, buf, 0),
|
|
na->numargs);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is the call-back function that is passed to bfd_map_over_sections().
|
|
* Based upon the request, check whether the passed-in section has what
|
|
* the caller needs. The MODULE_SECTIONS code is tricky because it has
|
|
* to keep a running alignment value as it walks through the section
|
|
* headers in order to eventually calculate the module's base data address.
|
|
*/
|
|
static void
|
|
section_header_info(bfd *bfd, asection *section, void *reqptr)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
ulong request;
|
|
asection **sec;
|
|
ulong section_end_address;
|
|
|
|
request = ((ulong)reqptr);
|
|
|
|
switch (request)
|
|
{
|
|
case (ulong)KERNEL_SECTIONS:
|
|
sec = (asection **)st->sections;
|
|
for (i = 0; (i < st->bfd->section_count) && *sec; i++)
|
|
sec++;
|
|
*sec = section;
|
|
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".text.init") ||
|
|
STREQ(bfd_get_section_name(bfd, section), ".init.text")) {
|
|
kt->stext_init = (ulong)
|
|
bfd_get_section_vma(bfd, section);
|
|
kt->etext_init = kt->stext_init +
|
|
(ulong)bfd_section_size(bfd, section);
|
|
}
|
|
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".text")) {
|
|
st->first_section_start = (ulong)
|
|
bfd_get_section_vma(bfd, section);
|
|
}
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".text") ||
|
|
STREQ(bfd_get_section_name(bfd, section), ".data")) {
|
|
if (!(bfd_get_section_flags(bfd, section) & SEC_LOAD))
|
|
st->flags |= NO_SEC_LOAD;
|
|
if (!(bfd_get_section_flags(bfd, section) &
|
|
SEC_HAS_CONTENTS))
|
|
st->flags |= NO_SEC_CONTENTS;
|
|
}
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".eh_frame")) {
|
|
st->dwarf_eh_frame_file_offset = (off_t)section->filepos;
|
|
st->dwarf_eh_frame_size = (ulong)bfd_section_size(bfd, section);
|
|
}
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".debug_frame")) {
|
|
st->dwarf_debug_frame_file_offset = (off_t)section->filepos;
|
|
st->dwarf_debug_frame_size = (ulong)bfd_section_size(bfd, section);
|
|
}
|
|
|
|
if (st->first_section_start != 0) {
|
|
section_end_address =
|
|
(ulong) bfd_get_section_vma(bfd, section) +
|
|
(ulong) bfd_section_size(bfd, section);
|
|
if (section_end_address > st->last_section_end)
|
|
st->last_section_end = section_end_address;
|
|
}
|
|
break;
|
|
|
|
case (ulong)MODULE_SECTIONS:
|
|
lm = st->current;
|
|
store_section_data(lm, bfd, section);
|
|
break;
|
|
|
|
case (ulong)VERIFY_SECTIONS:
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".text") ||
|
|
STREQ(bfd_get_section_name(bfd, section), ".data")) {
|
|
if (!(bfd_get_section_flags(bfd, section) & SEC_LOAD))
|
|
st->flags |= NO_SEC_LOAD;
|
|
if (!(bfd_get_section_flags(bfd, section) &
|
|
SEC_HAS_CONTENTS))
|
|
st->flags |= NO_SEC_CONTENTS;
|
|
}
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".eh_frame")) {
|
|
st->dwarf_eh_frame_file_offset = (off_t)section->filepos;
|
|
st->dwarf_eh_frame_size = (ulong)bfd_section_size(bfd, section);
|
|
}
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".debug_frame")) {
|
|
st->dwarf_debug_frame_file_offset = (off_t)section->filepos;
|
|
st->dwarf_debug_frame_size = (ulong)bfd_section_size(bfd, section);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error(FATAL, "invalid call to section_header_info\n");
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Emulate insmod by calculating the priorities of each section.
|
|
* The priority number will be used later by calculate_load_order()
|
|
* to determine the the starting addresses of the text and data
|
|
* sections.
|
|
*
|
|
* insmod uses the following code sequence -- which references the actual ELF
|
|
* section header structure data:
|
|
*
|
|
* ac = 0;
|
|
* if (a->name[0] != '.' || strlen(a->name) != 10 ||
|
|
* strcmp(a->name + 5, ".init")) ac |= 32;
|
|
* if (af & SHF_ALLOC) ac |= 16;
|
|
* if (!(af & SHF_WRITE)) ac |= 8;
|
|
* if (af & SHF_EXECINSTR) ac |= 4;
|
|
* if (a->header.sh_type != SHT_NOBITS) ac |= 2;
|
|
*
|
|
* BFD abstracts the ELF section header into an asection structure, so this
|
|
* code determines the priority using the relevant logic.
|
|
*/
|
|
|
|
static void
|
|
store_section_data(struct load_module *lm, bfd *bfd, asection *section)
|
|
{
|
|
int i;
|
|
int prio;
|
|
char *name;
|
|
|
|
prio = 0;
|
|
name = (char *)bfd_get_section_name(bfd, section);
|
|
|
|
if (name[0] != '.' || strlen(name) != 10 || strcmp(name + 5, ".init"))
|
|
prio |= 32;
|
|
if (section->flags & SEC_ALLOC)
|
|
prio |= 16;
|
|
if (section->flags & SEC_READONLY)
|
|
prio |= 8;
|
|
if (section->flags & SEC_CODE)
|
|
prio |= 4;
|
|
if (!STREQ(name, ".bss"))
|
|
prio |= 2;
|
|
|
|
i = lm->mod_sections;
|
|
lm->mod_section_data[i].section = section;
|
|
lm->mod_section_data[i].priority = prio;
|
|
lm->mod_section_data[i].flags = section->flags & ~SEC_FOUND;
|
|
/*
|
|
* The percpu section isn't included in kallsyms or module_core area.
|
|
*/
|
|
if (lm->mod_percpu &&
|
|
(STREQ(name,".data.percpu") || STREQ(name, ".data..percpu"))) {
|
|
lm->mod_percpu_size = bfd_section_size(bfd, section);
|
|
lm->mod_section_data[i].flags |= SEC_FOUND;
|
|
}
|
|
lm->mod_section_data[i].size = bfd_section_size(bfd, section);
|
|
lm->mod_section_data[i].offset = 0;
|
|
if (strlen(name) < MAX_MOD_SEC_NAME)
|
|
strcpy(lm->mod_section_data[i].name, name);
|
|
else
|
|
strncpy(lm->mod_section_data[i].name, name, MAX_MOD_SEC_NAME-1);
|
|
lm->mod_sections += 1;
|
|
}
|
|
|
|
/*
|
|
* insmod first calculates a priority for each module section, and re-orders
|
|
* the sections from their ELF object file position -- that priority was
|
|
* determined in store_section_priority(). Now, based upon a priority-based
|
|
* ordering, this routine calculates the starting offset for each section.
|
|
* This is the code segment from insmod that is being emulated here:
|
|
*
|
|
* unsigned long
|
|
* obj_load_size (struct obj_file *f)
|
|
* {
|
|
* unsigned long dot = 0;
|
|
* struct obj_section *sec;
|
|
*
|
|
* /+ Finalize the positions of the sections relative to one another. +/
|
|
*
|
|
* for (sec = f->load_order; sec ; sec = sec->load_next)
|
|
* {
|
|
* ElfW(Addr) align;
|
|
*
|
|
* align = sec->header.sh_addralign;
|
|
* if (align && (dot & (align - 1)))
|
|
* dot = (dot | (align - 1)) + 1;
|
|
*
|
|
* sec->header.sh_addr = dot;
|
|
* dot += sec->header.sh_size;
|
|
* }
|
|
*
|
|
* return dot;
|
|
* }
|
|
*
|
|
* Another insmod hack extends the .kstrtab section with a string containing
|
|
* the name of the module. If the .kstrtab comes before the .data section,
|
|
* it in turn gets bumped up.
|
|
*
|
|
* BFD abstracts the ELF section header into an asection structure, so this
|
|
* code determines the priority using the relevant logic.
|
|
*
|
|
* Later versions of insmod do the work for us by creating pseudo-symbols
|
|
* that contain the base address of the text, rodata, data and bss sections.
|
|
* When that's the case, veer off to check_insmod_builtin() to potentially
|
|
* override the offset value calculated here.
|
|
*/
|
|
|
|
static void
|
|
calculate_load_order_v1(struct load_module *lm, bfd *bfd)
|
|
{
|
|
int i;
|
|
asection *section;
|
|
ulong alignment;
|
|
ulong offset;
|
|
|
|
offset = 0;
|
|
|
|
switch (kt->flags & (KMOD_V1|KMOD_V2))
|
|
{
|
|
case KMOD_V1:
|
|
offset = lm->mod_size_of_struct;
|
|
break;
|
|
case KMOD_V2:
|
|
offset = lm->mod_base;
|
|
break;
|
|
}
|
|
|
|
qsort(&lm->mod_section_data[0], lm->mod_sections,
|
|
sizeof(struct mod_section_data), compare_prios);
|
|
|
|
for (i = (lm->mod_sections-1); i >= 0; i--) {
|
|
section = lm->mod_section_data[i].section;
|
|
|
|
alignment = power(2, bfd_get_section_alignment(bfd, section));
|
|
|
|
if (alignment && (offset & (alignment - 1)))
|
|
offset = (offset | (alignment - 1)) + 1;
|
|
|
|
lm->mod_section_data[i].offset = offset;
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%12s prio: %x flags: %x offset: %lx\n",
|
|
lm->mod_section_data[i].name,
|
|
lm->mod_section_data[i].priority,
|
|
lm->mod_section_data[i].flags,
|
|
lm->mod_section_data[i].offset);
|
|
|
|
if (st->flags & INSMOD_BUILTIN)
|
|
check_insmod_builtin(lm, i, &offset);
|
|
|
|
if (STREQ(lm->mod_section_data[i].name, ".text"))
|
|
lm->mod_text_start = lm->mod_base + offset;
|
|
|
|
if (STREQ(lm->mod_section_data[i].name, ".data"))
|
|
lm->mod_data_start = lm->mod_base + offset;
|
|
|
|
if (STREQ(lm->mod_section_data[i].name, ".bss"))
|
|
lm->mod_bss_start = lm->mod_base + offset;
|
|
|
|
if (STREQ(lm->mod_section_data[i].name, ".rodata"))
|
|
lm->mod_rodata_start = lm->mod_base + offset;
|
|
|
|
offset += bfd_section_size(bfd, section);
|
|
|
|
if (STREQ(bfd_get_section_name(bfd, section), ".kstrtab"))
|
|
offset += strlen(lm->mod_name)+1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Later versions of kmod no longer get the help from insmod,
|
|
* and while the heuristics might work, it's relatively
|
|
* straightforward to just try to match the sections in the object file
|
|
* with exported symbols.
|
|
*
|
|
* This works well if kallsyms is set, but may not work so well in other
|
|
* instances.
|
|
*/
|
|
static void
|
|
calculate_load_order_v2(struct load_module *lm, bfd *bfd, int dynamic,
|
|
void *minisyms, long symcount, unsigned int size)
|
|
{
|
|
struct syment *s1, *s2;
|
|
ulong sec_start;
|
|
bfd_byte *from, *fromend;
|
|
asymbol *store;
|
|
asymbol *sym;
|
|
symbol_info syminfo;
|
|
char *secname;
|
|
int i;
|
|
|
|
if ((store = bfd_make_empty_symbol(bfd)) == NULL)
|
|
error(FATAL, "bfd_make_empty_symbol() failed\n");
|
|
|
|
s1 = lm->mod_symtable;
|
|
s2 = lm->mod_symend;
|
|
while (s1 < s2) {
|
|
ulong sym_offset = s1->value - lm->mod_base;
|
|
if (MODULE_PSEUDO_SYMBOL(s1)) {
|
|
s1++;
|
|
continue;
|
|
}
|
|
|
|
/* Skip over symbols whose sections have been identified. */
|
|
for (i = 0; i < lm->mod_sections; i++) {
|
|
if ((lm->mod_section_data[i].flags & SEC_FOUND) == 0)
|
|
continue;
|
|
if (sym_offset >= lm->mod_section_data[i].offset
|
|
&& sym_offset < lm->mod_section_data[i].offset
|
|
+ lm->mod_section_data[i].size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Matched one of the sections. Skip symbol. */
|
|
if (i < lm->mod_sections) {
|
|
if (CRASHDEBUG(2)) {
|
|
fprintf(fp, "skip %lx %s %s\n", s1->value, s1->name,
|
|
lm->mod_section_data[i].name);
|
|
}
|
|
s1++;
|
|
continue;
|
|
}
|
|
|
|
/* Find the symbol in the object file. */
|
|
from = (bfd_byte *) minisyms;
|
|
fromend = from + symcount * size;
|
|
secname = NULL;
|
|
for (; from < fromend; from += size) {
|
|
if ((sym = bfd_minisymbol_to_symbol(bfd, dynamic, from,
|
|
store)) == NULL)
|
|
error(FATAL,
|
|
"bfd_minisymbol_to_symbol() failed\n");
|
|
|
|
bfd_get_symbol_info(bfd, sym, &syminfo);
|
|
if (CRASHDEBUG(3)) {
|
|
fprintf(fp,"matching sym %s %lx against bfd %s %lx\n",
|
|
s1->name, (long) s1->value, syminfo.name,
|
|
(long) syminfo.value);
|
|
}
|
|
if (strcmp(syminfo.name, s1->name) == 0) {
|
|
secname = (char *)bfd_get_section_name(bfd, sym->section);
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (secname == NULL) {
|
|
if (CRASHDEBUG(1)) {
|
|
fprintf(fp, "symbol %s not found in module\n", s1->name);
|
|
}
|
|
s1++;
|
|
continue;
|
|
}
|
|
|
|
/* Match the section it came in. */
|
|
for (i = 0; i < lm->mod_sections; i++) {
|
|
if (STREQ(lm->mod_section_data[i].name, secname)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == lm->mod_sections) {
|
|
fprintf(fp, "?? Section %s not found for symbol %s\n",
|
|
secname, s1->name);
|
|
s1++;
|
|
continue;
|
|
}
|
|
|
|
if (lm->mod_section_data[i].flags & SEC_FOUND) {
|
|
s1++;
|
|
continue;
|
|
}
|
|
|
|
/* Update the offset information for the section */
|
|
sec_start = s1->value - syminfo.value;
|
|
// sec_end = sec_start + lm->mod_section_data[i].size;
|
|
lm->mod_section_data[i].offset = sec_start - lm->mod_base;
|
|
lm->mod_section_data[i].flags |= SEC_FOUND;
|
|
|
|
if (CRASHDEBUG(2)) {
|
|
fprintf(fp, "update sec offset sym %s @ %lx val %lx section %s\n",
|
|
s1->name, s1->value, (ulong)syminfo.value, secname);
|
|
}
|
|
|
|
if (strcmp(secname, ".text") == 0)
|
|
lm->mod_text_start = sec_start;
|
|
|
|
if (strcmp(secname, ".bss") == 0)
|
|
lm->mod_bss_start = sec_start;
|
|
|
|
if (strcmp(secname, ".data") == 0)
|
|
lm->mod_data_start = sec_start;
|
|
|
|
if (strcmp(secname, ".data") == 0)
|
|
lm->mod_data_start = sec_start;
|
|
|
|
if (strcmp(secname, ".rodata") == 0)
|
|
lm->mod_rodata_start = sec_start;
|
|
s1++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Later versons of insmod store basic address information of each
|
|
* module in a format that looks like the following example of the
|
|
* nfsd module:
|
|
*
|
|
* d004d000 __insmod_nfsd_O/lib/modules/2.2.17/fs/nfsd.o_M3A7EE300_V131601
|
|
* d004d054 __insmod_nfsd_S.text_L30208
|
|
* d0054840 __insmod_nfsd_S.rodata_L8930
|
|
* d0056b40 __insmod_nfsd_S.data_L1220
|
|
* d00570c0 __insmod_nfsd_S.bss_L123840
|
|
*
|
|
* When that's true, override the offset value made by calculate_load_order().
|
|
*/
|
|
|
|
static void
|
|
check_insmod_builtin(struct load_module *lm, int index, ulong *offset)
|
|
{
|
|
struct syment *sp;
|
|
char buf[BUFSIZE];
|
|
ulong offs;
|
|
|
|
sprintf(buf, "__insmod_%s_S%s",
|
|
lm->mod_name,
|
|
lm->mod_section_data[index].name);
|
|
|
|
if (symbol_query(buf, NULL, &sp) == 1) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "check_insmod_builtin: %lx %s\n",
|
|
sp->value, sp->name);
|
|
offs = sp->value - lm->mod_base;
|
|
if (offs != *offset) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp,
|
|
"check_insmod_builtin: [%s] %s %lx != %lx\n",
|
|
lm->mod_name,
|
|
lm->mod_section_data[index].name,
|
|
offs, *offset);
|
|
*offset = offs;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Determine whether a module symbol is one of the insmod-created symbols
|
|
* described above.
|
|
*/
|
|
|
|
static int
|
|
is_insmod_builtin(struct load_module *lm, struct syment *sp)
|
|
{
|
|
char buf[BUFSIZE];
|
|
|
|
if (!(st->flags & INSMOD_BUILTIN))
|
|
return FALSE;
|
|
|
|
sprintf(buf, "__insmod_%s_S", lm->mod_name);
|
|
if (strstr(sp->name, buf))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Modified from typical "qsort" help functions to simulate section-ordering
|
|
* done by insmod when loading modules.
|
|
*/
|
|
static int
|
|
compare_prios(const void *v1, const void *v2)
|
|
{
|
|
struct mod_section_data *md1, *md2;
|
|
|
|
md1 = (struct mod_section_data *)v1;
|
|
md2 = (struct mod_section_data *)v2;
|
|
|
|
return (md1->priority < md2->priority ? -1 : 1);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* This routine scours a module object file namelist for global text and
|
|
* data symbols, sorting and storing them in a static table for quick
|
|
* reference. This allows access to non-EXPORT_SYMBOL() symbols.
|
|
* The object file is then passed to gdb for loading of all symbolic
|
|
* and debugging data.
|
|
*
|
|
* Thanks to David Addison (addy@quadrics.com) for the suggestion.
|
|
*/
|
|
int
|
|
load_module_symbols(char *modref, char *namelist, ulong base_addr)
|
|
{
|
|
static bfd *mbfd;
|
|
char **matching;
|
|
long symcount;
|
|
void *minisyms;
|
|
unsigned int size;
|
|
int result;
|
|
struct load_module *lm;
|
|
asymbol *sort_x;
|
|
asymbol *sort_y;
|
|
|
|
if (!is_module_name(modref, NULL, &lm))
|
|
error(FATAL, "%s: not a loaded module name\n", modref);
|
|
|
|
if ((lm->mod_flags & MOD_LOAD_SYMS) || strlen(lm->mod_namelist)) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: module symbols are already loaded\n",
|
|
modref);
|
|
return TRUE;
|
|
}
|
|
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "load_module_symbols: %s %s %lx %lx\n",
|
|
modref, namelist, base_addr, kt->flags);
|
|
|
|
switch (kt->flags & (KMOD_V1|KMOD_V2))
|
|
{
|
|
case KMOD_V1:
|
|
break;
|
|
case KMOD_V2:
|
|
st->current = lm;
|
|
BZERO(lm->mod_namelist, MAX_MOD_NAMELIST);
|
|
if (strlen(namelist) < MAX_MOD_NAMELIST)
|
|
strcpy(lm->mod_namelist, namelist);
|
|
else
|
|
strncpy(lm->mod_namelist, namelist, MAX_MOD_NAMELIST-1);
|
|
if (st->flags & USE_OLD_ADD_SYM)
|
|
goto add_symbols;
|
|
}
|
|
|
|
if ((mbfd = bfd_openr(namelist, NULL)) == NULL)
|
|
error(FATAL, "cannot open object file: %s\n", namelist);
|
|
|
|
if (!bfd_check_format_matches(mbfd, bfd_object, &matching))
|
|
error(FATAL, "cannot determine object file format: %s\n",
|
|
namelist);
|
|
|
|
if (LKCD_KERNTYPES() && (file_elf_version(namelist) == EV_DWARFEXTRACT))
|
|
goto add_symbols; /* no symbols, add the debuginfo */
|
|
|
|
if (!(bfd_get_file_flags(mbfd) & HAS_SYMS))
|
|
error(FATAL, "no symbols in object file: %s\n", namelist);
|
|
|
|
symcount = bfd_read_minisymbols(mbfd, FALSE, &minisyms, &size);
|
|
if (symcount < 0)
|
|
error(FATAL, "cannot access symbol table data: %s\n",
|
|
namelist);
|
|
else if (symcount == 0)
|
|
error(FATAL, "no symbols in object file: %s\n", namelist);
|
|
|
|
if (CRASHDEBUG(2)) {
|
|
fprintf(fp, "%ld symbols found in obj file %s\n", symcount,
|
|
namelist);
|
|
}
|
|
sort_x = bfd_make_empty_symbol(mbfd);
|
|
sort_y = bfd_make_empty_symbol(mbfd);
|
|
if (sort_x == NULL || sort_y == NULL)
|
|
error(FATAL, "bfd_make_empty_symbol() failed\n");
|
|
|
|
gnu_qsort(mbfd, minisyms, symcount, size, sort_x, sort_y);
|
|
|
|
store_load_module_symbols(mbfd, FALSE, minisyms, symcount,
|
|
size, base_addr, namelist);
|
|
|
|
free(minisyms);
|
|
|
|
bfd_close(mbfd);
|
|
|
|
add_symbols:
|
|
result = add_symbol_file(st->current);
|
|
|
|
if (CRASHDEBUG(2))
|
|
check_for_dups(st->current);
|
|
|
|
st->current = NULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Add a module's symbol file data to gdb's notion of the world.
|
|
*/
|
|
static int
|
|
add_symbol_file(struct load_module *lm)
|
|
{
|
|
struct gnu_request request, *req;
|
|
char buf[BUFSIZE];
|
|
int i, len;
|
|
char *secname;
|
|
|
|
req = &request;
|
|
BZERO(req, sizeof(struct gnu_request));
|
|
|
|
if ((lm->mod_flags & MOD_KALLSYMS) &&
|
|
add_symbol_file_kallsyms(lm, req))
|
|
return TRUE;
|
|
|
|
for (i = len = 0; i < lm->mod_sections; i++)
|
|
{
|
|
secname = lm->mod_section_data[i].name;
|
|
if ((lm->mod_section_data[i].flags & SEC_FOUND) &&
|
|
(!STREQ(secname, ".text") &&
|
|
!STREQ(secname, ".data.percpu") &&
|
|
!STREQ(secname, ".data..percpu"))) {
|
|
sprintf(buf, " -s %s 0x%lx", secname,
|
|
lm->mod_section_data[i].offset + lm->mod_base);
|
|
len += strlen(buf);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < lm->mod_sections; i++)
|
|
{
|
|
secname = lm->mod_section_data[i].name;
|
|
if ((lm->mod_section_data[i].flags & SEC_FOUND) &&
|
|
(STREQ(secname, ".data.percpu") ||
|
|
STREQ(secname, ".data..percpu"))) {
|
|
sprintf(buf, " -s %s 0x%lx", secname, lm->mod_percpu);
|
|
len += strlen(buf);
|
|
}
|
|
}
|
|
|
|
if (pc->curcmd_flags & MOD_READNOW)
|
|
lm->mod_flags |= MOD_DO_READNOW;
|
|
|
|
req->command = GNU_ADD_SYMBOL_FILE;
|
|
req->addr = (ulong)lm;
|
|
req->buf = GETBUF(len+BUFSIZE);
|
|
if (!CRASHDEBUG(1))
|
|
req->fp = pc->nullfp;
|
|
|
|
st->flags |= ADD_SYMBOL_FILE;
|
|
gdb_interface(req);
|
|
st->flags &= ~ADD_SYMBOL_FILE;
|
|
|
|
FREEBUF(req->buf);
|
|
sprintf(buf, "set complaints 0");
|
|
gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR);
|
|
|
|
return(!(req->flags & GNU_COMMAND_FAILED));
|
|
}
|
|
|
|
static int
|
|
add_symbol_file_percpu(struct load_module *lm, struct gnu_request *req, int buflen)
|
|
{
|
|
char pbuf[BUFSIZE];
|
|
int i, len;
|
|
char *secname;
|
|
|
|
len = strlen(req->buf);
|
|
for (i = 0; i < lm->mod_sections; i++) {
|
|
secname = lm->mod_section_data[i].name;
|
|
if ((lm->mod_section_data[i].flags & SEC_FOUND) &&
|
|
(STREQ(secname, ".data.percpu") ||
|
|
STREQ(secname, ".data..percpu"))) {
|
|
sprintf(pbuf, " -s %s 0x%lx", secname, lm->mod_percpu);
|
|
while ((len + strlen(pbuf)) >= buflen) {
|
|
RESIZEBUF(req->buf, buflen, buflen * 2);
|
|
buflen *= 2;
|
|
}
|
|
strcat(req->buf, pbuf);
|
|
len += strlen(pbuf);
|
|
}
|
|
}
|
|
return buflen;
|
|
}
|
|
|
|
/*
|
|
* Gather the module section data from the in-kernel data structures.
|
|
*/
|
|
static int
|
|
add_symbol_file_kallsyms(struct load_module *lm, struct gnu_request *req)
|
|
{
|
|
int len, buflen, done, nsections, retval;
|
|
ulong vaddr, array_entry, attribute, owner, name, address;
|
|
long name_type;
|
|
char buf[BUFSIZE];
|
|
char section_name[BUFSIZE/2];
|
|
ulong section_vaddr;
|
|
|
|
#if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1)
|
|
return FALSE;
|
|
#endif
|
|
if (!(st->flags & (MODSECT_VMASK|MODSECT_UNKNOWN))) {
|
|
STRUCT_SIZE_INIT(module_sect_attr, "module_sect_attr");
|
|
MEMBER_OFFSET_INIT(module_sect_attrs,
|
|
"module", "sect_attrs");
|
|
MEMBER_OFFSET_INIT(module_sect_attrs_attrs,
|
|
"module_sect_attrs", "attrs");
|
|
MEMBER_OFFSET_INIT(module_sect_attrs_nsections,
|
|
"module_sect_attrs", "nsections");
|
|
MEMBER_OFFSET_INIT(module_sect_attr_mattr,
|
|
"module_sect_attr", "mattr");
|
|
MEMBER_OFFSET_INIT(module_sect_attr_name,
|
|
"module_sect_attr", "name");
|
|
MEMBER_OFFSET_INIT(module_sect_attr_address,
|
|
"module_sect_attr", "address");
|
|
MEMBER_OFFSET_INIT(module_attribute_attr,
|
|
"module_attribute", "attr");
|
|
MEMBER_OFFSET_INIT(module_sect_attr_attr,
|
|
"module_sect_attr", "attr");
|
|
MEMBER_OFFSET_INIT(module_sections_attrs,
|
|
"module_sections", "attrs");
|
|
MEMBER_OFFSET_INIT(attribute_owner,
|
|
"attribute", "owner");
|
|
|
|
if (VALID_MEMBER(module_sect_attrs_attrs) &&
|
|
VALID_MEMBER(module_sect_attr_mattr) &&
|
|
VALID_MEMBER(module_attribute_attr) &&
|
|
VALID_MEMBER(module_sect_attrs_nsections))
|
|
st->flags |= MODSECT_V3;
|
|
else if (VALID_MEMBER(module_sect_attrs_attrs) &&
|
|
VALID_MEMBER(module_sect_attr_mattr) &&
|
|
VALID_MEMBER(module_attribute_attr))
|
|
st->flags |= MODSECT_V2;
|
|
else if (VALID_MEMBER(module_sect_attr_attr) &&
|
|
VALID_MEMBER(module_sections_attrs))
|
|
st->flags |= MODSECT_V1;
|
|
else
|
|
st->flags |= MODSECT_UNKNOWN;
|
|
|
|
if ((st->flags & MODSECT_UNKNOWN) ||
|
|
!VALID_STRUCT(module_sect_attr) ||
|
|
(INVALID_MEMBER(attribute_owner) &&
|
|
(st->flags & (MODSECT_V1|MODSECT_V2))) ||
|
|
INVALID_MEMBER(module_sect_attrs) ||
|
|
INVALID_MEMBER(module_sect_attr_name) ||
|
|
INVALID_MEMBER(module_sect_attr_address)) {
|
|
if (CRASHDEBUG(1))
|
|
error(WARNING,
|
|
"module section data structures "
|
|
"unrecognized or changed\n");
|
|
st->flags &= ~(MODSECT_VMASK);
|
|
st->flags |= MODSECT_UNKNOWN;
|
|
return FALSE;
|
|
}
|
|
} else if (st->flags & MODSECT_UNKNOWN)
|
|
return FALSE;
|
|
|
|
if (!readmem(lm->module_struct + OFFSET(module_sect_attrs),
|
|
KVADDR, &vaddr, sizeof(void *), "module.sect_attrs",
|
|
RETURN_ON_ERROR|QUIET))
|
|
return FALSE;
|
|
|
|
array_entry = attribute = 0;
|
|
|
|
switch (st->flags & MODSECT_VMASK)
|
|
{
|
|
case MODSECT_V1:
|
|
array_entry = vaddr + OFFSET(module_sections_attrs);
|
|
nsections = UNUSED;
|
|
break;
|
|
case MODSECT_V2:
|
|
array_entry = vaddr + OFFSET(module_sect_attrs_attrs);
|
|
nsections = UNUSED;
|
|
break;
|
|
case MODSECT_V3:
|
|
array_entry = vaddr + OFFSET(module_sect_attrs_attrs);
|
|
if (!readmem(vaddr + OFFSET(module_sect_attrs_nsections),
|
|
KVADDR, &nsections, sizeof(int),
|
|
"module_sect_attrs.nsections", RETURN_ON_ERROR|QUIET))
|
|
return FALSE;
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "nsections: %d\n", nsections);
|
|
break;
|
|
}
|
|
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "%s:\n", lm->mod_namelist);
|
|
|
|
name_type = MEMBER_TYPE("module_sect_attr", "name");
|
|
req->buf = GETBUF(buflen = 1024);
|
|
retval = FALSE;
|
|
|
|
for (done = FALSE; !done; array_entry += SIZE(module_sect_attr)) {
|
|
|
|
switch (st->flags & MODSECT_VMASK)
|
|
{
|
|
case MODSECT_V1:
|
|
attribute = array_entry + OFFSET(module_sect_attr_attr);
|
|
break;
|
|
case MODSECT_V2:
|
|
case MODSECT_V3:
|
|
attribute = array_entry + OFFSET(module_sect_attr_mattr)
|
|
+ OFFSET(module_attribute_attr);
|
|
break;
|
|
}
|
|
|
|
if (st->flags & (MODSECT_V1|MODSECT_V2))
|
|
owner = attribute + OFFSET(attribute_owner);
|
|
else
|
|
owner = UNUSED;
|
|
|
|
address = array_entry + OFFSET(module_sect_attr_address);
|
|
switch (name_type)
|
|
{
|
|
case TYPE_CODE_ARRAY:
|
|
name = array_entry + OFFSET(module_sect_attr_name);
|
|
break;
|
|
case TYPE_CODE_PTR:
|
|
if (!readmem(array_entry + OFFSET(module_sect_attr_name),
|
|
KVADDR, &name, sizeof(void *),
|
|
"module_sect_attr.name", RETURN_ON_ERROR|QUIET)) {
|
|
done = TRUE;
|
|
retval = FALSE;
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
done = TRUE;
|
|
retval = FALSE;
|
|
}
|
|
|
|
if (CRASHDEBUG(2)) {
|
|
fprintf(fp, "attribute: %lx ", attribute);
|
|
if (owner == UNUSED)
|
|
fprintf(fp, " owner: (not used)");
|
|
else
|
|
fprintf(fp, " owner: %lx ", owner);
|
|
fprintf(fp, " name: %lx ", name);
|
|
fprintf(fp, " address: %lx\n", address);
|
|
}
|
|
|
|
if (nsections == UNUSED) {
|
|
if (!readmem(owner, KVADDR, &vaddr, sizeof(void *),
|
|
"attribute.owner", RETURN_ON_ERROR|QUIET)) {
|
|
done = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (lm->module_struct != vaddr) {
|
|
done = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
BZERO(section_name, BUFSIZE/2);
|
|
if (!read_string(name, section_name, 32)) {
|
|
done = TRUE;
|
|
retval = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (!readmem(address, KVADDR, §ion_vaddr, sizeof(void *),
|
|
"module_sect_attr.address", RETURN_ON_ERROR|QUIET)) {
|
|
done = TRUE;
|
|
retval = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%lx %s\n", section_vaddr, section_name);
|
|
|
|
len = strlen(req->buf);
|
|
|
|
if (STREQ(section_name, ".text")) {
|
|
sprintf(buf, "add-symbol-file %s 0x%lx %s",
|
|
lm->mod_namelist, section_vaddr,
|
|
pc->curcmd_flags & MOD_READNOW ? "-readnow" : "");
|
|
while ((len + strlen(buf)) >= buflen) {
|
|
RESIZEBUF(req->buf, buflen, buflen * 2);
|
|
buflen *= 2;
|
|
}
|
|
shift_string_right(req->buf, strlen(buf));
|
|
BCOPY(buf, req->buf, strlen(buf));
|
|
retval = TRUE;
|
|
} else {
|
|
sprintf(buf, " -s %s 0x%lx", section_name, section_vaddr);
|
|
while ((len + strlen(buf)) >= buflen) {
|
|
RESIZEBUF(req->buf, buflen, buflen * 2);
|
|
buflen *= 2;
|
|
}
|
|
strcat(req->buf, buf);
|
|
}
|
|
|
|
if (nsections != UNUSED) {
|
|
if (--nsections == 0)
|
|
done = TRUE;
|
|
}
|
|
}
|
|
|
|
if (retval == FALSE) {
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: add_symbol_file_kallsyms failed\n",
|
|
lm->mod_namelist);
|
|
FREEBUF(req->buf);
|
|
req->buf = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Special case for per-cpu symbols
|
|
*/
|
|
buflen = add_symbol_file_percpu(lm, req, buflen);
|
|
|
|
lm->mod_flags |= MOD_NOPATCH;
|
|
req->command = GNU_ADD_SYMBOL_FILE;
|
|
req->addr = (ulong)lm;
|
|
if (!CRASHDEBUG(1))
|
|
req->fp = pc->nullfp;
|
|
|
|
st->flags |= ADD_SYMBOL_FILE;
|
|
gdb_interface(req);
|
|
st->flags &= ~ADD_SYMBOL_FILE;
|
|
|
|
FREEBUF(req->buf);
|
|
sprintf(buf, "set complaints 0");
|
|
gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR);
|
|
|
|
return(!(req->flags & GNU_COMMAND_FAILED));
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a syment structure of a valid symbol, determine which
|
|
* load_module (if any) it belongs to.
|
|
*/
|
|
static int
|
|
load_module_index(struct syment *sp)
|
|
{
|
|
int i;
|
|
ulong value;
|
|
struct load_module *lm;
|
|
|
|
value = sp->value;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
|
|
if (IN_MODULE(value, lm))
|
|
return i;
|
|
|
|
if (IN_MODULE_INIT(value, lm))
|
|
return i;
|
|
}
|
|
|
|
return (error(FATAL, "cannot find %lx (%s) in module space\n",
|
|
sp->value, sp->name));
|
|
}
|
|
|
|
/*
|
|
* Return the syment of a kallsyms-generated module symbol.
|
|
*/
|
|
static struct syment *
|
|
kallsyms_module_symbol(struct load_module *lm, symbol_info *syminfo)
|
|
{
|
|
struct syment *sp, *spx;
|
|
int cnt;
|
|
|
|
if (!(lm->mod_flags & MOD_KALLSYMS))
|
|
return NULL;
|
|
|
|
sp = NULL;
|
|
cnt = 0;
|
|
for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) {
|
|
if (!STREQ(spx->name, syminfo->name))
|
|
continue;
|
|
if (spx->cnt) {
|
|
cnt++;
|
|
continue;
|
|
}
|
|
|
|
spx->cnt++;
|
|
sp = spx;
|
|
break;
|
|
}
|
|
|
|
if (CRASHDEBUG(2)) {
|
|
if (cnt)
|
|
fprintf(fp, "kallsyms [%s] %s: multiply defined\n",
|
|
lm->mod_name, syminfo->name);
|
|
if (sp)
|
|
fprintf(fp, "kallsyms [%s] %s: %lx\n",
|
|
lm->mod_name, syminfo->name, sp->value);
|
|
else
|
|
fprintf(fp, "kallsyms [%s] %s: NOT FOUND\n",
|
|
lm->mod_name, syminfo->name);
|
|
}
|
|
|
|
return sp;
|
|
}
|
|
|
|
/*
|
|
* Replace the externally-defined module symbols found in store_load_modules()
|
|
* with all the text and data symbols found in the load module object file.
|
|
*/
|
|
static void
|
|
store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
|
long symcount, unsigned int size, ulong base_addr, char *namelist)
|
|
{
|
|
int i;
|
|
asymbol *store;
|
|
asymbol *sym;
|
|
bfd_byte *from, *fromend;
|
|
symbol_info syminfo;
|
|
struct syment *sp, *spx;
|
|
struct load_module *lm;
|
|
char name[BUFSIZE];
|
|
char *nameptr, *secname;
|
|
long index;
|
|
long symalloc;
|
|
int found;
|
|
|
|
if ((store = bfd_make_empty_symbol(bfd)) == NULL)
|
|
error(FATAL, "bfd_make_empty_symbol() failed\n");
|
|
|
|
st->current = lm = NULL;
|
|
|
|
/*
|
|
* Find out whether this module has already been loaded. Coming
|
|
* out of this for loop, lm->mod_load_symtable will either be set to
|
|
* a reusable symbol table, or NULL if it needs to be re-malloc'd.
|
|
*/
|
|
|
|
for (i = symalloc = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
|
|
if (lm->mod_base == base_addr) {
|
|
symalloc = symcount + lm->mod_ext_symcnt;
|
|
if (lm->mod_load_symtable &&
|
|
(lm->mod_symalloc < symalloc)) {
|
|
free(lm->mod_load_symtable);
|
|
namespace_ctl(NAMESPACE_FREE,
|
|
&lm->mod_load_namespace, NULL, NULL);
|
|
lm->mod_load_symtable = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == st->mods_installed)
|
|
error(FATAL, "cannot find module at %lx\n", base_addr);
|
|
|
|
if (!lm->mod_load_symtable) {
|
|
if ((lm->mod_load_symtable = (struct syment *)
|
|
calloc(symalloc, sizeof(struct syment))) == NULL)
|
|
error(FATAL, "module syment space malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
if (!namespace_ctl(NAMESPACE_INIT, &lm->mod_load_namespace,
|
|
(void *)symalloc, NULL))
|
|
error(FATAL, "module name space malloc: %s\n",
|
|
strerror(errno));
|
|
} else
|
|
namespace_ctl(NAMESPACE_REUSE, &lm->mod_load_namespace,
|
|
NULL, NULL);
|
|
|
|
st->current = lm;
|
|
lm->mod_symalloc = symalloc;
|
|
BZERO(lm->mod_namelist, MAX_MOD_NAMELIST);
|
|
if (strlen(namelist) < MAX_MOD_NAMELIST)
|
|
strcpy(lm->mod_namelist, namelist);
|
|
else
|
|
strncpy(lm->mod_namelist, namelist, MAX_MOD_NAMELIST-1);
|
|
lm->mod_text_start = lm->mod_data_start = 0;
|
|
lm->mod_rodata_start = lm->mod_bss_start = 0;
|
|
lm->mod_load_symcnt = 0;
|
|
lm->mod_sections = 0;
|
|
for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++)
|
|
spx->cnt = 0;
|
|
sp = lm->mod_load_symtable;
|
|
|
|
if (!(lm->mod_section_data = (struct mod_section_data *)
|
|
malloc(sizeof(struct mod_section_data) *
|
|
(bfd->section_count+1))))
|
|
error(FATAL, "module section data array malloc: %s\n",
|
|
strerror(errno));
|
|
|
|
bfd_map_over_sections(bfd, section_header_info, MODULE_SECTIONS);
|
|
|
|
if (kt->flags & KMOD_V1)
|
|
calculate_load_order_v1(lm, bfd);
|
|
else
|
|
calculate_load_order_v2(lm, bfd, dynamic, minisyms,
|
|
symcount, size);
|
|
|
|
|
|
from = (bfd_byte *) minisyms;
|
|
fromend = from + symcount * size;
|
|
for (; from < fromend; from += size)
|
|
{
|
|
if ((sym = bfd_minisymbol_to_symbol(bfd, dynamic, from, store))
|
|
== NULL)
|
|
error(FATAL, "bfd_minisymbol_to_symbol() failed\n");
|
|
|
|
bfd_get_symbol_info(bfd, sym, &syminfo);
|
|
|
|
secname = (char *)bfd_get_section_name(bfd, sym->section);
|
|
found = 0;
|
|
|
|
if (kt->flags & KMOD_V1) {
|
|
switch (syminfo.type)
|
|
{
|
|
case 'b':
|
|
case 'B':
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "%08lx (%c) [%s] %s\n",
|
|
(ulong)syminfo.value,
|
|
syminfo.type, secname, syminfo.name);
|
|
|
|
if (!lm->mod_bss_start)
|
|
break;
|
|
|
|
syminfo.value += lm->mod_bss_start;
|
|
found = 1;
|
|
break;
|
|
|
|
case 'd':
|
|
case 'D':
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "%08lx (%c) [%s] %s\n",
|
|
(ulong)syminfo.value,
|
|
syminfo.type, secname, syminfo.name);
|
|
|
|
if (STREQ(secname, ".rodata")) {
|
|
if (!lm->mod_rodata_start)
|
|
break;
|
|
syminfo.value += lm->mod_rodata_start;
|
|
} else {
|
|
if (!lm->mod_data_start)
|
|
break;
|
|
syminfo.value += lm->mod_data_start;
|
|
}
|
|
found = 1;
|
|
break;
|
|
|
|
case 't':
|
|
case 'T':
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "%08lx (%c) [%s] %s\n",
|
|
(ulong)syminfo.value,
|
|
syminfo.type, secname, syminfo.name);
|
|
|
|
if (! lm->mod_text_start) {
|
|
break;
|
|
}
|
|
|
|
if ((st->flags & INSMOD_BUILTIN) &&
|
|
(STREQ(name, "init_module") ||
|
|
STREQ(name, "cleanup_module")))
|
|
break;
|
|
|
|
syminfo.value += lm->mod_text_start;
|
|
found = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
/* Match the section it came in. */
|
|
for (i = 0; i < lm->mod_sections; i++) {
|
|
if (STREQ(lm->mod_section_data[i].name, secname)
|
|
&& (lm->mod_section_data[i].flags & SEC_FOUND)) {
|
|
break;
|
|
}
|
|
}
|
|
if (i < lm->mod_sections) {
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "%08lx (%c) [%s] %s\n",
|
|
(ulong)syminfo.value,
|
|
syminfo.type, secname, syminfo.name);
|
|
|
|
if ((st->flags & INSMOD_BUILTIN) &&
|
|
(STREQ(name, "init_module") ||
|
|
STREQ(name, "cleanup_module")))
|
|
found = FALSE;
|
|
else if (syminfo.name[0] == '.')
|
|
found = FALSE;
|
|
else if ((spx = kallsyms_module_symbol(lm, &syminfo))) {
|
|
syminfo.value = spx->value;
|
|
found = TRUE;
|
|
} else if (lm->mod_percpu &&
|
|
(STREQ(secname, ".data.percpu") ||
|
|
STREQ(secname, ".data..percpu"))) {
|
|
syminfo.value += lm->mod_percpu;
|
|
found = TRUE;
|
|
} else {
|
|
syminfo.value += lm->mod_section_data[i].offset + lm->mod_base;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
strcpy(name, syminfo.name);
|
|
strip_module_symbol_end(name);
|
|
strip_symbol_end(name, NULL);
|
|
if (machdep->verify_symbol(name, syminfo.value,
|
|
syminfo.type)) {
|
|
sp->value = syminfo.value;
|
|
sp->type = syminfo.type;
|
|
sp->flags |= MODULE_SYMBOL;
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&lm->mod_load_namespace, sp, name);
|
|
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "installing %c %08lx %s\n", syminfo.type, sp->value,
|
|
name);
|
|
|
|
sp++;
|
|
lm->mod_load_symcnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
lm->mod_load_symend = &lm->mod_load_symtable[lm->mod_load_symcnt];
|
|
|
|
/*
|
|
* Merge in any externals that didn't show up in the four
|
|
* syminfo data types accepted above, plus the two pseudo symbols.
|
|
* Note that the new syment name pointers haven't been resolved yet.
|
|
*/
|
|
for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) {
|
|
found = FALSE;
|
|
for (sp = lm->mod_load_symtable;
|
|
sp < lm->mod_load_symend; sp++) {
|
|
index = (long)sp->name;
|
|
nameptr = &lm->mod_load_namespace.address[index];
|
|
if (STREQ(spx->name, nameptr)) {
|
|
found = TRUE;
|
|
if (spx->value == sp->value) {
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp,
|
|
"%s: %s matches!\n",
|
|
lm->mod_name,
|
|
nameptr);
|
|
} else {
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp,
|
|
"[%s] %s: %lx != extern'd value: %lx\n",
|
|
lm->mod_name,
|
|
nameptr, sp->value,
|
|
spx->value);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "append ext %s (%lx)\n",
|
|
spx->name, spx->value);
|
|
/* append it here... */
|
|
namespace_ctl(NAMESPACE_INSTALL,
|
|
&lm->mod_load_namespace,
|
|
lm->mod_load_symend, spx->name);
|
|
|
|
lm->mod_load_symend->value = spx->value;
|
|
lm->mod_load_symend->type = spx->type;
|
|
lm->mod_load_symend->flags |= MODULE_SYMBOL;
|
|
lm->mod_load_symend++;
|
|
lm->mod_load_symcnt++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Append helpful pseudo symbols about found out sections.
|
|
* Use 'S' as its type which is never seen in existing symbols.
|
|
*/
|
|
for (i = 0; (pc->curcmd_flags & MOD_SECTIONS) &&
|
|
(i < lm->mod_sections); i++) {
|
|
if (!(lm->mod_section_data[i].flags & SEC_FOUND))
|
|
continue;
|
|
/* Section start */
|
|
lm->mod_load_symend->value = lm->mod_base +
|
|
lm->mod_section_data[i].offset;
|
|
lm->mod_load_symend->type = 'S';
|
|
lm->mod_load_symend->flags |= MODULE_SYMBOL;
|
|
sprintf(name, "_MODULE_SECTION_START [%s]",
|
|
lm->mod_section_data[i].name);
|
|
namespace_ctl(NAMESPACE_INSTALL, &lm->mod_load_namespace,
|
|
lm->mod_load_symend, name);
|
|
lm->mod_load_symend++;
|
|
lm->mod_load_symcnt++;
|
|
|
|
/* Section end */
|
|
lm->mod_load_symend->value = lm->mod_base +
|
|
lm->mod_section_data[i].offset +
|
|
lm->mod_section_data[i].size;
|
|
lm->mod_load_symend->type = 'S';
|
|
lm->mod_load_symend->flags |= MODULE_SYMBOL;
|
|
sprintf(name, "_MODULE_SECTION_END [%s]",
|
|
lm->mod_section_data[i].name);
|
|
namespace_ctl(NAMESPACE_INSTALL, &lm->mod_load_namespace,
|
|
lm->mod_load_symend, name);
|
|
lm->mod_load_symend++;
|
|
lm->mod_load_symcnt++;
|
|
}
|
|
|
|
namespace_ctl(NAMESPACE_COMPLETE, &lm->mod_load_namespace,
|
|
lm->mod_load_symtable, lm->mod_load_symend);
|
|
|
|
qsort(lm->mod_load_symtable, lm->mod_load_symcnt, sizeof(struct syment),
|
|
compare_syms);
|
|
|
|
lm->mod_load_symend--;
|
|
if (!MODULE_END(lm->mod_load_symend) &&
|
|
!IN_MODULE_PERCPU(lm->mod_load_symend->value, lm))
|
|
error(INFO, "%s: last symbol: %s is not _MODULE_END_%s?\n",
|
|
lm->mod_name, lm->mod_load_symend->name, lm->mod_name);
|
|
|
|
lm->mod_symtable = lm->mod_load_symtable;
|
|
lm->mod_symend = lm->mod_load_symend;
|
|
|
|
lm->mod_flags &= ~MOD_EXT_SYMS;
|
|
lm->mod_flags |= MOD_LOAD_SYMS;
|
|
|
|
st->flags |= LOAD_MODULE_SYMS;
|
|
}
|
|
|
|
/*
|
|
* Delete a load module's symbol table. If base_addr is NULL, delete the
|
|
* complete list of modules.
|
|
*/
|
|
void
|
|
delete_load_module(ulong base_addr)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
struct gnu_request request, *req;
|
|
|
|
req = &request;
|
|
BZERO(req, sizeof(struct gnu_request));
|
|
req->command = GNU_DELETE_SYMBOL_FILE;
|
|
|
|
if (base_addr == ALL_MODULES) {
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_flags & MOD_LOAD_SYMS) {
|
|
req->name = lm->mod_namelist;
|
|
gdb_interface(req);
|
|
}
|
|
if (lm->mod_load_symtable) {
|
|
free(lm->mod_load_symtable);
|
|
namespace_ctl(NAMESPACE_FREE,
|
|
&lm->mod_load_namespace, NULL, NULL);
|
|
}
|
|
if (lm->mod_flags & MOD_REMOTE)
|
|
unlink_module(lm);
|
|
lm->mod_symtable = lm->mod_ext_symtable;
|
|
lm->mod_symend = lm->mod_ext_symend;
|
|
lm->mod_flags &= ~(MOD_LOAD_SYMS|MOD_REMOTE|MOD_NOPATCH);
|
|
lm->mod_flags |= MOD_EXT_SYMS;
|
|
lm->mod_load_symtable = NULL;
|
|
lm->mod_load_symend = NULL;
|
|
lm->mod_namelist[0] = NULLCHAR;
|
|
lm->mod_load_symcnt = lm->mod_symalloc = 0;
|
|
lm->mod_text_start = lm->mod_data_start = 0;
|
|
lm->mod_bss_start = lm->mod_rodata_start = 0;
|
|
lm->mod_sections = 0;
|
|
lm->mod_percpu_size = 0;
|
|
if (lm->mod_section_data)
|
|
free(lm->mod_section_data);
|
|
lm->mod_section_data = (struct mod_section_data *)0;
|
|
lm->loaded_objfile = NULL;
|
|
}
|
|
st->flags &= ~LOAD_MODULE_SYMS;
|
|
return;
|
|
}
|
|
|
|
st->flags &= ~LOAD_MODULE_SYMS; /* restored below (if any found) */
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (lm->mod_base == base_addr) {
|
|
if (lm->mod_flags & MOD_LOAD_SYMS) {
|
|
req->name = lm->mod_namelist;
|
|
gdb_interface(req);
|
|
}
|
|
if (lm->mod_load_symtable) {
|
|
free(lm->mod_load_symtable);
|
|
namespace_ctl(NAMESPACE_FREE,
|
|
&lm->mod_load_namespace, NULL, NULL);
|
|
}
|
|
if (lm->mod_flags & MOD_REMOTE)
|
|
unlink_module(lm);
|
|
lm->mod_symtable = lm->mod_ext_symtable;
|
|
lm->mod_symend = lm->mod_ext_symend;
|
|
lm->mod_flags &= ~(MOD_LOAD_SYMS|MOD_REMOTE|MOD_NOPATCH);
|
|
lm->mod_flags |= MOD_EXT_SYMS;
|
|
lm->mod_load_symtable = NULL;
|
|
lm->mod_load_symend = NULL;
|
|
lm->mod_namelist[0] = NULLCHAR;
|
|
lm->mod_load_symcnt = lm->mod_symalloc = 0;
|
|
lm->mod_text_start = lm->mod_data_start = 0;
|
|
lm->mod_bss_start = lm->mod_rodata_start = 0;
|
|
lm->mod_percpu_size = 0;
|
|
lm->mod_sections = 0;
|
|
if (lm->mod_section_data)
|
|
free(lm->mod_section_data);
|
|
lm->mod_section_data = (struct mod_section_data *)0;
|
|
lm->loaded_objfile = NULL;
|
|
} else if (lm->mod_flags & MOD_LOAD_SYMS)
|
|
st->flags |= LOAD_MODULE_SYMS;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether a string is the name of a module. If requested, return
|
|
* the base address of the module.
|
|
*/
|
|
int
|
|
is_module_name(char *s, ulong *addr, struct load_module **lmp)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
|
|
if (NO_MODULES())
|
|
return FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (STREQ(s, lm->mod_name)) {
|
|
if (addr)
|
|
*addr = lm->mod_base;
|
|
if (lmp)
|
|
*lmp = lm;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether an value is the base address of a module. If requested,
|
|
* return the module name.
|
|
*/
|
|
int
|
|
is_module_address(ulong check_addr, char *module_name)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
|
|
if (NO_MODULES())
|
|
return FALSE;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (check_addr == lm->mod_base) {
|
|
if (module_name)
|
|
strcpy(module_name, lm->mod_name);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* In a MOD_EXT_SYMBOLS module, find a rough estimate as to where the
|
|
* .rodata section starts. The value will be used by is_kernel_text()
|
|
* when symbols are not loaded.
|
|
*/
|
|
|
|
static void
|
|
find_mod_etext(struct load_module *lm)
|
|
{
|
|
ulong start, end;
|
|
char *modbuf;
|
|
ulong maxchunk, alloc;
|
|
long offset = 0;
|
|
|
|
start = roundup(lm->mod_size_of_struct, sizeof(long)) + lm->mod_base;
|
|
end = lm->mod_base + lm->mod_size;
|
|
|
|
maxchunk = MIN(end-start, KILOBYTES(32));
|
|
|
|
modbuf = GETBUF(maxchunk);
|
|
|
|
while (start < end) {
|
|
alloc = MIN(maxchunk, end-start);
|
|
|
|
readmem(start, KVADDR, modbuf, alloc,
|
|
"module rodata search chunk", FAULT_ON_ERROR);
|
|
|
|
if ((offset = rodata_search((ulong *)modbuf, alloc)) >= 0)
|
|
break;
|
|
|
|
start += alloc;
|
|
}
|
|
|
|
FREEBUF(modbuf);
|
|
|
|
if (offset >= 0)
|
|
lm->mod_etext_guess = start + offset;
|
|
else
|
|
lm->mod_etext_guess = end;
|
|
|
|
}
|
|
|
|
#define ASCII_WORD_COUNT (16/sizeof(ulong))
|
|
|
|
static long
|
|
rodata_search(ulong *buf, ulong size)
|
|
{
|
|
int i, acnt, words;
|
|
long offset;
|
|
ulong *wordptr;
|
|
|
|
words = size/sizeof(ulong);
|
|
wordptr = buf;
|
|
|
|
for (i = acnt = 0, offset = -1; i < words; i++, wordptr++) {
|
|
if (ascii_long(*wordptr)) {
|
|
if (acnt++ == 0)
|
|
offset = i * sizeof(ulong);
|
|
} else {
|
|
acnt = 0;
|
|
offset = -1;
|
|
}
|
|
|
|
if (acnt == ASCII_WORD_COUNT)
|
|
break;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static int
|
|
ascii_long(ulong word)
|
|
{
|
|
int i, cnt;
|
|
unsigned char c;
|
|
|
|
for (i = cnt = 0; i < sizeof(ulong); i++) {
|
|
c = (unsigned char)((word >> (i*BITS_PER_BYTE)) & 0xff);
|
|
if ((c >= ' ') && (c < 0x7f))
|
|
cnt++;
|
|
}
|
|
|
|
return (cnt == sizeof(ulong));
|
|
}
|
|
|
|
/*
|
|
* Symbol sorting routines adapted from binutils/nm.c
|
|
*/
|
|
|
|
/* nm.c -- Describe symbol table of a rel file.
|
|
Copyright 1991, 92, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
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 of the License, 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; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
static bfd *gnu_sort_bfd;
|
|
static asymbol *gnu_sort_x;
|
|
static asymbol *gnu_sort_y;
|
|
|
|
#define valueof(x) ((x)->section->vma + (x)->value)
|
|
|
|
static int
|
|
non_numeric_forward(const void *P_x, const void *P_y)
|
|
{
|
|
asymbol *x, *y;
|
|
const char *xn, *yn;
|
|
|
|
x = bfd_minisymbol_to_symbol(gnu_sort_bfd, FALSE, P_x, gnu_sort_x);
|
|
y = bfd_minisymbol_to_symbol(gnu_sort_bfd, FALSE, P_y, gnu_sort_y);
|
|
if (x == NULL || y == NULL)
|
|
error(FATAL, "bfd_minisymbol_to_symbol failed\n");
|
|
|
|
xn = bfd_asymbol_name(x);
|
|
yn = bfd_asymbol_name(y);
|
|
|
|
return ((xn == NULL) ? ((yn == NULL) ? 0 : -1) :
|
|
((yn == NULL) ? 1 : strcmp (xn, yn)));
|
|
}
|
|
|
|
static int
|
|
numeric_forward(const void *P_x, const void *P_y)
|
|
{
|
|
asymbol *x, *y;
|
|
asection *xs, *ys;
|
|
|
|
x = bfd_minisymbol_to_symbol(gnu_sort_bfd, FALSE, P_x, gnu_sort_x);
|
|
y = bfd_minisymbol_to_symbol(gnu_sort_bfd, FALSE, P_y, gnu_sort_y);
|
|
if (x == NULL || y == NULL)
|
|
error(FATAL, "bfd_minisymbol_to_symbol failed\n");
|
|
|
|
if (st->_stext_vmlinux == UNINITIALIZED) {
|
|
if (STREQ(x->name, "_stext"))
|
|
st->_stext_vmlinux = valueof(x);
|
|
else if (STREQ(y->name, "_stext"))
|
|
st->_stext_vmlinux = valueof(y);
|
|
}
|
|
if (kt->flags2 & KASLR_CHECK) {
|
|
if (STREQ(x->name, "module_load_offset") ||
|
|
STREQ(y->name, "module_load_offset")) {
|
|
kt->flags2 &= ~KASLR_CHECK;
|
|
kt->flags2 |= (RELOC_AUTO|KASLR);
|
|
}
|
|
}
|
|
|
|
if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) {
|
|
/* Need for kaslr_offset and phys_base */
|
|
if (STREQ(x->name, "divide_error"))
|
|
st->divide_error_vmlinux = valueof(x);
|
|
else if (STREQ(y->name, "divide_error"))
|
|
st->divide_error_vmlinux = valueof(y);
|
|
|
|
if (STREQ(x->name, "idt_table"))
|
|
st->idt_table_vmlinux = valueof(x);
|
|
else if (STREQ(y->name, "idt_table"))
|
|
st->idt_table_vmlinux = valueof(y);
|
|
|
|
if (STREQ(x->name, "saved_command_line"))
|
|
st->saved_command_line_vmlinux = valueof(x);
|
|
else if (STREQ(y->name, "saved_command_line"))
|
|
st->saved_command_line_vmlinux = valueof(y);
|
|
|
|
if (STREQ(x->name, "pti_init"))
|
|
st->pti_init_vmlinux = valueof(x);
|
|
else if (STREQ(y->name, "kaiser_init"))
|
|
st->kaiser_init_vmlinux = valueof(y);
|
|
}
|
|
|
|
xs = bfd_get_section(x);
|
|
ys = bfd_get_section(y);
|
|
|
|
if (bfd_is_und_section(xs)) {
|
|
if (!bfd_is_und_section(ys))
|
|
return -1;
|
|
}
|
|
else if (bfd_is_und_section (ys))
|
|
return 1;
|
|
else if (valueof (x) != valueof (y))
|
|
return valueof (x) < valueof (y) ? -1 : 1;
|
|
|
|
return non_numeric_forward(P_x, P_y);
|
|
}
|
|
|
|
static void
|
|
gnu_qsort(bfd *bfd,
|
|
void *minisyms,
|
|
long symcount,
|
|
unsigned int size,
|
|
asymbol *x,
|
|
asymbol *y)
|
|
{
|
|
gnu_sort_bfd = bfd;
|
|
gnu_sort_x = x;
|
|
gnu_sort_y = y;
|
|
|
|
qsort(minisyms, symcount, size, numeric_forward);
|
|
}
|
|
|
|
/*
|
|
* Keep a stash of commonly-accessed text locations checked by the
|
|
* back_trace code. The saved values unsigned 32-bit values.
|
|
* The same routine is used to store and query, based upon whether
|
|
* the passed-in value and valptr args are non-zero.
|
|
*/
|
|
#define TEXT_CACHE (50)
|
|
#define MAX_TEXT_CACHE (TEXT_CACHE*4)
|
|
|
|
struct text_cache_entry {
|
|
ulong vaddr;
|
|
uint32_t value;
|
|
};
|
|
|
|
static struct text_cache {
|
|
int index;
|
|
int entries;
|
|
ulong hits;
|
|
ulong refs;
|
|
struct text_cache_entry *cache;
|
|
} text_cache = { 0 };
|
|
|
|
/*
|
|
* Cache the contents of 32-bit text addresses. If "value" is set, the purpose
|
|
* is to cache it. If "valptr" is set, a query is being made for the text
|
|
* address.
|
|
*/
|
|
int
|
|
text_value_cache(ulong vaddr, uint32_t value, uint32_t *valptr)
|
|
{
|
|
int i;
|
|
struct text_cache *tc;
|
|
|
|
if (!is_kernel_text(vaddr))
|
|
return FALSE;
|
|
|
|
tc = &text_cache;
|
|
|
|
if (!tc->cache) {
|
|
if (!(tc->cache = (struct text_cache_entry *)
|
|
malloc(sizeof(struct text_cache_entry) * TEXT_CACHE)))
|
|
return FALSE;
|
|
BZERO(tc->cache, sizeof(struct text_cache_entry) * TEXT_CACHE);
|
|
tc->index = 0;
|
|
tc->entries = TEXT_CACHE;
|
|
}
|
|
|
|
if (value) {
|
|
for (i = 0; i < tc->entries; i++) {
|
|
if (tc->cache[i].vaddr == vaddr)
|
|
return TRUE;
|
|
}
|
|
|
|
i = tc->index;
|
|
tc->cache[i].vaddr = vaddr;
|
|
tc->cache[i].value = value;
|
|
tc->index++;
|
|
if (tc->index == MAX_TEXT_CACHE) {
|
|
tc->index = 0;
|
|
} else if (tc->index == tc->entries) {
|
|
struct text_cache_entry *old_cache;
|
|
|
|
old_cache = tc->cache;
|
|
if ((tc->cache = (struct text_cache_entry *)
|
|
realloc(old_cache, sizeof(struct text_cache_entry) *
|
|
(TEXT_CACHE+tc->entries)))) {
|
|
BZERO(&tc->cache[tc->index],
|
|
sizeof(struct text_cache_entry) *
|
|
TEXT_CACHE);
|
|
tc->entries += TEXT_CACHE;
|
|
} else {
|
|
tc->cache = old_cache;
|
|
tc->index = 0;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
if (valptr) {
|
|
tc->refs++;
|
|
|
|
for (i = 0; i < tc->entries; i++) {
|
|
if (!tc->cache[i].vaddr)
|
|
return FALSE;
|
|
|
|
if (tc->cache[i].vaddr == vaddr) {
|
|
*valptr = tc->cache[i].value;
|
|
tc->hits++;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* The gdb disassembler reads text memory byte-by-byte, so this routine
|
|
* acts as a front-end to the 32-bit (4-byte) text storage.
|
|
*/
|
|
|
|
int
|
|
text_value_cache_byte(ulong vaddr, unsigned char *valptr)
|
|
{
|
|
int i;
|
|
int shift;
|
|
struct text_cache *tc;
|
|
ulong valtmp;
|
|
|
|
if (!is_kernel_text(vaddr))
|
|
return FALSE;
|
|
|
|
tc = &text_cache;
|
|
|
|
tc->refs++;
|
|
|
|
for (i = 0; i < tc->entries; i++) {
|
|
if (!tc->cache[i].vaddr)
|
|
return FALSE;
|
|
|
|
if ((vaddr >= tc->cache[i].vaddr) &&
|
|
(vaddr < (tc->cache[i].vaddr+SIZEOF_32BIT))) {
|
|
valtmp = tc->cache[i].value;
|
|
shift = (vaddr - tc->cache[i].vaddr) * 8;
|
|
valtmp >>= shift;
|
|
*valptr = valtmp & 0xff;
|
|
tc->hits++;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
dump_text_value_cache(int verbose)
|
|
{
|
|
int i;
|
|
struct syment *sp;
|
|
ulong offset;
|
|
struct text_cache *tc;
|
|
|
|
tc = &text_cache;
|
|
|
|
if (!verbose) {
|
|
if (!tc->refs || !tc->cache)
|
|
return;
|
|
|
|
fprintf(stderr, " text hit rate: %2ld%% (%ld of %ld)\n",
|
|
(tc->hits * 100)/tc->refs,
|
|
(ulong)tc->hits, (ulong)tc->refs);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; tc->cache && (i < tc->entries); i++) {
|
|
if (!tc->cache[i].vaddr)
|
|
break;
|
|
fprintf(fp, "[%2d]: %lx %08x ", i, tc->cache[i].vaddr,
|
|
tc->cache[i].value);
|
|
if ((sp = value_search(tc->cache[i].vaddr, &offset))) {
|
|
fprintf(fp, "(%s+", sp->name);
|
|
switch (pc->output_radix)
|
|
{
|
|
case 10:
|
|
fprintf(fp, "%ld)", offset);
|
|
break;
|
|
case 16:
|
|
fprintf(fp, "%lx)", offset);
|
|
break;
|
|
}
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
fprintf(fp,
|
|
"text_cache entries: %d index: %d hit rate: %ld%% (%ld of %ld)\n",
|
|
tc->entries, tc->index,
|
|
(tc->hits * 100)/(tc->refs ? tc->refs : 1),
|
|
tc->hits, tc->refs);
|
|
|
|
}
|
|
|
|
void
|
|
clear_text_value_cache(void)
|
|
{
|
|
int i;
|
|
struct text_cache *tc;
|
|
|
|
tc = &text_cache;
|
|
tc->index = 0;
|
|
|
|
for (i = 0; tc->cache && (i < tc->entries); i++) {
|
|
tc->cache[i].vaddr = 0;
|
|
tc->cache[i].value = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If a System.map file or a debug kernel was specified, the name hash
|
|
* has been filled -- so sync up gdb's notion of symbol values with
|
|
* the local values, taking dups into account. Given that gdb's
|
|
* minimal_symbol dump is sorted by value, shortcut the get_syment_array()
|
|
* call if the sp after the last one found is associated with the
|
|
* new one.
|
|
*/
|
|
|
|
#define last_sp addr2
|
|
|
|
int
|
|
patch_kernel_symbol(struct gnu_request *req)
|
|
{
|
|
int i, c;
|
|
long relocate_display;
|
|
struct syment *sp_array[1000], *sp;
|
|
|
|
if (req->name == PATCH_KERNEL_SYMBOLS_START) {
|
|
if (kt->relocate) {
|
|
if ((long)kt->relocate < 0)
|
|
relocate_display = (kt->relocate * -1) >> 20;
|
|
else
|
|
relocate_display = kt->relocate >> 20;
|
|
error(WARNING,
|
|
"\nkernel relocated [%ldMB]: patching %ld gdb minimal_symbol values\n",
|
|
relocate_display, st->symcnt);
|
|
}
|
|
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" :
|
|
"\nplease wait... (patching %ld gdb minimal_symbol values) ",
|
|
st->symcnt);
|
|
fflush(fp);
|
|
req->count = 0;
|
|
req->length = 0;
|
|
req->last_sp = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (req->name == PATCH_KERNEL_SYMBOLS_STOP) {
|
|
fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" :
|
|
"\r \r");
|
|
st->flags |= GDB_SYMS_PATCHED;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!req->name || !req->addr)
|
|
return FALSE;
|
|
|
|
sp = (struct syment *)req->last_sp;
|
|
sp += sp ? 1 : 0;
|
|
if (sp && (sp->cnt == 1) && !(sp->flags & SYMBOL_NAME_USED) &&
|
|
STREQ(sp->name, req->name)) {
|
|
*((ulong *)req->addr) = sp->value;
|
|
sp->flags |= SYMBOL_NAME_USED;
|
|
req->last_sp = (ulong)sp;
|
|
} else {
|
|
switch (c = get_syment_array(req->name, sp_array, 1000))
|
|
{
|
|
case 0: req->last_sp = 0;
|
|
return TRUE;
|
|
|
|
case 1:
|
|
*((ulong *)req->addr) = sp_array[0]->value;
|
|
sp_array[0]->flags |= SYMBOL_NAME_USED;
|
|
req->last_sp = (ulong)sp_array[0];
|
|
break;
|
|
|
|
default:
|
|
for (i = 0; i < c; i++) {
|
|
if (sp_array[i]->flags & SYMBOL_NAME_USED)
|
|
continue;
|
|
*((ulong *)req->addr) = sp_array[i]->value;
|
|
sp_array[i]->flags |= SYMBOL_NAME_USED;
|
|
req->last_sp = (ulong)sp_array[i];
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef last_sp
|
|
|
|
/*
|
|
* If the first offset/size is bogus, then use the second if it's OK.
|
|
* But if both are bogus, then check whether we're debugging datatypes,
|
|
* and act accordingly.
|
|
*/
|
|
long
|
|
OFFSET_option(long offset1, long offset2, char *func, char *file, int line,
|
|
char *item1, char *item2)
|
|
{
|
|
char errmsg[BUFSIZE];
|
|
|
|
if (offset1 >= 0)
|
|
return offset1;
|
|
if (offset2 >= 0)
|
|
return offset2;
|
|
|
|
if (pc->flags & DATADEBUG) {
|
|
void *retaddr[NUMBER_STACKFRAMES] = { 0 };
|
|
SAVE_RETURN_ADDRESS(retaddr);
|
|
sprintf(errmsg,
|
|
"invalid (optional) structure member offsets: %s or %s",
|
|
item1, item2);
|
|
datatype_error(retaddr, errmsg, func, file, line);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
long
|
|
SIZE_option(long size1, long size2, char *func, char *file, int line,
|
|
char *item1, char *item2)
|
|
{
|
|
char errmsg[BUFSIZE];
|
|
|
|
if (size1 >= 0)
|
|
return size1;
|
|
if (size2 >= 0)
|
|
return size2;
|
|
|
|
if (pc->flags & DATADEBUG) {
|
|
void *retaddr[NUMBER_STACKFRAMES] = { 0 };
|
|
SAVE_RETURN_ADDRESS(retaddr);
|
|
sprintf(errmsg, "invalid (optional) structure sizes: %s or %s",
|
|
item1, item2);
|
|
datatype_error(retaddr, errmsg, func, file, line);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Do the work of the former OFFSET() and SIZE() macros.
|
|
*
|
|
* For now verification that the offset is legitimate is only done
|
|
* if the "--data_debug" command line option was used. There
|
|
* could still be constructs like "OFFSET(x) >= 0" in the current
|
|
* code, or in user extensions. Perhaps there should be an option
|
|
* to turn it off instead?
|
|
*/
|
|
long
|
|
OFFSET_verify(long offset, char *func, char *file, int line, char *item)
|
|
{
|
|
char errmsg[BUFSIZE];
|
|
|
|
if (!(pc->flags & DATADEBUG))
|
|
return offset;
|
|
|
|
if (offset < 0) {
|
|
void *retaddr[NUMBER_STACKFRAMES] = { 0 };
|
|
SAVE_RETURN_ADDRESS(retaddr);
|
|
sprintf(errmsg, "invalid structure member offset: %s",
|
|
item);
|
|
datatype_error(retaddr, errmsg, func, file, line);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
long
|
|
SIZE_verify(long size, char *func, char *file, int line, char *item)
|
|
{
|
|
char errmsg[BUFSIZE];
|
|
|
|
if (!(pc->flags & DATADEBUG))
|
|
return size;
|
|
|
|
if (size < 0) {
|
|
void *retaddr[NUMBER_STACKFRAMES] = { 0 };
|
|
SAVE_RETURN_ADDRESS(retaddr);
|
|
sprintf(errmsg, "invalid structure size: %s", item);
|
|
datatype_error(retaddr, errmsg, func, file, line);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* Perform the common datatype error handling.
|
|
*/
|
|
static void
|
|
datatype_error(void **retaddr, char *errmsg, char *func, char *file, int line)
|
|
{
|
|
char buf[BUFSIZE];
|
|
int fd;
|
|
|
|
fprintf(stderr, "\n%s: %s\n", pc->curcmd, errmsg);
|
|
fprintf(stderr, "%s FILE: %s LINE: %d FUNCTION: %s()\n\n",
|
|
space(strlen(pc->curcmd)), file, line, func);
|
|
fflush(stderr);
|
|
|
|
dump_trace(retaddr);
|
|
|
|
if (pc->flags & TTY) {
|
|
if ((fd = open("/dev/tty", O_RDONLY)) >= 0) {
|
|
tcsetattr(fd, TCSANOW, &pc->termios_orig);
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
if (pc->flags & DROP_CORE)
|
|
drop_core("DROP_CORE flag set: forcing a segmentation fault\n");
|
|
|
|
if (CRASHDEBUG(1))
|
|
gdb_readnow_warning();
|
|
|
|
if (pc->flags & RUNTIME) {
|
|
sprintf(buf, "%s\n%s FILE: %s LINE: %d FUNCTION: %s()\n",
|
|
errmsg, space(strlen(pc->curcmd)), file, line, func);
|
|
error(FATAL, "%s\n", buf);
|
|
}
|
|
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Dump a trace leading to the improper datatype usage.
|
|
*/
|
|
void
|
|
dump_trace(void **retaddr)
|
|
{
|
|
int i, c;
|
|
char *thisfile;
|
|
char *arglist[MAXARGS];
|
|
char buf[BUFSIZE];
|
|
FILE *pipe;
|
|
ulong vaddr, size, lookfor;
|
|
ulong last_vaddr, last_size;
|
|
char symbol[BUFSIZE];
|
|
const char *nm_call;
|
|
|
|
fflush(fp);
|
|
fflush(stdout);
|
|
fflush(pc->stdpipe);
|
|
|
|
thisfile = get_thisfile();
|
|
|
|
fprintf(stderr, "[%s] error trace: ", thisfile);
|
|
for (i = (NUMBER_STACKFRAMES-1); i >= 0; i--) {
|
|
if (retaddr[i])
|
|
fprintf(stderr, "%s%lx%s",
|
|
i == 3 ? "" : "=> ",
|
|
(ulong)retaddr[i],
|
|
i == 0 ? "\n" : " ");
|
|
}
|
|
fflush(stderr);
|
|
|
|
if (!file_exists("/usr/bin/nm", NULL)) {
|
|
fprintf(stderr, "crash: /usr/bin/nm: no such file\n");
|
|
return;
|
|
}
|
|
|
|
if (is_binary_stripped(thisfile))
|
|
nm_call = "/usr/bin/nm -DSBn %s";
|
|
else
|
|
nm_call = "/usr/bin/nm -BSn %s";
|
|
|
|
last_size = 0;
|
|
|
|
for (i = 0; i < NUMBER_STACKFRAMES; i++) {
|
|
if (!(lookfor = (ulong)retaddr[i]))
|
|
continue;
|
|
|
|
sprintf(buf, nm_call, thisfile);
|
|
if (!(pipe = popen(buf, "r"))) {
|
|
perror("pipe");
|
|
break;
|
|
}
|
|
|
|
last_vaddr = 0;
|
|
BZERO(symbol, BUFSIZE);
|
|
|
|
while (fgets(buf, BUFSIZE, pipe)) {
|
|
c = parse_line(strip_linefeeds(buf), arglist);
|
|
if (c != 4)
|
|
continue;
|
|
vaddr = htol(arglist[0], FAULT_ON_ERROR, NULL);
|
|
size = htol(arglist[1], FAULT_ON_ERROR, NULL);
|
|
if (vaddr > lookfor) {
|
|
if ((lookfor - last_vaddr) > last_size)
|
|
fprintf(stderr, "%s %lx: (undetermined)\n",
|
|
i == 0 ? "\n" : "",
|
|
lookfor);
|
|
else
|
|
fprintf(stderr, "%s %lx: %s+%ld\n",
|
|
i == 0 ? "\n" : "",
|
|
lookfor, symbol,
|
|
lookfor-last_vaddr);
|
|
break;
|
|
}
|
|
strcpy(symbol, arglist[3]);
|
|
last_vaddr = vaddr;
|
|
last_size = size;
|
|
}
|
|
|
|
pclose(pipe);
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
/*
|
|
* Try best to determine which executable this is.
|
|
*/
|
|
static char *
|
|
get_thisfile(void)
|
|
{
|
|
char *buf1;
|
|
char buf2[BUFSIZE];
|
|
char *tok, *path;
|
|
|
|
if (pc->program_path[0] == '.' ||
|
|
pc->program_path[0] == '/')
|
|
return pc->program_path;
|
|
|
|
if ((path = getenv("PATH"))) {
|
|
strcpy(buf2, path);
|
|
} else
|
|
return pc->program_path;
|
|
|
|
buf1 = GETBUF(BUFSIZE);
|
|
tok = strtok(buf2, ":");
|
|
while (tok) {
|
|
sprintf(buf1, "%s/%s", tok, pc->program_name);
|
|
if (file_exists(buf1, NULL) && is_elf_file(buf1)) {
|
|
return buf1;
|
|
}
|
|
tok = strtok(NULL, ":");
|
|
}
|
|
|
|
return pc->program_path;
|
|
}
|
|
|
|
/*
|
|
* Check whether an address fits into any existing init_module() functions,
|
|
* and if so, return the load_module.
|
|
*/
|
|
struct load_module *
|
|
init_module_function(ulong vaddr)
|
|
{
|
|
int i;
|
|
struct load_module *lm;
|
|
|
|
if (((kt->flags & (KMOD_V1|KMOD_V2)) == KMOD_V1) ||
|
|
INVALID_MEMBER(module_init_text_size) ||
|
|
INVALID_MEMBER(module_module_init))
|
|
return NULL;
|
|
|
|
for (i = 0; i < st->mods_installed; i++) {
|
|
lm = &st->load_modules[i];
|
|
if (!lm->mod_init_module_ptr || !lm->mod_init_text_size)
|
|
continue;
|
|
|
|
if ((vaddr >= lm->mod_init_module_ptr) &&
|
|
(vaddr < (lm->mod_init_module_ptr+lm->mod_init_text_size))
|
|
&& accessible(vaddr))
|
|
return lm;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* The caller fills in the structure and member name fields of
|
|
* the passed-in struct_member_data structure, which are then
|
|
* passed to the gdb "printm" command to get the member data.
|
|
*
|
|
* Adapted from Qiao Nuohan's "pstruct" extension module.
|
|
*/
|
|
int
|
|
fill_struct_member_data(struct struct_member_data *smd)
|
|
{
|
|
int i, cnt;
|
|
char buf[BUFSIZE];
|
|
char *printm_list[MAXARGS];
|
|
|
|
cnt = 0;
|
|
sprintf(buf, "printm ((struct %s *)0x0).%s",
|
|
smd->structure, smd->member);
|
|
|
|
open_tmpfile2();
|
|
|
|
if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR))
|
|
return FALSE;
|
|
|
|
rewind(pc->tmpfile2);
|
|
if (fgets(buf, BUFSIZE, pc->tmpfile2)) {
|
|
if (CRASHDEBUG(2))
|
|
fprintf(fp, "%s.%s: %s",
|
|
smd->structure, smd->member, buf);
|
|
cnt = parse_line(buf, printm_list);
|
|
}
|
|
|
|
close_tmpfile2();
|
|
|
|
if (cnt != 6)
|
|
return FALSE;
|
|
for (i = 0; i < cnt; i++) {
|
|
if (!decimal(printm_list[i], 0))
|
|
return FALSE;
|
|
}
|
|
|
|
smd->type = dtol(printm_list[0], RETURN_ON_ERROR, NULL);
|
|
smd->unsigned_type = dtol(printm_list[1], RETURN_ON_ERROR, NULL);
|
|
smd->length = dtol(printm_list[2], RETURN_ON_ERROR, NULL);
|
|
smd->offset = dtol(printm_list[3], RETURN_ON_ERROR, NULL);
|
|
smd->bitpos = dtol(printm_list[4], RETURN_ON_ERROR, NULL);
|
|
smd->bitsize = dtol(printm_list[5], RETURN_ON_ERROR, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
add_to_downsized(char *name)
|
|
{
|
|
struct downsized *ds;
|
|
|
|
ds = &st->downsized;
|
|
|
|
while (ds->name)
|
|
ds = ds->next;
|
|
|
|
if (!(ds->name = (char *)malloc(strlen(name)+1)) ||
|
|
!(ds->next = (struct downsized *)calloc(1, sizeof(struct downsized))))
|
|
error(FATAL,
|
|
"cannot calloc/malloc downsized struct or \"%s\" name string\n", name);
|
|
|
|
strcpy(ds->name, name);
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%sadd_to_downsized: \"%s\"\n",
|
|
(pc->flags & PLEASE_WAIT) ? "\n" : "", name);
|
|
}
|
|
|
|
int
|
|
is_downsized(char *name)
|
|
{
|
|
struct downsized *ds;
|
|
|
|
for (ds = &st->downsized; ds->name; ds = ds->next) {
|
|
if (STREQ(name, ds->name))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|