ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_seam_longshift.c File Reference

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"
Include dependency graph for emu_seam_longshift.c:

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_tinternal_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).

Detailed Description

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:

  • Patching each site to a decodable no-op corrupts the firmware. The scan matches on 2-byte boundaries and cannot tell a real instruction from a halfword pair inside a literal pool that happens to fit the encoding, so a false positive that is merely never executed today would become a silently rewritten constant.
  • Writing PC from inside the UC_HOOK_CODE callback and calling uc_emu_stop() – the contract this seam used to use – makes the next uc_emu_start() return UC_ERR_INSN_INVALID, reported against the relaunch address rather than any real instruction. That defect killed the run on the FIRST long shift that ever executed, so in practice no long shift was ever emulated.

Instead the shift is applied in two phases around the mis-decoding instruction, using nothing but register reads and writes:

  1. At the site, BEFORE the core executes it, the operand pair {RdaHi:RdaLo} and the current NZCV flags are captured and the correct 64-bit result is computed.
  2. The core executes the encoding as ORRS, which clobbers exactly RdaHi and the flags, and nothing else – RdaLo is the ORRS Rn operand, not its Rd.
  3. At the following instruction the captured result is written over {RdaHi:RdaLo} and NZCV is restored, because a real immediate LSLL/LSRL/ASRL does not write the flags.

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.

Since
0.1.0

Definition in file emu_seam_longshift.c.

Enumeration Type Documentation

◆ anonymous enum

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

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.

◆ long_shift_t

enum long_shift_t : uint32_t
Enumerator
k_lsh_hw1_mask 

hw1[15:4] selects the group.

k_lsh_hw1_match 

hw1[15:4] == 0xEA5 for this family.

k_lsh_hw1_sat_bit 

hw1[0]: 1 = saturating/rounding subfamily.

k_lsh_rdalo_mask 

RdaLo at hw1[3:1] (an even register).

k_lsh_hw2_lo_mask 

hw2[3:0] selects immediate vs register form.

k_lsh_tail_imm 

hw2[3:0] == 0b1111 -> immediate form.

k_lsh_tail_reg 

hw2[3:0] == 0b1101 -> register form.

k_lsh_op_shift 

op field at hw2[5:4].

k_lsh_op_mask 

op width (value 3 is reserved -> rejected).

k_lsh_op_lsll 

00: 64-bit logical shift left.

k_lsh_op_lsrl 

01: 64-bit logical shift right.

k_lsh_op_asrl 

10: 64-bit arithmetic shift right.

k_lsh_rdahi_shift 

RdaHi at hw2[11:8].

k_lsh_rdahi_wide 

RdaHi == 0b1111 -> 32-bit form, rejected.

k_lsh_rm_shift 

Rm at hw2[15:12] in the register form.

k_lsh_reg_zero_msk 

hw2[7:6] must be zero in the register form.

k_lsh_imm3_shift 

imm3 at hw2[14:12].

k_lsh_imm3_mask 

imm3 width.

k_lsh_imm2_shift 

imm2 at hw2[7:6].

k_lsh_imm2_mask 

imm2 width.

k_lsh_imm3_to_imm5 

imm5 = (imm3 << 2) | imm2.

k_lsh_word_bits 

Word width; also the imm5==0 shift amount.

k_lsh_pair_bits 

Width of the {RdaHi:RdaLo} pair, bits.

k_lsh_amount_mask 

Register form uses Rm[7:0] as the amount.

k_lsh_amount_wrap 

Rm[7:0] is signed: subtract to go negative.

k_lsh_amount_sign 

Sign bit of the 8-bit shift amount.

k_lsh_insn_len 

Thumb-2 instruction length, bytes.

Definition at line 87 of file emu_seam_longshift.c.

Function Documentation

◆ emulate_long_shift_reg()

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.

Parameters
[in,out]ucUnicorn engine.
[in]pcAddress of the trapping instruction.
[in]codeThe 4 instruction bytes at pc.
Returns
true when code was a register-form long shift and was emulated.
Return values
trueRegisters written and PC advanced; caller should stop+relaunch.
falseNot a register-form long shift; try the next handler.
Precondition
code holds the 4 bytes the core failed to decode at pc.
uc is stopped inside the invalid-instruction hook.
Postcondition
On true, PC is pc + 4 and the register pair holds the result.
On false, no engine state is modified.
Note
Not thread-safe; called from the single-threaded run loop.
See also
long_shift_seam_install() Handles the immediate form, which never traps.
Since
0.1.0

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().

