/* * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2013-2015, Rene Gollent, rene@gollent.com. * Distributed under the terms of the MIT License. */ #include "ValueLoader.h" #include "Architecture.h" #include "BitBuffer.h" #include "CpuState.h" #include "Register.h" #include "TeamMemory.h" #include "Tracing.h" #include "ValueLocation.h" ValueLoader::ValueLoader(Architecture* architecture, TeamMemory* teamMemory, CpuState* cpuState) : fArchitecture(architecture), fTeamMemory(teamMemory), fCpuState(cpuState) { fArchitecture->AcquireReference(); fTeamMemory->AcquireReference(); if (fCpuState != NULL) fCpuState->AcquireReference(); } ValueLoader::~ValueLoader() { fArchitecture->ReleaseReference(); fTeamMemory->ReleaseReference(); if (fCpuState != NULL) fCpuState->ReleaseReference(); } status_t ValueLoader::LoadValue(ValueLocation* location, type_code valueType, bool shortValueIsFine, BVariant& _value) { static const size_t kMaxPieceSize = 16; uint64 totalBitSize = 0; int32 count = location->CountPieces(); for (int32 i = 0; i < count; i++) { ValuePieceLocation piece = location->PieceAt(i); switch (piece.type) { case VALUE_PIECE_LOCATION_INVALID: case VALUE_PIECE_LOCATION_UNKNOWN: return B_ENTRY_NOT_FOUND; case VALUE_PIECE_LOCATION_MEMORY: case VALUE_PIECE_LOCATION_REGISTER: case VALUE_PIECE_LOCATION_IMPLICIT: break; } if (piece.size > kMaxPieceSize) { TRACE_LOCALS(" -> overly long piece size (%" B_PRIu64 " bytes)\n", piece.size); return B_UNSUPPORTED; } totalBitSize += piece.bitSize; } TRACE_LOCALS(" -> totalBitSize: %" B_PRIu64 "\n", totalBitSize); if (totalBitSize == 0) { TRACE_LOCALS(" -> no size\n"); return B_ENTRY_NOT_FOUND; } if (totalBitSize > 64) { TRACE_LOCALS(" -> longer than 64 bits: unsupported\n"); return B_UNSUPPORTED; } uint64 valueBitSize = BVariant::SizeOfType(valueType) * 8; if (!shortValueIsFine && totalBitSize < valueBitSize) { TRACE_LOCALS(" -> too short for value type (%" B_PRIu64 " vs. %" B_PRIu64 " bits)\n", totalBitSize, valueBitSize); return B_BAD_VALUE; } // Load the data. Since the BitBuffer class we're using only supports big // endian bit semantics, we convert all data to big endian before pushing // them to the buffer. For later conversion to BVariant we need to make sure // the final buffer has the size of the value type, so we pad the most // significant bits with zeros. BitBuffer valueBuffer; if (totalBitSize < valueBitSize) valueBuffer.AddZeroBits(valueBitSize - totalBitSize); bool bigEndian = fArchitecture->IsBigEndian(); const Register* registers = fArchitecture->Registers(); for (int32 i = 0; i < count; i++) { ValuePieceLocation piece = location->PieceAt( bigEndian ? i : count - i - 1); uint32 bytesToRead = piece.size; uint32 bitSize = piece.bitSize; uint8 bitOffset = piece.bitOffset; // TODO: the offset's ordinal position and direction aren't // specified by DWARF, and simply follow the target language. // To handle non C/C++ languages properly, the corresponding // SourceLanguage will need to be passed in and extended to // return the relevant information. switch (piece.type) { case VALUE_PIECE_LOCATION_INVALID: case VALUE_PIECE_LOCATION_UNKNOWN: return B_ENTRY_NOT_FOUND; case VALUE_PIECE_LOCATION_MEMORY: case VALUE_PIECE_LOCATION_IMPLICIT: { target_addr_t address = piece.address; if (piece.type == VALUE_PIECE_LOCATION_MEMORY) { TRACE_LOCALS(" piece %" B_PRId32 ": memory address: %#" B_PRIx64 ", bits: %" B_PRIu32 "\n", i, address, bitSize); } else { TRACE_LOCALS(" piece %" B_PRId32 ": implicit value, " "bits: %" B_PRIu32 "\n", i, bitSize); } uint8 pieceBuffer[kMaxPieceSize]; ssize_t bytesRead; if (piece.type == VALUE_PIECE_LOCATION_MEMORY) { bytesRead = fTeamMemory->ReadMemory(address, pieceBuffer, bytesToRead); } else { memcpy(pieceBuffer, piece.value, piece.size); bytesRead = piece.size; } if (bytesRead < 0) return bytesRead; if ((uint32)bytesRead != bytesToRead) return B_BAD_ADDRESS; TRACE_LOCALS_ONLY( TRACE_LOCALS(" -> read: "); for (ssize_t k = 0; k < bytesRead; k++) TRACE_LOCALS("%02x", pieceBuffer[k]); TRACE_LOCALS("\n"); ) // convert to big endian if (!bigEndian) { for (int32 k = bytesRead / 2 - 1; k >= 0; k--) { std::swap(pieceBuffer[k], pieceBuffer[bytesRead - k - 1]); } } valueBuffer.AddBits(pieceBuffer, bitSize, bitOffset); break; } case VALUE_PIECE_LOCATION_REGISTER: { TRACE_LOCALS(" piece %" B_PRId32 ": register: %" B_PRIu32 ", bits: %" B_PRIu32 "\n", i, piece.reg, bitSize); if (fCpuState == NULL) { WARNING("ValueLoader::LoadValue(): register piece, but no " "CpuState\n"); return B_UNSUPPORTED; } BVariant registerValue; if (!fCpuState->GetRegisterValue(registers + piece.reg, registerValue)) { return B_ENTRY_NOT_FOUND; } if (registerValue.Size() < bytesToRead) return B_ENTRY_NOT_FOUND; if (!bigEndian) { registerValue.SwapEndianess(); bitOffset = registerValue.Size() * 8 - bitOffset - bitSize; } valueBuffer.AddBits(registerValue.Bytes(), bitSize, bitOffset); break; } } } // If we don't have enough bits in the buffer apparently adding some failed. if (valueBuffer.BitSize() < valueBitSize) return B_NO_MEMORY; // convert the bits into something we can work with BVariant value; status_t error = value.SetToTypedData(valueBuffer.Bytes(), valueType); if (error != B_OK) { TRACE_LOCALS(" -> failed to set typed data: %s\n", strerror(error)); return error; } // convert to host endianess #if B_HOST_IS_LENDIAN value.SwapEndianess(); #endif _value = value; return B_OK; } status_t ValueLoader::LoadRawValue(BVariant& location, size_t bytesToRead, void* _value) { ssize_t bytesRead = fTeamMemory->ReadMemory(location.ToUInt64(), _value, bytesToRead); if (bytesRead < 0) return bytesRead; if ((uint32)bytesRead != bytesToRead) return B_BAD_ADDRESS; return B_OK; } status_t ValueLoader::LoadStringValue(BVariant& location, size_t maxSize, BString& _value) { static const size_t kMaxStringSize = 255; return fTeamMemory->ReadMemoryString(location.ToUInt64(), std::min(maxSize, kMaxStringSize), _value); }