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

Commit b1dc1553 authored by Christopher Ferris's avatar Christopher Ferris Committed by android-build-merger
Browse files

Merge "Add EH frame and debug frame support."

am: 27fea2c2

Change-Id: Id28a8f7c2fc235ec51f941c9db6260f74d5bd1ed
parents 0329b8d0 27fea2c2
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -48,6 +48,8 @@ cc_defaults {
    srcs: [
    srcs: [
        "ArmExidx.cpp",
        "ArmExidx.cpp",
        "DwarfCfa.cpp",
        "DwarfCfa.cpp",
        "DwarfDebugFrame.cpp",
        "DwarfEhFrame.cpp",
        "DwarfMemory.cpp",
        "DwarfMemory.cpp",
        "DwarfOp.cpp",
        "DwarfOp.cpp",
        "DwarfSection.cpp",
        "DwarfSection.cpp",
@@ -96,6 +98,8 @@ cc_defaults {
        "tests/ArmExidxExtractTest.cpp",
        "tests/ArmExidxExtractTest.cpp",
        "tests/DwarfCfaLogTest.cpp",
        "tests/DwarfCfaLogTest.cpp",
        "tests/DwarfCfaTest.cpp",
        "tests/DwarfCfaTest.cpp",
        "tests/DwarfDebugFrameTest.cpp",
        "tests/DwarfEhFrameTest.cpp",
        "tests/DwarfMemoryTest.cpp",
        "tests/DwarfMemoryTest.cpp",
        "tests/DwarfOpLogTest.cpp",
        "tests/DwarfOpLogTest.cpp",
        "tests/DwarfOpTest.cpp",
        "tests/DwarfOpTest.cpp",
+311 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdint.h>
#include <stdlib.h>

#include <algorithm>

#include "DwarfDebugFrame.h"
#include "DwarfMemory.h"
#include "DwarfSection.h"
#include "DwarfStructs.h"
#include "Memory.h"

template <typename AddressType>
bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
  offset_ = offset;
  end_offset_ = offset + size;

  memory_.clear_func_offset();
  memory_.clear_text_offset();
  memory_.set_data_offset(offset);
  memory_.set_cur_offset(offset);

  return CreateSortedFdeList();
}

template <typename AddressType>
bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
  uint8_t version;
  if (!memory_.ReadBytes(&version, 1)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    return false;
  }
  // Read the augmentation string.
  std::vector<char> aug_string;
  char aug_value;
  bool get_encoding = false;
  do {
    if (!memory_.ReadBytes(&aug_value, 1)) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }
    if (aug_value == 'R') {
      get_encoding = true;
    }
    aug_string.push_back(aug_value);
  } while (aug_value != '\0');

  if (version == 4) {
    // Skip the Address Size field.
    memory_.set_cur_offset(memory_.cur_offset() + 1);

    // Read the segment size.
    if (!memory_.ReadBytes(segment_size, 1)) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }
  } else {
    *segment_size = 0;
  }

  if (aug_string[0] != 'z' || !get_encoding) {
    // No encoding
    return true;
  }

  // Skip code alignment factor
  uint8_t value;
  do {
    if (!memory_.ReadBytes(&value, 1)) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }
  } while (value & 0x80);

  // Skip data alignment factor
  do {
    if (!memory_.ReadBytes(&value, 1)) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }
  } while (value & 0x80);

  if (version == 1) {
    // Skip return address register.
    memory_.set_cur_offset(memory_.cur_offset() + 1);
  } else {
    // Skip return address register.
    do {
      if (!memory_.ReadBytes(&value, 1)) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }
    } while (value & 0x80);
  }

  // Skip the augmentation length.
  do {
    if (!memory_.ReadBytes(&value, 1)) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }
  } while (value & 0x80);

  for (size_t i = 1; i < aug_string.size(); i++) {
    if (aug_string[i] == 'R') {
      if (!memory_.ReadBytes(encoding, 1)) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }
      // Got the encoding, that's all we are looking for.
      return true;
    } else if (aug_string[i] == 'L') {
      memory_.set_cur_offset(memory_.cur_offset() + 1);
    } else if (aug_string[i] == 'P') {
      uint8_t encoding;
      if (!memory_.ReadBytes(&encoding, 1)) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }
      uint64_t value;
      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }
    }
  }

  // It should be impossible to get here.
  abort();
}

template <typename AddressType>
bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
                                              uint8_t encoding) {
  if (segment_size != 0) {
    memory_.set_cur_offset(memory_.cur_offset() + 1);
  }

  uint64_t start;
  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    return false;
  }

  uint64_t length;
  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    return false;
  }
  if (length != 0) {
    fdes_.emplace_back(entry_offset, start, length);
  }

  return true;
}

template <typename AddressType>
bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
  memory_.set_cur_offset(offset_);

  // Loop through all of the entries and read just enough to create
  // a sorted list of pcs.
  // This code assumes that first comes the cie, then the fdes that
  // it applies to.
  uint64_t cie_offset = 0;
  uint8_t address_encoding;
  uint8_t segment_size;
  while (memory_.cur_offset() < end_offset_) {
    uint64_t cur_entry_offset = memory_.cur_offset();

    // Figure out the entry length and type.
    uint32_t value32;
    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }

    uint64_t next_entry_offset;
    if (value32 == static_cast<uint32_t>(-1)) {
      uint64_t value64;
      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }
      next_entry_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_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }

      if (value64 == static_cast<uint64_t>(-1)) {
        // Cie 64 bit
        address_encoding = DW_EH_PE_sdata8;
        if (!GetCieInfo(&segment_size, &address_encoding)) {
          return false;
        }
        cie_offset = cur_entry_offset;
      } else {
        if (offset_ + value64 != cie_offset) {
          // This means that this Fde is not following the Cie.
          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
          return false;
        }

        // Fde 64 bit
        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
          return false;
        }
      }
    } else {
      next_entry_offset = memory_.cur_offset() + value32;

      // Read the Cie Id of a Cie or the pointer of the Fde.
      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }

      if (value32 == static_cast<uint32_t>(-1)) {
        // Cie 32 bit
        address_encoding = DW_EH_PE_sdata4;
        if (!GetCieInfo(&segment_size, &address_encoding)) {
          return false;
        }
        cie_offset = cur_entry_offset;
      } else {
        if (offset_ + value32 != cie_offset) {
          // This means that this Fde is not following the Cie.
          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
          return false;
        }

        // Fde 32 bit
        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
          return false;
        }
      }
    }

    if (next_entry_offset < memory_.cur_offset()) {
      // This indicates some kind of corruption, or malformed section data.
      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
      return false;
    }
    memory_.set_cur_offset(next_entry_offset);
  }

  // Sort the entries.
  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
    if (a.start == b.start) return a.end < b.end;
    return a.start < b.start;
  });

  fde_count_ = fdes_.size();

  return true;
}

template <typename AddressType>
bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
  if (fde_count_ == 0) {
    return false;
  }

  size_t first = 0;
  size_t last = fde_count_;
  while (first < last) {
    size_t current = (first + last) / 2;
    const FdeInfo* info = &fdes_[current];
    if (pc >= info->start && pc <= info->end) {
      *fde_offset = info->offset;
      return true;
    }

    if (pc < info->start) {
      last = current;
    } else {
      first = current + 1;
    }
  }
  return false;
}

template <typename AddressType>
const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
  if (index >= fdes_.size()) {
    return nullptr;
  }
  return this->GetFdeFromOffset(fdes_[index].offset);
}

// Explicitly instantiate DwarfDebugFrame.
template class DwarfDebugFrame<uint32_t>;
template class DwarfDebugFrame<uint64_t>;
+76 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H

#include <stdint.h>

#include <vector>

#include "DwarfSection.h"

template <typename AddressType>
class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
 public:
  // Add these so that the protected members of DwarfSectionImpl
  // can be accessed without needing a this->.
  using DwarfSectionImpl<AddressType>::memory_;
  using DwarfSectionImpl<AddressType>::fde_count_;
  using DwarfSectionImpl<AddressType>::last_error_;

  struct FdeInfo {
    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
        : offset(offset), start(start), end(start + length) {}

    uint64_t offset;
    AddressType start;
    AddressType end;
  };

  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
  virtual ~DwarfDebugFrame() = default;

  bool Init(uint64_t offset, uint64_t size) override;

  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;

  const DwarfFde* GetFdeFromIndex(size_t index) override;

  bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }

  bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }

  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }

  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }

  uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }

  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);

  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);

  bool CreateSortedFdeList();

 protected:
  uint64_t offset_;
  uint64_t end_offset_;

  std::vector<FdeInfo> fdes_;
};

#endif  // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+213 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <assert.h>
#include <stdint.h>

#include "DwarfEhFrame.h"
#include "DwarfMemory.h"
#include "DwarfSection.h"
#include "DwarfStructs.h"
#include "Memory.h"

template <typename AddressType>
bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
  uint8_t data[4];

  memory_.clear_func_offset();
  memory_.clear_text_offset();
  memory_.set_data_offset(offset);
  memory_.set_cur_offset(offset);

  // Read the first four bytes all at once.
  if (!memory_.ReadBytes(data, 4)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    return false;
  }

  version_ = data[0];
  if (version_ != 1) {
    // Unknown version.
    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
    return false;
  }

  ptr_encoding_ = data[1];
  uint8_t fde_count_encoding = data[2];
  table_encoding_ = data[3];
  table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);

  memory_.set_pc_offset(memory_.cur_offset());
  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    return false;
  }

  memory_.set_pc_offset(memory_.cur_offset());
  if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    return false;
  }

  entries_offset_ = memory_.cur_offset();
  entries_end_ = offset + size;
  entries_data_offset_ = offset;
  cur_entries_offset_ = entries_offset_;

  return true;
}

template <typename AddressType>
const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
  const FdeInfo* info = GetFdeInfoFromIndex(index);
  if (info == nullptr) {
    return nullptr;
  }
  return this->GetFdeFromOffset(info->offset);
}

template <typename AddressType>
const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
    size_t index) {
  auto entry = fde_info_.find(index);
  if (entry != fde_info_.end()) {
    return &fde_info_[index];
  }
  FdeInfo* info = &fde_info_[index];

  memory_.set_data_offset(entries_data_offset_);
  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
  memory_.set_pc_offset(memory_.cur_offset());
  uint64_t value;
  if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
      !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
    last_error_ = DWARF_ERROR_MEMORY_INVALID;
    fde_info_.erase(index);
    return nullptr;
  }
  info->pc = value;
  return info;
}

template <typename AddressType>
bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
                                                   uint64_t total_entries) {
  assert(fde_count_ > 0);
  assert(total_entries <= fde_count_);

  size_t first = 0;
  size_t last = total_entries;
  while (first < last) {
    size_t current = (first + last) / 2;
    const FdeInfo* info = GetFdeInfoFromIndex(current);
    if (pc == info->pc) {
      *fde_offset = info->offset;
      return true;
    }
    if (pc < info->pc) {
      last = current;
    } else {
      first = current + 1;
    }
  }
  if (last != 0) {
    const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
    *fde_offset = info->offset;
    return true;
  }
  return false;
}

template <typename AddressType>
bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
  assert(fde_count_ != 0);
  last_error_ = DWARF_ERROR_NONE;
  // We can do a binary search if the pc is in the range of the elements
  // that have already been cached.
  if (!fde_info_.empty()) {
    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
    if (pc >= info->pc) {
      *fde_offset = info->offset;
      return true;
    }
    if (pc < info->pc) {
      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
    }
  }

  if (cur_entries_offset_ == 0) {
    // All entries read, or error encountered.
    return false;
  }

  memory_.set_data_offset(entries_data_offset_);
  memory_.set_cur_offset(cur_entries_offset_);
  cur_entries_offset_ = 0;

  FdeInfo* prev_info = nullptr;
  for (size_t current = fde_info_.size();
       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
    memory_.set_pc_offset(memory_.cur_offset());
    uint64_t value;
    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }

    FdeInfo* info = &fde_info_[current];
    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
      fde_info_.erase(current);
      last_error_ = DWARF_ERROR_MEMORY_INVALID;
      return false;
    }
    info->pc = value;

    if (pc < info->pc) {
      if (prev_info == nullptr) {
        return false;
      }
      cur_entries_offset_ = memory_.cur_offset();
      *fde_offset = prev_info->offset;
      return true;
    }
    prev_info = info;
  }

  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
    *fde_offset = prev_info->offset;
    return true;
  }
  return false;
}

template <typename AddressType>
bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
  if (fde_count_ == 0) {
    return false;
  }

  if (table_entry_size_ > 0) {
    // Do a binary search since the size of each table entry is fixed.
    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
  } else {
    // Do a sequential search since each table entry size is variable.
    return GetFdeOffsetSequential(pc, fde_offset);
  }
}

// Explicitly instantiate DwarfEhFrame.
template class DwarfEhFrame<uint32_t>;
template class DwarfEhFrame<uint64_t>;
+89 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H

#include <stdint.h>

#include "DwarfSection.h"

// Forward declarations.
class Memory;

template <typename AddressType>
class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
 public:
  // Add these so that the protected members of DwarfSectionImpl
  // can be accessed without needing a this->.
  using DwarfSectionImpl<AddressType>::memory_;
  using DwarfSectionImpl<AddressType>::fde_count_;
  using DwarfSectionImpl<AddressType>::last_error_;

  struct FdeInfo {
    AddressType pc;
    uint64_t offset;
  };

  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
  virtual ~DwarfEhFrame() = default;

  bool Init(uint64_t offset, uint64_t size) override;

  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;

  const DwarfFde* GetFdeFromIndex(size_t index) override;

  bool IsCie32(uint32_t value32) override { return value32 == 0; }

  bool IsCie64(uint64_t value64) override { return value64 == 0; }

  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
    return memory_.cur_offset() - pointer - 4;
  }

  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
    return memory_.cur_offset() - pointer - 8;
  }

  uint64_t AdjustPcFromFde(uint64_t pc) override {
    // The eh_frame uses relative pcs.
    return pc + memory_.cur_offset();
  }

  const FdeInfo* GetFdeInfoFromIndex(size_t index);

  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);

  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);

 protected:
  uint8_t version_;
  uint8_t ptr_encoding_;
  uint8_t table_encoding_;
  size_t table_entry_size_;

  uint64_t ptr_offset_;

  uint64_t entries_offset_;
  uint64_t entries_end_;
  uint64_t entries_data_offset_;
  uint64_t cur_entries_offset_ = 0;

  std::unordered_map<uint64_t, FdeInfo> fde_info_;
};

#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
Loading