#include <err.h>
#include <stdlib.h>
#include <libproc.h>
#include <gelf.h>
#include <libelf.h>
#include <limits.h>
#include <string.h>
#include <libgen.h>
typedef enum {
SECMAP_CTF,
SECMAP_SYMTAB,
SECMAP_DEBUG,
SECMAP_MAX
} secmap_type_t;
typedef struct secmap_data {
core_content_t sd_content;
const char *sd_name;
} secmap_data_t;
secmap_data_t secmap_data[SECMAP_MAX] = {
{ CC_CONTENT_CTF, ".SUNW_ctf" },
{ CC_CONTENT_SYMTAB, ".symtab" },
{ CC_CONTENT_DEBUG, ".debug_" }
};
typedef struct {
uint64_t sm_addr;
char sm_obj[PATH_MAX];
size_t sm_nfound[SECMAP_MAX];
Elf *sm_elf;
GElf_Ehdr sm_ehdr;
boolean_t sm_ctf;
boolean_t sm_debug;
boolean_t sm_symtab;
} secmap_t;
static secmap_t *secmaps;
static size_t secmap_count;
static core_content_t secmap_content;
static int secmap_exit = EXIT_SUCCESS;
static void
secmap_fail(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
secmap_exit = EXIT_FAILURE;
}
static void
check_content(core_content_t content, struct ps_prochandle *Pr)
{
secmap_content = Pcontent(Pr);
if (secmap_content == CC_CONTENT_INVALID) {
secmap_fail("TEST FAILED: failed to get core content");
return;
}
if (secmap_content != content) {
secmap_fail("TEST FAILED: core file contains different "
"content than expected, found 0x%x, expected 0x%x",
secmap_content, content);
}
}
static secmap_t *
secmap_find(uint64_t addr)
{
for (size_t i = 0; i < secmap_count; i++) {
if (secmaps[i].sm_addr == addr) {
return (&secmaps[i]);
}
}
return (NULL);
}
static void
secmap_matches_content(secmap_type_t type)
{
boolean_t exist = (secmap_data[type].sd_content & secmap_content) != 0;
boolean_t found = B_FALSE;
if (type == SECMAP_SYMTAB && (secmap_content & CC_CONTENT_CTF) != 0) {
exist = B_TRUE;
}
for (size_t i = 0; i < secmap_count; i++) {
if (secmaps[i].sm_nfound[type] != 0) {
found = B_TRUE;
}
}
if (exist != found) {
secmap_fail("content type mismatch for %s: expected %s, but "
"found %s", secmap_data[type].sd_name,
exist ? "some" : "none",
found ? "some" : "none");
}
}
static secmap_t *
secmap_alloc(struct ps_prochandle *Pr, uint64_t addr)
{
int fd;
secmap_t *sm;
char *base;
sm = recallocarray(secmaps, secmap_count, secmap_count + 1,
sizeof (secmap_t));
if (sm == NULL) {
err(EXIT_FAILURE, "TEST FAILED: failed to allocate memory for "
"secmap %zu", secmap_count + 1);
}
secmaps = sm;
sm = &secmaps[secmap_count];
sm->sm_addr = addr;
secmap_count++;
if (Pobjname(Pr, addr, sm->sm_obj, sizeof (sm->sm_obj)) == NULL) {
core_content_t need = CC_CONTENT_ANON | CC_CONTENT_DATA;
if ((secmap_content & need) == need) {
secmap_fail("TEST FAILURE: object at address 0x%lx "
"has no name", addr);
}
return (sm);
}
fd = open(sm->sm_obj, O_RDONLY);
if (fd < 0) {
err(EXIT_FAILURE, "failed to open object %s", sm->sm_obj);
}
sm->sm_elf = elf_begin(fd, ELF_C_READ, NULL);
if (sm->sm_elf == NULL) {
err(EXIT_FAILURE, "failed to find open elf object %s: %s",
sm->sm_obj, elf_errmsg(elf_errno()));
}
if (gelf_getehdr(sm->sm_elf, &sm->sm_ehdr) == NULL) {
err(EXIT_FAILURE, "failed to get ehdr for %s: %s",
sm->sm_obj, elf_errmsg(elf_errno()));
}
base = basename(sm->sm_obj);
if (strcmp(base, "dumper.32") == 0 || strcmp(base, "dumper.64") == 0) {
sm->sm_debug = sm->sm_symtab = sm->sm_ctf = B_TRUE;
} else if (strcmp(base, "libc.so.1") == 0) {
sm->sm_symtab = sm->sm_ctf = B_TRUE;
} else if (strcmp(base, "ld.so.1") == 0) {
sm->sm_symtab = B_TRUE;
} else if (strcmp(base, "libproc.so.1") == 0) {
sm->sm_symtab = sm->sm_ctf = B_TRUE;
} else if (strcmp(base, "libdumper.so.1") == 0) {
sm->sm_debug = sm->sm_symtab = sm->sm_ctf = B_TRUE;
} else {
sm->sm_symtab = B_TRUE;
}
return (sm);
}
static void
secmap_data_cmp(secmap_t *sm, const char *sname, Elf_Scn *scn, GElf_Shdr *shdr)
{
for (Elf_Scn *comp_scn = elf_nextscn(sm->sm_elf, NULL);
comp_scn != NULL; comp_scn = elf_nextscn(sm->sm_elf, comp_scn)) {
GElf_Shdr comp_shdr;
const char *comp_name;
Elf_Data *src_data, *comp_data;
if (gelf_getshdr(comp_scn, &comp_shdr) == NULL) {
secmap_fail("failed to load section header from %s "
"during data comparison", sm->sm_obj);
return;
}
comp_name = elf_strptr(sm->sm_elf, sm->sm_ehdr.e_shstrndx,
comp_shdr.sh_name);
if (comp_name == NULL) {
secmap_fail("failed to load section name from %s "
"with index %lu", sm->sm_obj, comp_shdr.sh_name);
return;
}
if (strcmp(comp_name, sname) != 0)
continue;
if (comp_shdr.sh_type != shdr->sh_type ||
comp_shdr.sh_addralign != shdr->sh_addralign ||
comp_shdr.sh_size != shdr->sh_size ||
comp_shdr.sh_entsize != shdr->sh_entsize) {
continue;
}
if ((src_data = elf_getdata(scn, NULL)) == NULL) {
secmap_fail("failed to load section data from "
"source to compare to %s %s", sm->sm_obj, sname);
return;
}
if ((comp_data = elf_getdata(comp_scn, NULL)) == NULL) {
secmap_fail("failed to load section data from "
"source to compare to %s %s", sm->sm_obj, sname);
return;
}
if (comp_data->d_size != src_data->d_size) {
secmap_fail("data size mismatch for %s: %s, core: "
"%zu, file: %zu", sm->sm_obj, sname,
src_data->d_size, comp_data->d_size);
return;
}
if (memcmp(comp_data->d_buf, src_data->d_buf,
comp_data->d_size) != 0) {
secmap_fail("data mismatch between core and source "
"in %s: %s", sm->sm_obj, sname);
return;
}
return;
}
secmap_fail("failed to find matching section for %s in %s",
sname, sm->sm_obj);
}
static void
secmap_file_check(secmap_t *sm)
{
if (sm->sm_ctf && (secmap_content & CC_CONTENT_CTF) != 0 &&
sm->sm_nfound[SECMAP_CTF] == 0) {
secmap_fail("expected object %s to have CTF, but it doesn't",
sm->sm_obj);
}
if (sm->sm_symtab && (secmap_content & CC_CONTENT_SYMTAB) != 0 &&
sm->sm_nfound[SECMAP_SYMTAB] == 0) {
secmap_fail("expected object %s to have a symbol table, "
"but it doesn't", sm->sm_obj);
}
if (sm->sm_debug && (secmap_content & CC_CONTENT_DEBUG) != 0 &&
sm->sm_nfound[SECMAP_DEBUG] == 0) {
secmap_fail("expected object %s to have debug sections, "
"but it doesn't", sm->sm_obj);
}
}
int
main(int argc, char *argv[])
{
core_content_t content;
struct ps_prochandle *Pr;
int perr, fd;
Elf *elf;
Elf_Scn *scn;
GElf_Ehdr ehdr;
if (argc != 3) {
warnx("missing required file and core content");
(void) fprintf(stderr, "Usage: secmapper file content\n");
exit(EXIT_FAILURE);
}
if (elf_version(EV_CURRENT) == EV_NONE) {
errx(EXIT_FAILURE, "failed to init libelf");
}
Pr = Pgrab_core(argv[1], NULL, PGRAB_RDONLY, &perr);
if (Pr == NULL) {
errx(EXIT_FAILURE, "failed to open %s: %s", argv[1],
Pgrab_error(perr));
}
if ((fd = open(argv[1], O_RDONLY)) < 0) {
err(EXIT_FAILURE, "failed to open %s\n", argv[1]);
}
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
errx(EXIT_FAILURE, "failed to open elf file %s: %s", argv[1],
elf_errmsg(elf_errno()));
}
if (proc_str2content(argv[2], &content) != 0) {
err(EXIT_FAILURE, "failed to parse content %s", argv[2]);
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
errx(EXIT_FAILURE, "failed to get edr: %s",
elf_errmsg(elf_errno()));
}
check_content(content, Pr);
for (scn = elf_nextscn(elf, NULL); scn != NULL;
scn = elf_nextscn(elf, scn)) {
const char *sname;
GElf_Shdr shdr;
size_t index;
secmap_t *secmap;
index = elf_ndxscn(scn);
if (gelf_getshdr(scn, &shdr) == NULL) {
errx(EXIT_FAILURE, "failed to get section header for "
"shdr %zu: %s", index, elf_errmsg(elf_errno()));
}
if (shdr.sh_type == SHT_STRTAB) {
continue;
}
sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
if (sname == NULL) {
secmap_fail("TEST FAILURE: string name missing for "
"shdr %zu", index);
continue;
}
secmap = secmap_find(shdr.sh_addr);
if (secmap == NULL) {
secmap = secmap_alloc(Pr, shdr.sh_addr);
}
if (strcmp(sname, ".symtab") == 0) {
secmap->sm_nfound[SECMAP_SYMTAB]++;
} else if (strcmp(sname, ".SUNW_ctf") == 0) {
secmap->sm_nfound[SECMAP_CTF]++;
} else if (strncmp(sname, ".debug_", strlen(".debug_")) == 0) {
secmap->sm_nfound[SECMAP_DEBUG]++;
} else {
continue;
}
if (secmap->sm_elf != NULL) {
secmap_data_cmp(secmap, sname, scn, &shdr);
}
}
secmap_matches_content(SECMAP_CTF);
secmap_matches_content(SECMAP_SYMTAB);
secmap_matches_content(SECMAP_DEBUG);
for (size_t i = 0; i < secmap_count; i++) {
secmap_file_check(&secmaps[i]);
}
return (secmap_exit);
}