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_secflags   = (pop_secflags_t)Pidle_int,
 116         .pop_psinfo     = (pop_psinfo_t)Pidle_voidp,
 117         .pop_platform   = (pop_platform_t)Pidle_voidp,
 118         .pop_uname      = (pop_uname_t)Pidle_int,
 119         .pop_zonename   = (pop_zonename_t)Pidle_voidp,
 120 #if defined(__i386) || defined(__amd64)
 121         .pop_ldt        = (pop_ldt_t)Pidle_int
 122 #endif
 123 };
 124 
 125 static int
 126 idle_add_mapping(struct ps_prochandle *P, GElf_Phdr *php, file_info_t *fp)
 127 {
 128         prmap_t pmap;
 129 
 130         dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n",
 131             (u_longlong_t)php->p_vaddr, (u_longlong_t)php->p_filesz,
 132             (u_longlong_t)php->p_memsz, (u_longlong_t)php->p_offset);
 133 
 134         pmap.pr_vaddr = (uintptr_t)php->p_vaddr;
 135         pmap.pr_size = php->p_filesz;
 136         (void) strncpy(pmap.pr_mapname, fp->file_pname,
 137             sizeof (pmap.pr_mapname));
 138         pmap.pr_offset = php->p_offset;
 139 
 140         pmap.pr_mflags = 0;
 141         if (php->p_flags & PF_R)
 142                 pmap.pr_mflags |= MA_READ;
 143         if (php->p_flags & PF_W)
 144                 pmap.pr_mflags |= MA_WRITE;
 145         if (php->p_flags & PF_X)
 146                 pmap.pr_mflags |= MA_EXEC;
 147 
 148         pmap.pr_pagesize = 0;
 149         pmap.pr_shmid = -1;
 150 
 151         return (Padd_mapping(P, php->p_offset, fp, &pmap));
 152 }
 153 
 154 struct ps_prochandle *
 155 Pgrab_file(const char *fname, int *perr)
 156 {
 157         struct ps_prochandle *P = NULL;
 158         char buf[PATH_MAX];
 159         GElf_Ehdr ehdr;
 160         Elf *elf = NULL;
 161         size_t phnum;
 162         file_info_t *fp = NULL;
 163         int fd;
 164         int i;
 165 
 166         if ((fd = open64(fname, O_RDONLY)) < 0) {
 167                 dprintf("couldn't open file");
 168                 *perr = (errno == ENOENT) ? G_NOEXEC : G_STRANGE;
 169                 return (NULL);
 170         }
 171 
 172         if (elf_version(EV_CURRENT) == EV_NONE) {
 173                 dprintf("libproc ELF version is more recent than libelf");
 174                 *perr = G_ELF;
 175                 goto err;
 176         }
 177 
 178         if ((P = calloc(1, sizeof (struct ps_prochandle))) == NULL) {
 179                 *perr = G_STRANGE;
 180                 goto err;
 181         }
 182 
 183         (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);
 184         P->state = PS_IDLE;
 185         P->pid = (pid_t)-1;
 186         P->asfd = fd;
 187         P->ctlfd = -1;
 188         P->statfd = -1;
 189         P->agentctlfd = -1;
 190         P->agentstatfd = -1;
 191         P->info_valid = -1;
 192         Pinit_ops(&P->ops, &P_idle_ops);
 193         Pinitsym(P);
 194 
 195         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
 196                 *perr = G_ELF;
 197                 return (NULL);
 198         }
 199 
 200         /*
 201          * Construct a file_info_t that corresponds to this file.
 202          */
 203         if ((fp = calloc(1, sizeof (file_info_t))) == NULL) {
 204                 *perr = G_STRANGE;
 205                 goto err;
 206         }
 207 
 208         if ((fp->file_lo = calloc(1, sizeof (rd_loadobj_t))) == NULL) {
 209                 *perr = G_STRANGE;
 210                 goto err;
 211         }
 212 
 213         if (*fname == '/') {
 214                 (void) strncpy(fp->file_pname, fname, sizeof (fp->file_pname));
 215         } else {
 216                 size_t sz;
 217 
 218                 if (getcwd(fp->file_pname, sizeof (fp->file_pname) - 1) ==
 219                     NULL) {
 220                         *perr = G_STRANGE;
 221                         goto err;
 222                 }
 223 
 224                 sz = strlen(fp->file_pname);
 225                 (void) snprintf(&fp->file_pname[sz],
 226                     sizeof (fp->file_pname) - sz, "/%s", fname);
 227         }
 228 
 229         fp->file_fd = fd;
 230         fp->file_lo->rl_lmident = LM_ID_BASE;
 231         if ((fp->file_lname = strdup(fp->file_pname)) == NULL) {
 232                 *perr = G_STRANGE;
 233                 goto err;
 234         }
 235         fp->file_lbase = basename(fp->file_lname);
 236 
 237         if ((P->execname = strdup(fp->file_pname)) == NULL) {
 238                 *perr = G_STRANGE;
 239                 goto err;
 240         }
 241 
 242         P->num_files++;
 243         list_link(fp, &P->file_head);
 244 
 245         if (gelf_getehdr(elf, &ehdr) == NULL) {
 246                 *perr = G_STRANGE;
 247                 goto err;
 248         }
 249 
 250         if (elf_getphdrnum(elf, &phnum) == -1) {
 251                 *perr = G_STRANGE;
 252                 goto err;
 253         }
 254 
 255         dprintf("Pgrab_file: program header count = %lu\n", (ulong_t)phnum);
 256 
 257         /*
 258          * Sift through the program headers making the relevant maps.
 259          */
 260         for (i = 0; i < phnum; i++) {
 261                 GElf_Phdr phdr, *php;
 262 
 263                 if ((php = gelf_getphdr(elf, i, &phdr)) == NULL) {
 264                         *perr = G_STRANGE;
 265                         goto err;
 266                 }
 267 
 268                 if (php->p_type != PT_LOAD)
 269                         continue;
 270 
 271                 if (idle_add_mapping(P, php, fp) != 0) {
 272                         *perr = G_STRANGE;
 273                         goto err;
 274                 }
 275         }
 276         Psort_mappings(P);
 277 
 278         (void) elf_end(elf);
 279 
 280         P->map_exec = fp->file_map;
 281 
 282         P->status.pr_flags = PR_STOPPED;
 283         P->status.pr_nlwp = 0;
 284         P->status.pr_pid = (pid_t)-1;
 285         P->status.pr_ppid = (pid_t)-1;
 286         P->status.pr_pgid = (pid_t)-1;
 287         P->status.pr_sid = (pid_t)-1;
 288         P->status.pr_taskid = (taskid_t)-1;
 289         P->status.pr_projid = (projid_t)-1;
 290         P->status.pr_zoneid = (zoneid_t)-1;
 291         switch (ehdr.e_ident[EI_CLASS]) {
 292         case ELFCLASS32:
 293                 P->status.pr_dmodel = PR_MODEL_ILP32;
 294                 break;
 295         case ELFCLASS64:
 296                 P->status.pr_dmodel = PR_MODEL_LP64;
 297                 break;
 298         default:
 299                 *perr = G_FORMAT;
 300                 goto err;
 301         }
 302 
 303         /*
 304          * Pfindobj() checks what zone a process is associated with, so
 305          * we call it after initializing pr_zoneid to -1.  This ensures
 306          * we don't get associated with any zone on the system.
 307          */
 308         if (Pfindobj(P, fp->file_lname, buf, sizeof (buf)) != NULL) {
 309                 free(P->execname);
 310                 P->execname = strdup(buf);
 311                 if ((fp->file_rname = strdup(buf)) != NULL)
 312                         fp->file_rbase = basename(fp->file_rname);
 313         }
 314 
 315         /*
 316          * The file and map lists are complete, and will never need to be
 317          * adjusted.
 318          */
 319         P->info_valid = 1;
 320 
 321         return (P);
 322 err:
 323         (void) close(fd);
 324         if (P != NULL)
 325                 Pfree(P);
 326         if (elf != NULL)
 327                 (void) elf_end(elf);
 328         return (NULL);
 329 }