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