From ed7b5cc163668de285d6b012999e3ab20807d2bd Mon Sep 17 00:00:00 2001 From: NeverSightAI Date: Mon, 5 Oct 2026 12:14:27 -0700 Subject: [PATCH 1/2] Complete shared ARM64 LSE atomic effects and flag transfers --- .../workflows/windows-alignment-oracle.yml | 22 + docs/ar/architecture.md | 4 +- docs/ar/cpu-execution.md | 5 +- docs/ar/testing.md | 2 + docs/architecture.md | 4 +- docs/cpu-execution.md | 4 +- docs/de/architecture.md | 4 +- docs/de/cpu-execution.md | 5 +- docs/de/testing.md | 2 + docs/es/architecture.md | 4 +- docs/es/cpu-execution.md | 5 +- docs/es/testing.md | 2 + docs/fr/architecture.md | 4 +- docs/fr/cpu-execution.md | 5 +- docs/fr/testing.md | 2 + docs/it/architecture.md | 4 +- docs/it/cpu-execution.md | 5 +- docs/it/testing.md | 2 + docs/ja/architecture.md | 4 +- docs/ja/cpu-execution.md | 5 +- docs/ja/testing.md | 2 + docs/ko/architecture.md | 4 +- docs/ko/cpu-execution.md | 5 +- docs/ko/testing.md | 2 + docs/ru/architecture.md | 4 +- docs/ru/cpu-execution.md | 5 +- docs/ru/testing.md | 2 + docs/testing.md | 2 + docs/zh-CN/architecture.md | 4 +- docs/zh-CN/cpu-execution.md | 5 +- docs/zh-CN/testing.md | 2 + docs/zh-TW/architecture.md | 4 +- docs/zh-TW/cpu-execution.md | 5 +- docs/zh-TW/testing.md | 2 + lib/emulation/CMakeLists.txt | 7 +- lib/emulation/arch/aarch64/AArch64Atomic.cpp | 97 ++++ lib/emulation/arch/aarch64/AArch64Atomic.h | 42 ++ .../arch/aarch64/AArch64AtomicExecution.cpp | 138 +++++ .../aarch64/AArch64AtomicInstructions.def | 32 ++ .../arch/aarch64/AArch64AtomicMemory.cpp | 29 + .../arch/aarch64/AArch64AtomicMemory.h | 29 + .../arch/aarch64/AArch64Exclusive.cpp | 20 - lib/emulation/arch/aarch64/AArch64Exclusive.h | 20 +- .../aarch64/AArch64ExclusiveExecution.cpp | 4 +- .../aarch64/AArch64InstructionEffects.cpp | 5 +- lib/emulation/arch/aarch64/AArch64Machine.def | 2 +- .../arch/aarch64/CheckedAArch64Atomic.cpp | 37 ++ .../arch/aarch64/CheckedAArch64Backend.cpp | 11 +- .../arch/aarch64/CheckedAArch64Backend.h | 3 +- .../arch/aarch64/CheckedAArch64Exclusive.cpp | 21 - .../aarch64/CheckedAArch64Instructions.def | 7 +- ...Exclusive.cpp => UnicornAArch64Atomic.cpp} | 44 +- .../backends/unicorn/UnicornAArch64Atomic.h | 26 + .../backends/unicorn/UnicornBackend.cpp | 18 +- .../backends/unicorn/UnicornExclusive.h | 25 - .../process/WindowsProcessExceptions.cpp | 4 +- scripts/check_aarch64_atomics.py | 210 +++++++ scripts/tests/test_check_aarch64_atomics.py | 82 +++ third_party/unicorn | 2 +- unittests/emulation/AArch64AtomicCases.def | 350 ++++++++++++ unittests/emulation/AArch64AtomicTests.cpp | 539 ++++++++++++++++++ unittests/emulation/CMakeLists.txt | 1 + .../emulation/WindowsProcessFixtures.cmake | 15 + .../fixtures/AArch64AtomicOracle.def | 105 ++++ .../emulation/fixtures/aarch64_atomic.inc | 26 + .../fixtures/aarch64_atomic_oracle.c | 173 ++++++ 66 files changed, 2114 insertions(+), 152 deletions(-) create mode 100644 lib/emulation/arch/aarch64/AArch64Atomic.cpp create mode 100644 lib/emulation/arch/aarch64/AArch64Atomic.h create mode 100644 lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp create mode 100644 lib/emulation/arch/aarch64/AArch64AtomicInstructions.def create mode 100644 lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp create mode 100644 lib/emulation/arch/aarch64/AArch64AtomicMemory.h create mode 100644 lib/emulation/arch/aarch64/CheckedAArch64Atomic.cpp delete mode 100644 lib/emulation/arch/aarch64/CheckedAArch64Exclusive.cpp rename lib/emulation/backends/unicorn/{UnicornExclusive.cpp => UnicornAArch64Atomic.cpp} (68%) create mode 100644 lib/emulation/backends/unicorn/UnicornAArch64Atomic.h delete mode 100644 lib/emulation/backends/unicorn/UnicornExclusive.h create mode 100644 scripts/check_aarch64_atomics.py create mode 100644 scripts/tests/test_check_aarch64_atomics.py create mode 100644 unittests/emulation/AArch64AtomicCases.def create mode 100644 unittests/emulation/AArch64AtomicTests.cpp create mode 100644 unittests/emulation/fixtures/AArch64AtomicOracle.def create mode 100644 unittests/emulation/fixtures/aarch64_atomic.inc create mode 100644 unittests/emulation/fixtures/aarch64_atomic_oracle.c diff --git a/.github/workflows/windows-alignment-oracle.yml b/.github/workflows/windows-alignment-oracle.yml index 73f12e479e..774a2bc0a7 100644 --- a/.github/workflows/windows-alignment-oracle.yml +++ b/.github/workflows/windows-alignment-oracle.yml @@ -14,6 +14,11 @@ on: - 'unittests/emulation/AArch64ExclusiveCases.def' - 'unittests/emulation/fixtures/aarch64_exclusive*' - 'unittests/emulation/fixtures/AArch64ExclusiveOracle.def' + - 'scripts/check_aarch64_atomics.py' + - 'scripts/tests/test_check_aarch64_atomics.py' + - 'unittests/emulation/AArch64AtomicCases.def' + - 'unittests/emulation/fixtures/aarch64_atomic*' + - 'unittests/emulation/fixtures/AArch64AtomicOracle.def' push: branches: [dev] paths: @@ -27,6 +32,11 @@ on: - 'unittests/emulation/AArch64ExclusiveCases.def' - 'unittests/emulation/fixtures/aarch64_exclusive*' - 'unittests/emulation/fixtures/AArch64ExclusiveOracle.def' + - 'scripts/check_aarch64_atomics.py' + - 'scripts/tests/test_check_aarch64_atomics.py' + - 'unittests/emulation/AArch64AtomicCases.def' + - 'unittests/emulation/fixtures/aarch64_atomic*' + - 'unittests/emulation/fixtures/AArch64AtomicOracle.def' permissions: contents: read @@ -72,6 +82,18 @@ jobs: run: python -m unittest scripts.tests.test_check_aarch64_exclusives -v - name: Collect original ARM64 alignment and permission observations run: python scripts/check_aarch64_exclusives.py --output build-arm64-alignment + - name: Verify ARM64 atomic observation integrity rules + run: python -m unittest scripts.tests.test_check_aarch64_atomics -v + - name: Collect original ARM64 atomic observations + run: python scripts/check_aarch64_atomics.py --output build-arm64-atomics + - name: Preserve original ARM64 atomic observations + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: windows-atomics-arm64 + path: build-arm64-atomics/ + if-no-files-found: error + retention-days: 14 - name: Preserve original ARM64 native observations if: ${{ always() }} uses: actions/upload-artifact@v4 diff --git a/docs/ar/architecture.md b/docs/ar/architecture.md index 21e7fd6700..d031838d46 100644 --- a/docs/ar/architecture.md +++ b/docs/ar/architecture.md @@ -1315,7 +1315,9 @@ fail-closed عندما تكون دلالة التعليمة خارج التغط يمكن لـ `AuthenticatedSourceFrameLoads` عبور استدعاء موصل البيانات الوصفية عبر `swiftMetadataAccessorSourceCallHint`. يعيد مسارا التصريح استخدام الجدول الحالي المثبت بالمترجم، والمزوّد القوي الدقيق، وواجهة Swift ABI الكاملة للطلب واستجابة البيانات الوصفية. تبقى الاستجابة في سجلين وقواعد تغيير السجلات المتطايرة المعتادة محفوظة؛ وما زالت عمليات spill الخاصة تتطلب إثبات البايتات الواصلة المشترك. لا يمنح ذلك تخطيط القيم أو تأثيرات ذاكرة witness أو استعارة إطار أو نموذج تخصيص ديناميكي للمكدس. -يحدد `AArch64ExclusiveInstructions.def` الترميزات المقبولة، وينفذ `AArch64ExclusiveExecution` انتقالات السجلات والمراقب تحت قفل التنفيذ المادي. يحتفظ `RAMReservation` بهوية التخصيص ويتلقى إشعارات الكتابات المعتمدة من المضيف والعروض المحتفظ بها و`RAMTransaction` وتعليمات السلاسل وكتابات `ENTER` الجزئية السابقة للخطأ. لا تُبطل الكتابات المؤقتة والتراجع الحجز. التنفيذ مجهول نطاق الكتابة يُبطل الحجوزات القائمة، بينما يتتبع التنفيذ المصرح به النطاقات المنشورة فعليًا. تمسح أخطاء الضيف واعتراضات الخدمات وربط فضاء العناوين المراقب المحلي. تحتفظ مسارات `LDAR[B/H]` و`STLR[B/H]` الأصلية بعقد RAM ذي المحاذاة الطبيعية. يُبطل محوّل Unicorn البرمجي الحجوزات تحفظيًا في خطافات ما قبل الكتابة بعد قبول المراقبين. وبعد التخزين الحصري، يُبطل ترجمات جميع الأسماء البديلة القابلة للتنفيذ قبل متابعة التشغيل نفسه. يختار `AArch64ExclusiveAccess` سياسة المحاذاة: FEAT_LSE2 لملفات checked ونموذج المحرك لـ Unicorn البرمجي. تعتمد المطابقة على عرض المعامل ووحدة الحجز المادية. +يحدد `AArch64ExclusiveInstructions.def` الترميزات المقبولة، وينفذ `AArch64ExclusiveExecution` انتقالات السجلات والمراقب تحت قفل التنفيذ المادي. يحتفظ `RAMReservation` بهوية التخصيص ويتلقى إشعارات الكتابات المعتمدة من المضيف والعروض المحتفظ بها و`RAMTransaction` وتعليمات السلاسل وكتابات `ENTER` الجزئية السابقة للخطأ. لا تُبطل الكتابات المؤقتة والتراجع الحجز. التنفيذ مجهول نطاق الكتابة يُبطل الحجوزات القائمة، بينما يتتبع التنفيذ المصرح به النطاقات المنشورة فعليًا. تمسح أخطاء الضيف واعتراضات الخدمات وربط فضاء العناوين المراقب المحلي. تحتفظ مسارات `LDAR[B/H]` و`STLR[B/H]` الأصلية بعقد RAM ذي المحاذاة الطبيعية. يُبطل محوّل Unicorn البرمجي الحجوزات تحفظيًا في خطافات ما قبل الكتابة بعد قبول المراقبين. وبعد التخزين الحصري، يُبطل ترجمات جميع الأسماء البديلة القابلة للتنفيذ قبل متابعة التشغيل نفسه. يختار `AArch64AtomicAccess` سياسة المحاذاة: FEAT_LSE2 لملفات checked ونموذج المحرك لـ Unicorn البرمجي. تعتمد المطابقة على عرض المعامل ووحدة الحجز المادية. + +يملك `AArch64AtomicInstructions.def` قواعد قبول LSE، ويملك `AArch64AtomicExecution` الإكمال الذري. يشترك `AArch64AtomicMemory` مع العمليات الحصرية في المحاذاة والأخطاء ذات الأنواع؛ وتستخدم checked وجسر Unicorn البرمجي هذه الجهات المشتركة. تتحقق `objcNonEscapingBlockSourceFrameEffects` من الاستعارة المتزامنة لكتل المكدس باستخدام الاستعلام المشترك `sourceFrameCallArgumentStorage`. تعيد فحص التعليمات الأصلية والإرسال المؤهل الحالي والاستيراد القوي والترويسة والواصف وABI للاستدعاء وحقائق البايتات الواصلة. يحدد الواصف نطاقًا قابلًا للكتابة؛ لا تصبح بايتات الحشو مهيأة، وتبقى أنواع الحقول وأجسام الاستدعاء بحاجة إلى إثبات مستقل. تُرفض الملاحظات الدورية والتقاط مؤشرات الإطار الخاص، وتستخدم الاعتماديات جولات إثبات مكتملة. diff --git a/docs/ar/cpu-execution.md b/docs/ar/cpu-execution.md index 3aa0bc913d..afeeff331f 100644 --- a/docs/ar/cpu-execution.md +++ b/docs/ar/cpu-execution.md @@ -204,4 +204,7 @@ neverd cpu-capabilities \ [Setup, signing and native hardware validation (English)](../macos-hvf.md) -يدعم ARM64 checked تعليمات `LDXR/STXR` بعرض 8/16/32/64 بت، وأزواج `LDXP/STXP` بعرض 32/64 بت، وصيغ acquire/release و`CLREX`. تشترك KVM وWHP وUnicorn checked في مراقب على مستوى ISA يتتبع حجوزات مادية بحجم 16 بايت؛ ولا تمنع مخارج التنفيذ خطوة بخطوة تقدّم الحلقات. تُبطل الكتابات المعتمدة الحجز حتى إن بقيت البايتات كما هي، بما يشمل الأسماء البديلة والعروض المحتفظ بها. يحفظ الإيقاف الحالة غير المنشورة، ولا تلغي اللقطات الكتابات اللاحقة. تتبع العمليات الحصرية checked قواعد FEAT_LSE2: يُسمح بالمعامل غير المحاذى داخل كتلة محاذاة بحجم 16 بايت، ويؤدي عبور حدودها إلى `alignment`. تُطابق الكتابات ذات العرض نفسه وحدة الحجز المادية. تُفحص المحاذاة والصلاحيات قبل تقرير نجاح التخزين المشروط حتى عند انتهاء الحجز. لا يشمل هذا الملف MMIO أو تعليمات LSE الذرية الاختيارية أو SMP المتوازي. يستخدم Unicorn ARM64 هذا المراقب أيضًا ضمن عقد `Software`، بما في ذلك مشاركة RAM المادية بين وحدات CPU البرمجية ووحدات checked. يحتفظ عقد `Software` في Unicorn بنموذج المحاذاة الطبيعية للمحرك. +يدعم ARM64 checked تعليمات `LDXR/STXR` بعرض 8/16/32/64 بت، وأزواج `LDXP/STXP` بعرض 32/64 بت، وصيغ acquire/release و`CLREX`. تشترك KVM وWHP وUnicorn checked في مراقب على مستوى ISA يتتبع حجوزات مادية بحجم 16 بايت؛ ولا تمنع مخارج التنفيذ خطوة بخطوة تقدّم الحلقات. تُبطل الكتابات المعتمدة الحجز حتى إن بقيت البايتات كما هي، بما يشمل الأسماء البديلة والعروض المحتفظ بها. يحفظ الإيقاف الحالة غير المنشورة، ولا تلغي اللقطات الكتابات اللاحقة. تتبع العمليات الحصرية checked قواعد FEAT_LSE2: يُسمح بالمعامل غير المحاذى داخل كتلة محاذاة بحجم 16 بايت، ويؤدي عبور حدودها إلى `alignment`. تُطابق الكتابات ذات العرض نفسه وحدة الحجز المادية. تُفحص المحاذاة والصلاحيات قبل تقرير نجاح التخزين المشروط حتى عند انتهاء الحجز. لا يشمل هذا الملف MMIO أو SMP المتوازي. يستخدم Unicorn ARM64 هذا المراقب أيضًا ضمن عقد `Software`، بما في ذلك مشاركة RAM المادية بين وحدات CPU البرمجية ووحدات checked. يحتفظ عقد `Software` في Unicorn بنموذج المحاذاة الطبيعية للمحرك. + + +تدعم طبقة ISA المشتركة FEAT_LSE: ‏`CAS/CASP` و`SWP` و`LDADD/LDCLR/LDEOR/LDSET` وmin/max الموقعة وغير الموقعة، بعرض بايت ونصف كلمة وكلمة وكلمة مزدوجة، مع صيغ acquire/release. تتبع checked المحاذاة المذكورة أعلاه، بينما يحتفظ Unicorn `Software` بالمحاذاة الطبيعية. تراعي المقارنات عرض المعامل وتُوسّع القيم القديمة المعادة بأصفار. يتطلب CAS الفاشل إذن الكتابة أيضًا ويعيد كتابة القيمة القديمة، وهو خيار تسمح به Arm ويبطل الحجوزات المادية. ترى callbacks حالة CPU/RAM السابقة؛ ولا تنشر الإلغاءات والأخطاء المتزامنة آثارًا جزئية. يرتب قفل التنفيذ المادي وحدات CPU التعاونية دون توفير SMP متوازٍ أو عمليات MMIO ذرية. تنقل `MRS/MSR NZCV` أعلام الشرط الأربعة وفق قواعد البتات المحجوزة والسجل الصفري. \ No newline at end of file diff --git a/docs/ar/testing.md b/docs/ar/testing.md index 7d14b12723..6162ef28fe 100644 --- a/docs/ar/testing.md +++ b/docs/ar/testing.md @@ -1438,6 +1438,8 @@ build-release/bin/NeverDByteCellScalarizationTests يغطي `AArch64ExclusiveTests.cpp` العروض والأزواج وصيغ acquire/release وتداخل السجلات والأسماء البديلة والأخطاء واللقطات وإلغاء المراقبين أو فشلهم وتنافس وحدات CPU. ويغطي `RAMReservationTests.cpp` الكتابات المتطابقة وABA وإعادة التخصيص والتراجع وتأثير تعليمات السلاسل و`ENTER` في KVM/Unicorn. تنفذ عينات عمليات Windows ARM64 الأصلية حلقات حصرية. يشغّل `scripts/check_aarch64_exclusives.py` التعليمات الأصلية ويسجل استثناءات المحاذاة في Windows ARM64 CI. لا تثبت هذه المشاهدات تنفيذ خلفيات ARM64 KVM/WHP؛ وتبقى الملفات غير المتاحة مسجلة بوضوح كتغطية متجاوزة. تغطي الحالات نفسها Unicorn البرمجي والتداخل بين العقود والكتابات المتطابقة وABA والأسماء البديلة القابلة للتنفيذ ضمن التشغيل نفسه، إضافة إلى `DC ZVA` والإلغاء بواسطة المراقبين. يشغّل `windows-alignment-oracle.yml` مسبار ARM64 أيضًا: تغطي 1,320 مشاهدة كل إزاحة غير محاذاة وأربعة تسلسلات تحميل وتخزين وذاكرة قابلة للكتابة أو للقراءة فقط أو محظورة أو عابرة لحدود الصفحات. يحتفظ المسبار بعرض قيم السجلات الابتدائية كاملًا ويسجل أي كتابة جزئية معتمدة قبل الخطأ. يقارن `WindowsExclusiveProcessTests.cpp` عدد 1,320 مشاهدة أصلية لـ Windows ARM64 بالبصمات الأصلية في `WindowsExclusiveNative.def`. يطبّع مواضع الشفرة والبيانات فقط، مع إبقاء قيم السجلات وبيانات الاستثناءات وتأثيرات RAM. +يغطي `AArch64AtomicTests.cpp` عدد 168 ترميزًا مستقل التجميع لتعليمات LSE، وتداخل السجلات والمقارنات الموقعة والصلاحيات والإلغاء والحجوزات المادية ونقل NZCV. يجمع `scripts/check_aarch64_atomics.py` عدد 1,100 سجل أصلي من Windows ARM64 يتضمن النتائج وسياق الاستثناء ونطاق RAM كاملًا؛ وترفض اختبارات المحلل الأدلة الناقصة أو غير المتسقة. يظل تنفيذ KVM/WHP الأصلي بحاجة إلى تحقق مستقل. + تستخدم الأهداف الأصلية الثابتة بنيويًا آلية الجدولة الحالية مع التحقق من إمكانية التنفيذ مباشرة. ولا يحتفظ الهدف الرمزي الوحيد بالمحمول الوارد إلا بعد تعداد شامل. تتحقق اختبارات الانحدار من 128 انتقالًا ثابتًا ضمن ميزانية استعلامات التنفيذ الخطي، ومن 32 انتقالًا محسوبًا باستعلامي تعداد لكل انتقال، مع الحفاظ على بتات العنوان العليا الحرة ومجالات الفروع. تُرفض تغييرات الحالة الكاملة وغياب المحاذاة وحدّ الأهداف الصفري وميزانيات الاستعلامات أو التعليمات غير الكافية. تظل فحوص رفض الأهداف المتعددة والتعداد غير المكتمل مطلوبة. يحتفظ الفرع الأصلي بالمجال الوارد على حافة واحدة فقط بعد اكتمال إثبات UNSAT الذي يستبعد الحافة الأخرى. تتحقق الاختبارات من 32 انتقالًا شرطيًا في كلا الاتجاهين ضمن 512 بوابة للمحلّل، ومن ميزانيات الاستعلام الدقيقة والناقصة باستعلام واحد ونفاد البوابات. يجب رفض المحاذاة المعدّلة أو المفقودة والمقارنات المعكوسة والحالة النهائية المعدّلة؛ وتظل اختبارات التحكم غير المعرّف الاعتباطي وإمكانية تنفيذ كلتا الحافتين مطلوبة. diff --git a/docs/architecture.md b/docs/architecture.md index 4bece07f1c..07e6bc2a0e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2001,7 +2001,9 @@ this boundary for every OS and transport. Guarded translations and branch-type state propagation remain outside the contract; no loader or OS shortcut may silently turn a guarded execution request into this mode. -`AArch64ExclusiveInstructions.def` owns exclusive encoding admission; `AArch64ExclusiveExecution` completes register and monitor transitions under the physical execution lease. `RAMReservation` retains allocation identity and receives committed-write notifications from host writes, retained views, `RAMTransaction`, string operations and fault-prefix `ENTER` stores. Temporary writes and rollback do not invalidate reservations. Opaque execution invalidates outstanding reservations; declared execution tracks the actual committed footprint. Guest faults, service traps and address-space binding clear the local monitor. The original `LDAR[B/H]` and `STLR[B/H]` transport paths retain their naturally aligned RAM contract. The Unicorn software adapter conservatively invalidates reservations at accepted pre-write hooks, and retires every executable alias after an exclusive store before continuing in the same run. `AArch64ExclusiveAccess` selects the alignment policy; checked profiles select FEAT_LSE2, while software Unicorn preserves its engine feature model. Reservation matching uses the same operand width and physical granule. +`AArch64ExclusiveInstructions.def` owns exclusive encoding admission; `AArch64ExclusiveExecution` completes register and monitor transitions under the physical execution lease. `RAMReservation` retains allocation identity and receives committed-write notifications from host writes, retained views, `RAMTransaction`, string operations and fault-prefix `ENTER` stores. Temporary writes and rollback do not invalidate reservations. Opaque execution invalidates outstanding reservations; declared execution tracks the actual committed footprint. Guest faults, service traps and address-space binding clear the local monitor. The original `LDAR[B/H]` and `STLR[B/H]` transport paths retain their naturally aligned RAM contract. The Unicorn software adapter conservatively invalidates reservations at accepted pre-write hooks, and retires every executable alias after an exclusive store before continuing in the same run. `AArch64AtomicAccess` selects the alignment policy; checked profiles select FEAT_LSE2, while software Unicorn preserves its engine feature model. Reservation matching uses the same operand width and physical granule. + +`AArch64AtomicInstructions.def` owns LSE admission, `AArch64AtomicExecution` owns atomic completion, and `AArch64AtomicMemory` shares alignment and typed fault policy with exclusives. Both checked transports and the Unicorn software bridge use these owners. `AArch64InstructionEffects` owns scalar and FP/SIMD single/pair RAM footprints, including operands up to 128 bits. The shared address space validates every page before CPU entry; `RAMTransaction` commits only complete declared physical writes. A 128-bit write observer receives two ordered 64-bit words before effects. Stops and faults preserve RAM, vectors and writeback. Numeric Xn/Vn overlap is valid; wrapping pair footprints are rejected. Baseline NEON `LD1`–`LD4`/`ST1`–`ST4` lane and multiple-structure forms, and `LD1R`–`LD4R`, use the same owner. Each access describes one element in memory order, including register-list repetition, interleaving and V31-to-V0 wrap. Replication reads only one element per register. Exact encodings reject reserved sizes and address wrap before observers; original processor execution supplies lane retention, upper clearing and post-index writeback. All writes remain inside one RAM transaction. The scope is ordinary little-endian RAM; it adds no SVE, device transfers or parallel SMP. `NeverDAArch64MemoryTests` uses independent `AArch64CrossPageCases.def` and `AArch64VectorMemoryCases.def` encodings. diff --git a/docs/cpu-execution.md b/docs/cpu-execution.md index 2c743db68d..f098c10d46 100644 --- a/docs/cpu-execution.md +++ b/docs/cpu-execution.md @@ -407,7 +407,9 @@ guest OS. Unlisted HINTs and system-control writes remain unsupported. The original baseline `LDAR`, `LDARB`, `LDARH`, `STLR`, `STLRB` and `STLRH` execute through the selected transport on naturally aligned ordinary RAM. Their narrow loads zero-extend; invalid alignment and optional ordered encodings remain unsupported. -Checked ARM64 supports 8/16/32/64-bit `LDXR/STXR`, 32/64-bit register pairs `LDXP/STXP`, their acquire/release variants, and `CLREX`. A shared ISA monitor models 16-byte physical reservation granules for KVM, WHP and checked Unicorn; transport single-step exits do not destroy loop progress. Committed writes invalidate reservations even when bytes remain unchanged, including writes through aliases and retained views. Stops preserve unpublished state; snapshots cannot undo intervening writes. Checked exclusives use FEAT_LSE2 alignment: unaligned operands within one 16-byte aligned block execute; crossing that block raises an `alignment` fault. Equal-width stores match the reserved physical granule. Alignment and permissions are checked before conditional-store success, including when the monitor has expired. MMIO, optional LSE atomics and parallel SMP remain outside this profile. ARM64 Unicorn also uses this monitor in the `Software` contract, including mixed software/checked CPUs sharing physical RAM. See the [Arm instruction definitions](https://documentation-service.arm.com/static/67e40f3398aa3c3b6eea6a85). Unicorn’s `Software` profile retains its engine’s natural-alignment model. +Checked ARM64 supports 8/16/32/64-bit `LDXR/STXR`, 32/64-bit register pairs `LDXP/STXP`, their acquire/release variants, and `CLREX`. A shared ISA monitor models 16-byte physical reservation granules for KVM, WHP and checked Unicorn; transport single-step exits do not destroy loop progress. Committed writes invalidate reservations even when bytes remain unchanged, including writes through aliases and retained views. Stops preserve unpublished state; snapshots cannot undo intervening writes. Checked exclusives use FEAT_LSE2 alignment: unaligned operands within one 16-byte aligned block execute; crossing that block raises an `alignment` fault. Equal-width stores match the reserved physical granule. Alignment and permissions are checked before conditional-store success, including when the monitor has expired. MMIO and parallel SMP remain outside this profile. ARM64 Unicorn also uses this monitor in the `Software` contract, including mixed software/checked CPUs sharing physical RAM. See the [Arm instruction definitions](https://documentation-service.arm.com/static/67e40f3398aa3c3b6eea6a85). Unicorn’s `Software` profile retains its engine’s natural-alignment model. + +The same shared ISA layer supports FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` and signed/unsigned min/max, including byte/halfword/word/doubleword and acquire/release variants. Checked profiles use the alignment policy above; Unicorn `Software` uses natural alignment. Comparisons use the operand width, while returned old values zero-extend. Failed CAS still requires write permission and selects Arm’s permitted old-value writeback, invalidating physical reservations. Callbacks see old CPU/RAM state; cancellation and synchronous faults publish no partial atomic effects. The physical execution lease orders cooperative CPUs; this does not add parallel SMP or MMIO atomics. `MRS/MSR NZCV` transfers the four condition flags with architectural reserved-bit and zero-register behavior. `AArch64InstructionEffects` owns scalar and FP/SIMD single/pair RAM footprints, including operands up to 128 bits. The shared address space validates every page before CPU entry; `RAMTransaction` commits only complete declared physical writes. A 128-bit write observer receives two ordered 64-bit words before effects. Stops and faults preserve RAM, vectors and writeback. Numeric Xn/Vn overlap is valid; wrapping pair footprints are rejected. `NeverDAArch64MemoryTests` uses independent `AArch64CrossPageCases.def` and `AArch64VectorMemoryCases.def` encodings. diff --git a/docs/de/architecture.md b/docs/de/architecture.md index d54eeede39..7c308a40d2 100644 --- a/docs/de/architecture.md +++ b/docs/de/architecture.md @@ -1427,7 +1427,9 @@ Derselbe Frame-Effekt-Eigentümer behandelt die exakte starke Brücke `CGRectApp `AuthenticatedSourceFrameLoads` kann über `swiftMetadataAccessorSourceCallHint` einen Metadatenzugriff überqueren. Beide Deklarationswege verwenden den vorhandenen, vom Compiler belegten Katalog, den exakten starken Provider und die vollständige Swift-ABI für Anfrage und Metadatenantwort. Die Antwort in zwei Registern und die normalen Änderungen flüchtiger Register bleiben erhalten; private Spills benötigen weiterhin den gemeinsamen Nachweis der erreichenden Bytes. Dies liefert weder Wertlayout noch Witness-Speichereffekte, Frame-Ausleihe oder ein Modell dynamischer Stack-Allokation. -`AArch64ExclusiveInstructions.def` definiert zugelassene Kodierungen; `AArch64ExclusiveExecution` aktualisiert Register und Monitor unter der physischen Ausführungssperre. `RAMReservation` hält die Allokationsidentität und erhält Meldungen bestätigter Schreibzugriffe von Host, gehaltenen Sichten, `RAMTransaction`, String-Befehlen und partiellen `ENTER`-Stores vor einem Fehler. Temporäre Zugriffe und Rollback invalidieren keine Reservierungen. Undurchsichtige Ausführung invalidiert vorhandene Reservierungen; deklarierte Ausführung verfolgt tatsächlich bestätigte Bereiche. Gastfehler, Service-Traps und Adressraumbindung löschen den lokalen Monitor. Die ursprünglichen `LDAR[B/H]`- und `STLR[B/H]`-Pfade behalten ihren Vertrag für natürlich ausgerichtetes RAM. Der Unicorn-Softwareadapter verwirft Reservierungen vorsorglich in Schreib-Hooks, nachdem Beobachter den Zugriff zugelassen haben. Nach exklusiven Stores verwirft er alle Übersetzungen ausführbarer Aliase, bevor derselbe Lauf fortgesetzt wird. `AArch64ExclusiveAccess` wählt die Ausrichtung: FEAT_LSE2 für checked und das Engine-Modell für Software-Unicorn. Abgeglichen werden Operandenbreite und physischer Reservierungsbereich. +`AArch64ExclusiveInstructions.def` definiert zugelassene Kodierungen; `AArch64ExclusiveExecution` aktualisiert Register und Monitor unter der physischen Ausführungssperre. `RAMReservation` hält die Allokationsidentität und erhält Meldungen bestätigter Schreibzugriffe von Host, gehaltenen Sichten, `RAMTransaction`, String-Befehlen und partiellen `ENTER`-Stores vor einem Fehler. Temporäre Zugriffe und Rollback invalidieren keine Reservierungen. Undurchsichtige Ausführung invalidiert vorhandene Reservierungen; deklarierte Ausführung verfolgt tatsächlich bestätigte Bereiche. Gastfehler, Service-Traps und Adressraumbindung löschen den lokalen Monitor. Die ursprünglichen `LDAR[B/H]`- und `STLR[B/H]`-Pfade behalten ihren Vertrag für natürlich ausgerichtetes RAM. Der Unicorn-Softwareadapter verwirft Reservierungen vorsorglich in Schreib-Hooks, nachdem Beobachter den Zugriff zugelassen haben. Nach exklusiven Stores verwirft er alle Übersetzungen ausführbarer Aliase, bevor derselbe Lauf fortgesetzt wird. `AArch64AtomicAccess` wählt die Ausrichtung: FEAT_LSE2 für checked und das Engine-Modell für Software-Unicorn. Abgeglichen werden Operandenbreite und physischer Reservierungsbereich. + +`AArch64AtomicInstructions.def` besitzt die LSE-Zulassung, `AArch64AtomicExecution` den atomaren Abschluss. `AArch64AtomicMemory` teilt Ausrichtung und typisierte Fehler mit exklusiven Zugriffen; Checked-Transporte und die Unicorn-Softwarebrücke nutzen dieselben Zuständigkeiten. `objcNonEscapingBlockSourceFrameEffects` authentifiziert synchrone Ausleihen von Stack-Blöcken mit der gemeinsamen Abfrage `sourceFrameCallArgumentStorage`. Geprüft werden ursprüngliche Maschinenbefehle, aktueller qualifizierter Dispatch, starker Import, vollständiger Header und Deskriptor, Callback-ABI und ankommende Byte-Fakten. Der Deskriptor begrenzt schreibbare Zugriffe; Padding gilt weiterhin nicht als initialisiert. Feldtypen und Callback-Rümpfe benötigen eigene Quellnachweise. Zyklische Beobachtungen und erfasste private Frame-Zeiger werden abgelehnt; Abhängigkeiten verwenden abgeschlossene Beweisrunden. diff --git a/docs/de/cpu-execution.md b/docs/de/cpu-execution.md index c6d489b87d..64d78e2cd8 100644 --- a/docs/de/cpu-execution.md +++ b/docs/de/cpu-execution.md @@ -204,4 +204,7 @@ Geprüftes Unicorn verwendet `MachineRunControl`: Ein Zeitrahmen umfasst ARM64-P [Setup, signing and native hardware validation (English)](../macos-hvf.md) -Checked ARM64 unterstützt `LDXR/STXR` mit 8/16/32/64 Bit, Registerpaare `LDXP/STXP` mit 32/64 Bit, Acquire/Release-Varianten und `CLREX`. KVM, WHP und Checked Unicorn teilen einen ISA-Monitor mit physischen Reservierungsgranulen von 16 Byte; Einzelschritt-Ausstiege verhindern keinen Schleifenfortschritt. Bestätigte Schreibzugriffe invalidieren Reservierungen auch bei unveränderten Bytes, einschließlich Aliasen und gehaltenen Sichten. Stopps bewahren unveröffentlichte Zustände; Snapshots können zwischenzeitliche Schreibzugriffe nicht zurücknehmen. Checked-Exklusivzugriffe folgen FEAT_LSE2: nicht ausgerichtete Operanden innerhalb eines ausgerichteten 16-Byte-Blocks sind zulässig; das Überschreiten erzeugt `alignment`. Gleich breite bedingte Stores passen zum reservierten physischen Reservierungsbereich. Ausrichtung und Rechte werden vor dem Ergebnis des bedingten Speicherns geprüft, auch bei ungültiger Reservierung. MMIO, optionale LSE-Atomics und paralleles SMP bleiben ausgeschlossen. ARM64 Unicorn nutzt diesen Monitor auch im Vertrag `Software`, einschließlich gemeinsam genutzten physischen RAMs zwischen Software- und checked-CPUs. Unicorn behält im Vertrag `Software` das natürliche Ausrichtungsmodell seiner Engine bei. +Checked ARM64 unterstützt `LDXR/STXR` mit 8/16/32/64 Bit, Registerpaare `LDXP/STXP` mit 32/64 Bit, Acquire/Release-Varianten und `CLREX`. KVM, WHP und Checked Unicorn teilen einen ISA-Monitor mit physischen Reservierungsgranulen von 16 Byte; Einzelschritt-Ausstiege verhindern keinen Schleifenfortschritt. Bestätigte Schreibzugriffe invalidieren Reservierungen auch bei unveränderten Bytes, einschließlich Aliasen und gehaltenen Sichten. Stopps bewahren unveröffentlichte Zustände; Snapshots können zwischenzeitliche Schreibzugriffe nicht zurücknehmen. Checked-Exklusivzugriffe folgen FEAT_LSE2: nicht ausgerichtete Operanden innerhalb eines ausgerichteten 16-Byte-Blocks sind zulässig; das Überschreiten erzeugt `alignment`. Gleich breite bedingte Stores passen zum reservierten physischen Reservierungsbereich. Ausrichtung und Rechte werden vor dem Ergebnis des bedingten Speicherns geprüft, auch bei ungültiger Reservierung. MMIO und paralleles SMP bleiben ausgeschlossen. ARM64 Unicorn nutzt diesen Monitor auch im Vertrag `Software`, einschließlich gemeinsam genutzten physischen RAMs zwischen Software- und checked-CPUs. Unicorn behält im Vertrag `Software` das natürliche Ausrichtungsmodell seiner Engine bei. + + +Dieselbe ISA-Schicht unterstützt FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` sowie vorzeichenbehaftetes und vorzeichenloses Min/Max für Byte, Halbwort, Wort und Doppelwort einschließlich Acquire/Release. Checked verwendet die obige Ausrichtung; Unicorn `Software` behält natürliche Ausrichtung. Vergleiche beachten die Operandenbreite, zurückgegebene Altwerte werden nullerweitert. Ein erfolgloses CAS benötigt ebenfalls Schreibrechte und schreibt den Altwert zurück: Diese von Arm erlaubte Wahl invalidiert physische Reservierungen. Callbacks sehen den alten CPU/RAM-Zustand; Abbruch und synchrone Fehler veröffentlichen keine Teileffekte. Die physische Ausführungssperre ordnet kooperative CPUs, ohne paralleles SMP oder MMIO-Atomics bereitzustellen. `MRS/MSR NZCV` überträgt die vier Bedingungsflags gemäß den Regeln für reservierte Bits und das Nullregister. \ No newline at end of file diff --git a/docs/de/testing.md b/docs/de/testing.md index 2953c9089d..caca68ac49 100644 --- a/docs/de/testing.md +++ b/docs/de/testing.md @@ -1531,6 +1531,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` prüft Breiten, Paare, Acquire/Release, Registerüberlappung, Aliase, Fehler, Snapshots, Abbruch oder Fehler von Beobachtern sowie konkurrierende CPUs. `RAMReservationTests.cpp` prüft identische Schreibwerte, ABA, Wiederverwendung, Rollback sowie String- und `ENTER`-Interferenz auf KVM/Unicorn. Originale Windows-ARM64-Prozessdateien führen exklusive Schleifen aus. `scripts/check_aarch64_exclusives.py` erfasst Originalbefehle und Ausrichtungsfehler auf Windows-ARM64-CI. Diese nativen Beobachtungen belegen keine Ausführung der ARM64-KVM/WHP-Backends; nicht verfügbare Profile werden ausdrücklich übersprungen. Dieselben Fälle prüfen Software-Unicorn, vertragsübergreifende Zugriffe, gleichwertige und ABA-Schreibvorgänge, ausführbare Aliase innerhalb eines Laufs sowie `DC ZVA` und den Abbruch durch Beobachter. `windows-alignment-oracle.yml` führt auch den ARM64-Probelauf aus: 1.320 Beobachtungen decken jeden nicht ausgerichteten Offset, vier Lade-/Speicherfolgen sowie schreibbaren, schreibgeschützten, unzugänglichen und seitenübergreifenden Speicher ab. Registerstartwerte behalten ihre volle Bitbreite; vor einem Fehler abgeschlossene Teilzugriffe werden aufgezeichnet. `WindowsExclusiveProcessTests.cpp` gleicht 1.320 ursprüngliche Windows-ARM64-Beobachtungen mit den nativen Prüfsummen in `WindowsExclusiveNative.def` ab. Nur Code- und Datenadressen werden normalisiert; Register, Ausnahmemetadaten und RAM-Effekte bleiben erhalten. +`AArch64AtomicTests.cpp` prüft 168 unabhängig assemblierte LSE-Kodierungen, Registeraliase, vorzeichenbehaftete Vergleiche, Rechte, Abbruch, physische Reservierungen und NZCV-Transfers. `scripts/check_aarch64_atomics.py` sammelt 1.100 originale Windows-ARM64-Datensätze mit vollständigen Ergebnissen, Ausnahmekontexten und RAM-Bereichen; Parsertests weisen fehlende oder widersprüchliche Belege zurück. Native KVM/WHP-Ausführung muss gesondert validiert werden. + Strukturell konstante native Ziele verwenden direkt die bestehende Planung mit Machbarkeitsprüfung. Ein symbolisches Einzelziel behält das eingehende Prädikat erst nach vollständiger Aufzählung bei. Regressionen prüfen 128 konstante Übertragungen mit dem Abfragebudget geradliniger Ausführung und 32 berechnete Übertragungen mit zwei Aufzählungsabfragen pro Übertragung; freie obere Adressbits und Zweigdomänen bleiben erhalten. Geänderte Gesamtzustände, fehlende Ausrichtung, ein Ziellimit von null und zu kleine Abfrage- oder Befehlsbudgets werden abgelehnt. Bestehende Ablehnungstests für mehrere Ziele und unvollständige Aufzählung bleiben erforderlich. Ein nativer Zweig behält die eingehende Domäne auf einer Kante erst bei, nachdem ein abgeschlossener UNSAT-Beweis die andere Kante ausschließt. Tests prüfen 32 bedingte Übertragungen in beiden Richtungen mit 512 Solver-Gattern, genaue und um eins verkürzte Abfragebudgets sowie erschöpfte Gatter. Geänderte oder fehlende Ausrichtung, umgekehrte Vergleiche und geänderte Endzustände müssen abgelehnt werden; bestehende Tests für beliebige undefinierte Steuerwerte und zwei mögliche Kanten bleiben erforderlich. diff --git a/docs/es/architecture.md b/docs/es/architecture.md index 7ac6c3d87e..2a60008453 100644 --- a/docs/es/architecture.md +++ b/docs/es/architecture.md @@ -1426,7 +1426,9 @@ El mismo propietario de efectos del marco trata por separado el puente fuerte ex `AuthenticatedSourceFrameLoads` puede atravesar una llamada al accesor de metadatos mediante `swiftMetadataAccessorSourceCallHint`. Las dos rutas de declaración reutilizan el catálogo existente observado por el compilador, el proveedor fuerte exacto y la ABI Swift completa de solicitud y respuesta de metadatos. Se conservan la respuesta en dos registros y las alteraciones normales de registros volátiles; los spills privados siguen requiriendo la prueba compartida de bytes alcanzables. Esto no concede distribución de valores, efectos de memoria de witnesses, préstamos del frame ni un modelo de asignación dinámica de pila. -`AArch64ExclusiveInstructions.def` define las codificaciones admitidas; `AArch64ExclusiveExecution` cambia registros y monitor bajo el bloqueo físico. `RAMReservation` conserva la identidad de asignación y recibe notificaciones de escrituras confirmadas del host, vistas retenidas, `RAMTransaction`, instrucciones de cadenas y escrituras parciales de `ENTER` previas al fallo. Las escrituras temporales y su reversión no invalidan reservas. La ejecución opaca invalida las reservas existentes; la ejecución declarada sigue los intervalos realmente publicados. Fallos del invitado, trampas de servicio y vinculación de espacios de direcciones borran el monitor local. Los caminos originales `LDAR[B/H]` y `STLR[B/H]` conservan el contrato de RAM naturalmente alineada. El adaptador de software de Unicorn invalida conservadoramente las reservas en los hooks previos a escrituras aceptadas por los observadores. Tras una escritura exclusiva, retira las traducciones de todos los alias ejecutables antes de continuar la misma ejecución. `AArch64ExclusiveAccess` selecciona la alineación: FEAT_LSE2 en checked y el modelo del motor en Unicorn de software. La coincidencia exige el mismo ancho y gránulo físico. +`AArch64ExclusiveInstructions.def` define las codificaciones admitidas; `AArch64ExclusiveExecution` cambia registros y monitor bajo el bloqueo físico. `RAMReservation` conserva la identidad de asignación y recibe notificaciones de escrituras confirmadas del host, vistas retenidas, `RAMTransaction`, instrucciones de cadenas y escrituras parciales de `ENTER` previas al fallo. Las escrituras temporales y su reversión no invalidan reservas. La ejecución opaca invalida las reservas existentes; la ejecución declarada sigue los intervalos realmente publicados. Fallos del invitado, trampas de servicio y vinculación de espacios de direcciones borran el monitor local. Los caminos originales `LDAR[B/H]` y `STLR[B/H]` conservan el contrato de RAM naturalmente alineada. El adaptador de software de Unicorn invalida conservadoramente las reservas en los hooks previos a escrituras aceptadas por los observadores. Tras una escritura exclusiva, retira las traducciones de todos los alias ejecutables antes de continuar la misma ejecución. `AArch64AtomicAccess` selecciona la alineación: FEAT_LSE2 en checked y el modelo del motor en Unicorn de software. La coincidencia exige el mismo ancho y gránulo físico. + +`AArch64AtomicInstructions.def` controla la admisión LSE, `AArch64AtomicExecution` su finalización atómica y `AArch64AtomicMemory` comparte alineación y fallos tipados con los exclusivos. Los transportes checked y el puente de software Unicorn usan estas mismas autoridades. `objcNonEscapingBlockSourceFrameEffects` autentica préstamos síncronos de bloques de pila mediante la consulta compartida `sourceFrameCallArgumentStorage`. Revalida instrucciones originales, despacho cualificado actual, importación fuerte, cabecera y descriptor completos, ABI del callback y bytes entrantes. El descriptor limita las escrituras; no demuestra la inicialización del relleno. Los tipos de los campos y los cuerpos de callback requieren pruebas independientes. Se rechazan observaciones cíclicas y capturas de punteros al marco privado; las dependencias usan rondas de prueba completadas. diff --git a/docs/es/cpu-execution.md b/docs/es/cpu-execution.md index 9b41b61c49..859522fa6d 100644 --- a/docs/es/cpu-execution.md +++ b/docs/es/cpu-execution.md @@ -204,4 +204,7 @@ Unicorn comprobado usa `MachineRunControl`: un único margen cubre mantenimiento [Setup, signing and native hardware validation (English)](../macos-hvf.md) -ARM64 checked admite `LDXR/STXR` de 8/16/32/64 bits, pares `LDXP/STXP` de 32/64 bits, variantes acquire/release y `CLREX`. KVM, WHP y Unicorn checked comparten un monitor ISA con reservas físicas de 16 bytes; las salidas de ejecución paso a paso no impiden avanzar a los bucles. Las escrituras confirmadas invalidan las reservas incluso sin cambiar los bytes, también mediante alias y vistas retenidas. Una parada conserva el estado sin publicar; las instantáneas no deshacen escrituras intermedias. Los exclusivos checked siguen FEAT_LSE2: permiten operandos no alineados dentro de un bloque alineado de 16 bytes; cruzarlo genera `alignment`. Las escrituras del mismo ancho coinciden con el gránulo físico reservado. La alineación y los permisos se verifican antes del resultado del almacenamiento condicional, incluso con reserva caducada. MMIO, atómicas LSE opcionales y SMP paralelo quedan fuera del perfil. Unicorn ARM64 también usa este monitor con el contrato `Software`, incluso cuando CPU de software y checked comparten RAM física. El contrato `Software` de Unicorn conserva la alineación natural de su motor. +ARM64 checked admite `LDXR/STXR` de 8/16/32/64 bits, pares `LDXP/STXP` de 32/64 bits, variantes acquire/release y `CLREX`. KVM, WHP y Unicorn checked comparten un monitor ISA con reservas físicas de 16 bytes; las salidas de ejecución paso a paso no impiden avanzar a los bucles. Las escrituras confirmadas invalidan las reservas incluso sin cambiar los bytes, también mediante alias y vistas retenidas. Una parada conserva el estado sin publicar; las instantáneas no deshacen escrituras intermedias. Los exclusivos checked siguen FEAT_LSE2: permiten operandos no alineados dentro de un bloque alineado de 16 bytes; cruzarlo genera `alignment`. Las escrituras del mismo ancho coinciden con el gránulo físico reservado. La alineación y los permisos se verifican antes del resultado del almacenamiento condicional, incluso con reserva caducada. MMIO y SMP paralelo quedan fuera del perfil. Unicorn ARM64 también usa este monitor con el contrato `Software`, incluso cuando CPU de software y checked comparten RAM física. El contrato `Software` de Unicorn conserva la alineación natural de su motor. + + +La misma capa ISA admite FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` y min/max con o sin signo, para byte, media palabra, palabra y doble palabra y variantes acquire/release. Checked usa la alineación anterior; Unicorn `Software` conserva la natural. Las comparaciones respetan el ancho y los valores anteriores devueltos se extienden con ceros. Un CAS fallido también exige permiso de escritura y reescribe el valor anterior, opción permitida por Arm que invalida reservas físicas. Los callbacks ven CPU/RAM antes de confirmar; cancelación y fallos síncronos no publican efectos parciales. El bloqueo físico ordena CPU cooperativas, sin SMP paralelo ni atómicas MMIO. `MRS/MSR NZCV` transfiere los cuatro indicadores respetando bits reservados y registro cero. \ No newline at end of file diff --git a/docs/es/testing.md b/docs/es/testing.md index 1243f74992..2b80ba6342 100644 --- a/docs/es/testing.md +++ b/docs/es/testing.md @@ -1516,6 +1516,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` verifica anchuras, pares, acquire/release, solapamientos, alias, fallos, instantáneas, cancelación o fallo de observadores y competencia entre CPU. `RAMReservationTests.cpp` cubre escrituras idénticas, ABA, reutilización, reversión e interferencias de cadenas y `ENTER` en KVM/Unicorn. Las muestras Windows ARM64 originales ejecutan bucles exclusivos. `scripts/check_aarch64_exclusives.py` observa instrucciones originales y excepciones de alineación en CI Windows ARM64. Estas observaciones nativas no acreditan la ejecución de los backends ARM64 KVM/WHP; los perfiles no disponibles quedan explícitamente omitidos. Los mismos casos cubren Unicorn de software, interferencias entre contratos, escrituras idénticas y ABA, alias ejecutables dentro de una ejecución, y escrituras `DC ZVA` con cancelación por observadores. `windows-alignment-oracle.yml` también ejecuta la prueba ARM64: 1.320 observaciones cubren cada desplazamiento no alineado, cuatro secuencias de carga/almacenamiento y memoria escribible, de solo lectura, inaccesible o entre páginas. Conserva el ancho completo de los valores iniciales de registros y las escrituras parciales anteriores a un fallo. `WindowsExclusiveProcessTests.cpp` compara 1.320 observaciones originales de Windows ARM64 con los resúmenes nativos de `WindowsExclusiveNative.def`. Solo normaliza la ubicación del código y los datos; conserva registros, metadatos de excepción y efectos en RAM. +`AArch64AtomicTests.cpp` cubre 168 codificaciones LSE ensambladas de forma independiente, alias, comparaciones con signo, permisos, cancelación, reservas físicas y transferencias NZCV. `scripts/check_aarch64_atomics.py` recopila 1.100 registros originales de Windows ARM64 con resultados, contexto de excepción y huella RAM completos; el analizador rechaza pruebas ausentes o incoherentes. KVM/WHP nativos requieren validación aparte. + Los destinos nativos estructuralmente constantes usan directamente el planificador existente con comprobación de viabilidad. Un destino simbólico único conserva el predicado de entrada solo tras una enumeración exhaustiva. Las regresiones verifican 128 transferencias constantes con el presupuesto de consultas de una ejecución lineal y 32 transferencias calculadas con dos consultas de enumeración por transferencia, manteniendo libres los bits altos de dirección y los dominios de las ramas. Se rechazan cambios en el estado completo, falta de alineación, límites de destinos nulos y presupuestos insuficientes de consultas o instrucciones. Siguen siendo necesarias las comprobaciones de rechazo de múltiples destinos y enumeraciones incompletas. Una rama nativa conserva el dominio de entrada en una arista solo después de completar una prueba UNSAT que excluya la otra. Las pruebas verifican 32 transferencias condicionales en ambas orientaciones con 512 puertas del solucionador, presupuestos exactos y reducidos en una consulta, y puertas agotadas. Deben rechazarse la alineación modificada o ausente, las comparaciones invertidas y los estados finales alterados; siguen siendo necesarias las pruebas de control indefinido arbitrario y de dos aristas factibles. diff --git a/docs/fr/architecture.md b/docs/fr/architecture.md index 12a3ba69d5..5cc771e9ce 100644 --- a/docs/fr/architecture.md +++ b/docs/fr/architecture.md @@ -1433,7 +1433,9 @@ Le même propriétaire des effets du cadre traite séparément le pont fort exac `AuthenticatedSourceFrameLoads` peut traverser un appel d’accesseur de métadonnées via `swiftMetadataAccessorSourceCallHint`. Les deux chemins de déclaration réutilisent le catalogue existant observé par le compilateur, le fournisseur fort exact et l’ABI Swift complète de requête et de réponse de métadonnées. La réponse sur deux registres et les modifications normales des registres volatils sont conservées ; les spills privés exigent toujours la preuve commune des octets atteignants. Cela ne fournit ni disposition de valeur, ni effets mémoire des witnesses, ni emprunt de frame, ni modèle d’allocation dynamique de pile. -`AArch64ExclusiveInstructions.def` définit les encodages admis ; `AArch64ExclusiveExecution` effectue les transitions des registres et du moniteur sous le verrou physique. `RAMReservation` conserve l’identité d’allocation et reçoit les notifications des écritures validées : hôte, vues conservées, `RAMTransaction`, opérations de chaînes et écritures partielles de `ENTER` avant défaut. Les écritures temporaires et leur annulation n’invalident rien. Une exécution opaque invalide les réservations ; une exécution déclarée suit les plages réellement publiées. Défauts invités, appels de service et liaison d’espace d’adresses effacent le moniteur local. Les chemins originaux `LDAR[B/H]` et `STLR[B/H]` conservent leur contrat RAM naturellement alignée. L’adaptateur logiciel Unicorn invalide prudemment les réservations lors des hooks précédant les écritures acceptées par les observateurs. Après un stockage exclusif, il invalide les traductions de tous les alias exécutables avant de poursuivre la même exécution. `AArch64ExclusiveAccess` choisit la politique d’alignement : FEAT_LSE2 pour checked, modèle du moteur pour Unicorn logiciel. La correspondance conserve la largeur et le granule physique. +`AArch64ExclusiveInstructions.def` définit les encodages admis ; `AArch64ExclusiveExecution` effectue les transitions des registres et du moniteur sous le verrou physique. `RAMReservation` conserve l’identité d’allocation et reçoit les notifications des écritures validées : hôte, vues conservées, `RAMTransaction`, opérations de chaînes et écritures partielles de `ENTER` avant défaut. Les écritures temporaires et leur annulation n’invalident rien. Une exécution opaque invalide les réservations ; une exécution déclarée suit les plages réellement publiées. Défauts invités, appels de service et liaison d’espace d’adresses effacent le moniteur local. Les chemins originaux `LDAR[B/H]` et `STLR[B/H]` conservent leur contrat RAM naturellement alignée. L’adaptateur logiciel Unicorn invalide prudemment les réservations lors des hooks précédant les écritures acceptées par les observateurs. Après un stockage exclusif, il invalide les traductions de tous les alias exécutables avant de poursuivre la même exécution. `AArch64AtomicAccess` choisit la politique d’alignement : FEAT_LSE2 pour checked, modèle du moteur pour Unicorn logiciel. La correspondance conserve la largeur et le granule physique. + +`AArch64AtomicInstructions.def` possède l’admission LSE, `AArch64AtomicExecution` la validation atomique, et `AArch64AtomicMemory` partage l’alignement et les fautes typées avec les exclusifs. Les transports checked et le pont logiciel Unicorn utilisent ces mêmes autorités. `objcNonEscapingBlockSourceFrameEffects` authentifie les emprunts synchrones de blocs de pile avec la requête partagée `sourceFrameCallArgumentStorage`. Elle revérifie les instructions originales, le dispatch qualifié actuel, l’import fort, l’en-tête et le descripteur complets, l’ABI du callback et les octets entrants. Le descripteur borne les écritures ; le remplissage ne devient pas initialisé. Les types des champs et les corps des callbacks exigent des preuves indépendantes. Les observations cycliques et les captures de pointeurs vers le cadre privé sont rejetées ; les dépendances utilisent des tours de preuve terminés. diff --git a/docs/fr/cpu-execution.md b/docs/fr/cpu-execution.md index f22ffbd6f0..b1e8544532 100644 --- a/docs/fr/cpu-execution.md +++ b/docs/fr/cpu-execution.md @@ -204,4 +204,7 @@ Unicorn vérifié utilise `MachineRunControl` : une seule allocation couvre main [Setup, signing and native hardware validation (English)](../macos-hvf.md) -ARM64 checked prend en charge `LDXR/STXR` sur 8/16/32/64 bits, les paires `LDXP/STXP` sur 32/64 bits, leurs variantes acquire/release et `CLREX`. KVM, WHP et Unicorn checked partagent un moniteur ISA à granularité physique de 16 octets ; les sorties de pas à pas ne bloquent pas les boucles. Toute écriture validée invalide la réservation, même à valeur identique, y compris via les alias et vues conservées. Un arrêt conserve les effets non publiés ; un instantané ne peut annuler les écritures intervenues. Les exclusifs checked suivent FEAT_LSE2 : un opérande non aligné reste valide dans un bloc aligné de 16 octets ; franchir ce bloc produit `alignment`. Les stockages de même largeur correspondent au granule physique réservé. Alignement et permissions sont vérifiés avant le résultat du stockage conditionnel, même sans réservation valide. MMIO, les atomiques LSE optionnelles et SMP parallèle restent hors profil. Unicorn ARM64 utilise aussi ce moniteur avec le contrat `Software`, y compris lorsque des CPU logiciels et checked partagent la RAM physique. Le contrat `Software` d’Unicorn conserve l’alignement naturel de son moteur. +ARM64 checked prend en charge `LDXR/STXR` sur 8/16/32/64 bits, les paires `LDXP/STXP` sur 32/64 bits, leurs variantes acquire/release et `CLREX`. KVM, WHP et Unicorn checked partagent un moniteur ISA à granularité physique de 16 octets ; les sorties de pas à pas ne bloquent pas les boucles. Toute écriture validée invalide la réservation, même à valeur identique, y compris via les alias et vues conservées. Un arrêt conserve les effets non publiés ; un instantané ne peut annuler les écritures intervenues. Les exclusifs checked suivent FEAT_LSE2 : un opérande non aligné reste valide dans un bloc aligné de 16 octets ; franchir ce bloc produit `alignment`. Les stockages de même largeur correspondent au granule physique réservé. Alignement et permissions sont vérifiés avant le résultat du stockage conditionnel, même sans réservation valide. MMIO et SMP parallèle restent hors profil. Unicorn ARM64 utilise aussi ce moniteur avec le contrat `Software`, y compris lorsque des CPU logiciels et checked partagent la RAM physique. Le contrat `Software` d’Unicorn conserve l’alignement naturel de son moteur. + + +La même couche ISA prend en charge FEAT_LSE : `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` et min/max signés ou non, avec les largeurs octet, demi-mot, mot et double mot et les variantes acquire/release. Les profils checked suivent l’alignement ci-dessus ; Unicorn `Software` conserve l’alignement naturel. Les comparaisons utilisent la largeur de l’opérande et les anciennes valeurs retournées sont étendues par zéro. Un CAS échoué exige aussi le droit d’écriture et réécrit l’ancienne valeur, choix permis par Arm qui invalide les réservations physiques. Les callbacks voient l’état CPU/RAM avant validation ; annulation et faute synchrone ne publient aucun effet partiel. Le verrou physique ordonne les CPU coopératifs, sans SMP parallèle ni atomiques MMIO. `MRS/MSR NZCV` transfère les quatre indicateurs selon les règles des bits réservés et du registre zéro. \ No newline at end of file diff --git a/docs/fr/testing.md b/docs/fr/testing.md index 7b6315d2c3..d21667342b 100644 --- a/docs/fr/testing.md +++ b/docs/fr/testing.md @@ -1530,6 +1530,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` couvre largeurs, paires, acquire/release, chevauchements de registres, alias, défauts, instantanés, annulation ou échec des observateurs et concurrence entre CPU. `RAMReservationTests.cpp` couvre écritures identiques, ABA, réutilisation, annulation et interférences des chaînes et de `ENTER` sur KVM/Unicorn. Les programmes Windows ARM64 originaux exécutent des boucles exclusives. `scripts/check_aarch64_exclusives.py` observe les instructions originales et exceptions d’alignement dans la CI Windows ARM64. Ces observations natives ne prouvent pas l’exécution des backends ARM64 KVM/WHP ; les profils indisponibles restent explicitement ignorés. Les mêmes cas couvrent Unicorn logiciel, les interférences entre contrats, les écritures identiques et ABA, les alias exécutables au cours d’une même exécution, ainsi que `DC ZVA` et l’annulation par les observateurs. `windows-alignment-oracle.yml` exécute aussi la sonde ARM64 : 1 320 observations couvrent chaque décalage non aligné, quatre séquences de chargement/stockage et la mémoire accessible en écriture, en lecture seule, inaccessible ou traversant une limite de page. La sonde conserve la largeur complète des registres et les éventuelles écritures partielles précédant une faute. `WindowsExclusiveProcessTests.cpp` compare 1 320 observations Windows ARM64 originales aux empreintes natives de `WindowsExclusiveNative.def`. Seul le placement du code et des données est normalisé ; les registres, métadonnées d’exception et effets RAM sont conservés. +`AArch64AtomicTests.cpp` couvre 168 encodages LSE assemblés indépendamment, les alias de registres, les comparaisons signées, droits, annulations, réservations physiques et transferts NZCV. `scripts/check_aarch64_atomics.py` collecte 1 100 observations Windows ARM64 originales avec résultats, contexte d’exception et empreinte RAM complets ; les tests du parseur refusent les preuves absentes ou incohérentes. L’exécution KVM/WHP native exige une validation distincte. + Les cibles natives structurellement constantes utilisent directement l’ordonnanceur existant avec contrôle de faisabilité. Une cible symbolique unique ne conserve le prédicat entrant qu’après une énumération exhaustive. Les régressions vérifient 128 transferts constants avec le budget de requêtes d’une exécution linéaire et 32 transferts calculés avec deux requêtes d’énumération par transfert, en préservant les bits hauts libres des adresses et les domaines des branches. Les états complets modifiés, l’absence d’alignement, une limite de cibles nulle et les budgets de requêtes ou d’instructions insuffisants sont refusés. Les contrôles existants de refus des cibles multiples et des énumérations incomplètes restent requis. Une branche native ne conserve le domaine entrant sur une arête qu’après une preuve UNSAT complète excluant l’autre. Les tests vérifient 32 transferts conditionnels dans les deux orientations avec 512 portes du solveur, des budgets de requêtes exacts ou réduits d’une unité et des portes épuisées. Un alignement modifié ou absent, des comparaisons inversées et un état final modifié doivent être refusés ; les tests existants de contrôle indéfini arbitraire et de deux arêtes réalisables restent requis. diff --git a/docs/it/architecture.md b/docs/it/architecture.md index 1af725eded..9f8e2709c5 100644 --- a/docs/it/architecture.md +++ b/docs/it/architecture.md @@ -1423,7 +1423,9 @@ Lo stesso proprietario degli effetti del frame gestisce separatamente il ponte f `AuthenticatedSourceFrameLoads` può attraversare una chiamata a un accessore di metadati tramite `swiftMetadataAccessorSourceCallHint`. I due percorsi di dichiarazione riutilizzano il catalogo esistente osservato dal compilatore, il provider forte esatto e l’ABI Swift completa di richiesta e risposta dei metadati. La risposta su due registri e le normali modifiche ai registri volatili restano intatte; gli spill privati richiedono ancora la prova condivisa dei byte raggiungenti. Questo non fornisce layout dei valori, effetti di memoria dei witness, prestiti del frame o un modello di allocazione dinamica dello stack. -`AArch64ExclusiveInstructions.def` definisce le codifiche ammesse; `AArch64ExclusiveExecution` aggiorna registri e monitor sotto il blocco fisico. `RAMReservation` mantiene l’identità delle allocazioni e riceve notifiche delle scritture confermate da host, viste conservate, `RAMTransaction`, istruzioni su stringhe e scritture parziali di `ENTER` prima del fault. Scritture temporanee e rollback non invalidano le prenotazioni. L’esecuzione opaca invalida quelle esistenti; quella dichiarata segue gli intervalli realmente pubblicati. Fault del guest, trap di servizio e associazione dello spazio di indirizzi azzerano il monitor locale. I percorsi originali `LDAR[B/H]` e `STLR[B/H]` mantengono il contratto RAM con allineamento naturale. L’adattatore software Unicorn invalida conservativamente le prenotazioni negli hook precedenti alle scritture ammesse dagli osservatori. Dopo uno store esclusivo, invalida le traduzioni di tutti gli alias eseguibili prima di proseguire la stessa esecuzione. `AArch64ExclusiveAccess` sceglie l’allineamento: FEAT_LSE2 per checked e il modello del motore per Unicorn software. La corrispondenza usa la stessa larghezza e lo stesso granulo fisico. +`AArch64ExclusiveInstructions.def` definisce le codifiche ammesse; `AArch64ExclusiveExecution` aggiorna registri e monitor sotto il blocco fisico. `RAMReservation` mantiene l’identità delle allocazioni e riceve notifiche delle scritture confermate da host, viste conservate, `RAMTransaction`, istruzioni su stringhe e scritture parziali di `ENTER` prima del fault. Scritture temporanee e rollback non invalidano le prenotazioni. L’esecuzione opaca invalida quelle esistenti; quella dichiarata segue gli intervalli realmente pubblicati. Fault del guest, trap di servizio e associazione dello spazio di indirizzi azzerano il monitor locale. I percorsi originali `LDAR[B/H]` e `STLR[B/H]` mantengono il contratto RAM con allineamento naturale. L’adattatore software Unicorn invalida conservativamente le prenotazioni negli hook precedenti alle scritture ammesse dagli osservatori. Dopo uno store esclusivo, invalida le traduzioni di tutti gli alias eseguibili prima di proseguire la stessa esecuzione. `AArch64AtomicAccess` sceglie l’allineamento: FEAT_LSE2 per checked e il modello del motore per Unicorn software. La corrispondenza usa la stessa larghezza e lo stesso granulo fisico. + +`AArch64AtomicInstructions.def` gestisce l’ammissione LSE, `AArch64AtomicExecution` il completamento atomico e `AArch64AtomicMemory` condivide allineamento ed errori tipizzati con gli esclusivi. I trasporti checked e il ponte software Unicorn usano queste stesse autorità. `objcNonEscapingBlockSourceFrameEffects` autentica i prestiti sincroni dei blocchi sullo stack tramite la query condivisa `sourceFrameCallArgumentStorage`. Ricontrolla istruzioni originali, dispatch qualificato corrente, import forte, intestazione e descrittore completi, ABI del callback e byte in ingresso. Il descrittore limita le scritture; il padding non viene considerato inizializzato. Tipi dei campi e corpi dei callback richiedono prove indipendenti. Osservazioni cicliche e catture di puntatori al frame privato sono rifiutate; le dipendenze usano cicli di prova completati. diff --git a/docs/it/cpu-execution.md b/docs/it/cpu-execution.md index fda040b74d..233ad2e48a 100644 --- a/docs/it/cpu-execution.md +++ b/docs/it/cpu-execution.md @@ -204,4 +204,7 @@ Unicorn verificato usa `MachineRunControl`: un unico margine copre manutenzione [Setup, signing and native hardware validation (English)](../macos-hvf.md) -ARM64 checked supporta `LDXR/STXR` a 8/16/32/64 bit, coppie `LDXP/STXP` a 32/64 bit, varianti acquire/release e `CLREX`. KVM, WHP e Unicorn checked condividono un monitor ISA con prenotazioni fisiche di 16 byte; le uscite per passo singolo non bloccano i cicli. Le scritture confermate invalidano le prenotazioni anche senza cambiare i byte, inclusi alias e viste conservate. Gli arresti preservano lo stato non pubblicato; gli snapshot non annullano le scritture intermedie. Gli esclusivi checked seguono FEAT_LSE2: ammettono operandi non allineati entro un blocco allineato di 16 byte; attraversarlo produce `alignment`. Gli store della stessa larghezza corrispondono al granulo fisico prenotato. Allineamento e permessi sono verificati prima del risultato della scrittura condizionale, anche con prenotazione scaduta. MMIO, atomiche LSE opzionali e SMP parallelo restano esclusi. Unicorn ARM64 usa questo monitor anche nel contratto `Software`, inclusi CPU software e checked che condividono la RAM fisica. Il contratto `Software` di Unicorn mantiene l’allineamento naturale del motore. +ARM64 checked supporta `LDXR/STXR` a 8/16/32/64 bit, coppie `LDXP/STXP` a 32/64 bit, varianti acquire/release e `CLREX`. KVM, WHP e Unicorn checked condividono un monitor ISA con prenotazioni fisiche di 16 byte; le uscite per passo singolo non bloccano i cicli. Le scritture confermate invalidano le prenotazioni anche senza cambiare i byte, inclusi alias e viste conservate. Gli arresti preservano lo stato non pubblicato; gli snapshot non annullano le scritture intermedie. Gli esclusivi checked seguono FEAT_LSE2: ammettono operandi non allineati entro un blocco allineato di 16 byte; attraversarlo produce `alignment`. Gli store della stessa larghezza corrispondono al granulo fisico prenotato. Allineamento e permessi sono verificati prima del risultato della scrittura condizionale, anche con prenotazione scaduta. MMIO e SMP parallelo restano esclusi. Unicorn ARM64 usa questo monitor anche nel contratto `Software`, inclusi CPU software e checked che condividono la RAM fisica. Il contratto `Software` di Unicorn mantiene l’allineamento naturale del motore. + + +Lo stesso livello ISA supporta FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` e min/max con o senza segno, per byte, mezza parola, parola e doppia parola e varianti acquire/release. Checked usa l’allineamento sopra descritto; Unicorn `Software` mantiene quello naturale. Il confronto rispetta la larghezza e i vecchi valori restituiti sono estesi con zeri. Un CAS fallito richiede comunque permessi di scrittura e riscrive il vecchio valore, scelta ammessa da Arm che invalida le prenotazioni fisiche. I callback vedono lo stato CPU/RAM precedente; annullamenti ed errori sincroni non pubblicano effetti parziali. Il blocco fisico ordina le CPU cooperative, senza SMP parallelo o atomiche MMIO. `MRS/MSR NZCV` trasferisce i quattro flag rispettando bit riservati e registro zero. \ No newline at end of file diff --git a/docs/it/testing.md b/docs/it/testing.md index e48d257604..0fdda7f0c3 100644 --- a/docs/it/testing.md +++ b/docs/it/testing.md @@ -1511,6 +1511,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` verifica larghezze, coppie, acquire/release, sovrapposizioni, alias, fault, snapshot, annullamento o errore degli osservatori e CPU concorrenti. `RAMReservationTests.cpp` copre scritture identiche, ABA, riuso, rollback e interferenze di stringhe e `ENTER` su KVM/Unicorn. I programmi originali Windows ARM64 eseguono cicli esclusivi. `scripts/check_aarch64_exclusives.py` rileva istruzioni originali ed eccezioni di allineamento nella CI Windows ARM64. Queste osservazioni native non dimostrano l’esecuzione dei backend ARM64 KVM/WHP; i profili indisponibili restano esplicitamente saltati. Gli stessi casi verificano Unicorn software, interferenze tra contratti, scritture identiche e ABA, alias eseguibili nella stessa esecuzione, e scritture `DC ZVA` con annullamento da parte degli osservatori. `windows-alignment-oracle.yml` esegue anche la sonda ARM64: 1.320 osservazioni coprono ogni offset non allineato, quattro sequenze di caricamento/scrittura e memoria scrivibile, di sola lettura, inaccessibile o a cavallo di pagine. Conserva l’intera larghezza dei valori iniziali dei registri e le scritture parziali completate prima di un errore. `WindowsExclusiveProcessTests.cpp` confronta 1.320 osservazioni Windows ARM64 originali con i digest nativi in `WindowsExclusiveNative.def`. Normalizza solo la collocazione di codice e dati, mantenendo registri, metadati delle eccezioni ed effetti RAM. +`AArch64AtomicTests.cpp` copre 168 codifiche LSE assemblate indipendentemente, alias, confronti con segno, permessi, annullamento, prenotazioni fisiche e trasferimenti NZCV. `scripts/check_aarch64_atomics.py` raccoglie 1.100 record originali Windows ARM64 con risultati, contesto delle eccezioni e impronta RAM completi; i test del parser rifiutano prove mancanti o incoerenti. L’esecuzione nativa KVM/WHP richiede verifica separata. + Gli obiettivi nativi strutturalmente costanti usano direttamente la pianificazione esistente con verifica di fattibilità. Un singolo obiettivo simbolico mantiene il predicato di ingresso solo dopo un’enumerazione completa. Le regressioni verificano 128 trasferimenti costanti con il budget di interrogazioni dell’esecuzione lineare e 32 trasferimenti calcolati con due interrogazioni di enumerazione per trasferimento, conservando i bit alti liberi degli indirizzi e i domini dei rami. Stati completi modificati, allineamenti mancanti, limite degli obiettivi nullo e budget insufficienti di interrogazioni o istruzioni vengono rifiutati. Restano necessari i controlli di rifiuto per obiettivi multipli ed enumerazioni incomplete. Un ramo nativo mantiene il dominio di ingresso su un arco solo dopo una prova UNSAT completa che esclude l’altro arco. I test verificano 32 trasferimenti condizionali in entrambe le direzioni con 512 porte del risolutore, budget esatti o ridotti di una interrogazione e porte esaurite. Allineamenti modificati o assenti, confronti invertiti e stati finali alterati devono essere rifiutati; restano necessari i test esistenti per controllo indefinito arbitrario e due archi percorribili. diff --git a/docs/ja/architecture.md b/docs/ja/architecture.md index 598cd1fbc6..bb1b648602 100644 --- a/docs/ja/architecture.md +++ b/docs/ja/architecture.md @@ -1313,7 +1313,9 @@ CoreImage SDK の強い宣言は、`imageWithCGImage:` と `imageByApplyingTrans `AuthenticatedSourceFrameLoads` は `swiftMetadataAccessorSourceCallHint` を通じてメタデータアクセサの呼び出しを越えられる。両方の宣言経路は既存のコンパイラ証拠表、正確な強リンクの provider、完全な Swift 要求・メタデータ応答 ABI を再利用する。2 レジスタの応答と通常の揮発レジスタ破壊規則を維持し、私有 spill には共有の到達バイト証明を引き続き要求する。値レイアウト、witness のメモリ効果、フレーム借用、動的スタック割り当てのモデルは与えない。 -`AArch64ExclusiveInstructions.def` が排他命令のエンコーディングを管理し、`AArch64ExclusiveExecution` が物理実行ロック内でレジスタとモニターを更新します。`RAMReservation` は割り当ての同一性を保持し、ホスト、保持ビュー、`RAMTransaction`、文字列命令、障害前の `ENTER` 部分ストアからコミット通知を受けます。一時書き込みとロールバックは予約を無効化しません。書き込み範囲が不明な実行は既存予約を無効化し、範囲を宣言する実行は実際のコミットを追跡します。ゲスト障害、サービス・トラップ、アドレス空間の結び直しはローカルモニターを消去します。元の `LDAR[B/H]` と `STLR[B/H]` は、自然整列 RAM の契約のままバックエンドで実行します。 Unicorn のソフトウェアアダプターは観測コールバックが許可した書き込み前フックで予約を保守的に無効化し、排他ストア後は同じ実行を続ける前に全実行可能エイリアスの翻訳キャッシュを破棄します。 `AArch64ExclusiveAccess` が整列方針を選びます。checked は FEAT_LSE2、ソフトウェア Unicorn はエンジンの機能モデルに従い、予約は同じ幅と物理粒度で照合します。 +`AArch64ExclusiveInstructions.def` が排他命令のエンコーディングを管理し、`AArch64ExclusiveExecution` が物理実行ロック内でレジスタとモニターを更新します。`RAMReservation` は割り当ての同一性を保持し、ホスト、保持ビュー、`RAMTransaction`、文字列命令、障害前の `ENTER` 部分ストアからコミット通知を受けます。一時書き込みとロールバックは予約を無効化しません。書き込み範囲が不明な実行は既存予約を無効化し、範囲を宣言する実行は実際のコミットを追跡します。ゲスト障害、サービス・トラップ、アドレス空間の結び直しはローカルモニターを消去します。元の `LDAR[B/H]` と `STLR[B/H]` は、自然整列 RAM の契約のままバックエンドで実行します。 Unicorn のソフトウェアアダプターは観測コールバックが許可した書き込み前フックで予約を保守的に無効化し、排他ストア後は同じ実行を続ける前に全実行可能エイリアスの翻訳キャッシュを破棄します。 `AArch64AtomicAccess` が整列方針を選びます。checked は FEAT_LSE2、ソフトウェア Unicorn はエンジンの機能モデルに従い、予約は同じ幅と物理粒度で照合します。 + +`AArch64AtomicInstructions.def` が LSE の受け入れ、`AArch64AtomicExecution` が原子的完了を担当します。`AArch64AtomicMemory` は排他命令と整列・型付き障害方針を共有し、checked と Unicorn ソフトウェアブリッジが共用します。 `objcNonEscapingBlockSourceFrameEffects` は共有クエリ `sourceFrameCallArgumentStorage` でスタック block の同期借用を検証する。元の命令、現在の型付きディスパッチ、強い import、完全なヘッダーと記述子、コールバック ABI、到達するバイト情報を再確認する。記述子は書き込み範囲を定めるが、パディングの初期化は保証しない。キャプチャのフィールド型とコールバック本体には独立したソース証明が必要である。循環する観測と非公開フレームへのポインターのキャプチャを拒否し、依存関係には完了した証明ラウンドを使う。 diff --git a/docs/ja/cpu-execution.md b/docs/ja/cpu-execution.md index d38fc762b7..1aaf1e7856 100644 --- a/docs/ja/cpu-execution.md +++ b/docs/ja/cpu-execution.md @@ -204,4 +204,7 @@ checked Unicorn は `MachineRunControl` を使い、ARM64 の保守、ゲスト [Setup, signing and native hardware validation (English)](../macos-hvf.md) -checked ARM64 は 8/16/32/64 ビットの `LDXR/STXR`、32/64 ビットのレジスタ対 `LDXP/STXP`、acquire/release 形式、`CLREX` をサポートします。KVM、WHP、checked Unicorn は ISA 層の排他モニターを共有し、16 バイトの物理範囲で予約を管理します。転送層のシングルステップ終了でループの進行を妨げません。同じ値の書き込みも、エイリアスや保持ビューを含め、コミット時に予約を無効化します。停止は未公開状態を保ち、スナップショットは途中の書き込みを取り消せません。checked 排他命令は FEAT_LSE2 の整列規則を使います。16 バイト境界内の非整列アクセスは実行でき、境界を越えると `alignment` 障害になります。同じ幅の条件付きストアは予約された物理粒度で照合します。予約が失効していても、条件付きストアの判定前に整列と権限を検査します。MMIO、オプションの LSE アトミック命令、並列 SMP は対象外です。 ARM64 Unicorn の `Software` 契約もこのモニターを使用し、ソフトウェア CPU と checked CPU が物理 RAM を共有する場合にも対応します。 Unicorn の `Software` 契約はエンジンの自然整列モデルを維持します。 +checked ARM64 は 8/16/32/64 ビットの `LDXR/STXR`、32/64 ビットのレジスタ対 `LDXP/STXP`、acquire/release 形式、`CLREX` をサポートします。KVM、WHP、checked Unicorn は ISA 層の排他モニターを共有し、16 バイトの物理範囲で予約を管理します。転送層のシングルステップ終了でループの進行を妨げません。同じ値の書き込みも、エイリアスや保持ビューを含め、コミット時に予約を無効化します。停止は未公開状態を保ち、スナップショットは途中の書き込みを取り消せません。checked 排他命令は FEAT_LSE2 の整列規則を使います。16 バイト境界内の非整列アクセスは実行でき、境界を越えると `alignment` 障害になります。同じ幅の条件付きストアは予約された物理粒度で照合します。予約が失効していても、条件付きストアの判定前に整列と権限を検査します。MMIO、並列 SMP は対象外です。 ARM64 Unicorn の `Software` 契約もこのモニターを使用し、ソフトウェア CPU と checked CPU が物理 RAM を共有する場合にも対応します。 Unicorn の `Software` 契約はエンジンの自然整列モデルを維持します。 + + +同じ ISA 層は FEAT_LSE の `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET`、符号付き・符号なし min/max を、バイト・半語・語・倍語および acquire/release 形式でサポートします。checked は上記の整列方針を、Unicorn `Software` は自然整列を使います。比較はオペランド幅に従い、返す旧値はゼロ拡張します。CAS の比較失敗でも書き込み権限を確認し、Arm が認める旧値の書き戻しを選択して物理予約を無効化します。コールバックはコミット前の CPU/RAM を参照し、取消しや同期障害で部分結果を公開しません。物理実行ロックが協調 CPU の順序を保証し、並列 SMP や MMIO アトミックは提供しません。`MRS/MSR NZCV` は予約ビットとゼロレジスタの規則に従い、4 個の条件フラグを転送します。 \ No newline at end of file diff --git a/docs/ja/testing.md b/docs/ja/testing.md index 329ef74724..fa46860cd1 100644 --- a/docs/ja/testing.md +++ b/docs/ja/testing.md @@ -1415,6 +1415,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` は幅、レジスタ対、acquire/release、レジスタ重複、エイリアス、整列・権限障害、スナップショット、観測コールバックの停止・失敗、CPU 間競合を検査します。`RAMReservationTests.cpp` は同値書き込み、ABA、割り当て再利用、ロールバック、KVM/Unicorn の文字列命令と `ENTER` による干渉を検査します。元の ARM64 Windows プロセス試料は排他ループを実行します。`scripts/check_aarch64_exclusives.py` は Windows ARM64 CI で元の命令と整列例外を記録します。これは ARM64 KVM/WHP バックエンド実行の証拠とは別で、利用できない構成はスキップとして記録します。 同じ排他命令のテストはソフトウェア Unicorn、契約間の干渉、同値・ABA 書き込み、同一実行内の実行可能エイリアス、`DC ZVA` 書き込みと観測コールバックによる停止も検証します。 `windows-alignment-oracle.yml` は ARM64 プローブも実行します。1,320 件の観測で全非整列オフセット、4 種類のロード・ストア列、書き込み可能・読み取り専用・アクセス禁止・ページ境界越えのメモリを調べます。レジスタ初期値の全ビット幅と障害前に確定した部分書き込みを記録します。 `WindowsExclusiveProcessTests.cpp` は 1,320 件の元の Windows ARM64 観測を `WindowsExclusiveNative.def` のネイティブダイジェストと照合します。コードとデータの配置だけを正規化し、レジスタ値、例外メタデータ、RAM の変化を保持します。 +`AArch64AtomicTests.cpp` は独立にアセンブルした 168 種の LSE 符号化、レジスタ重複、符号付き比較、権限、取消し、物理予約、NZCV 転送を検証します。`scripts/check_aarch64_atomics.py` は演算結果、例外コンテキスト、RAM 範囲を含む 1,100 件の Windows ARM64 原記録を収集します。解析テストは欠落や不整合を拒否します。ネイティブ KVM/WHP 実行は別途検証が必要です。 + 構造上定数であるネイティブターゲットは、既存の実行可能性検査付きスケジューラーを直接使用します。記号的な単一ターゲットは完全な列挙の後にのみ入力述語を保持します。回帰テストは直線実行と同じクエリ予算で 128 回の定数転送を、転送ごとに 2 回の列挙クエリで 32 回の計算された転送を検証し、未制約のアドレス上位ビットと分岐領域を保持します。完全な状態結果の変更、アラインメントの欠如、ターゲット上限ゼロ、クエリまたは命令予算の不足は拒否します。複数ターゲットと未完了の列挙に対する既存の拒否検査も引き続き必要です。 ネイティブ分岐は、もう一方の辺を除外する完全な UNSAT 証明の後にのみ、残る辺で入力領域を保持します。テストは両方向の 32 回の条件付き転送を 512 個のソルバーゲート内で検証し、正確なクエリ予算、1 回不足する予算、ゲート不足を確認します。アラインメントの変更や欠如、比較の反転、終了状態の変更は拒否する必要があります。任意の未定義制御と両辺が実行可能な場合の既存テストも必要です。 diff --git a/docs/ko/architecture.md b/docs/ko/architecture.md index 193a9e258f..ad0357d408 100644 --- a/docs/ko/architecture.md +++ b/docs/ko/architecture.md @@ -1290,7 +1290,9 @@ CoreImage SDK의 강한 선언은 `imageWithCGImage:`와 `imageByApplyingTransfo `AuthenticatedSourceFrameLoads`는 `swiftMetadataAccessorSourceCallHint`를 통해 메타데이터 접근자 호출을 통과할 수 있다. 두 선언 경로는 기존 컴파일러 증거 표, 정확한 강한 링크 provider, 완전한 Swift 요청 및 메타데이터 응답 ABI를 재사용한다. 두 레지스터 응답과 일반적인 휘발성 레지스터 변경 규칙을 유지하며, 비공개 spill에는 공유 도달 바이트 증명을 계속 요구한다. 값 레이아웃, witness 메모리 효과, 프레임 차용 또는 동적 스택 할당 모델을 제공하지 않는다. -`AArch64ExclusiveInstructions.def`는 독점 명령 인코딩의 허용 기준을 소유하고 `AArch64ExclusiveExecution`는 물리 실행 잠금 안에서 레지스터와 모니터 상태를 갱신합니다. `RAMReservation`은 할당 식별성을 유지하며 호스트 쓰기, 보존 뷰, `RAMTransaction`, 문자열 명령 및 `ENTER` 오류 전 부분 저장의 커밋 통지를 받습니다. 임시 쓰기와 롤백은 예약을 무효화하지 않습니다. 쓰기 범위를 알 수 없는 실행은 기존 예약을 무효화하고, 범위를 선언하는 실행은 실제 커밋 범위를 추적합니다. 게스트 오류, 서비스 트랩, 주소 공간 바인딩은 로컬 모니터를 비웁니다. 기존 `LDAR[B/H]`와 `STLR[B/H]`는 자연 정렬 RAM 계약에 따라 백엔드에서 실행됩니다. Unicorn 소프트웨어 어댑터는 관찰 콜백이 허용한 쓰기 전 훅에서 예약을 보수적으로 무효화하고, 독점 저장 후 같은 실행을 계속하기 전에 모든 실행 가능 별칭의 번역 캐시를 폐기합니다. `AArch64ExclusiveAccess`가 정렬 정책을 선택합니다. checked는 FEAT_LSE2를 사용하고 소프트웨어 Unicorn은 엔진의 기능 모델을 유지하며, 예약은 같은 피연산자 폭과 물리 단위로 비교합니다. +`AArch64ExclusiveInstructions.def`는 독점 명령 인코딩의 허용 기준을 소유하고 `AArch64ExclusiveExecution`는 물리 실행 잠금 안에서 레지스터와 모니터 상태를 갱신합니다. `RAMReservation`은 할당 식별성을 유지하며 호스트 쓰기, 보존 뷰, `RAMTransaction`, 문자열 명령 및 `ENTER` 오류 전 부분 저장의 커밋 통지를 받습니다. 임시 쓰기와 롤백은 예약을 무효화하지 않습니다. 쓰기 범위를 알 수 없는 실행은 기존 예약을 무효화하고, 범위를 선언하는 실행은 실제 커밋 범위를 추적합니다. 게스트 오류, 서비스 트랩, 주소 공간 바인딩은 로컬 모니터를 비웁니다. 기존 `LDAR[B/H]`와 `STLR[B/H]`는 자연 정렬 RAM 계약에 따라 백엔드에서 실행됩니다. Unicorn 소프트웨어 어댑터는 관찰 콜백이 허용한 쓰기 전 훅에서 예약을 보수적으로 무효화하고, 독점 저장 후 같은 실행을 계속하기 전에 모든 실행 가능 별칭의 번역 캐시를 폐기합니다. `AArch64AtomicAccess`가 정렬 정책을 선택합니다. checked는 FEAT_LSE2를 사용하고 소프트웨어 Unicorn은 엔진의 기능 모델을 유지하며, 예약은 같은 피연산자 폭과 물리 단위로 비교합니다. + +`AArch64AtomicInstructions.def`는 LSE 허용 범위, `AArch64AtomicExecution`은 원자 연산 완료를 담당합니다. `AArch64AtomicMemory`는 독점 명령과 정렬 및 형식화된 오류 정책을 공유하며 checked 전송과 Unicorn 소프트웨어 브리지가 함께 사용합니다. `objcNonEscapingBlockSourceFrameEffects`는 공유 질의 `sourceFrameCallArgumentStorage`로 스택 block의 동기 대여를 인증한다. 원래 명령, 현재의 타입이 확인된 디스패치, 강한 import, 완전한 헤더와 설명자, 콜백 ABI 및 도달 바이트를 다시 검사한다. 설명자는 쓰기 범위를 정하지만 패딩 초기화를 보장하지 않는다. 캡처 필드 타입과 콜백 본문에는 독립적인 소스 증명이 필요하다. 순환 관찰과 비공개 프레임 포인터 캡처를 거부하며 의존성은 완료된 증명 라운드를 사용한다. diff --git a/docs/ko/cpu-execution.md b/docs/ko/cpu-execution.md index 9cb9f45707..4df785b1ac 100644 --- a/docs/ko/cpu-execution.md +++ b/docs/ko/cpu-execution.md @@ -204,4 +204,7 @@ checked Unicorn은 `MachineRunControl`을 사용하며 ARM64 유지보수, 게 [Setup, signing and native hardware validation (English)](../macos-hvf.md) -checked ARM64는 8/16/32/64비트 `LDXR/STXR`, 32/64비트 레지스터 쌍 `LDXP/STXP`, acquire/release 형태와 `CLREX`를 지원합니다. KVM, WHP, checked Unicorn은 ISA 계층의 독점 모니터를 공유하며 16바이트 물리 범위로 예약을 추적합니다. 전송 계층의 단일 단계 종료가 루프 진행을 막지 않습니다. 동일 값 쓰기도 커밋되면 예약을 무효화하며 별칭과 보존 뷰에도 같은 규칙을 적용합니다. 중지는 미공개 상태를 보존하며 스냅샷은 중간에 발생한 쓰기를 취소하지 못합니다. checked 독점 명령은 FEAT_LSE2 정렬 규칙을 사용합니다. 정렬된 16바이트 블록 안의 비정렬 피연산자는 실행되며, 블록을 넘으면 `alignment` 오류가 발생합니다. 같은 폭의 조건부 저장은 예약된 물리 단위로 일치 여부를 판단합니다. 예약이 만료된 경우에도 조건부 저장 판정 전에 정렬과 권한을 검사합니다. MMIO, 선택적 LSE 원자 명령, 병렬 SMP는 포함하지 않습니다. ARM64 Unicorn의 `Software` 계약도 이 모니터를 사용하며 소프트웨어 CPU와 checked CPU가 물리 RAM을 공유하는 경우를 포함합니다. Unicorn의 `Software` 계약은 엔진의 자연 정렬 모델을 유지합니다. +checked ARM64는 8/16/32/64비트 `LDXR/STXR`, 32/64비트 레지스터 쌍 `LDXP/STXP`, acquire/release 형태와 `CLREX`를 지원합니다. KVM, WHP, checked Unicorn은 ISA 계층의 독점 모니터를 공유하며 16바이트 물리 범위로 예약을 추적합니다. 전송 계층의 단일 단계 종료가 루프 진행을 막지 않습니다. 동일 값 쓰기도 커밋되면 예약을 무효화하며 별칭과 보존 뷰에도 같은 규칙을 적용합니다. 중지는 미공개 상태를 보존하며 스냅샷은 중간에 발생한 쓰기를 취소하지 못합니다. checked 독점 명령은 FEAT_LSE2 정렬 규칙을 사용합니다. 정렬된 16바이트 블록 안의 비정렬 피연산자는 실행되며, 블록을 넘으면 `alignment` 오류가 발생합니다. 같은 폭의 조건부 저장은 예약된 물리 단위로 일치 여부를 판단합니다. 예약이 만료된 경우에도 조건부 저장 판정 전에 정렬과 권한을 검사합니다. MMIO와 병렬 SMP는 포함하지 않습니다. ARM64 Unicorn의 `Software` 계약도 이 모니터를 사용하며 소프트웨어 CPU와 checked CPU가 물리 RAM을 공유하는 경우를 포함합니다. Unicorn의 `Software` 계약은 엔진의 자연 정렬 모델을 유지합니다. + + +같은 ISA 계층은 FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET`, 부호 있는/없는 min/max의 바이트·하프워드·워드·더블워드 및 acquire/release 형식을 지원합니다. checked는 위의 정렬 정책을, Unicorn `Software`는 자연 정렬을 사용합니다. 비교는 피연산자 폭에 따르고 반환되는 이전 값은 0으로 확장합니다. CAS 비교가 실패해도 쓰기 권한을 확인하고, Arm이 허용하는 이전 값 쓰기를 선택하여 물리 예약을 무효화합니다. 콜백은 커밋 전 CPU/RAM을 보며 취소와 동기 오류는 부분 결과를 공개하지 않습니다. 물리 실행 잠금은 협력적 CPU의 순서를 보장하며 병렬 SMP나 MMIO 원자 연산을 제공하지 않습니다. `MRS/MSR NZCV`는 예약 비트와 영 레지스터 규칙에 따라 네 조건 플래그를 전송합니다. \ No newline at end of file diff --git a/docs/ko/testing.md b/docs/ko/testing.md index 230ea780b6..01e6f951eb 100644 --- a/docs/ko/testing.md +++ b/docs/ko/testing.md @@ -1405,6 +1405,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp`는 폭, 레지스터 쌍, acquire/release, 레지스터 중첩, 별칭, 정렬·권한 오류, 스냅샷, 관찰 콜백 취소·실패 및 CPU 간 경쟁을 검사합니다. `RAMReservationTests.cpp`는 동일 값 쓰기, ABA, 할당 재사용, 롤백, KVM/Unicorn 문자열 명령과 `ENTER` 쓰기의 간섭을 검사합니다. 원본 ARM64 Windows 프로세스 예제는 독점 루프를 실행합니다. `scripts/check_aarch64_exclusives.py`는 Windows ARM64 CI에서 원본 명령과 정렬 예외 기록을 수집합니다. 이 증거가 ARM64 KVM/WHP 백엔드 실행을 입증하는 것은 아니며, 사용할 수 없는 구성은 명시적으로 건너뜁니다. 동일한 독점 명령 테스트는 소프트웨어 Unicorn, 계약 간 간섭, 동일 값 및 ABA 쓰기, 같은 실행 내 실행 가능 별칭, `DC ZVA` 쓰기와 관찰 콜백 취소도 검증합니다. `windows-alignment-oracle.yml`은 ARM64 프로브도 실행합니다. 1,320개 관측은 모든 비정렬 오프셋, 네 가지 로드·스토어 순서, 쓰기 가능·읽기 전용·접근 불가·페이지 경계 메모리를 다룹니다. 프로브는 레지스터 초기값의 전체 비트 폭과 오류 전에 커밋된 부분 쓰기를 보존합니다. `WindowsExclusiveProcessTests.cpp`는 1,320개의 원본 Windows ARM64 관측을 `WindowsExclusiveNative.def`의 네이티브 다이제스트와 비교합니다. 코드와 데이터 배치만 정규화하며 레지스터 값, 예외 메타데이터와 RAM 효과를 보존합니다. +`AArch64AtomicTests.cpp`는 독립적으로 어셈블한 LSE 인코딩 168개, 레지스터 별칭, 부호 비교, 권한, 취소, 물리 예약 및 NZCV 전송을 검사합니다. `scripts/check_aarch64_atomics.py`는 연산 결과, 예외 컨텍스트 및 RAM 범위를 담은 Windows ARM64 원본 기록 1,100개를 수집합니다. 파서 검사는 누락되거나 불일치하는 증거를 거부합니다. 네이티브 KVM/WHP 실행은 별도 검증이 필요합니다. + 구조적으로 상수인 네이티브 대상은 기존 실행 가능성 검사 스케줄러를 직접 사용합니다. 기호 단일 대상은 완전한 열거 뒤에만 입력 술어를 유지합니다. 회귀 테스트는 직선 실행과 같은 질의 예산으로 상수 전송 128회를, 전송당 열거 질의 2회로 계산된 전송 32회를 검증하고, 제약 없는 주소 상위 비트와 분기 영역을 유지합니다. 전체 상태 결과 변경, 정렬 제약 누락, 대상 한도 0, 질의나 명령 예산 부족은 거부합니다. 여러 대상과 불완전한 열거에 대한 기존 거부 검사도 계속 필요합니다. 네이티브 분기는 다른 간선을 배제하는 UNSAT 증명이 완료된 뒤에만 남은 간선에서 입력 영역을 유지합니다. 테스트는 양쪽 방향 각각 32회의 조건부 전송을 솔버 게이트 512개 이내에서 검증하고, 정확한 질의 예산, 1회 부족한 예산, 게이트 소진을 확인합니다. 정렬 변경이나 누락, 비교 반전, 종료 상태 변경은 거부해야 하며, 임의의 미정의 제어와 두 간선이 모두 실행 가능한 경우의 기존 테스트도 계속 필요합니다. diff --git a/docs/ru/architecture.md b/docs/ru/architecture.md index 8becb3b230..b8070d9799 100644 --- a/docs/ru/architecture.md +++ b/docs/ru/architecture.md @@ -1377,7 +1377,9 @@ Swift CoreGraphics `CGContext.move(to:)` и `addLine(to:)` сохраняют и `AuthenticatedSourceFrameLoads` может пересекать вызов доступа к метаданным через `swiftMetadataAccessorSourceCallHint`. Оба пути объявления используют существующий каталог с доказательствами компилятора, точного поставщика сильного импорта и полную Swift ABI запроса и ответа метаданных. Сохраняются ответ в двух регистрах и обычное уничтожение значений volatile-регистров; частные spill по-прежнему требуют общего доказательства достигающих байтов. Это не даёт схемы значения, эффектов памяти witness, заимствования кадра или модели динамического выделения стека. -`AArch64ExclusiveInstructions.def` задаёт допустимые кодировки; `AArch64ExclusiveExecution` обновляет регистры и монитор под блокировкой физической памяти. `RAMReservation` удерживает идентичность выделения и получает уведомления о подтверждённых записях хоста, удерживаемых представлений, `RAMTransaction`, строковых инструкций и частичных записей `ENTER` до ошибки. Временные записи и откат не сбрасывают резервирование. Непрозрачное выполнение сбрасывает существующие резервирования; выполнение с объявленными записями отслеживает реально опубликованные диапазоны. Ошибки гостя, сервисные ловушки и привязка адресного пространства очищают локальный монитор. Исходные пути `LDAR[B/H]` и `STLR[B/H]` сохраняют контракт естественно выровненной RAM. Программный адаптер Unicorn консервативно сбрасывает резервирования в хуках перед записью после разрешения наблюдателей. После эксклюзивной записи он удаляет трансляции всех исполняемых псевдонимов до продолжения того же запуска. `AArch64ExclusiveAccess` выбирает выравнивание: FEAT_LSE2 для checked и модель движка для программного Unicorn. Сопоставляются одинаковая разрядность и физическая гранула. +`AArch64ExclusiveInstructions.def` задаёт допустимые кодировки; `AArch64ExclusiveExecution` обновляет регистры и монитор под блокировкой физической памяти. `RAMReservation` удерживает идентичность выделения и получает уведомления о подтверждённых записях хоста, удерживаемых представлений, `RAMTransaction`, строковых инструкций и частичных записей `ENTER` до ошибки. Временные записи и откат не сбрасывают резервирование. Непрозрачное выполнение сбрасывает существующие резервирования; выполнение с объявленными записями отслеживает реально опубликованные диапазоны. Ошибки гостя, сервисные ловушки и привязка адресного пространства очищают локальный монитор. Исходные пути `LDAR[B/H]` и `STLR[B/H]` сохраняют контракт естественно выровненной RAM. Программный адаптер Unicorn консервативно сбрасывает резервирования в хуках перед записью после разрешения наблюдателей. После эксклюзивной записи он удаляет трансляции всех исполняемых псевдонимов до продолжения того же запуска. `AArch64AtomicAccess` выбирает выравнивание: FEAT_LSE2 для checked и модель движка для программного Unicorn. Сопоставляются одинаковая разрядность и физическая гранула. + +`AArch64AtomicInstructions.def` отвечает за допуск LSE, `AArch64AtomicExecution` — за атомарное завершение. `AArch64AtomicMemory` разделяет правила выравнивания и типизированных сбоев с эксклюзивными операциями; checked и программный мост Unicorn используют общие реализации. `objcNonEscapingBlockSourceFrameEffects` подтверждает синхронное заимствование стековых блоков через общий запрос `sourceFrameCallArgumentStorage`. Повторно проверяются исходные инструкции, текущая типизированная диспетчеризация, сильный импорт, полные заголовок и дескриптор, ABI callback и достигающие байты. Дескриптор ограничивает запись; заполнители не считаются инициализированными. Типы полей захвата и тела callback требуют независимых доказательств. Циклические наблюдения и захват указателей на частный кадр отклоняются; зависимости используют завершённые раунды доказательства. diff --git a/docs/ru/cpu-execution.md b/docs/ru/cpu-execution.md index 819eeb52d9..dedda50f20 100644 --- a/docs/ru/cpu-execution.md +++ b/docs/ru/cpu-execution.md @@ -204,4 +204,7 @@ KVM сравнивает общие регистры и полное состо [Setup, signing and native hardware validation (English)](../macos-hvf.md) -Checked ARM64 поддерживает `LDXR/STXR` для 8/16/32/64 бит, пары регистров `LDXP/STXP` для 32/64 бит, варианты acquire/release и `CLREX`. KVM, WHP и checked Unicorn используют общий монитор ISA с физическими гранулами резервирования по 16 байт; пошаговые выходы транспорта не мешают продвижению циклов. Подтверждённые записи сбрасывают резервирование даже без изменения байтов, включая псевдонимы и удерживаемые представления. Остановка сохраняет неопубликованное состояние; снимок не отменяет промежуточные записи. Эксклюзивные операции checked следуют FEAT_LSE2: невыровненный операнд допустим внутри выровненного 16-байтного блока; пересечение его границы даёт `alignment`. Записи той же разрядности сопоставляются с зарезервированной физической гранулой. Выравнивание и права проверяются до результата условной записи, даже при утраченном резервировании. MMIO, дополнительные атомарные инструкции LSE и параллельный SMP не входят в профиль. Unicorn ARM64 использует этот монитор и в контракте `Software`, включая совместную физическую RAM для программных и checked-CPU. Контракт `Software` Unicorn сохраняет естественное выравнивание движка. +Checked ARM64 поддерживает `LDXR/STXR` для 8/16/32/64 бит, пары регистров `LDXP/STXP` для 32/64 бит, варианты acquire/release и `CLREX`. KVM, WHP и checked Unicorn используют общий монитор ISA с физическими гранулами резервирования по 16 байт; пошаговые выходы транспорта не мешают продвижению циклов. Подтверждённые записи сбрасывают резервирование даже без изменения байтов, включая псевдонимы и удерживаемые представления. Остановка сохраняет неопубликованное состояние; снимок не отменяет промежуточные записи. Эксклюзивные операции checked следуют FEAT_LSE2: невыровненный операнд допустим внутри выровненного 16-байтного блока; пересечение его границы даёт `alignment`. Записи той же разрядности сопоставляются с зарезервированной физической гранулой. Выравнивание и права проверяются до результата условной записи, даже при утраченном резервировании. MMIO и параллельный SMP не входят в профиль. Unicorn ARM64 использует этот монитор и в контракте `Software`, включая совместную физическую RAM для программных и checked-CPU. Контракт `Software` Unicorn сохраняет естественное выравнивание движка. + + +Общий слой ISA поддерживает FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` и знаковые/беззнаковые min/max для байта, полуслова, слова и двойного слова, включая acquire/release. Checked использует описанное выше выравнивание, Unicorn `Software` — естественное. Сравнения учитывают ширину операнда, возвращаемые старые значения расширяются нулями. Неудачный CAS также требует права записи и записывает старое значение: такой разрешённый Arm выбор инвалидирует физические резервирования. Обработчики видят прежнее состояние CPU/RAM; отмена и синхронный сбой не публикуют частичные эффекты. Физическая блокировка упорядочивает кооперативные CPU, без параллельного SMP и атомарных MMIO. `MRS/MSR NZCV` переносит четыре флага согласно правилам зарезервированных битов и нулевого регистра. \ No newline at end of file diff --git a/docs/ru/testing.md b/docs/ru/testing.md index d50d7878eb..f174156905 100644 --- a/docs/ru/testing.md +++ b/docs/ru/testing.md @@ -1483,6 +1483,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` проверяет разрядности, пары, acquire/release, перекрытия регистров, псевдонимы, ошибки, снимки, остановку или сбой наблюдателей и конкуренцию CPU. `RAMReservationTests.cpp` проверяет одинаковые записи, ABA, повторное выделение, откат и влияние строковых инструкций и `ENTER` в KVM/Unicorn. Исходные процессы Windows ARM64 выполняют эксклюзивные циклы. `scripts/check_aarch64_exclusives.py` фиксирует исходные инструкции и исключения выравнивания в Windows ARM64 CI. Эти наблюдения не подтверждают выполнение ARM64-бэкендов KVM/WHP; недоступные профили явно пропускаются. Те же тесты охватывают программный Unicorn, взаимодействие контрактов, одинаковые и ABA-записи, исполняемые псевдонимы в одном запуске, а также `DC ZVA` и отмену наблюдателями. `windows-alignment-oracle.yml` также запускает ARM64-пробу: 1 320 наблюдений охватывают все невыровненные смещения, четыре последовательности чтения/записи и доступную для записи, только для чтения, недоступную и пересекающую границу страниц память. Сохраняются полная разрядность начальных значений регистров и частичные записи до ошибки. `WindowsExclusiveProcessTests.cpp` сравнивает 1 320 исходных наблюдений Windows ARM64 с нативными хешами в `WindowsExclusiveNative.def`. Нормализуется только размещение кода и данных; значения регистров, метаданные исключений и эффекты RAM сохраняются. +`AArch64AtomicTests.cpp` проверяет 168 независимо ассемблированных кодировок LSE, наложения регистров, знаковые сравнения, права, отмену, физические резервирования и NZCV. `scripts/check_aarch64_atomics.py` собирает 1 100 исходных записей Windows ARM64 с полными результатами, контекстом исключений и областью RAM; тесты парсера отвергают неполные и противоречивые доказательства. Нативное исполнение KVM/WHP проверяется отдельно. + Структурно константные нативные цели напрямую используют существующее планирование с проверкой достижимости. Единственная символическая цель сохраняет входной предикат только после полного перебора. Регрессии проверяют 128 константных переходов с бюджетом запросов линейного исполнения и 32 вычисляемых перехода с двумя запросами перебора на переход, сохраняя свободные старшие биты адресов и области ветвей. Изменённые полные состояния, отсутствие выравнивания, нулевой лимит целей и недостаточные бюджеты запросов или инструкций отклоняются. Существующие проверки отказа для нескольких целей и неполного перебора остаются обязательными. Нативная ветвь сохраняет входную область на одном ребре только после завершённого доказательства UNSAT, исключающего другое ребро. Тесты проверяют 32 условных перехода в обоих направлениях при лимите 512 вентилей решателя, точный и уменьшенный на один запрос бюджеты, а также исчерпание вентилей. Изменённое или отсутствующее выравнивание, обратные сравнения и изменённое конечное состояние должны отклоняться; существующие тесты произвольного неопределённого управления и двух достижимых рёбер остаются обязательными. diff --git a/docs/testing.md b/docs/testing.md index 26d2539be5..6ef7dbeb43 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -2287,6 +2287,8 @@ native Android equivalence; unavailable hardware cells remain explicit skips. `AArch64ExclusiveTests.cpp` checks scalar/pair widths, acquire/release forms, register overlaps, aliases, alignment and permission faults, snapshots, observer cancellation/failure and competing CPUs. `RAMReservationTests.cpp` checks identical-value writes, ABA, allocation reuse, rollback and KVM/Unicorn string and `ENTER` interference. Original ARM64 Windows process fixtures execute exclusive loops. `scripts/check_aarch64_exclusives.py` runs the original instructions and captures alignment exception records on Windows ARM64 CI. Native instruction observations do not establish ARM64 KVM/WHP backend execution; unavailable profiles remain explicit skips. The same exclusive cases cover software Unicorn, cross-contract interference, identical-value and ABA writes, same-run executable aliases, and `DC ZVA` writes with observer cancellation. `windows-alignment-oracle.yml` also runs the ARM64 probe: 1,320 observations cover every misaligned offset, four load/store sequences, and writable, read-only, inaccessible and split-page memory. The probe retains full-width register seeds and records any committed fault prefix. `WindowsExclusiveProcessTests.cpp` checks 1,320 original Windows ARM64 observations against the native digests in `WindowsExclusiveNative.def`, retaining register values, exception metadata and RAM effects while normalizing only code/data placement. +`AArch64AtomicTests.cpp` covers 168 independently assembled LSE encodings, register aliases, signed comparisons, permissions, cancellation, physical reservations and NZCV transfers. `scripts/check_aarch64_atomics.py` collects 1,100 original Windows ARM64 records with complete operand results, exception context and RAM footprints; parser tests reject missing or inconsistent evidence. Native KVM/WHP execution remains a separate validation requirement. + The shared XSAVE decoder distinguishes standard and compacted initial SSE state. With XSTATE_BV[1] clear, both forms initialize XMM registers; standard format still reads and validates MXCSR, while compacted format initializes MXCSR. `X64XsaveCases.def` supplies independent packet layouts and original host XRSTOR programs. `X64XsaveTests.cpp` checks rejected-state atomicity and compares both formats with actual host execution, preserving the caller’s FP/SSE state. The host oracle skips explicitly when the architecture or required instruction feature is unavailable. `X64FPState.def` declares compacted AVX, AVX-512, CET_U/CET_S and AMX transport layouts, including 64-byte component alignment. Present extension payloads must be architectural zero init state; absent payloads and alignment padding do not define state. Layout bits determine offsets, and unknown layouts, non-initial payloads or incorrect lengths fail before publication. `CompactedOffsetsFollowLayoutRatherThanPresentBits`, `WideLayoutIgnoresAbsentComponentsAndAlignmentPadding`, `InitialCETComponentsDoNotHideFPState` and `InitialWideComponentsDoNotHideFPState` cover 872-byte and 10752-byte WHP packets. This transport support does not admit those extension instructions. diff --git a/docs/zh-CN/architecture.md b/docs/zh-CN/architecture.md index 32bf4b4230..8045262834 100644 --- a/docs/zh-CN/architecture.md +++ b/docs/zh-CN/architecture.md @@ -1303,7 +1303,9 @@ CoreImage 的强 SDK 声明保留 `imageWithCGImage:` 和 `imageByApplyingTransf `AuthenticatedSourceFrameLoads` 可通过 `swiftMetadataAccessorSourceCallHint` 跨越元数据访问器调用。两条声明路径复用已有的编译器证据表、准确的强链接 provider,以及完整的 Swift 请求和元数据响应 ABI。双寄存器响应与通常的易失寄存器破坏规则保持完整;私有 spill 仍须通过共享的到达字节证明。这不提供值布局、见证内存效应、栈借用或动态栈分配模型。 -`AArch64ExclusiveInstructions.def` 统一定义独占指令的编码准入;`AArch64ExclusiveExecution` 在物理执行锁内完成寄存器及监视器状态转换。`RAMReservation` 保留分配身份,接收主机写入、保留视图、`RAMTransaction`、字符串指令及 `ENTER` 故障前部分写入的提交通知。临时写入和回滚不会使保留状态失效。不声明写入范围的执行会使已有保留状态失效;声明范围的执行按实际提交范围跟踪。客户机故障、服务陷阱和地址空间绑定会清除本地监视器。原始 `LDAR[B/H]` 和 `STLR[B/H]` 仍通过后端执行,并保留自然对齐 RAM 契约。 Unicorn 软件适配层在观察回调允许写入后,通过写入前钩子保守地使保留状态失效;独占存储完成后,会先清除所有可执行别名的翻译缓存,再继续同一次执行。 `AArch64ExclusiveAccess` 选择对齐策略:checked 配置采用 FEAT_LSE2,软件 Unicorn 保留引擎的特性模型;预约按相同操作数宽度及物理粒度匹配。 +`AArch64ExclusiveInstructions.def` 统一定义独占指令的编码准入;`AArch64ExclusiveExecution` 在物理执行锁内完成寄存器及监视器状态转换。`RAMReservation` 保留分配身份,接收主机写入、保留视图、`RAMTransaction`、字符串指令及 `ENTER` 故障前部分写入的提交通知。临时写入和回滚不会使保留状态失效。不声明写入范围的执行会使已有保留状态失效;声明范围的执行按实际提交范围跟踪。客户机故障、服务陷阱和地址空间绑定会清除本地监视器。原始 `LDAR[B/H]` 和 `STLR[B/H]` 仍通过后端执行,并保留自然对齐 RAM 契约。 Unicorn 软件适配层在观察回调允许写入后,通过写入前钩子保守地使保留状态失效;独占存储完成后,会先清除所有可执行别名的翻译缓存,再继续同一次执行。 `AArch64AtomicAccess` 选择对齐策略:checked 配置采用 FEAT_LSE2,软件 Unicorn 保留引擎的特性模型;预约按相同操作数宽度及物理粒度匹配。 + +`AArch64AtomicInstructions.def` 负责 LSE 指令准入,`AArch64AtomicExecution` 负责原子完成,`AArch64AtomicMemory` 与独占指令共用对齐和类型化故障规则。checked 传输和 Unicorn 软件桥接共用这些实现。 `objcNonEscapingBlockSourceFrameEffects` 通过共享的 `sourceFrameCallArgumentStorage` 查询认证栈上 block 的同步借用。它重放原始机器指令、当前接收者与派发、强导入、完整头部与描述符、回调 ABI 和到达字节。描述符限定可写范围,填充字节不因此获得初始化证明;捕获字段类型与回调本体仍须独立验证。循环中的不确定观察和私有栈地址捕获会被拒绝,依赖只使用已完成的证明轮次。 diff --git a/docs/zh-CN/cpu-execution.md b/docs/zh-CN/cpu-execution.md index dfebb82057..f7abb6db0c 100644 --- a/docs/zh-CN/cpu-execution.md +++ b/docs/zh-CN/cpu-execution.md @@ -204,4 +204,7 @@ checked Unicorn 使用 `MachineRunControl`:ARM64 维护、来宾执行和完 [配置、签名与硬件验证](macos-hvf.md) -checked ARM64 支持 8/16/32/64 位 `LDXR/STXR`、32/64 位寄存器对 `LDXP/STXP`、相应的 acquire/release 形式及 `CLREX`。KVM、WHP 和 checked Unicorn 共用 ISA 层的独占监视器,以 16 字节物理范围记录保留状态,传输层单步退出不会破坏循环进展。已提交的写入即使没有改变字节,也会使保留状态失效;别名和保留视图写入遵循同一规则。停止保留尚未发布的状态,快照不能撤销期间发生的写入。checked 独占指令采用 FEAT_LSE2 对齐规则:操作数可在同一个 16 字节对齐块内未对齐访问,跨越该块才产生 `alignment` 故障。同宽条件存储按保留的物理粒度匹配;即使监视器已失效,也先检查对齐和权限,再决定条件存储是否成功。该配置仍不包含 MMIO、可选 LSE 原子指令或并行 SMP。 ARM64 Unicorn 的 `Software` 契约也使用此监视器,包括软件和 checked CPU 共用物理 RAM 的情况。 Unicorn 的 `Software` 配置保留引擎的自然对齐模型。 +checked ARM64 支持 8/16/32/64 位 `LDXR/STXR`、32/64 位寄存器对 `LDXP/STXP`、相应的 acquire/release 形式及 `CLREX`。KVM、WHP 和 checked Unicorn 共用 ISA 层的独占监视器,以 16 字节物理范围记录保留状态,传输层单步退出不会破坏循环进展。已提交的写入即使没有改变字节,也会使保留状态失效;别名和保留视图写入遵循同一规则。停止保留尚未发布的状态,快照不能撤销期间发生的写入。checked 独占指令采用 FEAT_LSE2 对齐规则:操作数可在同一个 16 字节对齐块内未对齐访问,跨越该块才产生 `alignment` 故障。同宽条件存储按保留的物理粒度匹配;即使监视器已失效,也先检查对齐和权限,再决定条件存储是否成功。该配置仍不包含 MMIO 或并行 SMP。 ARM64 Unicorn 的 `Software` 契约也使用此监视器,包括软件和 checked CPU 共用物理 RAM 的情况。 Unicorn 的 `Software` 配置保留引擎的自然对齐模型。 + + +同一 ISA 层支持 FEAT_LSE `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET` 及有符号/无符号 min/max,包括字节、半字、字、双字和 acquire/release 形式。checked 配置采用上述对齐策略,Unicorn `Software` 保留自然对齐。比较按操作数宽度执行,返回的旧值零扩展。CAS 比较失败仍要求写权限,并选择 Arm 允许的旧值回写行为,使物理保留状态失效。回调看到提交前的 CPU/RAM 状态;取消和同步故障不会发布部分原子操作结果。物理执行锁保证协作式 CPU 的顺序,不提供并行 SMP 或 MMIO 原子操作。`MRS/MSR NZCV` 按架构规则传输四个条件标志,正确处理保留位和零寄存器。 \ No newline at end of file diff --git a/docs/zh-CN/testing.md b/docs/zh-CN/testing.md index db03109cca..24a7adf03b 100644 --- a/docs/zh-CN/testing.md +++ b/docs/zh-CN/testing.md @@ -1323,6 +1323,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` 覆盖标量及成对宽度、acquire/release 形式、寄存器重叠、别名、对齐与权限故障、快照、观察回调取消或失败以及双 CPU 竞争。`RAMReservationTests.cpp` 覆盖相同值写入、ABA、分配复用、回滚,以及 KVM/Unicorn 字符串和 `ENTER` 写入的干扰。原始 ARM64 Windows 进程样例执行独占循环;`scripts/check_aarch64_exclusives.py` 在 Windows ARM64 CI 上执行原始指令并保存对齐异常记录。原生指令证据不代表已验证 ARM64 KVM/WHP 后端执行;不可用配置仍明确记为跳过。 相同的独占指令用例还覆盖软件 Unicorn、跨契约干扰、同值及 ABA 写入、同次执行中的可执行别名,以及 `DC ZVA` 写入和观察回调取消。 `windows-alignment-oracle.yml` 也运行 ARM64 探针:1,320 条观察记录覆盖所有未对齐偏移、四种读写序列,以及可写、只读、不可访问和跨页内存。探针保留完整位宽的寄存器测试值,并记录故障前已提交的部分写入。 `WindowsExclusiveProcessTests.cpp` 将 1,320 条原始 Windows ARM64 观察结果与 `WindowsExclusiveNative.def` 中的原生摘要逐项核对,保留寄存器值、异常元数据和 RAM 效果,仅归一化代码与数据的放置地址。 +`AArch64AtomicTests.cpp` 覆盖 168 种独立汇编的 LSE 编码、寄存器别名、有符号比较、权限、取消、物理保留状态及 NZCV 传输。`scripts/check_aarch64_atomics.py` 收集 1,100 条原始 Windows ARM64 记录,包含完整操作数结果、异常上下文和 RAM 写入范围;解析测试拒绝缺失或不一致的证据。原生 KVM/WHP 执行仍需单独验证。 + 结构上已为常量的原生目标直接使用现有的可达性检查调度。符号单目标只有在穷尽枚举后才复用传入谓词。回归在直线执行的查询预算内验证 128 次常量跳转,并按每次转移两次枚举查询的预算验证 32 次计算目标跳转,保留未约束的地址高位和分支域。完整状态结果被修改、缺少对齐约束、目标数量上限为零或查询与指令预算不足时必须拒绝。多目标和未完成枚举的现有拒绝检查仍然必需。 只有完成 UNSAT 证明、排除另一条边后,原生分支才在当前边保留传入域。测试在 512 个求解门内验证两个方向各 32 次带条件跳转,并检查精确与少一次的查询预算及求解门耗尽。修改或删除对齐条件、反转比较以及修改终态都必须拒绝;任意未定义控制和两条边均可达的现有测试仍然必需。 diff --git a/docs/zh-TW/architecture.md b/docs/zh-TW/architecture.md index 2f3716ab22..c14a7f735a 100644 --- a/docs/zh-TW/architecture.md +++ b/docs/zh-TW/architecture.md @@ -1241,7 +1241,9 @@ CoreImage 的強 SDK 宣告保留 `imageWithCGImage:` 和 `imageByApplyingTransf `AuthenticatedSourceFrameLoads` 可透過 `swiftMetadataAccessorSourceCallHint` 跨越中繼資料存取器呼叫。兩條宣告路徑重用既有的編譯器證據表、準確的強連結 provider,以及完整的 Swift 請求與中繼資料回應 ABI。雙暫存器回應與一般的易失暫存器破壞規則保持完整;私有 spill 仍須通過共用的到達位元組證明。這不提供值配置、見證記憶體效應、堆疊借用或動態堆疊配置模型。 -`AArch64ExclusiveInstructions.def` 統一定義獨佔指令的編碼准入;`AArch64ExclusiveExecution` 在實體執行鎖內完成暫存器與監視器狀態轉換。`RAMReservation` 保留配置身分,接收主機寫入、保留檢視、`RAMTransaction`、字串指令及 `ENTER` 錯誤前部分寫入的提交通知。暫時寫入及回復不會使保留狀態失效。未宣告寫入範圍的執行會使既有保留狀態失效;已宣告範圍的執行按實際提交範圍追蹤。客體錯誤、服務陷阱及位址空間綁定會清除本地監視器。原始 `LDAR[B/H]` 與 `STLR[B/H]` 仍透過後端執行,保留自然對齊 RAM 契約。 Unicorn 軟體適配層在觀察回呼允許寫入後,透過寫入前掛鉤保守地使保留狀態失效;獨佔儲存完成後,會先清除所有可執行別名的翻譯快取,再繼續同一次執行。 `AArch64ExclusiveAccess` 選擇對齊策略:checked 設定採用 FEAT_LSE2,軟體 Unicorn 保留引擎的特性模型;保留狀態依相同運算元寬度與實體粒度比對。 +`AArch64ExclusiveInstructions.def` 統一定義獨佔指令的編碼准入;`AArch64ExclusiveExecution` 在實體執行鎖內完成暫存器與監視器狀態轉換。`RAMReservation` 保留配置身分,接收主機寫入、保留檢視、`RAMTransaction`、字串指令及 `ENTER` 錯誤前部分寫入的提交通知。暫時寫入及回復不會使保留狀態失效。未宣告寫入範圍的執行會使既有保留狀態失效;已宣告範圍的執行按實際提交範圍追蹤。客體錯誤、服務陷阱及位址空間綁定會清除本地監視器。原始 `LDAR[B/H]` 與 `STLR[B/H]` 仍透過後端執行,保留自然對齊 RAM 契約。 Unicorn 軟體適配層在觀察回呼允許寫入後,透過寫入前掛鉤保守地使保留狀態失效;獨佔儲存完成後,會先清除所有可執行別名的翻譯快取,再繼續同一次執行。 `AArch64AtomicAccess` 選擇對齊策略:checked 設定採用 FEAT_LSE2,軟體 Unicorn 保留引擎的特性模型;保留狀態依相同運算元寬度與實體粒度比對。 + +`AArch64AtomicInstructions.def` 負責 LSE 指令准入,`AArch64AtomicExecution` 負責原子完成,`AArch64AtomicMemory` 與獨占指令共用對齊和型別化故障規則。checked 傳輸與 Unicorn 軟體橋接共用這些實作。 `objcNonEscapingBlockSourceFrameEffects` 透過共用的 `sourceFrameCallArgumentStorage` 查詢認證堆疊上 block 的同步借用。它重播原始機器指令、目前接收者與派發、強匯入、完整標頭與描述符、回呼 ABI 和到達位元組。描述符限定可寫範圍,填充位元組不因此取得初始化證明;擷取欄位型別與回呼本體仍須獨立驗證。循環中的不確定觀察和私有堆疊位址擷取會被拒絕,相依性只使用已完成的證明輪次。 diff --git a/docs/zh-TW/cpu-execution.md b/docs/zh-TW/cpu-execution.md index 1a59a3fcc7..a4b239d925 100644 --- a/docs/zh-TW/cpu-execution.md +++ b/docs/zh-TW/cpu-execution.md @@ -204,4 +204,7 @@ checked Unicorn 使用 `MachineRunControl`:ARM64 維護、客體執行與完 [Setup, signing and native hardware validation (English)](../macos-hvf.md) -checked ARM64 支援 8/16/32/64 位元 `LDXR/STXR`、32/64 位元暫存器對 `LDXP/STXP`、對應的 acquire/release 形式及 `CLREX`。KVM、WHP 與 checked Unicorn 共用 ISA 層的獨佔監視器,以 16 位元組實體範圍記錄保留狀態,傳輸層單步退出不會破壞迴圈進展。已提交的寫入即使未改變位元組,也會使保留狀態失效;別名及保留檢視的寫入遵循相同規則。停止保留尚未發布的狀態,快照無法撤銷期間的寫入。checked 獨佔指令採用 FEAT_LSE2 對齊規則:運算元可在同一個 16 位元組對齊區塊內未對齊存取,跨越該區塊才產生 `alignment` 錯誤。同寬條件儲存依保留的實體粒度比對;即使監視器已失效,也先檢查對齊及權限,再決定條件儲存是否成功。此設定仍不包含 MMIO、可選 LSE 原子指令或平行 SMP。 ARM64 Unicorn 的 `Software` 契約也使用此監視器,包括軟體與 checked CPU 共用實體 RAM 的情況。 Unicorn 的 `Software` 設定保留引擎的自然對齊模型。 +checked ARM64 支援 8/16/32/64 位元 `LDXR/STXR`、32/64 位元暫存器對 `LDXP/STXP`、對應的 acquire/release 形式及 `CLREX`。KVM、WHP 與 checked Unicorn 共用 ISA 層的獨佔監視器,以 16 位元組實體範圍記錄保留狀態,傳輸層單步退出不會破壞迴圈進展。已提交的寫入即使未改變位元組,也會使保留狀態失效;別名及保留檢視的寫入遵循相同規則。停止保留尚未發布的狀態,快照無法撤銷期間的寫入。checked 獨佔指令採用 FEAT_LSE2 對齊規則:運算元可在同一個 16 位元組對齊區塊內未對齊存取,跨越該區塊才產生 `alignment` 錯誤。同寬條件儲存依保留的實體粒度比對;即使監視器已失效,也先檢查對齊及權限,再決定條件儲存是否成功。此設定仍不包含 MMIO 或平行 SMP。 ARM64 Unicorn 的 `Software` 契約也使用此監視器,包括軟體與 checked CPU 共用實體 RAM 的情況。 Unicorn 的 `Software` 設定保留引擎的自然對齊模型。 + + +同一 ISA 層支援 FEAT_LSE `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET` 與有符號/無符號 min/max,包含位元組、半字、字、雙字及 acquire/release 形式。checked 設定採用上述對齊策略,Unicorn `Software` 保留自然對齊。比較依運算元寬度執行,傳回的舊值零擴展。CAS 比較失敗仍要求寫入權限,並選擇 Arm 允許的舊值回寫,使實體保留狀態失效。回呼看到提交前的 CPU/RAM;取消與同步故障不發布部分原子結果。實體執行鎖確保協作式 CPU 的順序,不提供平行 SMP 或 MMIO 原子操作。`MRS/MSR NZCV` 依架構規則傳輸四個條件旗標,正確處理保留位與零暫存器。 \ No newline at end of file diff --git a/docs/zh-TW/testing.md b/docs/zh-TW/testing.md index 24fc231ee4..959507e7aa 100644 --- a/docs/zh-TW/testing.md +++ b/docs/zh-TW/testing.md @@ -1361,6 +1361,8 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` 涵蓋標量與成對寬度、acquire/release 形式、暫存器重疊、別名、對齊及權限錯誤、快照、觀察回呼取消或失敗,以及雙 CPU 競爭。`RAMReservationTests.cpp` 涵蓋相同值寫入、ABA、配置重用、回復,以及 KVM/Unicorn 字串和 `ENTER` 寫入的干擾。原始 ARM64 Windows 行程範例執行獨佔迴圈;`scripts/check_aarch64_exclusives.py` 在 Windows ARM64 CI 執行原始指令並保存對齊例外記錄。原生指令證據不代表已驗證 ARM64 KVM/WHP 後端執行;不可用設定仍明確列為略過。 相同的獨佔指令案例也涵蓋軟體 Unicorn、跨契約干擾、同值及 ABA 寫入、同次執行中的可執行別名,以及 `DC ZVA` 寫入與觀察回呼取消。 `windows-alignment-oracle.yml` 也執行 ARM64 探針:1,320 筆觀察記錄涵蓋所有未對齊偏移、四種讀寫序列,以及可寫、唯讀、不可存取和跨頁記憶體。探針保留完整位寬的暫存器測試值,並記錄錯誤前已提交的部分寫入。 `WindowsExclusiveProcessTests.cpp` 將 1,320 筆原始 Windows ARM64 觀察結果與 `WindowsExclusiveNative.def` 的原生摘要核對,保留暫存器值、例外中繼資料及 RAM 效果,只正規化程式碼與資料的放置位址。 +`AArch64AtomicTests.cpp` 涵蓋 168 種獨立組譯的 LSE 編碼、暫存器別名、有符號比較、權限、取消、實體保留狀態與 NZCV 傳輸。`scripts/check_aarch64_atomics.py` 收集 1,100 筆原始 Windows ARM64 紀錄,包含完整運算結果、例外上下文與 RAM 範圍;解析測試拒絕缺失或不一致的證據。原生 KVM/WHP 執行仍需單獨驗證。 + 結構上已為常數的原生目標直接使用既有的可達性檢查排程。符號單目標只有在窮盡列舉後才重用傳入述詞。迴歸在直線執行的查詢預算內驗證 128 次常數跳轉,並按每次轉移兩次列舉查詢的預算驗證 32 次計算目標跳轉,保留未約束的位址高位元和分支域。完整狀態結果遭修改、缺少對齊約束、目標數量上限為零或查詢與指令預算不足時必須拒絕。多目標和未完成列舉的既有拒絕檢查仍然必要。 只有完成 UNSAT 證明、排除另一條邊後,原生分支才在目前的邊保留傳入域。測試在 512 個求解閘內驗證兩個方向各 32 次條件跳轉,並檢查精確與少一次的查詢預算及求解閘耗盡。修改或移除對齊條件、反轉比較及修改終態都必須拒絕;任意未定義控制和兩條邊皆可達的既有測試仍然必要。 diff --git a/lib/emulation/CMakeLists.txt b/lib/emulation/CMakeLists.txt index 861f38b44b..b4eca766e3 100644 --- a/lib/emulation/CMakeLists.txt +++ b/lib/emulation/CMakeLists.txt @@ -22,6 +22,9 @@ add_neverd_component_library(NeverDEmulationArch arch/aarch64/AArch64State.cpp arch/aarch64/AArch64MachineProbe.cpp arch/aarch64/AArch64InstructionEffects.cpp + arch/aarch64/AArch64Atomic.cpp + arch/aarch64/AArch64AtomicExecution.cpp + arch/aarch64/AArch64AtomicMemory.cpp arch/aarch64/AArch64Exclusive.cpp arch/aarch64/AArch64ExclusiveExecution.cpp arch/x86_64/CheckedX64Backend.cpp @@ -29,7 +32,7 @@ add_neverd_component_library(NeverDEmulationArch arch/x86_64/CheckedX64Stack.cpp arch/x86_64/CheckedX64Frame.cpp arch/aarch64/CheckedAArch64Backend.cpp - arch/aarch64/CheckedAArch64Exclusive.cpp + arch/aarch64/CheckedAArch64Atomic.cpp LINK_COMPONENTS EmulationCore LLVM_COMPONENTS Support) @@ -50,7 +53,7 @@ if(NEVERD_EMULATION_BACKEND_UNICORN) backends/unicorn/UnicornBackend.cpp backends/unicorn/UnicornMachine.cpp backends/unicorn/UnicornArchitecture.cpp - backends/unicorn/UnicornExclusive.cpp + backends/unicorn/UnicornAArch64Atomic.cpp LINK_COMPONENTS EmulationArch LLVM_COMPONENTS Support LINK_LIBS unicorn) diff --git a/lib/emulation/arch/aarch64/AArch64Atomic.cpp b/lib/emulation/arch/aarch64/AArch64Atomic.cpp new file mode 100644 index 0000000000..e3e53c7e32 --- /dev/null +++ b/lib/emulation/arch/aarch64/AArch64Atomic.cpp @@ -0,0 +1,97 @@ +//===- AArch64Atomic.cpp - FEAT_LSE instruction admission -----------------===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#include "AArch64Atomic.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Endian.h" + +namespace neverd::emulation { +namespace { +#define NEVERD_AARCH64_FIELD(Name, Value) constexpr unsigned Name = Value; +#define NEVERD_AARCH64_ATOMIC_FIELD NEVERD_AARCH64_FIELD +#include "AArch64AtomicInstructions.def" +#include "CheckedAArch64Instructions.def" +#undef NEVERD_AARCH64_FIELD +#undef NEVERD_AARCH64_ATOMIC_FIELD +using Operation = AArch64AtomicInstruction::Operation; +struct Encoding { + uint32_t Mask, Value; + Operation Kind; + unsigned IDs[4][3]; + bool pair() const { return Kind == Operation::ComparePair; } + unsigned sizeIndex(uint32_t Word) const { return Word >> SizeShift; } + unsigned id(uint32_t Word) const { + const bool Compare = Kind == Operation::Compare || pair(); + const unsigned Acquire = + (Word >> (Compare ? CompareAcquireShift : RMWAcquireShift)) & 1; + const unsigned Release = + (Word >> (Compare ? CompareReleaseShift : RMWReleaseShift)) & 1; + const unsigned Width = pair() ? 0 : std::min(sizeIndex(Word), 2u); + return IDs[Acquire + 2 * Release][Width]; + } +}; +#define ATOMIC_IDS(Name) \ + {AARCH64_INS_##Name##B, AARCH64_INS_##Name##H, AARCH64_INS_##Name} +constexpr Encoding Encodings[] = { +#define NEVERD_AARCH64_ATOMIC_RMW(Name, Kind, Mask, Value) \ + {Mask, \ + Value, \ + Operation::Kind, \ + {ATOMIC_IDS(Name), ATOMIC_IDS(Name##A), ATOMIC_IDS(Name##L), \ + ATOMIC_IDS(Name##AL)}}, +#define NEVERD_AARCH64_ATOMIC_COMPARE NEVERD_AARCH64_ATOMIC_RMW +#define NEVERD_AARCH64_ATOMIC_PAIR(Name, Kind, Mask, Value) \ + {Mask, \ + Value, \ + Operation::Kind, \ + {{AARCH64_INS_##Name}, \ + {AARCH64_INS_##Name##A}, \ + {AARCH64_INS_##Name##L}, \ + {AARCH64_INS_##Name##AL}}}, +#include "AArch64AtomicInstructions.def" +#undef NEVERD_AARCH64_ATOMIC_RMW +#undef NEVERD_AARCH64_ATOMIC_COMPARE +#undef NEVERD_AARCH64_ATOMIC_PAIR +}; +#undef ATOMIC_IDS +} // namespace +bool isAArch64Atomic(uint32_t Word) { + return llvm::any_of( + Encodings, [Word](const auto &E) { return (Word & E.Mask) == E.Value; }); +} +llvm::Expected> +decodeAArch64Atomic(const cs_insn &I, const AArch64MachineState &CPU) { + if (I.size != aarch64::InstructionBytes || !I.detail) + return llvm::make_error(); + const uint32_t Word = llvm::support::endian::read32le(I.bytes); + for (const auto &E : Encodings) + if ((Word & E.Mask) == E.Value && I.id == E.id(Word)) + return decodeAArch64Atomic(Word, CPU); + return std::nullopt; +} +llvm::Expected> +decodeAArch64Atomic(uint32_t Word, const AArch64MachineState &CPU) { + for (const auto &E : Encodings) { + if ((Word & E.Mask) != E.Value) + continue; + const unsigned Source = (Word >> IndexShift) & RegisterMask; + const unsigned Target = Word & RegisterMask; + const unsigned Base = (Word >> BaseShift) & RegisterMask; + if (E.pair() && + (Source % PairRegisterAlignment || Target % PairRegisterAlignment)) + return llvm::make_error(); + return AArch64AtomicInstruction{ + E.Kind, + (E.pair() ? PairFirstWidth : 1u) << E.sizeIndex(Word), + E.pair() ? 2u : 1u, + Source, + Target, + Base == aarch64::GPRCount ? CPU.reg(AArch64Register::SP) + : CPU.Registers[Base]}; + } + return std::nullopt; +} +} // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/AArch64Atomic.h b/lib/emulation/arch/aarch64/AArch64Atomic.h new file mode 100644 index 0000000000..273c313641 --- /dev/null +++ b/lib/emulation/arch/aarch64/AArch64Atomic.h @@ -0,0 +1,42 @@ +//===- AArch64Atomic.h - Architectural LSE atomic instructions ------------===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#ifndef NEVERD_EMULATION_AARCH64_ATOMIC_H +#define NEVERD_EMULATION_AARCH64_ATOMIC_H +#include "AArch64AtomicMemory.h" + +#include + +namespace neverd::emulation { +struct AArch64AtomicInstruction { + enum class Operation { +#define NEVERD_AARCH64_ATOMIC_RMW(Name, Kind, Mask, Value) Kind, +#define NEVERD_AARCH64_ATOMIC_COMPARE NEVERD_AARCH64_ATOMIC_RMW +#define NEVERD_AARCH64_ATOMIC_PAIR NEVERD_AARCH64_ATOMIC_RMW +#include "AArch64AtomicInstructions.def" +#undef NEVERD_AARCH64_ATOMIC_RMW +#undef NEVERD_AARCH64_ATOMIC_COMPARE +#undef NEVERD_AARCH64_ATOMIC_PAIR + } Kind; + unsigned Width, Count, Source, Target; + uint64_t Address; + unsigned size() const { return Width * Count; } + bool compare() const { + return Kind == Operation::Compare || Kind == Operation::ComparePair; + } +}; +/// Reject undefined pair encodings before callbacks, RAM or CPU mutation. +llvm::Expected> +decodeAArch64Atomic(const cs_insn &, const AArch64MachineState &); +llvm::Expected> +decodeAArch64Atomic(uint32_t Word, const AArch64MachineState &); +bool isAArch64Atomic(uint32_t Word); +/// The caller holds the shared physical execution lease. Acquire/release +/// ordering is provided by that lease in this cooperative execution profile. +llvm::Error executeAArch64Atomic(const AArch64AtomicInstruction &, + AArch64MachineState &, MemoryProjection &, + const AArch64AtomicAccess &); +} // namespace neverd::emulation +#endif diff --git a/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp b/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp new file mode 100644 index 0000000000..7b4e4ba3c7 --- /dev/null +++ b/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp @@ -0,0 +1,138 @@ +//===- AArch64AtomicExecution.cpp - Commit LSE effects under a RAM lease +//---===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#include "../../core/RAMTransaction.h" +#include "AArch64Atomic.h" + +#include "llvm/Support/MathExtras.h" + +#include +#include +#include + +namespace neverd::emulation { +llvm::Error executeAArch64Atomic(const AArch64AtomicInstruction &I, + AArch64MachineState &CPU, + MemoryProjection &Memory, + const AArch64AtomicAccess &Access) { + using Operation = AArch64AtomicInstruction::Operation; + const unsigned Size = I.size(); + if (!isAArch64AtomicAligned(I.Address, Size, Access.Alignment)) { + BackendFault Fault{BackendFaultKind::Alignment, + CPU.reg(AArch64Register::PC), I.Address, Size, + BackendAccessKind::Write}; + Fault.Cause = BackendFaultCause::OperandAlignment; + return Access.RaiseFault(Fault); + } + const auto Page = Memory.mappings().find(I.Address & ~(memory::PageSize - 1)); + if (Page != Memory.mappings().end() && Page->second.IO) + return llvm::make_error(); + if (Access.Hooks.Read) + Access.Hooks.Read(I.Address, Size); + if (Access.Stopped()) + return llvm::Error::success(); + // Every CAS checks write permission, including a comparison mismatch. + if (auto E = Access.CheckAccess(I.Address, Size, Read | Write)) + return E; + if (Access.Stopped()) + return llvm::Error::success(); + std::array Bytes{}; + if (auto E = + Memory.read(I.Address, llvm::MutableArrayRef(Bytes).take_front(Size))) + return E; + const uint64_t Mask = llvm::maskTrailingOnes(I.Width * CHAR_BIT); + auto Register = [&](unsigned R) { + return R == aarch64::GPRCount ? uint64_t(0) : CPU.Registers[R] & Mask; + }; + auto MemoryValue = [&](unsigned N) { + uint64_t Value = 0; + for (unsigned B = 0; B < I.Width; ++B) + Value |= uint64_t(Bytes[N * I.Width + B]) << (B * CHAR_BIT); + return Value; + }; + bool Matches = true; + if (I.compare()) + for (unsigned N = 0; N < I.Count; ++N) + Matches &= MemoryValue(N) == Register(I.Source + N); + auto Next = CPU; + Next.reg(AArch64Register::PC) += aarch64::InstructionBytes; + for (unsigned N = 0; N < I.Count; ++N) { + const uint64_t Old = MemoryValue(N), Source = Register(I.Source + N); + const uint64_t Sign = uint64_t(1) << (I.Width * CHAR_BIT - 1); + // Biasing the sign bit compares signed operands without narrowing a host + // signed type. Returned old values are always zero-extended. + const bool SignedLess = (Old ^ Sign) < (Source ^ Sign); + uint64_t Value = 0; + switch (I.Kind) { + case Operation::Add: + Value = Old + Source; + break; + case Operation::Clear: + Value = Old & ~Source; + break; + case Operation::Xor: + Value = Old ^ Source; + break; + case Operation::Set: + Value = Old | Source; + break; + case Operation::SignedMax: + Value = SignedLess ? Source : Old; + break; + case Operation::SignedMin: + Value = SignedLess ? Old : Source; + break; + case Operation::UnsignedMax: + Value = std::max(Old, Source); + break; + case Operation::UnsignedMin: + Value = std::min(Old, Source); + break; + case Operation::Swap: + Value = Source; + break; + case Operation::Compare: + case Operation::ComparePair: + // Arm permits an old-value writeback on a failed comparison. This + // profile selects it explicitly, also invalidating physical monitors. + Value = Matches ? Register(I.Target + N) : Old; + break; + } + for (unsigned B = 0; B < I.Width; ++B) + Bytes[N * I.Width + B] = uint8_t(Value >> (B * CHAR_BIT)); + const unsigned Result = (I.compare() ? I.Source : I.Target) + N; + if (Result != aarch64::GPRCount) + Next.Registers[Result] = Old; + } + if (Access.Hooks.Write) + for (unsigned Offset = 0; Offset < Size; Offset += aarch64::WordBytes) { + const unsigned Count = + std::min(Size - Offset, aarch64::WordBytes); + uint64_t Value = 0; + for (unsigned B = 0; B < Count; ++B) + Value |= uint64_t(Bytes[Offset + B]) << (B * CHAR_BIT); + Access.Hooks.Write(I.Address + Offset, Count, Value); + if (Access.Stopped()) + return llvm::Error::success(); + } + const RAMWriteRange Range{I.Address, Size}; + auto Transaction = + RAMTransaction::create(Memory, Range, Size, Access.WritePermissions); + if (!Transaction) + return Transaction.takeError(); + std::memcpy(Memory.physicalPointer(Page->second.Physical + + I.Address % memory::PageSize), + Bytes.data(), Size); + if (auto E = (*Transaction)->stage()) + return E; + if (Access.Stopped()) + return llvm::Error::success(); + if (auto E = (*Transaction)->commit()) + return E; + CPU = Next; + return llvm::Error::success(); +} +} // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/AArch64AtomicInstructions.def b/lib/emulation/arch/aarch64/AArch64AtomicInstructions.def new file mode 100644 index 0000000000..cd0ca0008a --- /dev/null +++ b/lib/emulation/arch/aarch64/AArch64AtomicInstructions.def @@ -0,0 +1,32 @@ +//===- AArch64AtomicInstructions.def - FEAT_LSE atomic encoding families --===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +// Masks admit baseline LSE only. Width and acquire/release bits select the +// exact Capstone ID; register-pair constraints are checked before access. +#ifdef NEVERD_AARCH64_ATOMIC_RMW +NEVERD_AARCH64_ATOMIC_RMW(LDADD, Add, 0x3f20fc00, 0x38200000) +NEVERD_AARCH64_ATOMIC_RMW(LDCLR, Clear, 0x3f20fc00, 0x38201000) +NEVERD_AARCH64_ATOMIC_RMW(LDEOR, Xor, 0x3f20fc00, 0x38202000) +NEVERD_AARCH64_ATOMIC_RMW(LDSET, Set, 0x3f20fc00, 0x38203000) +NEVERD_AARCH64_ATOMIC_RMW(LDSMAX, SignedMax, 0x3f20fc00, 0x38204000) +NEVERD_AARCH64_ATOMIC_RMW(LDSMIN, SignedMin, 0x3f20fc00, 0x38205000) +NEVERD_AARCH64_ATOMIC_RMW(LDUMAX, UnsignedMax, 0x3f20fc00, 0x38206000) +NEVERD_AARCH64_ATOMIC_RMW(LDUMIN, UnsignedMin, 0x3f20fc00, 0x38207000) +NEVERD_AARCH64_ATOMIC_RMW(SWP, Swap, 0x3f20fc00, 0x38208000) +#endif +#ifdef NEVERD_AARCH64_ATOMIC_COMPARE +NEVERD_AARCH64_ATOMIC_COMPARE(CAS, Compare, 0x3fa07c00, 0x08a07c00) +#endif +#ifdef NEVERD_AARCH64_ATOMIC_PAIR +NEVERD_AARCH64_ATOMIC_PAIR(CASP, ComparePair, 0xbfa07c00, 0x08207c00) +#endif +#ifdef NEVERD_AARCH64_ATOMIC_FIELD +NEVERD_AARCH64_ATOMIC_FIELD(RMWAcquireShift, 23) +NEVERD_AARCH64_ATOMIC_FIELD(RMWReleaseShift, 22) +NEVERD_AARCH64_ATOMIC_FIELD(CompareAcquireShift, 22) +NEVERD_AARCH64_ATOMIC_FIELD(CompareReleaseShift, 15) +NEVERD_AARCH64_ATOMIC_FIELD(PairFirstWidth, 4) +NEVERD_AARCH64_ATOMIC_FIELD(PairRegisterAlignment, 2) +#endif diff --git a/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp b/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp new file mode 100644 index 0000000000..384333de57 --- /dev/null +++ b/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp @@ -0,0 +1,29 @@ +//===- AArch64AtomicMemory.cpp - Atomic alignment and fault policy --------===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#include "AArch64AtomicMemory.h" + +#include "llvm/Support/MathExtras.h" + +namespace neverd::emulation { +bool isAArch64AtomicAligned(uint64_t Address, uint64_t Size, + AArch64AtomicAlignment Alignment) { + assert(llvm::isPowerOf2_64(Size) && Size <= aarch64::AtomicAlignmentGranule); + if (Alignment == AArch64AtomicAlignment::Natural) + return Address % Size == 0; + return Address % aarch64::AtomicAlignmentGranule <= + aarch64::AtomicAlignmentGranule - Size; +} +bool isAArch64AtomicAlignmentFault(const BackendFault &Fault) { + return Fault.Kind == BackendFaultKind::Alignment && + Fault.Cause == BackendFaultCause::OperandAlignment && Fault.Address && + Fault.Size && llvm::isPowerOf2_64(*Fault.Size) && + *Fault.Size <= aarch64::AtomicAlignmentGranule && + *Fault.Address % *Fault.Size && + (Fault.Access == BackendAccessKind::Read || + Fault.Access == BackendAccessKind::Write) && + !Fault.Interrupt && !Fault.ErrorCode; +} +} // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/AArch64AtomicMemory.h b/lib/emulation/arch/aarch64/AArch64AtomicMemory.h new file mode 100644 index 0000000000..08b17707db --- /dev/null +++ b/lib/emulation/arch/aarch64/AArch64AtomicMemory.h @@ -0,0 +1,29 @@ +//===- AArch64AtomicMemory.h - Shared atomic memory access policy ---------===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#ifndef NEVERD_EMULATION_AARCH64_ATOMICMEMORY_H +#define NEVERD_EMULATION_AARCH64_ATOMICMEMORY_H +#include "AArch64Machine.h" + +#include "neverd/emulation/CPU.h" + +namespace neverd::emulation { +enum class AArch64AtomicAlignment { Natural, LSE2 }; +/// FEAT_LSE2 admits an atomic operand within one aligned 16-byte quantity. +/// Size is the complete decoded operand, including both members of a pair. +bool isAArch64AtomicAligned(uint64_t Address, uint64_t Size, + AArch64AtomicAlignment); +struct AArch64AtomicAccess { + const BackendHooks &Hooks; + llvm::function_ref Stopped; + llvm::function_ref RaiseFault; + llvm::function_ref CheckAccess; + unsigned WritePermissions; + AArch64AtomicAlignment Alignment; +}; +/// Validate the complete typed alignment outcome before guest OS translation. +bool isAArch64AtomicAlignmentFault(const BackendFault &); +} // namespace neverd::emulation +#endif diff --git a/lib/emulation/arch/aarch64/AArch64Exclusive.cpp b/lib/emulation/arch/aarch64/AArch64Exclusive.cpp index 4fb1f189fa..aab82972e5 100644 --- a/lib/emulation/arch/aarch64/AArch64Exclusive.cpp +++ b/lib/emulation/arch/aarch64/AArch64Exclusive.cpp @@ -7,7 +7,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Endian.h" -#include "llvm/Support/MathExtras.h" namespace neverd::emulation { namespace { @@ -69,23 +68,4 @@ decodeAArch64Exclusive(uint32_t Word, const AArch64MachineState &CPU) { } return std::nullopt; } -bool isAArch64ExclusiveAligned(uint64_t Address, uint64_t Size, - AArch64ExclusiveAlignment Alignment) { - assert(llvm::isPowerOf2_64(Size) && - Size <= aarch64::ExclusiveAlignmentGranule); - if (Alignment == AArch64ExclusiveAlignment::Natural) - return Address % Size == 0; - return Address % aarch64::ExclusiveAlignmentGranule <= - aarch64::ExclusiveAlignmentGranule - Size; -} -bool isAArch64ExclusiveAlignmentFault(const BackendFault &Fault) { - return Fault.Kind == BackendFaultKind::Alignment && - Fault.Cause == BackendFaultCause::OperandAlignment && Fault.Address && - Fault.Size && llvm::isPowerOf2_64(*Fault.Size) && - *Fault.Size <= aarch64::ExclusiveAlignmentGranule && - *Fault.Address % *Fault.Size && - (Fault.Access == BackendAccessKind::Read || - Fault.Access == BackendAccessKind::Write) && - !Fault.Interrupt && !Fault.ErrorCode; -} } // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/AArch64Exclusive.h b/lib/emulation/arch/aarch64/AArch64Exclusive.h index 6debe0404b..588ec62a80 100644 --- a/lib/emulation/arch/aarch64/AArch64Exclusive.h +++ b/lib/emulation/arch/aarch64/AArch64Exclusive.h @@ -5,7 +5,7 @@ //===----------------------------------------------------------------------===// #ifndef NEVERD_EMULATION_AARCH64_EXCLUSIVE_H #define NEVERD_EMULATION_AARCH64_EXCLUSIVE_H -#include "AArch64Machine.h" +#include "AArch64AtomicMemory.h" #include "neverd/emulation/CPU.h" @@ -25,28 +25,12 @@ decodeAArch64Exclusive(const cs_insn &, const AArch64MachineState &); llvm::Expected> decodeAArch64Exclusive(uint32_t Word, const AArch64MachineState &); bool isAArch64Exclusive(uint32_t Word); -enum class AArch64ExclusiveAlignment { Natural, LSE2 }; -/// FEAT_LSE2 accepts an exclusive operand within one 16-byte aligned quantity. -/// Size must be a decoded exclusive operand width, including register pairs. -bool isAArch64ExclusiveAligned(uint64_t Address, uint64_t Size, - AArch64ExclusiveAlignment); - -struct AArch64ExclusiveAccess { - const BackendHooks &Hooks; - llvm::function_ref Stopped; - llvm::function_ref RaiseFault; - llvm::function_ref CheckAccess; - unsigned WritePermissions; - AArch64ExclusiveAlignment Alignment; -}; /// Complete one exclusive instruction under the physical execution lease. /// Both software execution and checked transports use this monitor authority. llvm::Error executeAArch64Exclusive(const AArch64ExclusiveInstruction &, AArch64MachineState &, std::shared_ptr &, MemoryProjection &, - const AArch64ExclusiveAccess &); -/// Validate the complete typed alignment outcome before guest OS translation. -bool isAArch64ExclusiveAlignmentFault(const BackendFault &); + const AArch64AtomicAccess &); } // namespace neverd::emulation #endif diff --git a/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp b/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp index 290a09537e..5735ad8729 100644 --- a/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp +++ b/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp @@ -15,7 +15,7 @@ llvm::Error executeAArch64Exclusive(const AArch64ExclusiveInstruction &I, AArch64MachineState &CPU, std::shared_ptr &Exclusive, MemoryProjection &Memory, - const AArch64ExclusiveAccess &Access) { + const AArch64AtomicAccess &Access) { const auto &Hooks = Access.Hooks; using Operation = AArch64ExclusiveInstruction::Operation; auto Next = CPU; @@ -36,7 +36,7 @@ llvm::Error executeAArch64Exclusive(const AArch64ExclusiveInstruction &I, // Checked execution selects FEAT_LSE2's single-copy atomic quantity. Raw // Unicorn retains its engine's baseline alignment/feature-register model. // Alignment faults precede permissions even when the monitor has expired. - if (!isAArch64ExclusiveAligned(I.Address, Size, Access.Alignment)) { + if (!isAArch64AtomicAligned(I.Address, Size, Access.Alignment)) { BackendFault Fault{ BackendFaultKind::Alignment, CPU.reg(AArch64Register::PC), I.Address, Size, Load ? BackendAccessKind::Read : BackendAccessKind::Write}; diff --git a/lib/emulation/arch/aarch64/AArch64InstructionEffects.cpp b/lib/emulation/arch/aarch64/AArch64InstructionEffects.cpp index 6044e62f73..332492a6b7 100644 --- a/lib/emulation/arch/aarch64/AArch64InstructionEffects.cpp +++ b/lib/emulation/arch/aarch64/AArch64InstructionEffects.cpp @@ -199,9 +199,10 @@ getAArch64InstructionEffects(const cs_insn &I, (I.id == AARCH64_INS_MRS && isReadCacheType(Word)) || (I.id == AARCH64_INS_MSR && isWriteThreadPointer(Word)) || (I.id == AARCH64_INS_MRS && - (isReadFPControl(Word) || isReadFPStatus(Word))) || + (isReadNZCV(Word) || isReadFPControl(Word) || isReadFPStatus(Word))) || (I.id == AARCH64_INS_MSR && - (isWriteFPControl(Word) || isWriteFPStatus(Word))))) + (isWriteNZCV(Word) || isWriteFPControl(Word) || + isWriteFPStatus(Word))))) return llvm::make_error(); const auto Source = Word & RegisterMask; if (Kind == CacheMaintenance) { diff --git a/lib/emulation/arch/aarch64/AArch64Machine.def b/lib/emulation/arch/aarch64/AArch64Machine.def index dfb9c49bda..b4148bea25 100644 --- a/lib/emulation/arch/aarch64/AArch64Machine.def +++ b/lib/emulation/arch/aarch64/AArch64Machine.def @@ -10,7 +10,7 @@ NEVERD_AARCH64_VALUE(WordBytes, 8) NEVERD_AARCH64_VALUE(WordBits, 64) NEVERD_AARCH64_VALUE(GPRCount, 31) NEVERD_AARCH64_VALUE(ExclusiveGranule, 16) -NEVERD_AARCH64_VALUE(ExclusiveAlignmentGranule, 16) +NEVERD_AARCH64_VALUE(AtomicAlignmentGranule, 16) NEVERD_AARCH64_VALUE(ExclusiveSuccess, 0) NEVERD_AARCH64_VALUE(ExclusiveFailure, 1) NEVERD_AARCH64_VALUE(VectorCount, 32) diff --git a/lib/emulation/arch/aarch64/CheckedAArch64Atomic.cpp b/lib/emulation/arch/aarch64/CheckedAArch64Atomic.cpp new file mode 100644 index 0000000000..c2dd9f8d40 --- /dev/null +++ b/lib/emulation/arch/aarch64/CheckedAArch64Atomic.cpp @@ -0,0 +1,37 @@ +//===- CheckedAArch64Atomic.cpp - Checked atomic access policy ------------===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#include "AArch64Atomic.h" +#include "AArch64Exclusive.h" +#include "CheckedAArch64Backend.h" + +namespace neverd::emulation { +llvm::Expected CheckedAArch64Backend::executeAtomic(const cs_insn &I) { + auto Atomic = decodeAArch64Atomic(I, CPU); + if (!Atomic) + return Atomic.takeError(); + auto Local = decodeAArch64Exclusive(I, CPU); + if (!Local) + return Local.takeError(); + if (!*Atomic && !*Local) + return false; + auto Stopped = [&] { return StopRequested || FirstFault; }; + auto Raise = [&](BackendFault Fault) { return raiseFault(Fault, true); }; + auto Check = [&](uint64_t Address, uint64_t Size, unsigned Permissions) { + return access(Address, Size, Permissions, true, true); + }; + const AArch64AtomicAccess Access{Hooks, + Stopped, + Raise, + Check, + executionPermissions(Write), + AArch64AtomicAlignment::LSE2}; + if (auto E = *Atomic ? executeAArch64Atomic(**Atomic, CPU, *Memory, Access) + : executeAArch64Exclusive(**Local, CPU, Exclusive, + *Memory, Access)) + return std::move(E); + return true; +} +} // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/CheckedAArch64Backend.cpp b/lib/emulation/arch/aarch64/CheckedAArch64Backend.cpp index 84ed87938d..f32544a070 100644 --- a/lib/emulation/arch/aarch64/CheckedAArch64Backend.cpp +++ b/lib/emulation/arch/aarch64/CheckedAArch64Backend.cpp @@ -7,7 +7,6 @@ #include "../../core/ExecutionDiagnostics.h" #include "../../core/RAMTransaction.h" -#include "AArch64Exclusive.h" #include "AArch64InstructionEffects.h" #include "llvm/Support/Endian.h" @@ -124,11 +123,11 @@ CheckedAArch64Backend::decodeServiceRequest(const cs_insn &I) const { } llvm::Error CheckedAArch64Backend::execute(const cs_insn &I) { - auto ExclusiveInstruction = decodeAArch64Exclusive(I, CPU); - if (!ExclusiveInstruction) - return ExclusiveInstruction.takeError(); - if (*ExclusiveInstruction) - return executeExclusive(**ExclusiveInstruction); + auto Atomic = executeAtomic(I); + if (!Atomic) + return Atomic.takeError(); + if (*Atomic) + return llvm::Error::success(); auto Effects = getAArch64InstructionEffects(I, CPU); if (!Effects) return Effects.takeError(); diff --git a/lib/emulation/arch/aarch64/CheckedAArch64Backend.h b/lib/emulation/arch/aarch64/CheckedAArch64Backend.h index 92f7955b50..c940959cc6 100644 --- a/lib/emulation/arch/aarch64/CheckedAArch64Backend.h +++ b/lib/emulation/arch/aarch64/CheckedAArch64Backend.h @@ -8,7 +8,6 @@ #include "../../core/CheckedBackend.h" #include "AArch64Machine.h" namespace neverd::emulation { -struct AArch64ExclusiveInstruction; class CheckedAArch64Backend final : public CheckedBackend { public: static llvm::Expected> @@ -40,7 +39,7 @@ class CheckedAArch64Backend final : public CheckedBackend { CPU.reg(AArch64Register::PC) = PC; } llvm::Error execute(const cs_insn &) override; - llvm::Error executeExclusive(const AArch64ExclusiveInstruction &); + llvm::Expected executeAtomic(const cs_insn &); void onGuestException() override { Exclusive.reset(); } std::optional decodeServiceRequest(const cs_insn &) const override; diff --git a/lib/emulation/arch/aarch64/CheckedAArch64Exclusive.cpp b/lib/emulation/arch/aarch64/CheckedAArch64Exclusive.cpp deleted file mode 100644 index d8ee0214ce..0000000000 --- a/lib/emulation/arch/aarch64/CheckedAArch64Exclusive.cpp +++ /dev/null @@ -1,21 +0,0 @@ -//===- CheckedAArch64Exclusive.cpp - Checked exclusive access policy ------===// -// -// NeverD Decompiler -// -//===----------------------------------------------------------------------===// -#include "AArch64Exclusive.h" -#include "CheckedAArch64Backend.h" - -namespace neverd::emulation { -llvm::Error -CheckedAArch64Backend::executeExclusive(const AArch64ExclusiveInstruction &I) { - return executeAArch64Exclusive( - I, CPU, Exclusive, *Memory, - {Hooks, [&] { return StopRequested || FirstFault; }, - [&](BackendFault Fault) { return raiseFault(Fault, true); }, - [&](uint64_t Address, uint64_t Size, unsigned Permissions) { - return access(Address, Size, Permissions, true, true); - }, - executionPermissions(Write), AArch64ExclusiveAlignment::LSE2}); -} -} // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/CheckedAArch64Instructions.def b/lib/emulation/arch/aarch64/CheckedAArch64Instructions.def index c47234e2e6..5b6a468b11 100644 --- a/lib/emulation/arch/aarch64/CheckedAArch64Instructions.def +++ b/lib/emulation/arch/aarch64/CheckedAArch64Instructions.def @@ -5,8 +5,9 @@ //===----------------------------------------------------------------------===// // The baseline register subsets exclude SVE/SME, optional ISA extensions, // half-precision FP and constrained-unpredictable writeback. System access -// admits only the exact thread-pointer and FP control/status encodings below. -// FP arithmetic requires masked exceptions and baseline rounding/FZ/DN control. +// admits only the exact flags, thread-pointer and FP control/status encodings +// below. FP arithmetic requires masked exceptions and baseline rounding/FZ/DN +// control. #ifdef NEVERD_AARCH64_INSTRUCTION NEVERD_AARCH64_INSTRUCTION(ADC, Integer) NEVERD_AARCH64_INSTRUCTION(ADCS, Integer) @@ -156,6 +157,8 @@ NEVERD_AARCH64_ENCODING(ReadFPControl, 0xffffffe0, 0xd53b4400) NEVERD_AARCH64_ENCODING(WriteFPControl, 0xffffffe0, 0xd51b4400) NEVERD_AARCH64_ENCODING(ReadFPStatus, 0xffffffe0, 0xd53b4420) NEVERD_AARCH64_ENCODING(WriteFPStatus, 0xffffffe0, 0xd51b4420) +NEVERD_AARCH64_ENCODING(ReadNZCV, 0xffffffe0, 0xd53b4200) +NEVERD_AARCH64_ENCODING(WriteNZCV, 0xffffffe0, 0xd51b4200) NEVERD_AARCH64_ENCODING(ReadCacheType, 0xffffffe0, 0xd53b0020) NEVERD_AARCH64_ENCODING(CleanDataToUnification, 0xffffffe0, 0xd50b7b20) NEVERD_AARCH64_ENCODING(InvalidateInstructionToUnification, 0xffffffe0, diff --git a/lib/emulation/backends/unicorn/UnicornExclusive.cpp b/lib/emulation/backends/unicorn/UnicornAArch64Atomic.cpp similarity index 68% rename from lib/emulation/backends/unicorn/UnicornExclusive.cpp rename to lib/emulation/backends/unicorn/UnicornAArch64Atomic.cpp index 829f9068be..79bdfccd1b 100644 --- a/lib/emulation/backends/unicorn/UnicornExclusive.cpp +++ b/lib/emulation/backends/unicorn/UnicornAArch64Atomic.cpp @@ -1,10 +1,12 @@ -//===- UnicornExclusive.cpp - Shared ARM64 exclusive instruction bridge --===// +//===- UnicornAArch64Atomic.cpp - Shared ARM64 atomic instruction bridge +//---===// // // NeverD Decompiler // //===----------------------------------------------------------------------===// -#include "UnicornExclusive.h" +#include "UnicornAArch64Atomic.h" +#include "../../arch/aarch64/AArch64Atomic.h" #include "../../core/ExecutionDiagnostics.h" #include "llvm/Support/Endian.h" @@ -13,16 +15,16 @@ namespace neverd::emulation { llvm::Error -executeUnicornExclusive(uc_engine *Engine, uint64_t PC, - MemoryProjection &Memory, - std::shared_ptr &Exclusive, - const AArch64ExclusiveAccess &Access, - llvm::function_ref RegisterID) { +executeUnicornAArch64Atomic(uc_engine *Engine, uint64_t PC, + MemoryProjection &Memory, + std::shared_ptr &Exclusive, + const AArch64AtomicAccess &Access, + llvm::function_ref RegisterID) { std::array Bytes; if (auto E = Memory.read(PC, Bytes, Execute)) return E; const uint32_t Word = llvm::support::endian::read32le(Bytes.data()); - if (!isAArch64Exclusive(Word)) + if (!isAArch64Exclusive(Word) && !isAArch64Atomic(Word)) return llvm::Error::success(); AArch64MachineState Before; for (unsigned R = 0; R <= aarch64::GPRCount; ++R) { @@ -32,22 +34,30 @@ executeUnicornExclusive(uc_engine *Engine, uint64_t PC, return diagnostic::error(diagnostic::UnicornReadRegister); } Before.reg(AArch64Register::PC) = PC; - auto Decoded = decodeAArch64Exclusive(Word, Before); - if (!Decoded) - return Decoded.takeError(); - const auto &I = **Decoded; - if (I.Kind != AArch64ExclusiveInstruction::Operation::Clear) { - auto Page = Memory.mappings().find(I.Address & ~(memory::PageSize - 1)); + auto Local = decodeAArch64Exclusive(Word, Before); + if (!Local) + return Local.takeError(); + auto Atomic = decodeAArch64Atomic(Word, Before); + if (!Atomic) + return Atomic.takeError(); + const uint64_t Address = *Atomic ? (**Atomic).Address : (**Local).Address; + const bool Clear = + *Local && (**Local).Kind == AArch64ExclusiveInstruction::Operation::Clear; + if (!Clear) { + auto Page = Memory.mappings().find(Address & ~(memory::PageSize - 1)); if (Page != Memory.mappings().end() && Page->second.IO) return llvm::make_error(); } auto Next = Before; - if (auto E = executeAArch64Exclusive(I, Next, Exclusive, Memory, Access)) + if (auto E = *Atomic ? executeAArch64Atomic(**Atomic, Next, Memory, Access) + : executeAArch64Exclusive(**Local, Next, Exclusive, + Memory, Access)) return E; if (Next.reg(AArch64Register::PC) == PC) return llvm::Error::success(); - if (I.Kind == AArch64ExclusiveInstruction::Operation::Store) { - auto Target = Memory.mappings().find(I.Address & ~(memory::PageSize - 1)); + if (*Atomic || + (**Local).Kind == AArch64ExclusiveInstruction::Operation::Store) { + auto Target = Memory.mappings().find(Address & ~(memory::PageSize - 1)); // Completion writes the shared backing directly. Every executable alias // must retire its translated blocks before a same-run branch reaches it. if (Target != Memory.mappings().end()) diff --git a/lib/emulation/backends/unicorn/UnicornAArch64Atomic.h b/lib/emulation/backends/unicorn/UnicornAArch64Atomic.h new file mode 100644 index 0000000000..46a1073502 --- /dev/null +++ b/lib/emulation/backends/unicorn/UnicornAArch64Atomic.h @@ -0,0 +1,26 @@ +//===- UnicornAArch64Atomic.h - Shared ARM64 atomic instruction bridge +//-----===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#ifndef NEVERD_EMULATION_UNICORN_AARCH64ATOMIC_H +#define NEVERD_EMULATION_UNICORN_AARCH64ATOMIC_H +#include "../../arch/aarch64/AArch64Exclusive.h" + +#include + +namespace neverd::emulation { +/// Complete atomic and exclusive instructions under the shared physical lease +/// and monitor authority. Other instructions continue through the engine. +llvm::Error +executeUnicornAArch64Atomic(uc_engine *, uint64_t PC, MemoryProjection &, + std::shared_ptr &, + const AArch64AtomicAccess &, + llvm::function_ref RegisterID); +/// A raw engine write hook precedes the write. Conservatively invalidate its +/// physical footprint after observers accept it, including unchanged bytes. +void observeUnicornRAMWrite(MemoryProjection &, uint64_t Address, + uint64_t Size); +} // namespace neverd::emulation +#endif diff --git a/lib/emulation/backends/unicorn/UnicornBackend.cpp b/lib/emulation/backends/unicorn/UnicornBackend.cpp index 6679afec09..e4d3c47fb0 100644 --- a/lib/emulation/backends/unicorn/UnicornBackend.cpp +++ b/lib/emulation/backends/unicorn/UnicornBackend.cpp @@ -17,8 +17,8 @@ #include "../../core/ExecutionExitBuilder.h" #include "../../core/MemoryLayout.h" #include "../../core/MemoryProjection.h" +#include "UnicornAArch64Atomic.h" #include "UnicornArchitecture.h" -#include "UnicornExclusive.h" #include "UnicornMemory.h" #include "llvm/ADT/ScopeExit.h" @@ -296,7 +296,7 @@ struct UnicornBackend::Impl { Exclusive.reset(); } - llvm::Error exclusiveFault(BackendFault Fault) { + llvm::Error atomicFault(BackendFault Fault) { if (Hooks.RecoverableFault && Hooks.RecoverableFault(Fault)) { Exclusive.reset(); RecoverableFault = Fault; @@ -336,20 +336,20 @@ struct UnicornBackend::Impl { if (S.Architecture != GuestArchitecture::AArch64 || S.effectsStopped() || S.RecoverableFault) return; - auto E = executeUnicornExclusive( + auto E = executeUnicornAArch64Atomic( S.Engine, Address, *S.Memory, S.Exclusive, {S.Hooks, [&] { return S.effectsStopped() || S.RecoverableFault; }, - [&](BackendFault Fault) { return S.exclusiveFault(Fault); }, + [&](BackendFault Fault) { return S.atomicFault(Fault); }, [&](uint64_t At, uint64_t Count, unsigned Permissions) -> llvm::Error { if (auto F = S.Memory->firstAccessFailure(At, Count, Permissions)) - return S.exclusiveFault({F->Kind, Address, F->Address, F->Size, - Permissions == Read - ? BackendAccessKind::Read - : BackendAccessKind::Write}); + return S.atomicFault({F->Kind, Address, F->Address, F->Size, + Permissions == Read + ? BackendAccessKind::Read + : BackendAccessKind::Write}); return llvm::Error::success(); }, - Write, AArch64ExclusiveAlignment::Natural}, + Write, AArch64AtomicAlignment::Natural}, registerID); if (E) { S.InstructionRejected = E.isA(); diff --git a/lib/emulation/backends/unicorn/UnicornExclusive.h b/lib/emulation/backends/unicorn/UnicornExclusive.h deleted file mode 100644 index 630cadada9..0000000000 --- a/lib/emulation/backends/unicorn/UnicornExclusive.h +++ /dev/null @@ -1,25 +0,0 @@ -//===- UnicornExclusive.h - Shared ARM64 exclusive instruction bridge ----===// -// -// NeverD Decompiler -// -//===----------------------------------------------------------------------===// -#ifndef NEVERD_EMULATION_UNICORN_EXCLUSIVE_H -#define NEVERD_EMULATION_UNICORN_EXCLUSIVE_H -#include "../../arch/aarch64/AArch64Exclusive.h" - -#include - -namespace neverd::emulation { -/// Complete recognized exclusives before Unicorn enters its value-based -/// private monitor. Ordinary instructions continue through the engine. -llvm::Error -executeUnicornExclusive(uc_engine *, uint64_t PC, MemoryProjection &, - std::shared_ptr &, - const AArch64ExclusiveAccess &, - llvm::function_ref RegisterID); -/// A raw engine write hook precedes the write. Conservatively invalidate its -/// physical footprint after observers accept it, including unchanged bytes. -void observeUnicornRAMWrite(MemoryProjection &, uint64_t Address, - uint64_t Size); -} // namespace neverd::emulation -#endif diff --git a/lib/emulation/os/windows/process/WindowsProcessExceptions.cpp b/lib/emulation/os/windows/process/WindowsProcessExceptions.cpp index ca9e7a4024..9bb59bb68b 100644 --- a/lib/emulation/os/windows/process/WindowsProcessExceptions.cpp +++ b/lib/emulation/os/windows/process/WindowsProcessExceptions.cpp @@ -5,7 +5,7 @@ //===----------------------------------------------------------------------===// #include "WindowsProcessExceptions.h" -#include "../../../arch/aarch64/AArch64Exclusive.h" +#include "../../../arch/aarch64/AArch64AtomicMemory.h" #include "../../../arch/x86_64/X64Exception.h" #include "../exception/X64SIMDException.h" #include "WindowsProcessContext.h" @@ -88,7 +88,7 @@ ExceptionDispatcher::exception(GuestArchitecture Architecture, const BackendFault &Fault, std::optional MXCSR) { if (Architecture == GuestArchitecture::AArch64 && - isAArch64ExclusiveAlignmentFault(Fault)) + isAArch64AtomicAlignmentFault(Fault)) return Exception{StatusDatatypeMisalignment, 0, Fault.PC, {}}; if (Fault.Cause) { // The shared architecture owns the cause. Other #GP(0) results remain diff --git a/scripts/check_aarch64_atomics.py b/scripts/check_aarch64_atomics.py new file mode 100644 index 0000000000..f6bb602c2a --- /dev/null +++ b/scripts/check_aarch64_atomics.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""Collect original LSE results and complete Windows ARM64 exception records.""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path +import platform +import re +import shutil +import struct +import subprocess + +ROOT = Path(__file__).resolve().parents[1] +FIXTURES = ROOT / "unittests/emulation/fixtures" +DEFINITION = FIXTURES / "AArch64AtomicOracle.def" +CASES = FIXTURES.parent / "AArch64AtomicCases.def" + + +def contract(): + definition, cases = DEFINITION.read_text(encoding="utf-8"), CASES.read_text(encoding="utf-8") + values = {name: int(value.removesuffix("ULL"), 0) for name, value in re.findall( + r"NEVERD_ATOMIC_(?:ORACLE_)?VALUE\((\w+), (\w+)\)", definition + cases)} + fields = re.findall(r"NEVERD_ATOMIC_ORACLE_FIELD\((\w+)\)", definition) + instructions = [(name, kind, int(width), int(count)) for name, kind, width, count in re.findall( + r"NEVERD_ATOMIC_CASE\((\w+),\s*(\w+),\s*(\d+),\s*(\d+),", cases)] + scenarios = [(name, int(offset), values[protection], int(split), int(mismatch)) + for name, offset, protection, split, mismatch in re.findall( + r"NEVERD_ATOMIC_ORACLE_SCENARIO\((\w+), (\d+), (\w+), (\d+), (\d+)\)", definition)] + return values, fields, instructions, scenarios + + +def required_records(instructions, scenarios, granule): + for index, case in enumerate(instructions): + _, kind, width, count = case + size = width * count + for scenario, settings in enumerate(scenarios): + _, offset, _, _, mismatch = settings + if ((offset == 1 and size == granule) or (offset == 2 and size == 1) + or (mismatch and not kind.startswith("Compare"))): + continue + yield index, scenario, case, settings, granule - size + 1 if offset == 2 else offset + + +def initial_state(values, case, settings, offset, base): + _, kind, width, count = case + original = struct.pack(" list[dict]: + values, fields, instructions, scenarios = specification or contract() + required = list(required_records(instructions, scenarios, values["Granule"])) + record = struct.Struct("<" + "Q" * len(fields)) + if (not required or len(fields) != len(set(fields)) + or len(data) != record.size * len(required)): + raise ValueError("incomplete original ARM64 atomic observations") + observed = [] + for item, words in zip(required, record.iter_unpack(data), strict=True): + index, scenario, case, settings, offset = item + row = dict(zip(fields, words)) + if ((row["CaseIndex"], row["Scenario"]) != (index, scenario) + or not row["MemoryBase"] or row["MemoryBase"] % values["Granule"] + or not row["AtomicPC"] or row["AtomicPC"] % values["InstructionBytes"] + or row["Faults"] not in (0, 1) or row["ParameterCount"] > values["MaxParameters"] + or any(row[f"Parameter{i}"] for i in range(row["ParameterCount"], values["MaxParameters"]))): + raise ValueError("inconsistent original ARM64 atomic identity") + original, mask, old, before = initial_state(values, case, settings, offset, row["MemoryBase"]) + if row["Faults"]: + if (row["Code"] not in (values["AccessViolation"], values["AlignmentStatus"]) + or row["Flags"] or row["ExceptionPC"] != row["AtomicPC"] + or row["ContextPC"] != row["AtomicPC"] + or [row[f"Context{i}"] for i in range(5)] + [row["ContextFlags"]] != before + or settings[1] == 0 and settings[2] == values["PageReadWrite"]): + raise ValueError("inconsistent original ARM64 atomic fault context") + if row["Code"] == values["AccessViolation"]: + if (row["ParameterCount"] != 2 or row["Parameter0"] != 1 + or not before[4] <= row["Parameter1"] < before[4] + case[2] * case[3]): + raise ValueError("inconsistent original ARM64 atomic access violation") + elif row["ParameterCount"]: + raise ValueError("unexpected ARM64 atomic alignment parameters") + after, returned = original, before + else: + exception_fields = ["Code", "Flags", "ParameterCount", "ExceptionPC", "ContextPC", + "ContextFlags"] + [f"Context{i}" for i in range(5)] + if (any(row[field] for field in exception_fields) + or settings[2] != values["PageReadWrite"]): + raise ValueError("invalid successful ARM64 atomic observation") + after, returned = successful_state(original, mask, old, before, case, offset) + if ([row[f"Return{i}"] for i in range(5)] + [row["ReturnFlags"]] != returned + or struct.pack(" +#include +#include +#include + +namespace neverd::emulation { +namespace { +#define NEVERD_ATOMIC_VALUE(Name, Value) constexpr uint64_t Name = Value; +#define NEVERD_ATOMIC_TEXT(Name, Value) constexpr char Name[] = Value; +#include "AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_VALUE +#undef NEVERD_ATOMIC_TEXT +enum class Family { +#define NEVERD_ATOMIC_FAMILY(Name) Name, +#include "AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_FAMILY +}; +struct AtomicCase { + const char *Name; + Family Kind; + unsigned Width, Count; + uint32_t Word; + uint64_t Expected; + unsigned size() const { return Width * Count; } + bool compare() const { + return Kind == Family::Compare || Kind == Family::ComparePair; + } +}; +constexpr AtomicCase Cases[] = { +#define NEVERD_ATOMIC_CASE(Name, Kind, Width, Count, Word, Expected) \ + {#Name, Family::Kind, Width, Count, Word, Expected}, +#include "AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_CASE +}; +struct Profile { + const char *Name; + ExecutionBackendKind Backend; + ExecutionContract Contract; +}; +constexpr Profile Profiles[] = { +#define NEVERD_ATOMIC_PROFILE(Name, Backend, Contract) \ + {#Name, ExecutionBackendKind::Backend, ExecutionContract::Contract}, +#include "AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_PROFILE +}; +struct Group { + const char *Name; + Family Kind; +}; +constexpr Group Groups[] = { +#define NEVERD_ATOMIC_FAMILY(Name) {#Name, Family::Name}, +#include "AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_FAMILY +}; +using Parameter = std::tuple; +class AArch64Atomic : public testing::TestWithParam { +protected: + std::unique_ptr CPU; + void SetUp() override { initialize(); } + void initialize() { + const auto &P = std::get<0>(GetParam()); + auto Created = createExecutionBackend(P.Backend, P.Contract, Limit, + GuestArchitecture::AArch64); + if (!Created) { + auto E = Created.takeError(); + bool Unavailable = E.isA(); + auto Text = llvm::toString(std::move(E)); + if (Unavailable) + GTEST_SKIP() << Text; + FAIL() << Text; + } + CPU = std::move(Created->CPU); + llvm::cantFail( + CPU->map(Code, PageBytes, Read | Write | Execute | UserAccessible)); + llvm::cantFail(CPU->map(Data, PageBytes, Read | Write | UserAccessible)); + llvm::cantFail( + CPU->mapAlias(Alias, Data, PageBytes, Read | Write | UserAccessible)); + } + void instruction(uint32_t Word) { + std::array Bytes; + llvm::support::endian::write32le(Bytes.data(), Word); + llvm::support::endian::write32le(Bytes.data() + InstructionBytes, Nop); + llvm::cantFail(CPU->write(Code, Bytes)); + } + void input(const AtomicCase &C) { + instruction(C.Word); + for (unsigned R = 0; R <= RegisterMask; ++R) + llvm::cantFail( + CPU->setReg(AArch64Register(unsigned(AArch64Register::X0) + R), + InitialStatus + R)); + llvm::cantFail(CPU->writeInteger(Data, Initial, WordBytes)); + llvm::cantFail(CPU->writeInteger(Data + WordBytes, Operand, WordBytes)); + llvm::cantFail(CPU->setReg( + AArch64Register::X0, + C.compare() ? llvm::cantFail(CPU->readInteger(Data, C.Width)) + : Operand)); + llvm::cantFail(CPU->setReg( + AArch64Register::X1, + C.Count == 2 ? llvm::cantFail(CPU->readInteger(Data + C.Width, C.Width)) + : InitialStatus)); + llvm::cantFail(CPU->setReg(AArch64Register::X2, Operand)); + llvm::cantFail(CPU->setReg(AArch64Register::X3, Initial)); + llvm::cantFail(CPU->setReg(AArch64Register::X4, Alias)); + llvm::cantFail(CPU->setReg(AArch64Register::NZCV, InitialFlags)); + } + std::array memory() { + std::array Bytes{}; + llvm::cantFail(CPU->snapshotBacking(Data, Bytes)); + return Bytes; + } + std::array registers() { + std::array Values; + for (unsigned R = 0; R < Values.size(); ++R) + Values[R] = llvm::cantFail( + CPU->reg(AArch64Register(unsigned(AArch64Register::X0) + R))); + return Values; + } + bool selected(const AtomicCase &C) const { + return C.Kind == std::get<1>(GetParam()).Kind; + } + ExecutionExit run(BackendHooks Hooks = {}) { + Hooks.Instruction = [&](uint64_t At, uint32_t) { + if (At != Code) + CPU->stop(); + }; + llvm::cantFail(CPU->installHooks(std::move(Hooks))); + return llvm::cantFail(CPU->runUntilExit(Code, Timeout)); + } +}; + +TEST_P(AArch64Atomic, OriginalWidthsAndOrderingVariantsPublishAtomicResults) { + for (const auto &C : Cases) { + if (C.Kind != std::get<1>(GetParam()).Kind) + continue; + SCOPED_TRACE(C.Name); + input(C); + const auto Before = memory(); + auto ExpectedRegisters = registers(); + const uint64_t Mask = llvm::maskTrailingOnes(C.Width * CHAR_BIT); + const uint64_t OldLow = llvm::cantFail(CPU->readInteger(Data, C.Width)); + const uint64_t OldHigh = + llvm::cantFail(CPU->readInteger(Data + C.Width, C.Width)); + const unsigned Result = C.compare() ? 0 : TargetRegister; + ExpectedRegisters[Result] = OldLow; + if (C.Count == 2) + ExpectedRegisters[Result + 1] = OldHigh; + unsigned Reads = 0, Writes = 0; + BackendHooks Hooks; + Hooks.Read = [&](uint64_t At, uint32_t Size) { + ++Reads; + EXPECT_EQ(At, Alias); + EXPECT_EQ(Size, C.size()); + }; + Hooks.Write = [&](uint64_t At, uint32_t Size, uint64_t Value) { + EXPECT_EQ(At, Alias + Writes * WordBytes); + std::array Observed; + llvm::cantFail(CPU->read(Data, Observed)); + EXPECT_EQ(Observed, Before); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::PC)), Code); + uint64_t Expected = 0; + for (unsigned B = 0; B < Size; ++B) { + const unsigned Offset = Writes * WordBytes + B; + const uint64_t Source = Offset < C.Width ? C.Expected : Initial; + Expected |= uint64_t(uint8_t(Source >> (Offset % C.Width * CHAR_BIT))) + << (B * CHAR_BIT); + } + EXPECT_EQ(Value, Expected); + ++Writes; + }; + auto Exit = run(Hooks); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::PC)), + Code + InstructionBytes); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::NZCV)), InitialFlags); + EXPECT_EQ(registers(), ExpectedRegisters); + EXPECT_EQ(Reads, 1u); + EXPECT_EQ(Writes, (C.size() + WordBytes - 1) / WordBytes); + const auto After = memory(); + for (unsigned B = C.size(); B < Granule; ++B) + EXPECT_EQ(After[B], Before[B]); + EXPECT_EQ(llvm::cantFail(CPU->reg(C.compare() ? AArch64Register::X0 + : AArch64Register::X2)), + OldLow); + if (C.Count == 2) + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::X1)), OldHigh); + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data, C.Width)), C.Expected); + if (C.Count == 2) + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data + C.Width, C.Width)), + Initial & Mask); + } +} + +TEST_P(AArch64Atomic, CompareMismatchReturnsBothOldWordsWithoutChangingBytes) { + for (const auto &C : Cases) { + if (!selected(C) || !C.compare()) + continue; + for (unsigned N = 0; N < C.Count; ++N) { + SCOPED_TRACE(C.Name); + SCOPED_TRACE(N); + input(C); + const auto Register = N ? AArch64Register::X1 : AArch64Register::X0; + const auto Old = llvm::cantFail(CPU->reg(Register)); + llvm::cantFail(CPU->setReg(Register, Old ^ 1)); + const auto Before = memory(); + auto Expected = registers(); + Expected[N] = Old; + auto Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(memory(), Before); + EXPECT_EQ(registers(), Expected); + } + } +} + +TEST_P(AArch64Atomic, ResultAliasesAndZeroRegisterPreserveOriginalInputs) { + for (const auto &C : Cases) { + if (!selected(C)) + continue; + for (unsigned Result : + {0u, unsigned(BaseRegister), unsigned(RegisterMask)}) { + // CAS writes the comparison register; moving the replacement register + // tests a different operation, covered separately by pair/zero tests. + if (C.compare()) + continue; + SCOPED_TRACE(C.Name); + SCOPED_TRACE(Result); + input(C); + instruction((C.Word & ~uint32_t(RegisterMask)) | Result); + auto Expected = registers(); + if (Result != RegisterMask) + Expected[Result] = llvm::cantFail(CPU->readInteger(Data, C.Width)); + auto Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(registers(), Expected); + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data, C.Width)), C.Expected); + } + input(C); + instruction((C.Word & ~(uint32_t(RegisterMask) << BaseShift)) | + (uint32_t(RegisterMask) << BaseShift)); + llvm::cantFail(CPU->setReg(AArch64Register::SP, Alias)); + auto Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::SP)), Alias); + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data, C.Width)), C.Expected); + } +} + +TEST_P(AArch64Atomic, EveryObserverBoundaryCanCancelWithoutPartialPublication) { + for (const auto &C : Cases) { + if (!selected(C)) + continue; + for (unsigned Boundary = 0; + Boundary <= (C.size() + WordBytes - 1) / WordBytes; ++Boundary) { + SCOPED_TRACE(C.Name); + SCOPED_TRACE(Boundary); + input(C); + const auto Before = memory(); + const auto Registers = registers(); + BackendHooks Hooks; + unsigned Seen = 0; + Hooks.Read = [&](uint64_t, uint32_t) { + if (!Boundary) + CPU->stop(); + }; + Hooks.Write = [&](uint64_t, uint32_t, uint64_t) { + if (++Seen == Boundary) + CPU->stop(); + }; + auto Exit = run(Hooks); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(memory(), Before); + EXPECT_EQ(registers(), Registers); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::PC)), Code); + Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data, C.Width)), C.Expected); + } + } +} + +TEST_P(AArch64Atomic, ObserverExceptionsKeepAllUnpublishedState) { + for (const auto &C : Cases) { + if (!selected(C)) + continue; + for (unsigned Boundary = 0; + Boundary <= (C.size() + WordBytes - 1) / WordBytes; ++Boundary) { + SCOPED_TRACE(C.Name); + SCOPED_TRACE(Boundary); + ASSERT_NO_FATAL_FAILURE(initialize()); + input(C); + const auto Before = memory(); + const auto Registers = registers(); + BackendHooks Hooks; + unsigned Seen = 0; + Hooks.Read = [&](uint64_t, uint32_t) { + if (!Boundary) + throw std::runtime_error(ObserverFailure); + }; + Hooks.Write = [&](uint64_t, uint32_t, uint64_t) { + if (++Seen == Boundary) + throw std::runtime_error(ObserverFailure); + }; + auto Exit = run(Hooks); + EXPECT_EQ(Exit.Kind, ExecutionExitKind::BackendFailure); + EXPECT_EQ(memory(), Before); + EXPECT_EQ(registers(), Registers); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::PC)), Code); + } + } +} + +TEST_P(AArch64Atomic, AlignmentAndWritePermissionsPrecedeAnyCommit) { + for (const auto &C : Cases) { + if (!selected(C)) + continue; + for (unsigned Permissions : {unsigned(Read), 0u}) + for (bool Unaligned : {false, true}) { + if (Unaligned && C.size() == 1) + continue; + SCOPED_TRACE(C.Name); + SCOPED_TRACE(Permissions); + SCOPED_TRACE(Unaligned); + input(C); + if (C.compare()) + llvm::cantFail(CPU->setReg(AArch64Register::X0, 0)); + const auto Before = memory(); + const uint64_t Address = + Alias + (Unaligned ? Granule - C.size() + 1 : 0); + llvm::cantFail(CPU->setReg(AArch64Register::X4, Address)); + const auto Registers = registers(); + llvm::cantFail( + CPU->protect(Alias, PageBytes, Permissions | UserAccessible)); + BackendHooks Hooks; + Hooks.Read = [&](uint64_t, uint32_t) { EXPECT_FALSE(Unaligned); }; + Hooks.Write = [&](uint64_t, uint32_t, uint64_t) { ADD_FAILURE(); }; + Hooks.RecoverableFault = [&](const BackendFault &F) { + EXPECT_EQ(F.Kind, Unaligned ? BackendFaultKind::Alignment + : BackendFaultKind::Protection); + EXPECT_EQ(F.Address, Address); + EXPECT_EQ(F.Size, C.size()); + EXPECT_EQ(F.Access, BackendAccessKind::Write); + EXPECT_EQ(F.PC, Code); + return true; + }; + auto Exit = run(Hooks); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::RecoverableFault) + << Exit.Diagnostic; + ASSERT_TRUE(CPU->takeRecoverableFault()); + EXPECT_EQ(memory(), Before); + EXPECT_EQ(registers(), Registers); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::PC)), Code); + llvm::cantFail( + CPU->protect(Alias, PageBytes, Read | Write | UserAccessible)); + } + } +} + +TEST_P(AArch64Atomic, UnalignedOperandUsesTheSelectedFeatureModel) { + for (const auto &C : Cases) { + if (!selected(C) || C.size() == Granule) + continue; + for (unsigned Offset = 0; Offset <= Granule - C.size(); ++Offset) { + if (std::get<0>(GetParam()).Contract == ExecutionContract::Software && + Offset % C.size()) + continue; + SCOPED_TRACE(C.Name); + SCOPED_TRACE(Offset); + input(C); + llvm::cantFail(CPU->writeInteger(Data + Offset, Initial, C.Width)); + if (C.Count == 2) + llvm::cantFail( + CPU->writeInteger(Data + Offset + C.Width, Operand, C.Width)); + if (C.compare()) { + llvm::cantFail(CPU->setReg(AArch64Register::X0, Initial)); + if (C.Count == 2) + llvm::cantFail(CPU->setReg(AArch64Register::X1, Operand)); + } + llvm::cantFail(CPU->setReg(AArch64Register::X4, Alias + Offset)); + auto Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data + Offset, C.Width)), + C.Expected); + } + } +} + +TEST_P(AArch64Atomic, + SameValueWritesInvalidateOtherCPUReservationsAndSnapshots) { + const auto &P = std::get<0>(GetParam()); + for (const auto &C : Cases) { + if (!selected(C)) + continue; + SCOPED_TRACE(C.Name); + input(C); + // Zero is unchanged for every RMW family with a zero source. A failed + // compare also selects the documented old-value writeback policy. + llvm::cantFail(CPU->writeInteger(Data, 0, WordBytes)); + llvm::cantFail(CPU->writeInteger(Data + WordBytes, 0, WordBytes)); + llvm::cantFail(CPU->setReg(AArch64Register::X0, C.compare() ? 1 : 0)); + auto Other = llvm::cantFail(createExecutionBackend( + P.Backend, + P.Contract == ExecutionContract::Software + ? ExecutionContract::CheckedAArch64 + : P.Contract, + CPU->addressSpace(), GuestArchitecture::AArch64)); + const uint64_t OtherCode = Code + Granule; + const uint32_t Words[] = {ExclusiveLoad, Nop, ExclusiveStore, Nop}; + std::array Bytes; + for (unsigned N = 0; N < std::size(Words); ++N) + llvm::support::endian::write32le(Bytes.data() + N * InstructionBytes, + Words[N]); + llvm::cantFail(CPU->write(OtherCode, Bytes)); + llvm::cantFail(Other.CPU->setReg(AArch64Register::X1, Data)); + auto OtherStep = [&](unsigned Index) { + const uint64_t PC = OtherCode + Index * InstructionBytes; + BackendHooks Hooks; + Hooks.Instruction = [&, PC](uint64_t At, uint32_t) { + if (At != PC) + Other.CPU->stop(); + }; + llvm::cantFail(Other.CPU->installHooks(Hooks)); + EXPECT_EQ(llvm::cantFail(Other.CPU->runUntilExit(PC, Timeout)).Kind, + ExecutionExitKind::Stopped); + }; + OtherStep(0); + auto Snapshot = llvm::cantFail(Other.CPU->saveContext()); + BackendHooks Hooks; + Hooks.Write = [&](uint64_t, uint32_t, uint64_t) { CPU->stop(); }; + EXPECT_EQ(run(Hooks).Kind, ExecutionExitKind::Stopped); + OtherStep(2); + EXPECT_EQ(llvm::cantFail(Other.CPU->reg(AArch64Register::X2)), 0u); + OtherStep(0); + Snapshot = llvm::cantFail(Other.CPU->saveContext()); + EXPECT_EQ(run().Kind, ExecutionExitKind::Stopped); + llvm::cantFail(Other.CPU->restoreContext(*Snapshot)); + OtherStep(2); + EXPECT_EQ(llvm::cantFail(Other.CPU->reg(AArch64Register::X2)), 1u); + } +} + +TEST_P(AArch64Atomic, + PairZeroRegisterAndOverlappingComparisonUseOriginalInputs) { + for (const auto &C : Cases) { + if (!selected(C) || !C.compare()) + continue; + SCOPED_TRACE(C.Name); + input(C); + // CAS[P] may compare and replace with the same original register(s). + instruction(C.Word & ~uint32_t(RegisterMask)); + const auto Before = memory(); + auto Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(memory(), Before); + input(C); + const unsigned Source = C.Count == 2 ? RegisterMask - 1 : RegisterMask; + instruction((C.Word & ~(uint32_t(RegisterMask) << SourceShift)) | + (Source << SourceShift)); + // XZR is a zero comparison operand, never SP. A pair may end in XZR. + const unsigned LastOffset = (C.Count - 1) * C.Width; + llvm::cantFail(CPU->writeInteger(Data + LastOffset, 0, C.Width)); + const auto Old = llvm::cantFail(CPU->readInteger(Data, C.Width)); + if (C.Count == 2) + llvm::cantFail(CPU->setReg(AArch64Register::X30, Old)); + const auto SP = llvm::cantFail(CPU->reg(AArch64Register::SP)); + Exit = run(); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(llvm::cantFail(CPU->readInteger(Data, C.Width)), C.Expected); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::SP)), SP); + if (C.Count == 2) + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::X30)), Old); + } +} + +TEST_P(AArch64Atomic, OddPairRegisterEncodingIsRejectedBeforeAnyAccess) { + for (const auto &C : Cases) { + if (!selected(C) || C.Count != 2) + continue; + for (unsigned Shift : {0u, unsigned(SourceShift)}) { + ASSERT_NO_FATAL_FAILURE(initialize()); + SCOPED_TRACE(C.Name); + input(C); + instruction(C.Word | (1u << Shift)); + const auto Before = memory(); + const auto Registers = registers(); + BackendHooks Hooks; + Hooks.Read = [&](uint64_t, uint32_t) { ADD_FAILURE(); }; + Hooks.Write = [&](uint64_t, uint32_t, uint64_t) { ADD_FAILURE(); }; + auto Exit = run(Hooks); + EXPECT_EQ(Exit.Kind, ExecutionExitKind::UnsupportedOperation); + EXPECT_EQ(memory(), Before); + EXPECT_EQ(registers(), Registers); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::PC)), Code); + } + } +} + +TEST_P(AArch64Atomic, + FlagTransfersPreserveReservedBitsAndZeroRegisterSemantics) { + for (unsigned Flags = 0; Flags <= Granule - 1; ++Flags) { + const uint64_t Value = Flags * (FlagsMask / (Granule - 1)); + // All reserved input bits must be ignored by MSR; MRS zeroes them. + llvm::cantFail(CPU->setReg(AArch64Register::X0, Value | ~FlagsMask)); + instruction(WriteNZCV); + ASSERT_EQ(run().Kind, ExecutionExitKind::Stopped); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::NZCV)), Value); + instruction(ReadNZCV); + ASSERT_EQ(run().Kind, ExecutionExitKind::Stopped); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::X2)), Value); + const auto Registers = registers(); + instruction((ReadNZCV & ~RegisterMask) | RegisterMask); + ASSERT_EQ(run().Kind, ExecutionExitKind::Stopped); + EXPECT_EQ(registers(), Registers); + instruction(WriteNZCV | RegisterMask); + ASSERT_EQ(run().Kind, ExecutionExitKind::Stopped); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::NZCV)), 0u); + EXPECT_EQ(registers(), Registers); + } +} +INSTANTIATE_TEST_SUITE_P(Backends, AArch64Atomic, + testing::Combine(testing::ValuesIn(Profiles), + testing::ValuesIn(Groups)), + [](const auto &P) { + return std::string(std::get<0>(P.param).Name) + + NameSeparator + std::get<1>(P.param).Name; + }); +} // namespace +} // namespace neverd::emulation diff --git a/unittests/emulation/CMakeLists.txt b/unittests/emulation/CMakeLists.txt index 17a42320bc..faca7217f7 100644 --- a/unittests/emulation/CMakeLists.txt +++ b/unittests/emulation/CMakeLists.txt @@ -51,6 +51,7 @@ add_neverd_unittest(NeverDAArch64MemoryTests AArch64StructureMemoryTests.cpp AArch64AcquireReleaseTests.cpp AArch64ExclusiveTests.cpp + AArch64AtomicTests.cpp TIMEOUT 15 LINK_COMPONENTS EmulationCPU) diff --git a/unittests/emulation/WindowsProcessFixtures.cmake b/unittests/emulation/WindowsProcessFixtures.cmake index b4e7074af8..275c748635 100644 --- a/unittests/emulation/WindowsProcessFixtures.cmake +++ b/unittests/emulation/WindowsProcessFixtures.cmake @@ -69,6 +69,21 @@ add_custom_command(OUTPUT "${_exclusive_base}.exe" "${_exclusive_base}.obj" "${_windows_fixture_dir}/AArch64-kernel32.lib" VERBATIM) list(APPEND _windows_outputs "${_exclusive_base}.exe") +set(_atomic_base "${_windows_fixture_dir}/aarch64-atomic") +add_custom_command(OUTPUT "${_atomic_base}.exe" "${_atomic_base}.obj" + COMMAND "${NEVERD_TEST_CLANG_EXECUTABLE}" --target=aarch64-pc-windows-msvc + -std=c11 -ffreestanding -fno-builtin -fno-stack-protector + -fno-vectorize -fno-slp-vectorize -O1 -c + "${CMAKE_CURRENT_SOURCE_DIR}/fixtures/aarch64_atomic_oracle.c" + -o "${_atomic_base}.obj" + COMMAND "${NEVERD_PROCESS_LLD_LINK}" /nodefaultlib /entry:entry /subsystem:console + /machine:arm64 /timestamp:0 "${_atomic_base}.obj" + "${_windows_fixture_dir}/AArch64-kernel32.lib" "/out:${_atomic_base}.exe" + DEPENDS fixtures/aarch64_atomic_oracle.c fixtures/aarch64_atomic.inc + fixtures/AArch64AtomicOracle.def AArch64AtomicCases.def + "${_windows_fixture_dir}/AArch64-kernel32.lib" + VERBATIM) +list(APPEND _windows_outputs "${_atomic_base}.exe") add_custom_target(NeverDWindowsProcessFixtures DEPENDS ${_windows_outputs}) foreach(_owner NeverDWindowsProcessTests NeverDProcessPublicTests) if(TARGET ${_owner}) diff --git a/unittests/emulation/fixtures/AArch64AtomicOracle.def b/unittests/emulation/fixtures/AArch64AtomicOracle.def new file mode 100644 index 0000000000..3aca1ea8a2 --- /dev/null +++ b/unittests/emulation/fixtures/AArch64AtomicOracle.def @@ -0,0 +1,105 @@ +//===- AArch64AtomicOracle.def - Original native LSE observation contract +//--===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#ifdef NEVERD_ATOMIC_ORACLE_VALUE +NEVERD_ATOMIC_ORACLE_VALUE(StdoutSelector, 0xfffffff5) +NEVERD_ATOMIC_ORACLE_VALUE(StderrSelector, 0xfffffff4) +NEVERD_ATOMIC_ORACLE_VALUE(FailureStatus, 1) +NEVERD_ATOMIC_ORACLE_VALUE(NativeTimeoutSeconds, 30) +NEVERD_ATOMIC_ORACLE_VALUE(MaxParameters, 15) +NEVERD_ATOMIC_ORACLE_VALUE(ContextPCOffset, 264) +NEVERD_ATOMIC_ORACLE_VALUE(ContextGPROffset, 8) +NEVERD_ATOMIC_ORACLE_VALUE(ContextFlagsOffset, 4) +NEVERD_ATOMIC_ORACLE_VALUE(NZCVMask, 0xf0000000) +NEVERD_ATOMIC_ORACLE_VALUE(ByteBits, 8) +NEVERD_ATOMIC_ORACLE_VALUE(MemoryWordCount, 4) +NEVERD_ATOMIC_ORACLE_VALUE(MemoryCommitReserve, 0x3000) +NEVERD_ATOMIC_ORACLE_VALUE(MemoryRelease, 0x8000) +NEVERD_ATOMIC_ORACLE_VALUE(PageReadWrite, 4) +NEVERD_ATOMIC_ORACLE_VALUE(PageReadOnly, 2) +NEVERD_ATOMIC_ORACLE_VALUE(PageNoAccess, 1) +NEVERD_ATOMIC_ORACLE_VALUE(ContinueExecution, 0xffffffff) +NEVERD_ATOMIC_ORACLE_VALUE(AccessViolation, 0xc0000005) +NEVERD_ATOMIC_ORACLE_VALUE(AlignmentStatus, 0x80000002) +NEVERD_ATOMIC_ORACLE_VALUE(SiteHandler, 1) +NEVERD_ATOMIC_ORACLE_VALUE(SiteRegistration, 2) +NEVERD_ATOMIC_ORACLE_VALUE(SiteRetirement, 3) +NEVERD_ATOMIC_ORACLE_VALUE(SiteAllocation, 4) +NEVERD_ATOMIC_ORACLE_VALUE(SiteProtection, 5) +#endif +// Name, offset selector (0: aligned, 1: within block, 2: crossing block), +// protected page, split-page window, compare mismatch. +#ifdef NEVERD_ATOMIC_ORACLE_SCENARIO +NEVERD_ATOMIC_ORACLE_SCENARIO(Aligned, 0, PageReadWrite, 0, 0) +NEVERD_ATOMIC_ORACLE_SCENARIO(WithinBlock, 1, PageReadWrite, 0, 0) +NEVERD_ATOMIC_ORACLE_SCENARIO(CrossingBlock, 2, PageReadWrite, 0, 0) +NEVERD_ATOMIC_ORACLE_SCENARIO(ReadOnly, 0, PageReadOnly, 0, 0) +NEVERD_ATOMIC_ORACLE_SCENARIO(NoAccess, 0, PageNoAccess, 0, 0) +NEVERD_ATOMIC_ORACLE_SCENARIO(Mismatch, 0, PageReadWrite, 0, 1) +NEVERD_ATOMIC_ORACLE_SCENARIO(MismatchReadOnly, 0, PageReadOnly, 0, 1) +NEVERD_ATOMIC_ORACLE_SCENARIO(SplitReadOnly, 2, PageReadOnly, 1, 0) +NEVERD_ATOMIC_ORACLE_SCENARIO(SplitNoAccess, 2, PageNoAccess, 1, 0) +#endif +#ifdef NEVERD_ATOMIC_ORACLE_TEXT +NEVERD_ATOMIC_ORACLE_TEXT(Target, "aarch64-pc-windows-msvc") +NEVERD_ATOMIC_ORACLE_TEXT(Machine, "arm64") +NEVERD_ATOMIC_ORACLE_TEXT(Provider, "kernel32.dll") +NEVERD_ATOMIC_ORACLE_TEXT(Entry, "entry") +NEVERD_ATOMIC_ORACLE_TEXT(Program, "aarch64-atomic.exe") +#endif +#ifdef NEVERD_ATOMIC_ORACLE_API +NEVERD_ATOMIC_ORACLE_API(ExitProcess) +NEVERD_ATOMIC_ORACLE_API(GetStdHandle) +NEVERD_ATOMIC_ORACLE_API(WriteFile) +NEVERD_ATOMIC_ORACLE_API(AddVectoredExceptionHandler) +NEVERD_ATOMIC_ORACLE_API(RemoveVectoredExceptionHandler) +NEVERD_ATOMIC_ORACLE_API(VirtualAlloc) +NEVERD_ATOMIC_ORACLE_API(VirtualFree) +NEVERD_ATOMIC_ORACLE_API(VirtualProtect) +#endif +#ifdef NEVERD_ATOMIC_ORACLE_FIELD +NEVERD_ATOMIC_ORACLE_FIELD(CaseIndex) +NEVERD_ATOMIC_ORACLE_FIELD(Scenario) +NEVERD_ATOMIC_ORACLE_FIELD(MemoryBase) +NEVERD_ATOMIC_ORACLE_FIELD(AtomicPC) +NEVERD_ATOMIC_ORACLE_FIELD(Faults) +NEVERD_ATOMIC_ORACLE_FIELD(Code) +NEVERD_ATOMIC_ORACLE_FIELD(Flags) +NEVERD_ATOMIC_ORACLE_FIELD(ParameterCount) +NEVERD_ATOMIC_ORACLE_FIELD(ExceptionPC) +NEVERD_ATOMIC_ORACLE_FIELD(ContextPC) +NEVERD_ATOMIC_ORACLE_FIELD(Context0) +NEVERD_ATOMIC_ORACLE_FIELD(Context1) +NEVERD_ATOMIC_ORACLE_FIELD(Context2) +NEVERD_ATOMIC_ORACLE_FIELD(Context3) +NEVERD_ATOMIC_ORACLE_FIELD(Context4) +NEVERD_ATOMIC_ORACLE_FIELD(ContextFlags) +NEVERD_ATOMIC_ORACLE_FIELD(Return0) +NEVERD_ATOMIC_ORACLE_FIELD(Return1) +NEVERD_ATOMIC_ORACLE_FIELD(Return2) +NEVERD_ATOMIC_ORACLE_FIELD(Return3) +NEVERD_ATOMIC_ORACLE_FIELD(Return4) +NEVERD_ATOMIC_ORACLE_FIELD(ReturnFlags) +NEVERD_ATOMIC_ORACLE_FIELD(After0) +NEVERD_ATOMIC_ORACLE_FIELD(After1) +NEVERD_ATOMIC_ORACLE_FIELD(After2) +NEVERD_ATOMIC_ORACLE_FIELD(After3) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter0) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter1) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter2) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter3) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter4) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter5) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter6) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter7) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter8) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter9) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter10) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter11) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter12) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter13) +NEVERD_ATOMIC_ORACLE_FIELD(Parameter14) +#endif diff --git a/unittests/emulation/fixtures/aarch64_atomic.inc b/unittests/emulation/fixtures/aarch64_atomic.inc new file mode 100644 index 0000000000..6ce1953a21 --- /dev/null +++ b/unittests/emulation/fixtures/aarch64_atomic.inc @@ -0,0 +1,26 @@ +//===- aarch64_atomic.inc - Original LSE observation assembly -------------===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +// Each instruction uses X0[,X1], X2[,X3], [X4]. Record the exact instruction +// site and resume after it when Windows supplies an exception context. +#define NEVERD_ATOMIC_CASE(Name, Kind, Width, Count, Word, Expected) \ + static void atomic##Name(AtomicWord *Input, AtomicWord *Output) { \ + __asm__ volatile( \ + "adr x5, 1f\n\tstr x5, [%[site]]\n\t" \ + "adr x5, 2f\n\tstr x5, [%[resume]]\n\t" \ + "ldp x0, x1, [%[input]]\n\tldp x2, x3, [%[input], #16]\n\t" \ + "ldr x4, [%[input], #32]\n\tldr x5, [%[input], #40]\n\tmsr nzcv, " \ + "x5\n\t" \ + "1:\n\t.inst " #Word "\n\t" \ + "2:\n\tstp x0, x1, [%[output]]\n\tstp x2, x3, [%[output], #16]\n\t" \ + "str x4, [%[output], #32]\n\tmrs x5, nzcv\n\tstr x5, [%[output], " \ + "#40]\n\t" \ + : \ + : [input] "r"(Input), [output] "r"(Output), [site] "r"(&AtomicSite), \ + [resume] "r"(&ResumeSite) \ + : "x0", "x1", "x2", "x3", "x4", "x5", "memory", "cc"); \ + } +#include "../AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_CASE diff --git a/unittests/emulation/fixtures/aarch64_atomic_oracle.c b/unittests/emulation/fixtures/aarch64_atomic_oracle.c new file mode 100644 index 0000000000..08d3bfd3ed --- /dev/null +++ b/unittests/emulation/fixtures/aarch64_atomic_oracle.c @@ -0,0 +1,173 @@ +//===- aarch64_atomic_oracle.c - Native Windows ARM64 LSE observations ----===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +typedef unsigned long long AtomicWord; +#define NEVERD_ATOMIC_VALUE(Name, Value) static const AtomicWord Name = Value; +#include "../AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_VALUE +#define NEVERD_ATOMIC_ORACLE_VALUE(Name, Value) enum { Name = Value }; +#include "AArch64AtomicOracle.def" +#undef NEVERD_ATOMIC_ORACLE_VALUE +enum { +#define NEVERD_ATOMIC_FAMILY(Name) Name, +#include "../AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_FAMILY +}; +enum { +#define NEVERD_ATOMIC_ORACLE_FIELD(Name) Field##Name, +#include "AArch64AtomicOracle.def" +#undef NEVERD_ATOMIC_ORACLE_FIELD + FieldCount +}; +typedef struct NativeException { + unsigned Code, Flags; + struct NativeException *Nested; + void *Address; + unsigned Count, Padding; + AtomicWord Parameters[MaxParameters]; +} NativeException; +typedef struct { + NativeException *Record; + unsigned char *Context; +} NativePointers; +__declspec(dllimport) void ExitProcess(unsigned); +__declspec(dllimport) void *GetStdHandle(unsigned); +__declspec(dllimport) int WriteFile(void *, const void *, unsigned, unsigned *, + void *); +__declspec(dllimport) void *VirtualAlloc(void *, AtomicWord, unsigned, + unsigned); +__declspec(dllimport) int VirtualFree(void *, AtomicWord, unsigned); +__declspec(dllimport) int VirtualProtect(void *, AtomicWord, unsigned, + unsigned *); +__declspec(dllimport) void * +AddVectoredExceptionHandler(unsigned, unsigned (*)(NativePointers *)); +__declspec(dllimport) unsigned RemoveVectoredExceptionHandler(void *); +static AtomicWord AtomicSite, ResumeSite, Observation[FieldCount]; +static unsigned char *Pages; + +static void fail(unsigned Site, AtomicWord First, AtomicWord Second) { + AtomicWord Values[] = {Site, First, Second}; + unsigned Written; + WriteFile(GetStdHandle(StderrSelector), Values, sizeof(Values), &Written, 0); + ExitProcess(FailureStatus); +} +static unsigned handler(NativePointers *P) { + AtomicWord *PC = (AtomicWord *)(P->Context + ContextPCOffset); + if (*PC != AtomicSite || (AtomicWord)P->Record->Address != *PC || + ++Observation[FieldFaults] != 1 || P->Record->Count > MaxParameters) + fail(SiteHandler, *PC, P->Record->Code); + Observation[FieldCode] = P->Record->Code; + Observation[FieldFlags] = P->Record->Flags; + Observation[FieldParameterCount] = P->Record->Count; + Observation[FieldExceptionPC] = (AtomicWord)P->Record->Address; + Observation[FieldContextPC] = *PC; + AtomicWord *GPR = (AtomicWord *)(P->Context + ContextGPROffset); + for (unsigned I = 0; I < FieldContextFlags - FieldContext0; ++I) + Observation[FieldContext0 + I] = GPR[I]; + Observation[FieldContextFlags] = + *(unsigned *)(P->Context + ContextFlagsOffset) & NZCVMask; + for (unsigned I = 0; I < P->Record->Count; ++I) + Observation[FieldParameter0 + I] = P->Record->Parameters[I]; + *PC = ResumeSite; + return ContinueExecution; +} +static void emit(void) { + unsigned Written = 0; + if (!WriteFile(GetStdHandle(StdoutSelector), Observation, sizeof(Observation), + &Written, 0) || + Written != sizeof(Observation)) + ExitProcess(FailureStatus); +} +#include "aarch64_atomic.inc" +static const struct { + unsigned Kind, Width, Count; + void (*Execute)(AtomicWord *, AtomicWord *); +} Cases[] = { +#define NEVERD_ATOMIC_CASE(Name, Kind, Width, Count, Word, Expected) \ + {Kind, Width, Count, atomic##Name}, +#include "../AArch64AtomicCases.def" +#undef NEVERD_ATOMIC_CASE +}; +static const struct { + unsigned Offset, Protection, Split, Mismatch; +} Scenarios[] = { +#define NEVERD_ATOMIC_ORACLE_SCENARIO(Name, Offset, Protection, Split, \ + Mismatch) \ + {Offset, Protection, Split, Mismatch}, +#include "AArch64AtomicOracle.def" +#undef NEVERD_ATOMIC_ORACLE_SCENARIO +}; +static AtomicWord readBytes(const unsigned char *Memory, unsigned Width) { + AtomicWord Value = 0; + for (unsigned B = 0; B < Width; ++B) + Value |= (AtomicWord)Memory[B] << (B * ByteBits); + return Value; +} +static void observe(unsigned Index, unsigned Scenario) { + const unsigned Width = Cases[Index].Width, Size = Width * Cases[Index].Count; + const int CompareValue = + Cases[Index].Kind == Compare || Cases[Index].Kind == ComparePair; + if ((Scenarios[Scenario].Offset == 1 && Size == Granule) || + (Scenarios[Scenario].Offset == 2 && Size == 1) || + (Scenarios[Scenario].Mismatch && !CompareValue)) + return; + AtomicWord *Memory = + (AtomicWord *)(Pages + + (Scenarios[Scenario].Split ? PageBytes - Granule : 0)); + const unsigned Offset = Scenarios[Scenario].Offset == 2 + ? Granule - Size + 1 + : Scenarios[Scenario].Offset; + for (unsigned I = 0; I < MemoryWordCount; ++I) + Memory[I] = I % 2 ? Operand : Initial; + for (unsigned I = 0; I < FieldCount; ++I) + Observation[I] = 0; + Observation[FieldCaseIndex] = Index; + Observation[FieldScenario] = Scenario; + Observation[FieldMemoryBase] = (AtomicWord)Memory; + const unsigned char *Address = (unsigned char *)Memory + Offset; + const AtomicWord Mask = Width == sizeof(AtomicWord) + ? ~(AtomicWord)0 + : ((AtomicWord)1 << (Width * ByteBits)) - 1; + AtomicWord Input[] = {Operand, InitialStatus, Operand, + Initial, (AtomicWord)Address, InitialFlags}; + if (CompareValue) { + Input[0] = readBytes(Address, Width) | (InitialStatus & ~Mask); + if (Scenarios[Scenario].Mismatch) + Input[0] ^= 1; + if (Cases[Index].Count == 2) + Input[1] = readBytes(Address + Width, Width) | (InitialStatus & ~Mask); + } + void *Protected = Pages + (Scenarios[Scenario].Split ? PageBytes : 0); + unsigned OldProtection; + if (Scenarios[Scenario].Protection != PageReadWrite && + !VirtualProtect(Protected, PageBytes, Scenarios[Scenario].Protection, + &OldProtection)) + fail(SiteProtection, Index, Scenario); + Cases[Index].Execute(Input, Observation + FieldReturn0); + Observation[FieldAtomicPC] = AtomicSite; + if (Scenarios[Scenario].Protection != PageReadWrite && + !VirtualProtect(Protected, PageBytes, PageReadWrite, &OldProtection)) + fail(SiteProtection, Index, Scenario); + for (unsigned I = 0; I < MemoryWordCount; ++I) + Observation[FieldAfter0 + I] = Memory[I]; + emit(); +} +void entry(void) { + void *Handler = AddVectoredExceptionHandler(1, handler); + if (!Handler) + fail(SiteRegistration, 0, 0); + Pages = VirtualAlloc(0, PageBytes * 2, MemoryCommitReserve, PageReadWrite); + if (!Pages) + fail(SiteAllocation, 0, 0); + for (unsigned Index = 0; Index < sizeof(Cases) / sizeof(Cases[0]); ++Index) + for (unsigned Scenario = 0; + Scenario < sizeof(Scenarios) / sizeof(Scenarios[0]); ++Scenario) + observe(Index, Scenario); + if (!RemoveVectoredExceptionHandler(Handler)) + fail(SiteRetirement, 0, 0); + if (!VirtualFree(Pages, 0, MemoryRelease)) + fail(SiteAllocation, 0, 0); + ExitProcess(0); +} From 6570090b14e25f951e526e0142cfbb7bb05d766d Mon Sep 17 00:00:00 2001 From: NeverSightAI Date: Mon, 5 Oct 2026 12:47:32 -0700 Subject: [PATCH 2/2] Match native ARM64 atomic fault ordering and retain complete process evidence --- docs/ar/cpu-execution.md | 2 +- docs/ar/testing.md | 2 +- docs/cpu-execution.md | 2 +- docs/de/cpu-execution.md | 2 +- docs/de/testing.md | 2 +- docs/es/cpu-execution.md | 2 +- docs/es/testing.md | 2 +- docs/fr/cpu-execution.md | 2 +- docs/fr/testing.md | 2 +- docs/it/cpu-execution.md | 2 +- docs/it/testing.md | 2 +- docs/ja/cpu-execution.md | 2 +- docs/ja/testing.md | 2 +- docs/ko/cpu-execution.md | 2 +- docs/ko/testing.md | 2 +- docs/ru/cpu-execution.md | 2 +- docs/ru/testing.md | 2 +- docs/testing.md | 2 +- docs/zh-CN/cpu-execution.md | 2 +- docs/zh-CN/testing.md | 2 +- docs/zh-TW/cpu-execution.md | 2 +- docs/zh-TW/testing.md | 2 +- .../arch/aarch64/AArch64AtomicExecution.cpp | 47 ++----- .../arch/aarch64/AArch64AtomicMemory.cpp | 40 ++++++ .../arch/aarch64/AArch64AtomicMemory.h | 7 + .../aarch64/AArch64ExclusiveExecution.cpp | 34 +---- scripts/EmulationDocumentation.def | 19 ++- scripts/check_aarch64_atomics.py | 25 +++- scripts/tests/test_check_aarch64_atomics.py | 31 +++- third_party/unicorn | 2 +- unittests/emulation/AArch64AtomicCode.def | 38 +++++ unittests/emulation/AArch64AtomicTests.cpp | 62 +++++++- unittests/emulation/CMakeLists.txt | 1 + .../emulation/WindowsAtomicProcessTests.cpp | 133 ++++++++++++++++++ .../fixtures/WindowsAtomicResults.def | 63 +++++++++ 35 files changed, 454 insertions(+), 92 deletions(-) create mode 100644 unittests/emulation/AArch64AtomicCode.def create mode 100644 unittests/emulation/WindowsAtomicProcessTests.cpp create mode 100644 unittests/emulation/fixtures/WindowsAtomicResults.def diff --git a/docs/ar/cpu-execution.md b/docs/ar/cpu-execution.md index afeeff331f..d4addfef37 100644 --- a/docs/ar/cpu-execution.md +++ b/docs/ar/cpu-execution.md @@ -207,4 +207,4 @@ neverd cpu-capabilities \ يدعم ARM64 checked تعليمات `LDXR/STXR` بعرض 8/16/32/64 بت، وأزواج `LDXP/STXP` بعرض 32/64 بت، وصيغ acquire/release و`CLREX`. تشترك KVM وWHP وUnicorn checked في مراقب على مستوى ISA يتتبع حجوزات مادية بحجم 16 بايت؛ ولا تمنع مخارج التنفيذ خطوة بخطوة تقدّم الحلقات. تُبطل الكتابات المعتمدة الحجز حتى إن بقيت البايتات كما هي، بما يشمل الأسماء البديلة والعروض المحتفظ بها. يحفظ الإيقاف الحالة غير المنشورة، ولا تلغي اللقطات الكتابات اللاحقة. تتبع العمليات الحصرية checked قواعد FEAT_LSE2: يُسمح بالمعامل غير المحاذى داخل كتلة محاذاة بحجم 16 بايت، ويؤدي عبور حدودها إلى `alignment`. تُطابق الكتابات ذات العرض نفسه وحدة الحجز المادية. تُفحص المحاذاة والصلاحيات قبل تقرير نجاح التخزين المشروط حتى عند انتهاء الحجز. لا يشمل هذا الملف MMIO أو SMP المتوازي. يستخدم Unicorn ARM64 هذا المراقب أيضًا ضمن عقد `Software`، بما في ذلك مشاركة RAM المادية بين وحدات CPU البرمجية ووحدات checked. يحتفظ عقد `Software` في Unicorn بنموذج المحاذاة الطبيعية للمحرك. -تدعم طبقة ISA المشتركة FEAT_LSE: ‏`CAS/CASP` و`SWP` و`LDADD/LDCLR/LDEOR/LDSET` وmin/max الموقعة وغير الموقعة، بعرض بايت ونصف كلمة وكلمة وكلمة مزدوجة، مع صيغ acquire/release. تتبع checked المحاذاة المذكورة أعلاه، بينما يحتفظ Unicorn `Software` بالمحاذاة الطبيعية. تراعي المقارنات عرض المعامل وتُوسّع القيم القديمة المعادة بأصفار. يتطلب CAS الفاشل إذن الكتابة أيضًا ويعيد كتابة القيمة القديمة، وهو خيار تسمح به Arm ويبطل الحجوزات المادية. ترى callbacks حالة CPU/RAM السابقة؛ ولا تنشر الإلغاءات والأخطاء المتزامنة آثارًا جزئية. يرتب قفل التنفيذ المادي وحدات CPU التعاونية دون توفير SMP متوازٍ أو عمليات MMIO ذرية. تنقل `MRS/MSR NZCV` أعلام الشرط الأربعة وفق قواعد البتات المحجوزة والسجل الصفري. \ No newline at end of file +تدعم طبقة ISA المشتركة FEAT_LSE: ‏`CAS/CASP` و`SWP` و`LDADD/LDCLR/LDEOR/LDSET` وmin/max الموقعة وغير الموقعة، بعرض بايت ونصف كلمة وكلمة وكلمة مزدوجة، مع صيغ acquire/release. تتبع checked المحاذاة المذكورة أعلاه، بينما يحتفظ Unicorn `Software` بالمحاذاة الطبيعية. تراعي المقارنات عرض المعامل وتُوسّع القيم القديمة المعادة بأصفار. يتطلب CAS الفاشل إذن الكتابة أيضًا ويعيد كتابة القيمة القديمة، وهو خيار تسمح به Arm ويبطل الحجوزات المادية. ترى callbacks حالة CPU/RAM السابقة؛ ولا تنشر الإلغاءات والأخطاء المتزامنة آثارًا جزئية. يرتب قفل التنفيذ المادي وحدات CPU التعاونية دون توفير SMP متوازٍ أو عمليات MMIO ذرية. تنقل `MRS/MSR NZCV` أعلام الشرط الأربعة وفق قواعد البتات المحجوزة والسجل الصفري. تُفحص صلاحية القراءة أولًا: يؤدي المعامل غير القابل للقراءة إلى خطأ قراءة، والمعامل المخصص للقراءة فقط إلى خطأ كتابة، وفقًا للمشاهدات الأصلية على Windows ARM64. \ No newline at end of file diff --git a/docs/ar/testing.md b/docs/ar/testing.md index 6162ef28fe..30eaed7cf3 100644 --- a/docs/ar/testing.md +++ b/docs/ar/testing.md @@ -1438,7 +1438,7 @@ build-release/bin/NeverDByteCellScalarizationTests يغطي `AArch64ExclusiveTests.cpp` العروض والأزواج وصيغ acquire/release وتداخل السجلات والأسماء البديلة والأخطاء واللقطات وإلغاء المراقبين أو فشلهم وتنافس وحدات CPU. ويغطي `RAMReservationTests.cpp` الكتابات المتطابقة وABA وإعادة التخصيص والتراجع وتأثير تعليمات السلاسل و`ENTER` في KVM/Unicorn. تنفذ عينات عمليات Windows ARM64 الأصلية حلقات حصرية. يشغّل `scripts/check_aarch64_exclusives.py` التعليمات الأصلية ويسجل استثناءات المحاذاة في Windows ARM64 CI. لا تثبت هذه المشاهدات تنفيذ خلفيات ARM64 KVM/WHP؛ وتبقى الملفات غير المتاحة مسجلة بوضوح كتغطية متجاوزة. تغطي الحالات نفسها Unicorn البرمجي والتداخل بين العقود والكتابات المتطابقة وABA والأسماء البديلة القابلة للتنفيذ ضمن التشغيل نفسه، إضافة إلى `DC ZVA` والإلغاء بواسطة المراقبين. يشغّل `windows-alignment-oracle.yml` مسبار ARM64 أيضًا: تغطي 1,320 مشاهدة كل إزاحة غير محاذاة وأربعة تسلسلات تحميل وتخزين وذاكرة قابلة للكتابة أو للقراءة فقط أو محظورة أو عابرة لحدود الصفحات. يحتفظ المسبار بعرض قيم السجلات الابتدائية كاملًا ويسجل أي كتابة جزئية معتمدة قبل الخطأ. يقارن `WindowsExclusiveProcessTests.cpp` عدد 1,320 مشاهدة أصلية لـ Windows ARM64 بالبصمات الأصلية في `WindowsExclusiveNative.def`. يطبّع مواضع الشفرة والبيانات فقط، مع إبقاء قيم السجلات وبيانات الاستثناءات وتأثيرات RAM. -يغطي `AArch64AtomicTests.cpp` عدد 168 ترميزًا مستقل التجميع لتعليمات LSE، وتداخل السجلات والمقارنات الموقعة والصلاحيات والإلغاء والحجوزات المادية ونقل NZCV. يجمع `scripts/check_aarch64_atomics.py` عدد 1,100 سجل أصلي من Windows ARM64 يتضمن النتائج وسياق الاستثناء ونطاق RAM كاملًا؛ وترفض اختبارات المحلل الأدلة الناقصة أو غير المتسقة. يظل تنفيذ KVM/WHP الأصلي بحاجة إلى تحقق مستقل. +يغطي `AArch64AtomicTests.cpp` عدد 168 ترميزًا مستقل التجميع لتعليمات LSE، وتداخل السجلات والمقارنات الموقعة والصلاحيات والإلغاء والحجوزات المادية ونقل NZCV. يجمع `scripts/check_aarch64_atomics.py` عدد 1,100 سجل أصلي من Windows ARM64 يتضمن النتائج وسياق الاستثناء ونطاق RAM كاملًا؛ وترفض اختبارات المحلل الأدلة الناقصة أو غير المتسقة. يظل تنفيذ KVM/WHP الأصلي بحاجة إلى تحقق مستقل. ينفذ `WindowsAtomicProcessTests.cpp` اختبار تراجع العملية checked، ويحفظ `WindowsAtomicResults.def` بصمات السجلات الكاملة. تستخدم الأهداف الأصلية الثابتة بنيويًا آلية الجدولة الحالية مع التحقق من إمكانية التنفيذ مباشرة. ولا يحتفظ الهدف الرمزي الوحيد بالمحمول الوارد إلا بعد تعداد شامل. تتحقق اختبارات الانحدار من 128 انتقالًا ثابتًا ضمن ميزانية استعلامات التنفيذ الخطي، ومن 32 انتقالًا محسوبًا باستعلامي تعداد لكل انتقال، مع الحفاظ على بتات العنوان العليا الحرة ومجالات الفروع. تُرفض تغييرات الحالة الكاملة وغياب المحاذاة وحدّ الأهداف الصفري وميزانيات الاستعلامات أو التعليمات غير الكافية. تظل فحوص رفض الأهداف المتعددة والتعداد غير المكتمل مطلوبة. diff --git a/docs/cpu-execution.md b/docs/cpu-execution.md index f098c10d46..d22fbc0fd2 100644 --- a/docs/cpu-execution.md +++ b/docs/cpu-execution.md @@ -409,7 +409,7 @@ The original baseline `LDAR`, `LDARB`, `LDARH`, `STLR`, `STLRB` and `STLRH` exec Checked ARM64 supports 8/16/32/64-bit `LDXR/STXR`, 32/64-bit register pairs `LDXP/STXP`, their acquire/release variants, and `CLREX`. A shared ISA monitor models 16-byte physical reservation granules for KVM, WHP and checked Unicorn; transport single-step exits do not destroy loop progress. Committed writes invalidate reservations even when bytes remain unchanged, including writes through aliases and retained views. Stops preserve unpublished state; snapshots cannot undo intervening writes. Checked exclusives use FEAT_LSE2 alignment: unaligned operands within one 16-byte aligned block execute; crossing that block raises an `alignment` fault. Equal-width stores match the reserved physical granule. Alignment and permissions are checked before conditional-store success, including when the monitor has expired. MMIO and parallel SMP remain outside this profile. ARM64 Unicorn also uses this monitor in the `Software` contract, including mixed software/checked CPUs sharing physical RAM. See the [Arm instruction definitions](https://documentation-service.arm.com/static/67e40f3398aa3c3b6eea6a85). Unicorn’s `Software` profile retains its engine’s natural-alignment model. -The same shared ISA layer supports FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` and signed/unsigned min/max, including byte/halfword/word/doubleword and acquire/release variants. Checked profiles use the alignment policy above; Unicorn `Software` uses natural alignment. Comparisons use the operand width, while returned old values zero-extend. Failed CAS still requires write permission and selects Arm’s permitted old-value writeback, invalidating physical reservations. Callbacks see old CPU/RAM state; cancellation and synchronous faults publish no partial atomic effects. The physical execution lease orders cooperative CPUs; this does not add parallel SMP or MMIO atomics. `MRS/MSR NZCV` transfers the four condition flags with architectural reserved-bit and zero-register behavior. +The same shared ISA layer supports FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` and signed/unsigned min/max, including byte/halfword/word/doubleword and acquire/release variants. Checked profiles use the alignment policy above; Unicorn `Software` uses natural alignment. Comparisons use the operand width, while returned old values zero-extend. Failed CAS still requires write permission and selects Arm’s permitted old-value writeback, invalidating physical reservations. Callbacks see old CPU/RAM state; cancellation and synchronous faults publish no partial atomic effects. The physical execution lease orders cooperative CPUs; this does not add parallel SMP or MMIO atomics. `MRS/MSR NZCV` transfers the four condition flags with architectural reserved-bit and zero-register behavior. Read permission is checked before write permission: unreadable operands report a read fault, while read-only operands report a write fault, matching the original Windows ARM64 observations. `AArch64InstructionEffects` owns scalar and FP/SIMD single/pair RAM footprints, including operands up to 128 bits. The shared address space validates every page before CPU entry; `RAMTransaction` commits only complete declared physical writes. A 128-bit write observer receives two ordered 64-bit words before effects. Stops and faults preserve RAM, vectors and writeback. Numeric Xn/Vn overlap is valid; wrapping pair footprints are rejected. `NeverDAArch64MemoryTests` uses independent `AArch64CrossPageCases.def` and `AArch64VectorMemoryCases.def` encodings. diff --git a/docs/de/cpu-execution.md b/docs/de/cpu-execution.md index 64d78e2cd8..4ad2463caa 100644 --- a/docs/de/cpu-execution.md +++ b/docs/de/cpu-execution.md @@ -207,4 +207,4 @@ Geprüftes Unicorn verwendet `MachineRunControl`: Ein Zeitrahmen umfasst ARM64-P Checked ARM64 unterstützt `LDXR/STXR` mit 8/16/32/64 Bit, Registerpaare `LDXP/STXP` mit 32/64 Bit, Acquire/Release-Varianten und `CLREX`. KVM, WHP und Checked Unicorn teilen einen ISA-Monitor mit physischen Reservierungsgranulen von 16 Byte; Einzelschritt-Ausstiege verhindern keinen Schleifenfortschritt. Bestätigte Schreibzugriffe invalidieren Reservierungen auch bei unveränderten Bytes, einschließlich Aliasen und gehaltenen Sichten. Stopps bewahren unveröffentlichte Zustände; Snapshots können zwischenzeitliche Schreibzugriffe nicht zurücknehmen. Checked-Exklusivzugriffe folgen FEAT_LSE2: nicht ausgerichtete Operanden innerhalb eines ausgerichteten 16-Byte-Blocks sind zulässig; das Überschreiten erzeugt `alignment`. Gleich breite bedingte Stores passen zum reservierten physischen Reservierungsbereich. Ausrichtung und Rechte werden vor dem Ergebnis des bedingten Speicherns geprüft, auch bei ungültiger Reservierung. MMIO und paralleles SMP bleiben ausgeschlossen. ARM64 Unicorn nutzt diesen Monitor auch im Vertrag `Software`, einschließlich gemeinsam genutzten physischen RAMs zwischen Software- und checked-CPUs. Unicorn behält im Vertrag `Software` das natürliche Ausrichtungsmodell seiner Engine bei. -Dieselbe ISA-Schicht unterstützt FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` sowie vorzeichenbehaftetes und vorzeichenloses Min/Max für Byte, Halbwort, Wort und Doppelwort einschließlich Acquire/Release. Checked verwendet die obige Ausrichtung; Unicorn `Software` behält natürliche Ausrichtung. Vergleiche beachten die Operandenbreite, zurückgegebene Altwerte werden nullerweitert. Ein erfolgloses CAS benötigt ebenfalls Schreibrechte und schreibt den Altwert zurück: Diese von Arm erlaubte Wahl invalidiert physische Reservierungen. Callbacks sehen den alten CPU/RAM-Zustand; Abbruch und synchrone Fehler veröffentlichen keine Teileffekte. Die physische Ausführungssperre ordnet kooperative CPUs, ohne paralleles SMP oder MMIO-Atomics bereitzustellen. `MRS/MSR NZCV` überträgt die vier Bedingungsflags gemäß den Regeln für reservierte Bits und das Nullregister. \ No newline at end of file +Dieselbe ISA-Schicht unterstützt FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` sowie vorzeichenbehaftetes und vorzeichenloses Min/Max für Byte, Halbwort, Wort und Doppelwort einschließlich Acquire/Release. Checked verwendet die obige Ausrichtung; Unicorn `Software` behält natürliche Ausrichtung. Vergleiche beachten die Operandenbreite, zurückgegebene Altwerte werden nullerweitert. Ein erfolgloses CAS benötigt ebenfalls Schreibrechte und schreibt den Altwert zurück: Diese von Arm erlaubte Wahl invalidiert physische Reservierungen. Callbacks sehen den alten CPU/RAM-Zustand; Abbruch und synchrone Fehler veröffentlichen keine Teileffekte. Die physische Ausführungssperre ordnet kooperative CPUs, ohne paralleles SMP oder MMIO-Atomics bereitzustellen. `MRS/MSR NZCV` überträgt die vier Bedingungsflags gemäß den Regeln für reservierte Bits und das Nullregister. Leserechte werden zuerst geprüft: Ein nicht lesbarer Operand meldet einen Lesefehler, ein nur lesbarer Operand einen Schreibfehler, entsprechend den originalen Windows-ARM64-Beobachtungen. \ No newline at end of file diff --git a/docs/de/testing.md b/docs/de/testing.md index caca68ac49..76b4965b50 100644 --- a/docs/de/testing.md +++ b/docs/de/testing.md @@ -1531,7 +1531,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` prüft Breiten, Paare, Acquire/Release, Registerüberlappung, Aliase, Fehler, Snapshots, Abbruch oder Fehler von Beobachtern sowie konkurrierende CPUs. `RAMReservationTests.cpp` prüft identische Schreibwerte, ABA, Wiederverwendung, Rollback sowie String- und `ENTER`-Interferenz auf KVM/Unicorn. Originale Windows-ARM64-Prozessdateien führen exklusive Schleifen aus. `scripts/check_aarch64_exclusives.py` erfasst Originalbefehle und Ausrichtungsfehler auf Windows-ARM64-CI. Diese nativen Beobachtungen belegen keine Ausführung der ARM64-KVM/WHP-Backends; nicht verfügbare Profile werden ausdrücklich übersprungen. Dieselben Fälle prüfen Software-Unicorn, vertragsübergreifende Zugriffe, gleichwertige und ABA-Schreibvorgänge, ausführbare Aliase innerhalb eines Laufs sowie `DC ZVA` und den Abbruch durch Beobachter. `windows-alignment-oracle.yml` führt auch den ARM64-Probelauf aus: 1.320 Beobachtungen decken jeden nicht ausgerichteten Offset, vier Lade-/Speicherfolgen sowie schreibbaren, schreibgeschützten, unzugänglichen und seitenübergreifenden Speicher ab. Registerstartwerte behalten ihre volle Bitbreite; vor einem Fehler abgeschlossene Teilzugriffe werden aufgezeichnet. `WindowsExclusiveProcessTests.cpp` gleicht 1.320 ursprüngliche Windows-ARM64-Beobachtungen mit den nativen Prüfsummen in `WindowsExclusiveNative.def` ab. Nur Code- und Datenadressen werden normalisiert; Register, Ausnahmemetadaten und RAM-Effekte bleiben erhalten. -`AArch64AtomicTests.cpp` prüft 168 unabhängig assemblierte LSE-Kodierungen, Registeraliase, vorzeichenbehaftete Vergleiche, Rechte, Abbruch, physische Reservierungen und NZCV-Transfers. `scripts/check_aarch64_atomics.py` sammelt 1.100 originale Windows-ARM64-Datensätze mit vollständigen Ergebnissen, Ausnahmekontexten und RAM-Bereichen; Parsertests weisen fehlende oder widersprüchliche Belege zurück. Native KVM/WHP-Ausführung muss gesondert validiert werden. +`AArch64AtomicTests.cpp` prüft 168 unabhängig assemblierte LSE-Kodierungen, Registeraliase, vorzeichenbehaftete Vergleiche, Rechte, Abbruch, physische Reservierungen und NZCV-Transfers. `scripts/check_aarch64_atomics.py` sammelt 1.100 originale Windows-ARM64-Datensätze mit vollständigen Ergebnissen, Ausnahmekontexten und RAM-Bereichen; Parsertests weisen fehlende oder widersprüchliche Belege zurück. Native KVM/WHP-Ausführung muss gesondert validiert werden. `WindowsAtomicProcessTests.cpp` prüft den Checked-Prozess; `WindowsAtomicResults.def` enthält Prüfsummen der vollständigen Datensätze. Strukturell konstante native Ziele verwenden direkt die bestehende Planung mit Machbarkeitsprüfung. Ein symbolisches Einzelziel behält das eingehende Prädikat erst nach vollständiger Aufzählung bei. Regressionen prüfen 128 konstante Übertragungen mit dem Abfragebudget geradliniger Ausführung und 32 berechnete Übertragungen mit zwei Aufzählungsabfragen pro Übertragung; freie obere Adressbits und Zweigdomänen bleiben erhalten. Geänderte Gesamtzustände, fehlende Ausrichtung, ein Ziellimit von null und zu kleine Abfrage- oder Befehlsbudgets werden abgelehnt. Bestehende Ablehnungstests für mehrere Ziele und unvollständige Aufzählung bleiben erforderlich. diff --git a/docs/es/cpu-execution.md b/docs/es/cpu-execution.md index 859522fa6d..2e0849489a 100644 --- a/docs/es/cpu-execution.md +++ b/docs/es/cpu-execution.md @@ -207,4 +207,4 @@ Unicorn comprobado usa `MachineRunControl`: un único margen cubre mantenimiento ARM64 checked admite `LDXR/STXR` de 8/16/32/64 bits, pares `LDXP/STXP` de 32/64 bits, variantes acquire/release y `CLREX`. KVM, WHP y Unicorn checked comparten un monitor ISA con reservas físicas de 16 bytes; las salidas de ejecución paso a paso no impiden avanzar a los bucles. Las escrituras confirmadas invalidan las reservas incluso sin cambiar los bytes, también mediante alias y vistas retenidas. Una parada conserva el estado sin publicar; las instantáneas no deshacen escrituras intermedias. Los exclusivos checked siguen FEAT_LSE2: permiten operandos no alineados dentro de un bloque alineado de 16 bytes; cruzarlo genera `alignment`. Las escrituras del mismo ancho coinciden con el gránulo físico reservado. La alineación y los permisos se verifican antes del resultado del almacenamiento condicional, incluso con reserva caducada. MMIO y SMP paralelo quedan fuera del perfil. Unicorn ARM64 también usa este monitor con el contrato `Software`, incluso cuando CPU de software y checked comparten RAM física. El contrato `Software` de Unicorn conserva la alineación natural de su motor. -La misma capa ISA admite FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` y min/max con o sin signo, para byte, media palabra, palabra y doble palabra y variantes acquire/release. Checked usa la alineación anterior; Unicorn `Software` conserva la natural. Las comparaciones respetan el ancho y los valores anteriores devueltos se extienden con ceros. Un CAS fallido también exige permiso de escritura y reescribe el valor anterior, opción permitida por Arm que invalida reservas físicas. Los callbacks ven CPU/RAM antes de confirmar; cancelación y fallos síncronos no publican efectos parciales. El bloqueo físico ordena CPU cooperativas, sin SMP paralelo ni atómicas MMIO. `MRS/MSR NZCV` transfiere los cuatro indicadores respetando bits reservados y registro cero. \ No newline at end of file +La misma capa ISA admite FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` y min/max con o sin signo, para byte, media palabra, palabra y doble palabra y variantes acquire/release. Checked usa la alineación anterior; Unicorn `Software` conserva la natural. Las comparaciones respetan el ancho y los valores anteriores devueltos se extienden con ceros. Un CAS fallido también exige permiso de escritura y reescribe el valor anterior, opción permitida por Arm que invalida reservas físicas. Los callbacks ven CPU/RAM antes de confirmar; cancelación y fallos síncronos no publican efectos parciales. El bloqueo físico ordena CPU cooperativas, sin SMP paralelo ni atómicas MMIO. `MRS/MSR NZCV` transfiere los cuatro indicadores respetando bits reservados y registro cero. Se comprueba primero el permiso de lectura: un operando ilegible produce un fallo de lectura y uno de solo lectura un fallo de escritura, como en las observaciones originales de Windows ARM64. \ No newline at end of file diff --git a/docs/es/testing.md b/docs/es/testing.md index 2b80ba6342..b8b518d3f6 100644 --- a/docs/es/testing.md +++ b/docs/es/testing.md @@ -1516,7 +1516,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` verifica anchuras, pares, acquire/release, solapamientos, alias, fallos, instantáneas, cancelación o fallo de observadores y competencia entre CPU. `RAMReservationTests.cpp` cubre escrituras idénticas, ABA, reutilización, reversión e interferencias de cadenas y `ENTER` en KVM/Unicorn. Las muestras Windows ARM64 originales ejecutan bucles exclusivos. `scripts/check_aarch64_exclusives.py` observa instrucciones originales y excepciones de alineación en CI Windows ARM64. Estas observaciones nativas no acreditan la ejecución de los backends ARM64 KVM/WHP; los perfiles no disponibles quedan explícitamente omitidos. Los mismos casos cubren Unicorn de software, interferencias entre contratos, escrituras idénticas y ABA, alias ejecutables dentro de una ejecución, y escrituras `DC ZVA` con cancelación por observadores. `windows-alignment-oracle.yml` también ejecuta la prueba ARM64: 1.320 observaciones cubren cada desplazamiento no alineado, cuatro secuencias de carga/almacenamiento y memoria escribible, de solo lectura, inaccesible o entre páginas. Conserva el ancho completo de los valores iniciales de registros y las escrituras parciales anteriores a un fallo. `WindowsExclusiveProcessTests.cpp` compara 1.320 observaciones originales de Windows ARM64 con los resúmenes nativos de `WindowsExclusiveNative.def`. Solo normaliza la ubicación del código y los datos; conserva registros, metadatos de excepción y efectos en RAM. -`AArch64AtomicTests.cpp` cubre 168 codificaciones LSE ensambladas de forma independiente, alias, comparaciones con signo, permisos, cancelación, reservas físicas y transferencias NZCV. `scripts/check_aarch64_atomics.py` recopila 1.100 registros originales de Windows ARM64 con resultados, contexto de excepción y huella RAM completos; el analizador rechaza pruebas ausentes o incoherentes. KVM/WHP nativos requieren validación aparte. +`AArch64AtomicTests.cpp` cubre 168 codificaciones LSE ensambladas de forma independiente, alias, comparaciones con signo, permisos, cancelación, reservas físicas y transferencias NZCV. `scripts/check_aarch64_atomics.py` recopila 1.100 registros originales de Windows ARM64 con resultados, contexto de excepción y huella RAM completos; el analizador rechaza pruebas ausentes o incoherentes. KVM/WHP nativos requieren validación aparte. `WindowsAtomicProcessTests.cpp` verifica el proceso checked; `WindowsAtomicResults.def` conserva los resúmenes de los registros completos. Los destinos nativos estructuralmente constantes usan directamente el planificador existente con comprobación de viabilidad. Un destino simbólico único conserva el predicado de entrada solo tras una enumeración exhaustiva. Las regresiones verifican 128 transferencias constantes con el presupuesto de consultas de una ejecución lineal y 32 transferencias calculadas con dos consultas de enumeración por transferencia, manteniendo libres los bits altos de dirección y los dominios de las ramas. Se rechazan cambios en el estado completo, falta de alineación, límites de destinos nulos y presupuestos insuficientes de consultas o instrucciones. Siguen siendo necesarias las comprobaciones de rechazo de múltiples destinos y enumeraciones incompletas. diff --git a/docs/fr/cpu-execution.md b/docs/fr/cpu-execution.md index b1e8544532..6552ca93f1 100644 --- a/docs/fr/cpu-execution.md +++ b/docs/fr/cpu-execution.md @@ -207,4 +207,4 @@ Unicorn vérifié utilise `MachineRunControl` : une seule allocation couvre main ARM64 checked prend en charge `LDXR/STXR` sur 8/16/32/64 bits, les paires `LDXP/STXP` sur 32/64 bits, leurs variantes acquire/release et `CLREX`. KVM, WHP et Unicorn checked partagent un moniteur ISA à granularité physique de 16 octets ; les sorties de pas à pas ne bloquent pas les boucles. Toute écriture validée invalide la réservation, même à valeur identique, y compris via les alias et vues conservées. Un arrêt conserve les effets non publiés ; un instantané ne peut annuler les écritures intervenues. Les exclusifs checked suivent FEAT_LSE2 : un opérande non aligné reste valide dans un bloc aligné de 16 octets ; franchir ce bloc produit `alignment`. Les stockages de même largeur correspondent au granule physique réservé. Alignement et permissions sont vérifiés avant le résultat du stockage conditionnel, même sans réservation valide. MMIO et SMP parallèle restent hors profil. Unicorn ARM64 utilise aussi ce moniteur avec le contrat `Software`, y compris lorsque des CPU logiciels et checked partagent la RAM physique. Le contrat `Software` d’Unicorn conserve l’alignement naturel de son moteur. -La même couche ISA prend en charge FEAT_LSE : `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` et min/max signés ou non, avec les largeurs octet, demi-mot, mot et double mot et les variantes acquire/release. Les profils checked suivent l’alignement ci-dessus ; Unicorn `Software` conserve l’alignement naturel. Les comparaisons utilisent la largeur de l’opérande et les anciennes valeurs retournées sont étendues par zéro. Un CAS échoué exige aussi le droit d’écriture et réécrit l’ancienne valeur, choix permis par Arm qui invalide les réservations physiques. Les callbacks voient l’état CPU/RAM avant validation ; annulation et faute synchrone ne publient aucun effet partiel. Le verrou physique ordonne les CPU coopératifs, sans SMP parallèle ni atomiques MMIO. `MRS/MSR NZCV` transfère les quatre indicateurs selon les règles des bits réservés et du registre zéro. \ No newline at end of file +La même couche ISA prend en charge FEAT_LSE : `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` et min/max signés ou non, avec les largeurs octet, demi-mot, mot et double mot et les variantes acquire/release. Les profils checked suivent l’alignement ci-dessus ; Unicorn `Software` conserve l’alignement naturel. Les comparaisons utilisent la largeur de l’opérande et les anciennes valeurs retournées sont étendues par zéro. Un CAS échoué exige aussi le droit d’écriture et réécrit l’ancienne valeur, choix permis par Arm qui invalide les réservations physiques. Les callbacks voient l’état CPU/RAM avant validation ; annulation et faute synchrone ne publient aucun effet partiel. Le verrou physique ordonne les CPU coopératifs, sans SMP parallèle ni atomiques MMIO. `MRS/MSR NZCV` transfère les quatre indicateurs selon les règles des bits réservés et du registre zéro. Le droit de lecture est vérifié en premier : un opérande illisible produit un défaut de lecture, un opérande en lecture seule un défaut d’écriture, conformément aux observations Windows ARM64 originales. \ No newline at end of file diff --git a/docs/fr/testing.md b/docs/fr/testing.md index d21667342b..a471dab571 100644 --- a/docs/fr/testing.md +++ b/docs/fr/testing.md @@ -1530,7 +1530,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` couvre largeurs, paires, acquire/release, chevauchements de registres, alias, défauts, instantanés, annulation ou échec des observateurs et concurrence entre CPU. `RAMReservationTests.cpp` couvre écritures identiques, ABA, réutilisation, annulation et interférences des chaînes et de `ENTER` sur KVM/Unicorn. Les programmes Windows ARM64 originaux exécutent des boucles exclusives. `scripts/check_aarch64_exclusives.py` observe les instructions originales et exceptions d’alignement dans la CI Windows ARM64. Ces observations natives ne prouvent pas l’exécution des backends ARM64 KVM/WHP ; les profils indisponibles restent explicitement ignorés. Les mêmes cas couvrent Unicorn logiciel, les interférences entre contrats, les écritures identiques et ABA, les alias exécutables au cours d’une même exécution, ainsi que `DC ZVA` et l’annulation par les observateurs. `windows-alignment-oracle.yml` exécute aussi la sonde ARM64 : 1 320 observations couvrent chaque décalage non aligné, quatre séquences de chargement/stockage et la mémoire accessible en écriture, en lecture seule, inaccessible ou traversant une limite de page. La sonde conserve la largeur complète des registres et les éventuelles écritures partielles précédant une faute. `WindowsExclusiveProcessTests.cpp` compare 1 320 observations Windows ARM64 originales aux empreintes natives de `WindowsExclusiveNative.def`. Seul le placement du code et des données est normalisé ; les registres, métadonnées d’exception et effets RAM sont conservés. -`AArch64AtomicTests.cpp` couvre 168 encodages LSE assemblés indépendamment, les alias de registres, les comparaisons signées, droits, annulations, réservations physiques et transferts NZCV. `scripts/check_aarch64_atomics.py` collecte 1 100 observations Windows ARM64 originales avec résultats, contexte d’exception et empreinte RAM complets ; les tests du parseur refusent les preuves absentes ou incohérentes. L’exécution KVM/WHP native exige une validation distincte. +`AArch64AtomicTests.cpp` couvre 168 encodages LSE assemblés indépendamment, les alias de registres, les comparaisons signées, droits, annulations, réservations physiques et transferts NZCV. `scripts/check_aarch64_atomics.py` collecte 1 100 observations Windows ARM64 originales avec résultats, contexte d’exception et empreinte RAM complets ; les tests du parseur refusent les preuves absentes ou incohérentes. L’exécution KVM/WHP native exige une validation distincte. La régression du processus checked est `WindowsAtomicProcessTests.cpp` ; `WindowsAtomicResults.def` conserve les condensats des enregistrements complets. Les cibles natives structurellement constantes utilisent directement l’ordonnanceur existant avec contrôle de faisabilité. Une cible symbolique unique ne conserve le prédicat entrant qu’après une énumération exhaustive. Les régressions vérifient 128 transferts constants avec le budget de requêtes d’une exécution linéaire et 32 transferts calculés avec deux requêtes d’énumération par transfert, en préservant les bits hauts libres des adresses et les domaines des branches. Les états complets modifiés, l’absence d’alignement, une limite de cibles nulle et les budgets de requêtes ou d’instructions insuffisants sont refusés. Les contrôles existants de refus des cibles multiples et des énumérations incomplètes restent requis. diff --git a/docs/it/cpu-execution.md b/docs/it/cpu-execution.md index 233ad2e48a..05e2ca4471 100644 --- a/docs/it/cpu-execution.md +++ b/docs/it/cpu-execution.md @@ -207,4 +207,4 @@ Unicorn verificato usa `MachineRunControl`: un unico margine copre manutenzione ARM64 checked supporta `LDXR/STXR` a 8/16/32/64 bit, coppie `LDXP/STXP` a 32/64 bit, varianti acquire/release e `CLREX`. KVM, WHP e Unicorn checked condividono un monitor ISA con prenotazioni fisiche di 16 byte; le uscite per passo singolo non bloccano i cicli. Le scritture confermate invalidano le prenotazioni anche senza cambiare i byte, inclusi alias e viste conservate. Gli arresti preservano lo stato non pubblicato; gli snapshot non annullano le scritture intermedie. Gli esclusivi checked seguono FEAT_LSE2: ammettono operandi non allineati entro un blocco allineato di 16 byte; attraversarlo produce `alignment`. Gli store della stessa larghezza corrispondono al granulo fisico prenotato. Allineamento e permessi sono verificati prima del risultato della scrittura condizionale, anche con prenotazione scaduta. MMIO e SMP parallelo restano esclusi. Unicorn ARM64 usa questo monitor anche nel contratto `Software`, inclusi CPU software e checked che condividono la RAM fisica. Il contratto `Software` di Unicorn mantiene l’allineamento naturale del motore. -Lo stesso livello ISA supporta FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` e min/max con o senza segno, per byte, mezza parola, parola e doppia parola e varianti acquire/release. Checked usa l’allineamento sopra descritto; Unicorn `Software` mantiene quello naturale. Il confronto rispetta la larghezza e i vecchi valori restituiti sono estesi con zeri. Un CAS fallito richiede comunque permessi di scrittura e riscrive il vecchio valore, scelta ammessa da Arm che invalida le prenotazioni fisiche. I callback vedono lo stato CPU/RAM precedente; annullamenti ed errori sincroni non pubblicano effetti parziali. Il blocco fisico ordina le CPU cooperative, senza SMP parallelo o atomiche MMIO. `MRS/MSR NZCV` trasferisce i quattro flag rispettando bit riservati e registro zero. \ No newline at end of file +Lo stesso livello ISA supporta FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` e min/max con o senza segno, per byte, mezza parola, parola e doppia parola e varianti acquire/release. Checked usa l’allineamento sopra descritto; Unicorn `Software` mantiene quello naturale. Il confronto rispetta la larghezza e i vecchi valori restituiti sono estesi con zeri. Un CAS fallito richiede comunque permessi di scrittura e riscrive il vecchio valore, scelta ammessa da Arm che invalida le prenotazioni fisiche. I callback vedono lo stato CPU/RAM precedente; annullamenti ed errori sincroni non pubblicano effetti parziali. Il blocco fisico ordina le CPU cooperative, senza SMP parallelo o atomiche MMIO. `MRS/MSR NZCV` trasferisce i quattro flag rispettando bit riservati e registro zero. Si verifica prima il permesso di lettura: un operando illeggibile produce un errore di lettura, uno di sola lettura un errore di scrittura, come nelle osservazioni originali Windows ARM64. \ No newline at end of file diff --git a/docs/it/testing.md b/docs/it/testing.md index 0fdda7f0c3..ea09fa7d52 100644 --- a/docs/it/testing.md +++ b/docs/it/testing.md @@ -1511,7 +1511,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` verifica larghezze, coppie, acquire/release, sovrapposizioni, alias, fault, snapshot, annullamento o errore degli osservatori e CPU concorrenti. `RAMReservationTests.cpp` copre scritture identiche, ABA, riuso, rollback e interferenze di stringhe e `ENTER` su KVM/Unicorn. I programmi originali Windows ARM64 eseguono cicli esclusivi. `scripts/check_aarch64_exclusives.py` rileva istruzioni originali ed eccezioni di allineamento nella CI Windows ARM64. Queste osservazioni native non dimostrano l’esecuzione dei backend ARM64 KVM/WHP; i profili indisponibili restano esplicitamente saltati. Gli stessi casi verificano Unicorn software, interferenze tra contratti, scritture identiche e ABA, alias eseguibili nella stessa esecuzione, e scritture `DC ZVA` con annullamento da parte degli osservatori. `windows-alignment-oracle.yml` esegue anche la sonda ARM64: 1.320 osservazioni coprono ogni offset non allineato, quattro sequenze di caricamento/scrittura e memoria scrivibile, di sola lettura, inaccessibile o a cavallo di pagine. Conserva l’intera larghezza dei valori iniziali dei registri e le scritture parziali completate prima di un errore. `WindowsExclusiveProcessTests.cpp` confronta 1.320 osservazioni Windows ARM64 originali con i digest nativi in `WindowsExclusiveNative.def`. Normalizza solo la collocazione di codice e dati, mantenendo registri, metadati delle eccezioni ed effetti RAM. -`AArch64AtomicTests.cpp` copre 168 codifiche LSE assemblate indipendentemente, alias, confronti con segno, permessi, annullamento, prenotazioni fisiche e trasferimenti NZCV. `scripts/check_aarch64_atomics.py` raccoglie 1.100 record originali Windows ARM64 con risultati, contesto delle eccezioni e impronta RAM completi; i test del parser rifiutano prove mancanti o incoerenti. L’esecuzione nativa KVM/WHP richiede verifica separata. +`AArch64AtomicTests.cpp` copre 168 codifiche LSE assemblate indipendentemente, alias, confronti con segno, permessi, annullamento, prenotazioni fisiche e trasferimenti NZCV. `scripts/check_aarch64_atomics.py` raccoglie 1.100 record originali Windows ARM64 con risultati, contesto delle eccezioni e impronta RAM completi; i test del parser rifiutano prove mancanti o incoerenti. L’esecuzione nativa KVM/WHP richiede verifica separata. `WindowsAtomicProcessTests.cpp` verifica il processo checked; `WindowsAtomicResults.def` conserva i digest dei record completi. Gli obiettivi nativi strutturalmente costanti usano direttamente la pianificazione esistente con verifica di fattibilità. Un singolo obiettivo simbolico mantiene il predicato di ingresso solo dopo un’enumerazione completa. Le regressioni verificano 128 trasferimenti costanti con il budget di interrogazioni dell’esecuzione lineare e 32 trasferimenti calcolati con due interrogazioni di enumerazione per trasferimento, conservando i bit alti liberi degli indirizzi e i domini dei rami. Stati completi modificati, allineamenti mancanti, limite degli obiettivi nullo e budget insufficienti di interrogazioni o istruzioni vengono rifiutati. Restano necessari i controlli di rifiuto per obiettivi multipli ed enumerazioni incomplete. diff --git a/docs/ja/cpu-execution.md b/docs/ja/cpu-execution.md index 1aaf1e7856..883ea3e686 100644 --- a/docs/ja/cpu-execution.md +++ b/docs/ja/cpu-execution.md @@ -207,4 +207,4 @@ checked Unicorn は `MachineRunControl` を使い、ARM64 の保守、ゲスト checked ARM64 は 8/16/32/64 ビットの `LDXR/STXR`、32/64 ビットのレジスタ対 `LDXP/STXP`、acquire/release 形式、`CLREX` をサポートします。KVM、WHP、checked Unicorn は ISA 層の排他モニターを共有し、16 バイトの物理範囲で予約を管理します。転送層のシングルステップ終了でループの進行を妨げません。同じ値の書き込みも、エイリアスや保持ビューを含め、コミット時に予約を無効化します。停止は未公開状態を保ち、スナップショットは途中の書き込みを取り消せません。checked 排他命令は FEAT_LSE2 の整列規則を使います。16 バイト境界内の非整列アクセスは実行でき、境界を越えると `alignment` 障害になります。同じ幅の条件付きストアは予約された物理粒度で照合します。予約が失効していても、条件付きストアの判定前に整列と権限を検査します。MMIO、並列 SMP は対象外です。 ARM64 Unicorn の `Software` 契約もこのモニターを使用し、ソフトウェア CPU と checked CPU が物理 RAM を共有する場合にも対応します。 Unicorn の `Software` 契約はエンジンの自然整列モデルを維持します。 -同じ ISA 層は FEAT_LSE の `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET`、符号付き・符号なし min/max を、バイト・半語・語・倍語および acquire/release 形式でサポートします。checked は上記の整列方針を、Unicorn `Software` は自然整列を使います。比較はオペランド幅に従い、返す旧値はゼロ拡張します。CAS の比較失敗でも書き込み権限を確認し、Arm が認める旧値の書き戻しを選択して物理予約を無効化します。コールバックはコミット前の CPU/RAM を参照し、取消しや同期障害で部分結果を公開しません。物理実行ロックが協調 CPU の順序を保証し、並列 SMP や MMIO アトミックは提供しません。`MRS/MSR NZCV` は予約ビットとゼロレジスタの規則に従い、4 個の条件フラグを転送します。 \ No newline at end of file +同じ ISA 層は FEAT_LSE の `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET`、符号付き・符号なし min/max を、バイト・半語・語・倍語および acquire/release 形式でサポートします。checked は上記の整列方針を、Unicorn `Software` は自然整列を使います。比較はオペランド幅に従い、返す旧値はゼロ拡張します。CAS の比較失敗でも書き込み権限を確認し、Arm が認める旧値の書き戻しを選択して物理予約を無効化します。コールバックはコミット前の CPU/RAM を参照し、取消しや同期障害で部分結果を公開しません。物理実行ロックが協調 CPU の順序を保証し、並列 SMP や MMIO アトミックは提供しません。`MRS/MSR NZCV` は予約ビットとゼロレジスタの規則に従い、4 個の条件フラグを転送します。 読み取り権限を先に検査します。読み取り不可なら読み取り障害、読み取り専用なら書き込み障害となり、元の Windows ARM64 観測と一致します。 \ No newline at end of file diff --git a/docs/ja/testing.md b/docs/ja/testing.md index fa46860cd1..6f44ed7848 100644 --- a/docs/ja/testing.md +++ b/docs/ja/testing.md @@ -1415,7 +1415,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` は幅、レジスタ対、acquire/release、レジスタ重複、エイリアス、整列・権限障害、スナップショット、観測コールバックの停止・失敗、CPU 間競合を検査します。`RAMReservationTests.cpp` は同値書き込み、ABA、割り当て再利用、ロールバック、KVM/Unicorn の文字列命令と `ENTER` による干渉を検査します。元の ARM64 Windows プロセス試料は排他ループを実行します。`scripts/check_aarch64_exclusives.py` は Windows ARM64 CI で元の命令と整列例外を記録します。これは ARM64 KVM/WHP バックエンド実行の証拠とは別で、利用できない構成はスキップとして記録します。 同じ排他命令のテストはソフトウェア Unicorn、契約間の干渉、同値・ABA 書き込み、同一実行内の実行可能エイリアス、`DC ZVA` 書き込みと観測コールバックによる停止も検証します。 `windows-alignment-oracle.yml` は ARM64 プローブも実行します。1,320 件の観測で全非整列オフセット、4 種類のロード・ストア列、書き込み可能・読み取り専用・アクセス禁止・ページ境界越えのメモリを調べます。レジスタ初期値の全ビット幅と障害前に確定した部分書き込みを記録します。 `WindowsExclusiveProcessTests.cpp` は 1,320 件の元の Windows ARM64 観測を `WindowsExclusiveNative.def` のネイティブダイジェストと照合します。コードとデータの配置だけを正規化し、レジスタ値、例外メタデータ、RAM の変化を保持します。 -`AArch64AtomicTests.cpp` は独立にアセンブルした 168 種の LSE 符号化、レジスタ重複、符号付き比較、権限、取消し、物理予約、NZCV 転送を検証します。`scripts/check_aarch64_atomics.py` は演算結果、例外コンテキスト、RAM 範囲を含む 1,100 件の Windows ARM64 原記録を収集します。解析テストは欠落や不整合を拒否します。ネイティブ KVM/WHP 実行は別途検証が必要です。 +`AArch64AtomicTests.cpp` は独立にアセンブルした 168 種の LSE 符号化、レジスタ重複、符号付き比較、権限、取消し、物理予約、NZCV 転送を検証します。`scripts/check_aarch64_atomics.py` は演算結果、例外コンテキスト、RAM 範囲を含む 1,100 件の Windows ARM64 原記録を収集します。解析テストは欠落や不整合を拒否します。ネイティブ KVM/WHP 実行は別途検証が必要です。 checked プロセスの回帰は `WindowsAtomicProcessTests.cpp` が実行し、全記録のダイジェストは `WindowsAtomicResults.def` に保持します。 構造上定数であるネイティブターゲットは、既存の実行可能性検査付きスケジューラーを直接使用します。記号的な単一ターゲットは完全な列挙の後にのみ入力述語を保持します。回帰テストは直線実行と同じクエリ予算で 128 回の定数転送を、転送ごとに 2 回の列挙クエリで 32 回の計算された転送を検証し、未制約のアドレス上位ビットと分岐領域を保持します。完全な状態結果の変更、アラインメントの欠如、ターゲット上限ゼロ、クエリまたは命令予算の不足は拒否します。複数ターゲットと未完了の列挙に対する既存の拒否検査も引き続き必要です。 diff --git a/docs/ko/cpu-execution.md b/docs/ko/cpu-execution.md index 4df785b1ac..bcd6af7827 100644 --- a/docs/ko/cpu-execution.md +++ b/docs/ko/cpu-execution.md @@ -207,4 +207,4 @@ checked Unicorn은 `MachineRunControl`을 사용하며 ARM64 유지보수, 게 checked ARM64는 8/16/32/64비트 `LDXR/STXR`, 32/64비트 레지스터 쌍 `LDXP/STXP`, acquire/release 형태와 `CLREX`를 지원합니다. KVM, WHP, checked Unicorn은 ISA 계층의 독점 모니터를 공유하며 16바이트 물리 범위로 예약을 추적합니다. 전송 계층의 단일 단계 종료가 루프 진행을 막지 않습니다. 동일 값 쓰기도 커밋되면 예약을 무효화하며 별칭과 보존 뷰에도 같은 규칙을 적용합니다. 중지는 미공개 상태를 보존하며 스냅샷은 중간에 발생한 쓰기를 취소하지 못합니다. checked 독점 명령은 FEAT_LSE2 정렬 규칙을 사용합니다. 정렬된 16바이트 블록 안의 비정렬 피연산자는 실행되며, 블록을 넘으면 `alignment` 오류가 발생합니다. 같은 폭의 조건부 저장은 예약된 물리 단위로 일치 여부를 판단합니다. 예약이 만료된 경우에도 조건부 저장 판정 전에 정렬과 권한을 검사합니다. MMIO와 병렬 SMP는 포함하지 않습니다. ARM64 Unicorn의 `Software` 계약도 이 모니터를 사용하며 소프트웨어 CPU와 checked CPU가 물리 RAM을 공유하는 경우를 포함합니다. Unicorn의 `Software` 계약은 엔진의 자연 정렬 모델을 유지합니다. -같은 ISA 계층은 FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET`, 부호 있는/없는 min/max의 바이트·하프워드·워드·더블워드 및 acquire/release 형식을 지원합니다. checked는 위의 정렬 정책을, Unicorn `Software`는 자연 정렬을 사용합니다. 비교는 피연산자 폭에 따르고 반환되는 이전 값은 0으로 확장합니다. CAS 비교가 실패해도 쓰기 권한을 확인하고, Arm이 허용하는 이전 값 쓰기를 선택하여 물리 예약을 무효화합니다. 콜백은 커밋 전 CPU/RAM을 보며 취소와 동기 오류는 부분 결과를 공개하지 않습니다. 물리 실행 잠금은 협력적 CPU의 순서를 보장하며 병렬 SMP나 MMIO 원자 연산을 제공하지 않습니다. `MRS/MSR NZCV`는 예약 비트와 영 레지스터 규칙에 따라 네 조건 플래그를 전송합니다. \ No newline at end of file +같은 ISA 계층은 FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET`, 부호 있는/없는 min/max의 바이트·하프워드·워드·더블워드 및 acquire/release 형식을 지원합니다. checked는 위의 정렬 정책을, Unicorn `Software`는 자연 정렬을 사용합니다. 비교는 피연산자 폭에 따르고 반환되는 이전 값은 0으로 확장합니다. CAS 비교가 실패해도 쓰기 권한을 확인하고, Arm이 허용하는 이전 값 쓰기를 선택하여 물리 예약을 무효화합니다. 콜백은 커밋 전 CPU/RAM을 보며 취소와 동기 오류는 부분 결과를 공개하지 않습니다. 물리 실행 잠금은 협력적 CPU의 순서를 보장하며 병렬 SMP나 MMIO 원자 연산을 제공하지 않습니다. `MRS/MSR NZCV`는 예약 비트와 영 레지스터 규칙에 따라 네 조건 플래그를 전송합니다. 읽기 권한을 먼저 검사합니다. 읽을 수 없는 피연산자는 읽기 오류, 읽기 전용 피연산자는 쓰기 오류를 보고하며 원본 Windows ARM64 관측과 일치합니다. \ No newline at end of file diff --git a/docs/ko/testing.md b/docs/ko/testing.md index 01e6f951eb..35159344f0 100644 --- a/docs/ko/testing.md +++ b/docs/ko/testing.md @@ -1405,7 +1405,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp`는 폭, 레지스터 쌍, acquire/release, 레지스터 중첩, 별칭, 정렬·권한 오류, 스냅샷, 관찰 콜백 취소·실패 및 CPU 간 경쟁을 검사합니다. `RAMReservationTests.cpp`는 동일 값 쓰기, ABA, 할당 재사용, 롤백, KVM/Unicorn 문자열 명령과 `ENTER` 쓰기의 간섭을 검사합니다. 원본 ARM64 Windows 프로세스 예제는 독점 루프를 실행합니다. `scripts/check_aarch64_exclusives.py`는 Windows ARM64 CI에서 원본 명령과 정렬 예외 기록을 수집합니다. 이 증거가 ARM64 KVM/WHP 백엔드 실행을 입증하는 것은 아니며, 사용할 수 없는 구성은 명시적으로 건너뜁니다. 동일한 독점 명령 테스트는 소프트웨어 Unicorn, 계약 간 간섭, 동일 값 및 ABA 쓰기, 같은 실행 내 실행 가능 별칭, `DC ZVA` 쓰기와 관찰 콜백 취소도 검증합니다. `windows-alignment-oracle.yml`은 ARM64 프로브도 실행합니다. 1,320개 관측은 모든 비정렬 오프셋, 네 가지 로드·스토어 순서, 쓰기 가능·읽기 전용·접근 불가·페이지 경계 메모리를 다룹니다. 프로브는 레지스터 초기값의 전체 비트 폭과 오류 전에 커밋된 부분 쓰기를 보존합니다. `WindowsExclusiveProcessTests.cpp`는 1,320개의 원본 Windows ARM64 관측을 `WindowsExclusiveNative.def`의 네이티브 다이제스트와 비교합니다. 코드와 데이터 배치만 정규화하며 레지스터 값, 예외 메타데이터와 RAM 효과를 보존합니다. -`AArch64AtomicTests.cpp`는 독립적으로 어셈블한 LSE 인코딩 168개, 레지스터 별칭, 부호 비교, 권한, 취소, 물리 예약 및 NZCV 전송을 검사합니다. `scripts/check_aarch64_atomics.py`는 연산 결과, 예외 컨텍스트 및 RAM 범위를 담은 Windows ARM64 원본 기록 1,100개를 수집합니다. 파서 검사는 누락되거나 불일치하는 증거를 거부합니다. 네이티브 KVM/WHP 실행은 별도 검증이 필요합니다. +`AArch64AtomicTests.cpp`는 독립적으로 어셈블한 LSE 인코딩 168개, 레지스터 별칭, 부호 비교, 권한, 취소, 물리 예약 및 NZCV 전송을 검사합니다. `scripts/check_aarch64_atomics.py`는 연산 결과, 예외 컨텍스트 및 RAM 범위를 담은 Windows ARM64 원본 기록 1,100개를 수집합니다. 파서 검사는 누락되거나 불일치하는 증거를 거부합니다. 네이티브 KVM/WHP 실행은 별도 검증이 필요합니다. checked 프로세스 회귀는 `WindowsAtomicProcessTests.cpp`가 실행하며 전체 기록의 다이제스트는 `WindowsAtomicResults.def`에 보관합니다. 구조적으로 상수인 네이티브 대상은 기존 실행 가능성 검사 스케줄러를 직접 사용합니다. 기호 단일 대상은 완전한 열거 뒤에만 입력 술어를 유지합니다. 회귀 테스트는 직선 실행과 같은 질의 예산으로 상수 전송 128회를, 전송당 열거 질의 2회로 계산된 전송 32회를 검증하고, 제약 없는 주소 상위 비트와 분기 영역을 유지합니다. 전체 상태 결과 변경, 정렬 제약 누락, 대상 한도 0, 질의나 명령 예산 부족은 거부합니다. 여러 대상과 불완전한 열거에 대한 기존 거부 검사도 계속 필요합니다. diff --git a/docs/ru/cpu-execution.md b/docs/ru/cpu-execution.md index dedda50f20..0486d77ff4 100644 --- a/docs/ru/cpu-execution.md +++ b/docs/ru/cpu-execution.md @@ -207,4 +207,4 @@ KVM сравнивает общие регистры и полное состо Checked ARM64 поддерживает `LDXR/STXR` для 8/16/32/64 бит, пары регистров `LDXP/STXP` для 32/64 бит, варианты acquire/release и `CLREX`. KVM, WHP и checked Unicorn используют общий монитор ISA с физическими гранулами резервирования по 16 байт; пошаговые выходы транспорта не мешают продвижению циклов. Подтверждённые записи сбрасывают резервирование даже без изменения байтов, включая псевдонимы и удерживаемые представления. Остановка сохраняет неопубликованное состояние; снимок не отменяет промежуточные записи. Эксклюзивные операции checked следуют FEAT_LSE2: невыровненный операнд допустим внутри выровненного 16-байтного блока; пересечение его границы даёт `alignment`. Записи той же разрядности сопоставляются с зарезервированной физической гранулой. Выравнивание и права проверяются до результата условной записи, даже при утраченном резервировании. MMIO и параллельный SMP не входят в профиль. Unicorn ARM64 использует этот монитор и в контракте `Software`, включая совместную физическую RAM для программных и checked-CPU. Контракт `Software` Unicorn сохраняет естественное выравнивание движка. -Общий слой ISA поддерживает FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` и знаковые/беззнаковые min/max для байта, полуслова, слова и двойного слова, включая acquire/release. Checked использует описанное выше выравнивание, Unicorn `Software` — естественное. Сравнения учитывают ширину операнда, возвращаемые старые значения расширяются нулями. Неудачный CAS также требует права записи и записывает старое значение: такой разрешённый Arm выбор инвалидирует физические резервирования. Обработчики видят прежнее состояние CPU/RAM; отмена и синхронный сбой не публикуют частичные эффекты. Физическая блокировка упорядочивает кооперативные CPU, без параллельного SMP и атомарных MMIO. `MRS/MSR NZCV` переносит четыре флага согласно правилам зарезервированных битов и нулевого регистра. \ No newline at end of file +Общий слой ISA поддерживает FEAT_LSE `CAS/CASP`, `SWP`, `LDADD/LDCLR/LDEOR/LDSET` и знаковые/беззнаковые min/max для байта, полуслова, слова и двойного слова, включая acquire/release. Checked использует описанное выше выравнивание, Unicorn `Software` — естественное. Сравнения учитывают ширину операнда, возвращаемые старые значения расширяются нулями. Неудачный CAS также требует права записи и записывает старое значение: такой разрешённый Arm выбор инвалидирует физические резервирования. Обработчики видят прежнее состояние CPU/RAM; отмена и синхронный сбой не публикуют частичные эффекты. Физическая блокировка упорядочивает кооперативные CPU, без параллельного SMP и атомарных MMIO. `MRS/MSR NZCV` переносит четыре флага согласно правилам зарезервированных битов и нулевого регистра. Сначала проверяется право чтения: недоступный для чтения операнд даёт ошибку чтения, доступный только для чтения — ошибку записи, как в исходных наблюдениях Windows ARM64. \ No newline at end of file diff --git a/docs/ru/testing.md b/docs/ru/testing.md index f174156905..4f8d2549d7 100644 --- a/docs/ru/testing.md +++ b/docs/ru/testing.md @@ -1483,7 +1483,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` проверяет разрядности, пары, acquire/release, перекрытия регистров, псевдонимы, ошибки, снимки, остановку или сбой наблюдателей и конкуренцию CPU. `RAMReservationTests.cpp` проверяет одинаковые записи, ABA, повторное выделение, откат и влияние строковых инструкций и `ENTER` в KVM/Unicorn. Исходные процессы Windows ARM64 выполняют эксклюзивные циклы. `scripts/check_aarch64_exclusives.py` фиксирует исходные инструкции и исключения выравнивания в Windows ARM64 CI. Эти наблюдения не подтверждают выполнение ARM64-бэкендов KVM/WHP; недоступные профили явно пропускаются. Те же тесты охватывают программный Unicorn, взаимодействие контрактов, одинаковые и ABA-записи, исполняемые псевдонимы в одном запуске, а также `DC ZVA` и отмену наблюдателями. `windows-alignment-oracle.yml` также запускает ARM64-пробу: 1 320 наблюдений охватывают все невыровненные смещения, четыре последовательности чтения/записи и доступную для записи, только для чтения, недоступную и пересекающую границу страниц память. Сохраняются полная разрядность начальных значений регистров и частичные записи до ошибки. `WindowsExclusiveProcessTests.cpp` сравнивает 1 320 исходных наблюдений Windows ARM64 с нативными хешами в `WindowsExclusiveNative.def`. Нормализуется только размещение кода и данных; значения регистров, метаданные исключений и эффекты RAM сохраняются. -`AArch64AtomicTests.cpp` проверяет 168 независимо ассемблированных кодировок LSE, наложения регистров, знаковые сравнения, права, отмену, физические резервирования и NZCV. `scripts/check_aarch64_atomics.py` собирает 1 100 исходных записей Windows ARM64 с полными результатами, контекстом исключений и областью RAM; тесты парсера отвергают неполные и противоречивые доказательства. Нативное исполнение KVM/WHP проверяется отдельно. +`AArch64AtomicTests.cpp` проверяет 168 независимо ассемблированных кодировок LSE, наложения регистров, знаковые сравнения, права, отмену, физические резервирования и NZCV. `scripts/check_aarch64_atomics.py` собирает 1 100 исходных записей Windows ARM64 с полными результатами, контекстом исключений и областью RAM; тесты парсера отвергают неполные и противоречивые доказательства. Нативное исполнение KVM/WHP проверяется отдельно. `WindowsAtomicProcessTests.cpp` проверяет процесс checked; `WindowsAtomicResults.def` хранит хеши полных записей. Структурно константные нативные цели напрямую используют существующее планирование с проверкой достижимости. Единственная символическая цель сохраняет входной предикат только после полного перебора. Регрессии проверяют 128 константных переходов с бюджетом запросов линейного исполнения и 32 вычисляемых перехода с двумя запросами перебора на переход, сохраняя свободные старшие биты адресов и области ветвей. Изменённые полные состояния, отсутствие выравнивания, нулевой лимит целей и недостаточные бюджеты запросов или инструкций отклоняются. Существующие проверки отказа для нескольких целей и неполного перебора остаются обязательными. diff --git a/docs/testing.md b/docs/testing.md index 6ef7dbeb43..0f97b21efb 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -2287,7 +2287,7 @@ native Android equivalence; unavailable hardware cells remain explicit skips. `AArch64ExclusiveTests.cpp` checks scalar/pair widths, acquire/release forms, register overlaps, aliases, alignment and permission faults, snapshots, observer cancellation/failure and competing CPUs. `RAMReservationTests.cpp` checks identical-value writes, ABA, allocation reuse, rollback and KVM/Unicorn string and `ENTER` interference. Original ARM64 Windows process fixtures execute exclusive loops. `scripts/check_aarch64_exclusives.py` runs the original instructions and captures alignment exception records on Windows ARM64 CI. Native instruction observations do not establish ARM64 KVM/WHP backend execution; unavailable profiles remain explicit skips. The same exclusive cases cover software Unicorn, cross-contract interference, identical-value and ABA writes, same-run executable aliases, and `DC ZVA` writes with observer cancellation. `windows-alignment-oracle.yml` also runs the ARM64 probe: 1,320 observations cover every misaligned offset, four load/store sequences, and writable, read-only, inaccessible and split-page memory. The probe retains full-width register seeds and records any committed fault prefix. `WindowsExclusiveProcessTests.cpp` checks 1,320 original Windows ARM64 observations against the native digests in `WindowsExclusiveNative.def`, retaining register values, exception metadata and RAM effects while normalizing only code/data placement. -`AArch64AtomicTests.cpp` covers 168 independently assembled LSE encodings, register aliases, signed comparisons, permissions, cancellation, physical reservations and NZCV transfers. `scripts/check_aarch64_atomics.py` collects 1,100 original Windows ARM64 records with complete operand results, exception context and RAM footprints; parser tests reject missing or inconsistent evidence. Native KVM/WHP execution remains a separate validation requirement. +`AArch64AtomicTests.cpp` covers 168 independently assembled LSE encodings, register aliases, signed comparisons, permissions, cancellation, physical reservations and NZCV transfers. `scripts/check_aarch64_atomics.py` collects 1,100 original Windows ARM64 records with complete operand results, exception context and RAM footprints; parser tests reject missing or inconsistent evidence. Native KVM/WHP execution remains a separate validation requirement. The checked process regression is `WindowsAtomicProcessTests.cpp`, with complete record digests in `WindowsAtomicResults.def`. The shared XSAVE decoder distinguishes standard and compacted initial SSE state. With XSTATE_BV[1] clear, both forms initialize XMM registers; standard format still reads and validates MXCSR, while compacted format initializes MXCSR. `X64XsaveCases.def` supplies independent packet layouts and original host XRSTOR programs. `X64XsaveTests.cpp` checks rejected-state atomicity and compares both formats with actual host execution, preserving the caller’s FP/SSE state. The host oracle skips explicitly when the architecture or required instruction feature is unavailable. diff --git a/docs/zh-CN/cpu-execution.md b/docs/zh-CN/cpu-execution.md index f7abb6db0c..d3de8eae4b 100644 --- a/docs/zh-CN/cpu-execution.md +++ b/docs/zh-CN/cpu-execution.md @@ -207,4 +207,4 @@ checked Unicorn 使用 `MachineRunControl`:ARM64 维护、来宾执行和完 checked ARM64 支持 8/16/32/64 位 `LDXR/STXR`、32/64 位寄存器对 `LDXP/STXP`、相应的 acquire/release 形式及 `CLREX`。KVM、WHP 和 checked Unicorn 共用 ISA 层的独占监视器,以 16 字节物理范围记录保留状态,传输层单步退出不会破坏循环进展。已提交的写入即使没有改变字节,也会使保留状态失效;别名和保留视图写入遵循同一规则。停止保留尚未发布的状态,快照不能撤销期间发生的写入。checked 独占指令采用 FEAT_LSE2 对齐规则:操作数可在同一个 16 字节对齐块内未对齐访问,跨越该块才产生 `alignment` 故障。同宽条件存储按保留的物理粒度匹配;即使监视器已失效,也先检查对齐和权限,再决定条件存储是否成功。该配置仍不包含 MMIO 或并行 SMP。 ARM64 Unicorn 的 `Software` 契约也使用此监视器,包括软件和 checked CPU 共用物理 RAM 的情况。 Unicorn 的 `Software` 配置保留引擎的自然对齐模型。 -同一 ISA 层支持 FEAT_LSE `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET` 及有符号/无符号 min/max,包括字节、半字、字、双字和 acquire/release 形式。checked 配置采用上述对齐策略,Unicorn `Software` 保留自然对齐。比较按操作数宽度执行,返回的旧值零扩展。CAS 比较失败仍要求写权限,并选择 Arm 允许的旧值回写行为,使物理保留状态失效。回调看到提交前的 CPU/RAM 状态;取消和同步故障不会发布部分原子操作结果。物理执行锁保证协作式 CPU 的顺序,不提供并行 SMP 或 MMIO 原子操作。`MRS/MSR NZCV` 按架构规则传输四个条件标志,正确处理保留位和零寄存器。 \ No newline at end of file +同一 ISA 层支持 FEAT_LSE `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET` 及有符号/无符号 min/max,包括字节、半字、字、双字和 acquire/release 形式。checked 配置采用上述对齐策略,Unicorn `Software` 保留自然对齐。比较按操作数宽度执行,返回的旧值零扩展。CAS 比较失败仍要求写权限,并选择 Arm 允许的旧值回写行为,使物理保留状态失效。回调看到提交前的 CPU/RAM 状态;取消和同步故障不会发布部分原子操作结果。物理执行锁保证协作式 CPU 的顺序,不提供并行 SMP 或 MMIO 原子操作。`MRS/MSR NZCV` 按架构规则传输四个条件标志,正确处理保留位和零寄存器。 先检查读权限,再检查写权限:不可读操作数报告读故障,只读操作数报告写故障,与原始 Windows ARM64 观测一致。 \ No newline at end of file diff --git a/docs/zh-CN/testing.md b/docs/zh-CN/testing.md index 24a7adf03b..dd8272d3cc 100644 --- a/docs/zh-CN/testing.md +++ b/docs/zh-CN/testing.md @@ -1323,7 +1323,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` 覆盖标量及成对宽度、acquire/release 形式、寄存器重叠、别名、对齐与权限故障、快照、观察回调取消或失败以及双 CPU 竞争。`RAMReservationTests.cpp` 覆盖相同值写入、ABA、分配复用、回滚,以及 KVM/Unicorn 字符串和 `ENTER` 写入的干扰。原始 ARM64 Windows 进程样例执行独占循环;`scripts/check_aarch64_exclusives.py` 在 Windows ARM64 CI 上执行原始指令并保存对齐异常记录。原生指令证据不代表已验证 ARM64 KVM/WHP 后端执行;不可用配置仍明确记为跳过。 相同的独占指令用例还覆盖软件 Unicorn、跨契约干扰、同值及 ABA 写入、同次执行中的可执行别名,以及 `DC ZVA` 写入和观察回调取消。 `windows-alignment-oracle.yml` 也运行 ARM64 探针:1,320 条观察记录覆盖所有未对齐偏移、四种读写序列,以及可写、只读、不可访问和跨页内存。探针保留完整位宽的寄存器测试值,并记录故障前已提交的部分写入。 `WindowsExclusiveProcessTests.cpp` 将 1,320 条原始 Windows ARM64 观察结果与 `WindowsExclusiveNative.def` 中的原生摘要逐项核对,保留寄存器值、异常元数据和 RAM 效果,仅归一化代码与数据的放置地址。 -`AArch64AtomicTests.cpp` 覆盖 168 种独立汇编的 LSE 编码、寄存器别名、有符号比较、权限、取消、物理保留状态及 NZCV 传输。`scripts/check_aarch64_atomics.py` 收集 1,100 条原始 Windows ARM64 记录,包含完整操作数结果、异常上下文和 RAM 写入范围;解析测试拒绝缺失或不一致的证据。原生 KVM/WHP 执行仍需单独验证。 +`AArch64AtomicTests.cpp` 覆盖 168 种独立汇编的 LSE 编码、寄存器别名、有符号比较、权限、取消、物理保留状态及 NZCV 传输。`scripts/check_aarch64_atomics.py` 收集 1,100 条原始 Windows ARM64 记录,包含完整操作数结果、异常上下文和 RAM 写入范围;解析测试拒绝缺失或不一致的证据。原生 KVM/WHP 执行仍需单独验证。 checked 进程回归由 `WindowsAtomicProcessTests.cpp` 执行,完整记录摘要保存在 `WindowsAtomicResults.def`。 结构上已为常量的原生目标直接使用现有的可达性检查调度。符号单目标只有在穷尽枚举后才复用传入谓词。回归在直线执行的查询预算内验证 128 次常量跳转,并按每次转移两次枚举查询的预算验证 32 次计算目标跳转,保留未约束的地址高位和分支域。完整状态结果被修改、缺少对齐约束、目标数量上限为零或查询与指令预算不足时必须拒绝。多目标和未完成枚举的现有拒绝检查仍然必需。 diff --git a/docs/zh-TW/cpu-execution.md b/docs/zh-TW/cpu-execution.md index a4b239d925..5593ab0d0a 100644 --- a/docs/zh-TW/cpu-execution.md +++ b/docs/zh-TW/cpu-execution.md @@ -207,4 +207,4 @@ checked Unicorn 使用 `MachineRunControl`:ARM64 維護、客體執行與完 checked ARM64 支援 8/16/32/64 位元 `LDXR/STXR`、32/64 位元暫存器對 `LDXP/STXP`、對應的 acquire/release 形式及 `CLREX`。KVM、WHP 與 checked Unicorn 共用 ISA 層的獨佔監視器,以 16 位元組實體範圍記錄保留狀態,傳輸層單步退出不會破壞迴圈進展。已提交的寫入即使未改變位元組,也會使保留狀態失效;別名及保留檢視的寫入遵循相同規則。停止保留尚未發布的狀態,快照無法撤銷期間的寫入。checked 獨佔指令採用 FEAT_LSE2 對齊規則:運算元可在同一個 16 位元組對齊區塊內未對齊存取,跨越該區塊才產生 `alignment` 錯誤。同寬條件儲存依保留的實體粒度比對;即使監視器已失效,也先檢查對齊及權限,再決定條件儲存是否成功。此設定仍不包含 MMIO 或平行 SMP。 ARM64 Unicorn 的 `Software` 契約也使用此監視器,包括軟體與 checked CPU 共用實體 RAM 的情況。 Unicorn 的 `Software` 設定保留引擎的自然對齊模型。 -同一 ISA 層支援 FEAT_LSE `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET` 與有符號/無符號 min/max,包含位元組、半字、字、雙字及 acquire/release 形式。checked 設定採用上述對齊策略,Unicorn `Software` 保留自然對齊。比較依運算元寬度執行,傳回的舊值零擴展。CAS 比較失敗仍要求寫入權限,並選擇 Arm 允許的舊值回寫,使實體保留狀態失效。回呼看到提交前的 CPU/RAM;取消與同步故障不發布部分原子結果。實體執行鎖確保協作式 CPU 的順序,不提供平行 SMP 或 MMIO 原子操作。`MRS/MSR NZCV` 依架構規則傳輸四個條件旗標,正確處理保留位與零暫存器。 \ No newline at end of file +同一 ISA 層支援 FEAT_LSE `CAS/CASP`、`SWP`、`LDADD/LDCLR/LDEOR/LDSET` 與有符號/無符號 min/max,包含位元組、半字、字、雙字及 acquire/release 形式。checked 設定採用上述對齊策略,Unicorn `Software` 保留自然對齊。比較依運算元寬度執行,傳回的舊值零擴展。CAS 比較失敗仍要求寫入權限,並選擇 Arm 允許的舊值回寫,使實體保留狀態失效。回呼看到提交前的 CPU/RAM;取消與同步故障不發布部分原子結果。實體執行鎖確保協作式 CPU 的順序,不提供平行 SMP 或 MMIO 原子操作。`MRS/MSR NZCV` 依架構規則傳輸四個條件旗標,正確處理保留位與零暫存器。 先檢查讀取權限,再檢查寫入權限:不可讀的運算元回報讀取故障,唯讀運算元回報寫入故障,與原始 Windows ARM64 觀測一致。 \ No newline at end of file diff --git a/docs/zh-TW/testing.md b/docs/zh-TW/testing.md index 959507e7aa..e35e59b477 100644 --- a/docs/zh-TW/testing.md +++ b/docs/zh-TW/testing.md @@ -1361,7 +1361,7 @@ build-release/bin/NeverDByteCellScalarizationTests `AArch64ExclusiveTests.cpp` 涵蓋標量與成對寬度、acquire/release 形式、暫存器重疊、別名、對齊及權限錯誤、快照、觀察回呼取消或失敗,以及雙 CPU 競爭。`RAMReservationTests.cpp` 涵蓋相同值寫入、ABA、配置重用、回復,以及 KVM/Unicorn 字串和 `ENTER` 寫入的干擾。原始 ARM64 Windows 行程範例執行獨佔迴圈;`scripts/check_aarch64_exclusives.py` 在 Windows ARM64 CI 執行原始指令並保存對齊例外記錄。原生指令證據不代表已驗證 ARM64 KVM/WHP 後端執行;不可用設定仍明確列為略過。 相同的獨佔指令案例也涵蓋軟體 Unicorn、跨契約干擾、同值及 ABA 寫入、同次執行中的可執行別名,以及 `DC ZVA` 寫入與觀察回呼取消。 `windows-alignment-oracle.yml` 也執行 ARM64 探針:1,320 筆觀察記錄涵蓋所有未對齊偏移、四種讀寫序列,以及可寫、唯讀、不可存取和跨頁記憶體。探針保留完整位寬的暫存器測試值,並記錄錯誤前已提交的部分寫入。 `WindowsExclusiveProcessTests.cpp` 將 1,320 筆原始 Windows ARM64 觀察結果與 `WindowsExclusiveNative.def` 的原生摘要核對,保留暫存器值、例外中繼資料及 RAM 效果,只正規化程式碼與資料的放置位址。 -`AArch64AtomicTests.cpp` 涵蓋 168 種獨立組譯的 LSE 編碼、暫存器別名、有符號比較、權限、取消、實體保留狀態與 NZCV 傳輸。`scripts/check_aarch64_atomics.py` 收集 1,100 筆原始 Windows ARM64 紀錄,包含完整運算結果、例外上下文與 RAM 範圍;解析測試拒絕缺失或不一致的證據。原生 KVM/WHP 執行仍需單獨驗證。 +`AArch64AtomicTests.cpp` 涵蓋 168 種獨立組譯的 LSE 編碼、暫存器別名、有符號比較、權限、取消、實體保留狀態與 NZCV 傳輸。`scripts/check_aarch64_atomics.py` 收集 1,100 筆原始 Windows ARM64 紀錄,包含完整運算結果、例外上下文與 RAM 範圍;解析測試拒絕缺失或不一致的證據。原生 KVM/WHP 執行仍需單獨驗證。 checked 處理程序回歸由 `WindowsAtomicProcessTests.cpp` 執行,完整紀錄摘要保存在 `WindowsAtomicResults.def`。 結構上已為常數的原生目標直接使用既有的可達性檢查排程。符號單目標只有在窮盡列舉後才重用傳入述詞。迴歸在直線執行的查詢預算內驗證 128 次常數跳轉,並按每次轉移兩次列舉查詢的預算驗證 32 次計算目標跳轉,保留未約束的位址高位元和分支域。完整狀態結果遭修改、缺少對齊約束、目標數量上限為零或查詢與指令預算不足時必須拒絕。多目標和未完成列舉的既有拒絕檢查仍然必要。 diff --git a/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp b/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp index 7b4e4ba3c7..8d27f827a5 100644 --- a/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp +++ b/lib/emulation/arch/aarch64/AArch64AtomicExecution.cpp @@ -1,17 +1,14 @@ -//===- AArch64AtomicExecution.cpp - Commit LSE effects under a RAM lease -//---===// +//===- AArch64AtomicExecution.cpp - Shared LSE completion -----------------===// // // NeverD Decompiler // //===----------------------------------------------------------------------===// -#include "../../core/RAMTransaction.h" #include "AArch64Atomic.h" #include "llvm/Support/MathExtras.h" #include #include -#include namespace neverd::emulation { llvm::Error executeAArch64Atomic(const AArch64AtomicInstruction &I, @@ -34,11 +31,14 @@ llvm::Error executeAArch64Atomic(const AArch64AtomicInstruction &I, Access.Hooks.Read(I.Address, Size); if (Access.Stopped()) return llvm::Error::success(); - // Every CAS checks write permission, including a comparison mismatch. - if (auto E = Access.CheckAccess(I.Address, Size, Read | Write)) - return E; - if (Access.Stopped()) - return llvm::Error::success(); + // Native ARM64 distinguishes an unreadable operand from an unwritable + // destination. Every CAS checks both, including a comparison mismatch. + for (unsigned Permission : {Read, Write}) { + if (auto E = Access.CheckAccess(I.Address, Size, Permission)) + return E; + if (Access.Stopped()) + return llvm::Error::success(); + } std::array Bytes{}; if (auto E = Memory.read(I.Address, llvm::MutableArrayRef(Bytes).take_front(Size))) @@ -107,31 +107,12 @@ llvm::Error executeAArch64Atomic(const AArch64AtomicInstruction &I, if (Result != aarch64::GPRCount) Next.Registers[Result] = Old; } - if (Access.Hooks.Write) - for (unsigned Offset = 0; Offset < Size; Offset += aarch64::WordBytes) { - const unsigned Count = - std::min(Size - Offset, aarch64::WordBytes); - uint64_t Value = 0; - for (unsigned B = 0; B < Count; ++B) - Value |= uint64_t(Bytes[Offset + B]) << (B * CHAR_BIT); - Access.Hooks.Write(I.Address + Offset, Count, Value); - if (Access.Stopped()) - return llvm::Error::success(); - } - const RAMWriteRange Range{I.Address, Size}; - auto Transaction = - RAMTransaction::create(Memory, Range, Size, Access.WritePermissions); - if (!Transaction) - return Transaction.takeError(); - std::memcpy(Memory.physicalPointer(Page->second.Physical + - I.Address % memory::PageSize), - Bytes.data(), Size); - if (auto E = (*Transaction)->stage()) - return E; - if (Access.Stopped()) + auto Committed = commitAArch64AtomicWrite( + I.Address, llvm::ArrayRef(Bytes).take_front(Size), Memory, Access); + if (!Committed) + return Committed.takeError(); + if (!*Committed) return llvm::Error::success(); - if (auto E = (*Transaction)->commit()) - return E; CPU = Next; return llvm::Error::success(); } diff --git a/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp b/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp index 384333de57..6e720f3d0a 100644 --- a/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp +++ b/lib/emulation/arch/aarch64/AArch64AtomicMemory.cpp @@ -5,8 +5,14 @@ //===----------------------------------------------------------------------===// #include "AArch64AtomicMemory.h" +#include "../../core/RAMTransaction.h" + #include "llvm/Support/MathExtras.h" +#include +#include +#include + namespace neverd::emulation { bool isAArch64AtomicAligned(uint64_t Address, uint64_t Size, AArch64AtomicAlignment Alignment) { @@ -26,4 +32,38 @@ bool isAArch64AtomicAlignmentFault(const BackendFault &Fault) { Fault.Access == BackendAccessKind::Write) && !Fault.Interrupt && !Fault.ErrorCode; } +llvm::Expected +commitAArch64AtomicWrite(uint64_t Address, llvm::ArrayRef Bytes, + MemoryProjection &Memory, + const AArch64AtomicAccess &Access) { + assert(isAArch64AtomicAligned(Address, Bytes.size(), Access.Alignment)); + if (Access.Hooks.Write) + for (unsigned Offset = 0; Offset < Bytes.size(); + Offset += aarch64::WordBytes) { + const unsigned Count = + std::min(Bytes.size() - Offset, aarch64::WordBytes); + uint64_t Value = 0; + for (unsigned B = 0; B < Count; ++B) + Value |= uint64_t(Bytes[Offset + B]) << (B * CHAR_BIT); + Access.Hooks.Write(Address + Offset, Count, Value); + if (Access.Stopped()) + return false; + } + const RAMWriteRange Range{Address, Bytes.size()}; + auto Transaction = RAMTransaction::create(Memory, Range, Bytes.size(), + Access.WritePermissions); + if (!Transaction) + return Transaction.takeError(); + const auto &Page = Memory.mappings().at(Address & ~(memory::PageSize - 1)); + std::memcpy( + Memory.physicalPointer(Page.Physical + Address % memory::PageSize), + Bytes.data(), Bytes.size()); + if (auto E = (*Transaction)->stage()) + return std::move(E); + if (Access.Stopped()) + return false; + if (auto E = (*Transaction)->commit()) + return std::move(E); + return true; +} } // namespace neverd::emulation diff --git a/lib/emulation/arch/aarch64/AArch64AtomicMemory.h b/lib/emulation/arch/aarch64/AArch64AtomicMemory.h index 08b17707db..25bf44b80f 100644 --- a/lib/emulation/arch/aarch64/AArch64AtomicMemory.h +++ b/lib/emulation/arch/aarch64/AArch64AtomicMemory.h @@ -25,5 +25,12 @@ struct AArch64AtomicAccess { }; /// Validate the complete typed alignment outcome before guest OS translation. bool isAArch64AtomicAlignmentFault(const BackendFault &); +/// Observe and commit one validated, aligned RAM operand. False means that a +/// stop discarded the unpublished write; the caller must retain CPU/monitor +/// state. Both LSE and exclusive stores use this physical commit authority. +llvm::Expected commitAArch64AtomicWrite(uint64_t Address, + llvm::ArrayRef Bytes, + MemoryProjection &, + const AArch64AtomicAccess &); } // namespace neverd::emulation #endif diff --git a/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp b/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp index 5735ad8729..b86f148003 100644 --- a/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp +++ b/lib/emulation/arch/aarch64/AArch64ExclusiveExecution.cpp @@ -3,12 +3,9 @@ // NeverD Decompiler // //===----------------------------------------------------------------------===// -#include "../../core/RAMTransaction.h" #include "AArch64Exclusive.h" -#include #include -#include namespace neverd::emulation { llvm::Error executeAArch64Exclusive(const AArch64ExclusiveInstruction &I, @@ -88,33 +85,12 @@ llvm::Error executeAArch64Exclusive(const AArch64ExclusiveInstruction &I, for (unsigned B = 0; B < I.Width; ++B) Bytes[N * I.Width + B] = uint8_t(Value >> (B * CHAR_BIT)); } - if (Hooks.Write) - for (unsigned Offset = 0; Offset < Size; Offset += aarch64::WordBytes) { - const unsigned Count = - std::min(Size - Offset, aarch64::WordBytes); - uint64_t Value = 0; - for (unsigned B = 0; B < Count; ++B) - Value |= uint64_t(Bytes[Offset + B]) << (B * CHAR_BIT); - Hooks.Write(I.Address + Offset, Count, Value); - if (Access.Stopped()) - return llvm::Error::success(); - } - const RAMWriteRange Range{I.Address, Size}; - auto Transaction = - RAMTransaction::create(Memory, Range, Size, Access.WritePermissions); - if (!Transaction) - return Transaction.takeError(); - const auto &Page = - Memory.mappings().at(I.Address & ~(memory::PageSize - 1)); - std::memcpy( - Memory.physicalPointer(Page.Physical + I.Address % memory::PageSize), - Bytes.data(), Size); - if (auto E = (*Transaction)->stage()) - return E; - if (Access.Stopped()) + auto Committed = commitAArch64AtomicWrite( + I.Address, llvm::ArrayRef(Bytes).take_front(Size), Memory, Access); + if (!Committed) + return Committed.takeError(); + if (!*Committed) return llvm::Error::success(); - if (auto E = (*Transaction)->commit()) - return E; } if (!*Matches && Access.Stopped()) return llvm::Error::success(); diff --git a/scripts/EmulationDocumentation.def b/scripts/EmulationDocumentation.def index 65241b585b..fb0b674a10 100644 --- a/scripts/EmulationDocumentation.def +++ b/scripts/EmulationDocumentation.def @@ -1180,7 +1180,7 @@ NEVERD_EMULATION_DOC_PATH(AArch64ExclusiveArchitecture, "docs/{locale}/architect NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveArchitecture, "AArch64ExclusiveInstructions.def") NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveArchitecture, "AArch64ExclusiveExecution") NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveArchitecture, "RAMReservation") -NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveArchitecture, "AArch64ExclusiveAccess") +NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveArchitecture, "AArch64AtomicAccess") NEVERD_EMULATION_DOC_PATH(AArch64ExclusiveTests, "docs/testing.md") NEVERD_EMULATION_DOC_PATH(AArch64ExclusiveTests, "docs/{locale}/testing.md") @@ -1189,3 +1189,20 @@ NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveTests, "RAMReservationTests.cpp") NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveTests, "scripts/check_aarch64_exclusives.py") NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveTests, "WindowsExclusiveProcessTests.cpp") NEVERD_EMULATION_DOC_TOKEN(AArch64ExclusiveTests, "WindowsExclusiveNative.def") + +NEVERD_EMULATION_DOC_PATH(AArch64AtomicCPU, "docs/cpu-execution.md") +NEVERD_EMULATION_DOC_PATH(AArch64AtomicCPU, "docs/{locale}/cpu-execution.md") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicCPU, "CAS/CASP") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicCPU, "LDADD/LDCLR/LDEOR/LDSET") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicCPU, "MRS/MSR NZCV") +NEVERD_EMULATION_DOC_PATH(AArch64AtomicArchitecture, "docs/architecture.md") +NEVERD_EMULATION_DOC_PATH(AArch64AtomicArchitecture, "docs/{locale}/architecture.md") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicArchitecture, "AArch64AtomicInstructions.def") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicArchitecture, "AArch64AtomicExecution") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicArchitecture, "AArch64AtomicMemory") +NEVERD_EMULATION_DOC_PATH(AArch64AtomicTests, "docs/testing.md") +NEVERD_EMULATION_DOC_PATH(AArch64AtomicTests, "docs/{locale}/testing.md") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicTests, "AArch64AtomicTests.cpp") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicTests, "scripts/check_aarch64_atomics.py") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicTests, "WindowsAtomicProcessTests.cpp") +NEVERD_EMULATION_DOC_TOKEN(AArch64AtomicTests, "WindowsAtomicResults.def") diff --git a/scripts/check_aarch64_atomics.py b/scripts/check_aarch64_atomics.py index f6bb602c2a..cf3debb4f2 100644 --- a/scripts/check_aarch64_atomics.py +++ b/scripts/check_aarch64_atomics.py @@ -113,7 +113,8 @@ def validate_observations(data: bytes, specification=None) -> list[dict]: or settings[1] == 0 and settings[2] == values["PageReadWrite"]): raise ValueError("inconsistent original ARM64 atomic fault context") if row["Code"] == values["AccessViolation"]: - if (row["ParameterCount"] != 2 or row["Parameter0"] != 1 + write = settings[2] != values["PageNoAccess"] + if (row["ParameterCount"] != 2 or row["Parameter0"] != write or not before[4] <= row["Parameter1"] < before[4] + case[2] * case[3]): raise ValueError("inconsistent original ARM64 atomic access violation") elif row["ParameterCount"]: @@ -133,6 +134,27 @@ def validate_observations(data: bytes, specification=None) -> list[dict]: return observed +def group_digests(observations: list[dict]) -> list[tuple[str, int, str]]: + """Remove only placement, retaining ordering, operands, faults and RAM.""" + values, fields, cases, _ = contract() + kinds = {name: kind for name, kind, *_ in cases} + groups = {} + for observation in observations: + kind = kinds[observation["case"]] + row = dict(observation["record"]) + row["Return4"] -= row["MemoryBase"] + if row["Faults"]: + row["Context4"] -= row["MemoryBase"] + if row["Code"] == values["AccessViolation"]: + row["Parameter1"] -= row["MemoryBase"] + for field in ("MemoryBase", "AtomicPC", "ExceptionPC", "ContextPC"): + row[field] = 0 + count, digest = groups.setdefault(kind, [0, hashlib.sha256()]) + digest.update(struct.pack("<" + "Q" * len(fields), *(row[field] for field in fields))) + groups[kind][0] = count + 1 + return [(kind, count, digest.hexdigest()) for kind, (count, digest) in groups.items()] + + def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--output", type=Path, required=True) @@ -199,6 +221,7 @@ def run(command, name, timeout=60): if result.stderr: raise ValueError("unexpected native atomic diagnostic output") report["observations"] = validate_observations(result.stdout) + report["group_digests"] = group_digests(report["observations"]) report["native_observed"] = True report["status"] = "passed" finally: diff --git a/scripts/tests/test_check_aarch64_atomics.py b/scripts/tests/test_check_aarch64_atomics.py index 41845c920f..de73b5104e 100644 --- a/scripts/tests/test_check_aarch64_atomics.py +++ b/scripts/tests/test_check_aarch64_atomics.py @@ -3,13 +3,13 @@ import struct import unittest -from scripts.check_aarch64_atomics import contract, required_records, validate_observations +from scripts.check_aarch64_atomics import contract, group_digests, required_records, validate_observations class AtomicEvidenceTests(unittest.TestCase): def setUp(self): self.values, self.fields, _, scenarios = contract() - self.specification = (self.values, self.fields, [("SwapByte", "Swap", 1, 1)], + self.specification = (self.values, self.fields, [("Swap8Relaxed", "Swap", 1, 1)], [scenarios[0], scenarios[3]]) v = self.values before = [v["Operand"], v["InitialStatus"], v["Operand"], v["Initial"], 0x8000] @@ -37,6 +37,16 @@ def encode(self, rows): def test_complete_results_and_write_fault_are_accepted(self): self.assertEqual(len(validate_observations(self.encode(self.rows), self.specification)), 2) + def test_unreadable_operand_reports_read_before_write(self): + values, fields, cases, scenarios = self.specification + scenarios = [scenarios[0], ("NoAccess", 0, values["PageNoAccess"], 0, 0)] + specification = values, fields, cases, scenarios + rows = copy.deepcopy(self.rows) + with self.assertRaises(ValueError): + validate_observations(self.encode(rows), specification) + rows[1]["Parameter0"] = 0 + self.assertEqual(len(validate_observations(self.encode(rows), specification)), 2) + def test_missing_extra_reordered_and_duplicate_records_are_rejected(self): for rows in [[], self.rows[:1], self.rows + self.rows[:1], self.rows[::-1], [self.rows[0], self.rows[0]]]: @@ -77,6 +87,23 @@ def test_complete_case_inventory_has_unique_ordered_scenarios(self): self.assertEqual(len(required), len({(index, scenario) for index, scenario, *_ in required})) self.assertEqual({index for index, *_ in required}, set(range(len(cases)))) + def test_digest_removes_only_placement_and_retains_real_state(self): + expected = group_digests(validate_observations(self.encode(self.rows), self.specification)) + moved = copy.deepcopy(self.rows) + for row in moved: + for field in ["MemoryBase", "Return4"]: + row[field] += 0x100000 + row["AtomicPC"] += 0x200000 + if row["Faults"]: + for field in ["Context4", "Parameter1"]: + row[field] += 0x100000 + for field in ["ContextPC", "ExceptionPC"]: + row[field] += 0x200000 + observations = validate_observations(self.encode(moved), self.specification) + self.assertEqual(group_digests(observations), expected) + observations[0]["record"]["Return2"] ^= 1 + self.assertNotEqual(group_digests(observations), expected) + if __name__ == "__main__": unittest.main() diff --git a/third_party/unicorn b/third_party/unicorn index a05f7b70a3..b2efe647e2 160000 --- a/third_party/unicorn +++ b/third_party/unicorn @@ -1 +1 @@ -Subproject commit a05f7b70a3dff0e5bc246f52e6ff65f6771d4653 +Subproject commit b2efe647e25a71eea56b7a3e8d19af39033437d4 diff --git a/unittests/emulation/AArch64AtomicCode.def b/unittests/emulation/AArch64AtomicCode.def new file mode 100644 index 0000000000..1dc5b7c6ab --- /dev/null +++ b/unittests/emulation/AArch64AtomicCode.def @@ -0,0 +1,38 @@ +//===- AArch64AtomicCode.def - Original self-modifying atomic programs ----===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#ifdef NEVERD_ATOMIC_CODE_WORD +NEVERD_ATOMIC_CODE_WORD(0x10000110) // adr x16, target +NEVERD_ATOMIC_CODE_WORD(0xd63f0200) // blr x16 +NEVERD_ATOMIC_CODE_WORD(AtomicWord) +NEVERD_ATOMIC_CODE_WORD(0xd63f0200) // blr x16 +NEVERD_ATOMIC_CODE_WORD(0xd503201f) // stop +NEVERD_ATOMIC_CODE_WORD(0xd503201f) +NEVERD_ATOMIC_CODE_WORD(0xd503201f) +NEVERD_ATOMIC_CODE_WORD(0xd503201f) +NEVERD_ATOMIC_CODE_WORD(0x52800025) // target: mov w5, #1 +NEVERD_ATOMIC_CODE_WORD(0xd65f03c0) // ret +NEVERD_ATOMIC_CODE_WORD(0xd503201f) +NEVERD_ATOMIC_CODE_WORD(0xd503201f) +#endif +// Source value, expected function result. All widths edit the immediate field +// in MOV while preserving the destination register and the following RET. +#ifdef NEVERD_ATOMIC_CODE_SOURCE +NEVERD_ATOMIC_CODE_SOURCE(Add, 0x20, 2) +NEVERD_ATOMIC_CODE_SOURCE(Clear, 0x20, 0) +NEVERD_ATOMIC_CODE_SOURCE(Xor, 0x60, 2) +NEVERD_ATOMIC_CODE_SOURCE(Set, 0x40, 3) +NEVERD_ATOMIC_CODE_SOURCE(SignedMax, 0xd65f03c052800045ULL, 2) +NEVERD_ATOMIC_CODE_SOURCE(SignedMin, 0xd65f03c052800005ULL, 0) +NEVERD_ATOMIC_CODE_SOURCE(UnsignedMax, 0xd65f03c052800045ULL, 2) +NEVERD_ATOMIC_CODE_SOURCE(UnsignedMin, 0xd65f03c052800005ULL, 0) +NEVERD_ATOMIC_CODE_SOURCE(Swap, 0xd65f03c052800045ULL, 2) +NEVERD_ATOMIC_CODE_SOURCE(Compare, 0xd65f03c052800045ULL, 2) +NEVERD_ATOMIC_CODE_SOURCE(ComparePair, 0xd65f03c052800045ULL, 2) +#endif +#ifdef NEVERD_ATOMIC_CODE_VALUE +NEVERD_ATOMIC_CODE_VALUE(CodeTargetOffset, 32) +NEVERD_ATOMIC_CODE_VALUE(CodeEndOffset, 16) +#endif diff --git a/unittests/emulation/AArch64AtomicTests.cpp b/unittests/emulation/AArch64AtomicTests.cpp index 8f490bf0ff..7d0199a1dc 100644 --- a/unittests/emulation/AArch64AtomicTests.cpp +++ b/unittests/emulation/AArch64AtomicTests.cpp @@ -19,9 +19,12 @@ namespace neverd::emulation { namespace { #define NEVERD_ATOMIC_VALUE(Name, Value) constexpr uint64_t Name = Value; #define NEVERD_ATOMIC_TEXT(Name, Value) constexpr char Name[] = Value; +#define NEVERD_ATOMIC_CODE_VALUE NEVERD_ATOMIC_VALUE #include "AArch64AtomicCases.def" +#include "AArch64AtomicCode.def" #undef NEVERD_ATOMIC_VALUE #undef NEVERD_ATOMIC_TEXT +#undef NEVERD_ATOMIC_CODE_VALUE enum class Family { #define NEVERD_ATOMIC_FAMILY(Name) Name, #include "AArch64AtomicCases.def" @@ -350,7 +353,9 @@ TEST_P(AArch64Atomic, AlignmentAndWritePermissionsPrecedeAnyCommit) { : BackendFaultKind::Protection); EXPECT_EQ(F.Address, Address); EXPECT_EQ(F.Size, C.size()); - EXPECT_EQ(F.Access, BackendAccessKind::Write); + EXPECT_EQ(F.Access, !Unaligned && !Permissions + ? BackendAccessKind::Read + : BackendAccessKind::Write); EXPECT_EQ(F.PC, Code); return true; }; @@ -508,8 +513,8 @@ TEST_P(AArch64Atomic, OddPairRegisterEncodingIsRejectedBeforeAnyAccess) { TEST_P(AArch64Atomic, FlagTransfersPreserveReservedBitsAndZeroRegisterSemantics) { - for (unsigned Flags = 0; Flags <= Granule - 1; ++Flags) { - const uint64_t Value = Flags * (FlagsMask / (Granule - 1)); + for (uint64_t Value = 0; Value <= FlagsMask; + Value += FlagsMask & ~(FlagsMask - 1)) { // All reserved input bits must be ignored by MSR; MRS zeroes them. llvm::cantFail(CPU->setReg(AArch64Register::X0, Value | ~FlagsMask)); instruction(WriteNZCV); @@ -528,6 +533,57 @@ TEST_P(AArch64Atomic, EXPECT_EQ(registers(), Registers); } } + +TEST_P(AArch64Atomic, CommittedWritesRetireExecutableAliasesWithinTheSameRun) { + const struct { + Family Kind; + uint64_t Source, Result; + } Patches[] = { +#define NEVERD_ATOMIC_CODE_SOURCE(Kind, Source, Result) \ + {Family::Kind, Source, Result}, +#include "AArch64AtomicCode.def" +#undef NEVERD_ATOMIC_CODE_SOURCE + }; + llvm::cantFail(CPU->unmapAlias(Alias, PageBytes)); + llvm::cantFail( + CPU->mapAlias(Alias, Code, PageBytes, Read | Write | UserAccessible)); + for (const auto &Patch : Patches) + for (const auto &C : Cases) { + if (!selected(C) || C.Kind != Patch.Kind) + continue; + SCOPED_TRACE(C.Name); + input(C); + const auto AtomicWord = C.Word; + const uint32_t Words[] = { +#define NEVERD_ATOMIC_CODE_WORD(Word) Word, +#include "AArch64AtomicCode.def" +#undef NEVERD_ATOMIC_CODE_WORD + }; + for (unsigned N = 0; N < std::size(Words); ++N) + llvm::cantFail(CPU->writeInteger(Code + N * InstructionBytes, Words[N], + InstructionBytes)); + const uint64_t Target = Alias + CodeTargetOffset; + const auto Low = llvm::cantFail(CPU->readInteger(Target, C.Width)); + const auto High = + llvm::cantFail(CPU->readInteger(Target + C.Width, C.Width)); + llvm::cantFail( + CPU->setReg(AArch64Register::X0, C.compare() ? Low : Patch.Source)); + llvm::cantFail(CPU->setReg(AArch64Register::X1, High)); + llvm::cantFail(CPU->setReg(AArch64Register::X2, Patch.Source)); + llvm::cantFail(CPU->setReg(AArch64Register::X3, High)); + llvm::cantFail(CPU->setReg(AArch64Register::X4, Target)); + BackendHooks Hooks; + Hooks.Instruction = [&](uint64_t PC, uint32_t) { + if (PC == Code + CodeEndOffset) + CPU->stop(); + }; + llvm::cantFail(CPU->installHooks(Hooks)); + auto Exit = llvm::cantFail(CPU->runUntilExit(Code, Timeout)); + ASSERT_EQ(Exit.Kind, ExecutionExitKind::Stopped) << Exit.Diagnostic; + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::X5)), Patch.Result); + EXPECT_EQ(llvm::cantFail(CPU->reg(AArch64Register::NZCV)), InitialFlags); + } +} INSTANTIATE_TEST_SUITE_P(Backends, AArch64Atomic, testing::Combine(testing::ValuesIn(Profiles), testing::ValuesIn(Groups)), diff --git a/unittests/emulation/CMakeLists.txt b/unittests/emulation/CMakeLists.txt index faca7217f7..d0e13f7c08 100644 --- a/unittests/emulation/CMakeLists.txt +++ b/unittests/emulation/CMakeLists.txt @@ -229,6 +229,7 @@ add_neverd_unittest(NeverDWindowsProcessTests WindowsExceptionTests.cpp WindowsAlignmentProcessTests.cpp WindowsExclusiveProcessTests.cpp + WindowsAtomicProcessTests.cpp WindowsSIMDMappingTests.cpp WindowsSIMDExecutionTests.cpp WindowsContinuationTests.cpp diff --git a/unittests/emulation/WindowsAtomicProcessTests.cpp b/unittests/emulation/WindowsAtomicProcessTests.cpp new file mode 100644 index 0000000000..78e92e25ec --- /dev/null +++ b/unittests/emulation/WindowsAtomicProcessTests.cpp @@ -0,0 +1,133 @@ +//===- WindowsAtomicProcessTests.cpp - Complete LSE guest process results -===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +#include "neverd/emulation/CPU.h" +#include "neverd/emulation/ProcessSession.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/SHA256.h" + +#include + +namespace neverd::emulation { +namespace { +#define NEVERD_ATOMIC_VALUE(Name, Value) constexpr uint64_t Name = Value; +#define NEVERD_ATOMIC_ORACLE_VALUE NEVERD_ATOMIC_VALUE +#define NEVERD_ATOMIC_RESULT_VALUE NEVERD_ATOMIC_VALUE +#define NEVERD_ATOMIC_ORACLE_TEXT(Name, Value) constexpr char Name[] = Value; +#define NEVERD_ATOMIC_RESULT_TEXT NEVERD_ATOMIC_ORACLE_TEXT +#include "AArch64AtomicCases.def" +#include "fixtures/AArch64AtomicOracle.def" +#include "fixtures/WindowsAtomicResults.def" +#undef NEVERD_ATOMIC_VALUE +#undef NEVERD_ATOMIC_ORACLE_VALUE +#undef NEVERD_ATOMIC_RESULT_VALUE +#undef NEVERD_ATOMIC_ORACLE_TEXT +#undef NEVERD_ATOMIC_RESULT_TEXT +enum Field { +#define NEVERD_ATOMIC_ORACLE_FIELD(Name) Field##Name, +#include "fixtures/AArch64AtomicOracle.def" +#undef NEVERD_ATOMIC_ORACLE_FIELD + FieldCount +}; +constexpr struct { + const char *Name; + unsigned Records; + const char *Digest; +} Groups[] = { +#define NEVERD_ATOMIC_RESULT_GROUP(Name, Records, Digest) \ + {#Name, Records, Digest}, +#include "fixtures/WindowsAtomicResults.def" +#undef NEVERD_ATOMIC_RESULT_GROUP +}; +struct Profile { + ExecutionBackendKind Backend; + const char *Name; +}; +constexpr Profile Profiles[] = { +#define NEVERD_ATOMIC_RESULT_BACKEND(Name) {ExecutionBackendKind::Name, #Name}, +#include "fixtures/WindowsAtomicResults.def" +#undef NEVERD_ATOMIC_RESULT_BACKEND +}; +class WindowsAtomic : public testing::TestWithParam {}; + +TEST_P(WindowsAtomic, + PreservesAllOriginalResultsFaultContextsAndRAMFootprints) { +#ifndef NEVERD_WINDOWS_PROCESS_FIXTURE_DIR + GTEST_SKIP() << MissingTools; +#else + ExecutionConfiguration Config; + Config.Architecture = GuestArchitecture::AArch64; + Config.Backend = GetParam().Backend; + Config.Contract = ExecutionContract::CheckedUserAArch64; + auto Probe = probeExecutionBackend(Config); + ASSERT_TRUE(bool(Probe)) << llvm::toString(Probe.takeError()); + if (Probe->Availability != BackendAvailability::Available) + GTEST_SKIP() << Probe->Reason; + ProcessOptions Options; + Options.Backend = GetParam().Backend; + Options.Limits = {InstructionLimit, EventLimit, TimeoutMicroseconds}; + Options.OutputLimit = OutputLimit; + const auto Path = + std::filesystem::path(NEVERD_WINDOWS_PROCESS_FIXTURE_DIR) / Program; + auto Result = emulateProcess(Path, ProcessProfile::WindowsPE64, Options); + ASSERT_TRUE(bool(Result)) << llvm::toString(Result.takeError()); + ASSERT_EQ(Result->Stop, ProcessStopReason::Exited) << Result->Diagnostic; + ASSERT_EQ(Result->ExitStatus, 0u) << llvm::toHex(Result->StandardError); + ASSERT_TRUE(Result->StandardError.empty()) + << llvm::toHex(Result->StandardError); + uint64_t ExpectedSize = 0; + for (const auto &G : Groups) + ExpectedSize += G.Records * FieldCount * sizeof(uint64_t); + llvm::StringRef Bytes(Result->StandardOutput); + ASSERT_EQ(Bytes.size(), ExpectedSize); + for (const auto &G : Groups) { + SCOPED_TRACE(G.Name); + llvm::SHA256 Digest; + for (unsigned N = 0; N < G.Records; ++N) { + SCOPED_TRACE(N); + std::array Row; + for (auto &Value : Row) { + Value = llvm::support::endian::read64le(Bytes.data()); + Bytes = Bytes.drop_front(sizeof(uint64_t)); + } + ASSERT_NE(Row[FieldAtomicPC], 0u); + ASSERT_EQ(Row[FieldAtomicPC] % InstructionBytes, 0u); + ASSERT_NE(Row[FieldMemoryBase], 0u); + ASSERT_EQ(Row[FieldMemoryBase] % Granule, 0u); + ASSERT_GE(Row[FieldReturn4], Row[FieldMemoryBase]); + Row[FieldReturn4] -= Row[FieldMemoryBase]; + for (auto F : {FieldExceptionPC, FieldContextPC}) + EXPECT_EQ(Row[F], Row[FieldFaults] ? Row[FieldAtomicPC] : 0); + if (Row[FieldFaults]) { + ASSERT_GE(Row[FieldContext4], Row[FieldMemoryBase]); + Row[FieldContext4] -= Row[FieldMemoryBase]; + } + if (Row[FieldCode] == AccessViolation) { + ASSERT_EQ(Row[FieldParameterCount], 2u); + ASSERT_GE(Row[FieldParameter1], Row[FieldMemoryBase]); + Row[FieldParameter1] -= Row[FieldMemoryBase]; + } + for (auto F : + {FieldAtomicPC, FieldExceptionPC, FieldContextPC, FieldMemoryBase}) + Row[F] = 0; + std::array Normalized; + for (unsigned I = 0; I < FieldCount; ++I) + llvm::support::endian::write64le( + Normalized.data() + I * sizeof(uint64_t), Row[I]); + Digest.update(Normalized); + } + EXPECT_EQ(llvm::toHex(Digest.final(), true), G.Digest); + } + EXPECT_TRUE(Bytes.empty()); +#endif +} +INSTANTIATE_TEST_SUITE_P(Backends, WindowsAtomic, testing::ValuesIn(Profiles), + [](const auto &P) { return P.param.Name; }); +} // namespace +} // namespace neverd::emulation diff --git a/unittests/emulation/fixtures/WindowsAtomicResults.def b/unittests/emulation/fixtures/WindowsAtomicResults.def new file mode 100644 index 0000000000..266eb26bb0 --- /dev/null +++ b/unittests/emulation/fixtures/WindowsAtomicResults.def @@ -0,0 +1,63 @@ +//===- WindowsAtomicResults.def - Original LSE program result contract ----===// +// +// NeverD Decompiler +// +//===----------------------------------------------------------------------===// +// Original clean-source native Windows ARM64 observations: +// https://github.com/NeverSight/NeverD/actions/runs/37362062927 +// Source: 38f69732af94def5e2304fb30bee3d46c6584a8a +// Program SHA-256: +// 56a8a9d7acea40c125c82cf37d2e4a519e8b02d5364ebca3e93765a312a9cbdc +// All 1,100 native records pass the corrected read/write fault validator. +// Hashes retain operand results, NZCV, exception metadata and every RAM byte; +// only verified code/data placement is normalized. +#ifdef NEVERD_ATOMIC_RESULT_GROUP +NEVERD_ATOMIC_RESULT_GROUP( + Add, 100, + "4e3a7be9d1411f7101e6662c27282e80d5977c9b6c9e7eb298a14b7284380e00") +NEVERD_ATOMIC_RESULT_GROUP( + Clear, 100, + "c4b9f3e6cb6aa65325d0d5506729711ceefd6aefbd28fd62f75aa1162c23b364") +NEVERD_ATOMIC_RESULT_GROUP( + Xor, 100, + "6e30afd8c8994d02ec85f1bf7f9cf6e104d6554aad5e9ace1cf03e2692800f95") +NEVERD_ATOMIC_RESULT_GROUP( + Set, 100, + "8ad77a20fad64b8039167bd42ffa4b1a731dad907ee773b7af26979fc297dbff") +NEVERD_ATOMIC_RESULT_GROUP( + SignedMax, 100, + "ec45e0306beb1e9cfacf302cefc9e3870132b692f6c19936eba0db0a021b6533") +NEVERD_ATOMIC_RESULT_GROUP( + SignedMin, 100, + "d2e7d0395eb43352e6f93e5cbdf5aa5ecce97911774e1fd0d239d5003d811dc7") +NEVERD_ATOMIC_RESULT_GROUP( + UnsignedMax, 100, + "8c5af1bc51c50c84f80f64b8bbee2596f003dbe7f3c40b405d3755f0ee30b0c1") +NEVERD_ATOMIC_RESULT_GROUP( + UnsignedMin, 100, + "8afba7310779aa4cbf34b90df83d3ac2d330d01e4393e5daecba851c8a7fac16") +NEVERD_ATOMIC_RESULT_GROUP( + Swap, 100, + "f72d90e8019f3e7edd784b0ec47aed52159b8a6fbb7df3ccb6870204291c0e8f") +NEVERD_ATOMIC_RESULT_GROUP( + Compare, 132, + "c22d35f97571ea355ac6fb486172e42055fab34da4f213be2a1a4e2382b4d7ed") +NEVERD_ATOMIC_RESULT_GROUP( + ComparePair, 68, + "a0d9a5e9cd42b61ea20a9d57378fae72c252360b92b35b2ba904d375224d787f") +#endif +#ifdef NEVERD_ATOMIC_RESULT_VALUE +NEVERD_ATOMIC_RESULT_VALUE(InstructionLimit, 10000000) +NEVERD_ATOMIC_RESULT_VALUE(EventLimit, 100000) +NEVERD_ATOMIC_RESULT_VALUE(TimeoutMicroseconds, 120000000) +NEVERD_ATOMIC_RESULT_VALUE(OutputLimit, 1048576) +#endif +#ifdef NEVERD_ATOMIC_RESULT_TEXT +NEVERD_ATOMIC_RESULT_TEXT( + MissingTools, "Clang and lld-link are required for original PE fixtures") +#endif +#ifdef NEVERD_ATOMIC_RESULT_BACKEND +NEVERD_ATOMIC_RESULT_BACKEND(KVM) +NEVERD_ATOMIC_RESULT_BACKEND(WHP) +NEVERD_ATOMIC_RESULT_BACKEND(Unicorn) +#endif