diff --git a/compiler-rt/lib/msan/msan_interceptors.cpp b/compiler-rt/lib/msan/msan_interceptors.cpp --- a/compiler-rt/lib/msan/msan_interceptors.cpp +++ b/compiler-rt/lib/msan/msan_interceptors.cpp @@ -1539,6 +1539,79 @@ return res; } +#if SANITIZER_WORDSIZE == 32 +struct sym { + u32 st_name; + u32 st_value; + u32 st_size; + unsigned char st_info; + unsigned char st_other; + u16 st_shndx; +}; +#elif SANITIZER_WORDSIZE == 64 +struct sym { + u32 st_name; + unsigned char st_info; + unsigned char st_other; + u16 st_shndx; + u64 st_value; + u64 st_size; +}; +#else +#error "Unexpected SANITIZER_WORDSIZE" +#endif + +struct link_map { + unsigned long l_addr; + char *l_name; + unsigned long l_ld; + struct link_map *l_next, *l_prev; +}; + +INTERCEPTOR(int, dladdr1, void *addr, dlinfo *info, void **extra_info, + int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, dladdr1, addr, info, extra_info, flags); + int res = REAL(dladdr1)(addr, info, extra_info, flags); + if (res != 0) { + __msan_unpoison(info, sizeof(*info)); + if (info->dli_fname) + __msan_unpoison(info->dli_fname, internal_strlen(info->dli_fname) + 1); + if (info->dli_sname) + __msan_unpoison(info->dli_sname, internal_strlen(info->dli_sname) + 1); + + if (flags == 1) { // RTLD_DL_SYMENT + __msan_unpoison(extra_info, sizeof(void*)); + + struct sym *s = *((struct sym **)(extra_info)); + __msan_unpoison(s, sizeof(struct sym)); + } else if (flags == 2) { // RTLD_DL_LINKMAP + __msan_unpoison(extra_info, sizeof(void*)); + + struct link_map *map = *((struct link_map **)(extra_info)); + + // Walk forward + struct link_map *ptr = map; + while (ptr) { + __msan_unpoison(ptr, sizeof(struct link_map)); + if (ptr->l_name) + __msan_unpoison(ptr->l_name, internal_strlen(ptr->l_name) + 1); + ptr = ptr->l_next; + } + + // Walk backward + ptr = map->l_prev; + while (ptr) { + __msan_unpoison(ptr, sizeof(struct link_map)); + if (ptr->l_name) + __msan_unpoison(ptr->l_name, internal_strlen(ptr->l_name) + 1); + ptr = ptr->l_prev; + } + } + } + return res; +} + INTERCEPTOR(char *, dlerror, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dlerror, fake); @@ -1786,6 +1859,7 @@ MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; INTERCEPT_FUNCTION(strsignal); INTERCEPT_FUNCTION(dladdr); + INTERCEPT_FUNCTION(dladdr1); INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); diff --git a/compiler-rt/test/msan/dladdr1_test.c b/compiler-rt/test/msan/dladdr1_test.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/msan/dladdr1_test.c @@ -0,0 +1,95 @@ +/* RUN: %clang_msan -g %s -o %t + RUN: %clang_msan -g %s -DBUILD_SO -fPIC -o %t-so.so -shared + RUN: %run %t 2>&1 | FileCheck %s + + REQUIRES: glibc{{.*}} +*/ + +#define _GNU_SOURCE + +#ifndef BUILD_SO +#include +#include +#include +#include +#include +#include + +typedef volatile long *(* get_t)(); +get_t GetTls; + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + int i; + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + GetTls = (get_t)dlsym(handle, "GetTls"); + assert(dlerror() == 0); + + { + printf("Testing RTLD_DL_LINKMAP\n"); + fflush(stdout); + + Dl_info info; + struct link_map *map_ptr; + int ret = dladdr1(GetTls, &info, (void**)(&map_ptr), RTLD_DL_LINKMAP); + assert(ret != 0); + printf("fname: %s\n", info.dli_fname); + printf("fbase: %p\n", info.dli_fbase); + printf("sname: %s\n", info.dli_sname); + // CHECK: sname: GetTls + printf("saddr: %p\n", info.dli_saddr); + + assert(map_ptr != NULL); + printf("map_ptr: %p\n", map_ptr); + fflush(stdout); + + // Find the start of the link map + while(map_ptr->l_prev != NULL) { + fflush(stdout); + map_ptr = map_ptr->l_prev; + } + + fflush(stdout); + while(map_ptr != NULL) { + assert(map_ptr->l_name != NULL); + printf("0x%lx: '%s', %p\n", map_ptr->l_addr, map_ptr->l_name, map_ptr->l_ld); + fflush(stdout); + map_ptr = map_ptr->l_next; + } + // CHECK: libc.so + // CHECK: dladdr1_test + } + + // Test RTLD_DL_SYMENT + + { + printf("Testing RTLD_DL_SYMENT\n"); + fflush(stdout); + + Dl_info info; + ElfW(Sym) *sym; + int ret = dladdr1(GetTls, &info, (void**)(&sym), RTLD_DL_SYMENT); + assert(ret != 0); + printf("fname: %s\n", info.dli_fname); + printf("fbase: %p\n", info.dli_fbase); + printf("sname: %s\n", info.dli_sname); + // CHECK: sname: GetTls + printf("saddr: %p\n", info.dli_saddr); + + printf("sym: %d %d %d %d %lu %lu\n", + sym->st_name, sym->st_info, sym->st_other, + sym->st_shndx, sym->st_value, sym->st_size); + // CHECK: sym: + } + return 0; +} +#else // BUILD_SO +long var; +long *GetTls() { + return &var; +} +#endif