| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright (c) 1994, by Sun Microsytems, Inc. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * Interfaces for searching for elf specific information |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <link.h> |
| #include <sys/procfs.h> |
| |
| #include "tnfctl_int.h" |
| #include "dbg.h" |
| |
| |
| /* |
| * Declarations |
| */ |
| |
| static tnfctl_errcode_t dynsec_num(tnfctl_handle_t *hndl, uintptr_t baseaddr, |
| int objfd, int *num_dyn); |
| static tnfctl_errcode_t elf_dynmatch(Elf *elf, char *strs, Elf_Scn *dyn_scn, |
| GElf_Shdr *dyn_shdr, Elf_Data *dyn_data, |
| uintptr_t baseaddr, tnfctl_elf_search_t * search_info_p); |
| static tnfctl_errcode_t dyn_findtag( |
| Elf3264_Dyn *start, /* start of dynam table read in */ |
| Elf3264_Sword tag, /* tag to search for */ |
| uintptr_t dynam_addr, /* address of _DYNAMIC in target */ |
| int limit, /* number of entries in table */ |
| uintptr_t *dentry_address); /* return value */ |
| |
| |
| /* ---------------------------------------------------------------- */ |
| /* ----------------------- Public Functions ----------------------- */ |
| /* ---------------------------------------------------------------- */ |
| |
| /* |
| * _tnfctl_elf_dbgent() - this function finds the address of the |
| * debug struct (DT_DEBUG) in the target process. _DYNAMIC is a symbol |
| * present in every object. The one in the main executable references |
| * an array that is tagged with the kind of each member. We search |
| * for the tag of DT_DEBUG which is where the run time linker maintains |
| * a structure that references the shared object linked list. |
| * |
| * A side effect of searching for DT_DEBUG ensures that the executable is |
| * a dynamic executable - tracing only works on dynamic executables because |
| * static executables don't have relocation tables. |
| */ |
| tnfctl_errcode_t |
| _tnfctl_elf_dbgent(tnfctl_handle_t *hndl, uintptr_t * entaddr_p) |
| { |
| tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE; |
| prb_status_t prbstat = PRB_STATUS_OK; |
| int miscstat; |
| int objfd; |
| int num_dynentries = 0; |
| uintptr_t dynamic_addr; |
| uintptr_t baseaddr; |
| uintptr_t dentry_addr; |
| Elf3264_Dyn *dynam_tab = NULL; |
| long dynam_tab_size; |
| |
| *entaddr_p = NULL; |
| |
| prbstat = prb_mainobj_get(hndl->proc_p, &objfd, &baseaddr); |
| if (prbstat) |
| return (_tnfctl_map_to_errcode(prbstat)); |
| |
| /* find the address of the symbol _DYNAMIC */ |
| prexstat = _tnfctl_sym_find_in_obj(objfd, baseaddr, "_DYNAMIC", |
| &dynamic_addr); |
| if (prexstat) { |
| prexstat = TNFCTL_ERR_NOTDYNAMIC; |
| goto Cleanup; |
| } |
| |
| /* find the number of entries in the .dynamic section */ |
| prexstat = dynsec_num(hndl, baseaddr, objfd, &num_dynentries); |
| if (prexstat) |
| goto Cleanup; |
| |
| DBG_TNF_PROBE_2(_tnfctl_elf_dbgent_1, "libtnfctl", "sunw%verbosity 2", |
| tnf_long, num_of_dynentries, num_dynentries, |
| tnf_opaque, DYNAMIC_address, dynamic_addr); |
| |
| /* read in the dynamic table from the image of the process */ |
| dynam_tab_size = num_dynentries * sizeof (Elf3264_Dyn); |
| dynam_tab = malloc(dynam_tab_size); |
| if (!dynam_tab) { |
| close(objfd); |
| return (TNFCTL_ERR_ALLOCFAIL); |
| } |
| miscstat = hndl->p_read(hndl->proc_p, dynamic_addr, dynam_tab, |
| dynam_tab_size); |
| if (miscstat) { |
| prexstat = TNFCTL_ERR_INTERNAL; |
| goto Cleanup; |
| } |
| |
| prexstat = dyn_findtag(dynam_tab, DT_DEBUG, dynamic_addr, |
| num_dynentries, &dentry_addr); |
| if (prexstat) { |
| goto Cleanup; |
| } |
| *entaddr_p = dentry_addr; |
| |
| Cleanup: |
| close(objfd); |
| if (dynam_tab) |
| free(dynam_tab); |
| return (prexstat); |
| |
| } |
| |
| |
| /* ---------------------------------------------------------------- */ |
| /* ----------------------- Private Functions ---------------------- */ |
| /* ---------------------------------------------------------------- */ |
| |
| /* |
| * dyn_findtag() - searches tags in _DYNAMIC table |
| */ |
| static tnfctl_errcode_t |
| dyn_findtag(Elf3264_Dyn * start, /* start of dynam table read in */ |
| Elf3264_Sword tag, /* tag to search for */ |
| uintptr_t dynam_addr, /* base address of _DYNAMIC in target */ |
| int limit, /* number of entries in table */ |
| uintptr_t * dentry_address) |
| { /* return value */ |
| Elf3264_Dyn *dp; |
| |
| for (dp = start; dp->d_tag != DT_NULL; dp++) { |
| |
| DBG_TNF_PROBE_1(dyn_findtag_1, "libtnfctl", |
| "sunw%verbosity 3; sunw%debug 'in loop'", |
| tnf_long, tag, dp->d_tag); |
| |
| if (dp->d_tag == tag) { |
| *dentry_address = dynam_addr + |
| (dp - start) * sizeof (Elf3264_Dyn); |
| return (TNFCTL_ERR_NONE); |
| } |
| if (--limit <= 0) { |
| DBG((void) fprintf(stderr, |
| "dyn_findtag: exceeded limit of table\n")); |
| return (TNFCTL_ERR_INTERNAL); |
| } |
| } |
| |
| DBG((void) fprintf(stderr, |
| "dyn_findtag: couldn't find tag, last tag=%d\n", |
| (int) dp->d_tag)); |
| return (TNFCTL_ERR_INTERNAL); |
| } |
| |
| |
| /* |
| * dynsec_num() - find the number of entries in the .dynamic section |
| */ |
| /*ARGSUSED*/ |
| static tnfctl_errcode_t |
| dynsec_num(tnfctl_handle_t *hndl, uintptr_t baseaddr, |
| int objfd, int *num_dyn) |
| { |
| int num_ent = 0; |
| tnfctl_errcode_t prexstat; |
| tnfctl_elf_search_t search_info; |
| |
| DBG_TNF_PROBE_0(dynsec_num_1, "libtnfctl", |
| "sunw%verbosity 2;" |
| "sunw%debug 'counting number of entries in .dynamic section'"); |
| |
| search_info.section_func = elf_dynmatch; |
| search_info.section_data = &num_ent; |
| |
| prexstat = _tnfctl_traverse_object(objfd, baseaddr, &search_info); |
| if (prexstat) |
| return (prexstat); |
| |
| if (num_ent == 0) |
| return (TNFCTL_ERR_NOTDYNAMIC); |
| |
| *num_dyn = num_ent; |
| |
| return (TNFCTL_ERR_NONE); |
| } |
| |
| |
| /* |
| * elf_dynmatch() - this function searches for the .dynamic section and |
| * returns the number of entries in it. |
| */ |
| /*ARGSUSED*/ |
| static tnfctl_errcode_t |
| elf_dynmatch(Elf * elf, |
| char *strs, |
| Elf_Scn * dyn_scn, |
| GElf_Shdr * dyn_shdr, |
| Elf_Data * dyn_data, |
| uintptr_t baseaddr, |
| tnfctl_elf_search_t *search_info_p) |
| { |
| char *scn_name; |
| int *ret = (int *) search_info_p->section_data; |
| |
| /* bail if this isn't a .dynamic section */ |
| scn_name = strs + dyn_shdr->sh_name; |
| if (strcmp(scn_name, ".dynamic") != 0) |
| return (TNFCTL_ERR_NONE); |
| |
| if (dyn_shdr->sh_entsize == 0) { /* no dynamic section */ |
| *ret = 0; |
| } else { |
| *ret = (int) (dyn_shdr->sh_size / dyn_shdr->sh_entsize); |
| } |
| return (TNFCTL_ERR_NONE); |
| } |