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