// Copyright (c) 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/debug/buffered_dwarf_reader.h" #ifdef USE_SYMBOLIZE #include #include #include "base/numerics/safe_conversions.h" #include "base/third_party/symbolize/symbolize.h" namespace base::debug { BufferedDwarfReader::BufferedDwarfReader(int fd, uint64_t position) : fd_(fd), next_chunk_start_(position), last_chunk_start_(position) {} size_t BufferedDwarfReader::ReadCString(uint64_t max_position, char* out, size_t out_size) { char character; size_t bytes_written = 0; do { if (!ReadChar(character)) { return 0; } if (out && bytes_written < out_size) { out[bytes_written++] = character; } } while (character != '\0' && position() < max_position); if (out) { out[std::min(bytes_written, out_size - 1)] = '\0'; } return bytes_written; } bool BufferedDwarfReader::ReadLeb128(uint64_t& value) { value = 0; uint8_t byte; int shift = 0; do { if (!ReadInt8(byte)) return false; value |= static_cast(byte & 0x7F) << shift; shift += 7; } while (byte & 0x80); return true; } bool BufferedDwarfReader::ReadLeb128(int64_t& value) { value = 0; uint8_t byte; int shift = 0; bool sign_bit = false; do { if (!ReadInt8(byte)) return false; value |= static_cast(byte & 0x7F) << shift; shift += 7; sign_bit = byte & 0x40; } while (byte & 0x80); constexpr int bits_in_output = sizeof(value) * 8; if ((shift < bits_in_output) && sign_bit) { value |= -(1 << shift); } return true; } bool BufferedDwarfReader::ReadInitialLength(bool& is_64bit, uint64_t& length) { uint32_t token_32bit; if (!ReadInt32(token_32bit)) { return false; } // Dwarf 3 introduced an extended length field that both indicates this is // DWARF-64 and changes how the size is encoded. 0xfffffff0 and higher are // reserved with 0xffffffff meaning it's the extended field with the // following 64-bits being the full length. if (token_32bit < 0xfffffff0) { length = token_32bit; is_64bit = false; return true; } if (token_32bit != 0xffffffff) { return false; } if (!ReadInt64(length)) { return false; } is_64bit = true; return true; } bool BufferedDwarfReader::ReadOffset(bool is_64bit, uint64_t& offset) { if (is_64bit) { if (!ReadInt64(offset)) { return false; } } else { uint32_t tmp; if (!ReadInt32(tmp)) { return false; } offset = tmp; } return true; } bool BufferedDwarfReader::ReadAddress(uint8_t address_size, uint64_t& address) { // Note `address_size` indicates the numbrer of bytes in the address. switch (address_size) { case 2: { uint16_t tmp; if (!ReadInt16(tmp)) return false; address = tmp; } break; case 4: { uint32_t tmp; if (!ReadInt32(tmp)) return false; address = tmp; } break; case 8: { uint64_t tmp; if (!ReadInt64(tmp)) return false; address = tmp; } break; default: return false; } return true; } bool BufferedDwarfReader::ReadCommonHeader(bool& is_64bit, uint64_t& length, uint16_t& version, uint64_t& offset, uint8_t& address_size, uint64_t& end_position) { if (!ReadInitialLength(is_64bit, length)) { return false; } end_position = position() + length; if (!ReadInt16(version)) { return false; } if (!ReadOffset(is_64bit, offset)) { return false; } if (!ReadInt8(address_size)) { return false; } return true; } bool BufferedDwarfReader::BufferedRead(void* out, const size_t bytes) { size_t bytes_left = bytes; while (bytes_left > 0) { // Refresh the buffer. if (unconsumed_amount_ == 0) { if (!base::IsValueInRangeForNumericType(next_chunk_start_)) return false; const ssize_t unconsumed_amount = google::ReadFromOffset( fd_, buf_, sizeof(buf_), static_cast(next_chunk_start_)); if (unconsumed_amount_ <= 0) { // Read error. return false; } unconsumed_amount_ = static_cast(unconsumed_amount); last_chunk_start_ = next_chunk_start_; next_chunk_start_ += unconsumed_amount_; cursor_in_buffer_ = 0; } size_t to_copy = std::min(bytes_left, unconsumed_amount_); memcpy(out, &buf_[cursor_in_buffer_], to_copy); unconsumed_amount_ -= to_copy; cursor_in_buffer_ += to_copy; bytes_left -= to_copy; } return true; } } // namespace base::debug #endif