blob: 187c3b2142fa4fa33227c11ea720bf239bebe1a2 [file] [log] [blame]
// 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;
}