diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index a03e26f0..7f9e4197 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -30,6 +30,8 @@ libsysprof_analyze_private_sources = [ 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', 'sysprof-document-symbols.c', + 'sysprof-elf.c', + 'sysprof-elf-loader.c', 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', diff --git a/src/libsysprof-analyze/sysprof-elf-loader-private.h b/src/libsysprof-analyze/sysprof-elf-loader-private.h new file mode 100644 index 00000000..bfc0d74b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-loader-private.h @@ -0,0 +1,49 @@ +/* + * sysprof-elf-loader-private.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-elf-private.h" +#include "sysprof-mount-namespace-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ELF_LOADER (sysprof_elf_loader_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, SYSPROF, ELF_LOADER, GObject) + +SysprofElfLoader *sysprof_elf_loader_new (void); +const char * const *sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self); +void sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self, + const char * const *debug_dirs); +const char * const *sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self); +void sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self, + const char * const *debug_dirs); +SysprofElf *sysprof_elf_loader_load (SysprofElfLoader *self, + SysprofMountNamespace *mount_namespace, + const char *file, + const char *build_id, + GError **error); + + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c new file mode 100644 index 00000000..2a75b918 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -0,0 +1,258 @@ +/* sysprof-elf-loader.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-elf-loader-private.h" +#include "sysprof-strings-private.h" + +#define DEFAULT_DEBUG_DIRS SYSPROF_STRV_INIT("/usr/lib/debug") + +struct _SysprofElfLoader +{ + GObject parent_instance; + char **debug_dirs; + char **external_debug_dirs; +}; + +enum { + PROP_0, + PROP_DEBUG_DIRS, + PROP_EXTERNAL_DEBUG_DIRS, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_elf_loader_finalize (GObject *object) +{ + SysprofElfLoader *self = (SysprofElfLoader *)object; + + g_clear_pointer (&self->debug_dirs, g_strfreev); + g_clear_pointer (&self->external_debug_dirs, g_strfreev); + + G_OBJECT_CLASS (sysprof_elf_loader_parent_class)->finalize (object); +} + +static void +sysprof_elf_loader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofElfLoader *self = SYSPROF_ELF_LOADER (object); + + switch (prop_id) + { + case PROP_DEBUG_DIRS: + g_value_set_boxed (value, sysprof_elf_loader_get_debug_dirs (self)); + break; + + case PROP_EXTERNAL_DEBUG_DIRS: + g_value_set_boxed (value, sysprof_elf_loader_get_external_debug_dirs (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_loader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofElfLoader *self = SYSPROF_ELF_LOADER (object); + + switch (prop_id) + { + case PROP_DEBUG_DIRS: + sysprof_elf_loader_set_debug_dirs (self, g_value_get_boxed (value)); + break; + + case PROP_EXTERNAL_DEBUG_DIRS: + sysprof_elf_loader_set_external_debug_dirs (self, g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_loader_class_init (SysprofElfLoaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_elf_loader_finalize; + object_class->get_property = sysprof_elf_loader_get_property; + object_class->set_property = sysprof_elf_loader_set_property; + + properties [PROP_DEBUG_DIRS] = + g_param_spec_boxed ("debug-dirs", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_EXTERNAL_DEBUG_DIRS] = + g_param_spec_boxed ("external-debug-dirs", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_elf_loader_init (SysprofElfLoader *self) +{ + self->debug_dirs = g_strdupv ((char **)DEFAULT_DEBUG_DIRS); +} + +/** + * sysprof_elf_loader_get_debug_dirs: + * @self: a #SysprofElfLoader + * + * Gets the #SysprofElfLoader:debug-dirs property. + * + * See sysprof_elf_loader_set_debug_dirs() for information on how + * these directories are used. + * + * Returns: (nullable): an array of debug directories, or %NULL + */ +const char * const * +sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + + return (const char * const *)self->debug_dirs; +} + +/** + * sysprof_elf_loader_set_debug_dirs: + * @self: a #SysprofElfLoader + * @debug_dirs: the new debug directories to use + * + * Sets the #SysprofElfLoader:debug-dirs prpoerty. + * + * If @debug_dirs is %NULL, the default debug directories will + * be used. + * + * These directories will be used to resolve debug symbols within + * the mount namespace of the process whose symbols are being + * resolved. + * + * To set a global directory that may contain debug symbols, use + * sysprof_elf_loader_set_external_debug_dirs() which can be searched + * regardless of what mount namespace the resolving process was in. + */ +void +sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self, + const char * const *debug_dirs) +{ + g_return_if_fail (SYSPROF_IS_ELF_LOADER (self)); + g_return_if_fail (self->debug_dirs != NULL); + + if (sysprof_set_strv (&self->debug_dirs, debug_dirs)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_DIRS]); +} + +/** + * sysprof_elf_loader_get_external_debug_dirs: + * @self: a #SysprofElfLoader + * + * Gets the #SysprofElfLoader:external-debug-dirs property. + * + * See sysprof_elf_loader_set_external_debug_dirs() for how this + * property is used to locate ELF files. + * + * Returns: (nullable): an array of external debug directories, or %NULL + */ +const char * const * +sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + + return (const char * const *)self->external_debug_dirs; +} + +/** + * sysprof_elf_loader_set_external_debug_dirs: + * @self: a #SysprofElfLoader + * @external_debug_dirs: (nullable): array of debug directories to resolve + * `.gnu_debuglink` references in ELF files. + * + * Sets the #SysprofElfLoader:external-debug-dirs property. + * + * This is used to resolve `.gnu_debuglink` files across any process that is + * loading ELF files with this #SysprofElfLoader. That allows for symbolizing + * a capture file that was recording on a different system for which the + * debug symbols only exist locally. + * + * Note that this has no effect on stack trace unwinding, that must occur + * independently of this, when the samples are recorded by the specific + * unwinder in use. + */ +void +sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self, + const char * const *external_debug_dirs) +{ + g_return_if_fail (SYSPROF_IS_ELF_LOADER (self)); + + if (sysprof_set_strv (&self->external_debug_dirs, external_debug_dirs)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EXTERNAL_DEBUG_DIRS]); +} + +/** + * sysprof_elf_loader_load: + * @self: a #SysprofElfLoader + * @mount_namespace: a #SysprofMountNamespace for path resolving + * @file: the path of the file to load within the mount namespace + * @build_id: (nullable): an optional build-id that can be used to resolve + * the file alternatively to the file path + * + * Attempts to load a #SysprofElf for @file (or optionally by @build_id). + * + * This attempts to follow `.gnu_debuglink` ELF section headers and attach + * them to the resulting #SysprofElf so that additional symbol information + * is available. + * + * Returns: (transfer full): a #SysprofElf, or %NULL if the file could + * not be resolved. + */ +SysprofElf * +sysprof_elf_loader_load (SysprofElfLoader *self, + SysprofMountNamespace *mount_namespace, + const char *file, + const char *build_id, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + +failure: + g_set_error_literal (error, + G_FILE_ERROR, + G_FILE_ERROR_NOENT, + "Failed to locate file"); + + return NULL; +} diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h new file mode 100644 index 00000000..eb99edb4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -0,0 +1,43 @@ +/* sysprof-elf-private.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ELF (sysprof_elf_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) + +SysprofElf *sysprof_elf_new (const char *filename, + GError **error); +const char *sysprof_elf_get_build_id (SysprofElf *self); +const char *sysprof_elf_get_debug_link (SysprofElf *self); +const char *sysprof_elf_get_symbol_at_address (SysprofElf *self, + guint64 address, + guint64 *begin_address, + guint64 *end_address); +SysprofElf *sysprof_elf_get_debug_link_elf (SysprofElf *self); +void sysprof_elf_set_debug_link_elf (SysprofElf *self, + SysprofElf *debug_link_elf); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c new file mode 100644 index 00000000..c3662479 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -0,0 +1,201 @@ +/* sysprof-elf.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-elf-private.h" + +struct _SysprofElf +{ + GObject parent_instance; + char *build_id; + char *debug_link; + SysprofElf *debug_link_elf; +}; + +enum { + PROP_0, + PROP_BUILD_ID, + PROP_DEBUG_LINK, + PROP_DEBUG_LINK_ELF, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofElf, sysprof_elf, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_elf_finalize (GObject *object) +{ + SysprofElf *self = (SysprofElf *)object; + + g_clear_pointer (&self->build_id, g_free); + g_clear_pointer (&self->debug_link, g_free); + g_clear_object (&self->debug_link_elf); + + G_OBJECT_CLASS (sysprof_elf_parent_class)->finalize (object); +} + +static void +sysprof_elf_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofElf *self = SYSPROF_ELF (object); + + switch (prop_id) + { + case PROP_BUILD_ID: + g_value_set_string (value, sysprof_elf_get_build_id (self)); + break; + + case PROP_DEBUG_LINK: + g_value_set_string (value, sysprof_elf_get_debug_link (self)); + break; + + case PROP_DEBUG_LINK_ELF: + g_value_set_object (value, sysprof_elf_get_debug_link_elf (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofElf *self = SYSPROF_ELF (object); + + switch (prop_id) + { + case PROP_DEBUG_LINK_ELF: + sysprof_elf_set_debug_link_elf (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_class_init (SysprofElfClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_elf_finalize; + object_class->get_property = sysprof_elf_get_property; + object_class->set_property = sysprof_elf_set_property; + + properties [PROP_BUILD_ID] = + g_param_spec_string ("build-id", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DEBUG_LINK] = + g_param_spec_string ("debug-link", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DEBUG_LINK_ELF] = + g_param_spec_object ("debug-link-elf", NULL, NULL, + SYSPROF_TYPE_ELF, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_elf_init (SysprofElf *self) +{ +} + +SysprofElf * +sysprof_elf_new (const char *filename, + GError **error) +{ + g_return_val_if_fail (filename != NULL, NULL); + + return NULL; +} + +const char * +sysprof_elf_get_build_id (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->build_id; +} + +const char * +sysprof_elf_get_debug_link (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->debug_link; +} + +const char * +sysprof_elf_get_symbol_at_address (SysprofElf *self, + guint64 address, + guint64 *begin_address, + guint64 *end_address) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + g_return_val_if_fail (begin_address != NULL, NULL); + g_return_val_if_fail (end_address != NULL, NULL); + + *begin_address = *end_address = 0; + + return NULL; +} + +/** + * sysprof_elf_get_debug_link_elf: + * @self: a #SysprofElf + * + * Gets a #SysprofElf that was resolved from the `.gnu_debuglink` + * ELF section header. + * + * Returns: (transfer none) (nullable): a #SysprofElf or %NULL + */ +SysprofElf * +sysprof_elf_get_debug_link_elf (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->debug_link_elf; +} + +void +sysprof_elf_set_debug_link_elf (SysprofElf *self, + SysprofElf *debug_link_elf) +{ + g_return_if_fail (SYSPROF_IS_ELF (self)); + g_return_if_fail (!debug_link_elf || SYSPROF_IS_ELF (debug_link_elf)); + + if (g_set_object (&self->debug_link_elf, debug_link_elf)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_LINK_ELF]); +}