Home | History | Annotate | Download | only in common
      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 2006 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 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <fcntl.h>
     32 #include <unistd.h>
     33 #include <string.h>
     34 #include <errno.h>
     35 #include <limits.h>
     36 
     37 #include "libproc.h"
     38 #include "Pcontrol.h"
     39 #include "Pisadep.h"
     40 #include "Putil.h"
     41 
     42 #define	BLKSIZE	(8 * 1024)
     43 
     44 /*
     45  * Look for a SYSCALL instruction in the process's address space.
     46  */
     47 int
     48 Pscantext(struct ps_prochandle *P)
     49 {
     50 	char mapfile[PATH_MAX];
     51 	int mapfd;
     52 	off_t offset;		/* offset in text section */
     53 	off_t endoff;		/* ending offset in text section */
     54 	uintptr_t sysaddr;	/* address of SYSCALL instruction */
     55 	int syspri;		/* priority of SYSCALL instruction */
     56 	int nbytes;		/* number of bytes in buffer */
     57 	int n2bytes;		/* number of bytes in second buffer */
     58 	int nmappings;		/* current number of mappings */
     59 	prmap_t *pdp;		/* pointer to map descriptor */
     60 	prmap_t *prbuf;		/* buffer for map descriptors */
     61 	unsigned nmap;		/* number of map descriptors */
     62 	uint32_t buf[2 * BLKSIZE / sizeof (uint32_t)];	/* text buffer */
     63 	uchar_t *p;
     64 
     65 	/* try the most recently-seen syscall address */
     66 	syspri = 0;
     67 	sysaddr = 0;
     68 	if (P->sysaddr != 0 &&
     69 	    (syspri = Pissyscall(P, P->sysaddr)))
     70 		sysaddr = P->sysaddr;
     71 
     72 	/* try the previous instruction */
     73 	if (sysaddr == 0 || syspri != 1)
     74 		syspri = Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC],
     75 		    &sysaddr);
     76 
     77 	if (sysaddr != 0 && syspri == 1) {
     78 		P->sysaddr = sysaddr;
     79 		return (0);
     80 	}
     81 
     82 	/* open the /proc/<pid>/map file */
     83 	(void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map",
     84 	    procfs_path, (int)P->pid);
     85 	if ((mapfd = open(mapfile, O_RDONLY)) < 0) {
     86 		dprintf("failed to open %s: %s\n", mapfile, strerror(errno));
     87 		return (-1);
     88 	}
     89 
     90 	/* allocate a plausible initial buffer size */
     91 	nmap = 50;
     92 
     93 	/* read all the map structures, allocating more space as needed */
     94 	for (;;) {
     95 		prbuf = malloc(nmap * sizeof (prmap_t));
     96 		if (prbuf == NULL) {
     97 			dprintf("Pscantext: failed to allocate buffer\n");
     98 			(void) close(mapfd);
     99 			return (-1);
    100 		}
    101 		nmappings = pread(mapfd, prbuf, nmap * sizeof (prmap_t), 0L);
    102 		if (nmappings < 0) {
    103 			dprintf("Pscantext: failed to read map file: %s\n",
    104 			    strerror(errno));
    105 			free(prbuf);
    106 			(void) close(mapfd);
    107 			return (-1);
    108 		}
    109 		nmappings /= sizeof (prmap_t);
    110 		if (nmappings < nmap)	/* we read them all */
    111 			break;
    112 		/* allocate a bigger buffer */
    113 		free(prbuf);
    114 		nmap *= 2;
    115 	}
    116 	(void) close(mapfd);
    117 
    118 	/*
    119 	 * Scan each executable mapping looking for a syscall instruction.
    120 	 * In dynamically linked executables, syscall instructions are
    121 	 * typically only found in shared libraries.  Because shared libraries
    122 	 * are most often mapped at the top of the address space, we minimize
    123 	 * our expected search time by starting at the last mapping and working
    124 	 * our way down to the first mapping.
    125 	 */
    126 	for (pdp = &prbuf[nmappings - 1]; sysaddr == 0 && syspri != 1 &&
    127 	    pdp >= prbuf; pdp--) {
    128 
    129 		offset = (off_t)pdp->pr_vaddr;	/* beginning of text */
    130 		endoff = offset + pdp->pr_size;
    131 
    132 		/* avoid non-EXEC mappings; avoid the stack and heap */
    133 		if ((pdp->pr_mflags&MA_EXEC) == 0 ||
    134 		    (endoff > P->status.pr_stkbase &&
    135 		    offset < P->status.pr_stkbase + P->status.pr_stksize) ||
    136 		    (endoff > P->status.pr_brkbase &&
    137 		    offset < P->status.pr_brkbase + P->status.pr_brksize))
    138 			continue;
    139 
    140 		(void) lseek(P->asfd, (off_t)offset, 0);
    141 
    142 		if ((nbytes = read(P->asfd, buf, 2*BLKSIZE)) <= 0)
    143 			continue;
    144 
    145 		if (nbytes < BLKSIZE)
    146 			n2bytes = 0;
    147 		else {
    148 			n2bytes = nbytes - BLKSIZE;
    149 			nbytes  = BLKSIZE;
    150 		}
    151 
    152 		p = (uchar_t *)buf;
    153 
    154 		/* search text for a SYSCALL instruction */
    155 		while (sysaddr == 0 && syspri != 1 && offset < endoff) {
    156 			if (nbytes <= 0) {	/* shift buffers */
    157 				if ((nbytes = n2bytes) <= 0)
    158 					break;
    159 				(void) memcpy(buf,
    160 					&buf[BLKSIZE / sizeof (buf[0])],
    161 					nbytes);
    162 				n2bytes = 0;
    163 				p = (uchar_t *)buf;
    164 				if (nbytes == BLKSIZE &&
    165 				    offset + BLKSIZE < endoff)
    166 					n2bytes = read(P->asfd,
    167 						&buf[BLKSIZE / sizeof (buf[0])],
    168 						BLKSIZE);
    169 			}
    170 
    171 			if (syspri = Pissyscall_text(P, p, nbytes))
    172 				sysaddr = offset;
    173 
    174 			p += sizeof (instr_t);
    175 			offset += sizeof (instr_t);
    176 			nbytes -= sizeof (instr_t);
    177 		}
    178 	}
    179 
    180 	free(prbuf);
    181 
    182 	if ((P->sysaddr = sysaddr) != 0)
    183 		return (0);
    184 	else
    185 		return (-1);
    186 }
    187