Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b8f5459 authored by Ryan Prichard's avatar Ryan Prichard Committed by Christopher Ferris
Browse files

libunwindstack: Support signal frame CIEs.

Mark a CIE with a S in its augmentation string as signal frame.
This allows the code to properly handle signal frame data if none
of the signal frame pattern matchers work.

For a signal frame, DwarfSectionImpl<AddressType>::Eval needs to
continue the unwinding even if PC is zero. A zero PC means that the
program has crashed, and we should try to recover the real PC using the
return address on the stack or LR. This behavior is tested by
UnwindOffline.signal_{x86,x86_64}, which modify the libc.so files
so that the signal frame pattern matcher fails and the CIE/FDE
data is used instead.

Test: libunwindstack_test
Change-Id: I4655b070028fd984345311a5e743796f8c30ed36
parent 483364a7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -296,6 +296,8 @@ cc_defaults {
        "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
        "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
        "tests/files/offline/signal_load_bias_arm/*",
        "tests/files/offline/signal_fde_x86/*",
        "tests/files/offline/signal_fde_x86_64/*",
        "tests/files/offline/straddle_arm/*",
        "tests/files/offline/straddle_arm64/*",
    ],
+11 −3
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ namespace unwindstack {

DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}

bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
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) {
@@ -59,6 +60,8 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f
    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);
}
@@ -241,6 +244,9 @@ bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
          return false;
        }
        break;
      case 'S':
        cie->is_signal_frame = true;
        break;
    }
  }
  return true;
@@ -558,8 +564,10 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
  }

  // If the pc was set to zero, consider this the final frame.
  *finished = (cur_regs->pc() == 0) ? true : false;
  // 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);

+3 −2
Original line number Diff line number Diff line
@@ -188,14 +188,15 @@ bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memor
}

// 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 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);
  return interface_->Step(rel_pc, regs, process_memory, finished, is_signal_frame);
}

bool Elf::IsValidElf(Memory* memory) {
+6 −4
Original line number Diff line number Diff line
@@ -499,25 +499,27 @@ bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64
  return false;
}

bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
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)) {
  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)) {
  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)) {
      gnu_debugdata_interface_->Step(pc, regs, process_memory, finished, is_signal_frame)) {
    return true;
  }

+3 −2
Original line number Diff line number Diff line
@@ -100,12 +100,13 @@ void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint6
  total_entries_ = ph_filesz / 8;
}

bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
                           bool* is_signal_frame) {
  // Dwarf unwind information is precise about whether a pc is covered or not,
  // but arm unwind information only has ranges of pc. In order to avoid
  // incorrectly doing a bad unwind using arm unwind information for a
  // different function, always try and unwind with the dwarf information first.
  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
  return ElfInterface32::Step(pc, regs, process_memory, finished, is_signal_frame) ||
         StepExidx(pc, regs, process_memory, finished);
}

Loading