From 5ef9bdc8618b614ec402e27ab42bbe514ddb671f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marczewski?= Date: Wed, 23 Dec 2020 15:42:34 +0100 Subject: [PATCH] [Pal] Unify debug maps - Use the same mechanism (debug_map) in Pal/Linux and Pal/Linux-SGX. Previously, Pal/Linux emulated the _r_debug structure, normally maintained by ld.so, but that cannot be done in SGX outer PAL, because it's loaded by ld.so already. - Maintain the debug maps outside of SGX enclave. This allows initializing them before enclave start, and potentially makes them easier to use. - Initialize PAL debug map before enclave start. Previously, this was done from inside the enclave, so you couldn't set a breakpoint too early (e.g. in pal_linux_main). - Store only load address, without list of sections. This is to avoid parsing the list of sections just to report them to the debugger. Unfortunately, the GDB version that we support still needs these sections, but we can retrieve them in GDB plugin. - Move Python GDB code related to debug maps to a common file. --- .ci/ubuntu16.04.dockerfile | 1 + .ci/ubuntu18.04.dockerfile | 1 + Documentation/pal/porting.rst | 2 +- LibOS/shim/src/elf/shim_rtld.c | 2 +- LibOS/shim/src/shim_debug.c | 8 +- Pal/gdb_integration/debug_map_gdb.py | 171 +++++++++++++ Pal/include/lib/debug_map.h | 31 +++ Pal/include/pal/pal_debug.h | 4 +- Pal/lib/Makefile | 1 + Pal/lib/debug_map.c | 86 +++++++ Pal/src/db_rtld.c | 67 +---- Pal/src/host/Linux-SGX/db_rtld.c | 238 ++---------------- Pal/src/host/Linux-SGX/enclave_ocalls.c | 60 ++++- Pal/src/host/Linux-SGX/enclave_ocalls.h | 5 +- .../gdb_integration/debug_map_gdb.py | 1 + .../gdb_integration/graphene_sgx_gdb.py | 117 --------- Pal/src/host/Linux-SGX/ocall_types.h | 13 +- Pal/src/host/Linux-SGX/sgx_enclave.c | 27 +- Pal/src/host/Linux-SGX/sgx_internal.h | 5 - Pal/src/host/Linux-SGX/sgx_main.c | 10 +- Pal/src/host/Linux-SGX/sgx_rtld.h | 48 ---- Pal/src/host/Linux/db_process.c | 2 - Pal/src/host/Linux/db_rtld.c | 123 ++------- .../Linux/gdb_integration/debug_map_gdb.py | 1 + Pal/src/host/Linux/pal.map.template | 7 +- Pal/src/host/Linux/pal_security.h | 30 --- Pal/src/host/Skeleton/db_rtld.c | 17 +- Pal/src/pal-symbols | 4 +- Pal/src/pal_rtld.h | 4 +- Runtime/meson.build | 6 + Runtime/pal_loader | 2 + Scripts/regression.py | 1 + 32 files changed, 449 insertions(+), 646 deletions(-) create mode 100644 Pal/gdb_integration/debug_map_gdb.py create mode 100644 Pal/include/lib/debug_map.h create mode 100644 Pal/lib/debug_map.c create mode 120000 Pal/src/host/Linux-SGX/gdb_integration/debug_map_gdb.py delete mode 100644 Pal/src/host/Linux-SGX/sgx_rtld.h create mode 120000 Pal/src/host/Linux/gdb_integration/debug_map_gdb.py diff --git a/.ci/ubuntu16.04.dockerfile b/.ci/ubuntu16.04.dockerfile index 501d1b9f..d0ca1f5c 100644 --- a/.ci/ubuntu16.04.dockerfile +++ b/.ci/ubuntu16.04.dockerfile @@ -41,6 +41,7 @@ RUN apt-get update \ python \ python3-apport \ python3-apt \ + python3-pyelftools \ python3-lxml \ python3-minimal \ python3-numpy \ diff --git a/.ci/ubuntu18.04.dockerfile b/.ci/ubuntu18.04.dockerfile index 18036778..e0d8dd12 100644 --- a/.ci/ubuntu18.04.dockerfile +++ b/.ci/ubuntu18.04.dockerfile @@ -43,6 +43,7 @@ RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y \ python3-apport \ python3-apt \ python3-breathe \ + python3-pyelftools \ python3-lxml \ python3-numpy \ python3-pip \ diff --git a/Documentation/pal/porting.rst b/Documentation/pal/porting.rst index 3a48a407..493fa425 100644 --- a/Documentation/pal/porting.rst +++ b/Documentation/pal/porting.rst @@ -91,7 +91,7 @@ point :func:`pal_main()`. The definition of :func:`pal_main()` is: `g_loaded_maps` list. Otherwise, you need to implement `resolve_rtld` function to return addresses of the host ABI by names. -You may implement the optional `_DkDebugAddMap` and `_DkDebugDelMap` to use +You may implement the optional `_DkDebugMapAdd` and `_DkDebugMapRemove` to use a host-specific debugger such as GDB to debug applications in Graphene. 3. Test HelloWorld without loading library OS diff --git a/LibOS/shim/src/elf/shim_rtld.c b/LibOS/shim/src/elf/shim_rtld.c index 4f752788..67cda9c4 100644 --- a/LibOS/shim/src/elf/shim_rtld.c +++ b/LibOS/shim/src/elf/shim_rtld.c @@ -974,7 +974,7 @@ static int __load_elf_object(struct shim_handle* file, void* addr, int type) { if (type == OBJECT_REMAP) remove_r_debug((void*)map->l_addr); - append_r_debug(qstrgetstr(&map->l_file->uri), (void*)map->l_map_start, + append_r_debug(qstrgetstr(&map->l_file->uri), (void*)map->l_addr, (void*)map->l_real_ld); } diff --git a/LibOS/shim/src/shim_debug.c b/LibOS/shim/src/shim_debug.c index edd978c1..58fd397d 100644 --- a/LibOS/shim/src/shim_debug.c +++ b/LibOS/shim/src/shim_debug.c @@ -54,7 +54,7 @@ void clean_link_map_list(void) { struct gdb_link_map* m = link_map_list; for (; m; m = m->l_next) { - DkDebugDetachBinary(m->l_addr); + DkDebugMapRemove(m->l_addr); free(m); } @@ -78,7 +78,7 @@ void remove_r_debug(void* addr) { if (m->l_next) m->l_next->l_prev = m->l_prev; - DkDebugDetachBinary(addr); + DkDebugMapRemove(addr); } void append_r_debug(const char* uri, void* addr, void* dyn_addr) { @@ -110,7 +110,7 @@ void append_r_debug(const char* uri, void* addr, void* dyn_addr) { new->l_next = NULL; *tail = new; - DkDebugAttachBinary(uri, addr); + DkDebugMapAdd(uri, addr); } BEGIN_CP_FUNC(gdb_map) { @@ -156,7 +156,7 @@ BEGIN_RS_FUNC(gdb_map) { map->l_prev = prev; *tail = map; - DkDebugAttachBinary(map->l_name, map->l_addr); + DkDebugMapAdd(map->l_name, map->l_addr); DEBUG_RS("base=%p,name=%s", map->l_addr, map->l_name); } diff --git a/Pal/gdb_integration/debug_map_gdb.py b/Pal/gdb_integration/debug_map_gdb.py new file mode 100644 index 00000000..d79ee25e --- /dev/null +++ b/Pal/gdb_integration/debug_map_gdb.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-3.0-or-later */ +# Copyright (C) 2020 Intel Corporation +# Paweł Marczewski + +import os +import shlex + +import gdb # pylint: disable=import-error + +try: + from elftools.elf.elffile import ELFFile +except ImportError: + print('Python elftools module not found, please install (e.g. apt install python3-pyelftools)') + raise + +# TODO (GDB 8.2): We need to load the ELF file and determine the section addresses manually, because +# GDB before version 8.2 needs us to provide text address, and all the other section addresses, when +# loading a file: +# +# add-symbol-file -s
... +# +# GDB 8.2 makes the text_addr parameter optional, and adds an '-o ' parameter, which is +# enough for GDB to load all the sections. When we can depend on it, we will be able to stop parsing +# the ELF file here. + +def load_elf_sections(file_name, load_addr): + ''' + Open an ELF file and determine a list of sections along with addresses. + + Returns a list of (name, addr) elements. + ''' + + if not os.path.exists(file_name): + print('file not found: {}'.format(file_name)) + return {} + + sections = [] + with open(file_name, 'rb') as f: + elf = ELFFile(f) + + for section in elf.iter_sections(): + if section.name and section.header['sh_addr']: + # Workaround for old version of pyelftools (Ubuntu 16) + # that stores section.name as bytes. + name = section.name + if isinstance(name, bytes): + name = name.decode('ascii') + + addr = load_addr + section.header['sh_addr'] + sections.append((name, addr)) + + return sections + + +def retrieve_debug_maps(): + ''' + Retrieve the debug_map structure from the inferior process. The result is a dict with the + following structure: + + {load_addr: (file_name, text_addr, [(name, addr)])} + ''' + + debug_maps = {} + val_map = gdb.parse_and_eval('g_debug_map') + while int(val_map) != 0: + file_name = val_map['name'].string() + file_name = os.path.abspath(file_name) + load_addr = int(val_map['addr']) + + sections = load_elf_sections(file_name, load_addr) + text_addr = None + for name, addr in sections: + if name == '.text': + text_addr = addr + break + # We need the text_addr to use add-symbol-file (at least until GDB 8.2). + if text_addr is not None: + debug_maps[load_addr] = (file_name, text_addr, sections) + + val_map = val_map['next'] + + return debug_maps + + +class UpdateDebugMaps(gdb.Command): + """Update debug maps for the inferior process.""" + + def __init__(self): + super().__init__('update-debug-maps', gdb.COMMAND_USER) + + def invoke(self, arg, _from_tty): + self.dont_repeat() + assert arg == '' + + # Store the currently loaded maps inside the Progspace object, so that we can compare + # old and new states. See: + # https://sourceware.org/gdb/current/onlinedocs/gdb/Progspaces-In-Python.html + progspace = gdb.current_progspace() + if not hasattr(progspace, 'debug_maps'): + progspace.debug_maps = {} + + old = progspace.debug_maps + new = retrieve_debug_maps() + for load_addr in set(old) | set(new): + # Skip unload/reload if the map is unchanged + if old.get(load_addr) == new.get(load_addr): + continue + + if load_addr in old: + # Log the removing, because remove-symbol-file itself doesn't produce helpful output + # on errors. + file_name, text_addr, sections = old[load_addr] + print("Removing symbol file (was {}) from addr: 0x{:x}".format( + file_name, load_addr)) + try: + gdb.execute('remove-symbol-file -a 0x{:x}'.format(load_addr)) + except gdb.error: + print('warning: failed to remove symbol file') + + # Note that we escape text arguments to 'add-symbol-file' (file name and section names) + # using shlex.quote(), because GDB commands use a shell-like argument syntax. + + if load_addr in new: + file_name, text_addr, sections = new[load_addr] + cmd = 'add-symbol-file {} 0x{:x} '.format( + shlex.quote(file_name), text_addr) + cmd += ' '.join('-s {} 0x{:x}'.format(shlex.quote(name), addr) + for name, addr in sections + if name != '.text') + gdb.execute(cmd) + + progspace.debug_maps = new + + +class DebugMapBreakpoint(gdb.Breakpoint): + def __init__(self): + gdb.Breakpoint.__init__(self, spec="debug_map_update_debugger", internal=True) + + def stop(self): + gdb.execute('update-debug-maps') + # return False to continue automatically after the breakpoint + return False + + +def debug_map_stop_handler(event): + # Make sure we handle connecting to a new process correctly: + # update the debug maps if we never did it before. + if not isinstance(event, gdb.BreakpointEvent): + progspace = gdb.current_progspace() + if not hasattr(progspace, 'debug_maps'): + gdb.execute('update-debug-maps') + + +def debug_map_clear_objfiles_handler(event): + # Record that symbol files have been cleared on GDB's side (e.g. on program exit), so that we do + # not try to remove them again. + if hasattr(event.progspace, 'debug_maps'): + delattr(event.progspace, 'debug_maps') + + +def main(): + UpdateDebugMaps() + DebugMapBreakpoint() + + gdb.events.stop.connect(debug_map_stop_handler) + gdb.events.clear_objfiles.connect(debug_map_clear_objfiles_handler) + + +if __name__ == '__main__': + main() diff --git a/Pal/include/lib/debug_map.h b/Pal/include/lib/debug_map.h new file mode 100644 index 00000000..9e535dca --- /dev/null +++ b/Pal/include/lib/debug_map.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2020 Intel Corporation + * Paweł Marczewski + */ + +/* + * Internal debug maps, used to communicate with GDB. + * + * Note that this is part of a common library, and not part of libpal, to support setups in which + * the debug maps are maintained in an "outer" binary instead of the main PAL binary. + */ + +#ifndef PAL_DEBUG_MAP_H +#define PAL_DEBUG_MAP_H + +struct debug_map { + char* name; + void* addr; + + struct debug_map* _Atomic next; +}; + +extern struct debug_map* _Atomic g_debug_map; + +/* GDB will set a breakpoint on this function. */ +void debug_map_update_debugger(void); + +int debug_map_add(const char* name, void* addr); +int debug_map_remove(void* addr); + +#endif /* PAL_DEBUG_MAP_H */ diff --git a/Pal/include/pal/pal_debug.h b/Pal/include/pal/pal_debug.h index 83ad04f5..dff91fc3 100644 --- a/Pal/include/pal/pal_debug.h +++ b/Pal/include/pal/pal_debug.h @@ -14,7 +14,7 @@ int pal_printf(const char* fmt, ...) __attribute__((format(printf, 1, 2))); int pal_fdprintf(int fd, const char* fmt, ...) __attribute__((format(printf, 2, 3))); void warn(const char* format, ...); -void DkDebugAttachBinary(PAL_STR uri, PAL_PTR start_addr); -void DkDebugDetachBinary(PAL_PTR start_addr); +void DkDebugMapAdd(PAL_STR uri, PAL_PTR start_addr); +void DkDebugMapRemove(PAL_PTR start_addr); #endif /* PAL_DEBUG_H */ diff --git a/Pal/lib/Makefile b/Pal/lib/Makefile index 9d76ec80..08ad436d 100644 --- a/Pal/lib/Makefile +++ b/Pal/lib/Makefile @@ -109,6 +109,7 @@ $(filter-out crypto/mbedtls/crypto/library/aes.c,$(patsubst %.o,%.c,$(crypto_mbe objs += \ avl_tree.o \ crypto/udivmodti4.o \ + debug_map.o \ graphene/path.o \ network/hton.o \ network/inet_pton.o \ diff --git a/Pal/lib/debug_map.c b/Pal/lib/debug_map.c new file mode 100644 index 00000000..e833b0f6 --- /dev/null +++ b/Pal/lib/debug_map.c @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2020 Intel Corporation + * Paweł Marczewski + */ + +#include "debug_map.h" + +#include + +#include "spinlock.h" + +struct debug_map* _Atomic g_debug_map = NULL; + +/* Lock for modifying g_debug_map on our end. Even though the list can be read at any + * time, we need to prevent concurrent modification. */ +static spinlock_t g_debug_map_lock = INIT_SPINLOCK_UNLOCKED; + +static struct debug_map* debug_map_new(const char* name, void* addr) { + struct debug_map* map; + + if (!(map = malloc(sizeof(*map)))) + return NULL; + + if (!(map->name = strdup(name))) { + free(map); + return NULL; + } + + map->addr = addr; + map->next = NULL; + return map; +} + +/* This function is hooked by our gdb integration script and should be left as is. */ +__attribute__((__noinline__)) void debug_map_update_debugger(void) { + __asm__ volatile(""); // Required in addition to __noinline__ to prevent deleting this function. + // See GCC docs. +} + +int debug_map_add(const char* name, void* addr) { + struct debug_map* map = debug_map_new(name, addr); + if (!map) + return -ENOMEM; + + spinlock_lock(&g_debug_map_lock); + + map->next = g_debug_map; + g_debug_map = map; + + spinlock_unlock(&g_debug_map_lock); + + debug_map_update_debugger(); + + return 0; +} + +int debug_map_remove(void* addr) { + spinlock_lock(&g_debug_map_lock); + + struct debug_map* prev = NULL; + struct debug_map* map = g_debug_map; + while (map) { + if (map->addr == addr) + break; + prev = map; + map = map->next; + } + if (!map) { + spinlock_unlock(&g_debug_map_lock); + return -EINVAL; + } + if (prev) { + prev->next = map->next; + } else { + g_debug_map = map->next; + } + + spinlock_unlock(&g_debug_map_lock); + + debug_map_update_debugger(); + + free(map->name); + free(map); + + return 0; +} diff --git a/Pal/src/db_rtld.c b/Pal/src/db_rtld.c index 77aaa655..ca137b03 100644 --- a/Pal/src/db_rtld.c +++ b/Pal/src/db_rtld.c @@ -404,7 +404,7 @@ void free_elf_object(struct link_map* map) { map->l_next->l_prev = map->l_prev; #ifdef DEBUG - _DkDebugDelMap(map); + _DkDebugMapRemove((void*)map->l_addr); #endif if (g_loaded_maps == map) @@ -526,7 +526,7 @@ int load_elf_object_by_handle(PAL_HANDLE handle, enum object_type type, void** o g_exec_map = map; #ifdef DEBUG - _DkDebugAddMap(map); + _DkDebugMapAdd(map->l_name, (void*)map->l_addr); #endif if (out_loading_base) @@ -800,78 +800,25 @@ static int relocate_elf_object(struct link_map* l) { return 0; } -void DkDebugAttachBinary(PAL_STR uri, PAL_PTR start_addr) { +void DkDebugMapAdd(PAL_STR uri, PAL_PTR start_addr) { #ifndef DEBUG __UNUSED(uri); __UNUSED(start_addr); #else - if (!strstartswith(uri, URI_PREFIX_FILE) || !start_addr) + if (!strstartswith(uri, URI_PREFIX_FILE)) return; const char* realname = uri + URI_PREFIX_FILE_LEN; - struct link_map* l = new_elf_object(realname, OBJECT_EXTERNAL); - if (!l) - return; - /* This is the ELF header. We read it in `open_verify'. */ - const ElfW(Ehdr)* header = (ElfW(Ehdr)*)start_addr; - - l->l_entry = header->e_entry; - l->l_phnum = header->e_phnum; - l->l_map_start = (ElfW(Addr))start_addr; - - ElfW(Phdr)* phdr = (void*)((char*)start_addr + header->e_phoff); - const ElfW(Phdr)* ph; - ElfW(Addr) map_start = 0, map_end = 0; - - for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) - if (ph->p_type == PT_PHDR) { - if (!map_start || ph->p_vaddr < map_start) - map_start = ALLOC_ALIGN_DOWN(ph->p_vaddr); - if (!map_end || ph->p_vaddr + ph->p_memsz > map_end) - map_end = ALLOC_ALIGN_UP(ph->p_vaddr + ph->p_memsz); - } - - l->l_addr = l->l_map_start - map_start; - l->l_map_end = l->l_addr + map_end; - - for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) - switch (ph->p_type) { - /* These entries tell us where to find things once the file's - segments are mapped in. We record the addresses it says - verbatim, and later correct for the run-time load address. */ - case PT_DYNAMIC: - l->l_ld = l->l_real_ld = (ElfW(Dyn)*)((char*)l->l_addr + ph->p_vaddr); - l->l_ldnum = ph->p_memsz / sizeof(ElfW(Dyn)); - break; - - case PT_PHDR: - l->l_phdr = (ElfW(Phdr)*)((char*)l->l_addr + ph->p_vaddr); - break; - - case PT_GNU_RELRO: - l->l_relro_addr = l->l_addr + ph->p_vaddr; - l->l_relro_size = ph->p_memsz; - break; - } - - _DkDebugAddMap(l); - free(l); + _DkDebugMapAdd(realname, start_addr); #endif } -void DkDebugDetachBinary(PAL_PTR start_addr) { +void DkDebugMapRemove(PAL_PTR start_addr) { #ifndef DEBUG __UNUSED(start_addr); #else - for (struct link_map* l = g_loaded_maps; l; l = l->l_next) - if (l->l_map_start == (ElfW(Addr))start_addr) { - _DkDebugDelMap(l); - - if (l->l_type == OBJECT_EXTERNAL) - free_elf_object(l); - break; - } + _DkDebugMapRemove(start_addr); #endif } diff --git a/Pal/src/host/Linux-SGX/db_rtld.c b/Pal/src/host/Linux-SGX/db_rtld.c index 8563c509..22600856 100644 --- a/Pal/src/host/Linux-SGX/db_rtld.c +++ b/Pal/src/host/Linux-SGX/db_rtld.c @@ -2,222 +2,44 @@ /* Copyright (C) 2014 Stony Brook University */ /* - * This file contains utilities to load ELF binaries into the memory and link them against each - * other. The source code in this file was imported from the GNU C Library and modified. + * This file contains host-specific code related to linking and reporting ELFs to debugger. + * + * Overview of ELF files used in this host: + * - pal-sgx and libraries it uses (outside enclave) - handled by ld.so and reported by it (through + * _r_debug mechanism) + * - libpal.so (in enclave) - reported in sgx_main.c before enclave start + * - LibOS, application, libc... (in enclave) - reported through DkDebugMap* + * + * In addition, we report executable memory mappings to the profiling subsystem. */ -#include - #include "api.h" #include "elf-x86_64.h" #include "elf/elf.h" #include "pal.h" #include "pal_debug.h" -#include "pal_defs.h" -#include "pal_error.h" -#include "pal_internal.h" #include "pal_linux.h" -#include "pal_linux_defs.h" #include "pal_rtld.h" -#include "pal_security.h" -#include "sgx_rtld.h" -#include "spinlock.h" -#include "sysdeps/generic/ldsodefs.h" -/* Global debug map. To simplify setup, the pointer to g_debug_map is passed outside with - * ocall_update_debugger(). - * (Note that we pass a pointer to g_debug_map, not its current value, to avoid race conditions). */ -static struct debug_map* _Atomic g_debug_map = NULL; +void _DkDebugMapAdd(const char* name, void* addr) { + ocall_debug_map_add(name, addr); -/* Lock for modifying g_debug_map on our end. Even though the list can be read at any - * time, we need to prevent concurrent modification. */ -static spinlock_t g_debug_map_lock = INIT_SPINLOCK_UNLOCKED; - -static struct debug_map* debug_map_alloc(const char* file_name, void* load_addr) { - struct debug_map* map; - - if (!(map = malloc(sizeof(*map)))) - return NULL; - - if (!(map->file_name = strdup(file_name))) { - free(map); - return NULL; - } - - map->load_addr = load_addr; - map->section = NULL; - map->next = NULL; - return map; -} - -static struct debug_section* debug_map_add_section(struct debug_map* map, const char* section_name, - void* addr) { - struct debug_section* section; - - if (!(section = malloc(sizeof(*section)))) - return NULL; - - if (!(section->name = strdup(section_name))) { - free(section); - return NULL; - } - - section->addr = addr; - section->next = map->section; - map->section = section; - return section; -} - -static void debug_map_free(struct debug_map* map) { - struct debug_section* section = map->section; - while (section) { - struct debug_section* next = section->next; - free(section->name); - free(section); - section = next; - } - free(map->file_name); - free(map); -} - -static void debug_map_add(struct debug_map* map) { - spinlock_lock(&g_debug_map_lock); - - map->next = g_debug_map; - g_debug_map = map; - - spinlock_unlock(&g_debug_map_lock); - - ocall_update_debugger(&g_debug_map); -} - -static bool debug_map_del(void* load_addr) { - assert(g_debug_map); - - spinlock_lock(&g_debug_map_lock); - - struct debug_map* prev = NULL; - struct debug_map* map = g_debug_map; - while (map) { - if (map->load_addr == load_addr) - break; - prev = map; - map = map->next; - } - - if (!map) { - spinlock_unlock(&g_debug_map_lock); - return false; - } - - if (prev == NULL) - g_debug_map = map->next; - else - prev->next = map->next; - - spinlock_unlock(&g_debug_map_lock); - - debug_map_free(map); - - ocall_update_debugger(&g_debug_map); - return true; -} - -void _DkDebugAddMap(struct link_map* map) { - const ElfW(Ehdr)* ehdr = (void*)map->l_map_start; - int shdrsz = sizeof(ElfW(Shdr)) * ehdr->e_shnum; - ElfW(Shdr)* shdr = NULL; - ElfW(Phdr)* phdr = (void*)(map->l_map_start + ehdr->e_phoff); + const ElfW(Ehdr)* ehdr = addr; + ElfW(Phdr)* phdr = (void*)(addr + ehdr->e_phoff); const ElfW(Phdr)* ph; - - int fd = ocall_open(map->l_name, O_RDONLY, 0); - if (IS_ERR(fd)) - return; - - for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph) - if (ph->p_type == PT_LOAD && ehdr->e_shoff >= ph->p_offset && - ehdr->e_shoff < ph->p_offset + ph->p_filesz) { - shdr = (void*)map->l_addr + ph->p_vaddr + (ehdr->e_shoff - ph->p_offset); - break; - } - - if (!shdr) { - shdr = __alloca(shdrsz); - unsigned long s = ALLOC_ALIGN_DOWN(ehdr->e_shoff); - unsigned long e = ALLOC_ALIGN_UP(ehdr->e_shoff + shdrsz); - void* umem = NULL; - ocall_mmap_untrusted(&umem, e - s, PROT_READ, MAP_SHARED, fd, s); - memcpy(shdr, umem + ehdr->e_shoff - s, shdrsz); - ocall_munmap_untrusted(umem, e - s); - } - - ElfW(Shdr)* shdrend = (void*)shdr + shdrsz; - size_t shstroff = shdr[ehdr->e_shstrndx].sh_offset; - size_t shstrsz = shdr[ehdr->e_shstrndx].sh_size; - const char* shstrtab = NULL; - - for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph) - if (ph->p_type == PT_LOAD && shstroff >= ph->p_offset && - shstroff < ph->p_offset + ph->p_filesz) { - shstrtab = (void*)map->l_addr + ph->p_vaddr + (shstroff - ph->p_offset); - break; - } - - if (!shstrtab) { - shstrtab = __alloca(shstrsz); - unsigned long s = ALLOC_ALIGN_DOWN(shstroff); - unsigned long e = ALLOC_ALIGN_UP(shstroff + shstrsz); - void* umem = NULL; - ocall_mmap_untrusted(&umem, e - s, PROT_READ, MAP_SHARED, fd, s); - memcpy((void*)shstrtab, umem + shstroff - s, shstrsz); - ocall_munmap_untrusted(umem, e - s); - } - - ocall_close(fd); - - struct debug_map* debug_map = debug_map_alloc(map->l_name, (void*)map->l_addr); - if (!debug_map) { - SGX_DBG(DBG_E, "_DkDebugAddMap: error allocating new map\n"); - return; - } - - for (ElfW(Shdr)* s = shdr; s < shdrend; s++) { - if (!s->sh_name || !s->sh_addr) - continue; - if (s->sh_type == SHT_NULL) - continue; - if (strstartswith(shstrtab + s->sh_name, ".debug_")) - continue; - - if (!debug_map_add_section(debug_map, shstrtab + s->sh_name, - (void*)(map->l_addr + s->sh_addr))) { - SGX_DBG(DBG_E, "_DkDebugAddMap: error allocating new section\n"); - debug_map_free(debug_map); - return; - } - } - - debug_map_add(debug_map); - for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph) if (ph->p_type == PT_LOAD && ph->p_flags & PF_X) { uint64_t mapstart = ALLOC_ALIGN_DOWN(ph->p_vaddr); uint64_t mapend = ALLOC_ALIGN_UP(ph->p_vaddr + ph->p_filesz); uint64_t offset = ALLOC_ALIGN_DOWN(ph->p_offset); - ocall_report_mmap(map->l_name, map->l_addr + mapstart, mapend - mapstart, offset); + ocall_report_mmap(name, (uint64_t)addr + mapstart, mapend - mapstart, offset); } } -void _DkDebugDelMap(struct link_map* map) { - debug_map_del((void*)map->l_addr); +void _DkDebugMapRemove(void* addr) { + ocall_debug_map_remove(addr); } -extern void* g_section_text; -extern void* g_section_rodata; -extern void* g_section_dynamic; -extern void* g_section_data; -extern void* g_section_bss; - void setup_pal_map(struct link_map* pal_map) { const ElfW(Ehdr)* header = (void*)pal_map->l_addr; @@ -230,32 +52,4 @@ void setup_pal_map(struct link_map* pal_map) { pal_map->l_prev = pal_map->l_next = NULL; g_loaded_maps = pal_map; - - struct debug_map* debug_map = debug_map_alloc(pal_map->l_name, (void*)pal_map->l_addr); - if (!debug_map) { - SGX_DBG(DBG_E, "setup_pal_map: error allocating new map\n"); - return; - } - - if (!debug_map_add_section(debug_map, ".text", &g_section_text)) - goto fail; - - if (!debug_map_add_section(debug_map, ".rodata", &g_section_rodata)) - goto fail; - - if (!debug_map_add_section(debug_map, ".dynamic", &g_section_dynamic)) - goto fail; - - if (!debug_map_add_section(debug_map, ".data", &g_section_data)) - goto fail; - - if (!debug_map_add_section(debug_map, ".bss", &g_section_bss)) - goto fail; - - debug_map_add(debug_map); - return; - -fail: - SGX_DBG(DBG_E, "setup_pal_map: error allocating new section\n"); - debug_map_free(debug_map); } diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.c b/Pal/src/host/Linux-SGX/enclave_ocalls.c index ea1d87a5..fcf3afad 100644 --- a/Pal/src/host/Linux-SGX/enclave_ocalls.c +++ b/Pal/src/host/Linux-SGX/enclave_ocalls.c @@ -1460,9 +1460,12 @@ int ocall_delete(const char* pathname) { return retval; } -int ocall_update_debugger(struct debug_map* _Atomic* debug_map) { +int ocall_debug_map_add(const char* name, void* addr) { int retval = 0; - ms_ocall_update_debugger_t* ms; + +#ifdef DEBUG + size_t len = strlen(name) + 1; + ms_ocall_debug_map_add_t* ms; void* old_ustack = sgx_prepare_ustack(); ms = sgx_alloc_on_ustack_aligned(sizeof(*ms), alignof(*ms)); @@ -1471,18 +1474,59 @@ int ocall_update_debugger(struct debug_map* _Atomic* debug_map) { return -EPERM; } - WRITE_ONCE(ms->ms_debug_map, debug_map); + void* untrusted_name = sgx_copy_to_ustack(name, len); + if (!untrusted_name) { + sgx_reset_ustack(old_ustack); + return -EPERM; + } + + WRITE_ONCE(ms->ms_name, untrusted_name); + WRITE_ONCE(ms->ms_addr, addr); do { - retval = sgx_exitless_ocall(OCALL_UPDATE_DEBUGGER, ms); + retval = sgx_exitless_ocall(OCALL_DEBUG_MAP_ADD, ms); } while (retval == -EINTR); sgx_reset_ustack(old_ustack); +#else + __UNUSED(name); + __UNUSED(addr); +#endif + + return retval; +} + +int ocall_debug_map_remove(void* addr) { + int retval = 0; + +#ifdef DEBUG + ms_ocall_debug_map_remove_t* ms; + + void* old_ustack = sgx_prepare_ustack(); + ms = sgx_alloc_on_ustack_aligned(sizeof(*ms), alignof(*ms)); + if (!ms) { + sgx_reset_ustack(old_ustack); + return -EPERM; + } + + WRITE_ONCE(ms->ms_addr, addr); + + do { + retval = sgx_exitless_ocall(OCALL_DEBUG_MAP_REMOVE, ms); + } while (retval == -EINTR); + + sgx_reset_ustack(old_ustack); +#else + __UNUSED(addr); +#endif + return retval; } int ocall_report_mmap(const char* filename, uint64_t addr, uint64_t len, uint64_t offset) { int retval = 0; + +#ifdef DEBUG size_t filename_len = filename ? strlen(filename) + 1 : 0; ms_ocall_report_mmap_t* ms; @@ -1505,10 +1549,16 @@ int ocall_report_mmap(const char* filename, uint64_t addr, uint64_t len, uint64_ retval = sgx_exitless_ocall(OCALL_REPORT_MMAP, ms); sgx_reset_ustack(old_ustack); +#else + __UNUSED(filename); + __UNUSED(addr); + __UNUSED(len); + __UNUSED(offset); +#endif + return retval; } - int ocall_eventfd(unsigned int initval, int flags) { int retval = 0; ms_ocall_eventfd_t* ms; diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.h b/Pal/src/host/Linux-SGX/enclave_ocalls.h index e19581a9..9ad3d740 100644 --- a/Pal/src/host/Linux-SGX/enclave_ocalls.h +++ b/Pal/src/host/Linux-SGX/enclave_ocalls.h @@ -10,7 +10,6 @@ #include "linux_types.h" #include "pal_linux.h" #include "sgx_attest.h" -#include "sgx_rtld.h" noreturn void ocall_exit(int exitcode, int is_exitgroup); @@ -92,7 +91,9 @@ int ocall_rename(const char* oldpath, const char* newpath); int ocall_delete(const char* pathname); -int ocall_update_debugger(struct debug_map* _Atomic* debug_map); +int ocall_debug_map_add(const char* name, void* addr); + +int ocall_debug_map_remove(void* addr); int ocall_report_mmap(const char* filename, uint64_t addr, uint64_t len, uint64_t offset); diff --git a/Pal/src/host/Linux-SGX/gdb_integration/debug_map_gdb.py b/Pal/src/host/Linux-SGX/gdb_integration/debug_map_gdb.py new file mode 120000 index 00000000..970f4eb3 --- /dev/null +++ b/Pal/src/host/Linux-SGX/gdb_integration/debug_map_gdb.py @@ -0,0 +1 @@ +../../../../gdb_integration/debug_map_gdb.py \ No newline at end of file diff --git a/Pal/src/host/Linux-SGX/gdb_integration/graphene_sgx_gdb.py b/Pal/src/host/Linux-SGX/gdb_integration/graphene_sgx_gdb.py index 1377144f..f80d100f 100644 --- a/Pal/src/host/Linux-SGX/gdb_integration/graphene_sgx_gdb.py +++ b/Pal/src/host/Linux-SGX/gdb_integration/graphene_sgx_gdb.py @@ -7,97 +7,9 @@ import os import gdb # pylint: disable=import-error - _g_paginations = [] -def retrieve_debug_maps(): - ''' - Retrieve the debug_map structure from the inferior process. The result is a dict with the - following structure: - - {load_addr: (file_name, {name: addr})} - ''' - - if int(gdb.parse_and_eval('g_pal_enclave.debug_map')) == 0: - # Not initialized yet - return {} - - debug_maps = {} - val_map = gdb.parse_and_eval('*g_pal_enclave.debug_map') - while int(val_map) != 0: - file_name = val_map['file_name'].string() - file_name = os.path.abspath(file_name) - - load_addr = int(val_map['load_addr']) - - sections = {} - val_section = val_map['section'] - while int(val_section) != 0: - name = val_section['name'].string() - addr = int(val_section['addr']) - - sections[name] = addr - val_section = val_section['next'] - - # We need the text_addr to use add-symbol-file (at least until GDB 8.2). - if '.text' in sections: - debug_maps[load_addr] = (file_name, sections) - - val_map = val_map['next'] - - return debug_maps - - -class UpdateDebugMaps(gdb.Command): - """Update debug maps for the inferior process.""" - - def __init__(self): - super().__init__('update-debug-maps', gdb.COMMAND_USER) - - def invoke(self, arg, _from_tty): - self.dont_repeat() - assert arg == '' - - # Store the currently loaded maps inside the Progspace object, so that we can compare - # old and new states. See: - # https://sourceware.org/gdb/current/onlinedocs/gdb/Progspaces-In-Python.html - progspace = gdb.current_progspace() - if not hasattr(progspace, 'sgx_debug_maps'): - progspace.sgx_debug_maps = {} - - old = progspace.sgx_debug_maps - new = retrieve_debug_maps() - for load_addr in set(old) | set(new): - # Skip unload/reload if the map is unchanged - if old.get(load_addr) == new.get(load_addr): - continue - - # Note that this doesn't escape the file names. - - if load_addr in old: - # Log the removing, because remove-symbol-file itself doesn't produce helpful output - # on errors. - file_name, sections = old[load_addr] - print("Removing symbol file (was {}) from addr: 0x{:x}".format( - file_name, load_addr)) - try: - gdb.execute('remove-symbol-file -a 0x{:x}'.format(load_addr)) - except gdb.error: - print('warning: failed to remove symbol file') - - if load_addr in new: - file_name, sections = new[load_addr] - text_addr = sections['.text'] - cmd = 'add-symbol-file {} 0x{:x} '.format(file_name, text_addr) - cmd += ' '.join('-s {} 0x{:x}'.format(name, addr) - for name, addr in sections.items() - if name != '.text') - gdb.execute(cmd) - - progspace.sgx_debug_maps = new - - class PushPagination(gdb.Command): """Temporarily changing pagination and saving the old state. @@ -133,35 +45,9 @@ class PopPagination(gdb.Command): pagination = _g_paginations.pop() gdb.execute('set pagination ' + ('on' if pagination else 'off')) - -class UpdateBreakpoint(gdb.Breakpoint): - def __init__(self): - gdb.Breakpoint.__init__(self, spec="update_debugger", internal=1) - - def stop(self): - gdb.execute('update-debug-maps') - return False - - -def stop_handler(_event): - # Make sure we handle connecting to a new process correctly: - # update the debug maps if we never did it before. - progspace = gdb.current_progspace() - if not hasattr(progspace, 'sgx_debug_maps'): - gdb.execute('update-debug-maps') - - -def clear_objfiles_handler(event): - # Record that symbol files has been cleared on GDB's side (e.g. on program exit), so that we do - # not try to remove them again. - if hasattr(event.progspace, 'sgx_debug_maps'): - delattr(event.progspace, 'sgx_debug_maps') - - def main(): PushPagination() PopPagination() - UpdateDebugMaps() # Some of the things we want to do can't be done using gdb Python API, we need to fall back to a # standard gdb script. @@ -169,9 +55,6 @@ def main(): print("[%s] Loading %s..." % (os.path.basename(__file__), gdb_script)) gdb.execute("source " + gdb_script) - UpdateBreakpoint() - gdb.events.stop.connect(stop_handler) - gdb.events.clear_objfiles.connect(clear_objfiles_handler) if __name__ == "__main__": main() diff --git a/Pal/src/host/Linux-SGX/ocall_types.h b/Pal/src/host/Linux-SGX/ocall_types.h index ea64d1d0..cf80a8ad 100644 --- a/Pal/src/host/Linux-SGX/ocall_types.h +++ b/Pal/src/host/Linux-SGX/ocall_types.h @@ -10,7 +10,6 @@ #include "pal.h" #include "sgx_arch.h" #include "sgx_attest.h" -#include "sgx_rtld.h" /* * GCC's structure padding may cause leaking from uninialized @@ -60,7 +59,8 @@ enum { OCALL_POLL, OCALL_RENAME, OCALL_DELETE, - OCALL_UPDATE_DEBUGGER, + OCALL_DEBUG_MAP_ADD, + OCALL_DEBUG_MAP_REMOVE, OCALL_REPORT_MMAP, OCALL_EVENTFD, OCALL_GET_QUOTE, @@ -287,6 +287,15 @@ typedef struct { struct debug_map* _Atomic* ms_debug_map; } ms_ocall_update_debugger_t; +typedef struct { + const char* ms_name; + void* ms_addr; +} ms_ocall_debug_map_add_t; + +typedef struct { + void* ms_addr; +} ms_ocall_debug_map_remove_t; + typedef struct { const char* ms_filename; uint64_t ms_addr; diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.c b/Pal/src/host/Linux-SGX/sgx_enclave.c index 2976eccf..13425e52 100644 --- a/Pal/src/host/Linux-SGX/sgx_enclave.c +++ b/Pal/src/host/Linux-SGX/sgx_enclave.c @@ -16,6 +16,7 @@ #include #include "cpu.h" +#include "debug_map.h" #include "ecall_types.h" #include "linux_utils.h" #include "ocall_types.h" @@ -644,13 +645,26 @@ static long sgx_ocall_eventfd(void* pms) { return ret; } -static long sgx_ocall_update_debugger(void* pms) { - ms_ocall_update_debugger_t* ms = (ms_ocall_update_debugger_t*)pms; - ODEBUG(OCALL_UPDATE_DEBUGGER, ms); +static long sgx_ocall_debug_map_add(void* pms) { + ms_ocall_debug_map_add_t* ms = (ms_ocall_debug_map_add_t*)pms; #ifdef DEBUG - g_pal_enclave.debug_map = ms->ms_debug_map; - update_debugger(); + int ret = debug_map_add(ms->ms_name, ms->ms_addr); + if (ret < 0) + SGX_DBG(DBG_E, "debug_map_add(%s, %p): %d", ms->ms_name, ms->ms_addr, ret); +#else + __UNUSED(ms); +#endif + return 0; +} + +static long sgx_ocall_debug_map_remove(void* pms) { + ms_ocall_debug_map_remove_t* ms = (ms_ocall_debug_map_remove_t*)pms; + +#ifdef DEBUG + int ret = debug_map_remove(ms->ms_addr); + if (ret < 0) + SGX_DBG(DBG_E, "debug_map_remove(%p): %d", ms->ms_addr, ret); #else __UNUSED(ms); #endif @@ -714,7 +728,8 @@ sgx_ocall_fn_t ocall_table[OCALL_NR] = { [OCALL_POLL] = sgx_ocall_poll, [OCALL_RENAME] = sgx_ocall_rename, [OCALL_DELETE] = sgx_ocall_delete, - [OCALL_UPDATE_DEBUGGER] = sgx_ocall_update_debugger, + [OCALL_DEBUG_MAP_ADD] = sgx_ocall_debug_map_add, + [OCALL_DEBUG_MAP_REMOVE] = sgx_ocall_debug_map_remove, [OCALL_REPORT_MMAP] = sgx_ocall_report_mmap, [OCALL_EVENTFD] = sgx_ocall_eventfd, [OCALL_GET_QUOTE] = sgx_ocall_get_quote, diff --git a/Pal/src/host/Linux-SGX/sgx_internal.h b/Pal/src/host/Linux-SGX/sgx_internal.h index f29cf6c4..e0fa81c1 100644 --- a/Pal/src/host/Linux-SGX/sgx_internal.h +++ b/Pal/src/host/Linux-SGX/sgx_internal.h @@ -13,7 +13,6 @@ #include "api.h" #include "pal_linux.h" #include "pal_security.h" -#include "sgx_rtld.h" #include "sysdep-arch.h" #include "toml.h" @@ -72,10 +71,6 @@ struct pal_enclave { char* entrypoint_uri; /* URI of the entry executable for the LibOS */ #ifdef DEBUG - /* Pointer to information for GDB inside the enclave (see sgx_rtld.h). - * Set up using update_debugger() ocall. */ - struct debug_map* _Atomic* debug_map; - /* profiling */ bool profile_enable; char profile_filename[64]; diff --git a/Pal/src/host/Linux-SGX/sgx_main.c b/Pal/src/host/Linux-SGX/sgx_main.c index e28f3da8..243b184c 100644 --- a/Pal/src/host/Linux-SGX/sgx_main.c +++ b/Pal/src/host/Linux-SGX/sgx_main.c @@ -18,6 +18,7 @@ #include "hex.h" #include "toml.h" +#include "debug_map.h" #include "gdb_integration/sgx_gdb.h" #include "linux_utils.h" #include "rpc_queue.h" @@ -552,21 +553,20 @@ static int initialize_enclave(struct pal_enclave* enclave, const char* manifest_ #ifdef DEBUG if (enclave->profile_enable) { /* - * Report libpal map. All subsequent files will be reported via DkDebugAddMap(), but this + * Report libpal map. All subsequent files will be reported via DkDebugMapAdd(), but this * one has to be handled separately. * * We report it here, before enclave start (as opposed to setup_pal_map()), because we want * the mmap to appear in profiling data before the samples from libpal code, so that the * addresses for these samples can be resolved to symbols. - * - * TODO: Also report the map to GDB before enclave start (and not in setup_pal_map()), so - * that libpal symbols are known to gdb immediately after enclave start. */ ret = report_mmaps(enclave_image, enclave->libpal_uri + URI_PREFIX_FILE_LEN, pal_area->addr); if (IS_ERR(ret)) goto out; } + + debug_map_add(enclave->libpal_uri + URI_PREFIX_FILE_LEN, (void*)pal_area->addr); #endif ret = 0; @@ -982,8 +982,6 @@ static int load_enclave(struct pal_enclave* enclave, const char* exec_path, char env_i += strnlen(&env[env_i], env_size - env_i) + 1; } - - enclave->debug_map = NULL; #endif enclave->libpal_uri = alloc_concat(URI_PREFIX_FILE, URI_PREFIX_FILE_LEN, g_libpal_path, -1); diff --git a/Pal/src/host/Linux-SGX/sgx_rtld.h b/Pal/src/host/Linux-SGX/sgx_rtld.h deleted file mode 100644 index 1f7345af..00000000 --- a/Pal/src/host/Linux-SGX/sgx_rtld.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* Copyright (C) 2020 Intel Corporation - * Paweł Marczewski - */ - -/* - * Internal debug maps, used for SGX to communicate with debugger. We maintain it so that it is in a - * consistent state any time the process is stopped (any add/delete is an atomic modification of one - * pointer). - * - * The debug map is maintained inside the enclave, and the debugger is notified using - * ocall_update_debugger(). - */ - -#ifndef SGX_RTLD_H -#define SGX_RTLD_H - -/* - * TODO: (GDB 8.2) - * - * To add the files in GDB, we use the 'add-symbol-file' command. In the GDB versions we support, - * that command requires specifying a text section address (and, apparently, all the other - * sections). In GDB 8.2, the 'text_addr' parameter is optional, and there is a new '-o offset' - * option which allows to just specify load address for the whole file, and that's enough to load - * all the sections. - * - * Once we are able to rely on newer GDB, we can get rid the section list (struct debug_section). - * It's also possible that we will be able to use the r_debug structure instead, so that the same - * mechanism is - * used in Linux and Linux-SGX (even though in Linux-SGX we parse the structure manually in Python). - */ - -struct debug_section { - char* name; - void* addr; - - struct debug_section* next; -}; - -struct debug_map { - char* file_name; - void* load_addr; - struct debug_section* section; - - struct debug_map* _Atomic next; -}; - -#endif /* SGX_RTLD_H */ diff --git a/Pal/src/host/Linux/db_process.c b/Pal/src/host/Linux/db_process.c index 5ef23562..e1b1118f 100644 --- a/Pal/src/host/Linux/db_process.c +++ b/Pal/src/host/Linux/db_process.c @@ -196,8 +196,6 @@ int _DkProcessCreate(PAL_HANDLE* handle, const char* exec_uri, const char** args proc_args->parent_process_id = g_linux_state.parent_process_id; memcpy(&proc_args->pal_sec, &g_pal_sec, sizeof(struct pal_sec)); - proc_args->pal_sec._dl_debug_state = NULL; - proc_args->pal_sec._r_debug = NULL; proc_args->memory_quota = g_linux_state.memory_quota; char* data = (char*)(proc_args + 1); diff --git a/Pal/src/host/Linux/db_rtld.c b/Pal/src/host/Linux/db_rtld.c index 873481b4..93839d51 100644 --- a/Pal/src/host/Linux/db_rtld.c +++ b/Pal/src/host/Linux/db_rtld.c @@ -2,126 +2,32 @@ /* Copyright (C) 2014 Stony Brook University */ /* - * This file contains utilities to load ELF binaries into the memory and link them against each - * other. The source code in this file was imported from the GNU C Library and modified. + * This file contains host-specific code related to linking and reporting ELFs to debugger. + * + * Overview of ELF files used in this host: + * - libpal.so - used as main executable, so it doesn't need to be reported separately + * - LibOS, application, libc... - reported through DkDebugMap* */ #include "api.h" +#include "debug_map.h" #include "elf-arch.h" #include "elf/elf.h" #include "pal.h" #include "pal_debug.h" -#include "pal_defs.h" -#include "pal_error.h" -#include "pal_internal.h" #include "pal_linux.h" -#include "pal_linux_defs.h" #include "pal_rtld.h" -#include "pal_security.h" -#include "sysdeps/generic/ldsodefs.h" -/* This function exists solely to have a breakpoint set on it by the debugger. The debugger is - * supposed to find this function's address by examining the r_brk member of struct r_debug, but GDB - * 4.15 in fact looks for this particular symbol name in the PT_INTERP file. */ -static void __attribute__((noinline)) pal_dl_debug_state(void) { - if (g_pal_sec._dl_debug_state) - g_pal_sec._dl_debug_state(); +void _DkDebugMapAdd(const char* name, void* addr) { + int ret = debug_map_add(name, addr); + if (ret < 0) + printf("debug_map_add(%s, %p) failed: %d\n", name, addr, ret); } -extern __typeof(pal_dl_debug_state) _dl_debug_state __attribute((alias("pal_dl_debug_state"))); - -/* This structure communicates dl state to the debugger. The debugger normally finds it via the - * DT_DEBUG entry in the dynamic section, but in a statically-linked program there is no dynamic - * section for the debugger to examine and it looks for this particular symbol name. */ -struct r_debug g_pal_r_debug = {1, NULL, (ElfW(Addr))&pal_dl_debug_state, RT_CONSISTENT, 0}; -symbol_version_default(g_pal_r_debug, _r_debug, PAL); - -void _DkDebugAddMap(struct link_map* map) { -#ifdef DEBUG - struct r_debug* dbg = g_pal_sec._r_debug ?: &g_pal_r_debug; - int len = map->l_name ? strlen(map->l_name) + 1 : 0; - - struct link_map** prev = &dbg->r_map; - struct link_map* last = NULL; - struct link_map* tmp = *prev; - while (tmp) { - if (tmp->l_addr == map->l_addr && tmp->l_ld == map->l_ld && - !memcmp(tmp->l_name, map->l_name, len)) - return; - - last = tmp; - tmp = *(prev = &last->l_next); - } - - struct link_gdb_map* m = malloc(sizeof(struct link_gdb_map) + len); - if (!m) - return; - - if (len) { - m->l_name = (char*)m + sizeof(struct link_gdb_map); - memcpy((void*)m->l_name, map->l_name, len); - } else { - m->l_name = NULL; - } - - m->l_addr = map->l_addr; - m->l_ld = map->l_real_ld; - - dbg->r_state = RT_ADD; - pal_dl_debug_state(); - - *prev = (struct link_map*)m; - m->l_prev = last; - m->l_next = NULL; - - dbg->r_state = RT_CONSISTENT; - pal_dl_debug_state(); -#else - __UNUSED(map); -#endif -} - -void _DkDebugDelMap(struct link_map* map) { -#ifdef DEBUG - struct r_debug* dbg = g_pal_sec._r_debug ?: &g_pal_r_debug; - int len = map->l_name ? strlen(map->l_name) + 1 : 0; - - struct link_map** prev = &dbg->r_map; - struct link_map* last = NULL; - struct link_map* tmp = *prev; - struct link_map* found = NULL; - while (tmp) { - if (tmp->l_addr == map->l_addr && tmp->l_ld == map->l_ld && - !memcmp(tmp->l_name, map->l_name, len)) { - found = tmp; - break; - } - - last = tmp; - tmp = *(prev = &last->l_next); - } - - if (!found) - return; - - dbg->r_state = RT_DELETE; - pal_dl_debug_state(); - - if (last) - last->l_next = tmp->l_next; - else - dbg->r_map = tmp->l_next; - - if (tmp->l_next) - tmp->l_next->l_prev = last; - - free(tmp); - - dbg->r_state = RT_CONSISTENT; - pal_dl_debug_state(); -#else - __UNUSED(map); -#endif +void _DkDebugMapRemove(void* addr) { + int ret = debug_map_remove(addr); + if (ret < 0) + printf("debug_map_remove(%p) failed: %d\n", addr, ret); } void setup_pal_map(struct link_map* pal_map) { @@ -134,7 +40,6 @@ void setup_pal_map(struct link_map* pal_map) { pal_map->l_phnum = header->e_phnum; setup_elf_hash(pal_map); - _DkDebugAddMap(pal_map); pal_map->l_prev = pal_map->l_next = NULL; g_loaded_maps = pal_map; } diff --git a/Pal/src/host/Linux/gdb_integration/debug_map_gdb.py b/Pal/src/host/Linux/gdb_integration/debug_map_gdb.py new file mode 120000 index 00000000..970f4eb3 --- /dev/null +++ b/Pal/src/host/Linux/gdb_integration/debug_map_gdb.py @@ -0,0 +1 @@ +../../../../gdb_integration/debug_map_gdb.py \ No newline at end of file diff --git a/Pal/src/host/Linux/pal.map.template b/Pal/src/host/Linux/pal.map.template index b8d5c3a2..54dd39c4 100644 --- a/Pal/src/host/Linux/pal.map.template +++ b/Pal/src/host/Linux/pal.map.template @@ -1,9 +1,4 @@ PAL { - global: $(PAL_SYMBOLS) _r_debug; - + global: $(PAL_SYMBOLS) local: *; }; -PAL_PRIVATE { - global: - _dl_debug_state; -}; diff --git a/Pal/src/host/Linux/pal_security.h b/Pal/src/host/Linux/pal_security.h index acb1e862..3914f5c8 100644 --- a/Pal/src/host/Linux/pal_security.h +++ b/Pal/src/host/Linux/pal_security.h @@ -9,40 +9,10 @@ #include "pal.h" #include "sysdeps/generic/ldsodefs.h" -/* Rendezvous structure used by the run-time dynamic linker to communicate - details of shared object loading to the debugger. If the executable's - dynamic section has a DT_DEBUG element, the run-time linker sets that - element's value to the address where this structure can be found. */ -struct r_debug { - int r_version; /* Version number for this protocol. */ - - struct link_map* r_map; /* Head of the chain of loaded objects. */ - - /* This is the address of a function internal to the run-time linker, - that will always be called when the linker begins to map in a - library or unmap it, and again when the mapping change is complete. - The debugger can set a breakpoint at this address if it wants to - notice shared object mapping changes. */ - ElfW(Addr) r_brk; - enum { - /* This state value describes the mapping change taking place when - the `r_brk' address is called. */ - RT_CONSISTENT, /* Mapping change is complete. */ - RT_ADD, /* Beginning to add a new object. */ - RT_DELETE /* Beginning to remove an object mapping. */ - } r_state; - - ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */ -}; - extern struct pal_sec { /* system variables */ unsigned int process_id; int random_device; - - /* for debugger */ - void (*_dl_debug_state)(void); - struct r_debug* _r_debug; } g_pal_sec; #define RANDGEN_DEVICE "/dev/urandom" diff --git a/Pal/src/host/Skeleton/db_rtld.c b/Pal/src/host/Skeleton/db_rtld.c index 8e2c304b..abdac790 100644 --- a/Pal/src/host/Skeleton/db_rtld.c +++ b/Pal/src/host/Skeleton/db_rtld.c @@ -2,22 +2,11 @@ /* Copyright (C) 2014 Stony Brook University */ /* - * This file contains utilities to load ELF binaries into the memory and link them against each - * other. + * This file contains host-specific code related to linking and reporting ELFs to debugger. */ -#include - -#include "api.h" -#include "elf/elf.h" -#include "pal.h" -#include "pal_debug.h" -#include "pal_defs.h" -#include "pal_error.h" -#include "pal_internal.h" #include "pal_rtld.h" -#include "sysdeps/generic/ldsodefs.h" -void _DkDebugAddMap(struct link_map* map) {} +void _DkDebugMapAdd(const char* name, void* addr) {} -void _DkDebugDelMap(struct link_map* map) {} +void _DkDebugMapRemove(void* addr) {} diff --git a/Pal/src/pal-symbols b/Pal/src/pal-symbols index 83a201a7..f7654952 100644 --- a/Pal/src/pal-symbols +++ b/Pal/src/pal-symbols @@ -43,8 +43,8 @@ DkSegmentRegisterSet DkStreamChangeName DkStreamAttributesSetByHandle DkMemoryAvailableQuota -DkDebugAttachBinary -DkDebugDetachBinary +DkDebugMapAdd +DkDebugMapRemove DkAttestationReport DkAttestationQuote DkSetProtectedFilesKey diff --git a/Pal/src/pal_rtld.h b/Pal/src/pal_rtld.h index 793ea3b2..f2c5d1c7 100644 --- a/Pal/src/pal_rtld.h +++ b/Pal/src/pal_rtld.h @@ -152,8 +152,8 @@ ElfW(Sym)* do_lookup_map(ElfW(Sym)* ref, const char* undef_name, const uint_fast unsigned long int elf_hash, const struct link_map* map); /* for GDB debugging */ -void _DkDebugAddMap(struct link_map* map); -void _DkDebugDelMap(struct link_map* map); +void _DkDebugMapAdd(const char* name, void* addr); +void _DkDebugMapRemove(void* addr); noreturn void start_execution(const char** arguments, const char** environs); diff --git a/Runtime/meson.build b/Runtime/meson.build index 9d200f8e..1f66c10a 100644 --- a/Runtime/meson.build +++ b/Runtime/meson.build @@ -5,6 +5,9 @@ if direct install_data('../Pal/src/host/Linux/libpal.so', install_dir: join_paths(pkglibexecdir, 'linux')) install_subdir('../Pal/src/host/Linux/gdb_integration', + install_dir: join_paths(pkglibexecdir, 'linux'), + exclude_files: ['debug_map_gdb.py']) + install_subdir('../Pal/gdb_integration', install_dir: join_paths(pkglibexecdir, 'linux')) hostpalpath_linux = join_paths(prefix, pkglibexecdir, 'linux') @@ -29,6 +32,9 @@ if sgx '../Pal/src/host/Linux-SGX/libpal.so', install_dir: join_paths(pkglibexecdir, 'linux-sgx')) install_subdir('../Pal/src/host/Linux-SGX/gdb_integration', + install_dir: join_paths(pkglibexecdir, 'linux-sgx'), + exclude_files: ['debug_map_gdb.py']) + install_subdir('../Pal/gdb_integration', install_dir: join_paths(pkglibexecdir, 'linux-sgx')) hostpalpath_linux_sgx = join_paths(prefix, pkglibexecdir, 'linux-sgx') diff --git a/Runtime/pal_loader b/Runtime/pal_loader index 038752f1..4354515f 100755 --- a/Runtime/pal_loader +++ b/Runtime/pal_loader @@ -77,9 +77,11 @@ if [ "$GDB" == "1" ]; then PREFIX+=("-i=mi") fi if [ 0"$SGX" -gt 0 ]; then + PREFIX+=("-x" "$HOST_PAL_PATH/gdb_integration/debug_map_gdb.py") PREFIX+=("-x" "$HOST_PAL_PATH/gdb_integration/graphene_sgx_gdb.py") ENVS+=("LD_PRELOAD=$HOST_PAL_PATH/gdb_integration/sgx_gdb.so:$LD_PRELOAD") else + PREFIX+=("-x" "$HOST_PAL_PATH/gdb_integration/debug_map_gdb.py") PREFIX+=("-x" "$HOST_PAL_PATH/gdb_integration/graphene_gdb.py") fi if [ "$GDB_SCRIPT" != "" ]; then diff --git a/Scripts/regression.py b/Scripts/regression.py index df6abc55..1d982a1f 100644 --- a/Scripts/regression.py +++ b/Scripts/regression.py @@ -40,6 +40,7 @@ class RegressionTestCase(unittest.TestCase): # See also pal_loader. prefix = ['gdb', '-q'] env = os.environ.copy() + prefix += ['-x', os.path.join(host_pal_path, 'gdb_integration/debug_map_gdb.py')] if HAS_SGX: prefix += ['-x', os.path.join(host_pal_path, 'gdb_integration/graphene_sgx_gdb.py')] sgx_gdb = os.path.join(host_pal_path, 'gdb_integration/sgx_gdb.so')