| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/trace_event/cfi_backtrace_android.h" |
| |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| |
| #include "base/android/apk_assets.h" |
| #include "starboard/memory.h" |
| #include "starboard/types.h" |
| |
| #if !defined(ARCH_CPU_ARMEL) |
| #error This file should not be built for this architecture. |
| #endif |
| |
| /* |
| Basics of unwinding: |
| For each instruction in a function we need to know what is the offset of SP |
| (Stack Pointer) to reach the previous function's stack frame. To know which |
| function is being invoked, we need the return address of the next function. The |
| CFI information for an instruction is made up of 2 offsets, CFA (Call Frame |
| Address) offset and RA (Return Address) offset. The CFA offset is the change in |
| SP made by the function till the current instruction. This depends on amount of |
| memory allocated on stack by the function plus some registers that the function |
| stores that needs to be restored at the end of function. So, at each instruction |
| the CFA offset tells the offset from original SP before the function call. The |
| RA offset tells us the offset from the previous SP into the current function |
| where the return address is stored. |
| |
| The unwind table file has 2 tables UNW_INDEX and UNW_DATA, inspired from ARM |
| EHABI format. The first table contains function addresses and an index into the |
| UNW_DATA table. The second table contains one or more rows for the function |
| unwind information. |
| |
| UNW_INDEX contains two columns of N rows each, where N is the number of |
| functions. |
| 1. First column 4 byte rows of all the function start address as offset from |
| start of the binary, in sorted order. |
| 2. For each function addr, the second column contains 2 byte indices in order. |
| The indices are offsets (in count of 2 bytes) of the CFI data from start of |
| UNW_DATA. |
| The last entry in the table always contains CANT_UNWIND index to specify the |
| end address of the last function. |
| |
| UNW_DATA contains data of all the functions. Each function data contains N rows. |
| The data found at the address pointed from UNW_INDEX will be: |
| 2 bytes: N - number of rows that belong to current function. |
| N * 4 bytes: N rows of data. 16 bits : Address offset from function start. |
| 14 bits : CFA offset / 4. |
| 2 bits : RA offset / 4. |
| If the RA offset of a row is 0, then use the offset of the previous rows in the |
| same function. |
| TODO(ssid): Make sure RA offset is always present. |
| |
| See extract_unwind_tables.py for details about how this data is extracted from |
| breakpad symbol files. |
| */ |
| |
| extern "C" { |
| |
| // The address of |__executable_start| gives the start address of the |
| // executable or shared library. This value is used to find the offset address |
| // of the instruction in binary from PC. |
| extern char __executable_start; |
| |
| // The address |_etext| gives the end of the .text section in the binary. This |
| // value is more accurate than parsing the memory map since the mapped |
| // regions are usualy larger than the .text section. |
| extern char _etext; |
| } |
| |
| namespace base { |
| namespace trace_event { |
| |
| namespace { |
| |
| // The value of index when the function does not have unwind information. |
| constexpr uint32_t kCantUnwind = 0xFFFF; |
| |
| // The mask on the CFI row data that is used to get the high 14 bits and |
| // multiply it by 4 to get CFA offset. Since the last 2 bits are masked out, a |
| // shift is not necessary. |
| constexpr uint16_t kCFAMask = 0xfffc; |
| |
| // The mask on the CFI row data that is used to get the low 2 bits and multiply |
| // it by 4 to get the RA offset. |
| constexpr uint16_t kRAMask = 0x3; |
| constexpr uint16_t kRAShift = 2; |
| |
| // The code in this file assumes we are running in 32-bit builds since all the |
| // addresses in the unwind table are specified in 32 bits. |
| static_assert(sizeof(uintptr_t) == 4, |
| "The unwind table format is only valid for 32 bit builds."); |
| |
| // The CFI data in UNW_DATA table starts with number of rows (N) and then |
| // followed by N rows of 4 bytes long. The CFIUnwindDataRow represents a single |
| // row of CFI data of a function in the table. Since we cast the memory at the |
| // address after the address of number of rows, into an array of |
| // CFIUnwindDataRow, the size of the struct should be 4 bytes and the order of |
| // the members is fixed according to the given format. The first 2 bytes tell |
| // the address of function and last 2 bytes give the CFI data for the offset. |
| struct CFIUnwindDataRow { |
| // The address of the instruction in terms of offset from the start of the |
| // function. |
| uint16_t addr_offset; |
| // Represents the CFA and RA offsets to get information about next stack |
| // frame. This is the CFI data at the point before executing the instruction |
| // at |addr_offset| from the start of the function. |
| uint16_t cfi_data; |
| |
| // Return the RA offset for the current unwind row. |
| size_t ra_offset() const { return (cfi_data & kRAMask) << kRAShift; } |
| |
| // Returns the CFA offset for the current unwind row. |
| size_t cfa_offset() const { return cfi_data & kCFAMask; } |
| }; |
| |
| static_assert( |
| sizeof(CFIUnwindDataRow) == 4, |
| "The CFIUnwindDataRow struct must be exactly 4 bytes for searching."); |
| |
| } // namespace |
| |
| // static |
| CFIBacktraceAndroid* CFIBacktraceAndroid::GetInitializedInstance() { |
| static CFIBacktraceAndroid* instance = new CFIBacktraceAndroid(); |
| return instance; |
| } |
| |
| // static |
| uintptr_t CFIBacktraceAndroid::executable_start_addr() { |
| return reinterpret_cast<uintptr_t>(&__executable_start); |
| } |
| |
| // static |
| uintptr_t CFIBacktraceAndroid::executable_end_addr() { |
| return reinterpret_cast<uintptr_t>(&_etext); |
| } |
| |
| CFIBacktraceAndroid::CFIBacktraceAndroid() |
| : thread_local_cfi_cache_( |
| [](void* ptr) { delete static_cast<CFICache*>(ptr); }) { |
| Initialize(); |
| } |
| |
| CFIBacktraceAndroid::~CFIBacktraceAndroid() {} |
| |
| void CFIBacktraceAndroid::Initialize() { |
| // This file name is defined by extract_unwind_tables.gni. |
| static constexpr char kCfiFileName[] = "assets/unwind_cfi_32"; |
| MemoryMappedFile::Region cfi_region; |
| int fd = base::android::OpenApkAsset(kCfiFileName, &cfi_region); |
| if (fd < 0) |
| return; |
| cfi_mmap_ = std::make_unique<MemoryMappedFile>(); |
| // The CFI region starts at |cfi_region.offset|. |
| if (!cfi_mmap_->Initialize(base::File(fd), cfi_region)) |
| return; |
| |
| ParseCFITables(); |
| can_unwind_stack_frames_ = true; |
| } |
| |
| void CFIBacktraceAndroid::ParseCFITables() { |
| // The first 4 bytes in the file is the size of UNW_INDEX table. |
| static constexpr size_t kUnwIndexRowSize = |
| sizeof(*unw_index_function_col_) + sizeof(*unw_index_indices_col_); |
| size_t unw_index_size = 0; |
| memcpy(&unw_index_size, cfi_mmap_->data(), sizeof(unw_index_size)); |
| DCHECK_EQ(0u, unw_index_size % kUnwIndexRowSize); |
| // UNW_INDEX table starts after 4 bytes. |
| unw_index_function_col_ = |
| reinterpret_cast<const uintptr_t*>(cfi_mmap_->data()) + 1; |
| unw_index_row_count_ = unw_index_size / kUnwIndexRowSize; |
| unw_index_indices_col_ = reinterpret_cast<const uint16_t*>( |
| unw_index_function_col_ + unw_index_row_count_); |
| |
| // The UNW_DATA table data is right after the end of UNW_INDEX table. |
| // Interpret the UNW_DATA table as an array of 2 byte numbers since the |
| // indexes we have from the UNW_INDEX table are in terms of 2 bytes. |
| unw_data_start_addr_ = unw_index_indices_col_ + unw_index_row_count_; |
| } |
| |
| size_t CFIBacktraceAndroid::Unwind(const void** out_trace, size_t max_depth) { |
| // This function walks the stack using the call frame information to find the |
| // return addresses of all the functions that belong to current binary in call |
| // stack. For each function the CFI table defines the offset of the previous |
| // call frame and offset where the return address is stored. |
| if (!can_unwind_stack_frames()) |
| return 0; |
| |
| // Get the current register state. This register state can be taken at any |
| // point in the function and the unwind information would be for this point. |
| // Define local variables before trying to get the current PC and SP to make |
| // sure the register state obtained is consistent with each other. |
| uintptr_t pc = 0, sp = 0; |
| asm volatile("mov %0, pc" : "=r"(pc)); |
| asm volatile("mov %0, sp" : "=r"(sp)); |
| |
| return Unwind(pc, sp, out_trace, max_depth); |
| } |
| |
| size_t CFIBacktraceAndroid::Unwind(uintptr_t pc, |
| uintptr_t sp, |
| const void** out_trace, |
| size_t max_depth) { |
| if (!can_unwind_stack_frames()) |
| return 0; |
| |
| // We can only unwind as long as the pc is within the chrome.so. |
| size_t depth = 0; |
| while (is_chrome_address(pc) && depth < max_depth) { |
| out_trace[depth++] = reinterpret_cast<void*>(pc); |
| // The offset of function from the start of the chrome.so binary: |
| uintptr_t func_addr = pc - executable_start_addr(); |
| CFIRow cfi{}; |
| if (!FindCFIRowForPC(func_addr, &cfi)) |
| break; |
| |
| // The rules for unwinding using the CFI information are: |
| // SP_prev = SP_cur + cfa_offset and |
| // PC_prev = * (SP_prev - ra_offset). |
| sp = sp + cfi.cfa_offset; |
| memcpy(&pc, reinterpret_cast<uintptr_t*>(sp - cfi.ra_offset), |
| sizeof(uintptr_t)); |
| } |
| return depth; |
| } |
| |
| void CFIBacktraceAndroid::AllocateCacheForCurrentThread() { |
| GetThreadLocalCFICache(); |
| } |
| |
| bool CFIBacktraceAndroid::FindCFIRowForPC(uintptr_t func_addr, |
| CFIBacktraceAndroid::CFIRow* cfi) { |
| if (!can_unwind_stack_frames()) |
| return false; |
| |
| auto* cache = GetThreadLocalCFICache(); |
| *cfi = {0}; |
| if (cache->Find(func_addr, cfi)) |
| return true; |
| |
| // Consider each column of UNW_INDEX table as arrays of uintptr_t (function |
| // addresses) and uint16_t (indices). Define start and end iterator on the |
| // first column array (addresses) and use std::lower_bound() to binary search |
| // on this array to find the required function address. |
| static const uintptr_t* const unw_index_fn_end = |
| unw_index_function_col_ + unw_index_row_count_; |
| const uintptr_t* found = |
| std::lower_bound(unw_index_function_col_, unw_index_fn_end, func_addr); |
| |
| // If found is start, then the given function is not in the table. If the |
| // given pc is start of a function then we cannot unwind. |
| if (found == unw_index_function_col_ || *found == func_addr) |
| return false; |
| |
| // std::lower_bound() returns the iter that corresponds to the first address |
| // that is greater than the given address. So, the required iter is always one |
| // less than the value returned by std::lower_bound(). |
| --found; |
| uintptr_t func_start_addr = *found; |
| size_t row_num = found - unw_index_function_col_; |
| uint16_t index = unw_index_indices_col_[row_num]; |
| DCHECK_LE(func_start_addr, func_addr); |
| // If the index is CANT_UNWIND then we do not have unwind infomation for the |
| // function. |
| if (index == kCantUnwind) |
| return false; |
| |
| // The unwind data for the current function is at an offsset of the index |
| // found in UNW_INDEX table. |
| const uint16_t* unwind_data = unw_data_start_addr_ + index; |
| // The value of first 2 bytes is the CFI data row count for the function. |
| uint16_t row_count = 0; |
| memcpy(&row_count, unwind_data, sizeof(row_count)); |
| // And the actual CFI rows start after 2 bytes from the |unwind_data|. Cast |
| // the data into an array of CFIUnwindDataRow since the struct is designed to |
| // represent each row. We should be careful to read only |row_count| number of |
| // elements in the array. |
| const CFIUnwindDataRow* function_data = |
| reinterpret_cast<const CFIUnwindDataRow*>(unwind_data + 1); |
| |
| // Iterate through the CFI rows of the function to find the row that gives |
| // offset for the given instruction address. |
| CFIUnwindDataRow cfi_row = {0, 0}; |
| uint16_t ra_offset = 0; |
| for (uint16_t i = 0; i < row_count; ++i) { |
| CFIUnwindDataRow row; |
| memcpy(&row, function_data + i, sizeof(CFIUnwindDataRow)); |
| // The return address of the function is the instruction that is not yet |
| // been executed. The cfi row specifies the unwind info before executing the |
| // given instruction. If the given address is equal to the instruction |
| // offset, then use the current row. Or use the row with highest address |
| // less than the given address. |
| if (row.addr_offset + func_start_addr > func_addr) |
| break; |
| |
| cfi_row = row; |
| // The ra offset of the last specified row should be used, if unspecified. |
| // So, keep updating the RA offset till we reach the correct CFI row. |
| // TODO(ssid): This should be fixed in the format and we should always |
| // output ra offset. |
| if (cfi_row.ra_offset()) |
| ra_offset = cfi_row.ra_offset(); |
| } |
| DCHECK_NE(0u, cfi_row.addr_offset); |
| *cfi = {cfi_row.cfa_offset(), ra_offset}; |
| DCHECK(cfi->cfa_offset); |
| DCHECK(cfi->ra_offset); |
| |
| // safe to update since the cache is thread local. |
| cache->Add(func_addr, *cfi); |
| return true; |
| } |
| |
| CFIBacktraceAndroid::CFICache* CFIBacktraceAndroid::GetThreadLocalCFICache() { |
| auto* cache = static_cast<CFICache*>(thread_local_cfi_cache_.Get()); |
| if (!cache) { |
| cache = new CFICache(); |
| thread_local_cfi_cache_.Set(cache); |
| } |
| return cache; |
| } |
| |
| void CFIBacktraceAndroid::CFICache::Add(uintptr_t address, CFIRow cfi) { |
| cache_[address % kLimit] = {address, cfi}; |
| } |
| |
| bool CFIBacktraceAndroid::CFICache::Find(uintptr_t address, CFIRow* cfi) { |
| if (cache_[address % kLimit].address == address) { |
| *cfi = cache_[address % kLimit].cfi; |
| return true; |
| } |
| return false; |
| } |
| |
| } // namespace trace_event |
| } // namespace base |