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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * ELF support routines for processing versioned mon.out files.
30 */
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include "profv.h"
35
36 bool
is_shared_obj(char * name)37 is_shared_obj(char *name)
38 {
39 int fd;
40 Elf *elf;
41 GElf_Ehdr ehdr;
42
43 if ((fd = open(name, O_RDONLY)) == -1) {
44 (void) fprintf(stderr, "%s: can't open `%s'\n", cmdname, name);
45 exit(ERR_ELF);
46 }
47
48 if (elf_version(EV_CURRENT) == EV_NONE) {
49 (void) fprintf(stderr, "%s: libelf out of date\n", cmdname);
50 exit(ERR_ELF);
51 }
52
53 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
54 (void) fprintf(stderr, "%s: elf_begin failed\n", cmdname);
55 exit(ERR_ELF);
56 }
57
58 if (gelf_getehdr(elf, &ehdr) == NULL) {
59 (void) fprintf(stderr, "%s: can't read ELF header of %s\n",
60 cmdname, name);
61 exit(ERR_ELF);
62 }
63
64 (void) elf_end(elf);
65 (void) close(fd);
66
67 if (ehdr.e_type == ET_DYN)
68 return (TRUE);
69 else
70 return (FALSE);
71 }
72
73 static void
rm_dups(nltype * nl,size_t * nfuncs)74 rm_dups(nltype *nl, size_t *nfuncs)
75 {
76 size_t i, prev = 0, ndx = 0;
77 int prev_type, prev_bind, cur_type;
78
79 for (i = 1; i < *nfuncs; i++) {
80 /*
81 * If current value is different from prev, proceed.
82 */
83 if (nl[prev].value < nl[i].value) {
84 prev = i;
85 continue;
86 }
87
88 /*
89 * If current and prev have the syminfo, rm the latter.
90 */
91 if (nl[prev].info == nl[i].info) {
92 nl[i].name = NULL;
93 continue;
94 }
95
96 prev_type = ELF_ST_TYPE(nl[prev].info);
97 prev_bind = ELF_ST_BIND(nl[prev].info);
98 cur_type = ELF_ST_TYPE(nl[i].info);
99
100 /*
101 * Remove the one with STT_NOTYPE and keep the other.
102 */
103 if (prev_type != cur_type) {
104 if (prev_type != STT_NOTYPE)
105 nl[i].name = NULL;
106 else {
107 nl[prev].name = NULL;
108 prev = i;
109 }
110 continue;
111 }
112
113 /*
114 * If they have the same type, take the stronger bound
115 * function
116 */
117 if (prev_bind != STB_WEAK)
118 nl[i].name = NULL;
119 else {
120 nl[prev].name = NULL;
121 prev = i;
122 }
123 }
124
125
126 /*
127 * Actually remove the cleared symbols from namelist. We're not
128 * truncating namelist by realloc, though.
129 */
130 for (i = 0; (i < *nfuncs) && (nl[i].name != NULL); i++)
131 ;
132
133 ndx = i;
134 for (i = ndx + 1; i < *nfuncs; i++) {
135 if (nl[i].name) {
136 nl[ndx] = nl[i];
137 ndx++;
138 }
139 }
140
141 *nfuncs = ndx;
142 }
143
144 int
cmp_by_address(const void * arg1,const void * arg2)145 cmp_by_address(const void *arg1, const void *arg2)
146 {
147 nltype *a = (nltype *)arg1;
148 nltype *b = (nltype *)arg2;
149
150 if (a->value < b->value)
151 return (-1);
152 else if (a->value > b->value)
153 return (1);
154 else
155 return (0);
156 }
157
158 static int
is_function(Elf * elf,GElf_Sym * sym)159 is_function(Elf *elf, GElf_Sym *sym)
160 {
161 Elf_Scn *scn;
162 GElf_Shdr shdr;
163
164 /*
165 * With dynamic linking, it is possible that certain undefined
166 * symbols exist in the objects. The actual definition will be
167 * found elsewhere, so we'll just skip it for this object.
168 */
169 if (sym->st_shndx == SHN_UNDEF)
170 return (0);
171
172 if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {
173 if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL)
174 return (1);
175
176 if (GELF_ST_BIND(sym->st_info) == STB_WEAK)
177 return (1);
178
179 if (gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)
180 return (1);
181 }
182
183 /*
184 * It's not a function; determine if it's in an executable section.
185 */
186 if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
187 return (0);
188
189 /*
190 * If it isn't global, and it isn't weak, and it isn't
191 * a 'local with the gflag set', then get out.
192 */
193 if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
194 GELF_ST_BIND(sym->st_info) != STB_WEAK &&
195 !(gflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL))
196 return (0);
197
198 if (sym->st_shndx >= SHN_LORESERVE)
199 return (0);
200
201 scn = elf_getscn(elf, sym->st_shndx);
202 (void) gelf_getshdr(scn, &shdr);
203
204 if (!(shdr.sh_flags & SHF_EXECINSTR))
205 return (0);
206
207 return (1);
208 }
209
210 static void
fetch_symtab(Elf * elf,char * filename,mod_info_t * module)211 fetch_symtab(Elf *elf, char *filename, mod_info_t *module)
212 {
213 Elf_Scn *scn = NULL, *sym_pri = NULL, *sym_aux = NULL;
214 GElf_Word strndx = 0;
215 size_t i, nsyms, nfuncs;
216 GElf_Xword nsyms_pri, nsyms_aux = 0;
217 Elf_Data *symdata_pri, *symdata_aux;
218 nltype *nl, *npe;
219 int symtab_found = 0;
220
221
222 /*
223 * Scan the section headers looking for a symbol table. Our
224 * preference is to use .symtab, because it contains the full
225 * set of symbols. If we find it, we stop looking immediately
226 * and use it. In the absence of a .symtab section, we are
227 * willing to use the dynamic symbol table (.dynsym), possibly
228 * augmented by the .SUNW_ldynsym, which contains local symbols.
229 */
230 while ((symtab_found == 0) && ((scn = elf_nextscn(elf, scn)) != NULL)) {
231
232 GElf_Shdr shdr;
233
234 if (gelf_getshdr(scn, &shdr) == NULL)
235 continue;
236
237 switch (shdr.sh_type) {
238 case SHT_SYMTAB:
239 nsyms_pri = shdr.sh_size / shdr.sh_entsize;
240 strndx = shdr.sh_link;
241 sym_pri = scn;
242 /* Throw away .SUNW_ldynsym. It is for .dynsym only */
243 nsyms_aux = 0;
244 sym_aux = NULL;
245 /* We have found the best symbol table. Stop looking */
246 symtab_found = 1;
247 break;
248
249 case SHT_DYNSYM:
250 /* We will use .dynsym if no .symtab is found */
251 nsyms_pri = shdr.sh_size / shdr.sh_entsize;
252 strndx = shdr.sh_link;
253 sym_pri = scn;
254 break;
255
256 case SHT_SUNW_LDYNSYM:
257 /* Auxiliary table, used with .dynsym */
258 nsyms_aux = shdr.sh_size / shdr.sh_entsize;
259 sym_aux = scn;
260 break;
261 }
262 }
263
264 if (sym_pri == NULL || strndx == 0) {
265 (void) fprintf(stderr, "%s: missing symbol table in %s\n",
266 cmdname, filename);
267 exit(ERR_ELF);
268 }
269
270 nsyms = (size_t)(nsyms_pri + nsyms_aux);
271 if ((nsyms_pri + nsyms_aux) != (GElf_Xword)nsyms) {
272 (void) fprintf(stderr,
273 "%s: can't handle more than 2^32 symbols", cmdname);
274 exit(ERR_INPUT);
275 }
276
277 if ((symdata_pri = elf_getdata(sym_pri, NULL)) == NULL) {
278 (void) fprintf(stderr, "%s: can't read symbol data from %s\n",
279 cmdname, filename);
280 exit(ERR_ELF);
281 }
282
283 if ((sym_aux != NULL) &&
284 ((symdata_aux = elf_getdata(sym_aux, NULL)) == NULL)) {
285 (void) fprintf(stderr,
286 "%s: can't read .SUNW_ldynsym symbol data from %s\n",
287 cmdname, filename);
288 exit(ERR_ELF);
289 }
290
291 if ((npe = nl = (nltype *) calloc(nsyms, sizeof (nltype))) == NULL) {
292 (void) fprintf(stderr, "%s: can't alloc %x bytes for symbols\n",
293 cmdname, nsyms * sizeof (nltype));
294 exit(ERR_ELF);
295 }
296
297 /*
298 * Now we need to cruise through the symbol table eliminating
299 * all non-functions from consideration, and making strings
300 * real.
301 */
302 nfuncs = 0;
303
304 for (i = 1; i < nsyms; i++) {
305 GElf_Sym gsym;
306 char *name;
307
308 /*
309 * Look up the symbol. In the case where we have a
310 * .SUNW_ldynsym/.dynsym pair, we treat them as a single
311 * logical table, with the data in .SUNW_ldynsym coming
312 * before the data in .dynsym.
313 */
314 if (i >= nsyms_aux)
315 (void) gelf_getsym(symdata_pri, i - nsyms_aux, &gsym);
316 else
317 (void) gelf_getsym(symdata_aux, i, &gsym);
318
319 name = elf_strptr(elf, strndx, gsym.st_name);
320
321 /*
322 * We're interested in this symbol if it's a function
323 */
324 if (is_function(elf, &gsym)) {
325
326 npe->name = name;
327 npe->value = gsym.st_value;
328 npe->size = gsym.st_size;
329 npe->info = gsym.st_info;
330
331 npe++;
332 nfuncs++;
333 }
334
335 if (strcmp(name, PRF_END) == 0)
336 module->data_end = gsym.st_value;
337 }
338
339 if (npe == nl) {
340 (void) fprintf(stderr, "%s: no valid functions in %s\n",
341 cmdname, filename);
342 exit(ERR_INPUT);
343 }
344
345 /*
346 * And finally, sort the symbols by increasing address
347 * and remove the duplicates.
348 */
349 qsort(nl, nfuncs, sizeof (nltype), cmp_by_address);
350 rm_dups(nl, &nfuncs);
351
352 module->nl = nl;
353 module->nfuncs = nfuncs;
354 }
355
356 static GElf_Addr
get_txtorigin(Elf * elf,char * filename)357 get_txtorigin(Elf *elf, char *filename)
358 {
359 GElf_Ehdr ehdr;
360 GElf_Phdr phdr;
361 GElf_Half ndx;
362 GElf_Addr txt_origin = 0;
363 bool first_load_seg = TRUE;
364
365 if (gelf_getehdr(elf, &ehdr) == NULL) {
366 (void) fprintf(stderr, "%s: can't read ELF header of %s\n",
367 cmdname, filename);
368 exit(ERR_ELF);
369 }
370
371 for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
372 if (gelf_getphdr(elf, ndx, &phdr) == NULL)
373 continue;
374
375 if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
376 if (first_load_seg || phdr.p_vaddr < txt_origin)
377 txt_origin = phdr.p_vaddr;
378
379 if (first_load_seg)
380 first_load_seg = FALSE;
381 }
382 }
383
384 return (txt_origin);
385 }
386
387 void
get_syms(char * filename,mod_info_t * mi)388 get_syms(char *filename, mod_info_t *mi)
389 {
390 int fd;
391 Elf *elf;
392
393 if ((fd = open(filename, O_RDONLY)) == -1) {
394 perror(filename);
395 exit(ERR_SYSCALL);
396 }
397
398 if (elf_version(EV_CURRENT) == EV_NONE) {
399 (void) fprintf(stderr, "%s: libelf out of date\n", cmdname);
400 exit(ERR_ELF);
401 }
402
403 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
404 (void) fprintf(stderr, "%s: elf_begin failed\n", cmdname);
405 exit(ERR_ELF);
406 }
407
408 if (gelf_getclass(elf) != ELFCLASS64) {
409 (void) fprintf(stderr, "%s: unsupported mon.out format for "
410 "this class of object\n", cmdname);
411 exit(ERR_ELF);
412 }
413
414 mi->txt_origin = get_txtorigin(elf, filename);
415
416 fetch_symtab(elf, filename, mi);
417 }
418