| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fcntl.h> |
| #include <link.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/types.h> |
| #include <zircon/syscalls.h> |
| |
| #include "dso-list.h" |
| #include "utils.h" |
| |
| #define rdebug_off_lmap offsetof(struct r_debug, r_map) |
| |
| #define lmap_off_next offsetof(struct link_map, l_next) |
| #define lmap_off_name offsetof(struct link_map, l_name) |
| #define lmap_off_addr offsetof(struct link_map, l_addr) |
| |
| const char kDebugDirectory[] = "/boot/debug"; |
| const char kDebugSuffix[] = ".debug"; |
| |
| static dsoinfo_t* dsolist_add(dsoinfo_t** list, const char* name, uintptr_t base) { |
| if (!strncmp(name, "app:devhost:", 12)) { |
| // devhost processes use their name field to describe |
| // the root of their device sub-tree. |
| name = "app:/boot/bin/devhost"; |
| } |
| size_t len = strlen(name); |
| auto dso = reinterpret_cast<dsoinfo_t*> (calloc(1, sizeof(dsoinfo_t) + len + 1)); |
| if (dso == nullptr) { |
| return nullptr; |
| } |
| memcpy(dso->name, name, len + 1); |
| memset(dso->buildid, 'x', sizeof(dso->buildid) - 1); |
| dso->base = base; |
| dso->debug_file_tried = false; |
| dso->debug_file_status = ZX_ERR_BAD_STATE; |
| while (*list != nullptr) { |
| if ((*list)->base < dso->base) { |
| dso->next = *list; |
| *list = dso; |
| return dso; |
| } |
| list = &((*list)->next); |
| } |
| *list = dso; |
| dso->next = nullptr; |
| return dso; |
| } |
| |
| dsoinfo_t* dso_fetch_list(zx_handle_t h, const char* name) { |
| uintptr_t lmap, debug_addr; |
| zx_status_t status = zx_object_get_property(h, ZX_PROP_PROCESS_DEBUG_ADDR, |
| &debug_addr, sizeof(debug_addr)); |
| if (status != ZX_OK) { |
| print_zx_error("zx_object_get_property(ZX_PROP_PROCESS_DEBUG_ADDR), unable to fetch dso list", status); |
| return nullptr; |
| } |
| if (read_mem(h, debug_addr + rdebug_off_lmap, &lmap, sizeof(lmap))) { |
| return nullptr; |
| } |
| dsoinfo_t* dsolist = nullptr; |
| int iter = 0; |
| while (lmap != 0) { |
| if (iter++ > 50) { |
| print_error("dso_fetch_list detected too many entries, possible infinite loop"); |
| return nullptr; |
| } |
| char dsoname[64]; |
| zx_vaddr_t base; |
| uintptr_t next; |
| uintptr_t str; |
| if (read_mem(h, lmap + lmap_off_addr, &base, sizeof(base))) { |
| break; |
| } |
| if (read_mem(h, lmap + lmap_off_next, &next, sizeof(next))) { |
| break; |
| } |
| if (read_mem(h, lmap + lmap_off_name, &str, sizeof(str))) { |
| break; |
| } |
| if (fetch_string(h, str, dsoname, sizeof(dsoname))) { |
| break; |
| } |
| dsoinfo_t* dso = dsolist_add(&dsolist, dsoname[0] ? dsoname : name, base); |
| if (dso != nullptr) { |
| fetch_build_id(h, dso->base, dso->buildid, sizeof(dso->buildid)); |
| } |
| lmap = next; |
| } |
| |
| return dsolist; |
| } |
| |
| void dso_free_list(dsoinfo_t* list) { |
| while (list != NULL) { |
| dsoinfo_t* next = list->next; |
| free(list->debug_file); |
| free(list); |
| list = next; |
| } |
| } |
| |
| dsoinfo_t* dso_lookup(dsoinfo_t* dso_list, zx_vaddr_t pc) { |
| for (dsoinfo_t* dso = dso_list; dso != NULL; dso = dso->next) { |
| if (pc >= dso->base) { |
| return dso; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| void dso_print_list(dsoinfo_t* dso_list) { |
| for (dsoinfo_t* dso = dso_list; dso != nullptr; dso = dso->next) { |
| printf("dso: id=%s base=%p name=%s\n", |
| dso->buildid, (void*) dso->base, dso->name); |
| } |
| } |
| |
| zx_status_t dso_find_debug_file(dsoinfo_t* dso, const char** out_debug_file) { |
| // Have we already tried? |
| // Yeah, if we OOM it's possible it'll succeed next time, but |
| // it's not worth the extra complexity to avoid printing the debugging |
| // messages twice. |
| if (dso->debug_file_tried) { |
| switch (dso->debug_file_status) { |
| case ZX_OK: |
| ZX_DEBUG_ASSERT(dso->debug_file != nullptr); |
| *out_debug_file = dso->debug_file; |
| // fall through |
| default: |
| debugf(2, "returning %d, already tried to find debug file for %s\n", |
| dso->debug_file_status, dso->name); |
| return dso->debug_file_status; |
| } |
| } |
| |
| dso->debug_file_tried = true; |
| |
| char* path; |
| if (asprintf(&path, "%s/%s%s", kDebugDirectory, dso->buildid, kDebugSuffix) < 0) { |
| debugf(1, "OOM building debug file path for dso %s\n", dso->name); |
| dso->debug_file_status = ZX_ERR_NO_MEMORY; |
| return dso->debug_file_status; |
| } |
| |
| debugf(1, "looking for debug file %s\n", path); |
| |
| int fd = open(path, O_RDONLY); |
| if (fd < 0) { |
| debugf(1, "debug file for dso %s not found: %s\n", dso->name, path); |
| free(path); |
| dso->debug_file_status = ZX_ERR_NOT_FOUND; |
| } else { |
| debugf(1, "found debug file for dso %s: %s\n", dso->name, path); |
| close(fd); |
| dso->debug_file = path; |
| *out_debug_file = path; |
| dso->debug_file_status = ZX_OK; |
| } |
| |
| return dso->debug_file_status; |
| } |