blob: 4e3b2dc4299100d853103ae84f467258d50b7375 [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 <elf.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include <zircon/status.h>
#include "inspector/inspector.h"
#include "utils-impl.h"
namespace inspector {
int verbosity_level = 0;
extern "C" __EXPORT void inspector_set_verbosity(int level) { verbosity_level = level; }
// Same as basename, except will not modify |path|.
// Returns "" if |path| has a trailing /.
const char* path_basename(const char* path) {
const char* base = strrchr(path, '/');
if (base == nullptr)
return path;
return base + 1;
}
void do_print_debug(const char* file, int line, const char* func, const char* fmt, ...) {
fflush(stdout);
const char* base = path_basename(file);
va_list args;
va_start(args, fmt);
fprintf(stderr, "%s:%d: %s: ", base, line, func);
vfprintf(stderr, fmt, args);
va_end(args);
fflush(stderr); // TODO: output is getting lost
}
void do_print_error(const char* file, int line, const char* fmt, ...) {
const char* base = path_basename(file);
va_list args;
va_start(args, fmt);
fprintf(stderr, "inspector: %s:%d: ", base, line);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
void do_print_zx_error(const char* file, int line, const char* what, zx_status_t status) {
do_print_error(file, line, "%s: %d (%s)", what, status, zx_status_get_string(status));
}
zx_status_t read_mem(zx_handle_t h, zx_vaddr_t vaddr, void* ptr, size_t len) {
size_t actual;
zx_status_t status = zx_process_read_memory(h, vaddr, ptr, len, &actual);
if (status < 0) {
printf("read_mem @%p FAILED %zd\n", (void*)vaddr, len);
return status;
}
if (len != actual) {
printf("read_mem @%p FAILED, short read %zd\n", (void*)vaddr, len);
return ZX_ERR_IO;
}
return ZX_OK;
}
zx_status_t fetch_string(zx_handle_t h, zx_vaddr_t vaddr, char* ptr, size_t max) {
while (max > 1) {
zx_status_t status;
if ((status = read_mem(h, vaddr, ptr, 1)) < 0) {
*ptr = 0;
return status;
}
ptr++;
vaddr++;
max--;
}
*ptr = 0;
return ZX_OK;
}
#if UINT_MAX == ULONG_MAX
#define ehdr_off_phoff offsetof(Elf32_Ehdr, e_phoff)
#define ehdr_off_phnum offsetof(Elf32_Ehdr, e_phnum)
#define phdr_off_type offsetof(Elf32_Phdr, p_type)
#define phdr_off_offset offsetof(Elf32_Phdr, p_offset)
#define phdr_off_filesz offsetof(Elf32_Phdr, p_filesz)
typedef Elf32_Half elf_half_t;
typedef Elf32_Off elf_off_t;
// ELF used "word" for 32 bits, sigh.
typedef Elf32_Word elf_word_t;
typedef Elf32_Word elf_native_word_t;
typedef Elf32_Phdr elf_phdr_t;
#else
#define ehdr_off_phoff offsetof(Elf64_Ehdr, e_phoff)
#define ehdr_off_phnum offsetof(Elf64_Ehdr, e_phnum)
#define phdr_off_type offsetof(Elf64_Phdr, p_type)
#define phdr_off_offset offsetof(Elf64_Phdr, p_offset)
#define phdr_off_filesz offsetof(Elf64_Phdr, p_filesz)
typedef Elf64_Half elf_half_t;
typedef Elf64_Off elf_off_t;
typedef Elf64_Word elf_word_t;
typedef Elf64_Xword elf_native_word_t;
typedef Elf64_Phdr elf_phdr_t;
#endif
zx_status_t fetch_build_id(zx_handle_t h, zx_vaddr_t base, char* buf, size_t buf_size) {
zx_vaddr_t vaddr = base;
uint8_t tmp[4];
zx_status_t status;
if (buf_size < MAX_BUILDID_SIZE * 2 + 1)
return ZX_ERR_INVALID_ARGS;
status = read_mem(h, vaddr, tmp, 4);
if (status != ZX_OK)
return status;
if (memcmp(tmp, ELFMAG, SELFMAG))
return ZX_ERR_WRONG_TYPE;
elf_off_t phoff;
elf_half_t num;
status = read_mem(h, vaddr + ehdr_off_phoff, &phoff, sizeof(phoff));
if (status != ZX_OK)
return status;
status = read_mem(h, vaddr + ehdr_off_phnum, &num, sizeof(num));
if (status != ZX_OK)
return status;
for (unsigned n = 0; n < num; n++) {
zx_vaddr_t phaddr = vaddr + phoff + (n * sizeof(elf_phdr_t));
elf_word_t type;
status = read_mem(h, phaddr + phdr_off_type, &type, sizeof(type));
if (status != ZX_OK)
return status;
if (type != PT_NOTE)
continue;
elf_off_t off;
elf_native_word_t size;
status = read_mem(h, phaddr + phdr_off_offset, &off, sizeof(off));
if (status != ZX_OK)
return status;
status = read_mem(h, phaddr + phdr_off_filesz, &size, sizeof(size));
if (status != ZX_OK)
return status;
struct {
Elf32_Nhdr hdr;
char name[sizeof("GNU")];
} hdr;
while (size > sizeof(hdr)) {
status = read_mem(h, vaddr + off, &hdr, sizeof(hdr));
if (status != ZX_OK)
return status;
size_t header_size = sizeof(Elf32_Nhdr) + ((hdr.hdr.n_namesz + 3) & -4);
size_t payload_size = (hdr.hdr.n_descsz + 3) & -4;
off += header_size;
size -= header_size;
zx_vaddr_t payload_vaddr = vaddr + off;
off += payload_size;
size -= payload_size;
if (hdr.hdr.n_type != NT_GNU_BUILD_ID || hdr.hdr.n_namesz != sizeof("GNU") ||
memcmp(hdr.name, "GNU", sizeof("GNU")) != 0) {
continue;
}
if (hdr.hdr.n_descsz > MAX_BUILDID_SIZE) {
snprintf(buf, buf_size, "build_id_too_large_%u", hdr.hdr.n_descsz);
} else {
uint8_t buildid[MAX_BUILDID_SIZE];
status = read_mem(h, payload_vaddr, buildid, hdr.hdr.n_descsz);
if (status != ZX_OK)
return status;
for (uint32_t i = 0; i < hdr.hdr.n_descsz; ++i) {
snprintf(&buf[i * 2], 3, "%02x", buildid[i]);
}
}
return ZX_OK;
}
}
return ZX_ERR_NOT_FOUND;
}
} // namespace inspector