1 | /* |
2 | * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. |
8 | * |
9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
12 | * version 2 for more details (a copy is included in the LICENSE file that |
13 | * accompanied this code). |
14 | * |
15 | * You should have received a copy of the GNU General Public License version |
16 | * 2 along with this work; if not, write to the Free Software Foundation, |
17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
18 | * |
19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
20 | * or visit www.oracle.com if you need additional information or have any |
21 | * questions. |
22 | * |
23 | */ |
24 | #include <stdarg.h> |
25 | #include <stdio.h> |
26 | #include <stdlib.h> |
27 | #include <string.h> |
28 | #include <fcntl.h> |
29 | #include <sys/procfs.h> |
30 | #include "libproc_impl.h" |
31 | #include "proc_service.h" |
32 | |
33 | #define SA_ALTROOT "SA_ALTROOT" |
34 | |
35 | int pathmap_open(const char* name) { |
36 | static const char *alt_root = NULL; |
37 | static int alt_root_initialized = 0; |
38 | |
39 | int fd; |
40 | char alt_path[PATH_MAX + 1], *alt_path_end; |
41 | const char *s; |
42 | int free_space; |
43 | |
44 | if (!alt_root_initialized) { |
45 | alt_root_initialized = -1; |
46 | alt_root = getenv(SA_ALTROOT); |
47 | } |
48 | |
49 | if (alt_root == NULL) { |
50 | return open(name, O_RDONLY); |
51 | } |
52 | |
53 | |
54 | if (strlen(alt_root) + strlen(name) > PATH_MAX) { |
55 | // Buffer too small. |
56 | return -1; |
57 | } |
58 | |
59 | strncpy(alt_path, alt_root, PATH_MAX); |
60 | alt_path[PATH_MAX] = '\0'; |
61 | alt_path_end = alt_path + strlen(alt_path); |
62 | free_space = PATH_MAX + 1 - (alt_path_end-alt_path); |
63 | |
64 | // Strip path items one by one and try to open file with alt_root prepended. |
65 | s = name; |
66 | while (1) { |
67 | strncat(alt_path, s, free_space); |
68 | s += 1; // Skip /. |
69 | |
70 | fd = open(alt_path, O_RDONLY); |
71 | if (fd >= 0) { |
72 | print_debug("path %s substituted for %s\n" , alt_path, name); |
73 | return fd; |
74 | } |
75 | |
76 | // Linker always put full path to solib to process, so we can rely |
77 | // on presence of /. If slash is not present, it means, that SOlib doesn't |
78 | // physically exist (e.g. linux-gate.so) and we fail opening it anyway |
79 | if ((s = strchr(s, '/')) == NULL) { |
80 | break; |
81 | } |
82 | |
83 | // Cut off what we appended above. |
84 | *alt_path_end = '\0'; |
85 | } |
86 | |
87 | return -1; |
88 | } |
89 | |
90 | static bool _libsaproc_debug; |
91 | |
92 | void print_debug(const char* format,...) { |
93 | if (_libsaproc_debug) { |
94 | va_list alist; |
95 | |
96 | va_start(alist, format); |
97 | fputs("libsaproc DEBUG: " , stderr); |
98 | vfprintf(stderr, format, alist); |
99 | va_end(alist); |
100 | } |
101 | } |
102 | |
103 | void print_error(const char* format,...) { |
104 | va_list alist; |
105 | va_start(alist, format); |
106 | fputs("ERROR: " , stderr); |
107 | vfprintf(stderr, format, alist); |
108 | va_end(alist); |
109 | } |
110 | |
111 | bool is_debug() { |
112 | return _libsaproc_debug; |
113 | } |
114 | |
115 | // initialize libproc |
116 | JNIEXPORT bool JNICALL |
117 | init_libproc(bool debug) { |
118 | // init debug mode |
119 | _libsaproc_debug = debug; |
120 | return true; |
121 | } |
122 | |
123 | static void destroy_lib_info(struct ps_prochandle* ph) { |
124 | lib_info* lib = ph->libs; |
125 | while (lib) { |
126 | lib_info *next = lib->next; |
127 | if (lib->symtab) { |
128 | destroy_symtab(lib->symtab); |
129 | } |
130 | free(lib); |
131 | lib = next; |
132 | } |
133 | } |
134 | |
135 | static void destroy_thread_info(struct ps_prochandle* ph) { |
136 | thread_info* thr = ph->threads; |
137 | while (thr) { |
138 | thread_info *next = thr->next; |
139 | free(thr); |
140 | thr = next; |
141 | } |
142 | } |
143 | |
144 | // ps_prochandle cleanup |
145 | |
146 | // ps_prochandle cleanup |
147 | JNIEXPORT void JNICALL |
148 | Prelease(struct ps_prochandle* ph) { |
149 | // do the "derived class" clean-up first |
150 | ph->ops->release(ph); |
151 | destroy_lib_info(ph); |
152 | destroy_thread_info(ph); |
153 | free(ph); |
154 | } |
155 | |
156 | lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base) { |
157 | return add_lib_info_fd(ph, libname, -1, base); |
158 | } |
159 | |
160 | lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) { |
161 | lib_info* newlib; |
162 | |
163 | if ( (newlib = (lib_info*) calloc(1, sizeof(struct lib_info))) == NULL) { |
164 | print_debug("can't allocate memory for lib_info\n" ); |
165 | return NULL; |
166 | } |
167 | |
168 | if (strlen(libname) >= sizeof(newlib->name)) { |
169 | print_debug("libname %s too long\n" , libname); |
170 | free(newlib); |
171 | return NULL; |
172 | } |
173 | strcpy(newlib->name, libname); |
174 | |
175 | newlib->base = base; |
176 | |
177 | if (fd == -1) { |
178 | if ( (newlib->fd = pathmap_open(newlib->name)) < 0) { |
179 | print_debug("can't open shared object %s\n" , newlib->name); |
180 | free(newlib); |
181 | return NULL; |
182 | } |
183 | } else { |
184 | newlib->fd = fd; |
185 | } |
186 | |
187 | // check whether we have got an ELF file. /proc/<pid>/map |
188 | // gives out all file mappings and not just shared objects |
189 | if (is_elf_file(newlib->fd) == false) { |
190 | close(newlib->fd); |
191 | free(newlib); |
192 | return NULL; |
193 | } |
194 | |
195 | newlib->symtab = build_symtab(newlib->fd, libname); |
196 | if (newlib->symtab == NULL) { |
197 | print_debug("symbol table build failed for %s\n" , newlib->name); |
198 | } |
199 | |
200 | // even if symbol table building fails, we add the lib_info. |
201 | // This is because we may need to read from the ELF file for core file |
202 | // address read functionality. lookup_symbol checks for NULL symtab. |
203 | if (ph->libs) { |
204 | ph->lib_tail->next = newlib; |
205 | ph->lib_tail = newlib; |
206 | } else { |
207 | ph->libs = ph->lib_tail = newlib; |
208 | } |
209 | ph->num_libs++; |
210 | |
211 | return newlib; |
212 | } |
213 | |
214 | // lookup for a specific symbol |
215 | uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name, |
216 | const char* sym_name) { |
217 | // ignore object_name. search in all libraries |
218 | // FIXME: what should we do with object_name?? The library names are obtained |
219 | // by parsing /proc/<pid>/maps, which may not be the same as object_name. |
220 | // What we need is a utility to map object_name to real file name, something |
221 | // dlopen() does by looking at LD_LIBRARY_PATH and /etc/ld.so.cache. For |
222 | // now, we just ignore object_name and do a global search for the symbol. |
223 | |
224 | lib_info* lib = ph->libs; |
225 | while (lib) { |
226 | if (lib->symtab) { |
227 | uintptr_t res = search_symbol(lib->symtab, lib->base, sym_name, NULL); |
228 | if (res) return res; |
229 | } |
230 | lib = lib->next; |
231 | } |
232 | |
233 | print_debug("lookup failed for symbol '%s' in obj '%s'\n" , |
234 | sym_name, object_name); |
235 | return (uintptr_t) NULL; |
236 | } |
237 | |
238 | |
239 | const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset) { |
240 | const char* res = NULL; |
241 | lib_info* lib = ph->libs; |
242 | while (lib) { |
243 | if (lib->symtab && addr >= lib->base) { |
244 | res = nearest_symbol(lib->symtab, addr - lib->base, poffset); |
245 | if (res) return res; |
246 | } |
247 | lib = lib->next; |
248 | } |
249 | return NULL; |
250 | } |
251 | |
252 | // add a thread to ps_prochandle |
253 | thread_info* add_thread_info(struct ps_prochandle* ph, lwpid_t lwp_id) { |
254 | thread_info* newthr; |
255 | if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) { |
256 | print_debug("can't allocate memory for thread_info\n" ); |
257 | return NULL; |
258 | } |
259 | |
260 | // initialize thread info |
261 | newthr->lwp_id = lwp_id; |
262 | |
263 | // add new thread to the list |
264 | newthr->next = ph->threads; |
265 | ph->threads = newthr; |
266 | ph->num_threads++; |
267 | return newthr; |
268 | } |
269 | |
270 | void delete_thread_info(struct ps_prochandle* ph, thread_info* thr_to_be_removed) { |
271 | thread_info* current_thr = ph->threads; |
272 | |
273 | if (thr_to_be_removed == ph->threads) { |
274 | ph->threads = ph->threads->next; |
275 | } else { |
276 | thread_info* previous_thr; |
277 | while (current_thr && current_thr != thr_to_be_removed) { |
278 | previous_thr = current_thr; |
279 | current_thr = current_thr->next; |
280 | } |
281 | if (current_thr == NULL) { |
282 | print_error("Could not find the thread to be removed" ); |
283 | return; |
284 | } |
285 | previous_thr->next = current_thr->next; |
286 | } |
287 | ph->num_threads--; |
288 | free(current_thr); |
289 | } |
290 | |
291 | // get number of threads |
292 | int get_num_threads(struct ps_prochandle* ph) { |
293 | return ph->num_threads; |
294 | } |
295 | |
296 | // get lwp_id of n'th thread |
297 | lwpid_t get_lwp_id(struct ps_prochandle* ph, int index) { |
298 | int count = 0; |
299 | thread_info* thr = ph->threads; |
300 | while (thr) { |
301 | if (count == index) { |
302 | return thr->lwp_id; |
303 | } |
304 | count++; |
305 | thr = thr->next; |
306 | } |
307 | return -1; |
308 | } |
309 | |
310 | // get regs for a given lwp |
311 | bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct user_regs_struct* regs) { |
312 | return ph->ops->get_lwp_regs(ph, lwp_id, regs); |
313 | } |
314 | |
315 | // get number of shared objects |
316 | int get_num_libs(struct ps_prochandle* ph) { |
317 | return ph->num_libs; |
318 | } |
319 | |
320 | // get name of n'th solib |
321 | const char* get_lib_name(struct ps_prochandle* ph, int index) { |
322 | int count = 0; |
323 | lib_info* lib = ph->libs; |
324 | while (lib) { |
325 | if (count == index) { |
326 | return lib->name; |
327 | } |
328 | count++; |
329 | lib = lib->next; |
330 | } |
331 | return NULL; |
332 | } |
333 | |
334 | // get base address of a lib |
335 | uintptr_t get_lib_base(struct ps_prochandle* ph, int index) { |
336 | int count = 0; |
337 | lib_info* lib = ph->libs; |
338 | while (lib) { |
339 | if (count == index) { |
340 | return lib->base; |
341 | } |
342 | count++; |
343 | lib = lib->next; |
344 | } |
345 | return (uintptr_t)NULL; |
346 | } |
347 | |
348 | bool find_lib(struct ps_prochandle* ph, const char *lib_name) { |
349 | lib_info *p = ph->libs; |
350 | while (p) { |
351 | if (strcmp(p->name, lib_name) == 0) { |
352 | return true; |
353 | } |
354 | p = p->next; |
355 | } |
356 | return false; |
357 | } |
358 | |
359 | //-------------------------------------------------------------------------- |
360 | // proc service functions |
361 | |
362 | // get process id |
363 | JNIEXPORT pid_t JNICALL |
364 | ps_getpid(struct ps_prochandle *ph) { |
365 | return ph->pid; |
366 | } |
367 | |
368 | // ps_pglobal_lookup() looks up the symbol sym_name in the symbol table |
369 | // of the load object object_name in the target process identified by ph. |
370 | // It returns the symbol's value as an address in the target process in |
371 | // *sym_addr. |
372 | |
373 | JNIEXPORT ps_err_e JNICALL |
374 | ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name, |
375 | const char *sym_name, psaddr_t *sym_addr) { |
376 | *sym_addr = (psaddr_t) lookup_symbol(ph, object_name, sym_name); |
377 | return (*sym_addr ? PS_OK : PS_NOSYM); |
378 | } |
379 | |
380 | // read "size" bytes info "buf" from address "addr" |
381 | JNIEXPORT ps_err_e JNICALL |
382 | ps_pdread(struct ps_prochandle *ph, psaddr_t addr, |
383 | void *buf, size_t size) { |
384 | return ph->ops->p_pread(ph, (uintptr_t) addr, buf, size)? PS_OK: PS_ERR; |
385 | } |
386 | |
387 | // write "size" bytes of data to debuggee at address "addr" |
388 | JNIEXPORT ps_err_e JNICALL |
389 | ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, |
390 | const void *buf, size_t size) { |
391 | return ph->ops->p_pwrite(ph, (uintptr_t)addr, buf, size)? PS_OK: PS_ERR; |
392 | } |
393 | |
394 | // ------------------------------------------------------------------------ |
395 | // Functions below this point are not yet implemented. They are here only |
396 | // to make the linker happy. |
397 | |
398 | JNIEXPORT ps_err_e JNICALL |
399 | ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lid, const prfpregset_t *fpregs) { |
400 | print_debug("ps_lsetfpregs not implemented\n" ); |
401 | return PS_OK; |
402 | } |
403 | |
404 | JNIEXPORT ps_err_e JNICALL |
405 | ps_lsetregs(struct ps_prochandle *ph, lwpid_t lid, const prgregset_t gregset) { |
406 | print_debug("ps_lsetregs not implemented\n" ); |
407 | return PS_OK; |
408 | } |
409 | |
410 | JNIEXPORT ps_err_e JNICALL |
411 | ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs) { |
412 | print_debug("ps_lgetfpregs not implemented\n" ); |
413 | return PS_OK; |
414 | } |
415 | |
416 | JNIEXPORT ps_err_e JNICALL |
417 | ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset) { |
418 | print_debug("ps_lgetfpregs not implemented\n" ); |
419 | return PS_OK; |
420 | } |
421 | |
422 | |