The principle of dump_stack core (2) - symbol

surroundings

Linux-4.14
Aarch64
 

text

Call print_symbol in previous analysis ( "PC is at% s \ n", instruction_pointer (regs)) output current PC address when the contents of the output is: PC is at demo_init + 0xc / 0x1000 [demo]
Following analysis of this function print_symbol.
 1 static __printf(1, 2)
 2 void __check_printsym_format(const char *fmt, ...)
 3 {
 4 }
 5 
 6 static inline void print_symbol(const char *fmt, unsigned long addr)
 7 {
 8     __check_printsym_format(fmt, "");
 9     __print_symbol(fmt, (unsigned long)
10                __builtin_extract_return_addr((void *)addr));
11 }
 
Line 8, the format check
Line 9, __ builtin_extract_return_addr ((void *) addr) returns the actual addr, return here or addr, this function can be explained with reference to GCC documentation:
The following analysis __print_symbol :
1 /* Look up a kernel symbol and print it to the kernel messages. */
2 void __print_symbol(const char *fmt, unsigned long address)
3 {
4     char buffer[KSYM_SYMBOL_LEN];
5 
6     sprint_symbol(buffer, address);
7 
8     printk(fmt, buffer);
9 }
 
Line 6 is the core, this function converts a complete address to the corresponding kernel symbol string and a string is stored in buffer
 
The following analysis sprint_symbol :
 1 /**
 2  * sprint_symbol - Look up a kernel symbol and return it in a text buffer
 3  * @buffer: buffer to be stored
 4  * @address: address to lookup
 5  *
 6  * This function looks up a kernel symbol with @address and stores its name,
 7  * offset, size and module name to @buffer if possible. If no symbol was found,
 8  * just saves its @address as is.
 9  *
10  * This function returns the number of bytes stored in @buffer.
11  */
12 int sprint_symbol(char *buffer, unsigned long address)
13 {
14     return __sprint_symbol(buffer, address, 0, 1);
15 }
The annotation function is used to find an address of the kernel symbol address, and then find the symbolic name, offset, size, and the module name is stored in the buffer, if it is not found, but the address stored by the format string into the buffer.
Explain here: demo_init + 0xc / 0x1000 [Demo]
Symbol name: demo_init
Offset: 0xc
Size: 0x1000
Module name: Demo
Above line means: in the incoming address demo_init function, the offset from the start address of demo_init 0xC, demo_init function of code space occupied is 0x1000. Where is the kernel module demo
 
The following analysis __sprint_symbol :
 1 /* Look up a kernel symbol and return it in a text buffer. */
 2 static int __sprint_symbol(char *buffer, unsigned long address,
 3                int symbol_offset, int add_offset)
 4 {
 5     char *modname;
 6     const char *name;
 7     unsigned long offset, size;
 8     int len;
 9 
10     address += symbol_offset;
11     name = kallsyms_lookup(address, &size, &offset, &modname, buffer);
12     if (!name)
13         return sprintf(buffer, "0x%lx", address - symbol_offset);
14 
15     if (name != buffer)
16         strcpy(buffer, name);
17     len = strlen(buffer);
18     offset -= symbol_offset;
19 
20     if (add_offset)
21         len += sprintf(buffer + len, "+%#lx/%#lx", offset, size);
22 
23     if (modname)
24         len += sprintf(buffer + len, " [%s]", modname);
25 
26     return len;
27 }

