/* $ ./a.out `pgrep top` getpid=4922 child=4344 DEBUG=0x7f7ff7e107c0 r_version = 1 l_addr = 0x200000, l_name = 'top', l_ld = 0x410e00 l_addr = 0x7f7ff7800000, l_name = '/usr/lib/libterminfo.so.2', l_ld = 0x7f7ff7a0de10 l_addr = 0x7f7ff7400000, l_name = '/usr/lib/libm.so.0', l_ld = 0x7f7ff762ae50 l_addr = 0x7f7ff7000000, l_name = '/usr/lib/libkvm.so.6', l_ld = 0x7f7ff7207e60 l_addr = 0x7f7ff6c00000, l_name = '/usr/lib/libutil.so.7', l_ld = 0x7f7ff6e18e10 l_addr = 0x7f7ff6600000, l_name = '/usr/lib/libc.so.12', l_ld = 0x7f7ff69d7760 l_addr = 0x7f7ff5c00000, l_name = '/usr/lib/nss_mdnsd.so.0', l_ld = 0x7f7ff5e03e58 l_addr = 0x7f7ff5800000, l_name = '/usr/lib/libdns_sd.so.0', l_ld = 0x7f7ff5a07e80 l_addr = 0x7f7ff7c00000, l_name = '/usr/libexec/ld.elf_so', l_ld = 0x7f7ff7e0ff00 r_brk = 0x7f7ff7c00ce0 r_state = CONSISTENT */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ATF_CHECK(x) assert(x) #define MIN(x,y) ((x) >= (y) ? y : x) static void * copyin(pid_t pid, void *offs, size_t len) { struct ptrace_io_desc pio; void *buf; size_t n; buf = malloc(len); pio.piod_op = PIOD_READ_D; pio.piod_offs = offs; pio.piod_addr = buf; pio.piod_len = len; n = 0; for (;;) { errno = 0; ptrace(PT_IO, pid, &pio, 0); if (pio.piod_len == 0 || errno != 0) { /* EOF */ break; } n += pio.piod_len; if (len == pio.piod_len) break; pio.piod_offs = (void *)((intptr_t)offs + n); pio.piod_len = len - n; pio.piod_addr = (void *)((intptr_t)(void *)buf + n); } if (n != len) { free(buf); return NULL; } return buf; } static char * copyinstr(pid_t pid, void *offs) { struct ptrace_io_desc pio; char *buf = NULL; size_t bufchunks = 1; const size_t bufchunklen = 32; size_t i, n; bool canonical; reallocarr(&buf, bufchunks, bufchunklen); pio.piod_op = PIOD_READ_D; pio.piod_offs = offs; pio.piod_addr = buf; pio.piod_len = bufchunklen; n = 0; canonical = false; for (;;) { errno = 0; ptrace(PT_IO, pid, &pio, 0); if (pio.piod_len == 0 || errno != 0) { /* EOF */ break; } for (i = 0; i < MIN(bufchunklen, pio.piod_len); i++) { if (((char *)pio.piod_addr)[i] == '\0') { canonical = true; break; } } if (canonical) break; pio.piod_offs = (void *) ((intptr_t)pio.piod_offs + pio.piod_len); n += pio.piod_len; reallocarr(&buf, ++bufchunks, bufchunklen); pio.piod_addr = (void *)&buf[n]; } if (canonical) return buf; /* Free the buffer */ reallocarr(&buf, 0, bufchunklen); return NULL; } static long int getauxval(unsigned int type, pid_t pid) { char auxv[4096]; struct ptrace_io_desc pio; pio.piod_op = PIOD_READ_AUXV; pio.piod_offs = NULL; pio.piod_addr = auxv; pio.piod_len = sizeof(auxv); ptrace(PT_IO, pid, &pio, 0); const AuxInfo *aux; for (aux = (AuxInfo *)auxv; aux->a_type != AT_NULL; ++aux) { if (type == aux->a_type) return aux->a_v; } return 0; } static Elf_Dyn * get_dynamic_section(pid_t pid) { char buf[1024]; uintptr_t relocbase = (uintptr_t)~0U; const Elf_Phdr *phdr; Elf_Half phnum; const Elf_Phdr *phlimit, *dynphdr; phdr = (void *)getauxval(AT_PHDR, pid); phnum = (Elf_Half)getauxval(AT_PHNUM, pid); ATF_CHECK(phdr != NULL); ATF_CHECK(phnum != (Elf_Half)~0); phlimit = phdr + phnum; dynphdr = NULL; for (; phdr < phlimit; ++phdr) { Elf_Phdr *phdr_cpy = copyin(pid, (void *)phdr, sizeof(*phdr)); if (phdr_cpy->p_type == PT_DYNAMIC) dynphdr = phdr; if (phdr_cpy->p_type == PT_PHDR) relocbase = (uintptr_t)phdr - phdr_cpy->p_vaddr; free(phdr_cpy); } Elf_Phdr *dynphdr_cpy = copyin(pid, (void *)dynphdr, sizeof(*phdr)); Elf_Dyn *ret = (Elf_Dyn *)((char *)(dynphdr_cpy->p_vaddr) + relocbase); free(dynphdr_cpy); return ret; } static struct r_debug * get_rtld_r_debug(pid_t pid) { struct r_debug *debug; Elf_Dyn *dynp; for (dynp = get_dynamic_section(pid);; dynp++) { Elf_Dyn *dynp_cpy = copyin(pid, (void *)dynp, sizeof(*dynp)); // printf("dynp_cpy->d_tag=%d dynp_cpy->d_un.d_val=%#llx\n", (long long int)dynp_cpy->d_tag); if (dynp_cpy->d_tag == DT_DEBUG) { debug = (void *)dynp_cpy->d_un.d_val; free(dynp_cpy); break; } if (dynp_cpy->d_tag == DT_NULL) { free(dynp_cpy); break; } free(dynp_cpy); } ATF_CHECK(debug != NULL); return debug; } int main(int argc, char **argv) { struct r_debug *r_debug; int status; pid_t child, wchild; child = atoi(argv[1]); printf("getpid=%d child=%d\n", getpid(), child); ptrace(PT_ATTACH, child, NULL, 0); #if 0 ATF_CHECK(child != -1); if (child == 0) { ptrace(PT_TRACE_ME, 0, NULL, 0); // raise(SIGSTOP); execlp("/bin/echo", "/bin/echo", NULL); abort(); _exit(0); } wchild = waitpid(child, &status, 0); ATF_CHECK(wchild != -1); ATF_CHECK(wchild == child); ATF_CHECK(WIFSTOPPED(status)); ATF_CHECK(WSTOPSIG(status) == SIGTRAP); #endif // ptrace(PT_CONTINUE, child, (void *)1, 0); // wait(&status); wait(&status); r_debug = get_rtld_r_debug(child); printf("DEBUG=%p\n", r_debug); struct r_debug *r_debug_cpy = copyin(child, (void *)r_debug, sizeof(*r_debug)); printf("r_version = %d\n", r_debug_cpy->r_version); struct link_map *map = r_debug_cpy->r_map; for (;;) { struct link_map *map_cpy = copyin(child, (void *)map, sizeof(*map)); char *l_name_cpy = copyinstr(child, (void *)map_cpy->l_name); printf("l_addr = %p, l_name = '%s', l_ld = %p\n", map_cpy->l_addr, l_name_cpy, map_cpy->l_ld); map = map_cpy->l_next; free(l_name_cpy); free(map_cpy); if (map == NULL) break; } #if 0 for (map = r_debug->r_map; map; map = map->l_next) { printf("l_addr = %p, l_name = '%s', l_ld = %p\n", map->l_addr, map->l_name, map->l_ld); } #endif printf("r_brk = %p\n", r_debug_cpy->r_brk); const char *r_state; switch (r_debug_cpy->r_state) { case RT_CONSISTENT: r_state = "CONSISTENT"; break; case RT_ADD: r_state = "ADD"; break; case RT_DELETE: r_state = "DELETE"; break; default: r_state = "???"; } printf("r_state = %s\n", r_state); free(r_debug_cpy); ptrace(PT_DETACH, child, (void *)1, 0); return EXIT_SUCCESS; }