Abstruction -> great but some detail is ignored
**virtualization** :When something is virtualized, its interface and all resources visible through the interface are mapped onto the interface and resource of a real system actually implementing it
2. isomorphism
**architecture** :The architecture is often formally described through a specification
of an interface and the logical behavior of resources manipulated via the inter-
face.
4. The term implementation will be used to describe the actual embodiment
of an architecture.
In assembly language programming, the function prologue is a few lines of code at the beginning of a function, which prepare the stack and registers for use within the function. Similarly, the function epilogue appears at the end of the function, and restores the stack and registers to the state they were in before the function was called.
# Dynablock Lifecycle
This document explains:
1. How Dynablock
2. How Dynablock handle jump
Dynamic Block (`Dynablock`) Definition
In Box64:
A dynablock is a region of x64 instructions that are dynamically translated into a block of native code (JIT compiled) as a unit.
- Begins: At the entry of a function or at the target of a jump/branch.
- Ends: At the next jump, branch, or return (including conditional branches, calls, and sometimes exceptions).
- May be shorter if a code size or instruction limit is hit.
This mirrors the textbook definition of a dynamic basic block:
> A dynamic basic block is a sequence of instructions actually executed at runtime, starting immediately after a branch/jump, and ending at the next branch/jump during one execution path.
>The same instruction can belong to multiple dynamic basic blocks in different execution paths.
Note: In Box64, "basic blocks" can be extended to "superblocks" or "traces" if the dynarec allows fusing multiple basic blocks, but the initial scan is just as you described.
- structure
- Create
- Execute
- Modify
- Delete
- chaing
- Find
### Structure
```c
typedef struct instsize_s {
unsigned char x64:4;
unsigned char nat:4;
} instsize_t;
typedef struct callret_s {
uint32_t offs:31;
uint32_t type:1;
} callret_t;
typedef struct dynablock_s {
void* block; // block-sizeof(void*) == self
void* actual_block; // the actual start of the block (so block-sizeof(void*))
struct dynablock_s* previous; // a previous block that might need to be freed
void* x64_addr;
uintptr_t x64_size;
size_t native_size;
int size;
uint32_t hash;
uint8_t done;
uint8_t gone;
uint8_t dirty; // if need to be tested as soon as it's created
uint8_t always_test:2;
uint8_t is32bits:1;
int callret_size; // size of the array
int isize;
size_t arch_size; // size of of arch dependant infos
instsize_t* instsize;
void* arch; // arch dependant per inst info (can be NULL)
callret_t* callrets; // array of callret return, with NOP / UDF depending if the block is clean or dirty
void* jmpnext; // a branch jmpnext code when block is marked
size_t table64size;// to check table64
void* table64; // to relocate the table64
size_t relocsize; // number of relocations (should be an int only)
void* relocs; // relocations, when block is loaded
#ifdef GDBJIT
void* gdbjit_block;
#endif
} dynablock_t;
```
```c
/*
A Block must have this layout:
0x0000..0x0007 : dynablock_t* : self
0x0008..8+4*n : actual Native instructions, (n is the total number)
A .. A+8*n : Table64: n 64bits values
B .. B+7 : dynablock_t* : self (as part of JmpNext, that simulate another block)
B+8 .. B+15 : 2 Native code for jmpnext (or jmp epilog in case of empty block)
B+16 .. B+23 : jmpnext (or jmp_epilog) address. jumpnext is used when the block needs testing
B+24 .. B+31 : empty (in case an architecture needs more than 2 opcodes)
B+32 .. B+32+sz : instsize (compressed array with each instruction length on x64 and native side)
C .. C+sz : arch: arch specific info (likes flags info) per inst (can be absent)
*/
```
1. [Literal pool](https://en.wikipedia.org/wiki/Literal_pool)
The table64 in a dynablock_t is essentially Box64’s literal pool for 64‑bit values.
```markdown
|---------------------------| <- actual_block (malloc’d start)
| dynablock_t * self_ptr | // 8 bytes back‑pointer to this struct
|===========================| <- block (native code begins)
| native instructions ... | // size = native_size
|---------------------------|
| table64: N × uint64_t | // literals needing relocation
|---------------------------|
| jmpnext stub: |
| [self_ptr again] | // lets stub find its dynablock_t
| [2 instr opcodes] |
| [jmpnext target addr] |
| [padding if needed] |
|---------------------------|
| instsize: N × instsize_t | // compressed x64 vs native lengths
|---------------------------|
| arch: N × arch_data | // optional per‑inst extra flags
|---------------------------|
| callrets, relocs, etc. | // further metadata if present
|---------------------------| <- end of block
```
1. DB
2. DBjumptable
3. setJumpTableIfRef64
1. Hot-page
### getDB
Instruction address is literally “encoded” into an index into a nested array, which then yields the corresponding JIT block.
`box64_jmptbl4` and `box64_jmptbl3` provide a SPC-to-DB lookup
```gdb
#0 internalDBGetBlock (addr=addr@entry=4294971600, filladdr=filladdr@entry=4294971600, create=create@entry=1,
need_lock=need_lock@entry=1, is32bits=is32bits@entry=0, is_new=is_new@entry=1, emu=0x36d5de10)
at /root/box64/src/dynarec/dynablock.c:230
#1 0x000000003521cfea in DBGetBlock (emu=emu@entry=0x36d5de10, addr=4294971600, create=create@entry=1, is32bits=is32bits@entry=0)
at /root/box64/src/dynarec/dynablock.c:328
#2 0x0000000034aed39e in DynaRun (emu=emu@entry=0x36d5de10) at /root/box64/src/dynarec/dynarec.c:206
#3 0x0000000034ae6e54 in emulate (emu=0x36d5de10, elf_header=<optimized out>) at /root/box64/src/core.c:1455
#4 0x0000000034ae151c in main (argc=<optimized out>, argv=<optimized out>, env=<optimized out>) at /root/box64/src/main.c:11
```
```gdb
#0 FillBlock64 (addr=addr@entry=4294971600, alternate=alternate@entry=0, is32bits=is32bits@entry=0,
inst_max=inst_max@entry=32760, is_new=is_new@entry=1) at /root/box64/src/dynarec/dynarec_native.c:632
#1 0x000000003521cd9a in internalDBGetBlock (addr=addr@entry=4294971600, filladdr=filladdr@entry=4294971600,
create=create@entry=1, need_lock=need_lock@entry=1, is32bits=is32bits@entry=0, is_new=is_new@entry=1, emu=0x36d5de10)
at /root/box64/src/dynarec/dynablock.c:290
#2 0x000000003521cfea in DBGetBlock (emu=emu@entry=0x36d5de10, addr=4294971600, create=create@entry=1, is32bits=is32bits@entry=0)
at /root/box64/src/dynarec/dynablock.c:328
#3 0x0000000034aed39e in DynaRun (emu=emu@entry=0x36d5de10) at /root/box64/src/dynarec/dynarec.c:206
#4 0x0000000034ae6e54 in emulate (emu=0x36d5de10, elf_header=<optimized out>) at /root/box64/src/core.c:1455
#5 0x0000000034ae151c in main (argc=<optimized out>, argv=<optimized out>, env=<optimized out>) at /root/box64/src/main.c:11
```

### emulate
```c
int emulate(x64emu_t* emu, elfheader_t* elf_header)
{
// get entrypoint
my_context->ep = GetEntryPoint(my_context->maplib, elf_header);
atexit(endBox64);
loadProtectionFromMap();
// emulate!
printf_log(LOG_DEBUG, "Start x64emu on Main\n");
// Stack is ready, with stacked: NULL env NULL argv argc
ResetFlags(emu);
#ifdef BOX32
if(box64_is32bits) {
SetEIP(emu, my_context->ep);
Push32(emu, my_context->exit_bridge); // push to pop it just after
SetEDX(emu, Pop32(emu)); // RDX is exit function
} else
#endif
{
SetRIP(emu, my_context->ep);
Push64(emu, my_context->exit_bridge); // push to pop it just after
SetRDX(emu, Pop64(emu)); // RDX is exit function
}
DynaRun(emu);
// Get EAX
int ret = GetEAX(emu);
printf_log(LOG_DEBUG, "Emulation finished, EAX=%d\n", ret);
endBox64();
#ifdef HAVE_TRACE
if(trace_func) {
box_free(trace_func);
trace_func = NULL;
}
#endif
#ifdef DYNAREC
if (BOX64ENV(dynarec_perf_map) && BOX64ENV(dynarec_perf_map_fd) != -1) {
close(BOX64ENV(dynarec_perf_map_fd));
SET_BOX64ENV(dynarec_perf_map_fd, -1);
}
#endif
return ret;
}
```
```c
dynablock_t* block = (skip)?NULL:DBGetBlock(emu, R_RIP, 1, is32bits);
```
```c
typedef struct x64emu_s {
// cpu
reg64_t regs[16];
x64flags_t eflags;
reg64_t ip;
// sse
sse_regs_t xmm[16];
sse_regs_t ymm[16];
// fpu / mmx
mmx87_regs_t x87[8];
mmx87_regs_t mmx[8];
x87flags_t sw;
uint32_t top; // top is part of sw, but it's faster to have it separately
int fpu_stack;
x87control_t cw;
uint16_t dummy_cw; // align...
mmxcontrol_t mxcsr;
#ifdef RV64 // it would be better to use a dedicated register for this like arm64 xSavedSP, but we're running of of free registers.
uintptr_t xSPSave; // sp base value of current dynarec frame, used by call/ret optimization to reset stack when unmatch.
#endif
fpu_ld_t fpu_ld[8]; // for long double emulation / 80bits fld fst
fpu_ll_t fpu_ll[8]; // for 64bits fild / fist sequence
uint64_t fpu_tags; // tags for the x87 regs, stacked, only on a 16bits anyway
// old ip
uintptr_t old_ip;
// deferred flags
int dummy1; // to align on 64bits with df
deferred_flags_t df;
multiuint_t op1;
multiuint_t op2;
multiuint_t res;
uint32_t *x64emu_parity_tab; // helper
// segments
uint16_t segs[6]; // only 32bits value?
uint16_t dummy_seg6, dummy_seg7; // to stay aligned
uintptr_t segs_offs[6]; // computed offset associate with segment
uint32_t segs_serial[6]; // are seg offset clean (not 0) or does they need to be re-computed (0)? For GS, serial need to be the same as context->sel_serial
// parent context
box64context_t *context;
// cpu helpers
reg64_t zero;
reg64_t *sbiidx[16];
// emu control
int quit;
int error;
int fork; // quit because need to fork
int exit;
forkpty_t* forkpty_info;
emu_flags_t flags;
x64test_t test; // used for dynarec testing
// scratch stack, used for alignment of double and 64bits ints on arm. 200 elements should be enough
__int128_t dummy_align; // here to have scratch 128bits aligned
uint64_t scratch[N_SCRATCH];
// Warning, offsetof(x64emu_t, xxx) will be too big for fields below.
#ifdef HAVE_TRACE
sse_regs_t old_xmm[16];
sse_regs_t old_ymm[16];
reg64_t oldregs[16];
uintptr_t prev2_ip;
#endif
// local stack, do be deleted when emu is freed
void* stack2free; // this is the stack to free (can be NULL)
void* init_stack; // initial stack (owned or not)
uint32_t size_stack; // stack size (owned or not)
JUMPBUFF* jmpbuf;
#ifdef RV64
uintptr_t old_savedsp;
#endif
#ifdef _WIN32
uint64_t win64_teb;
#endif
int type; // EMUTYPE_xxx define
#ifdef BOX32
int libc_err; // copy of errno from libc
int libc_herr; // copy of h_errno from libc
unsigned short libctype[384]; // copy from __ctype_b address might be too high
const unsigned short* ref_ctype;
const unsigned short* ctype;
int libctolower[384]; // copy from __ctype_b_tolower address might be too high
const int* ref_tolower;
const int* tolower;
int libctoupper[384]; // copy from __ctype_b_toupper address might be too high
const int* ref_toupper;
const int* toupper;
void* res_state_32; //32bits version of res_state
void* res_state_64;
#endif
} x64emu_t;
```

### Interpreter `Run`
It is a classic decode-and-dispatch interpreter
dispatch loop:
```c
while(1)
{
// ... (trace/logging code omitted)
opcode = F8; // Fetch next opcode
// Handle prefixes: REP, REX, segment, etc.
// ...
// Handle decoding, more prefix, modrm, etc.
// ...
// Dispatch and execute: big switch-case for opcode
switch(opcode) {
case 0x00:
// ADD r/m8, r8
// decode operands, execute ADD8
break;
case 0x01:
// ADD r/m32, r32
// ...
break;
// ...all other cases
default:
// unimplemented opcode
break;
}
// ... (flags/single-step check, update RIP)
R_RIP = addr;
}
```
### Dynarec `DynaRun`
Calls `DBGetBlock()` to fetch (or create) a compiled "dynablock" for the current `RIP` (source ISA program counter)
Box64 uses the fast dynarec/JIT IF:
A valid "dynablock" exists for the current RIP.
That block is ready (fully built/compiled):
* `block` is not NULL
* `block->block` is not NULL
* `block->done` is true
* The Trap Flag (`F_TF`) is NOT set (if set, we’re in single-step/debug mode and must use the interpreter).
### AllocDynarecMap
```gdb
#0 AllocDynarecMap (x64_addr=x64_addr@entry=4294971600, size=size@entry=384,
is_new=is_new@entry=1) at /root/box64/src/custommem.c:1426
#1 0x000000003521f178 in FillBlock64 (addr=addr@entry=4294971600,
alternate=alternate@entry=0, is32bits=is32bits@entry=0,
inst_max=inst_max@entry=32760, is_new=is_new@entry=1)
at /root/box64/src/dynarec/dynarec_native.c:863
#2 0x000000003521cd9a in internalDBGetBlock (addr=addr@entry=4294971600,
filladdr=filladdr@entry=4294971600, create=create@entry=1,
need_lock=need_lock@entry=1, is32bits=is32bits@entry=0,
is_new=is_new@entry=1, emu=0x36d5de10)
at /root/box64/src/dynarec/dynablock.c:290
#3 0x000000003521cfea in DBGetBlock (emu=emu@entry=0x36d5de10,
addr=4294971600, create=create@entry=1, is32bits=is32bits@entry=0)
at /root/box64/src/dynarec/dynablock.c:328
#4 0x0000000034aed39e in DynaRun (emu=emu@entry=0x36d5de10)
at /root/box64/src/dynarec/dynarec.c:206
#5 0x0000000034ae6e54 in emulate (emu=0x36d5de10, elf_header=<optimized out>)
at /root/box64/src/core.c:1455
#6 0x0000000034ae151c in main (argc=<optimized out>, argv=<optimized out>,
env=<optimized out>) at /root/box64/src/main.c:11
```
```c
size_t sz = sizeof(void*) + native_size + helper.table64size*sizeof(uint64_t) + 4*sizeof(void*) + insts_rsize + arch_size + callret_size + sizeof(dynablock_t) + reloc_size;
// dynablock_t* block (arm insts) table64 jmpnext code instsize arch callrets dynablock relocs
void* actual_p = (void*)AllocDynarecMap(addr, sz, is_new);
```
1. How does it determine the Liveness of a block?
2. How does it decide the size of a dynamic block?
### `native_pass`
`native_pass` is the main loop that walks over instructions and builds a dynablock.
What is native_pass for?
It scans x86-64 instructions from memory at `addr`.
For each instruction, it:
Decodes it (with prefixes, rex, etc.).
Fills in a dyn->insts[] entry with info about that instruction.
Handles barriers, flags, floating-point state, block ending, etc.
It stops when it reaches a block boundary (like a jump/branch/ret, end of memory region, or max instructions).
It’s used in multiple passes: pass0 (setup, count, first analysis), pass1, pass2, pass3
| Function | What it does | Typical Usage |
| ---------------- | ---------------------------------------------- | ------------------------------------- |
| `jump_to_next` | Chained jump to the next dynablock (JIT block) | Normal “fall-through” code flow |
| `jump_to_epilog` | Return to dispatcher/main loop (end/unhandled) | Block end, returns, or odd situations |
What native_pass actually does:
It performs a static (ahead-of-time) scan of the x86-64 code when the dynablock is being built (not while running).
This means it just reads the raw bytes, decodes each instruction, records info, figures out where jumps/branches go, etc.
It doesn’t execute the code; it just analyzes it and sets up a data structure describing the instructions and their relationships.
The real execution (runtime) later uses the information gathered here—either by running the generated native code (if possible), or by falling back to the interpreter if something goes wrong.
Static chaining uses a simple, PC-relative load from a global jump-table (table64-assisted on RISC-V/ARM64) that was patched when each block was created.
Indirect chaining uses a tiny runtime lookup routine that falls back to interpreter + block creation if the target block is not yet compiled.
```c
jump_to_next(dyn, addr, 0, ninst, rex.is32bits);
```
### `jump_to_next`
```shell
./src/dynarec/rv64/dynarec_rv64_helper.c:636:void jump_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst, int is32bits)
./src/dynarec/rv64/dynarec_rv64_67.c:807: jump_to_next(dyn, addr + i8, 0, ninst, rex.is32bits); \
./src/dynarec/rv64/dynarec_rv64_0f.c:1765: jump_to_next(dyn, j64, 0, ninst, rex.is32bits); \
./src/dynarec/rv64/dynarec_rv64_00_3.c:912: jump_to_next(dyn, addr + i8, 0, ninst, rex.is32bits); \
./src/dynarec/rv64/dynarec_rv64_00_3.c:1074: jump_to_next(dyn, j64, 0, ninst, rex.is32bits);
./src/dynarec/rv64/dynarec_rv64_00_3.c:1111: jump_to_next(dyn, (uintptr_t)getAlternate((void*)j64), 0, ninst, rex.is32bits);
./src/dynarec/rv64/dynarec_rv64_00_3.c:1569: jump_to_next(dyn, 0, ed, ninst, rex.is32bits);
./src/dynarec/rv64/dynarec_rv64_00_3.c:1587: jump_to_next(dyn, 0, ed, ninst, rex.is32bits);
./src/dynarec/rv64/dynarec_rv64_helper.h:1280:#define jump_to_next STEPNAME(jump_to_next)
./src/dynarec/rv64/dynarec_rv64_helper.h:1454:void jump_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst, int is32bits);
./src/dynarec/rv64/dynarec_rv64_00_1.c:338: jump_to_next(dyn, addr + i8, 0, ninst, rex.is32bits); \
````
```c
void jump_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst, int is32bits)
{
MAYUSE(dyn);
MAYUSE(ninst);
MESSAGE(LOG_DUMP, "Jump to next\n");
if (is32bits)
ip &= 0xffffffffLL;
int dest;
if (reg) {
if (reg != xRIP) {
MV(xRIP, reg);
}
NOTEST(x2);
dest = indirect_lookup(dyn, ninst, is32bits, x2, x3);
} else {
uintptr_t p = getJumpTableAddress64(ip);
MAYUSE(p);
GETIP_(ip, x3);
if (dyn->need_reloc) AddRelocTable64JmpTbl(dyn, ninst, ip, STEP);
TABLE64_(x3, p);
LD(x2, x3, 0);
dest = x2;
}
CLEARIP();
SMEND();
#ifdef HAVE_TRACE
JALR(xRA, dest);
#else
JALR((dyn->insts[ninst].x64.has_callret ? xRA : xZR), dest);
#endif
}
```
1. Indirect Jump (reg != 0)
For register indirect jumps, especially procedure returns, the target may change from on execution of the jump to next
```c
case 4: // JMP Ed
INST_NAME("JMP Ed");
READFLAGS(X_PEND);
BARRIER(BARRIER_FLOAT);
GETEDz(0);
jump_to_next(dyn, 0, ed, ninst, rex.is32bits);
*need_epilog = 0;
*ok = 0;
break;
```
```c
case 2: // CALL Ed
INST_NAME("CALL Ed");
PASS2IF ((BOX64DRENV(dynarec_safeflags) > 1) || ((ninst && dyn->insts[ninst - 1].x64.set_flags) || ((ninst > 1) && dyn->insts[ninst - 2].x64.set_flags)), 1) {
READFLAGS(X_PEND); // that's suspicious
} else {
SETFLAGS(X_ALL, SF_SET_NODF, NAT_FLAGS_NOFUSION); // Hack to put flag in "don't care" state
}
GETEDz(0);
if (BOX64DRENV(dynarec_callret) && BOX64DRENV(dynarec_bigblock) > 1) {
BARRIER(BARRIER_FULL);
} else {
BARRIER(BARRIER_FLOAT);
*need_epilog = 0;
*ok = 0;
}
GETIP_(addr, x7);
if (BOX64DRENV(dynarec_callret)) {
SET_HASCALLRET();
j64 = (dyn->insts) ? (GETMARK - (dyn->native_size)) : 0;
AUIPC(x4, ((j64 + 0x800) >> 12) & 0xfffff);
ADDI(x4, x4, j64 & 0xfff);
MESSAGE(LOG_NONE, "\tCALLRET set return to +%di\n", j64 >> 2);
ADDI(xSP, xSP, -16);
SD(x4, xSP, 0);
SD(xRIP, xSP, 8);
}
PUSH1z(xRIP);
jump_to_next(dyn, 0, ed, ninst, rex.is32bits);
MARK;
if (BOX64DRENV(dynarec_callret) && dyn->vector_sew != VECTOR_SEWNA)
vector_vsetvli(dyn, ninst, x3, dyn->vector_sew, VECTOR_LMUL1, 1);
if (BOX64DRENV(dynarec_callret) && addr >= (dyn->start + dyn->isize)) {
// jumps out of current dynablock...
j64 = getJumpTableAddress64(addr);
if (dyn->need_reloc) AddRelocTable64RetEndBlock(dyn, ninst, addr, STEP);
TABLE64_(x4, j64);
LD(x4, x4, 0);
BR(x4);
}
break;
```
```c
if (reg) {
if (reg != xRIP) {
MV(xRIP, reg);
}
NOTEST(x2);
dest = indirect_lookup(dyn, ninst, is32bits, x2, x3);
}
```
2. Direct Jump (reg == 0)
### indirect_lookup
```c
static int indirect_lookup(dynarec_rv64_t* dyn, int ninst, int is32bits, int s1, int s2)
{
MAYUSE(dyn);
if (cpuext.xtheadbb && cpuext.xtheadmemidx) {
if (!is32bits) {
SRLI(s1, xRIP, 48);
BNEZ_safe(s1, (intptr_t)dyn->jmp_next - (intptr_t)dyn->block);
if (dyn->need_reloc) {
TABLE64C(s2, const_jmptbl48);
} else {
MOV64x(s2, getConst(const_jmptbl48));
}
TH_EXTU(s1, xRIP, JMPTABL_START2 + JMPTABL_SHIFT2 - 1, JMPTABL_START2);
TH_LRD(s2, s2, s1, 3);
} else {
TABLE64C(s2, const_jmptbl32);
}
TH_EXTU(s1, xRIP, JMPTABL_START1 + JMPTABL_SHIFT1 - 1, JMPTABL_START1);
TH_LRD(s2, s2, s1, 3);
TH_EXTU(s1, xRIP, JMPTABL_START0 + JMPTABL_SHIFT0 - 1, JMPTABL_START0);
TH_LRD(s1, s2, s1, 3);
} else {
if (!is32bits) {
SRLI(s1, xRIP, 48);
BNEZ_safe(s1, (intptr_t)dyn->jmp_next - (intptr_t)dyn->block);
MOV64x(s2, getConst(const_jmptbl48));
SRLI(s1, xRIP, JMPTABL_START2);
ADDSL(s2, s2, s1, 3, s1);
LD(s2, s2, 0);
} else {
TABLE64C(s2, const_jmptbl32);
}
MOV64x(x4, JMPTABLE_MASK1 << 3);
SRLI(s1, xRIP, JMPTABL_START1 - 3);
AND(s1, s1, x4);
ADD(s2, s2, s1);
LD(s2, s2, 0);
if (JMPTABLE_MASK0 < 2048) {
ANDI(s1, xRIP, JMPTABLE_MASK0);
} else {
MOV64x(x4, JMPTABLE_MASK0); // x4 = mask
AND(s1, xRIP, x4);
}
ADDSL(s2, s2, s1, 3, s1);
LD(s1, s2, 0);
}
return s1;
}
```
### AddRelocTable64JmpTbl
```c
void AddRelocTable64JmpTbl(dynarec_native_t* dyn, int ninst, uintptr_t addr, int pass)
{
if(!dyn->need_reloc)
return;
if(isTable64(dyn, getJumpTableAddress64(addr)))
return; // no need, already handled
// check if addr is in the same mmap area
// else, use a cancel block instead
uintptr_t start;
int ok = 1;
if(!IsAddrFileMapped(dyn->start, NULL, &start))
ok = 0;
else {
uintptr_t end = start + SizeFileMapped(dyn->start);
if(addr<start || addr>=end)
ok = 0;
if(ok && ((addr-start)>=0x100000000LL))
ok = 0;
}
if(!ok) return AddRelocCancelBlock(dyn, ninst, pass);
if(pass<3) {
dyn->reloc_size+=2;
return;
}
uintptr_t delta = addr - start;
reloc_t reloc = {0};
reloc.type = RELOC_TBL64TBLJMPH;
reloc.table64jmptblh.idx = dyn->table64size;
reloc.table64jmptblh.deltah = delta>>24;
dyn->relocs[dyn->reloc_size++] = reloc.x;
reloc.type = RELOC_TBL64TBLJMPL;
reloc.table64jmptbll.deltal = delta&0xffffff;
dyn->relocs[dyn->reloc_size++] = reloc.x;
}
```
```shell
[BOX64] New instruction 22, native=0x3f8d76dc44 (0x154)
[BOX64] New Instruction x64:0x3f0003721b, native:0x3f8d76dc44
[BOX64] 0x3f0003721b: 83 F8 20 CMP Ed, Ib
[BOX64] 0x3f8d76dc44: 5 emitted opcodes, inst=22, barrier=0 state=3/1(1), set=3F/0, use=0, need=0/0, fuse=1, sm=0(0/0), sew@entry=7, sew@exit=7, pred=21, last_ip=0x3f00037214
0200039b ADDIW t2, zero, 0x20(32)
02081e13 SLLI t3, rax_a6, 0x20(32)
020e5e13 SRLI t3, t3, 0x20(32)
02039e93 SLLI t4, t2, 0x20(32)
020ede93 SRLI t4, t4, 0x20(32)
```
```c
MESSAGE(LOG_DUMP, "New Instruction %s:%p, native:%p\n", is32bits?"x86":"x64",(void*)addr, (void*)dyn->block);
```
```
grep -rnw . -e "New Instruction"
```
```c
void print_newinst(dynarec_native_t* dyn, int ninst)
{
dynarec_log(LOG_NONE, "%sNew instruction %d, native=%p (0x%x)%s\n",
(dyn->need_dump > 1) ? "\e[4;32m" : "",
ninst, dyn->block, dyn->native_size,
(dyn->need_dump > 1) ? "\e[m" : "");
}
```
```c
printf_x64_instruction(dyn, rex.is32bits ? my_context->dec32 : my_context->dec, &dyn->insts[ninst].x64, name);
dynarec_log(LOG_NONE, "%s%p: %d emitted opcodes, inst=%d, %s%s\n",
(dyn->need_dump > 1) ? "\e[32m" : "",
(void*)(dyn->native_start + dyn->insts[ninst].address), dyn->insts[ninst].size / 4, ninst, buf, (dyn->need_dump > 1) ? "\e[m" : "");
```
` dynarec_native_t`
----
## Structure
Box64 dynamic blocks do not always stop at branches or jumps, unlike the classic definition of a basic block, which ends at every control flow instruction.
## Execution:
> 1. direct
> 2. indirect
> 3. epilog
Box64 follows the path of the source program's control flow and directly executes the next dynamic block if it is already translated.
Box64 maps already translated source program counters (`RIP`) to dynamic blocks using lookup tables (`box64_jmptbl4`, `box64_jmptbl3`). If a particular program counter hasn't yet been translated, the lookup will fail, triggering dynamic compilation.
Box64 implements dynamic block chaining via the function `jump_to_next`, allowing control to transfer directly from one translated block to another. This mechanism handles both direct and indirect jumps without returning to the emulation dispatcher (i.e., `DBGetBlock`).
For example, the following log shows the translation of the x86 instruction `E8 EB F8 FF FF CALL Id`:
(direct jump)
```shell
[BOX64] New instruction 7, native=0x3f92484014 (0x44)
[BOX64] New Instruction x64:0x3f020a2690, native:0x3f92484014
[BOX64] 0x3f020a2690: E8 EB F8 FF FF CALL Id (=> /root/Downloads/wine/lib/wine/x86_64-unix/win32u.so:wine_dbg_log.constprop.0)
[BOX64] 0x3f92484014: 11 emitted opcodes, inst=7, barrier=0 state=0/3(0), set=0/0, use=80, need=80/0, fuse=0, sm=0(0/0), sew@entry=7, sew@exit=7, pred=6, last_ip=0x3f020a2672
01f813b7 LUI t2, 0x1f81000(33034240)
0513839b ADDIW t2, t2, 0x51(81)
00d39393 SLLI t2, t2, 0xd(13)
69538393 ADDI t2, t2, 0x695(1685)
fe74bc23 SD t2, rsp_s1, 0xfffffff8(-8)
ff848493 ADDI rsp_s1, rsp_s1, 0xfffffff8(-8)
[BOX64] Jump to next
90eb0b13 ADDI rip_s6, rip_s6, 0xfffff90e(-1778)
[BOX64] Table64: 0x3f911afc78
00000e17 AUIPC t3, 0x0(0)
010e3e03 LD t3, t3, 0x10(16)
000e3383 LD t2, t3, 0x0(0)
00038067 JALR zero, t2, 0x0(0)
```
The function may just back to the caller It will back to `DynaRun` if there's no linker here
```shell
[BOX64] 0x3f020a2649: C3 RET
[BOX64] 0x3f92484420: 24 emitted opcodes, inst=8, barrier=0 state=0/3(0), set=0/0, use=80, need=80/0, fuse=0, sm=0(0/0), sew@entry=7, sew@exit=7, pred=7, last_ip=0x3f020a2638
[BOX64] Ret to epilog
0004bb03 LD rip_s6, rsp_s1, 0x0(0)
00848493 ADDI rsp_s1, rsp_s1, 0x8(8)
000b0313 MV t1, rip_s6
36049e37 LUI t3, 0x36049000(906268672)
ee8e0e1b ADDIW t3, t3, 0xfffffee8(-280)
02eb5393 SRLI t2, rip_s6, 0x2e(46)
00339393 SLLI t2, t2, 0x3(3)
007e0e33 ADD t3, t3, t2
000e3e03 LD t3, t3, 0x0(0)
00200eb7 LUI t4, 0x200000(2097152)
ff8e8e9b ADDIW t4, t4, 0xfffffff8(-8)
019b5393 SRLI t2, rip_s6, 0x19(25)
01d3f3b3 AND t2, t2, t4
007e0e33 ADD t3, t3, t2
000e3e03 LD t3, t3, 0x0(0)
007b5393 SRLI t2, rip_s6, 0x7(7)
01d3f3b3 AND t2, t2, t4
007e0e33 ADD t3, t3, t2
000e3e03 LD t3, t3, 0x0(0)
3ffb7393 ANDI t2, rip_s6, 0x3ff(1023)
00339393 SLLI t2, t2, 0x3(3)
007e0e33 ADD t3, t3, t2
000e3383 LD t2, t3, 0x0(0)
00038067 JALR zero, t2, 0x0(0)
```
Note that One dynamic block may have multiple possible exit points, e.g., several RET, tail JMP, or branch targets, all within one translated block.
```
./src/dynarec/rv64/dynarec_rv64_00_3.c:1116: MESSAGE(1, "Jump to %d / 0x%x\n", tmp, tmp);
```
Block ending depends on memory protection, configuration, and ability to merge more code.
## Creation:
1. Check for end-of-block or should-extend-block
## Literal pool
### table64
>[Is it possible to regenerate table64 for a dynablock?](https://github.com/ptitSeb/box64/issues/1711)
## Relocation
## Position-dependent information
## checking all jumps (whether they go to the same block or require linking to another block)
```c
#define BOX64DRENV(name) ((dyn->env && dyn->env->is_##name##_overridden)?dyn->env->name:box64env.name)
```
```c
if (BOX64DRENV(dynarec_callret) && addr >= (dyn->start + dyn->isize)) {
// jumps out of current dynablock...
j64 = getJumpTableAddress64(addr);
if (dyn->need_reloc) AddRelocTable64RetEndBlock(dyn, ninst, addr, STEP);
TABLE64_(x4, j64);
LD(x4, x4, 0);
BR(x4);
}
```
```c
if (BOX64DRENV(dynarec_callret) && addr >= (dyn->start + dyn->isize)) {
// jumps out of current dynablock...
j64 = getJumpTableAddress64(addr);
if (dyn->need_reloc) {
AddRelocTable64JmpTbl(dyn, ninst, addr, STEP);
TABLE64_(x4, j64);
} else {
MOV64x(x4, j64);
}
LD(x4, x4, 0);
BR(x4);
}
```