section: Added deep section inspection.

This allows loaded dynamic libraries to further provide default hooks,
suites, or even tests.

Fixes #160.
This commit is contained in:
Snaipe 2016-09-13 23:35:57 +02:00
parent 8c840a8fd2
commit 1c11db55ae
6 changed files with 238 additions and 137 deletions

View file

@ -21,15 +21,38 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define _GNU_SOURCE
#include "section.h"
#include "err.h"
#include <link.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#ifdef __FreeBSD__
# include <sys/elf_generic.h>
# define ElfW(type) ElfW_(Elf, type)
# define ElfW_(e, t) ElfW__(e, _ ## t)
# define ElfW__(e, t) e ## t
#endif
struct mod_handle {
int fd;
const ElfW(Ehdr) * map;
size_t len;
};
struct section_mapping {
const void *map;
size_t len;
size_t sec_len;
};
static int open_self(void)
{
#if defined (__linux__)
@ -71,7 +94,7 @@ do_open:
return open(fullpath, O_RDONLY);
}
static int open_module_map(mod_handle *mod)
static int open_module_map(struct mod_handle *mod)
{
/* Load the ELF header and the section header table */
const ElfW(Ehdr) * elf = mmap(NULL, sizeof (ElfW(Ehdr)),
@ -110,14 +133,19 @@ fail:
return 0;
}
static void close_module_map(mod_handle *mod)
static void close_module_map(struct mod_handle *mod)
{
munmap((void *) mod->map, mod->len);
}
int open_module_self(mod_handle *mod)
static int open_module(const char *name, struct mod_handle *mod)
{
int fd = open_self();
int fd;
if (!name[0])
fd = open_self();
else
fd = open(name, O_RDONLY);
if (fd == -1)
return 0;
@ -131,13 +159,14 @@ int open_module_self(mod_handle *mod)
return 1;
}
void close_module(mod_handle *mod)
static void close_module(struct mod_handle *mod)
{
close_module_map(mod);
close(mod->fd);
}
static const void *map_shdr(int fd, const ElfW (Shdr) *shdr, struct section_mapping *out)
static const void *map_shdr(int fd, const ElfW (Shdr) *shdr,
struct section_mapping *out)
{
size_t shdr_map_off = shdr->sh_offset & ~0xfffllu;
size_t shdr_map_len = shdr->sh_size + (shdr->sh_offset - shdr_map_off);
@ -161,8 +190,8 @@ static void unmap_shdr(struct section_mapping *map)
munmap((void *) map->map, map->len);
}
void *map_section_data(mod_handle *mod, const char *name,
struct section_mapping *map)
static int get_section_data(struct mod_handle *mod, const char *name,
void *base, struct cri_section *sect)
{
const ElfW(Shdr) * shdr = (void *) ((char *) mod->map + mod->map->e_shoff);
const ElfW(Shdr) * shstr_shdr = shdr + mod->map->e_shstrndx;
@ -170,22 +199,70 @@ void *map_section_data(mod_handle *mod, const char *name,
struct section_mapping shstr_map;
const char *shstr = map_shdr(mod->fd, shstr_shdr, &shstr_map);
const void *ptr = NULL;
for (size_t i = 0; i < mod->map->e_shnum; i++) {
const char *section_name = shstr + shdr[i].sh_name;
if (!strcmp(section_name, name)) {
const ElfW(Shdr) * hdr = shdr + i;
ptr = map_shdr(mod->fd, hdr, map);
map->sec_len = hdr->sh_size;
break;
sect->addr = (char *) base + hdr->sh_addr;
sect->length = hdr->sh_size;
unmap_shdr(&shstr_map);
return 1;
}
}
unmap_shdr(&shstr_map);
return (void *) ptr;
return 0;
}
void unmap_section_data(struct section_mapping *map)
struct callback {
const char *sectname;
struct cri_section *sect;
size_t size;
size_t i;
};
static int section_getaddr(struct dl_phdr_info *info,
CR_UNUSED size_t size, void *data)
{
unmap_shdr(map);
struct callback *ctx = data;
struct mod_handle mod;
if (!open_module(info->dlpi_name, &mod))
return 0;
struct cri_section sect;
if (get_section_data(&mod, ctx->sectname, (void *) info->dlpi_addr, &sect)) {
if (ctx->i >= ctx->size) {
ctx->size *= 1.5f;
ctx->sect = realloc(ctx->sect, sizeof (struct cri_section) * (ctx->size + 1));
if (!ctx->sect)
cr_panic("Could not allocate cri_section");
}
ctx->sect[ctx->i] = sect;
ctx->sect[ctx->i + 1].addr = NULL;
++ctx->i;
}
close_module(&mod);
return 0;
}
int cri_sections_getaddr(const char *sectname, struct cri_section **out)
{
struct callback ctx = {
.sectname = sectname,
.sect = malloc(sizeof (struct cri_section) * 3),
.size = 2,
};
if (!ctx.sect)
cr_panic("Could not allocate cri_section");
ctx.sect[0].addr = NULL;
dl_iterate_phdr(section_getaddr, &ctx);
*out = ctx.sect;
return 0;
}

View file

@ -51,24 +51,12 @@ static inline void *ptr_add(const void *ptr, size_t off)
return (char *) ptr + off;
}
int open_module_self(mod_handle *mod)
static int sections_getaddr(int lib, const char *sectname,
struct cri_section *sect)
{
*mod = 0;
return 1;
}
void close_module(mod_handle *mod)
{
(void) mod;
}
void *map_section_data(mod_handle *mod, const char *name,
struct section_mapping *map)
{
(void) map;
const struct mach_header *hdr = _dyld_get_image_header(*mod);
const struct mach_header *hdr = _dyld_get_image_header(lib);
const struct load_command *lc = ptr_add(hdr, sizeof (mach_hdr));
for (size_t i = 0; i < hdr->ncmds; ++i, lc = ptr_add(lc, lc->cmdsize)) {
if (lc->cmd == LC_SEGMENT) {
const struct segment_command *sc = (void *) lc;
@ -79,8 +67,9 @@ void *map_section_data(mod_handle *mod, const char *name,
if (strncmp(name, s->sectname, 16))
continue;
map->sec_len = s->size;
return get_real_address(*mod, (void *) (uintptr_t) s->addr);
sect->length = s->size;
sect->addr = get_real_address(lib, (void *) (uintptr_t) s->addr);
return 1;
}
} else if (lc->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *sc = (void *) lc;
@ -91,15 +80,46 @@ void *map_section_data(mod_handle *mod, const char *name,
if (strncmp(name, s->sectname, 16))
continue;
map->sec_len = s->size;
return get_real_address(*mod, (void *) (uintptr_t) s->addr);
sect->length = s->size;
sect->addr = get_real_address(lib, (void *) (uintptr_t) s->addr);
return 1;
}
}
}
return NULL;
return 0;
}
void unmap_section_data(struct section_mapping *map)
int cri_sections_getaddr(const char *sectname, struct cri_section **out)
{
(void) map;
struct cri_section *sect = malloc(sizeof (struct cri_section) * 3);
if (!sect)
cr_panic("Could not allocate cri_section");
sect[0].addr = NULL;
size_t i = 0;
size_t size = 2;
size_t nb_images = _dyld_image_count();
for (size_t lib = 0; lib < nb_images; ++lib) {
struct cri_section s;
if (!section_getaddr(lib, sectname, &s))
continue;
if (i >= size) {
size *= 1.5f;
sect = realloc(sect, sizeof (struct cri_section) * (size + 1));
if (!sect)
cr_panic("Could not allocate cri_section");
}
sect[i] = s;
sect[i + 1].addr = NULL;
++i;
}
*out = sect;
return 0;
}

View file

@ -21,39 +21,74 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "section.h"
#include <windows.h>
#include <tlhelp32.h>
int open_module_self(mod_handle *mod)
{
*mod = GetModuleHandle(NULL);
return 1;
}
#include "err.h"
#include "section.h"
void close_module(mod_handle *mod)
int section_getaddr(HMODULE mod, const char *sectname,
struct cri_section *sect)
{
(void) mod;
}
void *map_section_data(mod_handle *mod, const char *name,
struct section_mapping *map)
{
PIMAGE_DOS_HEADER dos_hdr = (PIMAGE_DOS_HEADER) *mod;
PIMAGE_DOS_HEADER dos_hdr = (PIMAGE_DOS_HEADER) mod;
PIMAGE_NT_HEADERS nt_hdr = (PIMAGE_NT_HEADERS) ((uintptr_t) dos_hdr
+ dos_hdr->e_lfanew);
PIMAGE_SECTION_HEADER sec_hdr = IMAGE_FIRST_SECTION(nt_hdr);
for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++, sec_hdr++) {
if (!strncmp((char *) sec_hdr->Name, name, IMAGE_SIZEOF_SHORT_NAME)) {
map->sec_len = sec_hdr->SizeOfRawData;
return (char *) dos_hdr + sec_hdr->VirtualAddress;
if (*(char *) sec_hdr == '/')
continue;
if (!strncmp((char *) sec_hdr->Name, sectname, IMAGE_SIZEOF_SHORT_NAME)) {
sect->length = sec_hdr->SizeOfRawData;
sect->addr = (char *) dos_hdr + sec_hdr->VirtualAddress;
return 1;
}
}
return NULL;
return 0;
}
void unmap_section_data(struct section_mapping *map)
int cri_sections_getaddr(const char *sectname, struct cri_section **out)
{
(void) map;
struct cri_section *sect = malloc(sizeof (struct cri_section) * 3);
if (!sect)
cr_panic("Could not allocate cri_section");
sect[0].addr = NULL;
size_t i = 0;
size_t size = 2;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
GetCurrentProcessId());
if (snap == INVALID_HANDLE_VALUE)
cr_panic("CreateToolhelp32Snapshot returned INVALID_HANDLE_VALUE");
MODULEENTRY32 mod = { .dwSize = sizeof (MODULEENTRY32) };
for (BOOL more = Module32First(snap, &mod); more;
more = Module32Next(snap, &mod)) {
struct cri_section s;
if (!section_getaddr(mod.hModule, sectname, &s))
continue;
if (i >= size) {
size *= 1.5f;
sect = realloc(sect, sizeof (struct cri_section) * (size + 1));
if (!sect)
cr_panic("Could not allocate cri_section");
}
sect[i] = s;
sect[i + 1].addr = NULL;
++i;
}
unsigned long err = (unsigned long) GetLastError();
if (err != ERROR_NO_MORE_FILES)
cr_panic("Error %lu while iterating modules", err);
*out = sect;
return 0;
}

