aboutsummaryrefslogtreecommitdiff
path: root/libsframe/testsuite/libsframe.stacktrace/libsframest/sframe-state.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsframe/testsuite/libsframe.stacktrace/libsframest/sframe-state.c')
-rw-r--r--libsframe/testsuite/libsframe.stacktrace/libsframest/sframe-state.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/libsframe/testsuite/libsframe.stacktrace/libsframest/sframe-state.c b/libsframe/testsuite/libsframe.stacktrace/libsframest/sframe-state.c
new file mode 100644
index 0000000..bb78c1a
--- /dev/null
+++ b/libsframe/testsuite/libsframe.stacktrace/libsframest/sframe-state.c
@@ -0,0 +1,380 @@
+/* sframe-state.c - The SFrame state for stacktracing.
+
+ Copyright (C) 2023 Free Software Foundation, Inc.
+
+ This file is part of libsframest.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include <link.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ucontext.h>
+#include <stdarg.h>
+#include "ansidecl.h"
+#include "sframe-api.h"
+#include "sframe-stacktrace-api.h"
+#include "sframe-stacktrace-regs.h"
+#include "sframe-state.h"
+
+#define _sf_printflike_(string_index, first_to_check) ATTRIBUTE_PRINTF (1, 2)
+
+static bool _sframe_unwind_debug; /* Control for printing out debug info. */
+static const int no_of_entries = NUM_OF_DSOS;
+
+void
+sframe_unwind_init_debug (void)
+{
+ static int inited;
+
+ if (!inited)
+ {
+ _sframe_unwind_debug = getenv ("SFRAME_UNWIND_DEBUG") != NULL;
+ inited = 1;
+ }
+}
+
+_sf_printflike_ (1, 2)
+static void
+debug_printf (const char *format, ...)
+{
+ if (_sframe_unwind_debug == true)
+ {
+ va_list args;
+
+ va_start (args, format);
+ __builtin_vprintf (format, args);
+ va_end (args);
+ }
+}
+
+#if 0
+/* sframe_bt_set_errno - Store the specified error code ERROR into ERRP if
+ it is non-NULL. */
+
+static void
+sframe_bt_set_errno (int *errp, int error)
+{
+ if (errp != NULL)
+ *errp = error;
+}
+
+#endif
+
+/* sframe_add_dso - Add .sframe info in D_DATA, which is associated with
+ a dynamic shared object, to D_LIST. */
+
+static int
+sframe_add_dso (struct sframe_stinfo_list *d_list,
+ struct sframe_stinfo d_data)
+{
+ int err = 0;
+
+ if (d_list->alloced == 0)
+ {
+ d_list->entry = malloc (no_of_entries * sizeof (struct sframe_stinfo));
+ if (d_list->entry == NULL)
+ return sframe_bt_ret_set_errno (&err, SFRAME_BT_ERR_MALLOC);
+
+ memset (d_list->entry, 0,
+ no_of_entries * sizeof (struct sframe_stinfo));
+ d_list->alloced = no_of_entries;
+ }
+ else if (d_list->used == d_list->alloced)
+ {
+ d_list->entry = realloc (d_list->entry,
+ ((d_list->alloced + no_of_entries)
+ * sizeof (struct sframe_stinfo)));
+ if (d_list->entry == NULL)
+ return sframe_bt_ret_set_errno (&err, SFRAME_BT_ERR_REALLOC);
+
+ memset (&d_list->entry[d_list->alloced], 0,
+ no_of_entries * sizeof (struct sframe_stinfo));
+ d_list->alloced += no_of_entries;
+ }
+
+ sframe_bt_ret_set_errno (&err, SFRAME_BT_OK);
+ d_list->entry[d_list->used++] = d_data;
+
+ return SFRAME_BT_OK;
+}
+
+/* sframe_free_cfi - Free up space allocated for .sframe info for CF. */
+
+void
+sframe_free_cfi (struct sframe_state *sf)
+{
+ struct sframe_stinfo_list *d_list;
+ int i;
+
+ if (sf == NULL)
+ return;
+
+ // free (sf->sui_ctx.sfdd_data);
+ sframe_decoder_free (&sf->sui_ctx.sfdd_sframe_ctx);
+ close (sf->sui_fd);
+
+ d_list = &sf-> sui_dsos;
+ if (d_list == NULL)
+ return;
+
+ for (i = 0; i < d_list->used; ++i)
+ {
+ // free (d_list->entry[i].sfdd_data);
+ sframe_decoder_free (&d_list->entry[i].sfdd_sframe_ctx);
+ }
+
+ free (d_list->entry);
+}
+
+/* sframe_find_context - Find the decode data that contains ADDR from CF.
+ Return the pointer to the decode data or NULL. */
+
+struct sframe_stinfo *
+sframe_find_context (struct sframe_state *sf, uint64_t addr)
+{
+ struct sframe_stinfo_list *d_list;
+ struct sframe_stinfo sdec_data;
+ int i;
+
+ if (sf == NULL)
+ return NULL;
+
+ if (sf->sui_ctx.sfdd_text_vma < addr
+ && sf->sui_ctx.sfdd_text_vma + sf->sui_ctx.sfdd_text_size > addr)
+ return &sf->sui_ctx;
+
+ d_list = &sf->sui_dsos;
+ for (i = 0; i < sf->sui_dsos.used; ++i)
+ {
+ sdec_data = d_list->entry[i];
+ if ((sdec_data.sfdd_text_vma <= addr)
+ && (sdec_data.sfdd_text_vma + sdec_data.sfdd_text_size >= addr))
+ return &d_list->entry[i];
+ }
+
+ return NULL;
+}
+
+/* sframe_load_ctx - Call decoder to create and set up the SFrame info for
+ either the main module or one of the DSOs from CF, based on the input
+ RADDR argument. Return the newly created decode context or NULL. */
+
+sframe_decoder_ctx *
+sframe_load_ctx (struct sframe_state *sf, uint64_t raddr)
+{
+ sframe_decoder_ctx *nctx;
+ struct sframe_stinfo *cdp;
+
+ if (sf == NULL)
+ return NULL;
+
+ cdp = sframe_find_context (sf, raddr);
+ if (cdp == NULL)
+ return NULL;
+
+ if (cdp->sfdd_sframe_ctx == NULL)
+ {
+ int err;
+ nctx = sframe_decode (cdp->sfdd_data, cdp->sfdd_data_size, &err);
+ if (nctx == NULL)
+ return NULL;
+ cdp->sfdd_sframe_ctx = nctx;
+ return nctx;
+ }
+
+ return NULL;
+}
+
+/* sframe_update_ctx - Check if need to do a decode context switch, based on
+ the input RADDR argument, from CF. A new decode context will be created
+ and set up if it isn't already done so. Return the new decode context in
+ CTX and vma in CFI_VMA. */
+
+void
+sframe_update_ctx (struct sframe_state *sf, uint64_t raddr,
+ sframe_decoder_ctx **ctx, uint64_t *cfi_vma)
+{
+ sframe_decoder_ctx *nctx;
+ struct sframe_stinfo *cdp;
+
+ cdp = sframe_find_context (sf, raddr);
+ if (cdp != NULL)
+ {
+ if (cdp->sfdd_sframe_ctx == NULL)
+ {
+ int err;
+ nctx = sframe_decode (cdp->sfdd_data, cdp->sfdd_data_size, &err);
+ if (nctx == NULL)
+ {
+ *ctx = NULL;
+ return;
+ }
+ cdp->sfdd_sframe_ctx = nctx;
+ }
+ *ctx = cdp->sfdd_sframe_ctx;
+ *cfi_vma = cdp->sfdd_sframe_vma;
+ }
+}
+
+/* sframe_fd_open - Open /proc image associated with the process id and return
+ the file descriptor. */
+
+static int
+sframe_fd_open (int *errp)
+{
+ int fd;
+
+ if ((fd = open ("/proc/self/mem", O_CLOEXEC)) == -1)
+ {
+ sframe_bt_ret_set_errno (errp, SFRAME_BT_ERR_OPEN);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* The callback from dl_iterate_phdr with header info in INFO.
+ Return SFrame info for either the main module or a DSO in DATA. */
+
+int
+sframe_callback (struct dl_phdr_info *info,
+ size_t size ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct sframe_state *sf = (struct sframe_state *) data;
+ int p_type, i, fd, sframe_err;
+ ssize_t len;
+ uint64_t text_vma = 0;
+ int text_size = 0;
+
+ if (data == NULL || info == NULL)
+ return 1;
+
+ debug_printf ("-- name: %s %14p\n", info->dlpi_name, (void *)info->dlpi_addr);
+
+ for (i = 0; i < info->dlpi_phnum; i++)
+ {
+ debug_printf (" %2d: [%" PRIu64 "; memsz %" PRIu64 "] flags: 0x%x; \n", i,
+ (uint64_t) info->dlpi_phdr[i].p_vaddr,
+ (uint64_t) info->dlpi_phdr[i].p_memsz,
+ info->dlpi_phdr[i].p_flags);
+
+ p_type = info->dlpi_phdr[i].p_type;
+ if (p_type == PT_LOAD && info->dlpi_phdr[i].p_flags & PF_X)
+ {
+ text_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+ text_size = info->dlpi_phdr[i].p_memsz;
+ continue;
+ }
+ if (p_type != PT_SFRAME)
+ continue;
+
+ if (info->dlpi_name[0] == '\0') /* the main module. */
+ {
+ fd = sframe_fd_open (&sframe_err);
+ if (fd == -1)
+ return 1;
+#if 0
+ if (lseek (fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+ SEEK_SET) == -1)
+ {
+ sframe_bt_ret_set_errno (&sframe_err, SFRAME_BT_ERR_LSEEK);
+ return 1;
+ }
+#endif
+
+ // sf->sui_ctx.sfdd_data = malloc (info->dlpi_phdr[i].p_memsz);
+ sf->sui_ctx.sfdd_data = (char *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+#if 0
+ if (sf->sui_ctx.sfdd_data == NULL)
+ {
+ sframe_bt_ret_set_errno (&sframe_err, SFRAME_BT_ERR_MALLOC);
+ return 1;
+ }
+
+ len = read (fd, sf->sui_ctx.sfdd_data, info->dlpi_phdr[i].p_memsz);
+ if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+ {
+ sframe_bt_ret_set_errno (&sframe_err, SFRAME_BT_ERR_READ);
+ return 1;
+ }
+#endif
+ len = info->dlpi_phdr[i].p_memsz;
+
+ assert (text_vma);
+ sf->sui_ctx.sfdd_data_size = len;
+ sf->sui_ctx.sfdd_sframe_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+ sf->sui_fd = fd;
+ sf->sui_ctx.sfdd_text_vma = text_vma;
+ sf->sui_ctx.sfdd_text_size = text_size;
+ text_vma = 0;
+ return 0;
+ }
+ else
+ { /* a dynamic shared object. */
+ struct sframe_stinfo dt;
+ memset (&dt, 0, sizeof (struct sframe_stinfo));
+ assert (sf->sui_fd);
+#if 0
+ if (lseek (sf->sui_fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+ SEEK_SET) == -1)
+ {
+ sframe_bt_ret_set_errno (&sframe_err, SFRAME_BT_ERR_LSEEK);
+ return 1;
+ }
+#endif
+
+ // dt.sfdd_data = malloc (info->dlpi_phdr[i].p_memsz);
+ dt.sfdd_data = (char *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+#if 0
+ if (dt.sfdd_data == NULL)
+ {
+ sframe_bt_ret_set_errno (&sframe_err, SFRAME_BT_ERR_MALLOC);
+ return 1;
+ }
+
+ len = read (sf->sui_fd, dt.sfdd_data, info->dlpi_phdr[i].p_memsz);
+ if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+ {
+ sframe_bt_ret_set_errno (&sframe_err, SFRAME_BT_ERR_READ);
+ return 1;
+ }
+#endif
+ len = info->dlpi_phdr[i].p_memsz;
+ assert (text_vma);
+ dt.sfdd_data_size = len;
+ dt.sfdd_sframe_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+ dt.sfdd_text_vma = text_vma;
+ dt.sfdd_text_size = text_size;
+ text_vma = 0;
+
+ sframe_err = sframe_add_dso (&sf->sui_dsos, dt);
+ // FIXME TODO
+ if (sframe_err != SFRAME_BT_OK)
+ return 1;
+ return 0;
+ }
+ }
+
+ return 0;
+}