1 /*- 2 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/elf.h> 31 #include <sys/user.h> 32 33 #include <assert.h> 34 #include <err.h> 35 #include <fcntl.h> 36 #include <gelf.h> 37 #include <libelf.h> 38 #include <stdbool.h> 39 #include <stdint.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include "core.h" 46 47 #define PROCSTAT_CORE_MAGIC 0x012DADB8 48 struct procstat_core 49 { 50 int pc_magic; 51 int pc_fd; 52 Elf *pc_elf; 53 GElf_Ehdr pc_ehdr; 54 GElf_Phdr pc_phdr; 55 }; 56 57 static bool core_offset(struct procstat_core *core, off_t offset); 58 static bool core_read(struct procstat_core *core, void *buf, size_t len); 59 60 struct procstat_core * 61 procstat_core_open(const char *filename) 62 { 63 struct procstat_core *core; 64 Elf *e; 65 GElf_Ehdr ehdr; 66 GElf_Phdr phdr; 67 size_t nph; 68 int fd, i; 69 70 if (elf_version(EV_CURRENT) == EV_NONE) { 71 warnx("ELF library too old"); 72 return (NULL); 73 } 74 fd = open(filename, O_RDONLY, 0); 75 if (fd == -1) { 76 warn("open(%s)", filename); 77 return (NULL); 78 } 79 e = elf_begin(fd, ELF_C_READ, NULL); 80 if (e == NULL) { 81 warnx("elf_begin: %s", elf_errmsg(-1)); 82 goto fail; 83 } 84 if (elf_kind(e) != ELF_K_ELF) { 85 warnx("%s is not an ELF object", filename); 86 goto fail; 87 } 88 if (gelf_getehdr(e, &ehdr) == NULL) { 89 warnx("gelf_getehdr: %s", elf_errmsg(-1)); 90 goto fail; 91 } 92 if (ehdr.e_type != ET_CORE) { 93 warnx("%s is not a CORE file", filename); 94 goto fail; 95 } 96 if (elf_getphnum(e, &nph) == 0) { 97 warnx("program headers not found"); 98 goto fail; 99 } 100 for (i = 0; i < ehdr.e_phnum; i++) { 101 if (gelf_getphdr(e, i, &phdr) != &phdr) { 102 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 103 goto fail; 104 } 105 if (phdr.p_type == PT_NOTE) 106 break; 107 } 108 if (i == ehdr.e_phnum) { 109 warnx("NOTE program header not found"); 110 goto fail; 111 } 112 core = malloc(sizeof(struct procstat_core)); 113 if (core == NULL) { 114 warn("malloc(%zu)", sizeof(struct procstat_core)); 115 goto fail; 116 } 117 core->pc_magic = PROCSTAT_CORE_MAGIC; 118 core->pc_fd = fd; 119 core->pc_elf = e; 120 core->pc_ehdr = ehdr; 121 core->pc_phdr = phdr; 122 123 return (core); 124 fail: 125 if (e != NULL) 126 elf_end(e); 127 close(fd); 128 129 return (NULL); 130 } 131 132 void 133 procstat_core_close(struct procstat_core *core) 134 { 135 136 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 137 138 elf_end(core->pc_elf); 139 close(core->pc_fd); 140 free(core); 141 } 142 143 void * 144 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, 145 size_t *lenp) 146 { 147 Elf_Note nhdr; 148 off_t offset, eoffset; 149 void *freebuf; 150 size_t len; 151 u_int32_t n_type; 152 int cstructsize, structsize; 153 char nbuf[8]; 154 155 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 156 157 switch(type) { 158 case PSC_TYPE_PROC: 159 n_type = NT_PROCSTAT_PROC; 160 structsize = sizeof(struct kinfo_proc); 161 break; 162 case PSC_TYPE_FILES: 163 n_type = NT_PROCSTAT_FILES; 164 structsize = sizeof(struct kinfo_file); 165 break; 166 case PSC_TYPE_VMMAP: 167 n_type = NT_PROCSTAT_VMMAP; 168 structsize = sizeof(struct kinfo_vmentry); 169 break; 170 case PSC_TYPE_GROUPS: 171 n_type = NT_PROCSTAT_GROUPS; 172 structsize = sizeof(gid_t); 173 break; 174 case PSC_TYPE_UMASK: 175 n_type = NT_PROCSTAT_UMASK; 176 structsize = sizeof(u_short); 177 break; 178 case PSC_TYPE_RLIMIT: 179 n_type = NT_PROCSTAT_RLIMIT; 180 structsize = sizeof(struct rlimit) * RLIM_NLIMITS; 181 break; 182 default: 183 warnx("unknown core stat type: %d", type); 184 return (NULL); 185 } 186 187 offset = core->pc_phdr.p_offset; 188 eoffset = offset + core->pc_phdr.p_filesz; 189 190 while (offset < eoffset) { 191 if (!core_offset(core, offset)) 192 return (NULL); 193 if (!core_read(core, &nhdr, sizeof(nhdr))) 194 return (NULL); 195 196 offset += sizeof(nhdr) + 197 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 198 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 199 200 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 201 break; 202 if (nhdr.n_type != n_type) 203 continue; 204 if (nhdr.n_namesz != 8) 205 continue; 206 if (!core_read(core, nbuf, sizeof(nbuf))) 207 return (NULL); 208 if (strcmp(nbuf, "FreeBSD") != 0) 209 continue; 210 if (nhdr.n_descsz < sizeof(cstructsize)) { 211 warnx("corrupted core file"); 212 return (NULL); 213 } 214 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 215 return (NULL); 216 if (cstructsize != structsize) { 217 warnx("version mismatch"); 218 return (NULL); 219 } 220 len = nhdr.n_descsz - sizeof(cstructsize); 221 if (len == 0) 222 return (NULL); 223 if (buf != NULL) { 224 len = MIN(len, *lenp); 225 freebuf = NULL; 226 } else { 227 freebuf = buf = malloc(len); 228 if (buf == NULL) { 229 warn("malloc(%zu)", len); 230 return (NULL); 231 } 232 } 233 if (!core_read(core, buf, len)) { 234 free(freebuf); 235 return (NULL); 236 } 237 *lenp = len; 238 return (buf); 239 } 240 241 return (NULL); 242 } 243 244 static bool 245 core_offset(struct procstat_core *core, off_t offset) 246 { 247 248 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 249 250 if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { 251 warn("core: lseek(%jd)", (intmax_t)offset); 252 return (false); 253 } 254 return (true); 255 } 256 257 static bool 258 core_read(struct procstat_core *core, void *buf, size_t len) 259 { 260 ssize_t n; 261 262 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 263 264 n = read(core->pc_fd, buf, len); 265 if (n == -1) { 266 warn("core: read"); 267 return (false); 268 } 269 if (n < (ssize_t)len) { 270 warnx("core: short read"); 271 return (false); 272 } 273 return (true); 274 } 275