本篇介绍
本篇接着Android libunwind 源码解读 继续往下分析。
接下来看下InitHeaders:
template <typename ElfTypes>
void ElfInterfaceImpl<ElfTypes>::InitHeaders() {
if (eh_frame_hdr_offset_ != 0) {
DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
eh_frame_.reset(eh_frame_hdr);
if (!eh_frame_hdr->EhFrameInit(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_) ||
!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
eh_frame_.reset(nullptr);
}
}
if (eh_frame_.get() == nullptr && eh_frame_offset_ != 0) {
// If there is an eh_frame section without an eh_frame_hdr section,
// or using the frame hdr object failed to init.
eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_)) {
eh_frame_.reset(nullptr);
}
}
if (eh_frame_.get() == nullptr) {
eh_frame_hdr_offset_ = 0;
eh_frame_hdr_section_bias_ = 0;
eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
eh_frame_offset_ = 0;
eh_frame_section_bias_ = 0;
eh_frame_size_ = static_cast<uint64_t>(-1);
}
if (debug_frame_offset_ != 0) {
debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, debug_frame_section_bias_)) {
debug_frame_.reset(nullptr);
debug_frame_offset_ = 0;
debug_frame_size_ = static_cast<uint64_t>(-1);
}
}
}
DwarfEhFrameWithHdr用来记录和Dwarf格式相关,在回栈的时候需要用到Dwarf信息。这儿最好了解下Dwarf格式,该信息会记录到ehframe中,看下如何初始化的:
bool DwarfEhFrameWithHdr<AddressType>::EhFrameInit(uint64_t offset, uint64_t size,
int64_t section_bias) {
return DwarfSectionImpl<AddressType>::Init(offset, size, section_bias);
}
继续看下:
template <typename AddressType>
bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t, int64_t section_bias) {
memory_.clear_func_offset();
memory_.clear_text_offset();
memory_.set_data_offset(offset);
memory_.set_cur_offset(offset);
hdr_section_bias_ = section_bias;
// Read the first four bytes all at once.
uint8_t data[4];
if (!memory_.ReadBytes(data, 4)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
version_ = data[0];
if (version_ != 1) {
// Unknown version.
last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
return false;
}
uint8_t ptr_encoding = data[1];
uint8_t fde_count_encoding = data[2];
table_encoding_ = data[3];
table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
// If we can't perform a binary search on the entries, it's not worth
// using this object. The calling code will fall back to the DwarfEhFrame
// object in this case.
if (table_entry_size_ == 0) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
memory_.set_pc_offset(memory_.cur_offset());
uint64_t ptr_offset;
if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding, &ptr_offset)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
memory_.set_pc_offset(memory_.cur_offset());
if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
if (fde_count_ == 0) {
last_error_.code = DWARF_ERROR_NO_FDES;
return false;
}
hdr_entries_offset_ = memory_.cur_offset();
hdr_entries_data_offset_ = offset;
return true;
}
这儿操作的memory 实际上是DwarfMemory,内部持有Memory。这儿的入口就是将当前偏移调整为eh_frame section的位置上。
使用dwarfdump命令可以解析该section的内容,比如我找了一个so,输出如下:
.eh_frame contents:
00000000 00000014 00000000 CIE
Format: DWARF32
Version: 1
Augmentation: "zR"
Code alignment factor: 1
Data alignment factor: -4
Return address column: 30
Augmentation data: 1B
最好可以了解下Dwarf的格式,比如CFI,CIE,DFE,CFA。
再看下eh_frame_的Init,eh_frame_是DwarfSection:
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
section_bias_ = section_bias;
entries_offset_ = offset;
entries_end_ = offset + size;
memory_.clear_func_offset();
memory_.clear_text_offset();
memory_.set_cur_offset(offset);
pc_offset_ = offset;
return true;
}
这时候只要DwarfEhFrameWithHdr或者DwarfSectionImpl有一个Init失败,就会导致eh_frame_被重置为null。
接下来需要会使用eh_frame初始化eh_frame_,也就是没有eh_frame_hdr信息的case。
接下来初始化debug_frame_信息,也就是读取.debug_frame section的信息。
接下来再看下InitGnuDebugdata,这时候读取的是 .gnu_debugdata 段的信息:
// It is expensive to initialize the .gnu_debugdata section. Provide a method
// to initialize this data separately.
void Elf::InitGnuDebugdata() {
if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
return;
}
gnu_debugdata_memory_ = interface_->CreateGnuDebugdataMemory();
gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
ElfInterface* gnu = gnu_debugdata_interface_.get();
if (gnu == nullptr) {
return;
}
// Ignore the load_bias from the compressed section, the correct load bias
// is in the uncompressed data.
int64_t load_bias;
if (gnu->Init(&load_bias)) {
gnu->InitHeaders();
interface_->SetGnuDebugdataInterface(gnu);
} else {
// Free all of the memory associated with the gnu_debugdata section.
gnu_debugdata_memory_.reset(nullptr);
gnu_debugdata_interface_.reset(nullptr);
}
}
如果包含了 .gnu_debugdata 段,那就会读取该段信息,实现如下:
std::unique_ptr<Memory> ElfInterface::CreateGnuDebugdataMemory() {
if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
return nullptr;
}
auto decompressed =
std::make_unique<MemoryXz>(memory_, gnu_debugdata_offset_, gnu_debugdata_size_, GetSoname());
if (!decompressed || !decompressed->Init()) {
gnu_debugdata_offset_ = 0;
gnu_debugdata_size_ = 0;
return nullptr;
}
return decompressed;
}
在Init的时候就会对该段的信息进行解压:
bool MemoryXz::Init() {
static std::once_flag crc_initialized;
std::call_once(crc_initialized, []() {
CrcGenerateTable();
Crc64GenerateTable();
});
if (compressed_size_ >= kMaxCompressedSize) {
return false;
}
if (!ReadBlocks()) {
return false;
}
// All blocks (except the last one) must have the same power-of-2 size.
if (blocks_.size() > 1) {
size_t block_size_log2 = __builtin_ctz(blocks_.front().decompressed_size);
auto correct_size = [=](XzBlock& b) { return b.decompressed_size == (1 << block_size_log2); };
if (std::all_of(blocks_.begin(), std::prev(blocks_.end()), correct_size) &&
blocks_.back().decompressed_size <= (1 << block_size_log2)) {
block_size_log2_ = block_size_log2;
} else {
// Inconsistent block-sizes. Decompress and merge everything now.
std::unique_ptr<uint8_t[]> data(new uint8_t[size_]);
size_t offset = 0;
for (XzBlock& block : blocks_) {
if (!Decompress(&block)) {
return false;
}
memcpy(data.get() + offset, block.decompressed_data.get(), block.decompressed_size);
offset += block.decompressed_size;
}
blocks_.clear();
blocks_.push_back(XzBlock{
.decompressed_data = std::move(data),
.decompressed_size = size_,
});
block_size_log2_ = 31; // Because 32 bits is too big (shift right by 32 is not allowed).
}
}
return true;
}
到这儿总算把Elf的Init 走完了,继续回到GetElf,
// If the init fails, keep the elf around as an invalid object so we
// don't try to reinit the object.
elf()->Init();
if (elf()->valid() && expected_arch != elf()->arch()) {
// Make the elf invalid, mismatch between arch and expected arch.
elf()->Invalidate();
}
if (!elf()->valid()) {
set_elf_start_offset(offset());
} else if (auto prev_real_map = GetPrevRealMap(); prev_real_map != nullptr &&
prev_real_map->flags() == PROT_READ &&
prev_real_map->offset() < offset()) {
// If there is a read-only map then a read-execute map that represents the
// same elf object, make sure the previous map is using the same elf
// object if it hasn't already been set. Locking this should not result
// in a deadlock as long as the invariant that the code only ever tries
// to lock the previous real map holds true.
std::lock_guard<std::mutex> guard(prev_real_map->elf_mutex());
if (prev_real_map->elf() == nullptr) {
// Need to verify if the map is the previous read-only map.
prev_real_map->set_elf(elf());
prev_real_map->set_memory_backed_elf(memory_backed_elf());
prev_real_map->set_elf_start_offset(elf_start_offset());
prev_real_map->set_elf_offset(prev_real_map->offset() - elf_start_offset());
} else if (prev_real_map->elf_start_offset() == elf_start_offset()) {
// Discard this elf, and use the elf from the previous map instead.
set_elf(prev_real_map->elf());
}
}
// Cache the elf only after all of the above checks since we might
// discard the original elf we created.
if (Elf::CachingEnabled()) {
Elf::CacheAdd(this);
}
return elf().get();
elf初始化后,如果是非法的elf,那么就需要将elf设置为非法,如果是合法的elf,那么还需要看下其他相邻的内存区域,如果对应的是同一个elf,那么将该合法的elf信息也设置到该内存区域的字段中。
接下来继续看unwind流程:
elf = map_info->GetElf(process_memory_, arch_);
step_pc = regs_->pc();
rel_pc = elf->GetRelPc(step_pc, map_info.get());
先把pc地址转成elf内的偏移,公式也大概可以猜到,就是pc地址减去elf的加载地址再加上偏移:
uint64_t Elf::GetRelPc(uint64_t pc, MapInfo* map_info) {
return pc - map_info->start() + load_bias_ + map_info->elf_offset();
}
对于jit场景,pc地址也是相对elf的地址:
// Everyone except elf data in gdb jit debug maps uses the relative pc.
if (!(map_info->flags() & MAPS_FLAGS_JIT_SYMFILE_MAP)) {
step_pc = rel_pc;
}
接下来就是pc调整:
if (adjust_pc) {
pc_adjustment = GetPcAdjustment(rel_pc, elf, arch_);
} else {
pc_adjustment = 0;
}
step_pc -= pc_adjustment;
参考 rel_pc 进行调整:
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf, ArchEnum arch) {
switch (arch) {
case ARCH_ARM: {
if (!elf->valid()) {
return 2;
}
uint64_t load_bias = elf->GetLoadBias();
if (rel_pc < load_bias) {
if (rel_pc < 2) {
return 0;
}
return 2;
}
uint64_t adjusted_rel_pc = rel_pc - load_bias;
if (adjusted_rel_pc < 5) {
if (adjusted_rel_pc < 2) {
return 0;
}
return 2;
}
if (adjusted_rel_pc & 1) {
// This is a thumb instruction, it could be 2 or 4 bytes.
uint32_t value;
if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
(value & 0xe000f000) != 0xe000f000) {
return 2;
}
}
return 4;
}
case ARCH_ARM64:
case ARCH_RISCV64: {
if (rel_pc < 4) {
return 0;
}
return 4;
}
case ARCH_X86:
case ARCH_X86_64: {
if (rel_pc == 0) {
return 0;
}
return 1;
}
case ARCH_UNKNOWN:
return 0;
}
}
对于ARCH_ARM64,就是调整4个字节。
接下来继续看:
// If the pc is in an invalid elf file, try and get an Elf object
// using the jit debug information.
if (!elf->valid() && jit_debug_ != nullptr && (map_info->flags() & PROT_EXEC)) {
uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
Elf* jit_elf = jit_debug_->Find(maps_, adjusted_jit_pc);
if (jit_elf != nullptr) {
// The jit debug information requires a non relative adjusted pc.
step_pc = adjusted_jit_pc;
elf = jit_elf;
}
}
如果elf非法,那就从jit debug中找,如果找到了,就更新elf,这儿就是java jit优化指令后的场景。
if (!ignore_frame) {
if (regs_->dex_pc() != 0) {
// Add a frame to represent the dex file.
FillInDexFrame();
// Clear the dex pc so that we don't repeat this frame later.
regs_->set_dex_pc(0);
// Make sure there is enough room for the real frame.
if (frames_.size() == max_frames_) {
last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
break;
}
}
frame = FillInFrame(map_info, elf, rel_pc, pc_adjustment);
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
}
如果dex_pc 不是0,那么当前就是java相关的指令,就添加dex frame,然后继续添加frame。先看下FillInDexFrame:
// Inject extra 'virtual' frame that represents the dex pc data.
// The dex pc is a magic register defined in the Mterp interpreter,
// and thus it will be restored/observed in the frame after it.
// Adding the dex frame first here will create something like:
// #7 pc 0015fa20 core.vdex java.util.Arrays.binarySearch+8
// #8 pc 006b1ba1 libartd.so ExecuteMterpImpl+14625
// #9 pc 0039a1ef libartd.so art::interpreter::Execute+719
void Unwinder::FillInDexFrame() {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
uint64_t dex_pc = regs_->dex_pc();
frame->pc = dex_pc;
frame->sp = regs_->sp();
frame->map_info = maps_->Find(dex_pc);
if (frame->map_info != nullptr) {
frame->rel_pc = dex_pc - frame->map_info->start();
// Initialize the load bias for this map so subsequent calls
// to GetLoadBias() will always return data.
frame->map_info->set_load_bias(0);
} else {
frame->rel_pc = dex_pc;
warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
return;
}
if (!resolve_names_) {
return;
}
#if defined(DEXFILE_SUPPORT)
if (dex_files_ == nullptr) {
return;
}
dex_files_->GetFunctionName(maps_, dex_pc, &frame->function_name, &frame->function_offset);
#endif
}
看下如何获取dex file中的函数名:
bool GetFunctionName(Maps* maps, uint64_t pc, SharedString* name, uint64_t* offset) {
// NB: If symfiles overlap in PC ranges, this will check all of them.
return ForEachSymfile(maps, pc, [pc, name, offset](Symfile* file) {
return file->GetFunctionName(pc, name, offset);
});
}
接下来看下 ForEachSymfile:
// Invoke callback for all symfiles that contain the given PC.
// Returns true if any callback returns true (which also aborts the iteration).
template <typename Callback /* (Symfile*) -> bool */>
bool ForEachSymfile(Maps* maps, uint64_t pc, Callback callback) {
// Use a single lock, this object should be used so infrequently that
// a fine grain lock is unnecessary.
std::lock_guard<std::mutex> guard(lock_);
if (descriptor_addr_ == 0) {
FindAndReadVariable(maps, global_variable_name_);
if (descriptor_addr_ == 0) {
return false;
}
}
// Try to find the entry in already loaded symbol files.
for (auto& it : entries_) {
Symfile* symfile = it.second.get();
// Check seqlock to make sure that entry is still valid (it may be very old).
if (symfile->IsValidPc(pc) && CheckSeqlock(it.first) && callback(symfile)) {
return true;
}
}
// Update all entries and retry.
ReadAllEntries(maps);
for (auto& it : entries_) {
Symfile* symfile = it.second.get();
// Note that the entry could become invalid since the ReadAllEntries above,
// but that is ok. We don't want to fail or refresh the entries yet again.
// This is as if we found the entry in time and it became invalid after return.
// This is relevant when ART moves/packs JIT entries. That is, the entry is
// technically deleted, but only because it was copied into merged uber-entry.
// So the JIT method is still alive and the deleted data is still correct.
if (symfile->IsValidPc(pc) && callback(symfile)) {
return true;
}
}
return false;
}
这时候就是从内存中按照pc地址查找函数名:
void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
std::string variable(var_str);
// When looking for global variables, do not arbitrarily search every
// readable map. Instead look for a specific pattern that must exist.
// The pattern should be a readable map, followed by a read-write
// map with a non-zero offset.
// For example:
// f0000-f1000 0 r-- /system/lib/libc.so
// f1000-f2000 1000 r-x /system/lib/libc.so
// f2000-f3000 2000 rw- /system/lib/libc.so
// This also works:
// f0000-f2000 0 r-- /system/lib/libc.so
// f2000-f3000 2000 rw- /system/lib/libc.so
// It is also possible to see empty maps after the read-only like so:
// f0000-f1000 0 r-- /system/lib/libc.so
// f1000-f2000 0 ---
// f2000-f3000 1000 r-x /system/lib/libc.so
// f3000-f4000 2000 rw- /system/lib/libc.so
MapInfo* map_zero = nullptr;
for (const auto& info : *maps) {
if ((info->flags() & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
map_zero != nullptr && Searchable(info->name()) && info->name() == map_zero->name()) {
Elf* elf = map_zero->GetElf(memory_, arch());
uint64_t ptr;
if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
uint64_t offset_end = info->offset() + info->end() - info->start();
if (ptr >= info->offset() && ptr < offset_end) {
ptr = info->start() + ptr - info->offset();
if (ReadVariableData(ptr)) {
break;
}
}
}
} else if (info->offset() == 0 && !info->name().empty()) {
map_zero = info.get();
}
}
}
这儿找的变量就是“__dex_debug_descriptor”,把该变量地址偏移等都获取到。
回到ForEachSymfile,接下来会遍历entries:
// Try to find the entry in already loaded symbol files.
for (auto& it : entries_) {
Symfile* symfile = it.second.get();
// Check seqlock to make sure that entry is still valid (it may be very old).
if (symfile->IsValidPc(pc) && CheckSeqlock(it.first) && callback(symfile)) {
return true;
}
}
首次读entries_会是空,接下来就是利用ReadAllEntries读取符号,下面方法会获取到JIT 中包含的符号地址信息:
// Read all JIT entries while assuming there might be concurrent modifications.
// If there is a race, the method will fail and the caller should retry the call.
bool ReadAllEntries(Maps* maps, bool* race) {
// New entries might be added while we iterate over the linked list.
// In particular, an entry could be effectively moved from end to start due to
// the ART repacking algorithm, which groups smaller entries into a big one.
// Therefore keep reading the most recent entries until we reach a fixed point.
std::map<UID, std::shared_ptr<Symfile>> entries;
for (size_t i = 0; i < kMaxHeadRetries; i++) {
size_t old_size = entries.size();
if (!ReadNewEntries(maps, &entries, race)) {
return false;
}
if (entries.size() == old_size) {
entries_.swap(entries);
return true;
}
}
return false; // Too many retries.
}
在读取的时候会反复读,指导读取到的entries数量不再变化:
// Read new JIT entries (head of linked list) until we find one that we have seen before.
// This method uses seqlocks extensively to ensure safety in case of concurrent modifications.
bool ReadNewEntries(Maps* maps, std::map<UID, std::shared_ptr<Symfile>>* entries, bool* race) {
// Read the address of the head entry in the linked list.
UID uid;
if (!ReadNextField(descriptor_addr_ + offsetof(JITDescriptor, first_entry), &uid, race)) {
return false;
}
// Follow the linked list.
while (uid.address != 0) {
// Check if we have reached an already cached entry (we restart from head repeatedly).
if (entries->count(uid) != 0) {
return true;
}
// Read the entry.
JITCodeEntry data{};
if (!memory_->ReadFully(uid.address, &data, jit_entry_size_)) {
return false;
}
data.symfile_addr = StripAddressTag(data.symfile_addr);
// Check the seqlock to verify the symfile_addr and symfile_size.
if (!CheckSeqlock(uid, race)) {
return false;
}
// Copy and load the symfile.
auto it = entries_.find(uid);
if (it != entries_.end()) {
// The symfile was already loaded - just copy the reference.
entries->emplace(uid, it->second);
} else if (data.symfile_addr != 0) {
std::shared_ptr<Symfile> symfile;
bool ok = this->Load(maps, memory_, data.symfile_addr, data.symfile_size.value, symfile);
// Check seqlock first because load can fail due to race (so we want to trigger retry).
// TODO: Extract the memory copy code before the load, so that it is immune to races.
if (!CheckSeqlock(uid, race)) {
return false; // The ELF/DEX data was removed before we loaded it.
}
// Exclude symbol files that fail to load (but continue loading other files).
if (ok) {
entries->emplace(uid, symfile);
}
}
// Go to next entry.
UID next_uid;
if (!ReadNextField(uid.address + offsetof(JITCodeEntry, next), &next_uid, race)) {
return false; // The next pointer was modified while we were reading it.
}
if (!CheckSeqlock(uid, race)) {
return false; // This entry was deleted before we moved to the next one.
}
uid = next_uid;
}
return true;
}
接下来继续从entries中读取:
for (auto& it : entries_) {
Symfile* symfile = it.second.get();
// Note that the entry could become invalid since the ReadAllEntries above,
// but that is ok. We don't want to fail or refresh the entries yet again.
// This is as if we found the entry in time and it became invalid after return.
// This is relevant when ART moves/packs JIT entries. That is, the entry is
// technically deleted, but only because it was copied into merged uber-entry.
// So the JIT method is still alive and the deleted data is still correct.
if (symfile->IsValidPc(pc) && callback(symfile)) {
return true;
}
}
再回到FillInDexFrame,此时函数名也拿到了。
接下来再看下FillInFrame:
FrameData* Unwinder::FillInFrame(std::shared_ptr<MapInfo>& map_info, Elf* /*elf*/, uint64_t rel_pc,
uint64_t pc_adjustment) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
frame->sp = regs_->sp();
frame->rel_pc = rel_pc - pc_adjustment;
frame->pc = regs_->pc() - pc_adjustment;
if (map_info == nullptr) {
// Nothing else to update.
return nullptr;
}
frame->map_info = map_info;
return frame;
}
这时候就又添加了一个调用栈帧。
接下来继续看下:
if (map_info->flags() & MAPS_FLAGS_DEVICE_MAP) {
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
// some of the speculative frames.
in_device_map = true;
} else {
auto sp_info = maps_->Find(regs_->sp());
if (sp_info != nullptr && sp_info->flags() & MAPS_FLAGS_DEVICE_MAP) {
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
// some of the speculative frames.
in_device_map = true;
} else {
bool is_signal_frame = false;
if (elf->StepIfSignalHandler(rel_pc, regs_, process_memory_.get())) {
stepped = true;
is_signal_frame = true;
} else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished,
&is_signal_frame)) {
stepped = true;
}
if (is_signal_frame && frame != nullptr) {
// Need to adjust the relative pc because the signal handler
// pc should not be adjusted.
frame->rel_pc = rel_pc;
frame->pc += pc_adjustment;
step_pc = rel_pc;
}
elf->GetLastError(&last_error_);
}
}
这儿就是进一步往上回栈,找调用方,有两种case,一种是信号处理函数:
// The relative pc expectd by this function is relative to the start of the elf.
bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
if (!valid_) {
return false;
}
// Convert the rel_pc to an elf_offset.
if (rel_pc < static_cast<uint64_t>(load_bias_)) {
return false;
}
return regs->StepIfSignalHandler(rel_pc - load_bias_, this, process_memory);
}
继续看下StepIfSignalHandler:
bool RegsArm64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
uint64_t data;
Memory* elf_memory = elf->memory();
// Read from elf memory since it is usually more expensive to read from
// process memory.
if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
return false;
}
// Look for the kernel sigreturn function.
// __kernel_rt_sigreturn:
// 0xd2801168 mov x8, #0x8b
// 0xd4000001 svc #0x0
if (data != 0xd4000001d2801168ULL) {
return false;
}
// SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
if (!process_memory->ReadFully(regs_[ARM64_REG_SP] + 0x80 + 0xb0 + 0x08, regs_.data(),
sizeof(uint64_t) * ARM64_REG_LAST)) {
return false;
}
return true;
}
如果是信号处理函数,就按照处理函数的格式读取内存数据。
// The relative pc is always relative to the start of the map from which it comes.
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
if (!valid_) {
return false;
}
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
return interface_->Step(rel_pc, regs, process_memory, finished, is_signal_frame);
}
从前面的信息我们知道记录函数回栈信息的几个结构,一个是.debug_frame 对应的debug_frame_, 一个是.eh_frame 的eh_frame_, 一个是gnu_debugdata_interface_,接下来就是从这三个结构中进行递推,继续看下:
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
last_error_.code = ERROR_NONE;
last_error_.address = 0;
// Try the debug_frame first since it contains the most specific unwind
// information.
DwarfSection* debug_frame = debug_frame_.get();
if (debug_frame != nullptr &&
debug_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
return true;
}
// Try the eh_frame next.
DwarfSection* eh_frame = eh_frame_.get();
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
return true;
}
if (gnu_debugdata_interface_ != nullptr &&
gnu_debugdata_interface_->Step(pc, regs, process_memory, finished, is_signal_frame)) {
return true;
}
// Set the error code based on the first error encountered.
DwarfSection* section = nullptr;
if (debug_frame_ != nullptr) {
section = debug_frame_.get();
} else if (eh_frame_ != nullptr) {
section = eh_frame_.get();
} else if (gnu_debugdata_interface_ != nullptr) {
last_error_ = gnu_debugdata_interface_->last_error();
return false;
} else {
return false;
}
// Convert the DWARF ERROR to an external error.
DwarfErrorCode code = section->LastErrorCode();
switch (code) {
case DWARF_ERROR_NONE:
last_error_.code = ERROR_NONE;
break;
case DWARF_ERROR_MEMORY_INVALID:
last_error_.code = ERROR_MEMORY_INVALID;
last_error_.address = section->LastErrorAddress();
break;
case DWARF_ERROR_ILLEGAL_VALUE:
case DWARF_ERROR_ILLEGAL_STATE:
case DWARF_ERROR_STACK_INDEX_NOT_VALID:
case DWARF_ERROR_TOO_MANY_ITERATIONS:
case DWARF_ERROR_CFA_NOT_DEFINED:
case DWARF_ERROR_NO_FDES:
last_error_.code = ERROR_UNWIND_INFO;
break;
case DWARF_ERROR_NOT_IMPLEMENTED:
case DWARF_ERROR_UNSUPPORTED_VERSION:
last_error_.code = ERROR_UNSUPPORTED;
break;
}
return false;
}
我们看下DwarfSection的递推:
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
// Lookup the pc in the cache.
auto it = loc_regs_.upper_bound(pc);
if (it == loc_regs_.end() || pc < it->second.pc_start) {
last_error_.code = DWARF_ERROR_NONE;
const DwarfFde* fde = GetFdeFromPc(pc);
if (fde == nullptr || fde->cie == nullptr) {
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
// Now get the location information for this pc.
DwarfLocations loc_regs;
if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
return false;
}
loc_regs.cie = fde->cie;
// Store it in the cache.
it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
}
*is_signal_frame = it->second.cie->is_signal_frame;
// Now eval the actual registers.
return Eval(it->second.cie, process_memory, it->second, regs, finished);
}
这儿就是解析Dwarf信息,利用pc地址推算函数以及上一层函数。
先按照pc地址计算获取FDE, 先看下FDE的结构:
struct DwarfCie {
uint8_t version = 0;
uint8_t fde_address_encoding = 0;
uint8_t lsda_encoding = 0;
uint8_t segment_size = 0;
std::vector<char> augmentation_string;
uint64_t personality_handler = 0;
uint64_t cfa_instructions_offset = 0;
uint64_t cfa_instructions_end = 0;
uint64_t code_alignment_factor = 0;
int64_t data_alignment_factor = 0;
uint64_t return_address_register = 0;
bool is_signal_frame = false;
};
struct DwarfFde {
uint64_t cie_offset = 0;
uint64_t cfa_instructions_offset = 0;
uint64_t cfa_instructions_end = 0;
uint64_t pc_start = 0;
uint64_t pc_end = 0;
uint64_t lsda_address = 0;
const DwarfCie* cie = nullptr;
};
详细结构可以查看Exception Frames
template <typename AddressType>
const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromPc(uint64_t pc) {
// Ensure that the binary search table is initialized.
if (fde_index_.empty()) {
BuildFdeIndex();
}
// Find the FDE offset in the binary search table.
auto comp = [](uint64_t pc, auto& entry) { return pc < entry.first; };
auto it = std::upper_bound(fde_index_.begin(), fde_index_.end(), pc, comp);
if (it == fde_index_.end()) {
return nullptr;
}
// Load the full FDE entry based on the offset.
const DwarfFde* fde = GetFdeFromOffset(/*fde_offset=*/it->second);
return fde != nullptr && fde->pc_start <= pc ? fde : nullptr;
}
接下来看下如何读取的FDE:
// Create binary search table to make FDE lookups fast (sorted by pc_end).
// We store only the FDE offset rather than the full entry to save memory.
//
// If there are overlapping entries, it inserts additional entries to ensure
// that one of the overlapping entries is found (it is undefined which one).
template <typename AddressType>
void DwarfSectionImpl<AddressType>::BuildFdeIndex() {
struct FdeInfo {
uint64_t pc_start, pc_end, fde_offset;
};
std::vector<FdeInfo> fdes;
for (uint64_t offset = entries_offset_; offset < entries_end_;) {
const uint64_t initial_offset = offset;
std::optional<DwarfFde> fde;
if (!GetNextCieOrFde(offset, fde)) {
break;
}
if (fde.has_value() && /* defensive check */ (fde->pc_start < fde->pc_end)) {
fdes.push_back({fde->pc_start, fde->pc_end, initial_offset});
}
if (offset <= initial_offset) {
break; // Jump back. Simply consider the processing done in this case.
}
}
std::sort(fdes.begin(), fdes.end(), [](const FdeInfo& a, const FdeInfo& b) {
return std::tie(a.pc_end, a.fde_offset) < std::tie(b.pc_end, b.fde_offset);
});
// If there are overlapping entries, ensure that we can always find one of them.
// For example, for entries: [300, 350) [400, 450) [100, 550) [600, 650)
// We add the following: [100, 300) [100, 400)
// Which ensures that the [100, 550) entry can be found in its whole range.
if (!fdes.empty()) {
FdeInfo filling = fdes.back(); // Entry with the minimal pc_start seen so far.
for (ssize_t i = fdes.size() - 1; i >= 0; i--) { // Iterate backwards.
uint64_t prev_pc_end = (i > 0) ? fdes[i - 1].pc_end : 0;
// If there is a gap between entries and the filling reaches the gap, fill it.
if (prev_pc_end < fdes[i].pc_start && filling.pc_start < fdes[i].pc_start) {
fdes.push_back({filling.pc_start, fdes[i].pc_start, filling.fde_offset});
}
if (fdes[i].pc_start < filling.pc_start) {
filling = fdes[i];
}
}
}
// Copy data to the final binary search table (pc_end, fde_offset) and sort it.
fde_index_.reserve(fdes.size());
for (const FdeInfo& it : fdes) {
fde_index_.emplace_back(it.pc_end, it.fde_offset);
}
if (!std::is_sorted(fde_index_.begin(), fde_index_.end())) {
std::sort(fde_index_.begin(), fde_index_.end());
}
}
将FDE按照pc地址进行排序。
接下来看下如何读取单个FDE:
// Read CIE or FDE entry at the given offset, and set the offset to the following entry.
// The 'fde' argument is set only if we have seen an FDE entry.
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::GetNextCieOrFde(uint64_t& next_entries_offset,
std::optional<DwarfFde>& fde_entry) {
const uint64_t start_offset = next_entries_offset;
memory_.set_data_offset(entries_offset_);
memory_.set_cur_offset(next_entries_offset);
uint32_t value32;
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
uint64_t cie_offset;
uint8_t cie_fde_encoding;
bool entry_is_cie = false;
if (value32 == static_cast<uint32_t>(-1)) {
// 64 bit entry.
uint64_t value64;
if (!memory_.ReadBytes(&value64, sizeof(value64))) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
next_entries_offset = memory_.cur_offset() + value64;
// Read the Cie Id of a Cie or the pointer of the Fde.
if (!memory_.ReadBytes(&value64, sizeof(value64))) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
if (value64 == cie64_value_) {
entry_is_cie = true;
cie_fde_encoding = DW_EH_PE_udata8;
} else {
cie_offset = GetCieOffsetFromFde64(value64);
}
} else {
next_entries_offset = memory_.cur_offset() + value32;
// 32 bit Cie
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
if (value32 == cie32_value_) {
entry_is_cie = true;
cie_fde_encoding = DW_EH_PE_udata4;
} else {
cie_offset = GetCieOffsetFromFde32(value32);
}
}
if (entry_is_cie) {
auto entry = cie_entries_.find(start_offset);
if (entry == cie_entries_.end()) {
DwarfCie* cie = &cie_entries_[start_offset];
cie->lsda_encoding = DW_EH_PE_omit;
cie->cfa_instructions_end = next_entries_offset;
cie->fde_address_encoding = cie_fde_encoding;
if (!FillInCie(cie)) {
cie_entries_.erase(start_offset);
return false;
}
}
fde_entry.reset();
} else {
fde_entry = DwarfFde{};
fde_entry->cfa_instructions_end = next_entries_offset;
fde_entry->cie_offset = cie_offset;
if (!FillInFde(&*fde_entry)) {
return false;
}
}
return true;
}
接下来就是利用FDE获取地址信息:
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
DwarfLocations* loc_regs, ArchEnum arch) {
DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Look for the cached copy of the cie data.
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
if (reg_entry == cie_loc_regs_.end()) {
if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
loc_regs)) {
last_error_ = cfa.last_error();
return false;
}
cie_loc_regs_[fde->cie_offset] = *loc_regs;
}
cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
last_error_ = cfa.last_error();
return false;
}
return true;
}
最后更新寄存器:
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
const DwarfLocations& loc_regs, Regs* regs,
bool* finished) {
RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
if (cie->return_address_register >= cur_regs->total_regs()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
// Get the cfa value;
auto cfa_entry = loc_regs.find(CFA_REG);
if (cfa_entry == loc_regs.end()) {
last_error_.code = DWARF_ERROR_CFA_NOT_DEFINED;
return false;
}
// Always set the dex pc to zero when evaluating.
cur_regs->set_dex_pc(0);
// Reset necessary pseudo registers before evaluation.
// This is needed for ARM64, for example.
regs->ResetPseudoRegisters();
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
.cie = cie,
.regular_memory = regular_memory,
.regs_info = RegsInfo<AddressType>(cur_regs)};
const DwarfLocation* loc = &cfa_entry->second;
// Only a few location types are valid for the cfa.
switch (loc->type) {
case DWARF_LOCATION_REGISTER:
if (loc->values[0] >= cur_regs->total_regs()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
eval_info.cfa = (*cur_regs)[loc->values[0]];
eval_info.cfa += loc->values[1];
break;
case DWARF_LOCATION_VAL_EXPRESSION: {
AddressType value;
if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
return false;
}
// There is only one type of valid expression for CFA evaluation.
eval_info.cfa = value;
break;
}
default:
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
for (const auto& entry : loc_regs) {
uint32_t reg = entry.first;
// Already handled the CFA register.
if (reg == CFA_REG) continue;
AddressType* reg_ptr;
if (reg >= cur_regs->total_regs()) {
if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
// Skip this unknown register.
continue;
}
if (!eval_info.regs_info.regs->SetPseudoRegister(reg, entry.second.values[0])) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
} else {
reg_ptr = eval_info.regs_info.Save(reg);
if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
return false;
}
}
}
// Find the return address location.
if (eval_info.return_address_undefined) {
cur_regs->set_pc(0);
} else {
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
}
// If the pc was set to zero, consider this the final frame. Exception: if
// this is the sigreturn frame, then we want to try to recover the real PC
// using the return address (from LR or the stack), so keep going.
*finished = (cur_regs->pc() == 0 && !cie->is_signal_frame) ? true : false;
cur_regs->set_sp(eval_info.cfa);
return true;
}
更新CFA地址,这样就可以找到上一层的函数,并且也恢复相关的寄存器。同时也会更新pc地址,这样就可以找到上一层函数中调用该函数的位置。
也会更新相应的其他寄存器:
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint32_t reg,
AddressType* reg_ptr, void* info) {
EvalInfo<AddressType>* eval_info = reinterpret_cast<EvalInfo<AddressType>*>(info);
Memory* regular_memory = eval_info->regular_memory;
switch (loc->type) {
case DWARF_LOCATION_OFFSET:
if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = eval_info->cfa + loc->values[0];
return false;
}
break;
case DWARF_LOCATION_VAL_OFFSET:
*reg_ptr = eval_info->cfa + loc->values[0];
break;
case DWARF_LOCATION_REGISTER: {
uint32_t cur_reg = loc->values[0];
if (cur_reg >= eval_info->regs_info.Total()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
*reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
break;
}
case DWARF_LOCATION_EXPRESSION:
case DWARF_LOCATION_VAL_EXPRESSION: {
AddressType value;
bool is_dex_pc = false;
if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
return false;
}
if (loc->type == DWARF_LOCATION_EXPRESSION) {
if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = value;
return false;
}
} else {
*reg_ptr = value;
if (is_dex_pc) {
eval_info->regs_info.regs->set_dex_pc(value);
}
}
break;
}
case DWARF_LOCATION_UNDEFINED:
if (reg == eval_info->cie->return_address_register) {
eval_info->return_address_undefined = true;
}
break;
case DWARF_LOCATION_PSEUDO_REGISTER:
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
default:
break;
}
return true;
}
继续会到Unwind流程:
if (frame != nullptr) {
if (!resolve_names_ ||
!elf->GetFunctionName(step_pc, &frame->function_name, &frame->function_offset)) {
frame->function_name = "";
frame->function_offset = 0;
}
}
获取函数名字,到了这儿,我们的栈帧中就可以看到函数名,偏移等信息了。
if (return_address_attempt) {
// Only remove the speculative frame if there are more than two frames
// or the pc in the first frame is in a valid map.
// This allows for a case where the code jumps into the middle of
// nowhere, but there is no other unwind information after that.
if (frames_.size() > 2 || (frames_.size() > 0 && maps_->Find(frames_[0].pc) != nullptr)) {
// Remove the speculative frame.
frames_.pop_back();
}
break;
} else if (in_device_map) {
// Do not attempt any other unwinding, pc or sp is in a device
// map.
break;
} else {
// Steping didn't work, try this secondary method.
if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
break;
}
return_address_attempt = true;
}
这儿如果用FDE无法回栈,也会尝试用返回地址,也就是lr,这个也就是我们耳熟能详的利用栈中的返回地址回栈。
return_address_attempt = false;
if (max_frames_ == frames_.size()) {
last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
}
如果栈帧的数量达到限制了,就可以结束了。
// If the pc and sp didn't change, then consider everything stopped.
if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
last_error_.code = ERROR_REPEATED_FRAME;
break;
}
如果PC地址不变了,也就不需要继续了,这时候回栈结束。
再继续会到UnwindWithSignal, 如果有印象的话还会记得我们的目标线程现在阻塞中等着我们唤醒:
void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid, std::unique_ptr<Regs>* initial_regs,
const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
ClearErrors();
if (tid == static_cast<pid_t>(android::base::GetThreadId())) {
last_error_.code = ERROR_UNSUPPORTED;
return;
}
if (!Init()) {
return;
}
ThreadEntry* entry = SendSignalToThread(signal, tid);
if (entry == nullptr) {
return;
}
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), entry->GetUcontext()));
if (initial_regs != nullptr) {
initial_regs->reset(regs->Clone());
}
SetRegs(regs.get());
UnwinderFromPid::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
// Tell the signal handler to exit and release the entry.
entry->Wake();
// Wait for the thread to indicate it is done with the ThreadEntry.
// If this fails, the Wait command will log an error message.
entry->Wait(WAIT_FOR_THREAD_TO_RESTART);
ThreadEntry::Remove(entry);
}
这么长的一个过程,终于把Unwind结束了,接下来就Wake下目标线程。
目标线程唤醒后的操作如下:
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
android::base::ErrnoRestorer restore;
ThreadEntry* entry = ThreadEntry::Get(android::base::GetThreadId(), false);
if (!entry) {
return;
}
entry->CopyUcontextFromSigcontext(sigcontext);
// Indicate the ucontext is now valid.
entry->Wake();
// Pause the thread until the unwind is complete. This avoids having
// the thread run ahead causing problems.
// The number indicates that we are waiting for the second Wake() call
// overall which is made by the thread requesting an unwind.
if (entry->Wait(WAIT_FOR_UNWIND_TO_COMPLETE)) {
// Do not remove the entry here because that can result in a deadlock
// if the code cannot properly send a signal to the thread under test.
entry->Wake();
}
// If the wait fails, the entry might have been freed, so only exit.
}
唤醒后就会再来一个Wake,这时候就会再把回栈的线程唤醒。回栈线程唤醒后就可以把对应的entry删除了。因此总结下unwind的交互流程:
- 回栈线程先发送信号给目标线程,此时回栈线程Wait;
- 目标线程收到信号后,唤醒回栈线程,然后自己进入Wait
- 回栈线程开始回栈,回栈完后,唤醒目标线程,自己进入Wait
- 目标线程被唤醒后,唤醒回栈线程,自己退出
- 回栈线程被唤醒后,整个流程结束。
是不是有点像网络协议?这就是计算机的思维,到处都通用。
此时Unwind流程就结束了,可能有人还有疑问,unwind后,获取到的是frame,如何能呈现出和tombstone那样的结构呢?那就简单了,通过FormatFrame:
std::string Unwinder::FormatFrame(ArchEnum arch, const FrameData& frame, bool display_build_id) {
std::string data;
if (ArchIs32Bit(arch)) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
}
auto map_info = frame.map_info;
if (map_info == nullptr) {
// No valid map associated with this frame.
data += " <unknown>";
} else if (!map_info->name().empty()) {
data += " ";
data += map_info->GetFullName();
} else {
data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", map_info->start());
}
if (map_info != nullptr && map_info->elf_start_offset() != 0) {
data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", map_info->elf_start_offset());
}
if (!frame.function_name.empty()) {
data += " (" + DemangleNameIfNeeded(frame.function_name);
if (frame.function_offset != 0) {
data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
}
data += ')';
}
if (map_info != nullptr && display_build_id) {
std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
data += " (BuildId: " + build_id + ')';
}
}
return data;
}
如何反混淆名字呢?看下如下方法:
std::string DemangleNameIfNeeded(const std::string& name) {
if (name.length() < 2 || name[0] != '_') {
return name;
}
char* demangled_str = nullptr;
if (name[1] == 'Z') {
// Try to demangle C++ name.
demangled_str = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
} else if (name[1] == 'R') {
// Try to demangle rust name.
demangled_str = rustc_demangle(name.c_str(), nullptr, nullptr, nullptr);
}
if (demangled_str == nullptr) {
return name;
}
std::string demangled_name(demangled_str);
free(demangled_str);
return demangled_name;
}
到了这儿就整个Unwind流程就梳理完了,那真的梳理完了么?从Unwind的流程看,是梳理完了,整个流程还挺有意思。不过还可以继续梳理,比如和unwind相关的知识可以继续拓展,由点到线再到面,从知识中收获理解,从理解中提升认知,从认知中认清自己,认清自己就达到了目的,这就是我的结论。