diff options
author | Chris Jones <70633990+chris-jones-arm@users.noreply.github.com> | 2024-02-15 15:05:25 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-15 15:05:25 +0000 |
commit | 30e7bbdc37d8f6320056429b39e4896575e4ae64 (patch) | |
tree | 23319bf1613eecfcb32231f853418ba618aa2272 | |
parent | accc97f1681043a9fc006a8263ebaa36ab434c53 (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-- | SConstruct | 7 | ||||
-rw-r--r-- | src/aarch64/logic-aarch64.cc | 127 | ||||
-rw-r--r-- | src/aarch64/simulator-aarch64.cc | 406 | ||||
-rw-r--r-- | src/aarch64/simulator-aarch64.h | 122 | ||||
-rw-r--r-- | src/globals-vixl.h | 12 | ||||
-rw-r--r-- | test/aarch64/test-simulator-aarch64.cc | 64 | ||||
-rw-r--r-- | tools/code_coverage.log | 3 |
7 files changed, 533 insertions, 208 deletions
@@ -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 |