kallsyms_lookup line 11 above is to get size, offset, modname based on address

 
kallsyms_lookup
 1 /*
 2  * Lookup an address
 3  * - modname is set to NULL if it's in the kernel.
 4  * - We guarantee that the returned name is valid until we reschedule even if.
 5  *   It resides in a module.
 6  * - We also guarantee that modname will be valid until rescheduled.
 7  */
 8 const char *kallsyms_lookup(unsigned long addr,
 9                 unsigned long *symbolsize,
10                 unsigned long *offset,
11                 char **modname, char *namebuf)
12 {
13     const char *ret;
14 
15     namebuf[KSYM_NAME_LEN - 1] = 0;
16     namebuf[0] = 0;
17 
18     if (is_ksym_addr(addr)) {
19         unsigned long pos;
20 
21         pos = get_symbol_pos(addr, symbolsize, offset);
22         /* Grab name */
23         kallsyms_expand_symbol(get_symbol_offset(pos),
24                        namebuf, KSYM_NAME_LEN);
25         if (modname)
26             *modname = NULL;
27 
28         ret = namebuf;
29         goto found;
30     }
31 
32     /* See if it's in a module or a BPF JITed image. */
33     ret = module_address_lookup(addr, symbolsize, offset,
34                     modname, namebuf);
35     if (!ret)
36         ret = bpf_address_lookup(addr, symbolsize,
37                      offset, modname, namebuf);
38 
39 found:
40     cleanup_symbol_name(namebuf);
41     return ret;
42 }
From the top three places will go look for symbols, first kernel, if not found, look for the kernel module, if you still do not find the words, and finally find the bpf in.
 
The following analysis of the line 18 to 30, i.e. Find the kernel, other later analysis.
Line 18 determines whether addr in the kernel code segments
Line 21, to analyze .tmp_kallsyms2.S generated when get_symbol_pos requires kernel compiled code, which is stored symbol information.
Roughly explain this file:
This file is dynamically generated, the tools are scripts / kallsyms.c, explain the following variables in the role of .tmp_kallsyms2.S:
 
 
kallsyms_offsets array is stored in the offset address of each symbol from _text for about System.map:
 
 
You can see the symbolic address System.map minus address _text, that is, the value kallsyms_offsets array.
 
 
kallsyms_relative_base is stored in the base address of the symbol, this value plus the actual address kallsyms_offsets array offset is the symbol
kallsyms_num_syms number is stored in the kernel symbol
kallsyms_names中存放的是每个符号的名字,每一行对应一个,不过这里为了压缩字符串,第一列表示后面的字节数,第二列开始表示的都是索引,索引的是kallsyms_token_index数组中的元素,而 kallsyms_token_index数组中存放的也是索引,它索引的是kallsyms_token_table
 
 
kallsyms_token_index:
 
 
kallsyms_token_table:
 
 
在遍历 kallsyms_names时为了加快索引速度,又引入了kallsyms_markers数组,这个数组每一个成员都是 kallsyms_names中每256行的首地址,所以将来在根据address获得内核符号的索引下标后,将这个索引除以256,然后再在这个256行中找到对应的那行就快多了。
 
下面分析get_symbol_pos :
 1 static unsigned long get_symbol_pos(unsigned long addr,
 2                     unsigned long *symbolsize,
 3                     unsigned long *offset)
 4 {
 5     unsigned long symbol_start = 0, symbol_end = 0;
 6     unsigned long i, low, high, mid;
 7 
 8     /* This kernel should never had been booted. */
 9     if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
10         BUG_ON(!kallsyms_addresses);
11     else
12         BUG_ON(!kallsyms_offsets);
13 
14     /* Do a binary search on the sorted kallsyms_addresses array. */
15     low = 0;
16     high = kallsyms_num_syms;
17 
18     while (high - low > 1) {
19         mid = low + (high - low) / 2;
20         if (kallsyms_sym_address(mid) <= addr)
21             low = mid;
22         else
23             high = mid;
24     }
25 
26     /*
27      * Search for the first aliased symbol. Aliased
28      * symbols are symbols with the same address.
29      */
30     while (low && kallsyms_sym_address(low-1) == kallsyms_sym_address(low))
31         --low;
32 
33     symbol_start = kallsyms_sym_address(low);
34 
35     /* Search for next non-aliased symbol. */
36     for (i = low + 1; i < kallsyms_num_syms; i++) {
37         if (kallsyms_sym_address(i) > symbol_start) {
38             symbol_end = kallsyms_sym_address(i);
39             break;
40         }
41     }
42 
43     /* If we found no next symbol, we use the end of the section. */
44     if (!symbol_end) {
45         if (is_kernel_inittext(addr))
46             symbol_end = (unsigned long)_einittext;
47         else if (IS_ENABLED(CONFIG_KALLSYMS_ALL))
48             symbol_end = (unsigned long)_end;
49         else
50             symbol_end = (unsigned long)_etext;
51     }
52 
53     if (symbolsize)
54         *symbolsize = symbol_end - symbol_start;
55     if (offset)
56         *offset = addr - symbol_start;
57 
58     return low;
59 }
第18~24,根据addr查找 kallsyms_offsets,获取addr在哪两个符号之间。这里用到了二分法的查找方式,最后addr就位于索引为low和high的两个符号之间,其实就是位于索引为low的函数内部
第30, kallsyms_offsets中可以看到有很多符号的地址是相同的,这行用于获取相同address的符号中的第一个对应的索引,即low
第33,获取索引为low的符号的地址symbol_start
第36~41,获取紧接着比 symbol_start大的一个符号地址,symbol_end
第54行,获取地址为 symbol_start内核函数的占用的空间的大小
第56行,获取address相对于 symbol_start的偏移量
第58行,返回address所在的内核函数的首地址对应的索引号
 
