blob: b16de7c00b598af20f855dfe33bf94cdd3e8e9ec [file] [log] [blame] [edit]
// Copyright 2019 The Fuchsia 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 "src/developer/debug/zxdb/expr/bitfield.h"
#include <cstdint>
#include "src/developer/debug/zxdb/common/int128_t.h"
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/developer/debug/zxdb/expr/found_member.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
namespace zxdb {
namespace {
// We use 128-bit numbers for bitfield computations so we can shift around 64 bit bitfields. This
// allows us to handle anything up to 120 bits, or 128 bits if the beginning is aligned. This
// limitation seems reasonable.
// We treat "signed int" bitfields as being signed and needing sign extensions. Whether "int"
// bitfields are signed or unsigned is actually implementation-defined in the C standard.
bool NeedsSignExtension(const fxl::RefPtr<EvalContext>& context, const Type* type, uint128_t value,
uint32_t bit_size) {
fxl::RefPtr<BaseType> base_type = context->GetConcreteTypeAs<BaseType>(type);
if (!base_type)
return false;
if (!BaseType::IsSigned(base_type->base_type()))
return false; // Unsigned type.
// Needs sign extension when the high bit is set.
return value & (1ull << (bit_size - 1));
}
} // namespace
ErrOrValue ResolveBitfieldMember(const fxl::RefPtr<EvalContext>& context, const ExprValue& base,
const FoundMember& found_member) {
const DataMember* data_member = found_member.data_member();
FX_DCHECK(data_member->is_bitfield());
// Use the FoundMember's offset (not DataMember's) because FoundMember's takes into account base
// classes and their offsets.
// TODO(bug 41503) handle virtual inheritance.
auto opt_byte_offset = found_member.GetDataMemberOffset();
if (!opt_byte_offset) {
return Err(
"The debugger does not yet support bitfield access on virtually inherited base "
"classes (bug 41503) or static members.");
}
// The actual size of the bitfield.
uint32_t bit_size = data_member->bit_size();
// The offset into the base struct, rounded to bytes.
uint32_t byte_offset = *opt_byte_offset;
// The bit offset in addition to byte_offset, should be in range [0, 7].
uint32_t bit_offset = data_member->data_bit_offset();
// DWARF 2 (byte_size, bit_offset, member_location) combination is used. Convert it to bit_offset.
if (data_member->byte_size()) {
bit_offset = data_member->byte_size() * 8 - data_member->bit_offset() - data_member->bit_size();
}
// Spill the bit_offset into byte_offset, so we could support arbitrary large bit_offset.
byte_offset += bit_offset / 8;
bit_offset = bit_offset % 8;
// Round up bit_size to byte size. Note that it's possible for a bitfield of 10 bits to span
// across 3 bytes, i.e., 1 bit + 8 bits + 1 bit.
uint32_t byte_size = (bit_offset + bit_size + 7) / 8;
// Check whether the bitfield can be fit in a uint128_t.
if (byte_size > sizeof(uint128_t)) {
// If the total coverage of this bitfield is more than out number size we can't do the
// operations and need to rewrite this code to manually do shifts on bytes rather than using
// numeric operations in C.
return Err("The bitfield spans more than 128 bits which is unsupported.");
}
// Destination type. Here we need to save the original (possibly non-concrete type) for assigning
// to the result type at the bottom.
const Type* dest_type = data_member->type().Get()->As<Type>();
if (!dest_type)
return Err("Bitfield member has no type.");
fxl::RefPtr<Type> concrete_dest_type = context->GetConcreteType(dest_type);
uint128_t bits = 0;
if (base.data().size() < byte_offset + byte_size ||
!base.data().RangeIsValid(byte_offset, byte_size))
return Err::OptimizedOut();
// Copy bytes to out bitfield.
memcpy(&bits, &base.data().bytes()[byte_offset], byte_size);
// Shift.
bits >>= bit_offset;
// Mask.
uint128_t valid_bit_mask = (static_cast<uint128_t>(1) << bit_size) - 1;
bits &= valid_bit_mask;
if (NeedsSignExtension(context, concrete_dest_type.get(), bits, bit_size)) {
// Set non-masked bits to 1.
bits |= ~valid_bit_mask;
}
ExprValueSource source = base.source().GetOffsetInto(byte_offset, bit_size, bit_offset);
// Save the data back to the desired size (assume little-endian so truncation from the right is
// correct).
std::vector<uint8_t> new_data(concrete_dest_type->byte_size());
memcpy(new_data.data(), &bits, new_data.size());
return ExprValue(RefPtrTo(dest_type), std::move(new_data), source);
}
void WriteBitfieldToMemory(const fxl::RefPtr<EvalContext>& context, const ExprValueSource& dest,
std::vector<uint8_t> data, fit::callback<void(const Err&)> cb) {
FX_DCHECK(dest.is_bitfield());
// Expect bitfields to fit in our biggest int.
if (data.size() > sizeof(uint128_t))
return cb(Err("Writing bitfields for data > 128-bits is not supported."));
uint128_t value = 0;
memcpy(&value, data.data(), data.size());
// Number of bytes affected by this bitfield.
size_t byte_size = (dest.bit_size() + dest.bit_shift() + 7) / 8;
if (!byte_size)
return cb(Err("Can't write a bitfield with no data."));
// To only write some bits we need to do a read and a write. Since these are asynchronous there
// is a possibility of racing with the program, but there will a race if the program is running
// even if we do the masking in the debug_agent. This implementation is simpler than passing the
// mask to the agent, so do that.
context->GetDataProvider()->GetMemoryAsync(
dest.address(), byte_size,
[context, dest, value, byte_size, cb = std::move(cb)](
const Err& err, std::vector<uint8_t> original_data) mutable {
if (err.has_error())
return cb(err);
if (original_data.size() != byte_size) // Short read means invalid address.
return cb(Err("Memory at address 0x%" PRIx64 " is invalid.", dest.address()));
uint128_t original = 0;
memcpy(&original, original_data.data(), original_data.size());
uint128_t result = dest.SetBits(original, value);
// Write out the new data.
std::vector<uint8_t> new_data(byte_size);
memcpy(new_data.data(), &result, byte_size);
context->GetDataProvider()->WriteMemory(dest.address(), std::move(new_data), std::move(cb));
});
}
} // namespace zxdb