Home | History | Annotate | Download | only in libunwindstack
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdint.h>
     18 
     19 #include <unwindstack/DwarfError.h>
     20 #include <unwindstack/DwarfStructs.h>
     21 #include <unwindstack/Memory.h>
     22 
     23 #include "Check.h"
     24 #include "DwarfEhFrameWithHdr.h"
     25 
     26 namespace unwindstack {
     27 
     28 template <typename AddressType>
     29 bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
     30   uint8_t data[4];
     31 
     32   memory_.clear_func_offset();
     33   memory_.clear_text_offset();
     34   memory_.set_data_offset(offset);
     35   memory_.set_cur_offset(offset);
     36 
     37   // Read the first four bytes all at once.
     38   if (!memory_.ReadBytes(data, 4)) {
     39     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     40     last_error_.address = memory_.cur_offset();
     41     return false;
     42   }
     43 
     44   version_ = data[0];
     45   if (version_ != 1) {
     46     // Unknown version.
     47     last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
     48     return false;
     49   }
     50 
     51   ptr_encoding_ = data[1];
     52   uint8_t fde_count_encoding = data[2];
     53   table_encoding_ = data[3];
     54   table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
     55 
     56   memory_.set_pc_offset(memory_.cur_offset());
     57   if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
     58     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     59     last_error_.address = memory_.cur_offset();
     60     return false;
     61   }
     62 
     63   memory_.set_pc_offset(memory_.cur_offset());
     64   if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
     65     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     66     last_error_.address = memory_.cur_offset();
     67     return false;
     68   }
     69 
     70   if (fde_count_ == 0) {
     71     last_error_.code = DWARF_ERROR_NO_FDES;
     72     return false;
     73   }
     74 
     75   entries_offset_ = memory_.cur_offset();
     76   entries_end_ = offset + size;
     77   entries_data_offset_ = offset;
     78   cur_entries_offset_ = entries_offset_;
     79 
     80   return true;
     81 }
     82 
     83 template <typename AddressType>
     84 const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
     85   const FdeInfo* info = GetFdeInfoFromIndex(index);
     86   if (info == nullptr) {
     87     return nullptr;
     88   }
     89   return this->GetFdeFromOffset(info->offset);
     90 }
     91 
     92 template <typename AddressType>
     93 const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
     94 DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
     95   auto entry = fde_info_.find(index);
     96   if (entry != fde_info_.end()) {
     97     return &fde_info_[index];
     98   }
     99   FdeInfo* info = &fde_info_[index];
    100 
    101   memory_.set_data_offset(entries_data_offset_);
    102   memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
    103   memory_.set_pc_offset(memory_.cur_offset());
    104   uint64_t value;
    105   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
    106       !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
    107     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
    108     last_error_.address = memory_.cur_offset();
    109     fde_info_.erase(index);
    110     return nullptr;
    111   }
    112   info->pc = value;
    113   return info;
    114 }
    115 
    116 template <typename AddressType>
    117 bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
    118                                                           uint64_t total_entries) {
    119   CHECK(fde_count_ > 0);
    120   CHECK(total_entries <= fde_count_);
    121 
    122   size_t first = 0;
    123   size_t last = total_entries;
    124   while (first < last) {
    125     size_t current = (first + last) / 2;
    126     const FdeInfo* info = GetFdeInfoFromIndex(current);
    127     if (info == nullptr) {
    128       return false;
    129     }
    130     if (pc == info->pc) {
    131       *fde_offset = info->offset;
    132       return true;
    133     }
    134     if (pc < info->pc) {
    135       last = current;
    136     } else {
    137       first = current + 1;
    138     }
    139   }
    140   if (last != 0) {
    141     const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
    142     if (info == nullptr) {
    143       return false;
    144     }
    145     *fde_offset = info->offset;
    146     return true;
    147   }
    148   return false;
    149 }
    150 
    151 template <typename AddressType>
    152 bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
    153   CHECK(fde_count_ != 0);
    154   last_error_.code = DWARF_ERROR_NONE;
    155   last_error_.address = 0;
    156 
    157   // We can do a binary search if the pc is in the range of the elements
    158   // that have already been cached.
    159   if (!fde_info_.empty()) {
    160     const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
    161     if (pc >= info->pc) {
    162       *fde_offset = info->offset;
    163       return true;
    164     }
    165     if (pc < info->pc) {
    166       return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
    167     }
    168   }
    169 
    170   if (cur_entries_offset_ == 0) {
    171     // All entries read, or error encountered.
    172     return false;
    173   }
    174 
    175   memory_.set_data_offset(entries_data_offset_);
    176   memory_.set_cur_offset(cur_entries_offset_);
    177   cur_entries_offset_ = 0;
    178 
    179   FdeInfo* prev_info = nullptr;
    180   for (size_t current = fde_info_.size();
    181        current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
    182     memory_.set_pc_offset(memory_.cur_offset());
    183     uint64_t value;
    184     if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
    185       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
    186       last_error_.address = memory_.cur_offset();
    187       return false;
    188     }
    189 
    190     FdeInfo* info = &fde_info_[current];
    191     if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
    192       fde_info_.erase(current);
    193       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
    194       last_error_.address = memory_.cur_offset();
    195       return false;
    196     }
    197     info->pc = value + 4;
    198 
    199     if (pc < info->pc) {
    200       if (prev_info == nullptr) {
    201         return false;
    202       }
    203       cur_entries_offset_ = memory_.cur_offset();
    204       *fde_offset = prev_info->offset;
    205       return true;
    206     }
    207     prev_info = info;
    208   }
    209 
    210   if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
    211     *fde_offset = prev_info->offset;
    212     return true;
    213   }
    214   return false;
    215 }
    216 
    217 template <typename AddressType>
    218 bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
    219   if (fde_count_ == 0) {
    220     return false;
    221   }
    222 
    223   if (table_entry_size_ > 0) {
    224     // Do a binary search since the size of each table entry is fixed.
    225     return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
    226   } else {
    227     // Do a sequential search since each table entry size is variable.
    228     return GetFdeOffsetSequential(pc, fde_offset);
    229   }
    230 }
    231 
    232 // Explicitly instantiate DwarfEhFrameWithHdr
    233 template class DwarfEhFrameWithHdr<uint32_t>;
    234 template class DwarfEhFrameWithHdr<uint64_t>;
    235 
    236 }  // namespace unwindstack
    237