接着分析kallsyms_lookup:
第21行,获取了address所在的内核函数的首地址对应的索引号
第23行,get_symbol_offset获取pos对应的内核符号字符串的地址相对于kallsyms_names的偏移量,可以结合之前对 .tmp_kallsyms2.S的分析理解
 1 /*
 2  * Find the offset on the compressed stream given and index in the
 3  * kallsyms array.
 4  */
 5 static unsigned int get_symbol_offset(unsigned long pos)
 6 {
 7     const u8 *name;
 8     int i;
 9 
10     /*
11      * Use the closest marker we have. We have markers every 256 positions,
12      * so that should be close enough.
13      */
14     name = &kallsyms_names[kallsyms_markers[pos >> 8]];
15 
16     /*
17      * Sequentially scan all the symbols up to the point we're searching
18      * for. Every symbol is stored in a [<len>][<len> bytes of data] format,
19      * so we just need to add the len to the current pointer for every
20      * symbol we wish to skip.
21      */
22     for (i = 0; i < (pos & 0xFF); i++)
23         name = name + (*name) + 1;
24 
25     return name - kallsyms_names;
26 }
kallsyms_expand_symbol:
 1 /*
 2  * Expand a compressed symbol data into the resulting uncompressed string,
 3  * if uncompressed string is too long (>= maxlen), it will be truncated,
 4  * given the offset to where the symbol is in the compressed stream.
 5  */
 6 static unsigned int kallsyms_expand_symbol(unsigned int off,
 7                        char *result, size_t maxlen)
 8 {
 9     int len, skipped_first = 0;
10     const u8 *tptr, *data;
11 
12     /* Get the compressed symbol length from the first symbol byte. */
13     data = &kallsyms_names[off];
14     len = *data;
15     data++;
16 
17     /*
18      * Update the offset to return the offset for the next symbol on
19      * the compressed stream.
20      */
21     off += len + 1;
22 
23     /*
24      * For every byte on the compressed symbol data, copy the table
25      * entry for that byte.
26      */
27     while (len) {
28         tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
29         data++;
30         len--;
31 
32         while (*tptr) {
33             if (skipped_first) {
34                 if (maxlen <= 1)
35                     goto tail;
36                 *result = *tptr;
37                 result++;
38                 maxlen--;
39             } else
40                 skipped_first = 1;
41             tptr++;
42         }
43     }
44 
45 tail:
46     if (maxlen)
47         *result = '\0';
48 
49     /* Return to offset to the next symbol. */
50     return off;
51 }
 
 
最后会将转换得到的内核符号的字符串名字拷贝到namebuf中。
 
完。

Guess you like

Origin www.cnblogs.com/pengdonglin137/p/11109648.html