aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Jones <70633990+chris-jones-arm@users.noreply.github.com>2024-02-15 15:05:25 +0000
committerGitHub <noreply@github.com>2024-02-15 15:05:25 +0000
commit30e7bbdc37d8f6320056429b39e4896575e4ae64 (patch)
tree23319bf1613eecfcb32231f853418ba618aa2272
parentaccc97f1681043a9fc006a8263ebaa36ab434c53 (diff)
Add support for implicit checks (#86)
Some runtimes make use of implicit checks, where a potentially invalid memory access is performed. If the memory access is invalid then a signal is raised which is then handled by a custom signal handler which decides how to handle the signal, often returning execution to a different function. This patch enables this use case by performing an optional memory access before any actual memory access. This ensures the signal is raised in a fixed location (_vixl_internal_AccessMemory) inside the simulator which allows for signal handlers to determine: 1. That the signal came from inside the simulator (using IsSimulatedMemoryAccess) and can therefore be handled. 2. That simulation can be resumed at a determined location (using GetSignalReturnAddress) by placing that location in the signal handlers ucontext instruction pointer register, thus allowing execution to continue at the next physical instruction. If a signal handler correctly handles a signal raised from inside the simulator then the simulator can return to the main loop to continue simulation at the program counter. If the signal handler needs to return to a different function then it can modify the PC via the existing WritePc function.
-rw-r--r--SConstruct7
-rw-r--r--src/aarch64/logic-aarch64.cc127
-rw-r--r--src/aarch64/simulator-aarch64.cc406
-rw-r--r--src/aarch64/simulator-aarch64.h122
-rw-r--r--src/globals-vixl.h12
-rw-r--r--test/aarch64/test-simulator-aarch64.cc64
-rw-r--r--tools/code_coverage.log3
7 files changed, 533 insertions, 208 deletions
diff --git a/SConstruct b/SConstruct
index 934a81e3..ac31cb23 100644
--- a/SConstruct
+++ b/SConstruct
@@ -120,6 +120,9 @@ options = {
'coverage:on' : {
'CCFLAGS': ['-fprofile-instr-generate', '-fcoverage-mapping'],
'LINKFLAGS': ['-fprofile-instr-generate', '-fcoverage-mapping']
+ },
+ 'implicit_checks:on' : {
+ 'CCFLAGS' : ['-DVIXL_ENABLE_IMPLICIT_CHECKS'],
}
}
@@ -265,6 +268,10 @@ vars.AddVariables(
EnumVariable('negative_testing',
'Enable negative testing (needs exceptions)',
'off', allowed_values=['on', 'off']),
+ EnumVariable('implicit_checks',
+ 'Allow signals raised from simulated invalid (e.g: out of'
+ + ' bounds) memory reads to be handled by the host.',
+ 'off', allowed_values=['on', 'off']),
DefaultVariable('symbols', 'Include debugging symbols in the binaries',
['on', 'off']),
DefaultVariable('simulator', 'Simulators to include', ['aarch64', 'none']),
diff --git a/src/aarch64/logic-aarch64.cc b/src/aarch64/logic-aarch64.cc
index 11229ad6..5e53d27c 100644
--- a/src/aarch64/logic-aarch64.cc
+++ b/src/aarch64/logic-aarch64.cc
@@ -182,24 +182,27 @@ uint64_t Simulator::GenerateRandomTag(uint16_t exclude) {
}
-void Simulator::ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr) {
+bool Simulator::ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr) {
dst.ClearForWrite(vform);
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst, vform, i, addr);
+ if (!LoadLane(dst, vform, i, addr)) {
+ return false;
+ }
addr += LaneSizeInBytesFromFormat(vform);
}
+ return true;
}
-void Simulator::ld1(VectorFormat vform,
+bool Simulator::ld1(VectorFormat vform,
LogicVRegister dst,
int index,
uint64_t addr) {
- LoadLane(dst, vform, index, addr);
+ return LoadLane(dst, vform, index, addr);
}
-void Simulator::ld1r(VectorFormat vform,
+bool Simulator::ld1r(VectorFormat vform,
VectorFormat unpack_vform,
LogicVRegister dst,
uint64_t addr,
@@ -208,20 +211,25 @@ void Simulator::ld1r(VectorFormat vform,
dst.ClearForWrite(vform);
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
if (is_signed) {
- LoadIntToLane(dst, vform, unpack_size, i, addr);
+ if (!LoadIntToLane(dst, vform, unpack_size, i, addr)) {
+ return false;
+ }
} else {
- LoadUintToLane(dst, vform, unpack_size, i, addr);
+ if (!LoadUintToLane(dst, vform, unpack_size, i, addr)) {
+ return false;
+ }
}
}
+ return true;
}
-void Simulator::ld1r(VectorFormat vform, LogicVRegister dst, uint64_t addr) {
- ld1r(vform, vform, dst, addr);
+bool Simulator::ld1r(VectorFormat vform, LogicVRegister dst, uint64_t addr) {
+ return ld1r(vform, vform, dst, addr);
}
-void Simulator::ld2(VectorFormat vform,
+bool Simulator::ld2(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
uint64_t addr1) {
@@ -230,15 +238,17 @@ void Simulator::ld2(VectorFormat vform,
int esize = LaneSizeInBytesFromFormat(vform);
uint64_t addr2 = addr1 + esize;
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst1, vform, i, addr1);
- LoadLane(dst2, vform, i, addr2);
+ if (!LoadLane(dst1, vform, i, addr1) || !LoadLane(dst2, vform, i, addr2)) {
+ return false;
+ }
addr1 += 2 * esize;
addr2 += 2 * esize;
}
+ return true;
}
-void Simulator::ld2(VectorFormat vform,
+bool Simulator::ld2(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
int index,
@@ -246,12 +256,12 @@ void Simulator::ld2(VectorFormat vform,
dst1.ClearForWrite(vform);
dst2.ClearForWrite(vform);
uint64_t addr2 = addr1 + LaneSizeInBytesFromFormat(vform);
- LoadLane(dst1, vform, index, addr1);
- LoadLane(dst2, vform, index, addr2);
+ return (LoadLane(dst1, vform, index, addr1) &&
+ LoadLane(dst2, vform, index, addr2));
}
-void Simulator::ld2r(VectorFormat vform,
+bool Simulator::ld2r(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
uint64_t addr) {
@@ -259,13 +269,15 @@ void Simulator::ld2r(VectorFormat vform,
dst2.ClearForWrite(vform);
uint64_t addr2 = addr + LaneSizeInBytesFromFormat(vform);
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst1, vform, i, addr);
- LoadLane(dst2, vform, i, addr2);
+ if (!LoadLane(dst1, vform, i, addr) || !LoadLane(dst2, vform, i, addr2)) {
+ return false;
+ }
}
+ return true;
}
-void Simulator::ld3(VectorFormat vform,
+bool Simulator::ld3(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -277,17 +289,19 @@ void Simulator::ld3(VectorFormat vform,
uint64_t addr2 = addr1 + esize;
uint64_t addr3 = addr2 + esize;
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst1, vform, i, addr1);
- LoadLane(dst2, vform, i, addr2);
- LoadLane(dst3, vform, i, addr3);
+ if (!LoadLane(dst1, vform, i, addr1) || !LoadLane(dst2, vform, i, addr2) ||
+ !LoadLane(dst3, vform, i, addr3)) {
+ return false;
+ }
addr1 += 3 * esize;
addr2 += 3 * esize;
addr3 += 3 * esize;
}
+ return true;
}
-void Simulator::ld3(VectorFormat vform,
+bool Simulator::ld3(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -298,13 +312,13 @@ void Simulator::ld3(VectorFormat vform,
dst3.ClearForWrite(vform);
uint64_t addr2 = addr1 + LaneSizeInBytesFromFormat(vform);
uint64_t addr3 = addr2 + LaneSizeInBytesFromFormat(vform);
- LoadLane(dst1, vform, index, addr1);
- LoadLane(dst2, vform, index, addr2);
- LoadLane(dst3, vform, index, addr3);
+ return (LoadLane(dst1, vform, index, addr1) &&
+ LoadLane(dst2, vform, index, addr2) &&
+ LoadLane(dst3, vform, index, addr3));
}
-void Simulator::ld3r(VectorFormat vform,
+bool Simulator::ld3r(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -315,14 +329,16 @@ void Simulator::ld3r(VectorFormat vform,
uint64_t addr2 = addr + LaneSizeInBytesFromFormat(vform);
uint64_t addr3 = addr2 + LaneSizeInBytesFromFormat(vform);
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst1, vform, i, addr);
- LoadLane(dst2, vform, i, addr2);
- LoadLane(dst3, vform, i, addr3);
+ if (!LoadLane(dst1, vform, i, addr) || !LoadLane(dst2, vform, i, addr2) ||
+ !LoadLane(dst3, vform, i, addr3)) {
+ return false;
+ }
}
+ return true;
}
-void Simulator::ld4(VectorFormat vform,
+bool Simulator::ld4(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -337,19 +353,20 @@ void Simulator::ld4(VectorFormat vform,
uint64_t addr3 = addr2 + esize;
uint64_t addr4 = addr3 + esize;
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst1, vform, i, addr1);
- LoadLane(dst2, vform, i, addr2);
- LoadLane(dst3, vform, i, addr3);
- LoadLane(dst4, vform, i, addr4);
+ if (!LoadLane(dst1, vform, i, addr1) || !LoadLane(dst2, vform, i, addr2) ||
+ !LoadLane(dst3, vform, i, addr3) || !LoadLane(dst4, vform, i, addr4)) {
+ return false;
+ }
addr1 += 4 * esize;
addr2 += 4 * esize;
addr3 += 4 * esize;
addr4 += 4 * esize;
}
+ return true;
}
-void Simulator::ld4(VectorFormat vform,
+bool Simulator::ld4(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -363,14 +380,14 @@ void Simulator::ld4(VectorFormat vform,
uint64_t addr2 = addr1 + LaneSizeInBytesFromFormat(vform);
uint64_t addr3 = addr2 + LaneSizeInBytesFromFormat(vform);
uint64_t addr4 = addr3 + LaneSizeInBytesFromFormat(vform);
- LoadLane(dst1, vform, index, addr1);
- LoadLane(dst2, vform, index, addr2);
- LoadLane(dst3, vform, index, addr3);
- LoadLane(dst4, vform, index, addr4);
+ return (LoadLane(dst1, vform, index, addr1) &&
+ LoadLane(dst2, vform, index, addr2) &&
+ LoadLane(dst3, vform, index, addr3) &&
+ LoadLane(dst4, vform, index, addr4));
}
-void Simulator::ld4r(VectorFormat vform,
+bool Simulator::ld4r(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -384,11 +401,12 @@ void Simulator::ld4r(VectorFormat vform,
uint64_t addr3 = addr2 + LaneSizeInBytesFromFormat(vform);
uint64_t addr4 = addr3 + LaneSizeInBytesFromFormat(vform);
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
- LoadLane(dst1, vform, i, addr);
- LoadLane(dst2, vform, i, addr2);
- LoadLane(dst3, vform, i, addr3);
- LoadLane(dst4, vform, i, addr4);
+ if (!LoadLane(dst1, vform, i, addr) || !LoadLane(dst2, vform, i, addr2) ||
+ !LoadLane(dst3, vform, i, addr3) || !LoadLane(dst4, vform, i, addr4)) {
+ return false;
+ }
}
+ return true;
}
@@ -7295,7 +7313,7 @@ void Simulator::SVEStructuredStoreHelper(VectorFormat vform,
}
}
-void Simulator::SVEStructuredLoadHelper(VectorFormat vform,
+bool Simulator::SVEStructuredLoadHelper(VectorFormat vform,
const LogicPRegister& pg,
unsigned zt_code,
const LogicSVEAddressVector& addr,
@@ -7330,9 +7348,13 @@ void Simulator::SVEStructuredLoadHelper(VectorFormat vform,
}
if (is_signed) {
- LoadIntToLane(zt[r], vform, msize_in_bytes, i, element_address);
+ if (!LoadIntToLane(zt[r], vform, msize_in_bytes, i, element_address)) {
+ return false;
+ }
} else {
- LoadUintToLane(zt[r], vform, msize_in_bytes, i, element_address);
+ if (!LoadUintToLane(zt[r], vform, msize_in_bytes, i, element_address)) {
+ return false;
+ }
}
}
}
@@ -7351,6 +7373,7 @@ void Simulator::SVEStructuredLoadHelper(VectorFormat vform,
"<-",
addr);
}
+ return true;
}
LogicPRegister Simulator::brka(LogicPRegister pd,
@@ -7458,7 +7481,9 @@ void Simulator::SVEFaultTolerantLoadHelper(VectorFormat vform,
// First-faulting loads always load the first active element, regardless
// of FFR. The result will be discarded if its FFR lane is inactive, but
// it could still generate a fault.
- value = MemReadUint(msize_in_bytes, element_address);
+ VIXL_DEFINE_OR_RETURN(mem_result,
+ MemReadUint(msize_in_bytes, element_address));
+ value = mem_result;
// All subsequent elements have non-fault semantics.
type = kSVENonFaultLoad;
@@ -7470,7 +7495,9 @@ void Simulator::SVEFaultTolerantLoadHelper(VectorFormat vform,
bool can_read = (i < fake_fault_at_lane) &&
CanReadMemory(element_address, msize_in_bytes);
if (can_read) {
- value = MemReadUint(msize_in_bytes, element_address);
+ VIXL_DEFINE_OR_RETURN(mem_result,
+ MemReadUint(msize_in_bytes, element_address));
+ value = mem_result;
} else {
// Propagate the fault to the end of FFR.
for (int j = i; j < LaneCountFromFormat(vform); j++) {
diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc
index 5166ec87..32f04c3d 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -42,6 +42,25 @@ using vixl::internal::SimFloat16;
const Instruction* Simulator::kEndOfSimAddress = NULL;
+MemoryReadResult TryMemoryRead(uintptr_t address, uintptr_t access_size) {
+#ifdef VIXL_ENABLE_IMPLICIT_CHECKS
+ for (uintptr_t i = 0; i < access_size; i++) {
+ if (_vixl_internal_ReadMemory(address, i) == MemoryReadResult::Failure) {
+ // The memory read failed.
+ return MemoryReadResult::Failure;
+ }
+ }
+
+ // Either the memory read did not raise a signal or the signal handler did
+ // not correctly return MemoryReadResult::Failure.
+ return MemoryReadResult::Success;
+#else
+ USE(address);
+ USE(access_size);
+ return MemoryReadResult::Success;
+#endif // VIXL_ENABLE_IMPLICIT_CHECKS
+}
+
bool MetaDataDepot::MetaDataMTE::is_active = false;
void SimSystemRegister::SetBits(int msb, int lsb, uint32_t bits) {
@@ -492,6 +511,32 @@ const Simulator::FormToVisitorFnMap* Simulator::GetFormToVisitorFnMap() {
return &form_to_visitor;
}
+// Try to access the piece of memory given by the address passed in RDI and the
+// offset passed in %rsi, using testb. If a signal is raised then the signal
+// handler should set RIP to _vixl_internal_ReadMemory_continue and RAX to
+// MemoryReadResult::Failure. If no signal is raised then zero RAX before
+// returning.
+#ifdef VIXL_ENABLE_IMPLICIT_CHECKS
+#ifdef __x86_64__
+asm(R"(
+ .globl _vixl_internal_ReadMemory
+ _vixl_internal_ReadMemory:
+ testb (%rdi, %rsi), %al
+ xorq %rax, %rax
+ ret
+ .globl _vixl_internal_ReadMemory_continue
+ _vixl_internal_ReadMemory_continue:
+ ret
+)");
+#else
+asm(R"(
+ .globl _vixl_internal_ReadMemory
+ _vixl_internal_ReadMemory:
+ ret
+)");
+#endif // __x86_64__
+#endif // VIXL_ENABLE_IMPLICIT_CHECKS
+
Simulator::Simulator(Decoder* decoder, FILE* stream, SimStack::Allocated stack)
: memory_(std::move(stack)),
last_instr_(NULL),
@@ -1772,8 +1817,9 @@ uint16_t Simulator::PrintPartialAccess(uint16_t access_mask,
const char* sep = "";
for (int i = struct_element_count - 1; i >= 0; i--) {
int offset = lane_size_in_bytes * i;
- uint64_t nibble = MemReadUint(lane_size_in_bytes, address + offset);
- fprintf(stream_, "%s%0*" PRIx64, sep, lane_size_in_nibbles, nibble);
+ auto nibble = MemReadUint(lane_size_in_bytes, address + offset);
+ VIXL_ASSERT(nibble);
+ fprintf(stream_, "%s%0*" PRIx64, sep, lane_size_in_nibbles, *nibble);
sep = "'";
}
fprintf(stream_,
@@ -4121,7 +4167,9 @@ void Simulator::LoadAcquireRCpcUnscaledOffsetHelper(const Instruction* instr) {
VIXL_ALIGNMENT_EXCEPTION();
}
- WriteRegister<T1>(rt, static_cast<T1>(MemRead<T2>(address)));
+ VIXL_DEFINE_OR_RETURN(value, MemRead<T2>(address));
+
+ WriteRegister<T1>(rt, static_cast<T1>(value));
// Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
@@ -4238,7 +4286,9 @@ void Simulator::VisitLoadStorePAC(const Instruction* instr) {
// Verify that the calculated address is available to the host.
VIXL_ASSERT(address == addr_ptr);
- WriteXRegister(dst, MemRead<uint64_t>(addr_ptr), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint64_t>(addr_ptr));
+
+ WriteXRegister(dst, value, NoRegLog);
unsigned access_size = 1 << 3;
LogRead(dst, GetPrintRegisterFormatForSize(access_size), addr_ptr);
}
@@ -4265,62 +4315,90 @@ void Simulator::LoadStoreHelper(const Instruction* instr,
int extend_to_size = 0;
LoadStoreOp op = static_cast<LoadStoreOp>(instr->Mask(LoadStoreMask));
switch (op) {
- case LDRB_w:
- WriteWRegister(srcdst, MemRead<uint8_t>(address), NoRegLog);
+ case LDRB_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint8_t>(address));
+ WriteWRegister(srcdst, value, NoRegLog);
extend_to_size = kWRegSizeInBytes;
break;
- case LDRH_w:
- WriteWRegister(srcdst, MemRead<uint16_t>(address), NoRegLog);
+ }
+ case LDRH_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint16_t>(address));
+ WriteWRegister(srcdst, value, NoRegLog);
extend_to_size = kWRegSizeInBytes;
break;
- case LDR_w:
- WriteWRegister(srcdst, MemRead<uint32_t>(address), NoRegLog);
+ }
+ case LDR_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint32_t>(address));
+ WriteWRegister(srcdst, value, NoRegLog);
extend_to_size = kWRegSizeInBytes;
break;
- case LDR_x:
- WriteXRegister(srcdst, MemRead<uint64_t>(address), NoRegLog);
+ }
+ case LDR_x: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint64_t>(address));
+ WriteXRegister(srcdst, value, NoRegLog);
extend_to_size = kXRegSizeInBytes;
break;
- case LDRSB_w:
- WriteWRegister(srcdst, MemRead<int8_t>(address), NoRegLog);
+ }
+ case LDRSB_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int8_t>(address));
+ WriteWRegister(srcdst, value, NoRegLog);
extend_to_size = kWRegSizeInBytes;
break;
- case LDRSH_w:
- WriteWRegister(srcdst, MemRead<int16_t>(address), NoRegLog);
+ }
+ case LDRSH_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int16_t>(address));
+ WriteWRegister(srcdst, value, NoRegLog);
extend_to_size = kWRegSizeInBytes;
break;
- case LDRSB_x:
- WriteXRegister(srcdst, MemRead<int8_t>(address), NoRegLog);
+ }
+ case LDRSB_x: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int8_t>(address));
+ WriteXRegister(srcdst, value, NoRegLog);
extend_to_size = kXRegSizeInBytes;
break;
- case LDRSH_x:
- WriteXRegister(srcdst, MemRead<int16_t>(address), NoRegLog);
+ }
+ case LDRSH_x: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int16_t>(address));
+ WriteXRegister(srcdst, value, NoRegLog);
extend_to_size = kXRegSizeInBytes;
break;
- case LDRSW_x:
- WriteXRegister(srcdst, MemRead<int32_t>(address), NoRegLog);
+ }
+ case LDRSW_x: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int32_t>(address));
+ WriteXRegister(srcdst, value, NoRegLog);
extend_to_size = kXRegSizeInBytes;
break;
- case LDR_b:
- WriteBRegister(srcdst, MemRead<uint8_t>(address), NoRegLog);
+ }
+ case LDR_b: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint8_t>(address));
+ WriteBRegister(srcdst, value, NoRegLog);
rt_is_vreg = true;
break;
- case LDR_h:
- WriteHRegister(srcdst, MemRead<uint16_t>(address), NoRegLog);
+ }
+ case LDR_h: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint16_t>(address));
+ WriteHRegister(srcdst, value, NoRegLog);
rt_is_vreg = true;
break;
- case LDR_s:
- WriteSRegister(srcdst, MemRead<float>(address), NoRegLog);
+ }
+ case LDR_s: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<float>(address));
+ WriteSRegister(srcdst, value, NoRegLog);
rt_is_vreg = true;
break;
- case LDR_d:
- WriteDRegister(srcdst, MemRead<double>(address), NoRegLog);
+ }
+ case LDR_d: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<double>(address));
+ WriteDRegister(srcdst, value, NoRegLog);
rt_is_vreg = true;
break;
- case LDR_q:
- WriteQRegister(srcdst, MemRead<qreg_t>(address), NoRegLog);
+ }
+ case LDR_q: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<qreg_t>(address));
+ WriteQRegister(srcdst, value, NoRegLog);
rt_is_vreg = true;
break;
+ }
case STRB_w:
MemWrite<uint8_t>(address, ReadWRegister(srcdst));
@@ -4432,36 +4510,48 @@ void Simulator::LoadStorePairHelper(const Instruction* instr,
// Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). We
// will print a more detailed log.
case LDP_w: {
- WriteWRegister(rt, MemRead<uint32_t>(address), NoRegLog);
- WriteWRegister(rt2, MemRead<uint32_t>(address2), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint32_t>(address));
+ VIXL_DEFINE_OR_RETURN(value2, MemRead<uint32_t>(address2));
+ WriteWRegister(rt, value, NoRegLog);
+ WriteWRegister(rt2, value2, NoRegLog);
break;
}
case LDP_s: {
- WriteSRegister(rt, MemRead<float>(address), NoRegLog);
- WriteSRegister(rt2, MemRead<float>(address2), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<float>(address));
+ VIXL_DEFINE_OR_RETURN(value2, MemRead<float>(address2));
+ WriteSRegister(rt, value, NoRegLog);
+ WriteSRegister(rt2, value2, NoRegLog);
rt_is_vreg = true;
break;
}
case LDP_x: {
- WriteXRegister(rt, MemRead<uint64_t>(address), NoRegLog);
- WriteXRegister(rt2, MemRead<uint64_t>(address2), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint64_t>(address));
+ VIXL_DEFINE_OR_RETURN(value2, MemRead<uint64_t>(address2));
+ WriteXRegister(rt, value, NoRegLog);
+ WriteXRegister(rt2, value2, NoRegLog);
break;
}
case LDP_d: {
- WriteDRegister(rt, MemRead<double>(address), NoRegLog);
- WriteDRegister(rt2, MemRead<double>(address2), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<double>(address));
+ VIXL_DEFINE_OR_RETURN(value2, MemRead<double>(address2));
+ WriteDRegister(rt, value, NoRegLog);
+ WriteDRegister(rt2, value2, NoRegLog);
rt_is_vreg = true;
break;
}
case LDP_q: {
- WriteQRegister(rt, MemRead<qreg_t>(address), NoRegLog);
- WriteQRegister(rt2, MemRead<qreg_t>(address2), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<qreg_t>(address));
+ VIXL_DEFINE_OR_RETURN(value2, MemRead<qreg_t>(address2));
+ WriteQRegister(rt, value, NoRegLog);
+ WriteQRegister(rt2, value2, NoRegLog);
rt_is_vreg = true;
break;
}
case LDPSW_x: {
- WriteXRegister(rt, MemRead<int32_t>(address), NoRegLog);
- WriteXRegister(rt2, MemRead<int32_t>(address2), NoRegLog);
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int32_t>(address));
+ VIXL_DEFINE_OR_RETURN(value2, MemRead<int32_t>(address2));
+ WriteXRegister(rt, value, NoRegLog);
+ WriteXRegister(rt2, value2, NoRegLog);
sign_extend = true;
break;
}
@@ -4549,7 +4639,8 @@ void Simulator::CompareAndSwapHelper(const Instruction* instr) {
// associated with that location, even if the compare subsequently fails.
local_monitor_.Clear();
- T data = MemRead<T>(address);
+ VIXL_DEFINE_OR_RETURN(data, MemRead<T>(address));
+
if (is_acquire) {
// Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
@@ -4596,8 +4687,8 @@ void Simulator::CompareAndSwapPairHelper(const Instruction* instr) {
// associated with that location, even if the compare subsequently fails.
local_monitor_.Clear();
- T data_low = MemRead<T>(address);
- T data_high = MemRead<T>(address2);
+ VIXL_DEFINE_OR_RETURN(data_low, MemRead<T>(address));
+ VIXL_DEFINE_OR_RETURN(data_high, MemRead<T>(address2));
if (is_acquire) {
// Approximate load-acquire by issuing a full barrier after the load.
@@ -4780,47 +4871,59 @@ void Simulator::VisitLoadStoreExclusive(const Instruction* instr) {
case LDXRB_w:
case LDAXRB_w:
case LDARB_w:
- case LDLARB:
- WriteWRegister(rt, MemRead<uint8_t>(address), NoRegLog);
+ case LDLARB: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint8_t>(address));
+ WriteWRegister(rt, value, NoRegLog);
reg_size = kWRegSizeInBytes;
break;
+ }
case LDXRH_w:
case LDAXRH_w:
case LDARH_w:
- case LDLARH:
- WriteWRegister(rt, MemRead<uint16_t>(address), NoRegLog);
+ case LDLARH: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint16_t>(address));
+ WriteWRegister(rt, value, NoRegLog);
reg_size = kWRegSizeInBytes;
break;
+ }
case LDXR_w:
case LDAXR_w:
case LDAR_w:
- case LDLAR_w:
- WriteWRegister(rt, MemRead<uint32_t>(address), NoRegLog);
+ case LDLAR_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint32_t>(address));
+ WriteWRegister(rt, value, NoRegLog);
reg_size = kWRegSizeInBytes;
break;
+ }
case LDXR_x:
case LDAXR_x:
case LDAR_x:
- case LDLAR_x:
- WriteXRegister(rt, MemRead<uint64_t>(address), NoRegLog);
+ case LDLAR_x: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint64_t>(address));
+ WriteXRegister(rt, value, NoRegLog);
reg_size = kXRegSizeInBytes;
break;
+ }
case LDXP_w:
- case LDAXP_w:
- WriteWRegister(rt, MemRead<uint32_t>(address), NoRegLog);
- WriteWRegister(rt2,
- MemRead<uint32_t>(address + element_size),
- NoRegLog);
+ case LDAXP_w: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint32_t>(address));
+ VIXL_DEFINE_OR_RETURN(value2,
+ MemRead<uint32_t>(address + element_size));
+ WriteWRegister(rt, value, NoRegLog);
+ WriteWRegister(rt2, value2, NoRegLog);
reg_size = kWRegSizeInBytes;
break;
+ }
case LDXP_x:
- case LDAXP_x:
- WriteXRegister(rt, MemRead<uint64_t>(address), NoRegLog);
- WriteXRegister(rt2,
- MemRead<uint64_t>(address + element_size),
- NoRegLog);
+ case LDAXP_x: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint64_t>(address));
+ VIXL_DEFINE_OR_RETURN(value2,
+ MemRead<uint64_t>(address + element_size));
+ WriteXRegister(rt, value, NoRegLog);
+ WriteXRegister(rt2, value2, NoRegLog);
reg_size = kXRegSizeInBytes;
break;
+ }
default:
VIXL_UNREACHABLE();
}
@@ -4922,7 +5025,7 @@ void Simulator::AtomicMemorySimpleHelper(const Instruction* instr) {
T value = ReadRegister<T>(rs);
- T data = MemRead<T>(address);
+ VIXL_DEFINE_OR_RETURN(data, MemRead<T>(address));
if (is_acquire) {
// Approximate load-acquire by issuing a full barrier after the load.
@@ -4991,7 +5094,8 @@ void Simulator::AtomicMemorySwapHelper(const Instruction* instr) {
CheckIsValidUnalignedAtomicAccess(rn, address, element_size);
- T data = MemRead<T>(address);
+ VIXL_DEFINE_OR_RETURN(data, MemRead<T>(address));
+
if (is_acquire) {
// Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
@@ -5020,7 +5124,9 @@ void Simulator::LoadAcquireRCpcHelper(const Instruction* instr) {
CheckIsValidUnalignedAtomicAccess(rn, address, element_size);
- WriteRegister<T>(rt, MemRead<T>(address));
+ VIXL_DEFINE_OR_RETURN(value, MemRead<T>(address));
+
+ WriteRegister<T>(rt, value);
// Approximate load-acquire by issuing a full barrier after the load.
__sync_synchronize();
@@ -5140,30 +5246,42 @@ void Simulator::VisitLoadLiteral(const Instruction* instr) {
switch (instr->Mask(LoadLiteralMask)) {
// Use NoRegLog to suppress the register trace (LOG_REGS, LOG_VREGS), then
// print a more detailed log.
- case LDR_w_lit:
- WriteWRegister(rt, MemRead<uint32_t>(address), NoRegLog);
+ case LDR_w_lit: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint32_t>(address));
+ WriteWRegister(rt, value, NoRegLog);
LogRead(rt, kPrintWReg, address);
break;
- case LDR_x_lit:
- WriteXRegister(rt, MemRead<uint64_t>(address), NoRegLog);
+ }
+ case LDR_x_lit: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint64_t>(address));
+ WriteXRegister(rt, value, NoRegLog);
LogRead(rt, kPrintXReg, address);
break;
- case LDR_s_lit:
- WriteSRegister(rt, MemRead<float>(address), NoRegLog);
+ }
+ case LDR_s_lit: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<float>(address));
+ WriteSRegister(rt, value, NoRegLog);
LogVRead(rt, kPrintSRegFP, address);
break;
- case LDR_d_lit:
- WriteDRegister(rt, MemRead<double>(address), NoRegLog);
+ }
+ case LDR_d_lit: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<double>(address));
+ WriteDRegister(rt, value, NoRegLog);
LogVRead(rt, kPrintDRegFP, address);
break;
- case LDR_q_lit:
- WriteQRegister(rt, MemRead<qreg_t>(address), NoRegLog);
+ }
+ case LDR_q_lit: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<qreg_t>(address));
+ WriteQRegister(rt, value, NoRegLog);
LogVRead(rt, kPrintReg1Q, address);
break;
- case LDRSW_x_lit:
- WriteXRegister(rt, MemRead<int32_t>(address), NoRegLog);
+ }
+ case LDRSW_x_lit: {
+ VIXL_DEFINE_OR_RETURN(value, MemRead<int32_t>(address));
+ WriteXRegister(rt, value, NoRegLog);
LogExtendingRead(rt, kPrintXReg, kWRegSizeInBytes, address);
break;
+ }
// Ignore prfm hint instructions.
case PRFM_lit:
@@ -6661,7 +6779,7 @@ void Simulator::SysOp_W(int op, int64_t val) {
// so temporarily disable MTE.
bool mte_enabled = MetaDataDepot::MetaDataMTE::IsActive();
MetaDataDepot::MetaDataMTE::SetActive(false);
- volatile uint8_t y = MemRead<uint8_t>(val);
+ volatile uint8_t y = *MemRead<uint8_t>(val);
MetaDataDepot::MetaDataMTE::SetActive(mte_enabled);
USE(y);
// TODO: Implement ZVA, GVA, GZVA.
@@ -8227,22 +8345,30 @@ void Simulator::NEONLoadStoreMultiStructHelper(const Instruction* instr,
switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) {
case NEON_LD1_4v:
case NEON_LD1_4v_post:
- ld1(vf, ReadVRegister(reg[3]), addr[3]);
+ if (!ld1(vf, ReadVRegister(reg[3]), addr[3])) {
+ return;
+ }
reg_count++;
VIXL_FALLTHROUGH();
case NEON_LD1_3v:
case NEON_LD1_3v_post:
- ld1(vf, ReadVRegister(reg[2]), addr[2]);
+ if (!ld1(vf, ReadVRegister(reg[2]), addr[2])) {
+ return;
+ }
reg_count++;
VIXL_FALLTHROUGH();
case NEON_LD1_2v:
case NEON_LD1_2v_post:
- ld1(vf, ReadVRegister(reg[1]), addr[1]);
+ if (!ld1(vf, ReadVRegister(reg[1]), addr[1])) {
+ return;
+ }
reg_count++;
VIXL_FALLTHROUGH();
case NEON_LD1_1v:
case NEON_LD1_1v_post:
- ld1(vf, ReadVRegister(reg[0]), addr[0]);
+ if (!ld1(vf, ReadVRegister(reg[0]), addr[0])) {
+ return;
+ }
break;
case NEON_ST1_4v:
case NEON_ST1_4v_post:
@@ -8312,12 +8438,14 @@ void Simulator::NEONLoadStoreMultiStructHelper(const Instruction* instr,
break;
case NEON_LD4_post:
case NEON_LD4:
- ld4(vf,
- ReadVRegister(reg[0]),
- ReadVRegister(reg[1]),
- ReadVRegister(reg[2]),
- ReadVRegister(reg[3]),
- addr[0]);
+ if (!ld4(vf,
+ ReadVRegister(reg[0]),
+ ReadVRegister(reg[1]),
+ ReadVRegister(reg[2]),
+ ReadVRegister(reg[3]),
+ addr[0])) {
+ return;
+ }
struct_parts = 4;
reg_count = 4;
break;
@@ -8492,9 +8620,13 @@ void Simulator::NEONLoadStoreSingleStructHelper(const Instruction* instr,
reg_count = 1;
if (replicating) {
VIXL_ASSERT(do_load);
- ld1r(vf, ReadVRegister(rt), addr);
+ if (!ld1r(vf, ReadVRegister(rt), addr)) {
+ return;
+ }
} else if (do_load) {
- ld1(vf, ReadVRegister(rt), lane, addr);
+ if (!ld1(vf, ReadVRegister(rt), lane, addr)) {
+ return;
+ }
} else {
st1(vf, ReadVRegister(rt), lane, addr);
}
@@ -8503,9 +8635,13 @@ void Simulator::NEONLoadStoreSingleStructHelper(const Instruction* instr,
reg_count = 2;
if (replicating) {
VIXL_ASSERT(do_load);
- ld2r(vf, ReadVRegister(rt), ReadVRegister(rt2), addr);
+ if (!ld2r(vf, ReadVRegister(rt), ReadVRegister(rt2), addr)) {
+ return;
+ }
} else if (do_load) {
- ld2(vf, ReadVRegister(rt), ReadVRegister(rt2), lane, addr);
+ if (!ld2(vf, ReadVRegister(rt), ReadVRegister(rt2), lane, addr)) {
+ return;
+ }
} else {
st2(vf, ReadVRegister(rt), ReadVRegister(rt2), lane, addr);
}
@@ -8514,18 +8650,22 @@ void Simulator::NEONLoadStoreSingleStructHelper(const Instruction* instr,
reg_count = 3;
if (replicating) {
VIXL_ASSERT(do_load);
- ld3r(vf,
- ReadVRegister(rt),
- ReadVRegister(rt2),
- ReadVRegister(rt3),
- addr);
+ if (!ld3r(vf,
+ ReadVRegister(rt),
+ ReadVRegister(rt2),
+ ReadVRegister(rt3),
+ addr)) {
+ return;
+ }
} else if (do_load) {
- ld3(vf,
- ReadVRegister(rt),
- ReadVRegister(rt2),
- ReadVRegister(rt3),
- lane,
- addr);
+ if (!ld3(vf,
+ ReadVRegister(rt),
+ ReadVRegister(rt2),
+ ReadVRegister(rt3),
+ lane,
+ addr)) {
+ return;
+ }
} else {
st3(vf,
ReadVRegister(rt),
@@ -8539,20 +8679,24 @@ void Simulator::NEONLoadStoreSingleStructHelper(const Instruction* instr,
reg_count = 4;
if (replicating) {
VIXL_ASSERT(do_load);
- ld4r(vf,
- ReadVRegister(rt),
- ReadVRegister(rt2),
- ReadVRegister(rt3),
- ReadVRegister(rt4),
- addr);
+ if (!ld4r(vf,
+ ReadVRegister(rt),
+ ReadVRegister(rt2),
+ ReadVRegister(rt3),
+ ReadVRegister(rt4),
+ addr)) {
+ return;
+ }
} else if (do_load) {
- ld4(vf,
- ReadVRegister(rt),
- ReadVRegister(rt2),
- ReadVRegister(rt3),
- ReadVRegister(rt4),
- lane,
- addr);
+ if (!ld4(vf,
+ ReadVRegister(rt),
+ ReadVRegister(rt2),
+ ReadVRegister(rt3),
+ ReadVRegister(rt4),
+ lane,
+ addr)) {
+ return;
+ }
} else {
st4(vf,
ReadVRegister(rt),
@@ -12057,7 +12201,8 @@ void Simulator::VisitSVELoadPredicateRegister(const Instruction* instr) {
uint64_t base = ReadXRegister(instr->GetRn(), Reg31IsStackPointer);
uint64_t address = base + multiplier * pl;
for (int i = 0; i < pl; i++) {
- pt.Insert(i, MemRead<uint8_t>(address + i));
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint8_t>(address + i));
+ pt.Insert(i, value);
}
LogPRead(instr->GetPt(), address);
break;
@@ -12078,7 +12223,8 @@ void Simulator::VisitSVELoadVectorRegister(const Instruction* instr) {
uint64_t base = ReadXRegister(instr->GetRn(), Reg31IsStackPointer);
uint64_t address = base + multiplier * vl;
for (int i = 0; i < vl; i++) {
- zt.Insert(i, MemRead<uint8_t>(address + i));
+ VIXL_DEFINE_OR_RETURN(value, MemRead<uint8_t>(address + i));
+ zt.Insert(i, value);
}
LogZRead(instr->GetRt(), address);
break;
@@ -14326,7 +14472,7 @@ void Simulator::SimulateCpyM(const Instruction* instr) {
}
while (xn--) {
- uint8_t temp = MemRead<uint8_t>(xs);
+ VIXL_DEFINE_OR_RETURN(temp, MemRead<uint8_t>(xs));
MemWrite<uint8_t>(xd, temp);
LogMemTransfer(xd, xs, temp);
xs += step;
@@ -14578,16 +14724,16 @@ void Simulator::DoRuntimeCall(const Instruction* instr) {
VIXL_STATIC_ASSERT(kRuntimeCallAddressSize == sizeof(uintptr_t));
// The appropriate `Simulator::SimulateRuntimeCall()` wrapper and the function
// to call are passed inlined in the assembly.
- uintptr_t call_wrapper_address =
- MemRead<uintptr_t>(instr + kRuntimeCallWrapperOffset);
- uintptr_t function_address =
- MemRead<uintptr_t>(instr + kRuntimeCallFunctionOffset);
- RuntimeCallType call_type = static_cast<RuntimeCallType>(
- MemRead<uint32_t>(instr + kRuntimeCallTypeOffset));
+ VIXL_DEFINE_OR_RETURN(call_wrapper_address,
+ MemRead<uintptr_t>(instr + kRuntimeCallWrapperOffset));
+ VIXL_DEFINE_OR_RETURN(function_address,
+ MemRead<uintptr_t>(instr + kRuntimeCallFunctionOffset));
+ VIXL_DEFINE_OR_RETURN(call_type,
+ MemRead<uint32_t>(instr + kRuntimeCallTypeOffset));
auto runtime_call_wrapper =
reinterpret_cast<void (*)(Simulator*, uintptr_t)>(call_wrapper_address);
- if (call_type == kCallRuntime) {
+ if (static_cast<RuntimeCallType>(call_type) == kCallRuntime) {
WriteRegister(kLinkRegCode,
instr->GetInstructionAtOffset(kRuntimeCallLength));
}
@@ -14618,7 +14764,7 @@ void Simulator::DoConfigureCPUFeatures(const Instruction* instr) {
// Read the kNone-terminated list of features.
CPUFeatures parameters;
while (true) {
- ElementType feature = MemRead<ElementType>(instr + offset);
+ VIXL_DEFINE_OR_RETURN(feature, MemRead<ElementType>(instr + offset));
offset += element_size;
if (feature == static_cast<ElementType>(CPUFeatures::kNone)) break;
parameters.Combine(static_cast<CPUFeatures::Feature>(feature));
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 5c2054d4..5b71fc10 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -68,6 +68,28 @@ namespace aarch64 {
class Simulator;
struct RuntimeCallStructHelper;
+enum class MemoryReadResult { Success = 0, Failure = 1 };
+
+// Try to read a piece of memory at the given address. Reading that memory
+// might raise a signal which, if handled by a custom signal handler, should
+// setup the native and simulated context in order to continue. Return whether
+// the memory access failed (i.e: raised a signal) or succeeded.
+MemoryReadResult TryMemoryRead(uintptr_t address, uintptr_t access_size);
+
+#ifdef VIXL_ENABLE_IMPLICIT_CHECKS
+// Access a byte of memory from the address at the given offset. If the memory
+// could be accessed then return MemoryReadResult::Success. If the memory could
+// not be accessed, and therefore raised a signal, setup the simulated context
+// and return MemoryReadResult::Failure.
+//
+// If a signal is raised then it is expected that the signal handler will place
+// MemoryReadResult::Failure in the native return register and the address of
+// _vixl_internal_ReadMemory_continue into the native instruction pointer.
+extern "C" MemoryReadResult _vixl_internal_ReadMemory(uintptr_t address,
+ uintptr_t offset);
+extern "C" uintptr_t _vixl_internal_ReadMemory_continue();
+#endif // VIXL_ENABLE_IMPLICIT_CHECKS
+
class SimStack {
public:
SimStack() {}
@@ -366,7 +388,7 @@ class Memory {
}
template <typename T, typename A>
- T Read(A address, Instruction const* pc = nullptr) const {
+ std::optional<T> Read(A address, Instruction const* pc = nullptr) const {
T value;
VIXL_STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
(sizeof(value) == 4) || (sizeof(value) == 8) ||
@@ -378,6 +400,10 @@ class Memory {
if (!IsMTETagsMatched(address, pc)) {
VIXL_ABORT_WITH_MSG("Tag mismatch.");
}
+ if (TryMemoryRead(reinterpret_cast<uintptr_t>(base), sizeof(value)) ==
+ MemoryReadResult::Failure) {
+ return std::nullopt;
+ }
memcpy(&value, base, sizeof(value));
return value;
}
@@ -398,7 +424,7 @@ class Memory {
}
template <typename A>
- uint64_t ReadUint(int size_in_bytes, A address) const {
+ std::optional<uint64_t> ReadUint(int size_in_bytes, A address) const {
switch (size_in_bytes) {
case 1:
return Read<uint8_t>(address);
@@ -414,7 +440,7 @@ class Memory {
}
template <typename A>
- int64_t ReadInt(int size_in_bytes, A address) const {
+ std::optional<int64_t> ReadInt(int size_in_bytes, A address) const {
switch (size_in_bytes) {
case 1:
return Read<int8_t>(address);
@@ -2013,7 +2039,7 @@ class Simulator : public DecoderVisitor {
}
template <typename T, typename A>
- T MemRead(A address) const {
+ std::optional<T> MemRead(A address) const {
Instruction const* pc = ReadPc();
return memory_.Read<T>(address, pc);
}
@@ -2025,12 +2051,12 @@ class Simulator : public DecoderVisitor {
}
template <typename A>
- uint64_t MemReadUint(int size_in_bytes, A address) const {
+ std::optional<uint64_t> MemReadUint(int size_in_bytes, A address) const {
return memory_.ReadUint(size_in_bytes, address);
}
template <typename A>
- int64_t MemReadInt(int size_in_bytes, A address) const {
+ std::optional<int64_t> MemReadInt(int size_in_bytes, A address) const {
return memory_.ReadInt(size_in_bytes, address);
}
@@ -2039,28 +2065,32 @@ class Simulator : public DecoderVisitor {
return memory_.Write(size_in_bytes, address, value);
}
- void LoadLane(LogicVRegister dst,
+ bool LoadLane(LogicVRegister dst,
VectorFormat vform,
int index,
uint64_t addr) const {
unsigned msize_in_bytes = LaneSizeInBytesFromFormat(vform);
- LoadUintToLane(dst, vform, msize_in_bytes, index, addr);
+ return LoadUintToLane(dst, vform, msize_in_bytes, index, addr);
}
- void LoadUintToLane(LogicVRegister dst,
+ bool LoadUintToLane(LogicVRegister dst,
VectorFormat vform,
unsigned msize_in_bytes,
int index,
uint64_t addr) const {
- dst.SetUint(vform, index, MemReadUint(msize_in_bytes, addr));
+ VIXL_DEFINE_OR_RETURN_FALSE(value, MemReadUint(msize_in_bytes, addr));
+ dst.SetUint(vform, index, value);
+ return true;
}
- void LoadIntToLane(LogicVRegister dst,
+ bool LoadIntToLane(LogicVRegister dst,
VectorFormat vform,
unsigned msize_in_bytes,
int index,
uint64_t addr) const {
- dst.SetInt(vform, index, MemReadInt(msize_in_bytes, addr));
+ VIXL_DEFINE_OR_RETURN_FALSE(value, MemReadInt(msize_in_bytes, addr));
+ dst.SetInt(vform, index, value);
+ return true;
}
void StoreLane(const LogicVRegister& src,
@@ -2079,7 +2109,9 @@ class Simulator : public DecoderVisitor {
return ReadCPURegister<T>(operand.GetCPURegister());
} else {
VIXL_ASSERT(operand.IsMemOperand());
- return MemRead<T>(ComputeMemOperandAddress(operand.GetMemOperand()));
+ auto res = MemRead<T>(ComputeMemOperandAddress(operand.GetMemOperand()));
+ VIXL_ASSERT(res);
+ return *res;
}
}
@@ -3132,6 +3164,41 @@ class Simulator : public DecoderVisitor {
Debugger* GetDebugger() const { return debugger_.get(); }
+#ifdef VIXL_ENABLE_IMPLICIT_CHECKS
+ // Returns true if the faulting instruction address (usually the program
+ // counter or instruction pointer) comes from an internal VIXL memory access.
+ // This can be used by signal handlers to check if a signal was raised from
+ // the simulator (via TryMemoryRead) before the actual read/write occurs.
+ bool IsSimulatedMemoryAccess(uintptr_t fault_pc) const {
+ return fault_pc == reinterpret_cast<uintptr_t>(&_vixl_internal_ReadMemory);
+ }
+
+ // Get the instruction address of the internal VIXL memory access continuation
+ // label. Signal handlers can resume execution at this address to return to
+ // TryMemoryRead which will continue simulation.
+ uintptr_t GetSignalReturnAddress() const {
+ return reinterpret_cast<uintptr_t>(&_vixl_internal_ReadMemory_continue);
+ }
+
+ // Replace the fault address reported by the kernel with the actual faulting
+ // address.
+ //
+ // This is required because TryMemoryRead reads a section of memory 1 byte at
+ // a time meaning the fault address reported may not be the base address of
+ // memory being accessed.
+ void ReplaceFaultAddress(siginfo_t* siginfo, void* context) {
+#ifdef __x86_64__
+ // The base address being accessed is passed in as the first argument to
+ // _vixl_internal_ReadMemory.
+ ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
+ siginfo->si_addr = reinterpret_cast<void*>(uc->uc_mcontext.gregs[REG_RDI]);
+#else
+ USE(siginfo);
+ USE(context);
+#endif // __x86_64__
+ }
+#endif // VIXL_ENABLE_IMPLICIT_CHECKS
+
protected:
const char* clr_normal;
const char* clr_flag_name;
@@ -3296,57 +3363,57 @@ class Simulator : public DecoderVisitor {
uint64_t op2,
int lane_size_in_bits) const;
- void ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr);
- void ld1(VectorFormat vform, LogicVRegister dst, int index, uint64_t addr);
- void ld1r(VectorFormat vform, LogicVRegister dst, uint64_t addr);
- void ld1r(VectorFormat vform,
+ bool ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr);
+ bool ld1(VectorFormat vform, LogicVRegister dst, int index, uint64_t addr);
+ bool ld1r(VectorFormat vform, LogicVRegister dst, uint64_t addr);
+ bool ld1r(VectorFormat vform,
VectorFormat unpack_vform,
LogicVRegister dst,
uint64_t addr,
bool is_signed = false);
- void ld2(VectorFormat vform,
+ bool ld2(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
uint64_t addr);
- void ld2(VectorFormat vform,
+ bool ld2(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
int index,
uint64_t addr);
- void ld2r(VectorFormat vform,
+ bool ld2r(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
uint64_t addr);
- void ld3(VectorFormat vform,
+ bool ld3(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
uint64_t addr);
- void ld3(VectorFormat vform,
+ bool ld3(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
int index,
uint64_t addr);
- void ld3r(VectorFormat vform,
+ bool ld3r(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
uint64_t addr);
- void ld4(VectorFormat vform,
+ bool ld4(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
LogicVRegister dst4,
uint64_t addr);
- void ld4(VectorFormat vform,
+ bool ld4(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
LogicVRegister dst4,
int index,
uint64_t addr);
- void ld4r(VectorFormat vform,
+ bool ld4r(VectorFormat vform,
LogicVRegister dst1,
LogicVRegister dst2,
LogicVRegister dst3,
@@ -4957,7 +5024,8 @@ class Simulator : public DecoderVisitor {
unsigned zt_code,
const LogicSVEAddressVector& addr);
// Load each active zt<i>[lane] from `addr.GetElementAddress(lane, ...)`.
- void SVEStructuredLoadHelper(VectorFormat vform,
+ // Returns false if a load failed.
+ bool SVEStructuredLoadHelper(VectorFormat vform,
const LogicPRegister& pg,
unsigned zt_code,
const LogicSVEAddressVector& addr,
diff --git a/src/globals-vixl.h b/src/globals-vixl.h
index 2efed250..b096c7f3 100644
--- a/src/globals-vixl.h
+++ b/src/globals-vixl.h
@@ -215,6 +215,18 @@ inline void USE(const T1&, const T2&, const T3&, const T4&) {}
} while (0)
#endif
+// Evaluate 'init' to an std::optional and return if it's empty. If 'init' is
+// not empty then define a variable 'name' with the value inside the
+// std::optional.
+#define VIXL_DEFINE_OR_RETURN(name, init) \
+ auto opt##name = init; \
+ if (!opt##name) return; \
+ auto name = *opt##name;
+#define VIXL_DEFINE_OR_RETURN_FALSE(name, init) \
+ auto opt##name = init; \
+ if (!opt##name) return false; \
+ auto name = *opt##name;
+
#if __cplusplus >= 201103L
#define VIXL_NO_RETURN [[noreturn]]
#else
diff --git a/test/aarch64/test-simulator-aarch64.cc b/test/aarch64/test-simulator-aarch64.cc
index 0a9dabed..1e2feaf0 100644
--- a/test/aarch64/test-simulator-aarch64.cc
+++ b/test/aarch64/test-simulator-aarch64.cc
@@ -5140,6 +5140,70 @@ TEST(RunFrom) {
3.0);
VIXL_CHECK(res_double == 6.0);
}
+
+#if defined(VIXL_ENABLE_IMPLICIT_CHECKS) && defined(__x86_64__)
+#include <signal.h>
+#include <ucontext.h>
+
+// Generate a function that creates a segfault by loading from an invalid
+// address.
+Instruction* GenerateSegFault(MacroAssembler* masm) {
+ masm->Reset();
+
+ // Reset the counter.
+ __ Mov(x1, 0);
+
+ // Perform a series of invalid memory reads.
+ __ Ldrb(w0, MemOperand());
+ __ Ldrh(w0, MemOperand());
+ __ Ldr(w0, MemOperand());
+ __ Ldr(x0, MemOperand());
+ __ Ldr(q0, MemOperand());
+
+ // Return the counter.
+ __ Mov(x0, x1);
+ __ Ret();
+
+ masm->FinalizeCode();
+ return masm->GetBuffer()->GetStartAddress<Instruction*>();
+}
+
+Simulator* gImplicitCheckSim;
+
+void HandleSegFault(int sig, siginfo_t* info, void* context) {
+ USE(sig);
+ USE(info);
+ Simulator* sim = gImplicitCheckSim;
+
+ // Did the signal come from the simulator?
+ ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
+ uintptr_t fault_pc = uc->uc_mcontext.gregs[REG_RIP];
+ VIXL_CHECK(sim->IsSimulatedMemoryAccess(fault_pc));
+
+ // Increment the counter (x1) each time we handle a signal.
+ int64_t counter = reinterpret_cast<int64_t>(sim->ReadXRegister(1));
+ sim->WriteXRegister(1, ++counter);
+
+ // Return to the VIXL memory access continuation point, which is also the
+ // next instruction, after this handler.
+ uc->uc_mcontext.gregs[REG_RIP] = sim->GetSignalReturnAddress();
+ // Return that the memory read failed.
+ uc->uc_mcontext.gregs[REG_RAX] = static_cast<greg_t>(MemoryReadResult::Failure);
+}
+
+TEST(ImplicitCheck) {
+ SETUP_WITH_FEATURES(CPUFeatures::kNEON);
+
+ gImplicitCheckSim = &simulator;
+ struct sigaction sa;
+ sa.sa_sigaction = HandleSegFault;
+ sigaction(SIGSEGV, &sa, NULL);
+
+ // Check that 5 segfaults were raised and dealt with.
+ int64_t result = simulator.RunFrom<int64_t>(GenerateSegFault(&masm));
+ VIXL_CHECK(result == 5);
+}
+#endif // __x86_64__
#endif
diff --git a/tools/code_coverage.log b/tools/code_coverage.log
index c27ab83a..3f459e44 100644
--- a/tools/code_coverage.log
+++ b/tools/code_coverage.log
@@ -21,4 +21,5 @@
1686666000 82.90% 97.57% 94.87%
1693487542 82.91% 97.57% 94.87%
1702052331 82.89% 97.59% 94.77%
-1707395574 82.89% 97.59% 94.77%
+1706691191 82.87% 97.59% 94.74%
+1707395574 82.89% 97.59% 94.77% \ No newline at end of file