OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "crazy_linker_rdebug.h" | 5 #include "crazy_linker_rdebug.h" |
6 | 6 |
7 #include <elf.h> | 7 #include <elf.h> |
8 #include <inttypes.h> | 8 #include <inttypes.h> |
9 #include <pthread.h> | 9 #include <pthread.h> |
10 #include <sys/mman.h> | 10 #include <sys/mman.h> |
11 #include <unistd.h> | 11 #include <unistd.h> |
12 | 12 |
13 #include "crazy_linker_debug.h" | 13 #include "crazy_linker_debug.h" |
| 14 #include "crazy_linker_globals.h" |
14 #include "crazy_linker_proc_maps.h" | 15 #include "crazy_linker_proc_maps.h" |
15 #include "crazy_linker_util.h" | 16 #include "crazy_linker_util.h" |
16 #include "crazy_linker_system.h" | 17 #include "crazy_linker_system.h" |
17 #include "elf_traits.h" | 18 #include "elf_traits.h" |
18 | 19 |
19 namespace crazy { | 20 namespace crazy { |
20 | 21 |
21 namespace { | 22 namespace { |
22 | 23 |
23 // Find the full path of the current executable. On success return true | 24 // Find the full path of the current executable. On success return true |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 (void*)*dynamic_size); | 149 (void*)*dynamic_size); |
149 return true; | 150 return true; |
150 } | 151 } |
151 | 152 |
152 LOG("%s: Executable is not mapped in current process.\n", __FUNCTION__); | 153 LOG("%s: Executable is not mapped in current process.\n", __FUNCTION__); |
153 return false; | 154 return false; |
154 } | 155 } |
155 | 156 |
156 // Helper class to temporarily remap a page to readable+writable until | 157 // Helper class to temporarily remap a page to readable+writable until |
157 // scope exit. | 158 // scope exit. |
158 class ScopedPageMapper { | 159 class ScopedPageReadWriteRemapper { |
159 public: | 160 public: |
160 ScopedPageMapper() : page_address_(0), page_prot_(0) {} | 161 ScopedPageReadWriteRemapper(void* address); |
161 void MapReadWrite(void* address); | 162 ~ScopedPageReadWriteRemapper(); |
162 ~ScopedPageMapper(); | 163 |
| 164 // Releases the page so that the destructor does not undo the remapping. |
| 165 void Release(); |
163 | 166 |
164 private: | 167 private: |
165 static const uintptr_t kPageSize = 4096; | 168 static const uintptr_t kPageSize = 4096; |
166 uintptr_t page_address_; | 169 uintptr_t page_address_; |
167 int page_prot_; | 170 int page_prot_; |
168 }; | 171 }; |
169 | 172 |
170 void ScopedPageMapper::MapReadWrite(void* address) { | 173 ScopedPageReadWriteRemapper::ScopedPageReadWriteRemapper(void* address) { |
171 page_address_ = reinterpret_cast<uintptr_t>(address) & ~(kPageSize - 1); | 174 page_address_ = reinterpret_cast<uintptr_t>(address) & ~(kPageSize - 1); |
172 page_prot_ = 0; | 175 page_prot_ = 0; |
173 if (!FindProtectionFlagsForAddress(address, &page_prot_) || | 176 if (!FindProtectionFlagsForAddress(address, &page_prot_)) { |
174 (page_prot_ & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) { | 177 LOG("Could not find protection flags for %p\n", address); |
175 // If the address is invalid, or if the page is already read+write, | |
176 // no need to do anything here. | |
177 page_address_ = 0; | 178 page_address_ = 0; |
178 return; | 179 return; |
179 } | 180 } |
| 181 |
| 182 // Note: page_prot_ may already indicate read/write, but because of |
| 183 // possible races with the system linker we cannot be confident that |
| 184 // this is reliable. So we always set read/write here. |
| 185 // |
| 186 // See commentary in WriteLinkMapField for more. |
180 int new_page_prot = page_prot_ | PROT_READ | PROT_WRITE; | 187 int new_page_prot = page_prot_ | PROT_READ | PROT_WRITE; |
181 int ret = mprotect( | 188 int ret = mprotect( |
182 reinterpret_cast<void*>(page_address_), kPageSize, new_page_prot); | 189 reinterpret_cast<void*>(page_address_), kPageSize, new_page_prot); |
183 if (ret < 0) { | 190 if (ret < 0) { |
184 LOG_ERRNO("Could not remap page to read/write"); | 191 LOG_ERRNO("Could not remap page to read/write"); |
185 page_address_ = 0; | 192 page_address_ = 0; |
186 } | 193 } |
187 } | 194 } |
188 | 195 |
189 ScopedPageMapper::~ScopedPageMapper() { | 196 ScopedPageReadWriteRemapper::~ScopedPageReadWriteRemapper() { |
190 if (page_address_) { | 197 if (page_address_) { |
191 int ret = | 198 int ret = |
192 mprotect(reinterpret_cast<void*>(page_address_), kPageSize, page_prot_); | 199 mprotect(reinterpret_cast<void*>(page_address_), kPageSize, page_prot_); |
193 if (ret < 0) | 200 if (ret < 0) |
194 LOG_ERRNO("Could not remap page to old protection flags"); | 201 LOG_ERRNO("Could not remap page to old protection flags"); |
195 } | 202 } |
196 } | 203 } |
197 | 204 |
| 205 void ScopedPageReadWriteRemapper::Release() { |
| 206 page_address_ = 0; |
| 207 page_prot_ = 0; |
| 208 } |
| 209 |
198 } // namespace | 210 } // namespace |
199 | 211 |
200 bool RDebug::Init() { | 212 bool RDebug::Init() { |
201 // The address of '_r_debug' is in the DT_DEBUG entry of the current | 213 // The address of '_r_debug' is in the DT_DEBUG entry of the current |
202 // executable. | 214 // executable. |
203 init_ = true; | 215 init_ = true; |
204 | 216 |
205 size_t dynamic_addr = 0; | 217 size_t dynamic_addr = 0; |
206 size_t dynamic_size = 0; | 218 size_t dynamic_size = 0; |
207 String path; | 219 String path; |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 LOG("%s: Posted for later execution, runnable=%p\n", __FUNCTION__, runnable); | 387 LOG("%s: Posted for later execution, runnable=%p\n", __FUNCTION__, runnable); |
376 | 388 |
377 if (is_blocking) { | 389 if (is_blocking) { |
378 RDebugRunnable::WaitForCallback(runnable); | 390 RDebugRunnable::WaitForCallback(runnable); |
379 LOG("%s: Completed execution, runnable=%p\n", __FUNCTION__, runnable); | 391 LOG("%s: Completed execution, runnable=%p\n", __FUNCTION__, runnable); |
380 } | 392 } |
381 | 393 |
382 return true; | 394 return true; |
383 } | 395 } |
384 | 396 |
| 397 // Helper function for AddEntryImpl and DelEntryImpl. |
| 398 // Sets *link_pointer to entry. link_pointer is either an 'l_prev' or an |
| 399 // 'l_next' field in a neighbouring linkmap_t. If link_pointer is in a |
| 400 // page that is mapped readonly, the page is remapped to be writable before |
| 401 // assignment. |
| 402 void RDebug::WriteLinkMapField(link_map_t** link_pointer, link_map_t* entry) { |
| 403 ScopedPageReadWriteRemapper mapper(link_pointer); |
| 404 LOG("%s: Remapped page for %p for read/write\n", __FUNCTION__, link_pointer); |
| 405 |
| 406 *link_pointer = entry; |
| 407 |
| 408 // We always mprotect the page containing link_pointer to read/write, |
| 409 // then write the entry. The page may already be read/write, but on |
| 410 // recent Android release is most likely readonly. Because of the way |
| 411 // the system linker operates we cannot tell with certainty what its |
| 412 // correct setting should be. |
| 413 // |
| 414 // Now, we always leave the page read/write. Here is why. If we set it |
| 415 // back to readonly at the point between where the system linker sets |
| 416 // it to read/write and where it writes to the address, this will cause |
| 417 // the system linker to crash. Clearly that is undesirable. From |
| 418 // observations this occurs most frequently on the gpu process. |
| 419 // |
| 420 // TODO(simonb): Revisit this, details in: |
| 421 // https://code.google.com/p/chromium/issues/detail?id=450659 |
| 422 // https://code.google.com/p/chromium/issues/detail?id=458346 |
| 423 mapper.Release(); |
| 424 LOG("%s: Released mapper, leaving page read/write\n", __FUNCTION__); |
| 425 } |
| 426 |
385 void RDebug::AddEntryImpl(link_map_t* entry) { | 427 void RDebug::AddEntryImpl(link_map_t* entry) { |
| 428 ScopedGlobalLock lock; |
386 LOG("%s: Adding: %s\n", __FUNCTION__, entry->l_name); | 429 LOG("%s: Adding: %s\n", __FUNCTION__, entry->l_name); |
387 if (!init_) | 430 if (!init_) |
388 Init(); | 431 Init(); |
389 | 432 |
390 if (!r_debug_) { | 433 if (!r_debug_) { |
391 LOG("%s: Nothing to do\n", __FUNCTION__); | 434 LOG("%s: Nothing to do\n", __FUNCTION__); |
392 return; | 435 return; |
393 } | 436 } |
394 | 437 |
395 // Tell GDB the list is going to be modified. | 438 // Tell GDB the list is going to be modified. |
(...skipping 30 matching lines...) Expand all Loading... |
426 link_map_t* after = before->l_next; | 469 link_map_t* after = before->l_next; |
427 | 470 |
428 // Prepare the new entry. | 471 // Prepare the new entry. |
429 entry->l_prev = before; | 472 entry->l_prev = before; |
430 entry->l_next = after; | 473 entry->l_next = after; |
431 | 474 |
432 // IMPORTANT: Before modifying the previous and next entries in the | 475 // IMPORTANT: Before modifying the previous and next entries in the |
433 // list, ensure that they are writable. This avoids crashing when | 476 // list, ensure that they are writable. This avoids crashing when |
434 // updating the 'l_prev' or 'l_next' fields of a system linker entry, | 477 // updating the 'l_prev' or 'l_next' fields of a system linker entry, |
435 // which are mapped read-only. | 478 // which are mapped read-only. |
436 { | 479 WriteLinkMapField(&before->l_next, entry); |
437 ScopedPageMapper mapper; | 480 WriteLinkMapField(&after->l_prev, entry); |
438 if (readonly_entries_) | |
439 mapper.MapReadWrite(before); | |
440 before->l_next = entry; | |
441 } | |
442 | |
443 { | |
444 ScopedPageMapper mapper; | |
445 if (readonly_entries_) | |
446 mapper.MapReadWrite(after); | |
447 after->l_prev = entry; | |
448 } | |
449 | 481 |
450 // Tell GDB that the list modification has completed. | 482 // Tell GDB that the list modification has completed. |
451 r_debug_->r_state = RT_CONSISTENT; | 483 r_debug_->r_state = RT_CONSISTENT; |
452 r_debug_->r_brk(); | 484 r_debug_->r_brk(); |
453 } | 485 } |
454 | 486 |
455 void RDebug::DelEntryImpl(link_map_t* entry) { | 487 void RDebug::DelEntryImpl(link_map_t* entry) { |
| 488 ScopedGlobalLock lock; |
456 LOG("%s: Deleting: %s\n", __FUNCTION__, entry->l_name); | 489 LOG("%s: Deleting: %s\n", __FUNCTION__, entry->l_name); |
457 if (!r_debug_) | 490 if (!r_debug_) |
458 return; | 491 return; |
459 | 492 |
460 // Tell GDB the list is going to be modified. | 493 // Tell GDB the list is going to be modified. |
461 r_debug_->r_state = RT_DELETE; | 494 r_debug_->r_state = RT_DELETE; |
462 r_debug_->r_brk(); | 495 r_debug_->r_brk(); |
463 | 496 |
464 // IMPORTANT: Before modifying the previous and next entries in the | 497 // IMPORTANT: Before modifying the previous and next entries in the |
465 // list, ensure that they are writable. See comment above for more | 498 // list, ensure that they are writable. See comment above for more |
466 // details. | 499 // details. |
467 if (entry->l_prev) { | 500 if (entry->l_prev) |
468 ScopedPageMapper mapper; | 501 WriteLinkMapField(&entry->l_prev->l_next, entry->l_next); |
469 if (readonly_entries_) | 502 if (entry->l_next) |
470 mapper.MapReadWrite(entry->l_prev); | 503 WriteLinkMapField(&entry->l_next->l_prev, entry->l_prev); |
471 entry->l_prev->l_next = entry->l_next; | |
472 } | |
473 | |
474 if (entry->l_next) { | |
475 ScopedPageMapper mapper; | |
476 if (readonly_entries_) | |
477 mapper.MapReadWrite(entry->l_next); | |
478 entry->l_next->l_prev = entry->l_prev; | |
479 } | |
480 | 504 |
481 if (r_debug_->r_map == entry) | 505 if (r_debug_->r_map == entry) |
482 r_debug_->r_map = entry->l_next; | 506 r_debug_->r_map = entry->l_next; |
483 | 507 |
484 entry->l_prev = NULL; | 508 entry->l_prev = NULL; |
485 entry->l_next = NULL; | 509 entry->l_next = NULL; |
486 | 510 |
487 // Tell GDB the list modification has completed. | 511 // Tell GDB the list modification has completed. |
488 r_debug_->r_state = RT_CONSISTENT; | 512 r_debug_->r_state = RT_CONSISTENT; |
489 r_debug_->r_brk(); | 513 r_debug_->r_brk(); |
490 } | 514 } |
491 | 515 |
492 } // namespace crazy | 516 } // namespace crazy |
OLD | NEW |