Path: blob/master/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c
41152 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/23#include <stdarg.h>24#include <stdio.h>25#include <stdlib.h>26#include <string.h>27#include <fcntl.h>28#include <sys/procfs.h>29#include "libproc_impl.h"30#include "proc_service.h"31#include "salibelf.h"3233#define SA_ALTROOT "SA_ALTROOT"3435int pathmap_open(const char* name) {36static const char *alt_root = NULL;37static int alt_root_initialized = 0;3839int fd;40char alt_path[PATH_MAX + 1], *alt_path_end;41const char *s;42int free_space;4344if (!alt_root_initialized) {45alt_root_initialized = -1;46alt_root = getenv(SA_ALTROOT);47}4849if (alt_root == NULL) {50return open(name, O_RDONLY);51}525354if (strlen(alt_root) + strlen(name) > PATH_MAX) {55// Buffer too small.56return -1;57}5859strncpy(alt_path, alt_root, PATH_MAX);60alt_path[PATH_MAX] = '\0';61alt_path_end = alt_path + strlen(alt_path);62free_space = PATH_MAX + 1 - (alt_path_end-alt_path);6364// Strip path items one by one and try to open file with alt_root prepended.65s = name;66while (1) {67strncat(alt_path, s, free_space);68s += 1; // Skip /.6970fd = open(alt_path, O_RDONLY);71if (fd >= 0) {72print_debug("path %s substituted for %s\n", alt_path, name);73return fd;74}7576// Linker always put full path to solib to process, so we can rely77// on presence of /. If slash is not present, it means, that SOlib doesn't78// physically exist (e.g. linux-gate.so) and we fail opening it anyway79if ((s = strchr(s, '/')) == NULL) {80break;81}8283// Cut off what we appended above.84*alt_path_end = '\0';85}8687return -1;88}8990static bool _libsaproc_debug;9192void print_debug(const char* format,...) {93if (_libsaproc_debug) {94va_list alist;9596va_start(alist, format);97fputs("libsaproc DEBUG: ", stderr);98vfprintf(stderr, format, alist);99va_end(alist);100}101}102103void print_error(const char* format,...) {104va_list alist;105va_start(alist, format);106fputs("ERROR: ", stderr);107vfprintf(stderr, format, alist);108va_end(alist);109}110111bool is_debug() {112return _libsaproc_debug;113}114115// initialize libproc116JNIEXPORT bool JNICALL117init_libproc(bool debug) {118// init debug mode119_libsaproc_debug = debug;120return true;121}122123static void destroy_lib_info(struct ps_prochandle* ph) {124lib_info* lib = ph->libs;125while (lib) {126lib_info *next = lib->next;127if (lib->symtab) {128destroy_symtab(lib->symtab);129}130free(lib->eh_frame.data);131free(lib);132lib = next;133}134}135136static void destroy_thread_info(struct ps_prochandle* ph) {137thread_info* thr = ph->threads;138while (thr) {139thread_info *next = thr->next;140free(thr);141thr = next;142}143}144145// ps_prochandle cleanup146147// ps_prochandle cleanup148JNIEXPORT void JNICALL149Prelease(struct ps_prochandle* ph) {150// do the "derived class" clean-up first151ph->ops->release(ph);152destroy_lib_info(ph);153destroy_thread_info(ph);154free(ph);155}156157lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base) {158return add_lib_info_fd(ph, libname, -1, base);159}160161static inline uintptr_t align_down(uintptr_t ptr, size_t page_size) {162return (ptr & ~(page_size - 1));163}164165static inline uintptr_t align_up(uintptr_t ptr, size_t page_size) {166return ((ptr + page_size - 1) & ~(page_size - 1));167}168169static bool fill_addr_info(lib_info* lib) {170off_t current_pos;171ELF_EHDR ehdr;172ELF_PHDR* phbuf = NULL;173ELF_PHDR* ph = NULL;174int cnt;175176current_pos = lseek(lib->fd, (off_t)0L, SEEK_CUR);177lseek(lib->fd, (off_t)0L, SEEK_SET);178read_elf_header(lib->fd, &ehdr);179if ((phbuf = read_program_header_table(lib->fd, &ehdr)) == NULL) {180lseek(lib->fd, current_pos, SEEK_SET);181return false;182}183184lib->end = (uintptr_t)-1L;185lib->exec_start = (uintptr_t)-1L;186lib->exec_end = (uintptr_t)-1L;187for (ph = phbuf, cnt = 0; cnt < ehdr.e_phnum; cnt++, ph++) {188if (ph->p_type == PT_LOAD) {189uintptr_t unaligned_start = lib->base + ph->p_vaddr;190uintptr_t aligned_start = align_down(unaligned_start, ph->p_align);191uintptr_t aligned_end = align_up(unaligned_start + ph->p_memsz, ph->p_align);192if ((lib->end == (uintptr_t)-1L) || (lib->end < aligned_end)) {193lib->end = aligned_end;194}195print_debug("%s [%d] 0x%lx-0x%lx: base = 0x%lx, "196"vaddr = 0x%lx, memsz = 0x%lx, filesz = 0x%lx\n",197lib->name, cnt, aligned_start, aligned_end, lib->base,198ph->p_vaddr, ph->p_memsz, ph->p_filesz);199if (ph->p_flags & PF_X) {200if ((lib->exec_start == -1L) || (lib->exec_start > aligned_start)) {201lib->exec_start = aligned_start;202}203if ((lib->exec_end == (uintptr_t)-1L) || (lib->exec_end < aligned_end)) {204lib->exec_end = aligned_end;205}206}207}208}209210free(phbuf);211lseek(lib->fd, current_pos, SEEK_SET);212213return (lib->end != -1L) && (lib->exec_start != -1L) && (lib->exec_end != -1L);214}215216bool read_eh_frame(struct ps_prochandle* ph, lib_info* lib) {217off_t current_pos = -1;218ELF_EHDR ehdr;219ELF_SHDR* shbuf = NULL;220ELF_SHDR* sh = NULL;221char* strtab = NULL;222void* result = NULL;223int cnt;224225current_pos = lseek(lib->fd, (off_t)0L, SEEK_CUR);226lseek(lib->fd, (off_t)0L, SEEK_SET);227228read_elf_header(lib->fd, &ehdr);229shbuf = read_section_header_table(lib->fd, &ehdr);230strtab = read_section_data(lib->fd, &ehdr, &shbuf[ehdr.e_shstrndx]);231232for (cnt = 0, sh = shbuf; cnt < ehdr.e_shnum; cnt++, sh++) {233if (strcmp(".eh_frame", sh->sh_name + strtab) == 0) {234lib->eh_frame.library_base_addr = lib->base;235lib->eh_frame.v_addr = sh->sh_addr;236lib->eh_frame.data = read_section_data(lib->fd, &ehdr, sh);237lib->eh_frame.size = sh->sh_size;238break;239}240}241242free(strtab);243free(shbuf);244lseek(lib->fd, current_pos, SEEK_SET);245return lib->eh_frame.data != NULL;246}247248lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) {249lib_info* newlib;250251if ( (newlib = (lib_info*) calloc(1, sizeof(struct lib_info))) == NULL) {252print_debug("can't allocate memory for lib_info\n");253return NULL;254}255256if (strlen(libname) >= sizeof(newlib->name)) {257print_debug("libname %s too long\n", libname);258free(newlib);259return NULL;260}261strcpy(newlib->name, libname);262263newlib->base = base;264265if (fd == -1) {266if ( (newlib->fd = pathmap_open(newlib->name)) < 0) {267print_debug("can't open shared object %s\n", newlib->name);268free(newlib);269return NULL;270}271} else {272newlib->fd = fd;273}274275// check whether we have got an ELF file. /proc/<pid>/map276// gives out all file mappings and not just shared objects277if (is_elf_file(newlib->fd) == false) {278close(newlib->fd);279free(newlib);280return NULL;281}282283newlib->symtab = build_symtab(newlib->fd, libname);284if (newlib->symtab == NULL) {285print_debug("symbol table build failed for %s\n", newlib->name);286}287288if (fill_addr_info(newlib)) {289if (!read_eh_frame(ph, newlib)) {290print_debug("Could not find .eh_frame section in %s\n", newlib->name);291}292} else {293print_debug("Could not find executable section in %s\n", newlib->name);294}295296// even if symbol table building fails, we add the lib_info.297// This is because we may need to read from the ELF file for core file298// address read functionality. lookup_symbol checks for NULL symtab.299if (ph->libs) {300ph->lib_tail->next = newlib;301ph->lib_tail = newlib;302} else {303ph->libs = ph->lib_tail = newlib;304}305ph->num_libs++;306307return newlib;308}309310// lookup for a specific symbol311uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name,312const char* sym_name) {313// ignore object_name. search in all libraries314// FIXME: what should we do with object_name?? The library names are obtained315// by parsing /proc/<pid>/maps, which may not be the same as object_name.316// What we need is a utility to map object_name to real file name, something317// dlopen() does by looking at LD_LIBRARY_PATH and /etc/ld.so.cache. For318// now, we just ignore object_name and do a global search for the symbol.319320lib_info* lib = ph->libs;321while (lib) {322if (lib->symtab) {323uintptr_t res = search_symbol(lib->symtab, lib->base, sym_name, NULL);324if (res) return res;325}326lib = lib->next;327}328329print_debug("lookup failed for symbol '%s' in obj '%s'\n",330sym_name, object_name);331return (uintptr_t) NULL;332}333334335const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset) {336const char* res = NULL;337lib_info* lib = ph->libs;338while (lib) {339if (lib->symtab && addr >= lib->base) {340res = nearest_symbol(lib->symtab, addr - lib->base, poffset);341if (res) return res;342}343lib = lib->next;344}345return NULL;346}347348// add a thread to ps_prochandle349thread_info* add_thread_info(struct ps_prochandle* ph, lwpid_t lwp_id) {350thread_info* newthr;351if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) {352print_debug("can't allocate memory for thread_info\n");353return NULL;354}355356// initialize thread info357newthr->lwp_id = lwp_id;358359// add new thread to the list360newthr->next = ph->threads;361ph->threads = newthr;362ph->num_threads++;363return newthr;364}365366void delete_thread_info(struct ps_prochandle* ph, thread_info* thr_to_be_removed) {367thread_info* current_thr = ph->threads;368369if (thr_to_be_removed == ph->threads) {370ph->threads = ph->threads->next;371} else {372thread_info* previous_thr = NULL;373while (current_thr && current_thr != thr_to_be_removed) {374previous_thr = current_thr;375current_thr = current_thr->next;376}377if (current_thr == NULL) {378print_error("Could not find the thread to be removed");379return;380}381previous_thr->next = current_thr->next;382}383ph->num_threads--;384free(current_thr);385}386387// get number of threads388int get_num_threads(struct ps_prochandle* ph) {389return ph->num_threads;390}391392// get lwp_id of n'th thread393lwpid_t get_lwp_id(struct ps_prochandle* ph, int index) {394int count = 0;395thread_info* thr = ph->threads;396while (thr) {397if (count == index) {398return thr->lwp_id;399}400count++;401thr = thr->next;402}403return -1;404}405406// get regs for a given lwp407bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct user_regs_struct* regs) {408return ph->ops->get_lwp_regs(ph, lwp_id, regs);409}410411// get number of shared objects412int get_num_libs(struct ps_prochandle* ph) {413return ph->num_libs;414}415416// get name of n'th solib417const char* get_lib_name(struct ps_prochandle* ph, int index) {418int count = 0;419lib_info* lib = ph->libs;420while (lib) {421if (count == index) {422return lib->name;423}424count++;425lib = lib->next;426}427return NULL;428}429430// get base address of a lib431uintptr_t get_lib_base(struct ps_prochandle* ph, int index) {432int count = 0;433lib_info* lib = ph->libs;434while (lib) {435if (count == index) {436return lib->base;437}438count++;439lib = lib->next;440}441return (uintptr_t)NULL;442}443444// get address range of lib445void get_lib_addr_range(struct ps_prochandle* ph, int index, uintptr_t* base, uintptr_t* memsz) {446int count = 0;447lib_info* lib = ph->libs;448while (lib) {449if (count == index) {450*base = lib->base;451*memsz = lib->end - lib->base;452return;453}454count++;455lib = lib->next;456}457}458459bool find_lib(struct ps_prochandle* ph, const char *lib_name) {460lib_info *p = ph->libs;461while (p) {462if (strcmp(p->name, lib_name) == 0) {463return true;464}465p = p->next;466}467return false;468}469470struct lib_info *find_lib_by_address(struct ps_prochandle* ph, uintptr_t pc) {471lib_info *p = ph->libs;472while (p) {473if ((p->exec_start <= pc) && (pc < p->exec_end)) {474return p;475}476p = p->next;477}478return NULL;479}480481//--------------------------------------------------------------------------482// proc service functions483484// get process id485JNIEXPORT pid_t JNICALL486ps_getpid(struct ps_prochandle *ph) {487return ph->pid;488}489490// ps_pglobal_lookup() looks up the symbol sym_name in the symbol table491// of the load object object_name in the target process identified by ph.492// It returns the symbol's value as an address in the target process in493// *sym_addr.494495JNIEXPORT ps_err_e JNICALL496ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name,497const char *sym_name, psaddr_t *sym_addr) {498*sym_addr = (psaddr_t) lookup_symbol(ph, object_name, sym_name);499return (*sym_addr ? PS_OK : PS_NOSYM);500}501502// read "size" bytes info "buf" from address "addr"503JNIEXPORT ps_err_e JNICALL504ps_pdread(struct ps_prochandle *ph, psaddr_t addr,505void *buf, size_t size) {506return ph->ops->p_pread(ph, (uintptr_t) addr, buf, size)? PS_OK: PS_ERR;507}508509// write "size" bytes of data to debuggee at address "addr"510JNIEXPORT ps_err_e JNICALL511ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr,512const void *buf, size_t size) {513return ph->ops->p_pwrite(ph, (uintptr_t)addr, buf, size)? PS_OK: PS_ERR;514}515516// ------------------------------------------------------------------------517// Functions below this point are not yet implemented. They are here only518// to make the linker happy.519520JNIEXPORT ps_err_e JNICALL521ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lid, const prfpregset_t *fpregs) {522print_debug("ps_lsetfpregs not implemented\n");523return PS_OK;524}525526JNIEXPORT ps_err_e JNICALL527ps_lsetregs(struct ps_prochandle *ph, lwpid_t lid, const prgregset_t gregset) {528print_debug("ps_lsetregs not implemented\n");529return PS_OK;530}531532JNIEXPORT ps_err_e JNICALL533ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs) {534print_debug("ps_lgetfpregs not implemented\n");535return PS_OK;536}537538JNIEXPORT ps_err_e JNICALL539ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset) {540print_debug("ps_lgetfpregs not implemented\n");541return PS_OK;542}543544545