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 
18 namespace inspector {
19 
20 int verbosity_level = 0;
21 
22 extern "C"
inspector_set_verbosity(int level)23 void 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 
path_basename(const char * path)30 const 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 
do_print_debug(const char * file,int line,const char * func,const char * fmt,...)37 void 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 
do_print_error(const char * file,int line,const char * fmt,...)48 void 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 
do_print_zx_error(const char * file,int line,const char * what,zx_status_t status)58 void 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 
read_mem(zx_handle_t h,zx_vaddr_t vaddr,void * ptr,size_t len)63 zx_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 
fetch_string(zx_handle_t h,zx_vaddr_t vaddr,char * ptr,size_t max)77 zx_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 
101 typedef Elf32_Half elf_half_t;
102 typedef Elf32_Off elf_off_t;
103 // ELF used "word" for 32 bits, sigh.
104 typedef Elf32_Word elf_word_t;
105 typedef Elf32_Word elf_native_word_t;
106 typedef 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 
117 typedef Elf64_Half elf_half_t;
118 typedef Elf64_Off elf_off_t;
119 typedef Elf64_Word elf_word_t;
120 typedef Elf64_Xword elf_native_word_t;
121 typedef Elf64_Phdr elf_phdr_t;
122 
123 #endif
124 
fetch_build_id(zx_handle_t h,zx_vaddr_t base,char * buf,size_t buf_size)125 zx_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