1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <elf.h>
6#include <limits.h>
7#include <stdarg.h>
8#include <stdio.h>
9#include <string.h>
10
11#include <zircon/syscalls.h>
12#include <zircon/syscalls/object.h>
13#include <zircon/status.h>
14
15#include "inspector/inspector.h"
16#include "utils-impl.h"
17
18namespace inspector {
19
20int verbosity_level = 0;
21
22extern "C"
23void inspector_set_verbosity(int level) {
24    verbosity_level = level;
25}
26
27// Same as basename, except will not modify |path|.
28// Returns "" if |path| has a trailing /.
29
30const char* path_basename(const char* path) {
31    const char* base = strrchr(path, '/');
32    if (base == nullptr)
33        return path;
34    return base + 1;
35}
36
37void do_print_debug(const char* file, int line, const char* func, const char* fmt, ...) {
38    fflush(stdout);
39    const char* base = path_basename(file);
40    va_list args;
41    va_start(args, fmt);
42    fprintf(stderr, "%s:%d: %s: ", base, line, func);
43    vfprintf(stderr, fmt, args);
44    va_end(args);
45    fflush(stderr); // TODO: output is getting lost
46}
47
48void do_print_error(const char* file, int line, const char* fmt, ...) {
49    const char* base = path_basename(file);
50    va_list args;
51    va_start(args, fmt);
52    fprintf(stderr, "inspector: %s:%d: ", base, line);
53    vfprintf(stderr, fmt, args);
54    fprintf(stderr, "\n");
55    va_end(args);
56}
57
58void do_print_zx_error(const char* file, int line, const char* what, zx_status_t status) {
59    do_print_error(file, line, "%s: %d (%s)",
60                   what, status, zx_status_get_string(status));
61}
62
63zx_status_t read_mem(zx_handle_t h, zx_vaddr_t vaddr, void* ptr, size_t len) {
64    size_t actual;
65    zx_status_t status = zx_process_read_memory(h, vaddr, ptr, len, &actual);
66    if (status < 0) {
67        printf("read_mem @%p FAILED %zd\n", (void*) vaddr, len);
68        return status;
69    }
70    if (len != actual) {
71        printf("read_mem @%p FAILED, short read %zd\n", (void*) vaddr, len);
72        return ZX_ERR_IO;
73    }
74    return ZX_OK;
75}
76
77zx_status_t fetch_string(zx_handle_t h, zx_vaddr_t vaddr, char* ptr, size_t max) {
78    while (max > 1) {
79        zx_status_t status;
80        if ((status = read_mem(h, vaddr, ptr, 1)) < 0) {
81            *ptr = 0;
82            return status;
83        }
84        ptr++;
85        vaddr++;
86        max--;
87    }
88    *ptr = 0;
89    return ZX_OK;
90}
91
92#if UINT_MAX == ULONG_MAX
93
94#define ehdr_off_phoff offsetof(Elf32_Ehdr, e_phoff)
95#define ehdr_off_phnum offsetof(Elf32_Ehdr, e_phnum)
96
97#define phdr_off_type offsetof(Elf32_Phdr, p_type)
98#define phdr_off_offset offsetof(Elf32_Phdr, p_offset)
99#define phdr_off_filesz offsetof(Elf32_Phdr, p_filesz)
100
101typedef Elf32_Half elf_half_t;
102typedef Elf32_Off elf_off_t;
103// ELF used "word" for 32 bits, sigh.
104typedef Elf32_Word elf_word_t;
105typedef Elf32_Word elf_native_word_t;
106typedef Elf32_Phdr elf_phdr_t;
107
108#else
109
110#define ehdr_off_phoff offsetof(Elf64_Ehdr, e_phoff)
111#define ehdr_off_phnum offsetof(Elf64_Ehdr, e_phnum)
112
113#define phdr_off_type offsetof(Elf64_Phdr, p_type)
114#define phdr_off_offset offsetof(Elf64_Phdr, p_offset)
115#define phdr_off_filesz offsetof(Elf64_Phdr, p_filesz)
116
117typedef Elf64_Half elf_half_t;
118typedef Elf64_Off elf_off_t;
119typedef Elf64_Word elf_word_t;
120typedef Elf64_Xword elf_native_word_t;
121typedef Elf64_Phdr elf_phdr_t;
122
123#endif
124
125zx_status_t fetch_build_id(zx_handle_t h, zx_vaddr_t base, char* buf, size_t buf_size) {
126    zx_vaddr_t vaddr = base;
127    uint8_t tmp[4];
128    zx_status_t status;
129
130    if (buf_size < MAX_BUILDID_SIZE * 2 + 1)
131        return ZX_ERR_INVALID_ARGS;
132
133    status = read_mem(h, vaddr, tmp, 4);
134    if (status != ZX_OK)
135        return status;
136    if (memcmp(tmp, ELFMAG, SELFMAG))
137        return ZX_ERR_WRONG_TYPE;
138
139    elf_off_t phoff;
140    elf_half_t num;
141    status = read_mem(h, vaddr + ehdr_off_phoff, &phoff, sizeof(phoff));
142    if (status != ZX_OK)
143        return status;
144    status = read_mem(h, vaddr + ehdr_off_phnum, &num, sizeof(num));
145    if (status != ZX_OK)
146        return status;
147
148    for (unsigned n = 0; n < num; n++) {
149        zx_vaddr_t phaddr = vaddr + phoff + (n * sizeof(elf_phdr_t));
150        elf_word_t type;
151        status = read_mem(h, phaddr + phdr_off_type, &type, sizeof(type));
152        if (status != ZX_OK)
153            return status;
154        if (type != PT_NOTE)
155            continue;
156
157        elf_off_t off;
158        elf_native_word_t size;
159        status = read_mem(h, phaddr + phdr_off_offset, &off, sizeof(off));
160        if (status != ZX_OK)
161            return status;
162        status = read_mem(h, phaddr + phdr_off_filesz, &size, sizeof(size));
163        if (status != ZX_OK)
164            return status;
165
166        struct {
167            Elf32_Nhdr hdr;
168            char name[sizeof("GNU")];
169        } hdr;
170        while (size > sizeof(hdr)) {
171            status = read_mem(h, vaddr + off, &hdr, sizeof(hdr));
172            if (status != ZX_OK)
173                return status;
174            size_t header_size =
175                sizeof(Elf32_Nhdr) + ((hdr.hdr.n_namesz + 3) & -4);
176            size_t payload_size = (hdr.hdr.n_descsz + 3) & -4;
177            off += header_size;
178            size -= header_size;
179            zx_vaddr_t payload_vaddr = vaddr + off;
180            off += payload_size;
181            size -= payload_size;
182            if (hdr.hdr.n_type != NT_GNU_BUILD_ID ||
183                hdr.hdr.n_namesz != sizeof("GNU") ||
184                memcmp(hdr.name, "GNU", sizeof("GNU")) != 0) {
185                continue;
186            }
187            if (hdr.hdr.n_descsz > MAX_BUILDID_SIZE) {
188                snprintf(buf, buf_size,
189                         "build_id_too_large_%u", hdr.hdr.n_descsz);
190            } else {
191                uint8_t buildid[MAX_BUILDID_SIZE];
192                status = read_mem(h, payload_vaddr, buildid, hdr.hdr.n_descsz);
193                if (status != ZX_OK)
194                    return status;
195                for (uint32_t i = 0; i < hdr.hdr.n_descsz; ++i) {
196                    snprintf(&buf[i * 2], 3, "%02x", buildid[i]);
197                }
198            }
199            return ZX_OK;
200        }
201    }
202
203    return ZX_ERR_NOT_FOUND;
204}
205
206}  // namespace inspector
207