◆ internal_install_seg_hooks()

RA8_INTERNAL bool internal_install_seg_hooks ( long_shift_scan_t * scan,
const uint8_t * bytes,
size_t length,
uint32_t vaddr )
static

Scan one transient segment chunk and arm long-shift hooks.

Records immediate-form sites and hooks each site plus its tail instruction.

Parameters
[in,out]scanEngine and running installed-site count.
[in]bytesTransient executable bytes.
[in]lengthNumber of readable transient bytes.
[in]vaddrVirtual address corresponding to bytes.
Returns
Whether scanning may continue.
Return values
trueThe complete chunk was scanned below the fixed site cap.
falseThe fixed site cap was reached.
Precondition
scan and bytes are non-null.
bytes spans length readable bytes.
Postcondition
Every accepted site has both execution hooks installed.
scan->n_hooks never exceeds k_lsh_sites_max.
Note
The transient bytes are not retained.
Since
0.1.0

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().

◆ internal_long_shift_amount()

RA8_INTERNAL int32_t internal_long_shift_amount ( uc_engine * uc,
const long_shift_insn_t * insn )
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.

Parameters
[in,out]ucUnicorn engine to read Rm from.
[in]insnDecoded instruction.
Returns
Shift amount in bits; negative means shift the opposite direction.
Precondition
insn came from a successful internal_long_shift_decode.
Postcondition
No engine state is modified (register read only).
Return values
valueThe operation-specific long shift amount value.
Precondition
The call executes on the emulator's single owning thread.
Postcondition
Ownership of caller-supplied storage is unchanged.
Note
The operation is synchronous and does not transfer heap ownership.
Since
0.1.0

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().

◆ internal_long_shift_apply()

RA8_INTERNAL uint64_t internal_long_shift_apply ( uint64_t val,
uint32_t op,
int32_t shift )
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.

Parameters
[in]valThe 64-bit {RdaHi:RdaLo} operand.
[in]opk_lsh_op_lsll / _lsrl / _asrl.
[in]shiftAmount in bits; negative reverses the direction.
Returns
The shifted value.
Precondition
op is one of the three plain long-shift operations.
Postcondition
The result is defined for every shift, including >= 64.
Return values
valueThe operation-specific long shift apply value.
Precondition
The call executes on the emulator's single owning thread.
Postcondition
Ownership of caller-supplied storage is unchanged.
Note
The operation is synchronous and does not transfer heap ownership.
Since
0.1.0

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().

◆ internal_long_shift_begin()

RA8_INTERNAL void internal_long_shift_begin ( uc_engine * uc,
uint32_t address )
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.

Parameters
[in,out]ucUnicorn engine to read the operands from.
[in]addressAddress of the long-shift instruction.
Returns
Nothing.
Precondition
uc is a running engine positioned at address.
Postcondition
s_lsh_pending is armed, or left untouched on an unreadable / non-matching site (a scan false positive).
Note
Not thread-safe.
Precondition
The call executes on the emulator's single owning thread.
Postcondition
Ownership of caller-supplied storage is unchanged.
Since
0.1.0

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().

◆ internal_long_shift_commit()

RA8_INTERNAL void internal_long_shift_commit ( uc_engine * uc,
long_shift_pending_t * slot )
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.

Parameters
[in,out]ucUnicorn engine to write the registers of.
[in,out]slotThe armed entry for the instruction now being entered.
Returns
Nothing.
Precondition
slot is a non-null, armed entry.
Postcondition
slot is disarmed and the register pair holds the result.
Note
Not thread-safe.
Precondition
The call executes on the emulator's single owning thread.
Postcondition
Ownership of caller-supplied storage is unchanged.
Since
0.1.0

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().

◆ internal_long_shift_decode()

RA8_INTERNAL bool internal_long_shift_decode ( uint16_t hw1,
uint16_t hw2,
long_shift_insn_t * out )
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.

