|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Armv8.1-M long-shift (LSLL/LSRL/ASRL) emulation seam (see emu_seams.h). More...
#include <stdio.h>#include <string.h>#include "emu_elf.h"#include "emu_elf_source_internal.h"#include "emu_engine.h"#include "emu_exc.h"#include "emu_host_io_internal.h"#include "emu_seams.h"Go to the source code of this file.
Data Structures | |
| struct | long_shift_insn_t |
| One decoded immediate-or-register Armv8.1-M long shift. More... | |
| struct | long_shift_pending_t |
| The correct result of one long shift, awaiting write-back. More... | |
| struct | long_shift_scan_t |
| Engine and bounded installed-hook count for a streamed scan. More... | |
Enumerations | |
| enum | long_shift_t : uint32_t { k_lsh_hw1_mask = 0xFFF0U , k_lsh_hw1_match = 0xEA50U , k_lsh_hw1_sat_bit = 0x0001U , k_lsh_rdalo_mask = 0x000EU , k_lsh_hw2_lo_mask = 0x000FU , k_lsh_tail_imm = 0x000FU , k_lsh_tail_reg = 0x000DU , k_lsh_op_shift = 4U , k_lsh_op_mask = 0x3U , k_lsh_op_lsll = 0U , k_lsh_op_lsrl = 1U , k_lsh_op_asrl = 2U , k_lsh_rdahi_shift = 8U , k_lsh_rdahi_wide = 0x0FU , k_lsh_rm_shift = 12U , k_lsh_reg_zero_msk = 0x00C0U , k_lsh_imm3_shift = 12U , k_lsh_imm3_mask = 0x7U , k_lsh_imm2_shift = 6U , k_lsh_imm2_mask = 0x3U , k_lsh_imm3_to_imm5 = 2U , k_lsh_word_bits = 32U , k_lsh_pair_bits = 64U , k_lsh_amount_mask = 0xFFU , k_lsh_amount_wrap = 0x100U , k_lsh_amount_sign = 0x80U , k_lsh_insn_len = 4U } |
| enum | : uint32_t { k_lsh_sites_max = 4096U , k_lsh_hooks_max = 8192U } |
| Max long-shift sites tracked per image (generous; real counts tiny). More... | |
| enum | : uint32_t { k_lsh_pending_max = 8U } |
| Depth of the outstanding-write-back table. More... | |
Functions | |
| static RA8_INTERNAL bool | internal_long_shift_decode (uint16_t hw1, uint16_t hw2, long_shift_insn_t *out) |
| Decode an Armv8.1-M long shift (LSLL/LSRL/ASRL), immediate or register. | |
| static RA8_INTERNAL int32_t | internal_long_shift_amount (uc_engine *uc, const long_shift_insn_t *insn) |
| Resolve the shift amount of a decoded long shift, in bits. | |
| static RA8_INTERNAL uint64_t | internal_long_shift_apply (uint64_t val, uint32_t op, int32_t shift) |
| Apply one long shift to a 64-bit value with correct host arithmetic. | |
| static RA8_INTERNAL long_shift_pending_t * | internal_long_shift_slot (uint32_t at, bool want_active) |
Find the pending write-back slot for at, or a free slot. | |
| static RA8_INTERNAL bool | internal_long_shift_is_site (uint32_t addr) |
Report whether addr is a long-shift site found by the image scan. | |
| static RA8_INTERNAL void | internal_long_shift_begin (uc_engine *uc, uint32_t address) |
| Compute one long shift on the host and stage it for write-back. | |
| static RA8_INTERNAL void | internal_long_shift_commit (uc_engine *uc, long_shift_pending_t *slot) |
| Write back the staged long-shift result over the ORRS's damage. | |
| static RA8_INTERNAL void | internal_on_long_shift (uc_engine *uc, uint64_t address, uint32_t size, void *user) |
| Perform on long shift for the emu seam longshift model. | |
| bool | emulate_long_shift_reg (uc_engine *uc, uint32_t pc, const uint8_t code[4]) |
| Emulate a register-form Armv8.1-M long shift (LSLL/ASRL) that trapped. | |
| static RA8_INTERNAL bool | internal_install_seg_hooks (long_shift_scan_t *scan, const uint8_t *bytes, size_t length, uint32_t vaddr) |
| Scan one transient segment chunk and arm long-shift hooks. | |
| static RA8_INTERNAL bool | internal_long_shift_segment (const elf_exec_segment_t *segment, void *opaque) |
| Stream and scan one executable segment through bounded scratch. | |
| void | long_shift_seam_install (uc_engine *uc, const emu_elf_source_t *elf) |
| Scan the loaded image and install a hook at every immediate long-shift. | |
Variables | |
| static uint32_t | s_lsh_sites [k_lsh_sites_max] |
| Execution addresses of every immediate long shift found in the image. | |
| static uint32_t | s_lsh_site_count |
| Number of valid entries in s_lsh_sites. | |
| static uc_hook | s_lsh_hooks [k_lsh_hooks_max] |
| Hook handles for the installed sites (kept alive for the whole run). | |
| static long_shift_pending_t | s_lsh_pending [k_lsh_pending_max] |
| Outstanding long-shift write-backs (see long_shift_pending_t). | |
Armv8.1-M long-shift (LSLL/LSRL/ASRL) emulation seam (see emu_seams.h).
Same M85-vs-M33 gap as the other seams, but with a sharper failure mode: the M33 does NOT trap these. Their encoding overlaps ORR.W (register) with Rm == PC, so the core silently mis-executes the shift as an ORRS and yields a wrong 64-bit result with no fault. GCC emits them for any 64-bit shift that crosses the word boundary, so the silent miscompile corrupts 64-bit divides and miniz's ZIP central-directory size math alike. A one-time image scan finds every immediate long-shift site and hooks it so the correct 64-bit result is applied on the host, where 64-bit arithmetic is correct.
The seam never edits the image and never stops the engine. Both were tried and both are wrong:
Instead the shift is applied in two phases around the mis-decoding instruction, using nothing but register reads and writes:
One dispatcher serves both phases, so a long shift immediately followed by another long shift resolves in the correct order regardless of the order Unicorn happens to invoke hooks in.
Definition in file emu_seam_longshift.c.
| anonymous enum : uint32_t |
Max long-shift sites tracked per image (generous; real counts tiny).
| Enumerator | |
|---|---|
| k_lsh_sites_max | Lsh sites maximum. |
| k_lsh_hooks_max | Two hooks per site: the site and its tail. |
Definition at line 287 of file emu_seam_longshift.c.
| anonymous enum : uint32_t |
Depth of the outstanding-write-back table.
| Enumerator | |
|---|---|
| k_lsh_pending_max | Lsh pending maximum. |
Definition at line 334 of file emu_seam_longshift.c.
| enum long_shift_t : uint32_t |
Definition at line 87 of file emu_seam_longshift.c.
| bool emulate_long_shift_reg | ( | uc_engine * | uc, |
| uint32_t | pc, | ||
| const uint8_t | code[4] ) |
Emulate a register-form Armv8.1-M long shift (LSLL/ASRL) that trapped.
The register form (`lsll r0, r1, ip`) aliases to an ORR.W whose Rm field is SP, which the core refuses outright, so unlike the immediate form it arrives here as a genuine undefined-instruction trap rather than silently mis-executing. Decodes the site, applies the shift to the {RdaHi:RdaLo} pair with correct 64-bit host arithmetic – the amount is the SIGNED low byte of Rm, so a negative value shifts the other way – writes the pair back and advances PC past the 4-byte instruction. Flags are untouched because the aliased ORRS never executed.
| [in,out] | uc | Unicorn engine. |
| [in] | pc | Address of the trapping instruction. |
| [in] | code | The 4 instruction bytes at pc. |
code was a register-form long shift and was emulated. | true | Registers written and PC advanced; caller should stop+relaunch. |
| false | Not a register-form long shift; try the next handler. |
code holds the 4 bytes the core failed to decode at pc. uc is stopped inside the invalid-instruction hook. pc + 4 and the register pair holds the result. Definition at line 511 of file emu_seam_longshift.c.
References long_shift_insn_t::by_reg, internal_long_shift_amount(), internal_long_shift_apply(), internal_long_shift_decode(), k_arm_reg_id, k_byte_bits, k_lsh_insn_len, k_lsh_word_bits, long_shift_insn_t::op, long_shift_insn_t::rdahi, and long_shift_insn_t::rdalo.
Referenced by internal_dispatch_armv81_seam().
|
static |
Scan one transient segment chunk and arm long-shift hooks.
Records immediate-form sites and hooks each site plus its tail instruction.
| [in,out] | scan | Engine and running installed-site count. |
| [in] | bytes | Transient executable bytes. |
| [in] | length | Number of readable transient bytes. |
| [in] | vaddr | Virtual address corresponding to bytes. |
| true | The complete chunk was scanned below the fixed site cap. |
| false | The fixed site cap was reached. |
scan and bytes are non-null. bytes spans length readable bytes. Definition at line 558 of file emu_seam_longshift.c.
References long_shift_insn_t::by_reg, internal_long_shift_decode(), internal_on_long_shift(), k_byte_bits, k_lsh_insn_len, k_lsh_sites_max, priv_emu_io_errf(), RA8_INTERNAL, s_lsh_hooks, s_lsh_site_count, and s_lsh_sites.
Referenced by internal_long_shift_segment().
|
static |
Resolve the shift amount of a decoded long shift, in bits.
The immediate form carries its amount in the encoding. The register form takes the low byte of Rm as a SIGNED amount, so a negative value shifts the opposite way; that is the MVE definition and it is why the amount cannot be resolved until the core has reached the instruction.
| [in,out] | uc | Unicorn engine to read Rm from. |
| [in] | insn | Decoded instruction. |
insn came from a successful internal_long_shift_decode. | value | The operation-specific long shift amount value. |
Definition at line 230 of file emu_seam_longshift.c.
References long_shift_insn_t::by_reg, long_shift_insn_t::imm, k_arm_reg_id, k_lsh_amount_mask, k_lsh_amount_sign, k_lsh_amount_wrap, RA8_INTERNAL, and long_shift_insn_t::rm.
Referenced by emulate_long_shift_reg(), and internal_long_shift_begin().
|
static |
Apply one long shift to a 64-bit value with correct host arithmetic.
Shifts of 64 bits or more are resolved without invoking C's undefined behaviour for an over-wide shift: a logical shift yields zero and an arithmetic right shift yields the sign fill. A negative amount reverses the direction, per the MVE register form.
| [in] | val | The 64-bit {RdaHi:RdaLo} operand. |
| [in] | op | k_lsh_op_lsll / _lsrl / _asrl. |
| [in] | shift | Amount in bits; negative reverses the direction. |
op is one of the three plain long-shift operations. shift, including >= 64. | value | The operation-specific long shift apply value. |
Definition at line 265 of file emu_seam_longshift.c.
References k_lsh_op_asrl, k_lsh_op_lsll, k_lsh_pair_bits, and RA8_INTERNAL.
Referenced by emulate_long_shift_reg(), and internal_long_shift_begin().
|
static |
Compute one long shift on the host and stage it for write-back.
Reads the instruction at address, decodes it, forms the 64-bit {RdaHi:RdaLo} operand, applies the shift with correct 64-bit host arithmetic, and records the result plus the pre-existing NZCV flags in s_lsh_pending. Nothing is written to the core here: the mis-decoding ORRS runs next and would overwrite RdaHi and the flags anyway.
| [in,out] | uc | Unicorn engine to read the operands from. |
| [in] | address | Address of the long-shift instruction. |
uc is a running engine positioned at address. Definition at line 418 of file emu_seam_longshift.c.
References emu_mem_read(), internal_long_shift_amount(), internal_long_shift_apply(), internal_long_shift_decode(), internal_long_shift_slot(), k_arm_reg_id, k_byte_bits, k_lsh_insn_len, k_lsh_word_bits, long_shift_insn_t::op, RA8_INTERNAL, long_shift_insn_t::rdahi, and long_shift_insn_t::rdalo.
Referenced by internal_on_long_shift().
|
static |
Write back the staged long-shift result over the ORRS's damage.
Restores {RdaHi:RdaLo} to the correct 64-bit result and puts NZCV back as it stood before the mis-decoded ORRS, because a real immediate LSLL/LSRL/ASRL leaves the flags alone. Disarms the entry.
| [in,out] | uc | Unicorn engine to write the registers of. |
| [in,out] | slot | The armed entry for the instruction now being entered. |
slot is a non-null, armed entry. slot is disarmed and the register pair holds the result. Definition at line 476 of file emu_seam_longshift.c.
References long_shift_pending_t::active, long_shift_pending_t::hi, k_arm_reg_id, long_shift_pending_t::lo, long_shift_pending_t::nzcv, RA8_INTERNAL, long_shift_pending_t::rdahi, and long_shift_pending_t::rdalo.
Referenced by internal_on_long_shift().
|
static |
Decode an Armv8.1-M long shift (LSLL/LSRL/ASRL), immediate or register.
Validates the two-halfword encoding (see long_shift_t) and, on a match, fills out with the destination pair, the operation, and either the immediate amount or the register that supplies it. Rejects the saturating / rounding subfamily (hw1[0] set) and the 32-bit UQRSHL/SQRSHR forms (RdaHi == 0b1111), both of which alias this encoding but do not share its semantics. Shared by the image scan that installs the per-site hooks and by the hook that emulates one at run time.
| [in] | hw1 | First (low-address) instruction halfword. |
| [in] | hw2 | Second instruction halfword. |
| [out] | out | Receives the decoded instruction on a match. |
hw1/hw2 form a plain long shift; false otherwise. out is non-null. out is fully written; on false, it is untouched. | true | The long shift decode condition holds or completed successfully; false otherwise. |
Definition at line 167 of file emu_seam_longshift.c.
References long_shift_insn_t::by_reg, long_shift_insn_t::imm, k_lo4_mask, k_lsh_hw1_mask, k_lsh_hw1_match, k_lsh_hw1_sat_bit, k_lsh_hw2_lo_mask, k_lsh_imm2_mask, k_lsh_imm2_shift, k_lsh_imm3_mask, k_lsh_imm3_shift, k_lsh_imm3_to_imm5, k_lsh_op_asrl, k_lsh_op_lsrl, k_lsh_op_mask, k_lsh_op_shift, k_lsh_rdahi_shift, k_lsh_rdahi_wide, k_lsh_rdalo_mask, k_lsh_reg_zero_msk, k_lsh_rm_shift, k_lsh_tail_imm, k_lsh_tail_reg, k_lsh_word_bits, long_shift_insn_t::op, and long_shift_insn_t::rm.
Referenced by emulate_long_shift_reg(), internal_install_seg_hooks(), and internal_long_shift_begin().
|
static |
Report whether addr is a long-shift site found by the image scan.
| [in] | addr | Execution address to test. |
addr holds an immediate long shift. Report whether addr is a long-shift site found by the image scan; this step is contained within the emu seam longshift model and uses bounded caller or module-owned storage.
| true | The long shift is site condition holds or completed successfully; false otherwise. |
Definition at line 387 of file emu_seam_longshift.c.
References RA8_INTERNAL, s_lsh_site_count, and s_lsh_sites.
Referenced by internal_on_long_shift().
|
static |
Stream and scan one executable segment through bounded scratch.
Preserves a two-byte overlap so split Thumb-2 instructions are decoded once.
| [in] | segment | Bounds-checked executable segment. |
| [in,out] | opaque | long_shift_scan_t scan state. |
| true | The complete segment was scanned. |
| false | A source read failed or the fixed site cap was reached. |
segment is non-null and its source remains open. opaque points to writable scan state. < Transient instruction-scan bytes.
Definition at line 621 of file emu_seam_longshift.c.
References elf_exec_segment_t::filesz, internal_install_seg_hooks(), k_emu_elf_io_ok, k_lsh_insn_len, elf_exec_segment_t::offset, priv_emu_elf_read(), RA8_INTERNAL, elf_exec_segment_t::source, emu_elf_io_result_t::status, and elf_exec_segment_t::vaddr.
Referenced by long_shift_seam_install().
|
static |
Find the pending write-back slot for at, or a free slot.
Used both to look up an outstanding entry (want_active true) and to claim a slot for a new one (want_active false, preferring an entry already keyed to at so a re-executed site cannot accumulate duplicates).
| [in] | at | Write-back address to match. |
| [in] | want_active | true to find an armed entry for at; false to claim. |
Definition at line 355 of file emu_seam_longshift.c.
References k_lsh_pending_max, RA8_INTERNAL, and s_lsh_pending.
Referenced by internal_long_shift_begin(), and internal_on_long_shift().
|
static |
Perform on long shift for the emu seam longshift model.
Perform on long shift for the emu seam longshift model; this step is contained within the emu seam longshift model and uses bounded caller or module-owned storage.
| [in,out] | uc | Unicorn engine whose emulated state is read or updated. |
| [in] | address | Guest address involved in the operation. |
| [in] | size | Size of the requested region or access in bytes. |
| [in,out] | user | Hook context supplied when the callback was registered. |
Definition at line 497 of file emu_seam_longshift.c.
References internal_long_shift_begin(), internal_long_shift_commit(), internal_long_shift_is_site(), and internal_long_shift_slot().
Referenced by internal_install_seg_hooks().
| void long_shift_seam_install | ( | uc_engine * | uc, |
| const emu_elf_source_t * | elf ) |
Scan the loaded image and install a hook at every immediate long-shift.
Walks the ELF32 PT_LOAD program headers, and for each executable segment scans its bytes on 2-byte boundaries for the long-shift encoding (internal_long_shift_decode). A targeted UC_HOOK_CODE is installed at each site's VMA, and at the following instruction, so internal_on_long_shift can emulate it. Matches use the segment's VMA (p_vaddr), so a .sram_text ramfunc region is hooked at its execution address even though it is not yet copied at install time. A scan false-positive (a halfword pair inside data or mid-instruction that happens to match) is harmless: the core never starts execution there, so the hook never fires, and the seam never rewrites the image. Zero hooks – hence zero steady-state cost – for firmware that contains no long shifts.
| [in,out] | uc | Unicorn engine to install the hooks on. |
| [in] | elf | In-memory ELF image (still alive at call time). |
elf is a 32-bit ARM ELF (already validated by load_elf). Definition at line 675 of file emu_seam_longshift.c.
References elf_foreach_exec_segment(), internal_long_shift_segment(), memset(), priv_emu_io_errf(), s_lsh_pending, and s_lsh_site_count.
Referenced by internal_main_install_run_seams().
|
static |
Hook handles for the installed sites (kept alive for the whole run).
Definition at line 297 of file emu_seam_longshift.c.
Referenced by internal_install_seg_hooks().
|
static |
Outstanding long-shift write-backs (see long_shift_pending_t).
Definition at line 339 of file emu_seam_longshift.c.
Referenced by internal_long_shift_slot(), and long_shift_seam_install().
|
static |
Number of valid entries in s_lsh_sites.
Definition at line 295 of file emu_seam_longshift.c.
Referenced by internal_install_seg_hooks(), internal_long_shift_is_site(), and long_shift_seam_install().
|
static |
Execution addresses of every immediate long shift found in the image.
Definition at line 293 of file emu_seam_longshift.c.
Referenced by internal_install_seg_hooks(), and internal_long_shift_is_site().