Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include <string> |
michael@0 | 6 | #include <cstring> |
michael@0 | 7 | #include <cstdlib> |
michael@0 | 8 | #include <cstdio> |
michael@0 | 9 | #include <dlfcn.h> |
michael@0 | 10 | #include <unistd.h> |
michael@0 | 11 | #include <algorithm> |
michael@0 | 12 | #include <fcntl.h> |
michael@0 | 13 | #include "ElfLoader.h" |
michael@0 | 14 | #include "CustomElf.h" |
michael@0 | 15 | #include "Mappable.h" |
michael@0 | 16 | #include "Logging.h" |
michael@0 | 17 | #include <inttypes.h> |
michael@0 | 18 | |
michael@0 | 19 | #if defined(ANDROID) |
michael@0 | 20 | #include <sys/syscall.h> |
michael@0 | 21 | |
michael@0 | 22 | #include <android/api-level.h> |
michael@0 | 23 | #if __ANDROID_API__ < 8 |
michael@0 | 24 | /* Android API < 8 doesn't provide sigaltstack */ |
michael@0 | 25 | |
michael@0 | 26 | extern "C" { |
michael@0 | 27 | |
michael@0 | 28 | inline int sigaltstack(const stack_t *ss, stack_t *oss) { |
michael@0 | 29 | return syscall(__NR_sigaltstack, ss, oss); |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | } /* extern "C" */ |
michael@0 | 33 | #endif /* __ANDROID_API__ */ |
michael@0 | 34 | #endif /* ANDROID */ |
michael@0 | 35 | |
michael@0 | 36 | #ifdef __ARM_EABI__ |
michael@0 | 37 | extern "C" const void * |
michael@0 | 38 | __gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak)); |
michael@0 | 39 | #endif |
michael@0 | 40 | |
michael@0 | 41 | using namespace mozilla; |
michael@0 | 42 | |
michael@0 | 43 | /** |
michael@0 | 44 | * dlfcn.h replacements functions |
michael@0 | 45 | */ |
michael@0 | 46 | |
michael@0 | 47 | void * |
michael@0 | 48 | __wrap_dlopen(const char *path, int flags) |
michael@0 | 49 | { |
michael@0 | 50 | RefPtr<LibHandle> handle = ElfLoader::Singleton.Load(path, flags); |
michael@0 | 51 | if (handle) |
michael@0 | 52 | handle->AddDirectRef(); |
michael@0 | 53 | return handle; |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | const char * |
michael@0 | 57 | __wrap_dlerror(void) |
michael@0 | 58 | { |
michael@0 | 59 | const char *error = ElfLoader::Singleton.lastError; |
michael@0 | 60 | ElfLoader::Singleton.lastError = nullptr; |
michael@0 | 61 | return error; |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | void * |
michael@0 | 65 | __wrap_dlsym(void *handle, const char *symbol) |
michael@0 | 66 | { |
michael@0 | 67 | if (!handle) { |
michael@0 | 68 | ElfLoader::Singleton.lastError = "dlsym(NULL, sym) unsupported"; |
michael@0 | 69 | return nullptr; |
michael@0 | 70 | } |
michael@0 | 71 | if (handle != RTLD_DEFAULT && handle != RTLD_NEXT) { |
michael@0 | 72 | LibHandle *h = reinterpret_cast<LibHandle *>(handle); |
michael@0 | 73 | return h->GetSymbolPtr(symbol); |
michael@0 | 74 | } |
michael@0 | 75 | return dlsym(handle, symbol); |
michael@0 | 76 | } |
michael@0 | 77 | |
michael@0 | 78 | int |
michael@0 | 79 | __wrap_dlclose(void *handle) |
michael@0 | 80 | { |
michael@0 | 81 | if (!handle) { |
michael@0 | 82 | ElfLoader::Singleton.lastError = "No handle given to dlclose()"; |
michael@0 | 83 | return -1; |
michael@0 | 84 | } |
michael@0 | 85 | reinterpret_cast<LibHandle *>(handle)->ReleaseDirectRef(); |
michael@0 | 86 | return 0; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | int |
michael@0 | 90 | __wrap_dladdr(void *addr, Dl_info *info) |
michael@0 | 91 | { |
michael@0 | 92 | RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(addr); |
michael@0 | 93 | if (!handle) |
michael@0 | 94 | return 0; |
michael@0 | 95 | info->dli_fname = handle->GetPath(); |
michael@0 | 96 | return 1; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | int |
michael@0 | 100 | __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data) |
michael@0 | 101 | { |
michael@0 | 102 | if (!ElfLoader::Singleton.dbg) |
michael@0 | 103 | return -1; |
michael@0 | 104 | |
michael@0 | 105 | for (ElfLoader::DebuggerHelper::iterator it = ElfLoader::Singleton.dbg.begin(); |
michael@0 | 106 | it < ElfLoader::Singleton.dbg.end(); ++it) { |
michael@0 | 107 | dl_phdr_info info; |
michael@0 | 108 | info.dlpi_addr = reinterpret_cast<Elf::Addr>(it->l_addr); |
michael@0 | 109 | info.dlpi_name = it->l_name; |
michael@0 | 110 | info.dlpi_phdr = nullptr; |
michael@0 | 111 | info.dlpi_phnum = 0; |
michael@0 | 112 | |
michael@0 | 113 | // Assuming l_addr points to Elf headers (in most cases, this is true), |
michael@0 | 114 | // get the Phdr location from there. |
michael@0 | 115 | uint8_t mapped; |
michael@0 | 116 | // If the page is not mapped, mincore returns an error. |
michael@0 | 117 | if (!mincore(const_cast<void*>(it->l_addr), PageSize(), &mapped)) { |
michael@0 | 118 | const Elf::Ehdr *ehdr = Elf::Ehdr::validate(it->l_addr); |
michael@0 | 119 | if (ehdr) { |
michael@0 | 120 | info.dlpi_phdr = reinterpret_cast<const Elf::Phdr *>( |
michael@0 | 121 | reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff); |
michael@0 | 122 | info.dlpi_phnum = ehdr->e_phnum; |
michael@0 | 123 | } |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | int ret = callback(&info, sizeof(dl_phdr_info), data); |
michael@0 | 127 | if (ret) |
michael@0 | 128 | return ret; |
michael@0 | 129 | } |
michael@0 | 130 | return 0; |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | #ifdef __ARM_EABI__ |
michael@0 | 134 | const void * |
michael@0 | 135 | __wrap___gnu_Unwind_Find_exidx(void *pc, int *pcount) |
michael@0 | 136 | { |
michael@0 | 137 | RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(pc); |
michael@0 | 138 | if (handle) |
michael@0 | 139 | return handle->FindExidx(pcount); |
michael@0 | 140 | if (__gnu_Unwind_Find_exidx) |
michael@0 | 141 | return __gnu_Unwind_Find_exidx(pc, pcount); |
michael@0 | 142 | *pcount = 0; |
michael@0 | 143 | return nullptr; |
michael@0 | 144 | } |
michael@0 | 145 | #endif |
michael@0 | 146 | |
michael@0 | 147 | /** |
michael@0 | 148 | * faulty.lib public API |
michael@0 | 149 | */ |
michael@0 | 150 | |
michael@0 | 151 | MFBT_API size_t |
michael@0 | 152 | __dl_get_mappable_length(void *handle) { |
michael@0 | 153 | if (!handle) |
michael@0 | 154 | return 0; |
michael@0 | 155 | return reinterpret_cast<LibHandle *>(handle)->GetMappableLength(); |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | MFBT_API void * |
michael@0 | 159 | __dl_mmap(void *handle, void *addr, size_t length, off_t offset) |
michael@0 | 160 | { |
michael@0 | 161 | if (!handle) |
michael@0 | 162 | return nullptr; |
michael@0 | 163 | return reinterpret_cast<LibHandle *>(handle)->MappableMMap(addr, length, |
michael@0 | 164 | offset); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | MFBT_API void |
michael@0 | 168 | __dl_munmap(void *handle, void *addr, size_t length) |
michael@0 | 169 | { |
michael@0 | 170 | if (!handle) |
michael@0 | 171 | return; |
michael@0 | 172 | return reinterpret_cast<LibHandle *>(handle)->MappableMUnmap(addr, length); |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | MFBT_API bool |
michael@0 | 176 | IsSignalHandlingBroken() |
michael@0 | 177 | { |
michael@0 | 178 | return ElfLoader::Singleton.isSignalHandlingBroken(); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | namespace { |
michael@0 | 182 | |
michael@0 | 183 | /** |
michael@0 | 184 | * Returns the part after the last '/' for the given path |
michael@0 | 185 | */ |
michael@0 | 186 | const char * |
michael@0 | 187 | LeafName(const char *path) |
michael@0 | 188 | { |
michael@0 | 189 | const char *lastSlash = strrchr(path, '/'); |
michael@0 | 190 | if (lastSlash) |
michael@0 | 191 | return lastSlash + 1; |
michael@0 | 192 | return path; |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | } /* Anonymous namespace */ |
michael@0 | 196 | |
michael@0 | 197 | /** |
michael@0 | 198 | * LibHandle |
michael@0 | 199 | */ |
michael@0 | 200 | LibHandle::~LibHandle() |
michael@0 | 201 | { |
michael@0 | 202 | free(path); |
michael@0 | 203 | } |
michael@0 | 204 | |
michael@0 | 205 | const char * |
michael@0 | 206 | LibHandle::GetName() const |
michael@0 | 207 | { |
michael@0 | 208 | return path ? LeafName(path) : nullptr; |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | size_t |
michael@0 | 212 | LibHandle::GetMappableLength() const |
michael@0 | 213 | { |
michael@0 | 214 | if (!mappable) |
michael@0 | 215 | mappable = GetMappable(); |
michael@0 | 216 | if (!mappable) |
michael@0 | 217 | return 0; |
michael@0 | 218 | return mappable->GetLength(); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | void * |
michael@0 | 222 | LibHandle::MappableMMap(void *addr, size_t length, off_t offset) const |
michael@0 | 223 | { |
michael@0 | 224 | if (!mappable) |
michael@0 | 225 | mappable = GetMappable(); |
michael@0 | 226 | if (!mappable) |
michael@0 | 227 | return MAP_FAILED; |
michael@0 | 228 | void* mapped = mappable->mmap(addr, length, PROT_READ, MAP_PRIVATE, offset); |
michael@0 | 229 | if (mapped != MAP_FAILED) { |
michael@0 | 230 | /* Ensure the availability of all pages within the mapping */ |
michael@0 | 231 | for (size_t off = 0; off < length; off += PageSize()) { |
michael@0 | 232 | mappable->ensure(reinterpret_cast<char *>(mapped) + off); |
michael@0 | 233 | } |
michael@0 | 234 | } |
michael@0 | 235 | return mapped; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | void |
michael@0 | 239 | LibHandle::MappableMUnmap(void *addr, size_t length) const |
michael@0 | 240 | { |
michael@0 | 241 | if (mappable) |
michael@0 | 242 | mappable->munmap(addr, length); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | /** |
michael@0 | 246 | * SystemElf |
michael@0 | 247 | */ |
michael@0 | 248 | TemporaryRef<LibHandle> |
michael@0 | 249 | SystemElf::Load(const char *path, int flags) |
michael@0 | 250 | { |
michael@0 | 251 | /* The Android linker returns a handle when the file name matches an |
michael@0 | 252 | * already loaded library, even when the full path doesn't exist */ |
michael@0 | 253 | if (path && path[0] == '/' && (access(path, F_OK) == -1)){ |
michael@0 | 254 | DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, (void *)nullptr); |
michael@0 | 255 | return nullptr; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | void *handle = dlopen(path, flags); |
michael@0 | 259 | DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, handle); |
michael@0 | 260 | ElfLoader::Singleton.lastError = dlerror(); |
michael@0 | 261 | if (handle) { |
michael@0 | 262 | SystemElf *elf = new SystemElf(path, handle); |
michael@0 | 263 | ElfLoader::Singleton.Register(elf); |
michael@0 | 264 | return elf; |
michael@0 | 265 | } |
michael@0 | 266 | return nullptr; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | SystemElf::~SystemElf() |
michael@0 | 270 | { |
michael@0 | 271 | if (!dlhandle) |
michael@0 | 272 | return; |
michael@0 | 273 | DEBUG_LOG("dlclose(%p [\"%s\"])", dlhandle, GetPath()); |
michael@0 | 274 | dlclose(dlhandle); |
michael@0 | 275 | ElfLoader::Singleton.lastError = dlerror(); |
michael@0 | 276 | ElfLoader::Singleton.Forget(this); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | void * |
michael@0 | 280 | SystemElf::GetSymbolPtr(const char *symbol) const |
michael@0 | 281 | { |
michael@0 | 282 | void *sym = dlsym(dlhandle, symbol); |
michael@0 | 283 | DEBUG_LOG("dlsym(%p [\"%s\"], \"%s\") = %p", dlhandle, GetPath(), symbol, sym); |
michael@0 | 284 | ElfLoader::Singleton.lastError = dlerror(); |
michael@0 | 285 | return sym; |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | Mappable * |
michael@0 | 289 | SystemElf::GetMappable() const |
michael@0 | 290 | { |
michael@0 | 291 | const char *path = GetPath(); |
michael@0 | 292 | if (!path) |
michael@0 | 293 | return nullptr; |
michael@0 | 294 | #ifdef ANDROID |
michael@0 | 295 | /* On Android, if we don't have the full path, try in /system/lib */ |
michael@0 | 296 | const char *name = LeafName(path); |
michael@0 | 297 | std::string systemPath; |
michael@0 | 298 | if (name == path) { |
michael@0 | 299 | systemPath = "/system/lib/"; |
michael@0 | 300 | systemPath += path; |
michael@0 | 301 | path = systemPath.c_str(); |
michael@0 | 302 | } |
michael@0 | 303 | #endif |
michael@0 | 304 | |
michael@0 | 305 | return MappableFile::Create(path); |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | #ifdef __ARM_EABI__ |
michael@0 | 309 | const void * |
michael@0 | 310 | SystemElf::FindExidx(int *pcount) const |
michael@0 | 311 | { |
michael@0 | 312 | /* TODO: properly implement when ElfLoader::GetHandleByPtr |
michael@0 | 313 | does return SystemElf handles */ |
michael@0 | 314 | *pcount = 0; |
michael@0 | 315 | return nullptr; |
michael@0 | 316 | } |
michael@0 | 317 | #endif |
michael@0 | 318 | |
michael@0 | 319 | /** |
michael@0 | 320 | * ElfLoader |
michael@0 | 321 | */ |
michael@0 | 322 | |
michael@0 | 323 | /* Unique ElfLoader instance */ |
michael@0 | 324 | ElfLoader ElfLoader::Singleton; |
michael@0 | 325 | |
michael@0 | 326 | TemporaryRef<LibHandle> |
michael@0 | 327 | ElfLoader::Load(const char *path, int flags, LibHandle *parent) |
michael@0 | 328 | { |
michael@0 | 329 | /* Ensure logging is initialized or refresh if environment changed. */ |
michael@0 | 330 | Logging::Init(); |
michael@0 | 331 | |
michael@0 | 332 | RefPtr<LibHandle> handle; |
michael@0 | 333 | |
michael@0 | 334 | /* Handle dlopen(nullptr) directly. */ |
michael@0 | 335 | if (!path) { |
michael@0 | 336 | handle = SystemElf::Load(nullptr, flags); |
michael@0 | 337 | return handle; |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | /* TODO: Handle relative paths correctly */ |
michael@0 | 341 | const char *name = LeafName(path); |
michael@0 | 342 | |
michael@0 | 343 | /* Search the list of handles we already have for a match. When the given |
michael@0 | 344 | * path is not absolute, compare file names, otherwise compare full paths. */ |
michael@0 | 345 | if (name == path) { |
michael@0 | 346 | for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) |
michael@0 | 347 | if ((*it)->GetName() && (strcmp((*it)->GetName(), name) == 0)) |
michael@0 | 348 | return *it; |
michael@0 | 349 | } else { |
michael@0 | 350 | for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) |
michael@0 | 351 | if ((*it)->GetPath() && (strcmp((*it)->GetPath(), path) == 0)) |
michael@0 | 352 | return *it; |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | char *abs_path = nullptr; |
michael@0 | 356 | const char *requested_path = path; |
michael@0 | 357 | |
michael@0 | 358 | /* When the path is not absolute and the library is being loaded for |
michael@0 | 359 | * another, first try to load the library from the directory containing |
michael@0 | 360 | * that parent library. */ |
michael@0 | 361 | if ((name == path) && parent) { |
michael@0 | 362 | const char *parentPath = parent->GetPath(); |
michael@0 | 363 | abs_path = new char[strlen(parentPath) + strlen(path)]; |
michael@0 | 364 | strcpy(abs_path, parentPath); |
michael@0 | 365 | char *slash = strrchr(abs_path, '/'); |
michael@0 | 366 | strcpy(slash + 1, path); |
michael@0 | 367 | path = abs_path; |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | Mappable *mappable = GetMappableFromPath(path); |
michael@0 | 371 | |
michael@0 | 372 | /* Try loading with the custom linker if we have a Mappable */ |
michael@0 | 373 | if (mappable) |
michael@0 | 374 | handle = CustomElf::Load(mappable, path, flags); |
michael@0 | 375 | |
michael@0 | 376 | /* Try loading with the system linker if everything above failed */ |
michael@0 | 377 | if (!handle) |
michael@0 | 378 | handle = SystemElf::Load(path, flags); |
michael@0 | 379 | |
michael@0 | 380 | /* If we didn't have an absolute path and haven't been able to load |
michael@0 | 381 | * a library yet, try in the system search path */ |
michael@0 | 382 | if (!handle && abs_path) |
michael@0 | 383 | handle = SystemElf::Load(name, flags); |
michael@0 | 384 | |
michael@0 | 385 | delete [] abs_path; |
michael@0 | 386 | DEBUG_LOG("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", requested_path, flags, |
michael@0 | 387 | reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "", |
michael@0 | 388 | static_cast<void *>(handle)); |
michael@0 | 389 | |
michael@0 | 390 | return handle; |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | mozilla::TemporaryRef<LibHandle> |
michael@0 | 394 | ElfLoader::GetHandleByPtr(void *addr) |
michael@0 | 395 | { |
michael@0 | 396 | /* Scan the list of handles we already have for a match */ |
michael@0 | 397 | for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) { |
michael@0 | 398 | if ((*it)->Contains(addr)) |
michael@0 | 399 | return *it; |
michael@0 | 400 | } |
michael@0 | 401 | return nullptr; |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | Mappable * |
michael@0 | 405 | ElfLoader::GetMappableFromPath(const char *path) |
michael@0 | 406 | { |
michael@0 | 407 | const char *name = LeafName(path); |
michael@0 | 408 | Mappable *mappable = nullptr; |
michael@0 | 409 | RefPtr<Zip> zip; |
michael@0 | 410 | const char *subpath; |
michael@0 | 411 | if ((subpath = strchr(path, '!'))) { |
michael@0 | 412 | char *zip_path = strndup(path, subpath - path); |
michael@0 | 413 | while (*(++subpath) == '/') { } |
michael@0 | 414 | zip = ZipCollection::GetZip(zip_path); |
michael@0 | 415 | Zip::Stream s; |
michael@0 | 416 | if (zip && zip->GetStream(subpath, &s)) { |
michael@0 | 417 | /* When the MOZ_LINKER_EXTRACT environment variable is set to "1", |
michael@0 | 418 | * compressed libraries are going to be (temporarily) extracted as |
michael@0 | 419 | * files, in the directory pointed by the MOZ_LINKER_CACHE |
michael@0 | 420 | * environment variable. */ |
michael@0 | 421 | const char *extract = getenv("MOZ_LINKER_EXTRACT"); |
michael@0 | 422 | if (extract && !strncmp(extract, "1", 2 /* Including '\0' */)) |
michael@0 | 423 | mappable = MappableExtractFile::Create(name, zip, &s); |
michael@0 | 424 | if (!mappable) { |
michael@0 | 425 | if (s.GetType() == Zip::Stream::DEFLATE) { |
michael@0 | 426 | mappable = MappableDeflate::Create(name, zip, &s); |
michael@0 | 427 | } else if (s.GetType() == Zip::Stream::STORE) { |
michael@0 | 428 | mappable = MappableSeekableZStream::Create(name, zip, &s); |
michael@0 | 429 | } |
michael@0 | 430 | } |
michael@0 | 431 | } |
michael@0 | 432 | } |
michael@0 | 433 | /* If we couldn't load above, try with a MappableFile */ |
michael@0 | 434 | if (!mappable && !zip) |
michael@0 | 435 | mappable = MappableFile::Create(path); |
michael@0 | 436 | |
michael@0 | 437 | return mappable; |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | void |
michael@0 | 441 | ElfLoader::Register(LibHandle *handle) |
michael@0 | 442 | { |
michael@0 | 443 | handles.push_back(handle); |
michael@0 | 444 | if (dbg && !handle->IsSystemElf()) |
michael@0 | 445 | dbg.Add(static_cast<CustomElf *>(handle)); |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | void |
michael@0 | 449 | ElfLoader::Forget(LibHandle *handle) |
michael@0 | 450 | { |
michael@0 | 451 | /* Ensure logging is initialized or refresh if environment changed. */ |
michael@0 | 452 | Logging::Init(); |
michael@0 | 453 | |
michael@0 | 454 | LibHandleList::iterator it = std::find(handles.begin(), handles.end(), handle); |
michael@0 | 455 | if (it != handles.end()) { |
michael@0 | 456 | DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"])", reinterpret_cast<void *>(handle), |
michael@0 | 457 | handle->GetPath()); |
michael@0 | 458 | if (dbg && !handle->IsSystemElf()) |
michael@0 | 459 | dbg.Remove(static_cast<CustomElf *>(handle)); |
michael@0 | 460 | handles.erase(it); |
michael@0 | 461 | } else { |
michael@0 | 462 | DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"]): Handle not found", |
michael@0 | 463 | reinterpret_cast<void *>(handle), handle->GetPath()); |
michael@0 | 464 | } |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | ElfLoader::~ElfLoader() |
michael@0 | 468 | { |
michael@0 | 469 | LibHandleList list; |
michael@0 | 470 | /* Build up a list of all library handles with direct (external) references. |
michael@0 | 471 | * We actually skip system library handles because we want to keep at least |
michael@0 | 472 | * some of these open. Most notably, Mozilla codebase keeps a few libgnome |
michael@0 | 473 | * libraries deliberately open because of the mess that libORBit destruction |
michael@0 | 474 | * is. dlclose()ing these libraries actually leads to problems. */ |
michael@0 | 475 | for (LibHandleList::reverse_iterator it = handles.rbegin(); |
michael@0 | 476 | it < handles.rend(); ++it) { |
michael@0 | 477 | if ((*it)->DirectRefCount()) { |
michael@0 | 478 | if ((*it)->IsSystemElf()) { |
michael@0 | 479 | static_cast<SystemElf *>(*it)->Forget(); |
michael@0 | 480 | } else { |
michael@0 | 481 | list.push_back(*it); |
michael@0 | 482 | } |
michael@0 | 483 | } |
michael@0 | 484 | } |
michael@0 | 485 | /* Force release all external references to the handles collected above */ |
michael@0 | 486 | for (LibHandleList::iterator it = list.begin(); it < list.end(); ++it) { |
michael@0 | 487 | while ((*it)->ReleaseDirectRef()) { } |
michael@0 | 488 | } |
michael@0 | 489 | /* Remove the remaining system handles. */ |
michael@0 | 490 | if (handles.size()) { |
michael@0 | 491 | list = handles; |
michael@0 | 492 | for (LibHandleList::reverse_iterator it = list.rbegin(); |
michael@0 | 493 | it < list.rend(); ++it) { |
michael@0 | 494 | if ((*it)->IsSystemElf()) { |
michael@0 | 495 | DEBUG_LOG("ElfLoader::~ElfLoader(): Remaining handle for \"%s\" " |
michael@0 | 496 | "[%d direct refs, %d refs total]", (*it)->GetPath(), |
michael@0 | 497 | (*it)->DirectRefCount(), (*it)->refCount()); |
michael@0 | 498 | } else { |
michael@0 | 499 | DEBUG_LOG("ElfLoader::~ElfLoader(): Unexpected remaining handle for \"%s\" " |
michael@0 | 500 | "[%d direct refs, %d refs total]", (*it)->GetPath(), |
michael@0 | 501 | (*it)->DirectRefCount(), (*it)->refCount()); |
michael@0 | 502 | /* Not removing, since it could have references to other libraries, |
michael@0 | 503 | * destroying them as a side effect, and possibly leaving dangling |
michael@0 | 504 | * pointers in the handle list we're scanning */ |
michael@0 | 505 | } |
michael@0 | 506 | } |
michael@0 | 507 | } |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | void |
michael@0 | 511 | ElfLoader::stats(const char *when) |
michael@0 | 512 | { |
michael@0 | 513 | for (LibHandleList::iterator it = Singleton.handles.begin(); |
michael@0 | 514 | it < Singleton.handles.end(); ++it) |
michael@0 | 515 | if (!(*it)->IsSystemElf()) |
michael@0 | 516 | static_cast<CustomElf *>(*it)->stats(when); |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | #ifdef __ARM_EABI__ |
michael@0 | 520 | int |
michael@0 | 521 | ElfLoader::__wrap_aeabi_atexit(void *that, ElfLoader::Destructor destructor, |
michael@0 | 522 | void *dso_handle) |
michael@0 | 523 | { |
michael@0 | 524 | Singleton.destructors.push_back( |
michael@0 | 525 | DestructorCaller(destructor, that, dso_handle)); |
michael@0 | 526 | return 0; |
michael@0 | 527 | } |
michael@0 | 528 | #else |
michael@0 | 529 | int |
michael@0 | 530 | ElfLoader::__wrap_cxa_atexit(ElfLoader::Destructor destructor, void *that, |
michael@0 | 531 | void *dso_handle) |
michael@0 | 532 | { |
michael@0 | 533 | Singleton.destructors.push_back( |
michael@0 | 534 | DestructorCaller(destructor, that, dso_handle)); |
michael@0 | 535 | return 0; |
michael@0 | 536 | } |
michael@0 | 537 | #endif |
michael@0 | 538 | |
michael@0 | 539 | void |
michael@0 | 540 | ElfLoader::__wrap_cxa_finalize(void *dso_handle) |
michael@0 | 541 | { |
michael@0 | 542 | /* Call all destructors for the given DSO handle in reverse order they were |
michael@0 | 543 | * registered. */ |
michael@0 | 544 | std::vector<DestructorCaller>::reverse_iterator it; |
michael@0 | 545 | for (it = Singleton.destructors.rbegin(); |
michael@0 | 546 | it < Singleton.destructors.rend(); ++it) { |
michael@0 | 547 | if (it->IsForHandle(dso_handle)) { |
michael@0 | 548 | it->Call(); |
michael@0 | 549 | } |
michael@0 | 550 | } |
michael@0 | 551 | } |
michael@0 | 552 | |
michael@0 | 553 | void |
michael@0 | 554 | ElfLoader::DestructorCaller::Call() |
michael@0 | 555 | { |
michael@0 | 556 | if (destructor) { |
michael@0 | 557 | DEBUG_LOG("ElfLoader::DestructorCaller::Call(%p, %p, %p)", |
michael@0 | 558 | FunctionPtr(destructor), object, dso_handle); |
michael@0 | 559 | destructor(object); |
michael@0 | 560 | destructor = nullptr; |
michael@0 | 561 | } |
michael@0 | 562 | } |
michael@0 | 563 | |
michael@0 | 564 | ElfLoader::DebuggerHelper::DebuggerHelper(): dbg(nullptr) |
michael@0 | 565 | { |
michael@0 | 566 | /* Find ELF auxiliary vectors. |
michael@0 | 567 | * |
michael@0 | 568 | * The kernel stores the following data on the stack when starting a |
michael@0 | 569 | * program: |
michael@0 | 570 | * argc |
michael@0 | 571 | * argv[0] (pointer into argv strings defined below) |
michael@0 | 572 | * argv[1] (likewise) |
michael@0 | 573 | * ... |
michael@0 | 574 | * argv[argc - 1] (likewise) |
michael@0 | 575 | * nullptr |
michael@0 | 576 | * envp[0] (pointer into environment strings defined below) |
michael@0 | 577 | * envp[1] (likewise) |
michael@0 | 578 | * ... |
michael@0 | 579 | * envp[n] (likewise) |
michael@0 | 580 | * nullptr |
michael@0 | 581 | * ... (more NULLs on some platforms such as Android 4.3) |
michael@0 | 582 | * auxv[0] (first ELF auxiliary vector) |
michael@0 | 583 | * auxv[1] (second ELF auxiliary vector) |
michael@0 | 584 | * ... |
michael@0 | 585 | * auxv[p] (last ELF auxiliary vector) |
michael@0 | 586 | * (AT_NULL, nullptr) |
michael@0 | 587 | * padding |
michael@0 | 588 | * argv strings, separated with '\0' |
michael@0 | 589 | * environment strings, separated with '\0' |
michael@0 | 590 | * nullptr |
michael@0 | 591 | * |
michael@0 | 592 | * What we are after are the auxv values defined by the following struct. |
michael@0 | 593 | */ |
michael@0 | 594 | struct AuxVector { |
michael@0 | 595 | Elf::Addr type; |
michael@0 | 596 | Elf::Addr value; |
michael@0 | 597 | }; |
michael@0 | 598 | |
michael@0 | 599 | /* Pointer to the environment variables list */ |
michael@0 | 600 | extern char **environ; |
michael@0 | 601 | |
michael@0 | 602 | /* The environment may have changed since the program started, in which |
michael@0 | 603 | * case the environ variables list isn't the list the kernel put on stack |
michael@0 | 604 | * anymore. But in this new list, variables that didn't change still point |
michael@0 | 605 | * to the strings the kernel put on stack. It is quite unlikely that two |
michael@0 | 606 | * modified environment variables point to two consecutive strings in memory, |
michael@0 | 607 | * so we assume that if two consecutive environment variables point to two |
michael@0 | 608 | * consecutive strings, we found strings the kernel put on stack. */ |
michael@0 | 609 | char **env; |
michael@0 | 610 | for (env = environ; *env; env++) |
michael@0 | 611 | if (*env + strlen(*env) + 1 == env[1]) |
michael@0 | 612 | break; |
michael@0 | 613 | if (!*env) |
michael@0 | 614 | return; |
michael@0 | 615 | |
michael@0 | 616 | /* Next, we scan the stack backwards to find a pointer to one of those |
michael@0 | 617 | * strings we found above, which will give us the location of the original |
michael@0 | 618 | * envp list. As we are looking for pointers, we need to look at 32-bits or |
michael@0 | 619 | * 64-bits aligned values, depening on the architecture. */ |
michael@0 | 620 | char **scan = reinterpret_cast<char **>( |
michael@0 | 621 | reinterpret_cast<uintptr_t>(*env) & ~(sizeof(void *) - 1)); |
michael@0 | 622 | while (*env != *scan) |
michael@0 | 623 | scan--; |
michael@0 | 624 | |
michael@0 | 625 | /* Finally, scan forward to find the last environment variable pointer and |
michael@0 | 626 | * thus the first auxiliary vector. */ |
michael@0 | 627 | while (*scan++); |
michael@0 | 628 | |
michael@0 | 629 | /* Some platforms have more NULLs here, so skip them if we encounter them */ |
michael@0 | 630 | while (!*scan) |
michael@0 | 631 | scan++; |
michael@0 | 632 | |
michael@0 | 633 | AuxVector *auxv = reinterpret_cast<AuxVector *>(scan); |
michael@0 | 634 | |
michael@0 | 635 | /* The two values of interest in the auxiliary vectors are AT_PHDR and |
michael@0 | 636 | * AT_PHNUM, which gives us the the location and size of the ELF program |
michael@0 | 637 | * headers. */ |
michael@0 | 638 | Array<Elf::Phdr> phdrs; |
michael@0 | 639 | char *base = nullptr; |
michael@0 | 640 | while (auxv->type) { |
michael@0 | 641 | if (auxv->type == AT_PHDR) { |
michael@0 | 642 | phdrs.Init(reinterpret_cast<Elf::Phdr*>(auxv->value)); |
michael@0 | 643 | /* Assume the base address is the first byte of the same page */ |
michael@0 | 644 | base = reinterpret_cast<char *>(PageAlignedPtr(auxv->value)); |
michael@0 | 645 | } |
michael@0 | 646 | if (auxv->type == AT_PHNUM) |
michael@0 | 647 | phdrs.Init(auxv->value); |
michael@0 | 648 | auxv++; |
michael@0 | 649 | } |
michael@0 | 650 | |
michael@0 | 651 | if (!phdrs) { |
michael@0 | 652 | DEBUG_LOG("Couldn't find program headers"); |
michael@0 | 653 | return; |
michael@0 | 654 | } |
michael@0 | 655 | |
michael@0 | 656 | /* In some cases, the address for the program headers we get from the |
michael@0 | 657 | * auxiliary vectors is not mapped, because of the PT_LOAD segments |
michael@0 | 658 | * definitions in the program executable. Trying to map anonymous memory |
michael@0 | 659 | * with a hint giving the base address will return a different address |
michael@0 | 660 | * if something is mapped there, and the base address otherwise. */ |
michael@0 | 661 | MappedPtr mem(MemoryRange::mmap(base, PageSize(), PROT_NONE, |
michael@0 | 662 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); |
michael@0 | 663 | if (mem == base) { |
michael@0 | 664 | /* If program headers aren't mapped, try to map them */ |
michael@0 | 665 | int fd = open("/proc/self/exe", O_RDONLY); |
michael@0 | 666 | if (fd == -1) { |
michael@0 | 667 | DEBUG_LOG("Failed to open /proc/self/exe"); |
michael@0 | 668 | return; |
michael@0 | 669 | } |
michael@0 | 670 | mem.Assign(MemoryRange::mmap(base, PageSize(), PROT_READ, MAP_PRIVATE, |
michael@0 | 671 | fd, 0)); |
michael@0 | 672 | /* If we don't manage to map at the right address, just give up. */ |
michael@0 | 673 | if (mem != base) { |
michael@0 | 674 | DEBUG_LOG("Couldn't read program headers"); |
michael@0 | 675 | return; |
michael@0 | 676 | } |
michael@0 | 677 | } |
michael@0 | 678 | /* Sanity check: the first bytes at the base address should be an ELF |
michael@0 | 679 | * header. */ |
michael@0 | 680 | if (!Elf::Ehdr::validate(base)) { |
michael@0 | 681 | DEBUG_LOG("Couldn't find program base"); |
michael@0 | 682 | return; |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | /* Search for the program PT_DYNAMIC segment */ |
michael@0 | 686 | Array<Elf::Dyn> dyns; |
michael@0 | 687 | for (Array<Elf::Phdr>::iterator phdr = phdrs.begin(); phdr < phdrs.end(); |
michael@0 | 688 | ++phdr) { |
michael@0 | 689 | /* While the program headers are expected within the first mapped page of |
michael@0 | 690 | * the program executable, the executable PT_LOADs may actually make them |
michael@0 | 691 | * loaded at an address that is not the wanted base address of the |
michael@0 | 692 | * library. We thus need to adjust the base address, compensating for the |
michael@0 | 693 | * virtual address of the PT_LOAD segment corresponding to offset 0. */ |
michael@0 | 694 | if (phdr->p_type == PT_LOAD && phdr->p_offset == 0) |
michael@0 | 695 | base -= phdr->p_vaddr; |
michael@0 | 696 | if (phdr->p_type == PT_DYNAMIC) |
michael@0 | 697 | dyns.Init(base + phdr->p_vaddr, phdr->p_filesz); |
michael@0 | 698 | } |
michael@0 | 699 | if (!dyns) { |
michael@0 | 700 | DEBUG_LOG("Failed to find PT_DYNAMIC section in program"); |
michael@0 | 701 | return; |
michael@0 | 702 | } |
michael@0 | 703 | |
michael@0 | 704 | /* Search for the DT_DEBUG information */ |
michael@0 | 705 | for (Array<Elf::Dyn>::iterator dyn = dyns.begin(); dyn < dyns.end(); ++dyn) { |
michael@0 | 706 | if (dyn->d_tag == DT_DEBUG) { |
michael@0 | 707 | dbg = reinterpret_cast<r_debug *>(dyn->d_un.d_ptr); |
michael@0 | 708 | break; |
michael@0 | 709 | } |
michael@0 | 710 | } |
michael@0 | 711 | DEBUG_LOG("DT_DEBUG points at %p", static_cast<void *>(dbg)); |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | /** |
michael@0 | 715 | * Helper class to ensure the given pointer is writable within the scope of |
michael@0 | 716 | * an instance. Permissions to the memory page where the pointer lies are |
michael@0 | 717 | * restored to their original value when the instance is destroyed. |
michael@0 | 718 | */ |
michael@0 | 719 | class EnsureWritable |
michael@0 | 720 | { |
michael@0 | 721 | public: |
michael@0 | 722 | template <typename T> |
michael@0 | 723 | EnsureWritable(T *ptr, size_t length_ = sizeof(T)) |
michael@0 | 724 | { |
michael@0 | 725 | MOZ_ASSERT(length_ < PageSize()); |
michael@0 | 726 | prot = -1; |
michael@0 | 727 | page = MAP_FAILED; |
michael@0 | 728 | |
michael@0 | 729 | char *firstPage = PageAlignedPtr(reinterpret_cast<char *>(ptr)); |
michael@0 | 730 | char *lastPageEnd = PageAlignedEndPtr(reinterpret_cast<char *>(ptr) + length_); |
michael@0 | 731 | length = lastPageEnd - firstPage; |
michael@0 | 732 | uintptr_t start = reinterpret_cast<uintptr_t>(firstPage); |
michael@0 | 733 | uintptr_t end; |
michael@0 | 734 | |
michael@0 | 735 | prot = getProt(start, &end); |
michael@0 | 736 | if (prot == -1 || (start + length) > end) |
michael@0 | 737 | MOZ_CRASH(); |
michael@0 | 738 | |
michael@0 | 739 | if (prot & PROT_WRITE) |
michael@0 | 740 | return; |
michael@0 | 741 | |
michael@0 | 742 | page = firstPage; |
michael@0 | 743 | mprotect(page, length, prot | PROT_WRITE); |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | ~EnsureWritable() |
michael@0 | 747 | { |
michael@0 | 748 | if (page != MAP_FAILED) { |
michael@0 | 749 | mprotect(page, length, prot); |
michael@0 | 750 | } |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | private: |
michael@0 | 754 | int getProt(uintptr_t addr, uintptr_t *end) |
michael@0 | 755 | { |
michael@0 | 756 | /* The interesting part of the /proc/self/maps format looks like: |
michael@0 | 757 | * startAddr-endAddr rwxp */ |
michael@0 | 758 | int result = 0; |
michael@0 | 759 | AutoCloseFILE f(fopen("/proc/self/maps", "r")); |
michael@0 | 760 | while (f) { |
michael@0 | 761 | unsigned long long startAddr, endAddr; |
michael@0 | 762 | char perms[5]; |
michael@0 | 763 | if (fscanf(f, "%llx-%llx %4s %*1024[^\n] ", &startAddr, &endAddr, perms) != 3) |
michael@0 | 764 | return -1; |
michael@0 | 765 | if (addr < startAddr || addr >= endAddr) |
michael@0 | 766 | continue; |
michael@0 | 767 | if (perms[0] == 'r') |
michael@0 | 768 | result |= PROT_READ; |
michael@0 | 769 | else if (perms[0] != '-') |
michael@0 | 770 | return -1; |
michael@0 | 771 | if (perms[1] == 'w') |
michael@0 | 772 | result |= PROT_WRITE; |
michael@0 | 773 | else if (perms[1] != '-') |
michael@0 | 774 | return -1; |
michael@0 | 775 | if (perms[2] == 'x') |
michael@0 | 776 | result |= PROT_EXEC; |
michael@0 | 777 | else if (perms[2] != '-') |
michael@0 | 778 | return -1; |
michael@0 | 779 | *end = endAddr; |
michael@0 | 780 | return result; |
michael@0 | 781 | } |
michael@0 | 782 | return -1; |
michael@0 | 783 | } |
michael@0 | 784 | |
michael@0 | 785 | int prot; |
michael@0 | 786 | void *page; |
michael@0 | 787 | size_t length; |
michael@0 | 788 | }; |
michael@0 | 789 | |
michael@0 | 790 | /** |
michael@0 | 791 | * The system linker maintains a doubly linked list of library it loads |
michael@0 | 792 | * for use by the debugger. Unfortunately, it also uses the list pointers |
michael@0 | 793 | * in a lot of operations and adding our data in the list is likely to |
michael@0 | 794 | * trigger crashes when the linker tries to use data we don't provide or |
michael@0 | 795 | * that fall off the amount data we allocated. Fortunately, the linker only |
michael@0 | 796 | * traverses the list forward and accesses the head of the list from a |
michael@0 | 797 | * private pointer instead of using the value in the r_debug structure. |
michael@0 | 798 | * This means we can safely add members at the beginning of the list. |
michael@0 | 799 | * Unfortunately, gdb checks the coherency of l_prev values, so we have |
michael@0 | 800 | * to adjust the l_prev value for the first element the system linker |
michael@0 | 801 | * knows about. Fortunately, it doesn't use l_prev, and the first element |
michael@0 | 802 | * is not ever going to be released before our elements, since it is the |
michael@0 | 803 | * program executable, so the system linker should not be changing |
michael@0 | 804 | * r_debug::r_map. |
michael@0 | 805 | */ |
michael@0 | 806 | void |
michael@0 | 807 | ElfLoader::DebuggerHelper::Add(ElfLoader::link_map *map) |
michael@0 | 808 | { |
michael@0 | 809 | if (!dbg->r_brk) |
michael@0 | 810 | return; |
michael@0 | 811 | dbg->r_state = r_debug::RT_ADD; |
michael@0 | 812 | dbg->r_brk(); |
michael@0 | 813 | map->l_prev = nullptr; |
michael@0 | 814 | map->l_next = dbg->r_map; |
michael@0 | 815 | if (!firstAdded) { |
michael@0 | 816 | firstAdded = map; |
michael@0 | 817 | /* When adding a library for the first time, r_map points to data |
michael@0 | 818 | * handled by the system linker, and that data may be read-only */ |
michael@0 | 819 | EnsureWritable w(&dbg->r_map->l_prev); |
michael@0 | 820 | dbg->r_map->l_prev = map; |
michael@0 | 821 | } else |
michael@0 | 822 | dbg->r_map->l_prev = map; |
michael@0 | 823 | dbg->r_map = map; |
michael@0 | 824 | dbg->r_state = r_debug::RT_CONSISTENT; |
michael@0 | 825 | dbg->r_brk(); |
michael@0 | 826 | } |
michael@0 | 827 | |
michael@0 | 828 | void |
michael@0 | 829 | ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map) |
michael@0 | 830 | { |
michael@0 | 831 | if (!dbg->r_brk) |
michael@0 | 832 | return; |
michael@0 | 833 | dbg->r_state = r_debug::RT_DELETE; |
michael@0 | 834 | dbg->r_brk(); |
michael@0 | 835 | if (dbg->r_map == map) |
michael@0 | 836 | dbg->r_map = map->l_next; |
michael@0 | 837 | else |
michael@0 | 838 | map->l_prev->l_next = map->l_next; |
michael@0 | 839 | if (map == firstAdded) { |
michael@0 | 840 | firstAdded = map->l_prev; |
michael@0 | 841 | /* When removing the first added library, its l_next is going to be |
michael@0 | 842 | * data handled by the system linker, and that data may be read-only */ |
michael@0 | 843 | EnsureWritable w(&map->l_next->l_prev); |
michael@0 | 844 | map->l_next->l_prev = map->l_prev; |
michael@0 | 845 | } else |
michael@0 | 846 | map->l_next->l_prev = map->l_prev; |
michael@0 | 847 | dbg->r_state = r_debug::RT_CONSISTENT; |
michael@0 | 848 | dbg->r_brk(); |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | #if defined(ANDROID) |
michael@0 | 852 | /* As some system libraries may be calling signal() or sigaction() to |
michael@0 | 853 | * set a SIGSEGV handler, effectively breaking MappableSeekableZStream, |
michael@0 | 854 | * or worse, restore our SIGSEGV handler with wrong flags (which using |
michael@0 | 855 | * signal() will do), we want to hook into the system's sigaction() to |
michael@0 | 856 | * replace it with our own wrapper instead, so that our handler is never |
michael@0 | 857 | * replaced. We used to only do that with libraries this linker loads, |
michael@0 | 858 | * but it turns out at least one system library does call signal() and |
michael@0 | 859 | * breaks us (libsc-a3xx.so on the Samsung Galaxy S4). |
michael@0 | 860 | * As libc's signal (bsd_signal/sysv_signal, really) calls sigaction |
michael@0 | 861 | * under the hood, instead of calling the signal system call directly, |
michael@0 | 862 | * we only need to hook sigaction. This is true for both bionic and |
michael@0 | 863 | * glibc. |
michael@0 | 864 | */ |
michael@0 | 865 | |
michael@0 | 866 | /* libc's sigaction */ |
michael@0 | 867 | extern "C" int |
michael@0 | 868 | sigaction(int signum, const struct sigaction *act, |
michael@0 | 869 | struct sigaction *oldact); |
michael@0 | 870 | |
michael@0 | 871 | /* Simple reimplementation of sigaction. This is roughly equivalent |
michael@0 | 872 | * to the assembly that comes in bionic, but not quite equivalent to |
michael@0 | 873 | * glibc's implementation, so we only use this on Android. */ |
michael@0 | 874 | int |
michael@0 | 875 | sys_sigaction(int signum, const struct sigaction *act, |
michael@0 | 876 | struct sigaction *oldact) |
michael@0 | 877 | { |
michael@0 | 878 | return syscall(__NR_sigaction, signum, act, oldact); |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | /* Replace the first instructions of the given function with a jump |
michael@0 | 882 | * to the given new function. */ |
michael@0 | 883 | template <typename T> |
michael@0 | 884 | static bool |
michael@0 | 885 | Divert(T func, T new_func) |
michael@0 | 886 | { |
michael@0 | 887 | void *ptr = FunctionPtr(func); |
michael@0 | 888 | uintptr_t addr = reinterpret_cast<uintptr_t>(ptr); |
michael@0 | 889 | |
michael@0 | 890 | #if defined(__i386__) |
michael@0 | 891 | // A 32-bit jump is a 5 bytes instruction. |
michael@0 | 892 | EnsureWritable w(ptr, 5); |
michael@0 | 893 | *reinterpret_cast<unsigned char *>(addr) = 0xe9; // jmp |
michael@0 | 894 | *reinterpret_cast<intptr_t *>(addr + 1) = |
michael@0 | 895 | reinterpret_cast<uintptr_t>(new_func) - addr - 5; // target displacement |
michael@0 | 896 | return true; |
michael@0 | 897 | #elif defined(__arm__) |
michael@0 | 898 | const unsigned char trampoline[] = { |
michael@0 | 899 | // .thumb |
michael@0 | 900 | 0x46, 0x04, // nop |
michael@0 | 901 | 0x78, 0x47, // bx pc |
michael@0 | 902 | 0x46, 0x04, // nop |
michael@0 | 903 | // .arm |
michael@0 | 904 | 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] |
michael@0 | 905 | // .word <new_func> |
michael@0 | 906 | }; |
michael@0 | 907 | const unsigned char *start; |
michael@0 | 908 | if (addr & 0x01) { |
michael@0 | 909 | /* Function is thumb, the actual address of the code is without the |
michael@0 | 910 | * least significant bit. */ |
michael@0 | 911 | addr--; |
michael@0 | 912 | /* The arm part of the trampoline needs to be 32-bit aligned */ |
michael@0 | 913 | if (addr & 0x02) |
michael@0 | 914 | start = trampoline; |
michael@0 | 915 | else |
michael@0 | 916 | start = trampoline + 2; |
michael@0 | 917 | } else { |
michael@0 | 918 | /* Function is arm, we only need the arm part of the trampoline */ |
michael@0 | 919 | start = trampoline + 6; |
michael@0 | 920 | } |
michael@0 | 921 | |
michael@0 | 922 | size_t len = sizeof(trampoline) - (start - trampoline); |
michael@0 | 923 | EnsureWritable w(reinterpret_cast<void *>(addr), len + sizeof(void *)); |
michael@0 | 924 | memcpy(reinterpret_cast<void *>(addr), start, len); |
michael@0 | 925 | *reinterpret_cast<void **>(addr + len) = FunctionPtr(new_func); |
michael@0 | 926 | cacheflush(addr, addr + len + sizeof(void *), 0); |
michael@0 | 927 | return true; |
michael@0 | 928 | #else |
michael@0 | 929 | return false; |
michael@0 | 930 | #endif |
michael@0 | 931 | } |
michael@0 | 932 | #else |
michael@0 | 933 | #define sys_sigaction sigaction |
michael@0 | 934 | template <typename T> |
michael@0 | 935 | static bool |
michael@0 | 936 | Divert(T func, T new_func) |
michael@0 | 937 | { |
michael@0 | 938 | return false; |
michael@0 | 939 | } |
michael@0 | 940 | #endif |
michael@0 | 941 | |
michael@0 | 942 | namespace { |
michael@0 | 943 | |
michael@0 | 944 | /* Clock that only accounts for time spent in the current process. */ |
michael@0 | 945 | static uint64_t ProcessTimeStamp_Now() |
michael@0 | 946 | { |
michael@0 | 947 | struct timespec ts; |
michael@0 | 948 | int rv = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); |
michael@0 | 949 | |
michael@0 | 950 | if (rv != 0) { |
michael@0 | 951 | return 0; |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000; |
michael@0 | 955 | return baseNs + (uint64_t)ts.tv_nsec; |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | } |
michael@0 | 959 | |
michael@0 | 960 | /* Data structure used to pass data to the temporary signal handler, |
michael@0 | 961 | * as well as triggering a test crash. */ |
michael@0 | 962 | struct TmpData { |
michael@0 | 963 | volatile int crash_int; |
michael@0 | 964 | volatile uint64_t crash_timestamp; |
michael@0 | 965 | }; |
michael@0 | 966 | |
michael@0 | 967 | SEGVHandler::SEGVHandler() |
michael@0 | 968 | : registeredHandler(false), signalHandlingBroken(false) |
michael@0 | 969 | , signalHandlingSlow(false) |
michael@0 | 970 | { |
michael@0 | 971 | /* Initialize oldStack.ss_flags to an invalid value when used to set |
michael@0 | 972 | * an alternative stack, meaning we haven't got information about the |
michael@0 | 973 | * original alternative stack and thus don't mean to restore it */ |
michael@0 | 974 | oldStack.ss_flags = SS_ONSTACK; |
michael@0 | 975 | if (!Divert(sigaction, __wrap_sigaction)) |
michael@0 | 976 | return; |
michael@0 | 977 | |
michael@0 | 978 | /* Get the current segfault signal handler. */ |
michael@0 | 979 | sys_sigaction(SIGSEGV, nullptr, &this->action); |
michael@0 | 980 | |
michael@0 | 981 | /* Some devices don't provide useful information to their SIGSEGV handlers, |
michael@0 | 982 | * making it impossible for on-demand decompression to work. To check if |
michael@0 | 983 | * we're on such a device, setup a temporary handler and deliberately |
michael@0 | 984 | * trigger a segfault. The handler will set signalHandlingBroken if the |
michael@0 | 985 | * provided information is bogus. |
michael@0 | 986 | * Some other devices have a kernel option enabled that makes SIGSEGV handler |
michael@0 | 987 | * have an overhead so high that it affects how on-demand decompression |
michael@0 | 988 | * performs. The handler will also set signalHandlingSlow if the triggered |
michael@0 | 989 | * SIGSEGV took too much time. */ |
michael@0 | 990 | struct sigaction action; |
michael@0 | 991 | action.sa_sigaction = &SEGVHandler::test_handler; |
michael@0 | 992 | sigemptyset(&action.sa_mask); |
michael@0 | 993 | action.sa_flags = SA_SIGINFO | SA_NODEFER; |
michael@0 | 994 | action.sa_restorer = nullptr; |
michael@0 | 995 | stackPtr.Assign(MemoryRange::mmap(nullptr, PageSize(), |
michael@0 | 996 | PROT_READ | PROT_WRITE, |
michael@0 | 997 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); |
michael@0 | 998 | if (stackPtr.get() == MAP_FAILED) |
michael@0 | 999 | return; |
michael@0 | 1000 | if (sys_sigaction(SIGSEGV, &action, nullptr)) |
michael@0 | 1001 | return; |
michael@0 | 1002 | |
michael@0 | 1003 | TmpData *data = reinterpret_cast<TmpData*>(stackPtr.get()); |
michael@0 | 1004 | data->crash_timestamp = ProcessTimeStamp_Now(); |
michael@0 | 1005 | mprotect(stackPtr, stackPtr.GetLength(), PROT_NONE); |
michael@0 | 1006 | data->crash_int = 123; |
michael@0 | 1007 | /* Restore the original segfault signal handler. */ |
michael@0 | 1008 | sys_sigaction(SIGSEGV, &this->action, nullptr); |
michael@0 | 1009 | stackPtr.Assign(MAP_FAILED, 0); |
michael@0 | 1010 | if (signalHandlingBroken || signalHandlingSlow) |
michael@0 | 1011 | return; |
michael@0 | 1012 | |
michael@0 | 1013 | /* Setup an alternative stack if the already existing one is not big |
michael@0 | 1014 | * enough, or if there is none. */ |
michael@0 | 1015 | if (sigaltstack(nullptr, &oldStack) == 0) { |
michael@0 | 1016 | if (oldStack.ss_flags == SS_ONSTACK) |
michael@0 | 1017 | oldStack.ss_flags = 0; |
michael@0 | 1018 | if (!oldStack.ss_sp || oldStack.ss_size < stackSize) { |
michael@0 | 1019 | stackPtr.Assign(MemoryRange::mmap(nullptr, stackSize, |
michael@0 | 1020 | PROT_READ | PROT_WRITE, |
michael@0 | 1021 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); |
michael@0 | 1022 | if (stackPtr.get() == MAP_FAILED) |
michael@0 | 1023 | return; |
michael@0 | 1024 | stack_t stack; |
michael@0 | 1025 | stack.ss_sp = stackPtr; |
michael@0 | 1026 | stack.ss_size = stackSize; |
michael@0 | 1027 | stack.ss_flags = 0; |
michael@0 | 1028 | if (sigaltstack(&stack, nullptr) != 0) |
michael@0 | 1029 | return; |
michael@0 | 1030 | } |
michael@0 | 1031 | } |
michael@0 | 1032 | /* Register our own handler, and store the already registered one in |
michael@0 | 1033 | * SEGVHandler's struct sigaction member */ |
michael@0 | 1034 | action.sa_sigaction = &SEGVHandler::handler; |
michael@0 | 1035 | action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; |
michael@0 | 1036 | registeredHandler = !sys_sigaction(SIGSEGV, &action, nullptr); |
michael@0 | 1037 | } |
michael@0 | 1038 | |
michael@0 | 1039 | SEGVHandler::~SEGVHandler() |
michael@0 | 1040 | { |
michael@0 | 1041 | /* Restore alternative stack for signals */ |
michael@0 | 1042 | if (oldStack.ss_flags != SS_ONSTACK) |
michael@0 | 1043 | sigaltstack(&oldStack, nullptr); |
michael@0 | 1044 | /* Restore original signal handler */ |
michael@0 | 1045 | if (registeredHandler) |
michael@0 | 1046 | sys_sigaction(SIGSEGV, &this->action, nullptr); |
michael@0 | 1047 | } |
michael@0 | 1048 | |
michael@0 | 1049 | /* Test handler for a deliberately triggered SIGSEGV that determines whether |
michael@0 | 1050 | * useful information is provided to signal handlers, particularly whether |
michael@0 | 1051 | * si_addr is filled in properly, and whether the segfault handler is called |
michael@0 | 1052 | * quickly enough. */ |
michael@0 | 1053 | void SEGVHandler::test_handler(int signum, siginfo_t *info, void *context) |
michael@0 | 1054 | { |
michael@0 | 1055 | SEGVHandler &that = ElfLoader::Singleton; |
michael@0 | 1056 | if (signum != SIGSEGV || |
michael@0 | 1057 | info == nullptr || info->si_addr != that.stackPtr.get()) |
michael@0 | 1058 | that.signalHandlingBroken = true; |
michael@0 | 1059 | mprotect(that.stackPtr, that.stackPtr.GetLength(), PROT_READ | PROT_WRITE); |
michael@0 | 1060 | TmpData *data = reinterpret_cast<TmpData*>(that.stackPtr.get()); |
michael@0 | 1061 | uint64_t latency = ProcessTimeStamp_Now() - data->crash_timestamp; |
michael@0 | 1062 | DEBUG_LOG("SEGVHandler latency: %" PRIu64, latency); |
michael@0 | 1063 | /* See bug 886736 for timings on different devices, 150 µs is reasonably above |
michael@0 | 1064 | * the latency on "working" devices and seems to be reasonably fast to incur |
michael@0 | 1065 | * a huge overhead to on-demand decompression. */ |
michael@0 | 1066 | if (latency > 150000) |
michael@0 | 1067 | that.signalHandlingSlow = true; |
michael@0 | 1068 | } |
michael@0 | 1069 | |
michael@0 | 1070 | /* TODO: "properly" handle signal masks and flags */ |
michael@0 | 1071 | void SEGVHandler::handler(int signum, siginfo_t *info, void *context) |
michael@0 | 1072 | { |
michael@0 | 1073 | //ASSERT(signum == SIGSEGV); |
michael@0 | 1074 | DEBUG_LOG("Caught segmentation fault @%p", info->si_addr); |
michael@0 | 1075 | |
michael@0 | 1076 | /* Check whether we segfaulted in the address space of a CustomElf. We're |
michael@0 | 1077 | * only expecting that to happen as an access error. */ |
michael@0 | 1078 | if (info->si_code == SEGV_ACCERR) { |
michael@0 | 1079 | mozilla::RefPtr<LibHandle> handle = |
michael@0 | 1080 | ElfLoader::Singleton.GetHandleByPtr(info->si_addr); |
michael@0 | 1081 | if (handle && !handle->IsSystemElf()) { |
michael@0 | 1082 | DEBUG_LOG("Within the address space of a CustomElf"); |
michael@0 | 1083 | CustomElf *elf = static_cast<CustomElf *>(static_cast<LibHandle *>(handle)); |
michael@0 | 1084 | if (elf->mappable->ensure(info->si_addr)) |
michael@0 | 1085 | return; |
michael@0 | 1086 | } |
michael@0 | 1087 | } |
michael@0 | 1088 | |
michael@0 | 1089 | /* Redispatch to the registered handler */ |
michael@0 | 1090 | SEGVHandler &that = ElfLoader::Singleton; |
michael@0 | 1091 | if (that.action.sa_flags & SA_SIGINFO) { |
michael@0 | 1092 | DEBUG_LOG("Redispatching to registered handler @%p", |
michael@0 | 1093 | FunctionPtr(that.action.sa_sigaction)); |
michael@0 | 1094 | that.action.sa_sigaction(signum, info, context); |
michael@0 | 1095 | } else if (that.action.sa_handler == SIG_DFL) { |
michael@0 | 1096 | DEBUG_LOG("Redispatching to default handler"); |
michael@0 | 1097 | /* Reset the handler to the default one, and trigger it. */ |
michael@0 | 1098 | sys_sigaction(signum, &that.action, nullptr); |
michael@0 | 1099 | raise(signum); |
michael@0 | 1100 | } else if (that.action.sa_handler != SIG_IGN) { |
michael@0 | 1101 | DEBUG_LOG("Redispatching to registered handler @%p", |
michael@0 | 1102 | FunctionPtr(that.action.sa_handler)); |
michael@0 | 1103 | that.action.sa_handler(signum); |
michael@0 | 1104 | } else { |
michael@0 | 1105 | DEBUG_LOG("Ignoring"); |
michael@0 | 1106 | } |
michael@0 | 1107 | } |
michael@0 | 1108 | |
michael@0 | 1109 | int |
michael@0 | 1110 | SEGVHandler::__wrap_sigaction(int signum, const struct sigaction *act, |
michael@0 | 1111 | struct sigaction *oldact) |
michael@0 | 1112 | { |
michael@0 | 1113 | SEGVHandler &that = ElfLoader::Singleton; |
michael@0 | 1114 | |
michael@0 | 1115 | /* Use system sigaction() function for all but SIGSEGV signals. */ |
michael@0 | 1116 | if (!that.registeredHandler || (signum != SIGSEGV)) |
michael@0 | 1117 | return sys_sigaction(signum, act, oldact); |
michael@0 | 1118 | |
michael@0 | 1119 | if (oldact) |
michael@0 | 1120 | *oldact = that.action; |
michael@0 | 1121 | if (act) |
michael@0 | 1122 | that.action = *act; |
michael@0 | 1123 | return 0; |
michael@0 | 1124 | } |
michael@0 | 1125 | |
michael@0 | 1126 | Logging Logging::Singleton; |