blob: 8dc74284c4feb1fa7d352efb1715da510c92b5c1 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// 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 <algorithm>
#include <cstring>
#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<uint64_t>(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<uint64_t>(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<size_t>(next_chunk_start_))
return false;
const ssize_t unconsumed_amount = google::ReadFromOffset(
fd_, buf_, sizeof(buf_), static_cast<size_t>(next_chunk_start_));
if (unconsumed_amount <= 0) {
// Read error.
return false;
}
unconsumed_amount_ = static_cast<size_t>(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