xref: /titanic_51/usr/src/lib/libtnfctl/elf.c (revision 45818ee124adeaaf947698996b4f4c722afc6d1f)
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 = 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 }
245