[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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.