2 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
49 #define PROCSTAT_CORE_MAGIC 0x012DADB8
59 static bool core_offset(struct procstat_core *core, off_t offset);
60 static bool core_read(struct procstat_core *core, void *buf, size_t len);
61 static ssize_t core_read_mem(struct procstat_core *core, void *buf,
62 size_t len, vm_offset_t addr, bool readall);
63 static void *get_args(struct procstat_core *core, vm_offset_t psstrings,
64 enum psc_type type, void *buf, size_t *lenp);
66 struct procstat_core *
67 procstat_core_open(const char *filename)
69 struct procstat_core *core;
76 if (elf_version(EV_CURRENT) == EV_NONE) {
77 warnx("ELF library too old");
80 fd = open(filename, O_RDONLY, 0);
82 warn("open(%s)", filename);
85 e = elf_begin(fd, ELF_C_READ, NULL);
87 warnx("elf_begin: %s", elf_errmsg(-1));
90 if (elf_kind(e) != ELF_K_ELF) {
91 warnx("%s is not an ELF object", filename);
94 if (gelf_getehdr(e, &ehdr) == NULL) {
95 warnx("gelf_getehdr: %s", elf_errmsg(-1));
98 if (ehdr.e_type != ET_CORE) {
99 warnx("%s is not a CORE file", filename);
102 if (elf_getphnum(e, &nph) == 0) {
103 warnx("program headers not found");
106 for (i = 0; i < ehdr.e_phnum; i++) {
107 if (gelf_getphdr(e, i, &phdr) != &phdr) {
108 warnx("gelf_getphdr: %s", elf_errmsg(-1));
111 if (phdr.p_type == PT_NOTE)
114 if (i == ehdr.e_phnum) {
115 warnx("NOTE program header not found");
118 core = malloc(sizeof(struct procstat_core));
120 warn("malloc(%zu)", sizeof(struct procstat_core));
123 core->pc_magic = PROCSTAT_CORE_MAGIC;
126 core->pc_ehdr = ehdr;
127 core->pc_phdr = phdr;
139 procstat_core_close(struct procstat_core *core)
142 assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
144 elf_end(core->pc_elf);
150 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
154 off_t offset, eoffset;
155 vm_offset_t psstrings;
159 int cstructsize, structsize;
162 assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
166 n_type = NT_PROCSTAT_PROC;
167 structsize = sizeof(struct kinfo_proc);
170 n_type = NT_PROCSTAT_FILES;
171 structsize = sizeof(struct kinfo_file);
174 n_type = NT_PROCSTAT_VMMAP;
175 structsize = sizeof(struct kinfo_vmentry);
177 case PSC_TYPE_GROUPS:
178 n_type = NT_PROCSTAT_GROUPS;
179 structsize = sizeof(gid_t);
182 n_type = NT_PROCSTAT_UMASK;
183 structsize = sizeof(u_short);
185 case PSC_TYPE_RLIMIT:
186 n_type = NT_PROCSTAT_RLIMIT;
187 structsize = sizeof(struct rlimit) * RLIM_NLIMITS;
190 n_type = NT_PROCSTAT_OSREL;
191 structsize = sizeof(int);
193 case PSC_TYPE_PSSTRINGS:
196 n_type = NT_PROCSTAT_PSSTRINGS;
197 structsize = sizeof(vm_offset_t);
200 n_type = NT_PROCSTAT_AUXV;
201 structsize = sizeof(Elf_Auxinfo);
204 warnx("unknown core stat type: %d", type);
208 offset = core->pc_phdr.p_offset;
209 eoffset = offset + core->pc_phdr.p_filesz;
211 while (offset < eoffset) {
212 if (!core_offset(core, offset))
214 if (!core_read(core, &nhdr, sizeof(nhdr)))
217 offset += sizeof(nhdr) +
218 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) +
219 roundup2(nhdr.n_descsz, sizeof(Elf32_Size));
221 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0)
223 if (nhdr.n_type != n_type)
225 if (nhdr.n_namesz != 8)
227 if (!core_read(core, nbuf, sizeof(nbuf)))
229 if (strcmp(nbuf, "FreeBSD") != 0)
231 if (nhdr.n_descsz < sizeof(cstructsize)) {
232 warnx("corrupted core file");
235 if (!core_read(core, &cstructsize, sizeof(cstructsize)))
237 if (cstructsize != structsize) {
238 warnx("version mismatch");
241 len = nhdr.n_descsz - sizeof(cstructsize);
245 len = MIN(len, *lenp);
248 freebuf = buf = malloc(len);
250 warn("malloc(%zu)", len);
254 if (!core_read(core, buf, len)) {
258 if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) {
259 if (len < sizeof(psstrings)) {
263 psstrings = *(vm_offset_t *)buf;
269 buf = get_args(core, psstrings, type, buf, &len);
279 core_offset(struct procstat_core *core, off_t offset)
282 assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
284 if (lseek(core->pc_fd, offset, SEEK_SET) == -1) {
285 warn("core: lseek(%jd)", (intmax_t)offset);
292 core_read(struct procstat_core *core, void *buf, size_t len)
296 assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
298 n = read(core->pc_fd, buf, len);
303 if (n < (ssize_t)len) {
304 warnx("core: short read");
311 core_read_mem(struct procstat_core *core, void *buf, size_t len,
312 vm_offset_t addr, bool readall)
318 assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
320 for (i = 0; i < core->pc_ehdr.e_phnum; i++) {
321 if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) {
322 warnx("gelf_getphdr: %s", elf_errmsg(-1));
325 if (phdr.p_type != PT_LOAD)
327 if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz)
329 offset = phdr.p_offset + (addr - phdr.p_vaddr);
330 if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) {
332 warnx("format error: "
333 "attempt to read out of segment");
336 len = (phdr.p_vaddr + phdr.p_memsz) - addr;
338 if (!core_offset(core, offset))
340 if (!core_read(core, buf, len))
344 warnx("format error: address %ju not found", (uintmax_t)addr);
348 #define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */
351 get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type,
352 void *args, size_t *lenp)
354 struct ps_strings pss;
358 size_t chunksz, done, len, nchr, size;
362 assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV);
364 if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1)
366 if (type == PSC_TYPE_ARGV) {
367 addr = (vm_offset_t)pss.ps_argvstr;
368 nstr = pss.ps_nargvstr;
369 } else /* type == PSC_TYPE_ENVV */ {
370 addr = (vm_offset_t)pss.ps_envstr;
371 nstr = pss.ps_nenvstr;
373 if (addr == 0 || nstr == 0)
375 if (nstr > ARG_MAX) {
376 warnx("format error");
379 size = nstr * sizeof(char *);
382 warn("malloc(%zu)", size);
387 if (core_read_mem(core, argv, size, addr, true) == -1)
390 nchr = MIN(ARG_MAX, *lenp);
393 freeargs = args = malloc(nchr);
395 warn("malloc(%zu)", nchr);
404 * The program may have scribbled into its argv array, e.g. to
405 * remove some arguments. If that has happened, break out
406 * before trying to read from NULL.
410 for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) {
411 chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done);
414 n = core_read_mem(core, p, chunksz, addr, false);
417 len = strnlen(p, chunksz);