Parameters
[in]hw1First (low-address) instruction halfword.
[in]hw2Second instruction halfword.
[out]outReceives the decoded instruction on a match.
Returns
true if hw1/hw2 form a plain long shift; false otherwise.
Precondition
out is non-null.
Postcondition
On true, out is fully written; on false, it is untouched.
Return values
trueThe long shift decode condition holds or completed successfully; false otherwise.
Precondition
The call executes on the emulator's single owning thread.
Postcondition
Ownership of caller-supplied storage is unchanged.
Note
The operation is synchronous and does not transfer heap ownership.
Since
0.1.0

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().

◆ internal_long_shift_is_site()

RA8_INTERNAL bool internal_long_shift_is_site ( uint32_t addr)
static

Report whether addr is a long-shift site found by the image scan.

Parameters
[in]addrExecution address to test.
Returns
true when addr holds an immediate long shift.
Precondition
The image scan has run (an empty table simply answers false).
Postcondition
No state is modified (read-only predicate).

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.

Return values
trueThe long shift is site condition holds or completed successfully; false otherwise.
Precondition
The call executes on the emulator's single owning thread.
Postcondition
Ownership of caller-supplied storage is unchanged.
Note
The operation is synchronous and does not transfer heap ownership.
Since
0.1.0

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().

◆ internal_long_shift_segment()

RA8_INTERNAL bool internal_long_shift_segment ( const elf_exec_segment_t * segment,
void * opaque )
static

Stream and scan one executable segment through bounded scratch.

Preserves a two-byte overlap so split Thumb-2 instructions are decoded once.

Parameters
[in]segmentBounds-checked executable segment.
[in,out]opaquelong_shift_scan_t scan state.
Returns
Whether executable-segment iteration may continue.
Return values
trueThe complete segment was scanned.
falseA source read failed or the fixed site cap was reached.
Precondition
segment is non-null and its source remains open.
opaque points to writable scan state.
Postcondition
Every discovered immediate-form site is recorded and hooked.
No transient source byte pointer is retained.
Note
Stack scratch is fixed at 4096 bytes.
Since
0.1.0

< 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().

◆ internal_long_shift_slot()

RA8_INTERNAL long_shift_pending_t * internal_long_shift_slot ( uint32_t at,
bool want_active )
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).

Parameters
[in]atWrite-back address to match.
[in]want_activetrue to find an armed entry for at; false to claim.
Returns
Pointer to the matching or claimable slot, or nullptr if none.
Precondition
The table is initialised (it is zeroed at load, and per-run by install).
Postcondition
No entry is modified by the lookup itself.

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().

◆ internal_on_long_shift()

RA8_INTERNAL void internal_on_long_shift ( uc_engine * uc,
uint64_t address,
uint32_t size,
void * user )
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.

Parameters
[in,out]ucUnicorn engine whose emulated state is read or updated.
[in]addressGuest address involved in the operation.
[in]sizeSize of the requested region or access in bytes.
[in,out]userHook context supplied when the callback was registered.
Precondition
Arguments satisfy the ranges documented for on long shift.
The call executes on the emulator's single owning thread.
Postcondition
State changes remain confined to the emu seam longshift model and documented output objects.
Ownership of caller-supplied storage is unchanged.
Note
The operation is synchronous and does not transfer heap ownership.
Since
0.1.0

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().

◆ long_shift_seam_install()

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.

Parameters
[in,out]ucUnicorn engine to install the hooks on.
[in]elfIn-memory ELF image (still alive at call time).
Returns
Nothing.
Precondition
elf is a 32-bit ARM ELF (already validated by load_elf).
Postcondition
One UC_HOOK_CODE pair per long-shift site (up to k_lsh_sites_max) is armed.
Note
Not thread-safe; call once during setup before the run loop.
Since
0.1.0

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().

Variable Documentation

◆ s_lsh_hooks

uc_hook s_lsh_hooks[k_lsh_hooks_max]
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().

◆ s_lsh_pending

long_shift_pending_t s_lsh_pending[k_lsh_pending_max]
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().

◆ s_lsh_site_count

uint32_t s_lsh_site_count
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().

◆ s_lsh_sites

uint32_t s_lsh_sites[k_lsh_sites_max]
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().