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')