michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "ElfLoader.h" michael@0: #include "CustomElf.h" michael@0: #include "Mappable.h" michael@0: #include "Logging.h" michael@0: #include michael@0: michael@0: #if defined(ANDROID) michael@0: #include michael@0: michael@0: #include michael@0: #if __ANDROID_API__ < 8 michael@0: /* Android API < 8 doesn't provide sigaltstack */ michael@0: michael@0: extern "C" { michael@0: michael@0: inline int sigaltstack(const stack_t *ss, stack_t *oss) { michael@0: return syscall(__NR_sigaltstack, ss, oss); michael@0: } michael@0: michael@0: } /* extern "C" */ michael@0: #endif /* __ANDROID_API__ */ michael@0: #endif /* ANDROID */ michael@0: michael@0: #ifdef __ARM_EABI__ michael@0: extern "C" const void * michael@0: __gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak)); michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /** michael@0: * dlfcn.h replacements functions michael@0: */ michael@0: michael@0: void * michael@0: __wrap_dlopen(const char *path, int flags) michael@0: { michael@0: RefPtr handle = ElfLoader::Singleton.Load(path, flags); michael@0: if (handle) michael@0: handle->AddDirectRef(); michael@0: return handle; michael@0: } michael@0: michael@0: const char * michael@0: __wrap_dlerror(void) michael@0: { michael@0: const char *error = ElfLoader::Singleton.lastError; michael@0: ElfLoader::Singleton.lastError = nullptr; michael@0: return error; michael@0: } michael@0: michael@0: void * michael@0: __wrap_dlsym(void *handle, const char *symbol) michael@0: { michael@0: if (!handle) { michael@0: ElfLoader::Singleton.lastError = "dlsym(NULL, sym) unsupported"; michael@0: return nullptr; michael@0: } michael@0: if (handle != RTLD_DEFAULT && handle != RTLD_NEXT) { michael@0: LibHandle *h = reinterpret_cast(handle); michael@0: return h->GetSymbolPtr(symbol); michael@0: } michael@0: return dlsym(handle, symbol); michael@0: } michael@0: michael@0: int michael@0: __wrap_dlclose(void *handle) michael@0: { michael@0: if (!handle) { michael@0: ElfLoader::Singleton.lastError = "No handle given to dlclose()"; michael@0: return -1; michael@0: } michael@0: reinterpret_cast(handle)->ReleaseDirectRef(); michael@0: return 0; michael@0: } michael@0: michael@0: int michael@0: __wrap_dladdr(void *addr, Dl_info *info) michael@0: { michael@0: RefPtr handle = ElfLoader::Singleton.GetHandleByPtr(addr); michael@0: if (!handle) michael@0: return 0; michael@0: info->dli_fname = handle->GetPath(); michael@0: return 1; michael@0: } michael@0: michael@0: int michael@0: __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data) michael@0: { michael@0: if (!ElfLoader::Singleton.dbg) michael@0: return -1; michael@0: michael@0: for (ElfLoader::DebuggerHelper::iterator it = ElfLoader::Singleton.dbg.begin(); michael@0: it < ElfLoader::Singleton.dbg.end(); ++it) { michael@0: dl_phdr_info info; michael@0: info.dlpi_addr = reinterpret_cast(it->l_addr); michael@0: info.dlpi_name = it->l_name; michael@0: info.dlpi_phdr = nullptr; michael@0: info.dlpi_phnum = 0; michael@0: michael@0: // Assuming l_addr points to Elf headers (in most cases, this is true), michael@0: // get the Phdr location from there. michael@0: uint8_t mapped; michael@0: // If the page is not mapped, mincore returns an error. michael@0: if (!mincore(const_cast(it->l_addr), PageSize(), &mapped)) { michael@0: const Elf::Ehdr *ehdr = Elf::Ehdr::validate(it->l_addr); michael@0: if (ehdr) { michael@0: info.dlpi_phdr = reinterpret_cast( michael@0: reinterpret_cast(ehdr) + ehdr->e_phoff); michael@0: info.dlpi_phnum = ehdr->e_phnum; michael@0: } michael@0: } michael@0: michael@0: int ret = callback(&info, sizeof(dl_phdr_info), data); michael@0: if (ret) michael@0: return ret; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: #ifdef __ARM_EABI__ michael@0: const void * michael@0: __wrap___gnu_Unwind_Find_exidx(void *pc, int *pcount) michael@0: { michael@0: RefPtr handle = ElfLoader::Singleton.GetHandleByPtr(pc); michael@0: if (handle) michael@0: return handle->FindExidx(pcount); michael@0: if (__gnu_Unwind_Find_exidx) michael@0: return __gnu_Unwind_Find_exidx(pc, pcount); michael@0: *pcount = 0; michael@0: return nullptr; michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * faulty.lib public API michael@0: */ michael@0: michael@0: MFBT_API size_t michael@0: __dl_get_mappable_length(void *handle) { michael@0: if (!handle) michael@0: return 0; michael@0: return reinterpret_cast(handle)->GetMappableLength(); michael@0: } michael@0: michael@0: MFBT_API void * michael@0: __dl_mmap(void *handle, void *addr, size_t length, off_t offset) michael@0: { michael@0: if (!handle) michael@0: return nullptr; michael@0: return reinterpret_cast(handle)->MappableMMap(addr, length, michael@0: offset); michael@0: } michael@0: michael@0: MFBT_API void michael@0: __dl_munmap(void *handle, void *addr, size_t length) michael@0: { michael@0: if (!handle) michael@0: return; michael@0: return reinterpret_cast(handle)->MappableMUnmap(addr, length); michael@0: } michael@0: michael@0: MFBT_API bool michael@0: IsSignalHandlingBroken() michael@0: { michael@0: return ElfLoader::Singleton.isSignalHandlingBroken(); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: /** michael@0: * Returns the part after the last '/' for the given path michael@0: */ michael@0: const char * michael@0: LeafName(const char *path) michael@0: { michael@0: const char *lastSlash = strrchr(path, '/'); michael@0: if (lastSlash) michael@0: return lastSlash + 1; michael@0: return path; michael@0: } michael@0: michael@0: } /* Anonymous namespace */ michael@0: michael@0: /** michael@0: * LibHandle michael@0: */ michael@0: LibHandle::~LibHandle() michael@0: { michael@0: free(path); michael@0: } michael@0: michael@0: const char * michael@0: LibHandle::GetName() const michael@0: { michael@0: return path ? LeafName(path) : nullptr; michael@0: } michael@0: michael@0: size_t michael@0: LibHandle::GetMappableLength() const michael@0: { michael@0: if (!mappable) michael@0: mappable = GetMappable(); michael@0: if (!mappable) michael@0: return 0; michael@0: return mappable->GetLength(); michael@0: } michael@0: michael@0: void * michael@0: LibHandle::MappableMMap(void *addr, size_t length, off_t offset) const michael@0: { michael@0: if (!mappable) michael@0: mappable = GetMappable(); michael@0: if (!mappable) michael@0: return MAP_FAILED; michael@0: void* mapped = mappable->mmap(addr, length, PROT_READ, MAP_PRIVATE, offset); michael@0: if (mapped != MAP_FAILED) { michael@0: /* Ensure the availability of all pages within the mapping */ michael@0: for (size_t off = 0; off < length; off += PageSize()) { michael@0: mappable->ensure(reinterpret_cast(mapped) + off); michael@0: } michael@0: } michael@0: return mapped; michael@0: } michael@0: michael@0: void michael@0: LibHandle::MappableMUnmap(void *addr, size_t length) const michael@0: { michael@0: if (mappable) michael@0: mappable->munmap(addr, length); michael@0: } michael@0: michael@0: /** michael@0: * SystemElf michael@0: */ michael@0: TemporaryRef michael@0: SystemElf::Load(const char *path, int flags) michael@0: { michael@0: /* The Android linker returns a handle when the file name matches an michael@0: * already loaded library, even when the full path doesn't exist */ michael@0: if (path && path[0] == '/' && (access(path, F_OK) == -1)){ michael@0: DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, (void *)nullptr); michael@0: return nullptr; michael@0: } michael@0: michael@0: void *handle = dlopen(path, flags); michael@0: DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, handle); michael@0: ElfLoader::Singleton.lastError = dlerror(); michael@0: if (handle) { michael@0: SystemElf *elf = new SystemElf(path, handle); michael@0: ElfLoader::Singleton.Register(elf); michael@0: return elf; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: SystemElf::~SystemElf() michael@0: { michael@0: if (!dlhandle) michael@0: return; michael@0: DEBUG_LOG("dlclose(%p [\"%s\"])", dlhandle, GetPath()); michael@0: dlclose(dlhandle); michael@0: ElfLoader::Singleton.lastError = dlerror(); michael@0: ElfLoader::Singleton.Forget(this); michael@0: } michael@0: michael@0: void * michael@0: SystemElf::GetSymbolPtr(const char *symbol) const michael@0: { michael@0: void *sym = dlsym(dlhandle, symbol); michael@0: DEBUG_LOG("dlsym(%p [\"%s\"], \"%s\") = %p", dlhandle, GetPath(), symbol, sym); michael@0: ElfLoader::Singleton.lastError = dlerror(); michael@0: return sym; michael@0: } michael@0: michael@0: Mappable * michael@0: SystemElf::GetMappable() const michael@0: { michael@0: const char *path = GetPath(); michael@0: if (!path) michael@0: return nullptr; michael@0: #ifdef ANDROID michael@0: /* On Android, if we don't have the full path, try in /system/lib */ michael@0: const char *name = LeafName(path); michael@0: std::string systemPath; michael@0: if (name == path) { michael@0: systemPath = "/system/lib/"; michael@0: systemPath += path; michael@0: path = systemPath.c_str(); michael@0: } michael@0: #endif michael@0: michael@0: return MappableFile::Create(path); michael@0: } michael@0: michael@0: #ifdef __ARM_EABI__ michael@0: const void * michael@0: SystemElf::FindExidx(int *pcount) const michael@0: { michael@0: /* TODO: properly implement when ElfLoader::GetHandleByPtr michael@0: does return SystemElf handles */ michael@0: *pcount = 0; michael@0: return nullptr; michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * ElfLoader michael@0: */ michael@0: michael@0: /* Unique ElfLoader instance */ michael@0: ElfLoader ElfLoader::Singleton; michael@0: michael@0: TemporaryRef michael@0: ElfLoader::Load(const char *path, int flags, LibHandle *parent) michael@0: { michael@0: /* Ensure logging is initialized or refresh if environment changed. */ michael@0: Logging::Init(); michael@0: michael@0: RefPtr handle; michael@0: michael@0: /* Handle dlopen(nullptr) directly. */ michael@0: if (!path) { michael@0: handle = SystemElf::Load(nullptr, flags); michael@0: return handle; michael@0: } michael@0: michael@0: /* TODO: Handle relative paths correctly */ michael@0: const char *name = LeafName(path); michael@0: michael@0: /* Search the list of handles we already have for a match. When the given michael@0: * path is not absolute, compare file names, otherwise compare full paths. */ michael@0: if (name == path) { michael@0: for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) michael@0: if ((*it)->GetName() && (strcmp((*it)->GetName(), name) == 0)) michael@0: return *it; michael@0: } else { michael@0: for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) michael@0: if ((*it)->GetPath() && (strcmp((*it)->GetPath(), path) == 0)) michael@0: return *it; michael@0: } michael@0: michael@0: char *abs_path = nullptr; michael@0: const char *requested_path = path; michael@0: michael@0: /* When the path is not absolute and the library is being loaded for michael@0: * another, first try to load the library from the directory containing michael@0: * that parent library. */ michael@0: if ((name == path) && parent) { michael@0: const char *parentPath = parent->GetPath(); michael@0: abs_path = new char[strlen(parentPath) + strlen(path)]; michael@0: strcpy(abs_path, parentPath); michael@0: char *slash = strrchr(abs_path, '/'); michael@0: strcpy(slash + 1, path); michael@0: path = abs_path; michael@0: } michael@0: michael@0: Mappable *mappable = GetMappableFromPath(path); michael@0: michael@0: /* Try loading with the custom linker if we have a Mappable */ michael@0: if (mappable) michael@0: handle = CustomElf::Load(mappable, path, flags); michael@0: michael@0: /* Try loading with the system linker if everything above failed */ michael@0: if (!handle) michael@0: handle = SystemElf::Load(path, flags); michael@0: michael@0: /* If we didn't have an absolute path and haven't been able to load michael@0: * a library yet, try in the system search path */ michael@0: if (!handle && abs_path) michael@0: handle = SystemElf::Load(name, flags); michael@0: michael@0: delete [] abs_path; michael@0: DEBUG_LOG("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", requested_path, flags, michael@0: reinterpret_cast(parent), parent ? parent->GetPath() : "", michael@0: static_cast(handle)); michael@0: michael@0: return handle; michael@0: } michael@0: michael@0: mozilla::TemporaryRef michael@0: ElfLoader::GetHandleByPtr(void *addr) michael@0: { michael@0: /* Scan the list of handles we already have for a match */ michael@0: for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) { michael@0: if ((*it)->Contains(addr)) michael@0: return *it; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: Mappable * michael@0: ElfLoader::GetMappableFromPath(const char *path) michael@0: { michael@0: const char *name = LeafName(path); michael@0: Mappable *mappable = nullptr; michael@0: RefPtr zip; michael@0: const char *subpath; michael@0: if ((subpath = strchr(path, '!'))) { michael@0: char *zip_path = strndup(path, subpath - path); michael@0: while (*(++subpath) == '/') { } michael@0: zip = ZipCollection::GetZip(zip_path); michael@0: Zip::Stream s; michael@0: if (zip && zip->GetStream(subpath, &s)) { michael@0: /* When the MOZ_LINKER_EXTRACT environment variable is set to "1", michael@0: * compressed libraries are going to be (temporarily) extracted as michael@0: * files, in the directory pointed by the MOZ_LINKER_CACHE michael@0: * environment variable. */ michael@0: const char *extract = getenv("MOZ_LINKER_EXTRACT"); michael@0: if (extract && !strncmp(extract, "1", 2 /* Including '\0' */)) michael@0: mappable = MappableExtractFile::Create(name, zip, &s); michael@0: if (!mappable) { michael@0: if (s.GetType() == Zip::Stream::DEFLATE) { michael@0: mappable = MappableDeflate::Create(name, zip, &s); michael@0: } else if (s.GetType() == Zip::Stream::STORE) { michael@0: mappable = MappableSeekableZStream::Create(name, zip, &s); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: /* If we couldn't load above, try with a MappableFile */ michael@0: if (!mappable && !zip) michael@0: mappable = MappableFile::Create(path); michael@0: michael@0: return mappable; michael@0: } michael@0: michael@0: void michael@0: ElfLoader::Register(LibHandle *handle) michael@0: { michael@0: handles.push_back(handle); michael@0: if (dbg && !handle->IsSystemElf()) michael@0: dbg.Add(static_cast(handle)); michael@0: } michael@0: michael@0: void michael@0: ElfLoader::Forget(LibHandle *handle) michael@0: { michael@0: /* Ensure logging is initialized or refresh if environment changed. */ michael@0: Logging::Init(); michael@0: michael@0: LibHandleList::iterator it = std::find(handles.begin(), handles.end(), handle); michael@0: if (it != handles.end()) { michael@0: DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"])", reinterpret_cast(handle), michael@0: handle->GetPath()); michael@0: if (dbg && !handle->IsSystemElf()) michael@0: dbg.Remove(static_cast(handle)); michael@0: handles.erase(it); michael@0: } else { michael@0: DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"]): Handle not found", michael@0: reinterpret_cast(handle), handle->GetPath()); michael@0: } michael@0: } michael@0: michael@0: ElfLoader::~ElfLoader() michael@0: { michael@0: LibHandleList list; michael@0: /* Build up a list of all library handles with direct (external) references. michael@0: * We actually skip system library handles because we want to keep at least michael@0: * some of these open. Most notably, Mozilla codebase keeps a few libgnome michael@0: * libraries deliberately open because of the mess that libORBit destruction michael@0: * is. dlclose()ing these libraries actually leads to problems. */ michael@0: for (LibHandleList::reverse_iterator it = handles.rbegin(); michael@0: it < handles.rend(); ++it) { michael@0: if ((*it)->DirectRefCount()) { michael@0: if ((*it)->IsSystemElf()) { michael@0: static_cast(*it)->Forget(); michael@0: } else { michael@0: list.push_back(*it); michael@0: } michael@0: } michael@0: } michael@0: /* Force release all external references to the handles collected above */ michael@0: for (LibHandleList::iterator it = list.begin(); it < list.end(); ++it) { michael@0: while ((*it)->ReleaseDirectRef()) { } michael@0: } michael@0: /* Remove the remaining system handles. */ michael@0: if (handles.size()) { michael@0: list = handles; michael@0: for (LibHandleList::reverse_iterator it = list.rbegin(); michael@0: it < list.rend(); ++it) { michael@0: if ((*it)->IsSystemElf()) { michael@0: DEBUG_LOG("ElfLoader::~ElfLoader(): Remaining handle for \"%s\" " michael@0: "[%d direct refs, %d refs total]", (*it)->GetPath(), michael@0: (*it)->DirectRefCount(), (*it)->refCount()); michael@0: } else { michael@0: DEBUG_LOG("ElfLoader::~ElfLoader(): Unexpected remaining handle for \"%s\" " michael@0: "[%d direct refs, %d refs total]", (*it)->GetPath(), michael@0: (*it)->DirectRefCount(), (*it)->refCount()); michael@0: /* Not removing, since it could have references to other libraries, michael@0: * destroying them as a side effect, and possibly leaving dangling michael@0: * pointers in the handle list we're scanning */ michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: ElfLoader::stats(const char *when) michael@0: { michael@0: for (LibHandleList::iterator it = Singleton.handles.begin(); michael@0: it < Singleton.handles.end(); ++it) michael@0: if (!(*it)->IsSystemElf()) michael@0: static_cast(*it)->stats(when); michael@0: } michael@0: michael@0: #ifdef __ARM_EABI__ michael@0: int michael@0: ElfLoader::__wrap_aeabi_atexit(void *that, ElfLoader::Destructor destructor, michael@0: void *dso_handle) michael@0: { michael@0: Singleton.destructors.push_back( michael@0: DestructorCaller(destructor, that, dso_handle)); michael@0: return 0; michael@0: } michael@0: #else michael@0: int michael@0: ElfLoader::__wrap_cxa_atexit(ElfLoader::Destructor destructor, void *that, michael@0: void *dso_handle) michael@0: { michael@0: Singleton.destructors.push_back( michael@0: DestructorCaller(destructor, that, dso_handle)); michael@0: return 0; michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: ElfLoader::__wrap_cxa_finalize(void *dso_handle) michael@0: { michael@0: /* Call all destructors for the given DSO handle in reverse order they were michael@0: * registered. */ michael@0: std::vector::reverse_iterator it; michael@0: for (it = Singleton.destructors.rbegin(); michael@0: it < Singleton.destructors.rend(); ++it) { michael@0: if (it->IsForHandle(dso_handle)) { michael@0: it->Call(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: ElfLoader::DestructorCaller::Call() michael@0: { michael@0: if (destructor) { michael@0: DEBUG_LOG("ElfLoader::DestructorCaller::Call(%p, %p, %p)", michael@0: FunctionPtr(destructor), object, dso_handle); michael@0: destructor(object); michael@0: destructor = nullptr; michael@0: } michael@0: } michael@0: michael@0: ElfLoader::DebuggerHelper::DebuggerHelper(): dbg(nullptr) michael@0: { michael@0: /* Find ELF auxiliary vectors. michael@0: * michael@0: * The kernel stores the following data on the stack when starting a michael@0: * program: michael@0: * argc michael@0: * argv[0] (pointer into argv strings defined below) michael@0: * argv[1] (likewise) michael@0: * ... michael@0: * argv[argc - 1] (likewise) michael@0: * nullptr michael@0: * envp[0] (pointer into environment strings defined below) michael@0: * envp[1] (likewise) michael@0: * ... michael@0: * envp[n] (likewise) michael@0: * nullptr michael@0: * ... (more NULLs on some platforms such as Android 4.3) michael@0: * auxv[0] (first ELF auxiliary vector) michael@0: * auxv[1] (second ELF auxiliary vector) michael@0: * ... michael@0: * auxv[p] (last ELF auxiliary vector) michael@0: * (AT_NULL, nullptr) michael@0: * padding michael@0: * argv strings, separated with '\0' michael@0: * environment strings, separated with '\0' michael@0: * nullptr michael@0: * michael@0: * What we are after are the auxv values defined by the following struct. michael@0: */ michael@0: struct AuxVector { michael@0: Elf::Addr type; michael@0: Elf::Addr value; michael@0: }; michael@0: michael@0: /* Pointer to the environment variables list */ michael@0: extern char **environ; michael@0: michael@0: /* The environment may have changed since the program started, in which michael@0: * case the environ variables list isn't the list the kernel put on stack michael@0: * anymore. But in this new list, variables that didn't change still point michael@0: * to the strings the kernel put on stack. It is quite unlikely that two michael@0: * modified environment variables point to two consecutive strings in memory, michael@0: * so we assume that if two consecutive environment variables point to two michael@0: * consecutive strings, we found strings the kernel put on stack. */ michael@0: char **env; michael@0: for (env = environ; *env; env++) michael@0: if (*env + strlen(*env) + 1 == env[1]) michael@0: break; michael@0: if (!*env) michael@0: return; michael@0: michael@0: /* Next, we scan the stack backwards to find a pointer to one of those michael@0: * strings we found above, which will give us the location of the original michael@0: * envp list. As we are looking for pointers, we need to look at 32-bits or michael@0: * 64-bits aligned values, depening on the architecture. */ michael@0: char **scan = reinterpret_cast( michael@0: reinterpret_cast(*env) & ~(sizeof(void *) - 1)); michael@0: while (*env != *scan) michael@0: scan--; michael@0: michael@0: /* Finally, scan forward to find the last environment variable pointer and michael@0: * thus the first auxiliary vector. */ michael@0: while (*scan++); michael@0: michael@0: /* Some platforms have more NULLs here, so skip them if we encounter them */ michael@0: while (!*scan) michael@0: scan++; michael@0: michael@0: AuxVector *auxv = reinterpret_cast(scan); michael@0: michael@0: /* The two values of interest in the auxiliary vectors are AT_PHDR and michael@0: * AT_PHNUM, which gives us the the location and size of the ELF program michael@0: * headers. */ michael@0: Array phdrs; michael@0: char *base = nullptr; michael@0: while (auxv->type) { michael@0: if (auxv->type == AT_PHDR) { michael@0: phdrs.Init(reinterpret_cast(auxv->value)); michael@0: /* Assume the base address is the first byte of the same page */ michael@0: base = reinterpret_cast(PageAlignedPtr(auxv->value)); michael@0: } michael@0: if (auxv->type == AT_PHNUM) michael@0: phdrs.Init(auxv->value); michael@0: auxv++; michael@0: } michael@0: michael@0: if (!phdrs) { michael@0: DEBUG_LOG("Couldn't find program headers"); michael@0: return; michael@0: } michael@0: michael@0: /* In some cases, the address for the program headers we get from the michael@0: * auxiliary vectors is not mapped, because of the PT_LOAD segments michael@0: * definitions in the program executable. Trying to map anonymous memory michael@0: * with a hint giving the base address will return a different address michael@0: * if something is mapped there, and the base address otherwise. */ michael@0: MappedPtr mem(MemoryRange::mmap(base, PageSize(), PROT_NONE, michael@0: MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); michael@0: if (mem == base) { michael@0: /* If program headers aren't mapped, try to map them */ michael@0: int fd = open("/proc/self/exe", O_RDONLY); michael@0: if (fd == -1) { michael@0: DEBUG_LOG("Failed to open /proc/self/exe"); michael@0: return; michael@0: } michael@0: mem.Assign(MemoryRange::mmap(base, PageSize(), PROT_READ, MAP_PRIVATE, michael@0: fd, 0)); michael@0: /* If we don't manage to map at the right address, just give up. */ michael@0: if (mem != base) { michael@0: DEBUG_LOG("Couldn't read program headers"); michael@0: return; michael@0: } michael@0: } michael@0: /* Sanity check: the first bytes at the base address should be an ELF michael@0: * header. */ michael@0: if (!Elf::Ehdr::validate(base)) { michael@0: DEBUG_LOG("Couldn't find program base"); michael@0: return; michael@0: } michael@0: michael@0: /* Search for the program PT_DYNAMIC segment */ michael@0: Array dyns; michael@0: for (Array::iterator phdr = phdrs.begin(); phdr < phdrs.end(); michael@0: ++phdr) { michael@0: /* While the program headers are expected within the first mapped page of michael@0: * the program executable, the executable PT_LOADs may actually make them michael@0: * loaded at an address that is not the wanted base address of the michael@0: * library. We thus need to adjust the base address, compensating for the michael@0: * virtual address of the PT_LOAD segment corresponding to offset 0. */ michael@0: if (phdr->p_type == PT_LOAD && phdr->p_offset == 0) michael@0: base -= phdr->p_vaddr; michael@0: if (phdr->p_type == PT_DYNAMIC) michael@0: dyns.Init(base + phdr->p_vaddr, phdr->p_filesz); michael@0: } michael@0: if (!dyns) { michael@0: DEBUG_LOG("Failed to find PT_DYNAMIC section in program"); michael@0: return; michael@0: } michael@0: michael@0: /* Search for the DT_DEBUG information */ michael@0: for (Array::iterator dyn = dyns.begin(); dyn < dyns.end(); ++dyn) { michael@0: if (dyn->d_tag == DT_DEBUG) { michael@0: dbg = reinterpret_cast(dyn->d_un.d_ptr); michael@0: break; michael@0: } michael@0: } michael@0: DEBUG_LOG("DT_DEBUG points at %p", static_cast(dbg)); michael@0: } michael@0: michael@0: /** michael@0: * Helper class to ensure the given pointer is writable within the scope of michael@0: * an instance. Permissions to the memory page where the pointer lies are michael@0: * restored to their original value when the instance is destroyed. michael@0: */ michael@0: class EnsureWritable michael@0: { michael@0: public: michael@0: template michael@0: EnsureWritable(T *ptr, size_t length_ = sizeof(T)) michael@0: { michael@0: MOZ_ASSERT(length_ < PageSize()); michael@0: prot = -1; michael@0: page = MAP_FAILED; michael@0: michael@0: char *firstPage = PageAlignedPtr(reinterpret_cast(ptr)); michael@0: char *lastPageEnd = PageAlignedEndPtr(reinterpret_cast(ptr) + length_); michael@0: length = lastPageEnd - firstPage; michael@0: uintptr_t start = reinterpret_cast(firstPage); michael@0: uintptr_t end; michael@0: michael@0: prot = getProt(start, &end); michael@0: if (prot == -1 || (start + length) > end) michael@0: MOZ_CRASH(); michael@0: michael@0: if (prot & PROT_WRITE) michael@0: return; michael@0: michael@0: page = firstPage; michael@0: mprotect(page, length, prot | PROT_WRITE); michael@0: } michael@0: michael@0: ~EnsureWritable() michael@0: { michael@0: if (page != MAP_FAILED) { michael@0: mprotect(page, length, prot); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: int getProt(uintptr_t addr, uintptr_t *end) michael@0: { michael@0: /* The interesting part of the /proc/self/maps format looks like: michael@0: * startAddr-endAddr rwxp */ michael@0: int result = 0; michael@0: AutoCloseFILE f(fopen("/proc/self/maps", "r")); michael@0: while (f) { michael@0: unsigned long long startAddr, endAddr; michael@0: char perms[5]; michael@0: if (fscanf(f, "%llx-%llx %4s %*1024[^\n] ", &startAddr, &endAddr, perms) != 3) michael@0: return -1; michael@0: if (addr < startAddr || addr >= endAddr) michael@0: continue; michael@0: if (perms[0] == 'r') michael@0: result |= PROT_READ; michael@0: else if (perms[0] != '-') michael@0: return -1; michael@0: if (perms[1] == 'w') michael@0: result |= PROT_WRITE; michael@0: else if (perms[1] != '-') michael@0: return -1; michael@0: if (perms[2] == 'x') michael@0: result |= PROT_EXEC; michael@0: else if (perms[2] != '-') michael@0: return -1; michael@0: *end = endAddr; michael@0: return result; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: int prot; michael@0: void *page; michael@0: size_t length; michael@0: }; michael@0: michael@0: /** michael@0: * The system linker maintains a doubly linked list of library it loads michael@0: * for use by the debugger. Unfortunately, it also uses the list pointers michael@0: * in a lot of operations and adding our data in the list is likely to michael@0: * trigger crashes when the linker tries to use data we don't provide or michael@0: * that fall off the amount data we allocated. Fortunately, the linker only michael@0: * traverses the list forward and accesses the head of the list from a michael@0: * private pointer instead of using the value in the r_debug structure. michael@0: * This means we can safely add members at the beginning of the list. michael@0: * Unfortunately, gdb checks the coherency of l_prev values, so we have michael@0: * to adjust the l_prev value for the first element the system linker michael@0: * knows about. Fortunately, it doesn't use l_prev, and the first element michael@0: * is not ever going to be released before our elements, since it is the michael@0: * program executable, so the system linker should not be changing michael@0: * r_debug::r_map. michael@0: */ michael@0: void michael@0: ElfLoader::DebuggerHelper::Add(ElfLoader::link_map *map) michael@0: { michael@0: if (!dbg->r_brk) michael@0: return; michael@0: dbg->r_state = r_debug::RT_ADD; michael@0: dbg->r_brk(); michael@0: map->l_prev = nullptr; michael@0: map->l_next = dbg->r_map; michael@0: if (!firstAdded) { michael@0: firstAdded = map; michael@0: /* When adding a library for the first time, r_map points to data michael@0: * handled by the system linker, and that data may be read-only */ michael@0: EnsureWritable w(&dbg->r_map->l_prev); michael@0: dbg->r_map->l_prev = map; michael@0: } else michael@0: dbg->r_map->l_prev = map; michael@0: dbg->r_map = map; michael@0: dbg->r_state = r_debug::RT_CONSISTENT; michael@0: dbg->r_brk(); michael@0: } michael@0: michael@0: void michael@0: ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map) michael@0: { michael@0: if (!dbg->r_brk) michael@0: return; michael@0: dbg->r_state = r_debug::RT_DELETE; michael@0: dbg->r_brk(); michael@0: if (dbg->r_map == map) michael@0: dbg->r_map = map->l_next; michael@0: else michael@0: map->l_prev->l_next = map->l_next; michael@0: if (map == firstAdded) { michael@0: firstAdded = map->l_prev; michael@0: /* When removing the first added library, its l_next is going to be michael@0: * data handled by the system linker, and that data may be read-only */ michael@0: EnsureWritable w(&map->l_next->l_prev); michael@0: map->l_next->l_prev = map->l_prev; michael@0: } else michael@0: map->l_next->l_prev = map->l_prev; michael@0: dbg->r_state = r_debug::RT_CONSISTENT; michael@0: dbg->r_brk(); michael@0: } michael@0: michael@0: #if defined(ANDROID) michael@0: /* As some system libraries may be calling signal() or sigaction() to michael@0: * set a SIGSEGV handler, effectively breaking MappableSeekableZStream, michael@0: * or worse, restore our SIGSEGV handler with wrong flags (which using michael@0: * signal() will do), we want to hook into the system's sigaction() to michael@0: * replace it with our own wrapper instead, so that our handler is never michael@0: * replaced. We used to only do that with libraries this linker loads, michael@0: * but it turns out at least one system library does call signal() and michael@0: * breaks us (libsc-a3xx.so on the Samsung Galaxy S4). michael@0: * As libc's signal (bsd_signal/sysv_signal, really) calls sigaction michael@0: * under the hood, instead of calling the signal system call directly, michael@0: * we only need to hook sigaction. This is true for both bionic and michael@0: * glibc. michael@0: */ michael@0: michael@0: /* libc's sigaction */ michael@0: extern "C" int michael@0: sigaction(int signum, const struct sigaction *act, michael@0: struct sigaction *oldact); michael@0: michael@0: /* Simple reimplementation of sigaction. This is roughly equivalent michael@0: * to the assembly that comes in bionic, but not quite equivalent to michael@0: * glibc's implementation, so we only use this on Android. */ michael@0: int michael@0: sys_sigaction(int signum, const struct sigaction *act, michael@0: struct sigaction *oldact) michael@0: { michael@0: return syscall(__NR_sigaction, signum, act, oldact); michael@0: } michael@0: michael@0: /* Replace the first instructions of the given function with a jump michael@0: * to the given new function. */ michael@0: template michael@0: static bool michael@0: Divert(T func, T new_func) michael@0: { michael@0: void *ptr = FunctionPtr(func); michael@0: uintptr_t addr = reinterpret_cast(ptr); michael@0: michael@0: #if defined(__i386__) michael@0: // A 32-bit jump is a 5 bytes instruction. michael@0: EnsureWritable w(ptr, 5); michael@0: *reinterpret_cast(addr) = 0xe9; // jmp michael@0: *reinterpret_cast(addr + 1) = michael@0: reinterpret_cast(new_func) - addr - 5; // target displacement michael@0: return true; michael@0: #elif defined(__arm__) michael@0: const unsigned char trampoline[] = { michael@0: // .thumb michael@0: 0x46, 0x04, // nop michael@0: 0x78, 0x47, // bx pc michael@0: 0x46, 0x04, // nop michael@0: // .arm michael@0: 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] michael@0: // .word michael@0: }; michael@0: const unsigned char *start; michael@0: if (addr & 0x01) { michael@0: /* Function is thumb, the actual address of the code is without the michael@0: * least significant bit. */ michael@0: addr--; michael@0: /* The arm part of the trampoline needs to be 32-bit aligned */ michael@0: if (addr & 0x02) michael@0: start = trampoline; michael@0: else michael@0: start = trampoline + 2; michael@0: } else { michael@0: /* Function is arm, we only need the arm part of the trampoline */ michael@0: start = trampoline + 6; michael@0: } michael@0: michael@0: size_t len = sizeof(trampoline) - (start - trampoline); michael@0: EnsureWritable w(reinterpret_cast(addr), len + sizeof(void *)); michael@0: memcpy(reinterpret_cast(addr), start, len); michael@0: *reinterpret_cast(addr + len) = FunctionPtr(new_func); michael@0: cacheflush(addr, addr + len + sizeof(void *), 0); michael@0: return true; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: #else michael@0: #define sys_sigaction sigaction michael@0: template michael@0: static bool michael@0: Divert(T func, T new_func) michael@0: { michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: namespace { michael@0: michael@0: /* Clock that only accounts for time spent in the current process. */ michael@0: static uint64_t ProcessTimeStamp_Now() michael@0: { michael@0: struct timespec ts; michael@0: int rv = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); michael@0: michael@0: if (rv != 0) { michael@0: return 0; michael@0: } michael@0: michael@0: uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000; michael@0: return baseNs + (uint64_t)ts.tv_nsec; michael@0: } michael@0: michael@0: } michael@0: michael@0: /* Data structure used to pass data to the temporary signal handler, michael@0: * as well as triggering a test crash. */ michael@0: struct TmpData { michael@0: volatile int crash_int; michael@0: volatile uint64_t crash_timestamp; michael@0: }; michael@0: michael@0: SEGVHandler::SEGVHandler() michael@0: : registeredHandler(false), signalHandlingBroken(false) michael@0: , signalHandlingSlow(false) michael@0: { michael@0: /* Initialize oldStack.ss_flags to an invalid value when used to set michael@0: * an alternative stack, meaning we haven't got information about the michael@0: * original alternative stack and thus don't mean to restore it */ michael@0: oldStack.ss_flags = SS_ONSTACK; michael@0: if (!Divert(sigaction, __wrap_sigaction)) michael@0: return; michael@0: michael@0: /* Get the current segfault signal handler. */ michael@0: sys_sigaction(SIGSEGV, nullptr, &this->action); michael@0: michael@0: /* Some devices don't provide useful information to their SIGSEGV handlers, michael@0: * making it impossible for on-demand decompression to work. To check if michael@0: * we're on such a device, setup a temporary handler and deliberately michael@0: * trigger a segfault. The handler will set signalHandlingBroken if the michael@0: * provided information is bogus. michael@0: * Some other devices have a kernel option enabled that makes SIGSEGV handler michael@0: * have an overhead so high that it affects how on-demand decompression michael@0: * performs. The handler will also set signalHandlingSlow if the triggered michael@0: * SIGSEGV took too much time. */ michael@0: struct sigaction action; michael@0: action.sa_sigaction = &SEGVHandler::test_handler; michael@0: sigemptyset(&action.sa_mask); michael@0: action.sa_flags = SA_SIGINFO | SA_NODEFER; michael@0: action.sa_restorer = nullptr; michael@0: stackPtr.Assign(MemoryRange::mmap(nullptr, PageSize(), michael@0: PROT_READ | PROT_WRITE, michael@0: MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); michael@0: if (stackPtr.get() == MAP_FAILED) michael@0: return; michael@0: if (sys_sigaction(SIGSEGV, &action, nullptr)) michael@0: return; michael@0: michael@0: TmpData *data = reinterpret_cast(stackPtr.get()); michael@0: data->crash_timestamp = ProcessTimeStamp_Now(); michael@0: mprotect(stackPtr, stackPtr.GetLength(), PROT_NONE); michael@0: data->crash_int = 123; michael@0: /* Restore the original segfault signal handler. */ michael@0: sys_sigaction(SIGSEGV, &this->action, nullptr); michael@0: stackPtr.Assign(MAP_FAILED, 0); michael@0: if (signalHandlingBroken || signalHandlingSlow) michael@0: return; michael@0: michael@0: /* Setup an alternative stack if the already existing one is not big michael@0: * enough, or if there is none. */ michael@0: if (sigaltstack(nullptr, &oldStack) == 0) { michael@0: if (oldStack.ss_flags == SS_ONSTACK) michael@0: oldStack.ss_flags = 0; michael@0: if (!oldStack.ss_sp || oldStack.ss_size < stackSize) { michael@0: stackPtr.Assign(MemoryRange::mmap(nullptr, stackSize, michael@0: PROT_READ | PROT_WRITE, michael@0: MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); michael@0: if (stackPtr.get() == MAP_FAILED) michael@0: return; michael@0: stack_t stack; michael@0: stack.ss_sp = stackPtr; michael@0: stack.ss_size = stackSize; michael@0: stack.ss_flags = 0; michael@0: if (sigaltstack(&stack, nullptr) != 0) michael@0: return; michael@0: } michael@0: } michael@0: /* Register our own handler, and store the already registered one in michael@0: * SEGVHandler's struct sigaction member */ michael@0: action.sa_sigaction = &SEGVHandler::handler; michael@0: action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; michael@0: registeredHandler = !sys_sigaction(SIGSEGV, &action, nullptr); michael@0: } michael@0: michael@0: SEGVHandler::~SEGVHandler() michael@0: { michael@0: /* Restore alternative stack for signals */ michael@0: if (oldStack.ss_flags != SS_ONSTACK) michael@0: sigaltstack(&oldStack, nullptr); michael@0: /* Restore original signal handler */ michael@0: if (registeredHandler) michael@0: sys_sigaction(SIGSEGV, &this->action, nullptr); michael@0: } michael@0: michael@0: /* Test handler for a deliberately triggered SIGSEGV that determines whether michael@0: * useful information is provided to signal handlers, particularly whether michael@0: * si_addr is filled in properly, and whether the segfault handler is called michael@0: * quickly enough. */ michael@0: void SEGVHandler::test_handler(int signum, siginfo_t *info, void *context) michael@0: { michael@0: SEGVHandler &that = ElfLoader::Singleton; michael@0: if (signum != SIGSEGV || michael@0: info == nullptr || info->si_addr != that.stackPtr.get()) michael@0: that.signalHandlingBroken = true; michael@0: mprotect(that.stackPtr, that.stackPtr.GetLength(), PROT_READ | PROT_WRITE); michael@0: TmpData *data = reinterpret_cast(that.stackPtr.get()); michael@0: uint64_t latency = ProcessTimeStamp_Now() - data->crash_timestamp; michael@0: DEBUG_LOG("SEGVHandler latency: %" PRIu64, latency); michael@0: /* See bug 886736 for timings on different devices, 150 µs is reasonably above michael@0: * the latency on "working" devices and seems to be reasonably fast to incur michael@0: * a huge overhead to on-demand decompression. */ michael@0: if (latency > 150000) michael@0: that.signalHandlingSlow = true; michael@0: } michael@0: michael@0: /* TODO: "properly" handle signal masks and flags */ michael@0: void SEGVHandler::handler(int signum, siginfo_t *info, void *context) michael@0: { michael@0: //ASSERT(signum == SIGSEGV); michael@0: DEBUG_LOG("Caught segmentation fault @%p", info->si_addr); michael@0: michael@0: /* Check whether we segfaulted in the address space of a CustomElf. We're michael@0: * only expecting that to happen as an access error. */ michael@0: if (info->si_code == SEGV_ACCERR) { michael@0: mozilla::RefPtr handle = michael@0: ElfLoader::Singleton.GetHandleByPtr(info->si_addr); michael@0: if (handle && !handle->IsSystemElf()) { michael@0: DEBUG_LOG("Within the address space of a CustomElf"); michael@0: CustomElf *elf = static_cast(static_cast(handle)); michael@0: if (elf->mappable->ensure(info->si_addr)) michael@0: return; michael@0: } michael@0: } michael@0: michael@0: /* Redispatch to the registered handler */ michael@0: SEGVHandler &that = ElfLoader::Singleton; michael@0: if (that.action.sa_flags & SA_SIGINFO) { michael@0: DEBUG_LOG("Redispatching to registered handler @%p", michael@0: FunctionPtr(that.action.sa_sigaction)); michael@0: that.action.sa_sigaction(signum, info, context); michael@0: } else if (that.action.sa_handler == SIG_DFL) { michael@0: DEBUG_LOG("Redispatching to default handler"); michael@0: /* Reset the handler to the default one, and trigger it. */ michael@0: sys_sigaction(signum, &that.action, nullptr); michael@0: raise(signum); michael@0: } else if (that.action.sa_handler != SIG_IGN) { michael@0: DEBUG_LOG("Redispatching to registered handler @%p", michael@0: FunctionPtr(that.action.sa_handler)); michael@0: that.action.sa_handler(signum); michael@0: } else { michael@0: DEBUG_LOG("Ignoring"); michael@0: } michael@0: } michael@0: michael@0: int michael@0: SEGVHandler::__wrap_sigaction(int signum, const struct sigaction *act, michael@0: struct sigaction *oldact) michael@0: { michael@0: SEGVHandler &that = ElfLoader::Singleton; michael@0: michael@0: /* Use system sigaction() function for all but SIGSEGV signals. */ michael@0: if (!that.registeredHandler || (signum != SIGSEGV)) michael@0: return sys_sigaction(signum, act, oldact); michael@0: michael@0: if (oldact) michael@0: *oldact = that.action; michael@0: if (act) michael@0: that.action = *act; michael@0: return 0; michael@0: } michael@0: michael@0: Logging Logging::Singleton;