View file

@ -26,41 +26,13 @@
#include "config.h"
#if defined (__ELF__)
# define MODULE_INVALID NULL
# include <link.h>
#include <stddef.h>
# ifdef __FreeBSD__
# include <sys/elf_generic.h>
# define ElfW(type) ElfW_(Elf, type)
# define ElfW_(e, t) ElfW__(e, _ ## t)
# define ElfW__(e, t) e ## t
# endif
typedef struct mod_handle {
int fd;
const ElfW(Ehdr) * map;
size_t len;
} mod_handle;
#elif defined (__APPLE__)
# define MODULE_INVALID -1
typedef int mod_handle;
#elif defined (_WIN32)
# include <windows.h>
# define MODULE_INVALID NULL
typedef HMODULE mod_handle;
#endif
struct section_mapping {
const void *map;
size_t len;
size_t sec_len;
struct cri_section {
void *addr;
size_t length;
};
int open_module_self(mod_handle *mod);
void close_module(mod_handle *mod);
void *map_section_data(mod_handle *mod, const char *name,
struct section_mapping *map);
void unmap_section_data(struct section_mapping *map);
int cri_sections_getaddr(const char *sectname, struct cri_section **sect);
#endif /* !SECTION_H_ */

View file

@ -37,21 +37,19 @@
#define CR_HSEC_STR_(S) CR_HSEC_STR__(S)
#define CR_HSEC_STR__(S) #S
#define IMPL_CALL_REPORT_HOOKS(Kind) \
void call_report_hooks_ ## Kind(void *data) { \
mod_handle self; \
struct section_mapping sect; \
if (!open_module_self(&self)) \
abort(); \
void *start = map_section_data(&self, CR_HSEC_STR(Kind), &sect); \
if (!start) { \
close_module(&self); \
return; \
} \
void *end = (char *) start + sect.sec_len; \
for (f_report_hook *hook = start; hook < (f_report_hook *) end; ++hook) \
(*hook ? *hook : nothing)(data); \
close_module(&self); \
#define IMPL_CALL_REPORT_HOOKS(Kind) \
void call_report_hooks_ ## Kind(void *data) { \
struct cri_section *sections; \
if (cri_sections_getaddr(CR_HSEC_STR(Kind), &sections)) \
return; \
for (struct cri_section *s = sections; s->addr; ++s) { \
void *start = s->addr; \
f_report_hook *end = (void *) \
((char *) s->addr + s->length); \
for (f_report_hook *hook = start; hook < end; ++hook) \
(*hook ? *hook : nothing)(data); \
} \
free(sections); \
}
IMPL_CALL_REPORT_HOOKS(PRE_ALL)

