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/*
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <string.h>
31#include <errno.h>
32#include <sys/types.h>
33#include <sys/signal.h>
34#include <sys/fault.h>
35#include <sys/syscall.h>
36#include <procfs.h>
37#include <sys/auxv.h>
38#include <libelf.h>
39#include <sys/stat.h>
40#include <sys/mman.h>
41#include <link.h>
42#include <sys/param.h>
43#include <sys/machelf.h>
44#include <stdarg.h>
45
46#include "rdb.h"
47
48static char *
49conv_lmid(Lmid_t ident, char *buf, size_t len)
50{
51	if (len < 17)
52		return (NULL);
53	if (ident == LM_ID_BASE)
54		return (strncpy(buf, "  BASE  ", len));
55
56	if (ident == LM_ID_LDSO)
57		return (strncpy(buf, "  LDSO  ", len));
58
59	(void) sprintf(buf, "0x%llx", (unsigned long long)ident);
60	return (buf);
61}
62
63map_info_t *
64str_to_map(struct ps_prochandle *ph, const char *soname)
65{
66	map_info_t *mip;
67
68	if (soname == PS_OBJ_LDSO)
69		mip = (map_info_t *)&(ph->pp_ldsomap);
70	else if (soname == PS_OBJ_EXEC)
71		mip = (map_info_t *)&(ph->pp_execmap);
72	else {
73		for (mip = ph->pp_lmaplist.ml_head; mip; mip = mip->mi_next)
74			if (strcmp(soname, mip->mi_name) == 0)
75				break;
76	}
77	return (mip);
78}
79
80map_info_t *
81addr_to_map(struct ps_prochandle *ph, ulong_t addr)
82{
83	map_info_t *mip;
84	if (ph->pp_lmaplist.ml_head == NULL) {
85		/*
86		 * To early to have the full Link Map info available
87		 * so we use the initial info obtained from procfs
88		 */
89		if ((addr >= ph->pp_ldsomap.mi_addr) &&
90		    (addr <= ph->pp_ldsomap.mi_end))
91			return ((map_info_t *)&(ph->pp_ldsomap));
92
93		if ((addr >= ph->pp_execmap.mi_addr) &&
94		    (addr <= ph->pp_execmap.mi_end))
95			return ((map_info_t *)&(ph->pp_execmap));
96
97		return (NULL);
98	}
99
100	for (mip = ph->pp_lmaplist.ml_head; mip; mip = mip->mi_next)
101		if ((addr >= mip->mi_addr) &&
102		    (addr <= mip->mi_end))
103			return (mip);
104
105	return (NULL);
106}
107
108retc_t
109display_linkmaps(struct ps_prochandle *ph)
110{
111	char	flagstr[1024];
112	map_info_t *mip;
113
114	if (ph->pp_lmaplist.ml_head == NULL) {
115		(void) printf("link-maps not yet available\n");
116		return (RET_FAILED);
117	}
118	(void) printf("Link Maps\n");
119	(void) printf("---------\n");
120	for (mip = ph->pp_lmaplist.ml_head; mip; mip = mip->mi_next) {
121		char sbuf[32];
122		rd_loadobj_t *lp = &mip->mi_loadobj;
123		(void) printf("link-map: id: %s name: ",
124		    conv_lmid(lp->rl_lmident, sbuf, 32));
125		if (mip->mi_refname)
126			(void) printf("%s(%s)\n", mip->mi_name,
127			    mip->mi_refname);
128		else
129			(void) printf("%s\n", mip->mi_name);
130
131		(void) printf("       base: 0x%08lx   padd_base: 0x%08lx\n",
132		    lp->rl_base, lp->rl_padstart);
133		(void) printf("  data_base: 0x%08llx\n",
134		    (unsigned long long)lp->rl_data_base);
135		(void) printf("        end: 0x%08lx    padd_end: 0x%08lx\n",
136		    lp->rl_bend, lp->rl_padend);
137		flagstr[0] = '\0';
138
139		if (lp->rl_flags & RD_FLG_MEM_OBJECT) {
140			(void) strcat(flagstr, " MEMOBJECT");
141		}
142		(void) printf("    dynamic: 0x%08lx       flags: "
143		    "0x%08x:[%s ]\n", lp->rl_dynamic, lp->rl_flags, flagstr);
144	}
145
146	return (RET_OK);
147}
148
149retc_t
150display_maps(struct ps_prochandle *ph)
151{
152	struct stat	stbuf;
153	void 		*ptr;
154	prmap_t 	*mapptr;
155
156	if (fstat(ph->pp_mapfd, &stbuf) == -1)
157		perr("stat map");
158
159	ptr = malloc(stbuf.st_size);
160	if (pread(ph->pp_mapfd, ptr, stbuf.st_size, 0) == -1)
161		perr("dm: reading map");
162
163	(void) puts("\nMappings");
164	(void) puts("--------");
165	if (ph->pp_dmodel == PR_MODEL_LP64)
166		(void) puts("addr               size     prot ident name");
167	else
168		(void) puts("addr       size     prot ident name");
169
170	for (mapptr = (prmap_t *)ptr;
171	    (uintptr_t)mapptr < ((uintptr_t)ptr + stbuf.st_size);
172	    mapptr++) {
173		map_info_t *mip;
174
175		if (ph->pp_dmodel == PR_MODEL_LP64)
176			(void) printf("%#18llx %#08llx %#04x",
177			    EC_ADDR(mapptr->pr_vaddr), EC_OFF(mapptr->pr_size),
178			    mapptr->pr_mflags);
179		else
180			(void) printf("0x%08llx 0x%06llx 0x%02x",
181			    EC_ADDR(mapptr->pr_vaddr), EC_OFF(mapptr->pr_size),
182			    mapptr->pr_mflags);
183
184		if ((mip = addr_to_map(ph,
185		    (ulong_t)(mapptr->pr_vaddr))) != NULL) {
186			if (mip->mi_refname) {
187				(void) printf(" 0x%02lx  %s(%s)",
188				    mip->mi_lmident, mip->mi_name,
189				    mip->mi_refname);
190			} else
191				(void) printf(" 0x%02lx  %s", mip->mi_lmident,
192				    mip->mi_name);
193		}
194		(void) putchar('\n');
195	}
196	(void) putchar('\n');
197
198	free(ptr);
199	return (RET_OK);
200}
201
202retc_t
203load_map(struct ps_prochandle *procp, caddr_t baddr, map_info_t *mp)
204{
205	Elf 		*elf;
206	GElf_Ehdr 	ehdr;
207	GElf_Phdr	phdr;
208	Elf_Scn 	*scn = NULL;
209	int		cnt;
210	prmap_t 	*mapptr;
211	void 		*ptr;
212	struct stat	stbuf;
213	int		filefd = -1;
214
215	if (fstat(procp->pp_mapfd, &stbuf) == -1)
216		perr("stat map");
217
218	ptr = malloc(stbuf.st_size);
219	if (pread(procp->pp_mapfd, ptr, stbuf.st_size, 0) == -1)
220		perr("dm: reading map");
221
222	for (mapptr = (prmap_t *)ptr;
223	    (uintptr_t)mapptr < ((uintptr_t)ptr + stbuf.st_size);
224	    mapptr++) {
225
226		if ((mapptr->pr_vaddr <= (uintptr_t)baddr) &&
227		    ((mapptr->pr_vaddr + mapptr->pr_size) >
228		    (uintptr_t)baddr)) {
229			if (mapptr->pr_mapname[0]) {
230				char	procname[MAXPATHLEN];
231
232				(void) snprintf(procname, MAXPATHLEN - 1,
233				    "/proc/%d/object/%s", procp->pp_pid,
234				    mapptr->pr_mapname);
235				filefd = open(procname, O_RDONLY);
236			}
237			break;
238		}
239	}
240	free(ptr);
241
242	if (filefd == -1) {
243		(void) fprintf(stderr, "unable to find file association to "
244		    "mapping address 0x%08llx\n", EC_NATPTR(baddr));
245		return (RET_FAILED);
246	}
247
248	if ((elf = elf_begin(filefd, ELF_C_READ, 0)) == NULL) {
249		(void) fprintf(stderr, "elf_begin(): %s\n", elf_errmsg(-1));
250		return (RET_FAILED);
251	}
252
253	if (elf_kind(elf) != ELF_K_ELF) {
254		(void) printf("non-elf file\n");
255		(void) elf_end(elf);
256		return (RET_FAILED);
257	}
258
259	mp->mi_elf = elf;
260	mp->mi_flags = 0;
261	mp->mi_mapfd = filefd;
262
263	if (gelf_getehdr(mp->mi_elf, &ehdr) == NULL) {
264		(void) printf("gelf_getehdr(): %s\n", elf_errmsg(-1));
265		(void) elf_end(mp->mi_elf);
266		return (RET_FAILED);
267	}
268	mp->mi_ehdr = ehdr;
269	if (ehdr.e_type == ET_EXEC)
270		mp->mi_flags |= FLG_MI_EXEC;
271
272	mp->mi_end = 0;
273#if	defined(_ELF64)
274	mp->mi_addr = (ulong_t)0xffffffffffffffff;
275#else
276	mp->mi_addr = (ulong_t)0xffffffff;
277#endif
278	for (cnt = 0; cnt < (int)(ehdr.e_phnum); cnt++) {
279		if (gelf_getphdr(mp->mi_elf, cnt, &phdr) == NULL) {
280			(void) printf("gelf_getphdr(): %s\n", elf_errmsg(-1));
281			(void) elf_end(mp->mi_elf);
282			return (RET_FAILED);
283		}
284
285		if (phdr.p_type == PT_LOAD) {
286			if (mp->mi_end < (ulong_t)(phdr.p_vaddr +
287			    phdr.p_memsz))
288				mp->mi_end = (ulong_t)(phdr.p_vaddr +
289				    phdr.p_memsz);
290			if (mp->mi_addr > phdr.p_vaddr)
291				mp->mi_addr = phdr.p_vaddr;
292		}
293	}
294
295	mp->mi_pltbase = 0;
296	mp->mi_pltsize = 0;
297	mp->mi_pltentsz = 0;
298	mp->mi_dynsym.st_symn = 0;
299	while ((scn = elf_nextscn(mp->mi_elf, scn)) != NULL) {
300		GElf_Shdr 	shdr;
301		Elf_Data	*dp;
302		Elf_Scn		*tscn = NULL;
303
304		if (gelf_getshdr(scn, &shdr) == NULL) {
305			(void) printf("gelf_getshdr(): %s\n", elf_errmsg(-1));
306			(void) elf_end(mp->mi_elf);
307			return (RET_FAILED);
308		}
309
310		switch (shdr.sh_type) {
311		case SHT_DYNSYM:
312			dp = elf_getdata(scn, 0);
313			mp->mi_dynsym.st_syms_pri = dp;
314			tscn = elf_getscn(mp->mi_elf, shdr.sh_link);
315			mp->mi_dynsym.st_symn +=
316			    shdr.sh_size / shdr.sh_entsize;
317			dp = elf_getdata(tscn, 0);
318			mp->mi_dynsym.st_strs = (char *)dp->d_buf;
319			break;
320		case SHT_SUNW_LDYNSYM:
321			dp = elf_getdata(scn, 0);
322			mp->mi_dynsym.st_syms_aux = dp;
323			mp->mi_dynsym.st_symn_aux =
324			    shdr.sh_size / shdr.sh_entsize;
325			mp->mi_dynsym.st_symn += mp->mi_dynsym.st_symn_aux;
326			break;
327		case SHT_SYMTAB:
328			dp = elf_getdata(scn, 0);
329			mp->mi_symtab.st_syms_pri = dp;
330			tscn = elf_getscn(mp->mi_elf, shdr.sh_link);
331			mp->mi_symtab.st_symn =
332			    shdr.sh_size / shdr.sh_entsize;
333			dp = elf_getdata(tscn, 0);
334			mp->mi_symtab.st_strs = (char *)dp->d_buf;
335			break;
336		case PLTSECTT:
337			if (strcmp(PLTSECT, elf_strptr(mp->mi_elf,
338			    ehdr.e_shstrndx, shdr.sh_name)) == 0) {
339				mp->mi_pltbase = shdr.sh_addr;
340				mp->mi_pltsize = shdr.sh_size;
341				mp->mi_pltentsz = shdr.sh_entsize;
342			}
343			break;
344		default:
345			/* nothing */
346			break;
347		}
348	}
349	return (RET_OK);
350}
351
352static int
353map_iter(const rd_loadobj_t *lop, void *cd)
354{
355	struct ps_prochandle 	*ph = (struct ps_prochandle *)cd;
356	map_info_t 		*mip;
357	char			buf[MAXPATHLEN];
358
359	if ((mip = (map_info_t *)calloc(1, sizeof (map_info_t))) == NULL) {
360		(void) fprintf(stderr, "map_iter: memory error: allocation "
361		    "failed\n");
362		return (0);
363	}
364
365	mip->mi_loadobj = *lop;
366
367	if (proc_string_read(ph, lop->rl_nameaddr,
368	    buf, MAXPATHLEN) == RET_FAILED) {
369		(void) fprintf(stderr, "mi: bad object name address "
370		    "passed: 0x%lx\n", lop->rl_nameaddr);
371		free(mip);
372		return (0);
373	}
374	mip->mi_name = strdup(buf);
375
376
377	if (lop->rl_refnameaddr) {
378		if (proc_string_read(ph, lop->rl_refnameaddr, buf,
379		    MAXPATHLEN) == RET_FAILED) {
380			(void) fprintf(stderr, "mi1: bad object name address "
381			    "passed: 0x%lx\n", lop->rl_refnameaddr);
382			free(mip);
383			return (0);
384		}
385		mip->mi_refname = strdup(buf);
386	} else
387		mip->mi_refname = NULL;
388
389	/*
390	 * Relocatable objects are processed to create in-memory shared objects,
391	 * and as such have no file associated with the allocated memory shared
392	 * object.
393	 */
394	if ((lop->rl_flags & RD_FLG_MEM_OBJECT) == 0)
395		(void) load_map(ph, (caddr_t)lop->rl_base, mip);
396	if ((mip->mi_flags & FLG_MI_EXEC) == 0) {
397		mip->mi_end += lop->rl_base;
398		mip->mi_addr += lop->rl_base;
399	}
400	mip->mi_lmident = lop->rl_lmident;
401	mip->mi_next = NULL;
402
403	if (ph->pp_lmaplist.ml_head == NULL) {
404		ph->pp_lmaplist.ml_head = ph->pp_lmaplist.ml_tail = mip;
405		return (1);
406	}
407
408	ph->pp_lmaplist.ml_tail->mi_next = mip;
409	ph->pp_lmaplist.ml_tail = mip;
410
411	return (1);
412}
413
414void
415free_linkmaps(struct ps_prochandle *ph)
416{
417	map_info_t *cur, *prev;
418
419	for (cur = ph->pp_lmaplist.ml_head, prev = NULL; cur;
420	    prev = cur, cur = cur->mi_next) {
421		if (prev) {
422			(void) elf_end(prev->mi_elf);
423			(void) close(prev->mi_mapfd);
424			free(prev->mi_name);
425			if (prev->mi_refname)
426				free(prev->mi_refname);
427			free(prev);
428		}
429	}
430	if (prev) {
431		(void) elf_end(prev->mi_elf);
432		(void) close(prev->mi_mapfd);
433		free(prev->mi_name);
434		if (prev->mi_refname)
435			free(prev->mi_refname);
436		free(prev);
437	}
438	ph->pp_lmaplist.ml_head = ph->pp_lmaplist.ml_tail = NULL;
439}
440
441retc_t
442get_linkmaps(struct ps_prochandle *ph)
443{
444	free_linkmaps(ph);
445	(void) rd_loadobj_iter(ph->pp_rap, map_iter, ph);
446	return (RET_OK);
447}
448
449retc_t
450set_objpad(struct ps_prochandle *ph, size_t padsize)
451{
452	if (rd_objpad_enable(ph->pp_rap, padsize) != RD_OK) {
453		(void) printf("rdb: error setting object padding\n");
454		return (RET_FAILED);
455	}
456	return (RET_OK);
457}
458