diff --git a/src/compat/section-elf.c b/src/compat/section-elf.c index 680935c..5c50a51 100644 --- a/src/compat/section-elf.c +++ b/src/compat/section-elf.c @@ -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 #include #include #include +#include #include #include #include +#ifdef __FreeBSD__ +# include +# 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; } diff --git a/src/compat/section-mach-o.c b/src/compat/section-mach-o.c index e7d406a..ad3b301 100644 --- a/src/compat/section-mach-o.c +++ b/src/compat/section-mach-o.c @@ -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; } diff --git a/src/compat/section-pe.c b/src/compat/section-pe.c index 044e2d8..47fe61b 100644 --- a/src/compat/section-pe.c +++ b/src/compat/section-pe.c @@ -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 +#include -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; } diff --git a/src/compat/section.h b/src/compat/section.h index a4325fb..e437e11 100644 --- a/src/compat/section.h +++ b/src/compat/section.h @@ -26,41 +26,13 @@ #include "config.h" -#if defined (__ELF__) -# define MODULE_INVALID NULL -# include +#include -# ifdef __FreeBSD__ -# include -# 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 -# 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_ */ diff --git a/src/core/report.c b/src/core/report.c index 3938949..fd1b820 100644 --- a/src/core/report.c +++ b/src/core/report.c @@ -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) diff --git a/src/core/runner.c b/src/core/runner.c index 7f2157e..16f4923 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -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; }