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:
parent
8c840a8fd2
commit
1c11db55ae
6 changed files with 238 additions and 137 deletions
|
@ -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, §)) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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), §); \
|
||||
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), §ions)) \
|
||||
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)
|
||||
|
|
|
@ -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", §ions)) {
|
||||
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", §ions)) {
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue