Android libunwind 源码解读(2)

本篇介绍

本篇接着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的交互流程:

  1. 回栈线程先发送信号给目标线程,此时回栈线程Wait;
  2. 目标线程收到信号后,唤醒回栈线程,然后自己进入Wait
  3. 回栈线程开始回栈,回栈完后,唤醒目标线程,自己进入Wait
  4. 目标线程被唤醒后,唤醒回栈线程,自己退出
  5. 回栈线程被唤醒后,整个流程结束。

是不是有点像网络协议?这就是计算机的思维,到处都通用。
此时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相关的知识可以继续拓展,由点到线再到面,从知识中收获理解,从理解中提升认知,从认知中认清自己,认清自己就达到了目的,这就是我的结论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容