Skip to content

Commit

Permalink
Re-enable support for local calls on Windows (x86_64)
Browse files Browse the repository at this point in the history
Re-enabling required properly handling the home space on the stack when
using the fastcall calling convention.

Note: There is an open issue about stack alignment requirements now
falling on the shoulders of the ebpf compiler (or program writer).

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
  • Loading branch information
hawkinsw committed Jun 14, 2023
1 parent 91f1e6f commit 56ce00a
Showing 1 changed file with 27 additions and 24 deletions.
51 changes: 27 additions & 24 deletions vm/ubpf_jit_x86_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ static int register_map[REGISTER_MAP_SIZE] = {
RBP,
};
#else
#define BPF_LOCAL_CALL_SUPPORTED TRUE
#define RCX_ALT R9
static int platform_nonvolatile_registers[] = {RBP, RBX, R13, R14, R15};
static int platform_parameter_registers[] = {RDI, RSI, RDX, RCX, R8, R9};
Expand Down Expand Up @@ -112,8 +111,17 @@ emit_local_call(struct jit_state* state, uint32_t target_pc)
emit_push(state, map_register(BPF_REG_7));
emit_push(state, map_register(BPF_REG_8));
emit_push(state, map_register(BPF_REG_9));
#if defined(_WIN32)
/* Windows x64 ABI requires home register space */
/* Allocate home register space - 4 registers */
emit_alu64_imm32(state, 0x81, 5, RSP, 4 * sizeof(uint64_t));
#endif
emit1(state, 0xe8); // e8 is the opcode for a CALL
emit_jump_offset(state, target_pc);
#if defined(_WIN32)
/* Deallocate home register space - 4 registers */
emit_alu64_imm32(state, 0x81, 0, RSP, 4 * sizeof(uint64_t));
#endif
emit_pop(state, map_register(BPF_REG_9));
emit_pop(state, map_register(BPF_REG_8));
emit_pop(state, map_register(BPF_REG_7));
Expand Down Expand Up @@ -166,30 +174,34 @@ translate(struct ubpf_vm* vm, struct jit_state* state, char** errmsg)

uint64_t ubpf_stack_configuration_delta = UBPF_STACK_SIZE;
/*
* Assuming that the stack is 16-byte aligned when
* we start executing the jit'd code, we need to maintain
* that invariant. The UBPF_STACK_SIZE is guaranteed to be
* divisible by 16. However, if we pushed an odd number of
* Assuming that the stack is 16-byte aligned right before
* the call insn that brought us to this code, when
* we start executing the jit'd code, we need to regain a 16-byte
* alignment. The UBPF_STACK_SIZE is guaranteed to be
* divisible by 16. However, if we pushed an even number of
* registers on the stack when we are saving state (see above),
* then we have violated the invariant. So, we'll have to
* make an additional tweak to rectify the situation.
* then we have to add an additional 8 bytes to get back
* to a 16-byte alignment.
*/
if (sizeof(platform_nonvolatile_registers) % 16 != 0) {
if (!(_countof(platform_nonvolatile_registers) % 2)) {
ubpf_stack_configuration_delta += 0x8;
}

/* Allocate stack space */
emit_alu64_imm32(state, 0x81, 5, RSP, ubpf_stack_configuration_delta);

// TODO: https://github.com/iovisor/ubpf/issues/285
// Windows crashes when we emit the extra call instruction and a helper function
// calls AcquireSRWLockExclusive/ReleaseSRWLockExclusive. RCA of the crash is
// still unknown. So, we disable the local calls for now.
#if defined(BPF_LOCAL_CALL_SUPPORTED)
#if defined(_WIN32)
/* Windows x64 ABI requires home register space */
/* Allocate home register space - 4 registers */
emit_alu64_imm32(state, 0x81, 5, RSP, 4 * sizeof(uint64_t));
#endif

/*
* Use a call to set up a place where we can land after eBPF program's
* final EXIT call. This will make JIT of BPF EXIT call easier in the
* presence of calls to local functions.
* final EXIT call. This makes it appear to the ebpf programs
* as if they are called like a function. It is their responsibility
* to deal with the non-16-byte aligned stack pointer that goes along
* with this pretense.
*/
emit1(state, 0xe8);
emit4(state, 5);
Expand All @@ -198,7 +210,6 @@ translate(struct ubpf_vm* vm, struct jit_state* state, char** errmsg)
* after the eBPF program is finished executing.
*/
emit_jmp(state, TARGET_PC_EXIT);
#endif

for (i = 0; i < vm->num_insts; i++) {
struct ebpf_inst inst = ubpf_fetch_instruction(vm, i);
Expand Down Expand Up @@ -555,21 +566,13 @@ translate(struct ubpf_vm* vm, struct jit_state* state, char** errmsg)
emit_jcc(state, 0x84, TARGET_PC_EXIT);
}
}
#if defined(BPF_LOCAL_CALL_SUPPORTED)
else if (inst.src == 1) {
target_pc = i + inst.imm + 1;
emit_local_call(state, target_pc);
}
#endif
break;
case EBPF_OP_EXIT:
#if defined(BPF_LOCAL_CALL_SUPPORTED)
emit_ret(state);
#else
if (i != vm->num_insts - 1) {
emit_jmp(state, TARGET_PC_EXIT);
}
#endif
break;

case EBPF_OP_LDXW:
Expand Down

0 comments on commit 56ce00a

Please sign in to comment.