| // 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 |