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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <stdlib.h>
  27 #include <libelf.h>
  28 #include <libgen.h>
  29 #include <string.h>
  30 #include <strings.h>
  31 #include <errno.h>
  32 #include <sys/sysmacros.h>
  33 
  34 #include "libproc.h"
  35 #include "Pcontrol.h"
  36 
  37 static ssize_t
  38 Pread_idle(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr)
  39 {
  40         size_t resid = n;
  41 
  42         while (resid > 0) {
  43                 map_info_t *mp;
  44                 uintptr_t mapoff;
  45                 ssize_t len;
  46                 off64_t off;
  47 
  48                 if ((mp = Paddr2mptr(P, addr)) == NULL)
  49                         break;
  50 
  51                 mapoff = addr - mp->map_pmap.pr_vaddr;
  52                 len = MIN(resid, mp->map_pmap.pr_size - mapoff);
  53                 off = mp->map_offset + mapoff;
  54 
  55                 if ((len = pread64(P->asfd, buf, len, off)) <= 0)
  56                         break;
  57 
  58                 resid -= len;
  59                 addr += len;
  60                 buf = (char *)buf + len;
  61         }
  62 
  63         return (n - resid);
  64 }
  65 
  66 /*ARGSUSED*/
  67 static ssize_t
  68 Pwrite_idle(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr)
  69 {
  70         errno = EIO;
  71         return (-1);
  72 }
  73 
  74 static const ps_rwops_t P_idle_ops = {
  75         Pread_idle,
  76         Pwrite_idle
  77 };
  78 
  79 static int
  80 idle_add_mapping(struct ps_prochandle *P, GElf_Phdr *php, file_info_t *fp)
  81 {
  82         prmap_t pmap;
  83 
  84         dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n",
  85             (u_longlong_t)php->p_vaddr, (u_longlong_t)php->p_filesz,
  86             (u_longlong_t)php->p_memsz, (u_longlong_t)php->p_offset);
  87 
  88         pmap.pr_vaddr = (uintptr_t)php->p_vaddr;
  89         pmap.pr_size = php->p_filesz;
  90         (void) strncpy(pmap.pr_mapname, fp->file_pname,
  91             sizeof (pmap.pr_mapname));
  92         pmap.pr_offset = php->p_offset;
  93 
  94         pmap.pr_mflags = 0;
  95         if (php->p_flags & PF_R)
  96                 pmap.pr_mflags |= MA_READ;
  97         if (php->p_flags & PF_W)
  98                 pmap.pr_mflags |= MA_WRITE;
  99         if (php->p_flags & PF_X)
 100                 pmap.pr_mflags |= MA_EXEC;
 101 
 102         pmap.pr_pagesize = 0;
 103         pmap.pr_shmid = -1;
 104 
 105         return (Padd_mapping(P, php->p_offset, fp, &pmap));
 106 }
 107 
 108 struct ps_prochandle *
 109 Pgrab_file(const char *fname, int *perr)
 110 {
 111         struct ps_prochandle *P = NULL;
 112         char buf[PATH_MAX];
 113         GElf_Ehdr ehdr;
 114         Elf *elf = NULL;
 115         size_t phnum;
 116         file_info_t *fp = NULL;
 117         int fd;
 118         int i;
 119 
 120         if ((fd = open64(fname, O_RDONLY)) < 0) {
 121                 dprintf("couldn't open file");
 122                 *perr = (errno == ENOENT) ? G_NOEXEC : G_STRANGE;
 123                 return (NULL);
 124         }
 125 
 126         if (elf_version(EV_CURRENT) == EV_NONE) {
 127                 dprintf("libproc ELF version is more recent than libelf");
 128                 *perr = G_ELF;
 129                 goto err;
 130         }
 131 
 132         if ((P = calloc(1, sizeof (struct ps_prochandle))) == NULL) {
 133                 *perr = G_STRANGE;
 134                 goto err;
 135         }
 136 
 137         (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);
 138         P->state = PS_IDLE;
 139         P->pid = (pid_t)-1;
 140         P->asfd = fd;
 141         P->ctlfd = -1;
 142         P->statfd = -1;
 143         P->agentctlfd = -1;
 144         P->agentstatfd = -1;
 145         P->info_valid = -1;
 146         P->ops = &P_idle_ops;
 147         Pinitsym(P);
 148 
 149         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
 150                 *perr = G_ELF;
 151                 return (NULL);
 152         }
 153 
 154         /*
 155          * Construct a file_info_t that corresponds to this file.
 156          */
 157         if ((fp = calloc(1, sizeof (file_info_t))) == NULL) {
 158                 *perr = G_STRANGE;
 159                 goto err;
 160         }
 161 
 162         if ((fp->file_lo = calloc(1, sizeof (rd_loadobj_t))) == NULL) {
 163                 *perr = G_STRANGE;
 164                 goto err;
 165         }
 166 
 167         if (*fname == '/') {
 168                 (void) strncpy(fp->file_pname, fname, sizeof (fp->file_pname));
 169         } else {
 170                 size_t sz;
 171 
 172                 if (getcwd(fp->file_pname, sizeof (fp->file_pname) - 1) ==
 173                     NULL) {
 174                         *perr = G_STRANGE;
 175                         goto err;
 176                 }
 177 
 178                 sz = strlen(fp->file_pname);
 179                 (void) snprintf(&fp->file_pname[sz],
 180                     sizeof (fp->file_pname) - sz, "/%s", fname);
 181         }
 182 
 183         fp->file_fd = fd;
 184         fp->file_lo->rl_lmident = LM_ID_BASE;
 185         if ((fp->file_lname = strdup(fp->file_pname)) == NULL) {
 186                 *perr = G_STRANGE;
 187                 goto err;
 188         }
 189         fp->file_lbase = basename(fp->file_lname);
 190 
 191         if ((P->execname = strdup(fp->file_pname)) == NULL) {
 192                 *perr = G_STRANGE;
 193                 goto err;
 194         }
 195 
 196         P->num_files++;
 197         list_link(fp, &P->file_head);
 198 
 199         if (gelf_getehdr(elf, &ehdr) == NULL) {
 200                 *perr = G_STRANGE;
 201                 goto err;
 202         }
 203 
 204         if (elf_getphdrnum(elf, &phnum) == -1) {
 205                 *perr = G_STRANGE;
 206                 goto err;
 207         }
 208 
 209         dprintf("Pgrab_file: program header count = %lu\n", (ulong_t)phnum);
 210 
 211         /*
 212          * Sift through the program headers making the relevant maps.
 213          */
 214         for (i = 0; i < phnum; i++) {
 215                 GElf_Phdr phdr, *php;
 216 
 217                 if ((php = gelf_getphdr(elf, i, &phdr)) == NULL) {
 218                         *perr = G_STRANGE;
 219                         goto err;
 220                 }
 221 
 222                 if (php->p_type != PT_LOAD)
 223                         continue;
 224 
 225                 if (idle_add_mapping(P, php, fp) != 0) {
 226                         *perr = G_STRANGE;
 227                         goto err;
 228                 }
 229         }
 230         Psort_mappings(P);
 231 
 232         (void) elf_end(elf);
 233 
 234         P->map_exec = fp->file_map;
 235 
 236         P->status.pr_flags = PR_STOPPED;
 237         P->status.pr_nlwp = 0;
 238         P->status.pr_pid = (pid_t)-1;
 239         P->status.pr_ppid = (pid_t)-1;
 240         P->status.pr_pgid = (pid_t)-1;
 241         P->status.pr_sid = (pid_t)-1;
 242         P->status.pr_taskid = (taskid_t)-1;
 243         P->status.pr_projid = (projid_t)-1;
 244         P->status.pr_zoneid = (zoneid_t)-1;
 245         switch (ehdr.e_ident[EI_CLASS]) {
 246         case ELFCLASS32:
 247                 P->status.pr_dmodel = PR_MODEL_ILP32;
 248                 break;
 249         case ELFCLASS64:
 250                 P->status.pr_dmodel = PR_MODEL_LP64;
 251                 break;
 252         default:
 253                 *perr = G_FORMAT;
 254                 goto err;
 255         }
 256 
 257         /*
 258          * Pfindobj() checks what zone a process is associated with, so
 259          * we call it after initializing pr_zoneid to -1.  This ensures
 260          * we don't get associated with any zone on the system.
 261          */
 262         if (Pfindobj(P, fp->file_lname, buf, sizeof (buf)) != NULL) {
 263                 free(P->execname);
 264                 P->execname = strdup(buf);
 265                 if ((fp->file_rname = strdup(buf)) != NULL)
 266                         fp->file_rbase = basename(fp->file_rname);
 267         }
 268 
 269         /*
 270          * The file and map lists are complete, and will never need to be
 271          * adjusted.
 272          */
 273         P->info_valid = 1;
 274 
 275         return (P);
 276 err:
 277         (void) close(fd);
 278         if (P != NULL)
 279                 Pfree(P);
 280         if (elf != NULL)
 281                 (void) elf_end(elf);
 282         return (NULL);
 283 }