2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <errno.h>
2N/A#include <limits.h>
2N/A
2N/A#include "libproc.h"
2N/A#include "Pcontrol.h"
2N/A#include "Pisadep.h"
2N/A#include "Putil.h"
2N/A
2N/A#define BLKSIZE (8 * 1024)
2N/A
2N/A/*
2N/A * Look for a SYSCALL instruction in the process's address space.
2N/A */
2N/Aint
2N/APscantext(struct ps_prochandle *P)
2N/A{
2N/A char mapfile[PATH_MAX];
2N/A int mapfd;
2N/A off_t offset; /* offset in text section */
2N/A off_t endoff; /* ending offset in text section */
2N/A uintptr_t sysaddr; /* address of SYSCALL instruction */
2N/A int syspri; /* priority of SYSCALL instruction */
2N/A int nbytes; /* number of bytes in buffer */
2N/A int n2bytes; /* number of bytes in second buffer */
2N/A int nmappings; /* current number of mappings */
2N/A prmap_t *pdp; /* pointer to map descriptor */
2N/A prmap_t *prbuf; /* buffer for map descriptors */
2N/A unsigned nmap; /* number of map descriptors */
2N/A uint32_t buf[2 * BLKSIZE / sizeof (uint32_t)]; /* text buffer */
2N/A uchar_t *p;
2N/A
2N/A /* try the most recently-seen syscall address */
2N/A syspri = 0;
2N/A sysaddr = 0;
2N/A if (P->sysaddr != 0 &&
2N/A (syspri = Pissyscall(P, P->sysaddr)))
2N/A sysaddr = P->sysaddr;
2N/A
2N/A /* try the previous instruction */
2N/A if (sysaddr == 0 || syspri != 1)
2N/A syspri = Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC],
2N/A &sysaddr);
2N/A
2N/A if (sysaddr != 0 && syspri == 1) {
2N/A P->sysaddr = sysaddr;
2N/A return (0);
2N/A }
2N/A
2N/A /* open the /proc/<pid>/map file */
2N/A (void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map",
2N/A procfs_path, (int)P->pid);
2N/A if ((mapfd = open(mapfile, O_RDONLY)) < 0) {
2N/A dprintf("failed to open %s: %s\n", mapfile, strerror(errno));
2N/A return (-1);
2N/A }
2N/A
2N/A /* allocate a plausible initial buffer size */
2N/A nmap = 50;
2N/A
2N/A /* read all the map structures, allocating more space as needed */
2N/A for (;;) {
2N/A prbuf = malloc(nmap * sizeof (prmap_t));
2N/A if (prbuf == NULL) {
2N/A dprintf("Pscantext: failed to allocate buffer\n");
2N/A (void) close(mapfd);
2N/A return (-1);
2N/A }
2N/A nmappings = pread(mapfd, prbuf, nmap * sizeof (prmap_t), 0L);
2N/A if (nmappings < 0) {
2N/A dprintf("Pscantext: failed to read map file: %s\n",
2N/A strerror(errno));
2N/A free(prbuf);
2N/A (void) close(mapfd);
2N/A return (-1);
2N/A }
2N/A nmappings /= sizeof (prmap_t);
2N/A if (nmappings < nmap) /* we read them all */
2N/A break;
2N/A /* allocate a bigger buffer */
2N/A free(prbuf);
2N/A nmap *= 2;
2N/A }
2N/A (void) close(mapfd);
2N/A
2N/A /*
2N/A * Scan each executable mapping looking for a syscall instruction.
2N/A * In dynamically linked executables, syscall instructions are
2N/A * typically only found in shared libraries. Because shared libraries
2N/A * are most often mapped at the top of the address space, we minimize
2N/A * our expected search time by starting at the last mapping and working
2N/A * our way down to the first mapping.
2N/A */
2N/A for (pdp = &prbuf[nmappings - 1]; sysaddr == 0 && syspri != 1 &&
2N/A pdp >= prbuf; pdp--) {
2N/A
2N/A offset = (off_t)pdp->pr_vaddr; /* beginning of text */
2N/A endoff = offset + pdp->pr_size;
2N/A
2N/A /* avoid non-EXEC mappings; avoid the stack and heap */
2N/A if ((pdp->pr_mflags&MA_EXEC) == 0 ||
2N/A (endoff > P->status.pr_stkbase &&
2N/A offset < P->status.pr_stkbase + P->status.pr_stksize) ||
2N/A (endoff > P->status.pr_brkbase &&
2N/A offset < P->status.pr_brkbase + P->status.pr_brksize))
2N/A continue;
2N/A
2N/A (void) lseek(P->asfd, (off_t)offset, 0);
2N/A
2N/A if ((nbytes = read(P->asfd, buf, 2*BLKSIZE)) <= 0)
2N/A continue;
2N/A
2N/A if (nbytes < BLKSIZE)
2N/A n2bytes = 0;
2N/A else {
2N/A n2bytes = nbytes - BLKSIZE;
2N/A nbytes = BLKSIZE;
2N/A }
2N/A
2N/A p = (uchar_t *)buf;
2N/A
2N/A /* search text for a SYSCALL instruction */
2N/A while (sysaddr == 0 && syspri != 1 && offset < endoff) {
2N/A if (nbytes <= 0) { /* shift buffers */
2N/A if ((nbytes = n2bytes) <= 0)
2N/A break;
2N/A (void) memcpy(buf,
2N/A &buf[BLKSIZE / sizeof (buf[0])],
2N/A nbytes);
2N/A n2bytes = 0;
2N/A p = (uchar_t *)buf;
2N/A if (nbytes == BLKSIZE &&
2N/A offset + BLKSIZE < endoff)
2N/A n2bytes = read(P->asfd,
2N/A &buf[BLKSIZE / sizeof (buf[0])],
2N/A BLKSIZE);
2N/A }
2N/A
2N/A if (syspri = Pissyscall_text(P, p, nbytes))
2N/A sysaddr = offset;
2N/A
2N/A p += sizeof (instr_t);
2N/A offset += sizeof (instr_t);
2N/A nbytes -= sizeof (instr_t);
2N/A }
2N/A }
2N/A
2N/A free(prbuf);
2N/A
2N/A if ((P->sysaddr = sysaddr) != 0)
2N/A return (0);
2N/A else
2N/A return (-1);
2N/A}