// STILL WRITING: // find_wrapper_fncaddrs() must search through each library for matching symbols // Stop using __wrap_write and have plugin produce write instead. // This should use __dmtcp_plugin*_write from current library, // and then 'write' from all other libraries. // NEXT_FNC will call __real_write, and we still need to connect that. // We can make it call 'write' that will use wrappers address. // // BUG: buf[idx].addr = (char *)base_sym->st_value; is a small hexadecimal # // Incorrect at: find_wrapper_fncaddrs(), line 219 #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include // for dl_iterate_phdr(); #include #include #include #include #include "util-plugin.h" #define VERBOSE #ifdef VERBOSE # define PRINTF(...) printf(__VA_ARGS__) #else # define PRINTF(...) #endif #define MAX_WRAPPERS 100 wrapper_t * allocate_wrapper_pointers(const char *function) { wrapper_t wrappers_tmp[100]; int idx = -1; if (str_starts_with(function, "__dmtcp_") && (idx = str_index(function + strlen("__dmtcp_"), '_')) != -1) { function += strlen("__dmtcp_") + idx + 1; } else { dprintf(2, "allocate_wrapper_pointers called on '%s';" " doesn't begin with '__dmtcp_*_'\n", function); exit(1); } find_wrapper_fncaddrs(function, wrappers_tmp, sizeof(wrappers_tmp)/sizeof(wrappers_tmp[0])); int i; for (i = 0; i < sizeof(wrappers_tmp)/sizeof(wrappers_tmp[0]); i++) { if (wrappers_tmp[i].addr == NULL) break; } wrapper_t *wrappers = malloc((i+1)*sizeof(wrappers_tmp[0])); memcpy(wrappers, wrappers_tmp, (i+1)*sizeof(wrappers_tmp[0])); return wrappers; } // ============================================================ /************************************************************************** * http://stackoverflow.com/questions/8875876/getting-the-elf-header-of-the-main-executable **************************************************************************/ // This is called once for each shared object that was loaded. // data == dl_shobj_array_ptr static int dl_iterate_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { static int num_obj = 0; dl_shobj_t obj[100]; if (num_obj >= 100) { dprintf(2, "More than 100 objects. Please modify %s:%d\n", __FILE__, __LINE__ - 2); } Elf64_Sym * dynsym = NULL; char * dynstr = NULL; int dynsyment = 0; int dynstrsz = 0; Elf64_Shdr * hash = NULL; // Might not be avail. if gnu_hash is present Elf64_Shdr * gnu_hash = NULL; Elf64_Xword needed = 0; // Force default to "" (offset 0 in dynstr) Elf64_Xword soname = 0; // Force default to "" (offset 0 in dynstr) Elf64_Xword rpath = 0; // Force default to "" (offset 0 in dynstr) int j; PRINTF("relocation (lib base addr): 0x%lx\n", (long)info->dlpi_addr); PRINTF("info->dlpi_name: %s\n", ( info->dlpi_name[0] == '\0' ? "(base executable)" : info->dlpi_name )); for (j = 0; j < info->dlpi_phnum; j++) { Elf64_Phdr phdr = info->dlpi_phdr[j]; // Elf program header if (phdr.p_type == PT_LOAD) { PRINTF("Memory segment of %s loaded at %p\n", (*info->dlpi_name == ' ' ? info->dlpi_name : (char *)getauxval(AT_EXECFN)), (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr)); } if (phdr.p_type == PT_DYNAMIC) { PRINTF("PT_DYNAMIC found at %p\n", (void *) (info->dlpi_addr + phdr.p_vaddr)); // phdr now points to dynamic segment, containing just dynamic section dynsym = NULL; dynstr = NULL; dynsyment = 0; dynstrsz = 0; hash = NULL; // Might not be avail. if gnu_hash is present gnu_hash = NULL; needed = 0; // Force default to "" (offset 0 in dynstr) soname = 0; // Force default to "" (offset 0 in dynstr) rpath = 0; // Force default to "" (offset 0 in dynstr) Elf64_Dyn *dyn; dyn = (void *) (info->dlpi_addr + phdr.p_vaddr); for (dyn /*= (void *)phdr.p_vaddr*/; dyn->d_tag != DT_NULL; dyn++ ) { switch (dyn->d_tag) { case DT_NULL: PRINTF("DT_NULL found\n"); break; case DT_SYMTAB: // FIXME: When is d_ptr an offset and when is it the actual address? if (dyn->d_un.d_ptr > info->dlpi_addr) { dynsym = (void *)dyn->d_un.d_ptr; } else { dynsym = (void *)(info->dlpi_addr + dyn->d_un.d_ptr); } PRINTF("dynsym section found at %p\n", dynsym); break; case DT_STRTAB: // FIXME: When is d_ptr an offset and when is it the actual address? if (dyn->d_un.d_ptr > info->dlpi_addr) { dynstr = (void *)dyn->d_un.d_ptr; } else { dynstr = (void *)(info->dlpi_addr + dyn->d_un.d_ptr); } PRINTF("dynstr section found at %p as %s\n", dynstr, (dyn->d_un.d_ptr > info->dlpi_addr ? "virtual address (d_ptr)" : "offset from base addr (d_ptr)")); break; case DT_SYMENT: dynsyment = dyn->d_un.d_val; PRINTF("DT_SYMENT: size of dynsyment symbol entry: %d\n", dynsyment); break; case DT_STRSZ: dynstrsz = dyn->d_un.d_val; PRINTF("DT_STRSZ: size of dynstr table: %d\n", dynstrsz); break; case DT_NEEDED: // This is optional in executable and optional in shared object needed = dyn->d_un.d_val; break; case DT_SONAME: // This is ignored in executable and optional in shared object soname = dyn->d_un.d_val; break; case DT_RPATH: rpath = dyn->d_un.d_val; break; } } if (dynstr != NULL) { PRINTF("needed: %s (and possibly others)\n", dynstr + needed); PRINTF("soname: %s\n", dynstr + soname); PRINTF("rpath: %s\n", dynstr + soname); } else { PRINTF("dynstr was never set; Should be mandatory??\n"); } } } PRINTF("\n"); obj[num_obj].name = info->dlpi_name; obj[num_obj].base_addr = (char *)info->dlpi_addr; obj[num_obj].dynsym = dynsym; // round down to dynsyment alignment obj[num_obj].dynsymsz = (dynstr - (char *)dynsym) & ~(dynsyment-1); obj[num_obj].dynstr = dynstr; obj[num_obj].dynsyment = dynsyment; obj[num_obj].dynstrsz = dynstrsz; obj[num_obj].hash = hash; obj[num_obj].gnu_hash = gnu_hash; obj[num_obj].needed = needed; obj[num_obj].soname = soname; obj[num_obj].rpath = rpath; num_obj++; obj[num_obj].dynsym = NULL; // Set next entry as end-of-shared-objects *(dl_shobj_t **)data = obj; return 0; // 0 means success ('man dl_iterate_phdr' lies about this ret. val) } // Singleton: always returns same array dl_shobj_t *get_dynamic_symbols() { static dl_shobj_t *dl_shobj_array = NULL; if (dl_shobj_array == NULL) { dl_iterate_phdr(dl_iterate_phdr_callback, &dl_shobj_array); } return dl_shobj_array; } // ===================================================================== wrapper_t *find_wrapper_fncaddrs(const char *function, wrapper_t *buf, size_t len) { Elf64_Sym *symtab_ent; unsigned int symtab_size; char *strtab_ent; dl_shobj_t *dl_shobj_array = get_dynamic_symbols(); static int first_time = 1; if (first_time) { first_time = 0; int num_obj = 0; for ( num_obj = 0; dl_shobj_array[num_obj].dynsym != NULL; num_obj++) ; PRINTF("%d shared objects were found\n", num_obj); for ( num_obj = 0; dl_shobj_array[num_obj].dynsym != NULL; num_obj++) PRINTF("* %s\n", dl_shobj_array[num_obj].name); PRINTF("\n"); } int idx = 0; buf[idx].addr = NULL; int shobj_idx = 0; for ( shobj_idx = 0; dl_shobj_array[shobj_idx].dynsym != NULL; shobj_idx++) { int shobj_has_wrapper = 0; // initial assumption symtab_ent = dl_shobj_array[shobj_idx].dynsym; symtab_size = dl_shobj_array[shobj_idx].dynsymsz; strtab_ent = dl_shobj_array[shobj_idx].dynstr; if (dl_shobj_array[shobj_idx].base_addr == NULL) { continue; // This must be the base executable and not a library. } Elf64_Sym * sym = NULL; Elf64_Sym * base_sym = NULL; for ( sym = symtab_ent; (char *)sym < (char *)symtab_ent + symtab_size ; sym++) { if (sym->st_name != 0) { const char *symtab_symbol = strtab_ent + sym->st_name; if (str_starts_with(symtab_symbol, "__dmtcp_") && str_ends_with(symtab_symbol, function) && *(symtab_symbol + strlen(symtab_symbol) - strlen(function) - 1) == '_') { shobj_has_wrapper = 1; // wrapper fnc __dmtcp_*_FNC found buf[idx].addr = dl_shobj_array[shobj_idx].base_addr + sym->st_value; buf[idx++].name = symtab_symbol; } if (strcmp(symtab_symbol, function) == 0) { base_sym = sym; // Typically, this is from libc } } } // If a library has wrappers, __dmtcp_*_FNC, then the symbol FNC // is used only for interposition, and not as the base function. // Hence, a library may have wrapeprs or a base function, but not both. if (base_sym && ! shobj_has_wrapper) { buf[idx].addr = dl_shobj_array[shobj_idx].base_addr + base_sym->st_value; buf[idx++].name = strtab_ent + base_sym->st_name; } buf[idx].addr = NULL; buf[idx].name = NULL; } return buf; } // Find all wrappers of form: __dmtcp_*_SYMBOL // This version for debugging hasn't been tested. Can probably be removed. void print_wrapper_symbols(char *symbol, Elf64_Sym *symtab_ent, unsigned int symtab_size, char *strtab_ent) { Elf64_Sym * sym = NULL; for ( sym = symtab_ent; (char *)sym < (char *)symtab_ent + symtab_size ; sym++) { if (sym->st_name != 0) { const char *symtab_symbol = strtab_ent + sym->st_name; if (str_starts_with(symtab_symbol, "__dmtcp_") && str_ends_with(symtab_symbol, symbol) && *(symtab_symbol + strlen(symtab_symbol) - strlen(symbol) - 1) == '_') { printf("wrapper symbol: %s\n", strtab_ent + sym->st_name); printf("sym->st_value: %p\n", sym->st_value); } } } }