View file

@ -74,10 +74,6 @@ static msg_t msg_valgrind_jobs = "%sWarning! Criterion has detected "
"explicitely set. Reports might appear confusing!%s\n";
#endif
/* This is here to make the test suite & test sections non-empty */
CR_SECTION_("cr_sts") struct criterion_suite *dummy_suite = NULL;
CR_SECTION_("cr_tst") struct criterion_test *dummy_test = NULL;
static int cmp_suite(void *a, void *b)
{
struct criterion_suite *s1 = a, *s2 = b;
@ -125,50 +121,53 @@ struct criterion_test_set *criterion_init(void)
{
struct criterion_ordered_set *suites = new_ordered_set(cmp_suite, dtor_suite_set);
mod_handle self;
struct cri_section *sections = NULL;
if (!open_module_self(&self))
cr_panic("Could not open self executable file");
if (!cri_sections_getaddr("cr_sts", &sections)) {
for (struct cri_section *s = sections; s->addr; ++s) {
void *start = s->addr;
void *end = (char *) start + s->length;
struct section_mapping suite_sect = { .sec_len = 0 };
void *suite_start = map_section_data(&self, "cr_sts", &suite_sect);
void *suite_end = (char *) suite_start + suite_sect.sec_len;
FOREACH_SUITE_SEC(s, start, end) {
if (!*s || !*(*s)->name)
continue;
FOREACH_SUITE_SEC(s, suite_start, suite_end) {
if (!*s || !*(*s)->name)
continue;
struct criterion_suite_set css = {
.suite = **s,
};
insert_ordered_set(suites, &css, sizeof (css));
struct criterion_suite_set css = {
.suite = **s,
};
insert_ordered_set(suites, &css, sizeof (css));
}
}
}
free(sections);
struct criterion_test_set *set = smalloc(
.size = sizeof (struct criterion_test_set),
.dtor = dtor_test_set
);
.dtor = dtor_test_set);
*set = (struct criterion_test_set) {
suites,
0,
};
struct section_mapping test_sect = { .sec_len = 0 };
void *test_start = map_section_data(&self, "cr_tst", &test_sect);
void *test_end = (char *) test_start + test_sect.sec_len;
sections = NULL;
if (!cri_sections_getaddr("cr_tst", &sections)) {
for (struct cri_section *s = sections; s->addr; ++s) {
void *start = s->addr;
void *end = (char *) start + s->length;
FOREACH_TEST_SEC(test, test_start, test_end) {
if (!*test)
continue;
FOREACH_TEST_SEC(test, start, end) {
if (!*test)
continue;
if (!*(*test)->category || !*(*test)->name)
continue;
if (!*(*test)->category || !*(*test)->name)
continue;
criterion_register_test(set, *test);
criterion_register_test(set, *test);
}
}
}
close_module(&self);
free(sections);
return set;
}