[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH RFC 2/5] plat/common: Introduce read_allsymbol
After kallsyms translates all symbol name into assembly code file, comipler and linker will link this assembly file to unikraft image. read_allsymbol will read all symbol names from the obj file. Signed-off-by: Jia He <justin.he@xxxxxxx> --- plat/common/include/read_allsymbol.h | 70 ++++++ plat/common/read_allsymbol.c | 332 +++++++++++++++++++++++++++ plat/kvm/Makefile.uk | 8 + 3 files changed, 410 insertions(+) create mode 100644 plat/common/include/read_allsymbol.h create mode 100644 plat/common/read_allsymbol.c diff --git a/plat/common/include/read_allsymbol.h b/plat/common/include/read_allsymbol.h new file mode 100644 index 0000000..6b89417 --- /dev/null +++ b/plat/common/include/read_allsymbol.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Authors: Jia He <justin.he@xxxxxxx> + * + * Copyright (c) 2019, Arm Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +#ifndef _LINUX_UKALLSYMS_H +#define _LINUX_UKALLSYMS_H + +#include <errno.h> +#include <sections.h> + +#define KSYM_NAME_LEN 128 +#define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + (KSYM_NAME_LEN - 1) + \ + 2*(64*3/10) + 1) + +static inline int is_ksym_addr(unsigned long addr) +{ + if (addr >= (unsigned long)__TEXT && addr <= (unsigned long)__END) + return 1; + return 0; +} + +#ifdef CONFIG_DEBUG_BACK_TRACE + +/* Look up a kernel symbol and return it in a text buffer. */ +extern int sprint_symbol(char *buffer, unsigned long address); +extern void uk_dump_backtrace(void); + +#else /* !CONFIG_DEBUG_BACK_TRACE */ +static inline int sprint_symbol(char *buffer, unsigned long addr) +{ + *buffer = '\0'; + return 0; +} + +void uk_dump_backtrace(void) +{ +} +#endif /*CONFIG_DEBUG_BACK_TRACE */ + +#endif /*_LINUX_UKALLSYMS_H*/ + diff --git a/plat/common/read_allsymbol.c b/plat/common/read_allsymbol.c new file mode 100644 index 0000000..6afc785 --- /dev/null +++ b/plat/common/read_allsymbol.c @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * read_allsyms.c: printing of symbolic oopses and stack traces. + * Authors: Jia He <justin.he@xxxxxxx> + * + * Copyright (c) 2019, Arm Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* + * This is simplified and rewritten from kernel/kallsyms.c + * CommitID: 2a1a3fa0 + */ +#include <inttypes.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> + +#include <uk/essentials.h> +#include <uk/print.h> +#include <uk/assert.h> + +#include "read_allsymbol.h" + +/* + * These will be re-linked against their real values + * during the second link stage. + */ +extern const unsigned long ukallsyms_addresses[] __weak; +extern const __u8 ukallsyms_names[] __weak; + +extern const unsigned long ukallsyms_num_syms __weak; + +extern const __u8 ukallsyms_token_table[] __weak; +extern const __u16 ukallsyms_token_index[] __weak; +extern const unsigned long ukallsyms_markers[] __weak; + +struct stackframe { + unsigned long fp; + unsigned long pc; +}; + +/* + * Expand a compressed symbol data into the resulting uncompressed string, + * if uncompressed string is too long (>= maxlen), it will be truncated, + * given the offset to where the symbol is in the compressed stream. + * e.g. + * 0x03, 0xbe, 0xbc, 0x71 + * len is 0x03, then ukallsyms_token_index[0xbe] is the index of this token + * in ukallsyms_token_table[] + */ +static unsigned int expand_symbol(unsigned int off, char *result, size_t maxlen) +{ + int len, skipped = 0; + const __u8 *token, *data; + + /* Get the compressed symbol length from the first symbol byte. */ + data = &ukallsyms_names[off]; + len = *data; + data++; + + /* + * Update the offset to return the offset for the next symbol on + * the compressed stream. + */ + off += len + 1; + + /* + * For every byte on the compressed symbol data, copy the table + * entry for that byte. + */ + while (len) { + token = &ukallsyms_token_table[ukallsyms_token_index[*data]]; + data++; + len--; + + /* skip the first char which is the length */ + while (*token) { + if (skipped) { + if (maxlen <= 1) + goto tail; + *result = *token; + result++; + maxlen--; + } else + skipped = 1; + token++; + } + } + +tail: + /* Truncate if it is longer than maxlen */ + if (maxlen) + *result = '\0'; + + /* Return to offset to the next symbol. */ + return off; +} + +/* + * Find the offset on the compressed stream given and index in the + * ukallsyms array. + */ +static unsigned int get_symbol_offset(unsigned long pos) +{ + const __u8 *name; + unsigned int i; + + /* + * Use the closest marker we have. We have markers every 256 positions, + * so that should be close enough. + */ + name = &ukallsyms_names[ukallsyms_markers[pos >> 8]]; + + /* + * Sequentially scan all the symbols up to the point we're searching + * for. Every symbol is stored in a [<len>][<len> bytes of data] format, + * so we just need to add the len to the current pointer for every + * symbol we wish to skip. + */ + for (i = 0; i < (unsigned int)(pos & 0xFF); i++) + name = name + (*name) + 1; + + return name - ukallsyms_names; +} + +static unsigned long get_symbol_pos(unsigned long addr, + unsigned long *symbol_size, + unsigned long *offset) +{ + unsigned long symbol_start = 0, symbol_end = 0; + unsigned long i, low, high, mid = 0; + + /* Do a binary search on the sorted ukallsyms_addresses array. */ + low = 0; + high = ukallsyms_num_syms; + + while (high - low > 1) { + mid = low + (high - low) / 2; + if (ukallsyms_addresses[mid] <= addr) + low = mid; + else + high = mid; + } + + /* + * Search for the first aliased symbol. Aliased + * symbols are symbols with the same address. + */ + while (low && ukallsyms_addresses[low-1] == ukallsyms_addresses[low]) + --low; + + symbol_start = ukallsyms_addresses[low]; + + /* Search for next non-aliased symbol. */ + for (i = low + 1; i < ukallsyms_num_syms; i++) { + if (ukallsyms_addresses[i] > symbol_start) { + symbol_end = ukallsyms_addresses[i]; + break; + } + } + + /* If we found no next symbol, we use the end of the section. */ + if (!symbol_end) { + symbol_end = (unsigned long)__END; + } + + if (symbol_size) + *symbol_size = symbol_end - symbol_start; + if (offset) + *offset = addr - symbol_start; + + return low; +} + +/* + * Lookup an address + * - We guarantee that the returned name is valid until we reschedule even if. + * It resides in a module. + */ +static const char *allsyms_lookup(unsigned long addr, + unsigned long *symbol_size, + unsigned long *offset, + char *namebuf) +{ + namebuf[KSYM_NAME_LEN - 1] = 0; + namebuf[0] = 0; + + if (is_ksym_addr(addr)) { + unsigned long pos; + + pos = get_symbol_pos(addr, symbol_size, offset); + /* Grab name */ + expand_symbol(get_symbol_offset(pos), namebuf, KSYM_NAME_LEN); + + return namebuf; + } + + return 0; +} + + +/** + * sprint_symbol - Look up a kernel symbol and return it in a text buffer + * @buffer: buffer to be stored + * @address: address to lookup + * + * This function looks up a kernel symbol with @address and stores its name, + * offset, size and module name to @buffer if possible. If no symbol was found, + * just saves its @address as is. + * + * This function returns the number of bytes stored in @buffer. + */ +int sprint_symbol(char *buffer, unsigned long address) +{ + const char *name; + unsigned long offset, size; + int len; + + name = allsyms_lookup(address, &size, &offset, buffer); + if (!name) + return sprintf(buffer, "0x%lx", address); + + if (name != buffer) + strcpy(buffer, name); + len = strlen(buffer); + + len += sprintf(buffer + len, "+%#lx/%#lx", offset, size); + + return len; +} + +/* + * AArch64 PCS assigns the frame pointer to x29. + * + * A simple function prologue looks like this: + * sub sp, sp, #0x10 + * stp x29, x30, [sp] + * mov x29, sp + * + * A simple function epilogue looks like this: + * mov sp, x29 + * ldp x29, x30, [sp] + * add sp, sp, #0x10 + */ +static int unwind_frame(struct stackframe *frame) +{ + unsigned long fp = frame->fp; + + if (fp & 0xf) + return -1; + + frame->fp = (*(unsigned long *)(fp)); + frame->pc = (*(unsigned long *)(fp + 8)); + + /* + * Frames created upon entry from EL0 have NULL FP and PC values, so + * don't bother reporting these. Frames created by __noreturn functions + * might have a valid FP even if PC is bogus, so only terminate where + * both are NULL. + */ + if (!frame->fp && !frame->pc) + return -1; + + if (!is_ksym_addr(frame->pc)) + return -1; + return 0; +} + +static void dump_backtrace_entry(unsigned long where) +{ + char buffer[KSYM_NAME_LEN]; + + sprint_symbol(buffer, where); + printf("\t[0x%lx] %s\n", where, buffer); +} + +int ukallsyms_on_each_symbol(int (*fn)(void *, const char *, unsigned long), + void *data) +{ + char namebuf[KSYM_NAME_LEN]; + unsigned long i; + unsigned int off; + int ret; + + for (i = 0, off = 0; i < ukallsyms_num_syms; i++) { + off = expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); + ret = fn(data, namebuf, ukallsyms_addresses[i]); + if (ret != 0) + return ret; + } + return 0; +} + +void uk_dump_backtrace(void) +{ + struct stackframe frame; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.pc = (unsigned long)uk_dump_backtrace; + + printf("Call trace:\n"); + do { + dump_backtrace_entry(frame.pc); + } while (!unwind_frame(&frame)); +} + diff --git a/plat/kvm/Makefile.uk b/plat/kvm/Makefile.uk index 5d87352..4cdb759 100644 --- a/plat/kvm/Makefile.uk +++ b/plat/kvm/Makefile.uk @@ -100,6 +100,14 @@ LIBKVMPLAT_SRCS-y += $(UK_PLAT_COMMON_BASE)/lcpu.c|common LIBKVMPLAT_SRCS-y += $(UK_PLAT_COMMON_BASE)/memory.c|common LIBKVMPLAT_SRCS-y += $(UK_PLAT_KVM_DEF_LDS) +## +## Support dumping backtrace +## +ifeq ($(CONFIG_DEBUG_BACK_TRACE),y) +LIBKVMPLAT_SRCS-y += $(UK_PLAT_COMMON_BASE)/read_allsymbol.c|common +endif + + ## ## PCI library definitions ## -- 2.17.1 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |