| /* | 
 |  * 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 <unwindstack/DwarfStructs.h> | 
 | #include <unwindstack/Memory.h> | 
 |  | 
 | #include "DwarfDebugFrame.h" | 
 | #include "DwarfEncoding.h" | 
 | #include "DwarfError.h" | 
 |  | 
 | namespace unwindstack { | 
 |  | 
 | 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>; | 
 |  | 
 | }  // namespace unwindstack |