1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright (c) 1994, by Sun Microsytems, Inc.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 /*
  29  * Interfaces for searching for elf specific information
  30  */
  31 
  32 #include <unistd.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 #include <errno.h>
  36 #include <link.h>
  37 #include <sys/procfs.h>
  38 
  39 #include "tnfctl_int.h"
  40 #include "dbg.h"
  41 
  42 
  43 /*
  44  * Declarations
  45  */
  46 
  47 static tnfctl_errcode_t dynsec_num(tnfctl_handle_t *hndl, uintptr_t baseaddr,
  48                                         int objfd, int *num_dyn);
  49 static tnfctl_errcode_t elf_dynmatch(Elf *elf, char *strs, Elf_Scn *dyn_scn,
  50         GElf_Shdr *dyn_shdr, Elf_Data *dyn_data,
  51         uintptr_t baseaddr, tnfctl_elf_search_t * search_info_p);
  52 static tnfctl_errcode_t dyn_findtag(
  53         Elf3264_Dyn     *start,         /* start of dynam table read in */
  54         Elf3264_Sword   tag,            /* tag to search for */
  55         uintptr_t       dynam_addr,     /* address of _DYNAMIC in target */
  56         int             limit,          /* number of entries in table */
  57         uintptr_t       *dentry_address);       /* return value */
  58 
  59 
  60 /* ---------------------------------------------------------------- */
  61 /* ----------------------- Public Functions ----------------------- */
  62 /* ---------------------------------------------------------------- */
  63 
  64 /*
  65  * _tnfctl_elf_dbgent() - this function finds the address of the
  66  * debug struct (DT_DEBUG) in the target process.  _DYNAMIC is a symbol
  67  * present in every object.  The one in the main executable references
  68  * an array that is tagged with the kind of each member.  We search
  69  * for the tag of DT_DEBUG which is where the run time linker maintains
  70  * a structure that references the shared object linked list.
  71  *
  72  * A side effect of searching for DT_DEBUG ensures that the executable is
  73  * a dynamic executable - tracing only works on dynamic executables because
  74  * static executables don't have relocation tables.
  75  */
  76 tnfctl_errcode_t
  77 _tnfctl_elf_dbgent(tnfctl_handle_t *hndl, uintptr_t * entaddr_p)
  78 {
  79         tnfctl_errcode_t        prexstat = TNFCTL_ERR_NONE;
  80         prb_status_t    prbstat = PRB_STATUS_OK;
  81         int             miscstat;
  82         int             objfd;
  83         int             num_dynentries = 0;
  84         uintptr_t       dynamic_addr;
  85         uintptr_t       baseaddr;
  86         uintptr_t       dentry_addr;
  87         Elf3264_Dyn     *dynam_tab = NULL;
  88         long            dynam_tab_size;
  89 
  90         *entaddr_p = (uintptr_t)NULL;
  91 
  92         prbstat = prb_mainobj_get(hndl->proc_p, &objfd, &baseaddr);
  93         if (prbstat)
  94                 return (_tnfctl_map_to_errcode(prbstat));
  95 
  96         /* find the address of the symbol _DYNAMIC */
  97         prexstat = _tnfctl_sym_find_in_obj(objfd, baseaddr, "_DYNAMIC",
  98                         &dynamic_addr);
  99         if (prexstat) {
 100                 prexstat = TNFCTL_ERR_NOTDYNAMIC;
 101                 goto Cleanup;
 102         }
 103 
 104         /* find the number of entries in the .dynamic section */
 105         prexstat = dynsec_num(hndl, baseaddr, objfd, &num_dynentries);
 106         if (prexstat)
 107                 goto Cleanup;
 108 
 109         DBG_TNF_PROBE_2(_tnfctl_elf_dbgent_1, "libtnfctl", "sunw%verbosity 2",
 110                 tnf_long, num_of_dynentries, num_dynentries,
 111                 tnf_opaque, DYNAMIC_address, dynamic_addr);
 112 
 113         /* read in the dynamic table from the image of the process */
 114         dynam_tab_size = num_dynentries * sizeof (Elf3264_Dyn);
 115         dynam_tab = malloc(dynam_tab_size);
 116         if (!dynam_tab) {
 117                 close(objfd);
 118                 return (TNFCTL_ERR_ALLOCFAIL);
 119         }
 120         miscstat = hndl->p_read(hndl->proc_p, dynamic_addr, dynam_tab,
 121                                                         dynam_tab_size);
 122         if (miscstat) {
 123                 prexstat = TNFCTL_ERR_INTERNAL;
 124                 goto Cleanup;
 125         }
 126 
 127         prexstat = dyn_findtag(dynam_tab, DT_DEBUG, dynamic_addr,
 128                 num_dynentries, &dentry_addr);
 129         if (prexstat) {
 130                 goto Cleanup;
 131         }
 132         *entaddr_p = dentry_addr;
 133 
 134 Cleanup:
 135         close(objfd);
 136         if (dynam_tab)
 137                 free(dynam_tab);
 138         return (prexstat);
 139 
 140 }
 141 
 142 
 143 /* ---------------------------------------------------------------- */
 144 /* ----------------------- Private Functions ---------------------- */
 145 /* ---------------------------------------------------------------- */
 146 
 147 /*
 148  * dyn_findtag() - searches tags in _DYNAMIC table
 149  */
 150 static tnfctl_errcode_t
 151 dyn_findtag(Elf3264_Dyn * start,        /* start of dynam table read in */
 152                 Elf3264_Sword tag,      /* tag to search for */
 153                 uintptr_t dynam_addr,   /* base address of _DYNAMIC in target */
 154                 int limit, /* number of entries in table */
 155                 uintptr_t * dentry_address)
 156 {                               /* return value */
 157         Elf3264_Dyn       *dp;
 158 
 159         for (dp = start; dp->d_tag != DT_NULL; dp++) {
 160 
 161                 DBG_TNF_PROBE_1(dyn_findtag_1, "libtnfctl",
 162                         "sunw%verbosity 3; sunw%debug 'in loop'",
 163                         tnf_long, tag, dp->d_tag);
 164 
 165                 if (dp->d_tag == tag) {
 166                         *dentry_address = dynam_addr +
 167                                 (dp - start) * sizeof (Elf3264_Dyn);
 168                         return (TNFCTL_ERR_NONE);
 169                 }
 170                 if (--limit <= 0) {
 171                         DBG((void) fprintf(stderr,
 172                                 "dyn_findtag: exceeded limit of table\n"));
 173                         return (TNFCTL_ERR_INTERNAL);
 174                 }
 175         }
 176 
 177         DBG((void) fprintf(stderr,
 178                 "dyn_findtag: couldn't find tag, last tag=%d\n",
 179                 (int) dp->d_tag));
 180         return (TNFCTL_ERR_INTERNAL);
 181 }
 182 
 183 
 184 /*
 185  * dynsec_num() - find the number of entries in the .dynamic section
 186  */
 187 /*ARGSUSED*/
 188 static tnfctl_errcode_t
 189 dynsec_num(tnfctl_handle_t *hndl, uintptr_t baseaddr,
 190         int objfd, int *num_dyn)
 191 {
 192         int             num_ent = 0;
 193         tnfctl_errcode_t        prexstat;
 194         tnfctl_elf_search_t search_info;
 195 
 196         DBG_TNF_PROBE_0(dynsec_num_1, "libtnfctl",
 197                 "sunw%verbosity 2;"
 198                 "sunw%debug 'counting number of entries in .dynamic section'");
 199 
 200         search_info.section_func = elf_dynmatch;
 201         search_info.section_data = &num_ent;
 202 
 203         prexstat = _tnfctl_traverse_object(objfd, baseaddr, &search_info);
 204         if (prexstat)
 205                 return (prexstat);
 206 
 207         if (num_ent == 0)
 208                 return (TNFCTL_ERR_NOTDYNAMIC);
 209 
 210         *num_dyn = num_ent;
 211 
 212         return (TNFCTL_ERR_NONE);
 213 }
 214 
 215 
 216 /*
 217  * elf_dynmatch() - this function searches for the .dynamic section and
 218  * returns the number of entries in it.
 219  */
 220 /*ARGSUSED*/
 221 static tnfctl_errcode_t
 222 elf_dynmatch(Elf * elf,
 223         char *strs,
 224         Elf_Scn * dyn_scn,
 225         GElf_Shdr * dyn_shdr,
 226         Elf_Data * dyn_data,
 227         uintptr_t baseaddr,
 228         tnfctl_elf_search_t *search_info_p)
 229 {
 230         char    *scn_name;
 231         int     *ret = (int *) search_info_p->section_data;
 232 
 233         /* bail if this isn't a .dynamic section */
 234         scn_name = strs + dyn_shdr->sh_name;
 235         if (strcmp(scn_name, ".dynamic") != 0)
 236                 return (TNFCTL_ERR_NONE);
 237 
 238         if (dyn_shdr->sh_entsize == 0) {     /* no dynamic section */
 239                 *ret = 0;
 240         } else {
 241                 *ret = (int) (dyn_shdr->sh_size / dyn_shdr->sh_entsize);
 242         }
 243         return (TNFCTL_ERR_NONE);
 244 }