// 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 #include #include #include #include #include #include #include #include "inspector/inspector.h" #include "utils-impl.h" namespace inspector { int verbosity_level = 0; extern "C" 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