test.c (3644B)
1 #include <stdio.h> 2 #include <stddef.h> 3 #include <dlfcn.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <sys/types.h> 7 #include <unistd.h> 8 #include <sys/wait.h> 9 #include <elf.h> 10 #include <stdbool.h> 11 12 #define MAX_SYMBOL_LEN 32 13 #define MAX_NUM_SYMBOLS 128 14 char symbols[MAX_NUM_SYMBOLS*MAX_SYMBOL_LEN] = {0}; 15 char* symbols_off = (char*)&symbols; 16 17 #define MAX_SYMTABS 16 18 char symtabs[sizeof(Elf64_Shdr)*2*MAX_SYMTABS] = {0}; 19 char* symtabs_off = (char*)&symtabs; 20 21 void get_symbols(char* path) { 22 FILE* fptr = fopen(path, "r"); 23 Elf64_Ehdr header = {0}; 24 fread(&header, sizeof(Elf64_Ehdr), 1, fptr); 25 if (strncmp((char*)&header, ELFMAG, 3) != 0) { 26 printf("Attempted read of invalid ELF file."); 27 } 28 fseek(fptr, header.e_shoff, SEEK_SET); 29 Elf64_Shdr syment[2] = {0}; 30 for (int i = 0; i < header.e_shnum-1; i++) { 31 fread(&syment, sizeof(Elf64_Shdr), 2, fptr); // NOTE: questionable 32 if (syment[0].sh_type == SHT_SYMTAB && syment[1].sh_type == SHT_STRTAB) { 33 memcpy(symtabs_off, &syment[0], sizeof(Elf64_Shdr)); 34 memcpy(symtabs_off+sizeof(Elf64_Shdr), &syment[1], sizeof(Elf64_Shdr)); 35 symtabs_off = symtabs_off+(sizeof(Elf64_Shdr)*2); 36 } 37 fseek(fptr, -sizeof(Elf64_Shdr), SEEK_CUR); // HACK 38 } 39 for (int i = 0; i < (symtabs_off-symtabs)/(sizeof(Elf64_Shdr)*2); i++) { 40 Elf64_Shdr symtab0 = ((Elf64_Shdr*)symtabs)[i*2]; 41 Elf64_Shdr symtab1 = ((Elf64_Shdr*)symtabs)[i*2+1]; 42 43 fseek(fptr, symtab1.sh_offset, SEEK_SET); 44 char* strtbl = malloc(symtab1.sh_size); 45 fread(strtbl, symtab1.sh_size, 1, fptr); 46 fseek(fptr, symtab0.sh_offset, SEEK_SET); 47 Elf64_Sym symbol = {0}; 48 for (size_t i = 0; i < symtab0.sh_size; i+=sizeof(Elf64_Sym)) { 49 fread(&symbol, sizeof(Elf64_Sym), 1, fptr); 50 if (symbol.st_name != 0) { 51 char* mangled = (char*)(&strtbl[symbol.st_name]); 52 if (strncmp(mangled, "test_", 5) == 0) { 53 strcpy(symbols_off, mangled); 54 symbols_off += MAX_SYMBOL_LEN; 55 } 56 } 57 } 58 } 59 fclose(fptr); 60 } 61 62 int run_test_safe(bool(*testf)(void)) { 63 pid_t pid = fork(); 64 if (pid == 0) { 65 testf(); 66 exit(0); 67 } else { 68 int status; 69 waitpid(pid, &status, 0); 70 return status; 71 } 72 } 73 74 #define ANSI_RED "\x1b[31m" 75 #define ANSI_GREEN "\x1b[32m" 76 #define ANSI_BOLD "\x1b[1m" 77 #define ANSI_RESET "\x1b[0m" 78 79 int main(int argc, char** argv) { 80 bool imode = false; 81 if (argc > 2 && strcmp(argv[2], "-i") == 0) imode = true; 82 get_symbols(argv[1]); 83 char path[64] = {0}; 84 sprintf(path, "./%s", argv[1]); 85 void* lib = dlopen(path, RTLD_LAZY); 86 printf(ANSI_BOLD "Running tests...\n" ANSI_RESET); 87 size_t passed = 0; 88 size_t total = (symbols_off-symbols)/MAX_SYMBOL_LEN; 89 for (char* sym = symbols; sym < symbols_off; sym+=MAX_SYMBOL_LEN) { 90 printf(" %s ", sym+5); 91 fflush(STDIN_FILENO); 92 bool(*testf)(void) = dlsym(lib, sym); 93 int status = run_test_safe(testf); 94 95 if (status == 0) { 96 passed += 1; 97 puts(" " ANSI_GREEN "[GOOD]" ANSI_RESET); 98 } else { 99 printf(" " ANSI_RED "[BAD]" ANSI_RESET); 100 if (status != 1) printf(ANSI_RED " (%d)" ANSI_RESET, status); 101 printf("\n"); 102 } 103 if (status != 0 && status != 1 && imode) testf(); 104 } 105 printf("\n" ANSI_GREEN "%ld tests" ANSI_RESET " passed, " 106 ANSI_RED "%ld tests" ANSI_RESET " failed.\n" ANSI_RESET, 107 passed, total-passed); 108 dlclose(lib); 109 }