It doesn't work because the call instruction is incorrect. Actually there is no CALL absolute_address
instruction on the x86.
In your example you generate following X86 code:
FF 15 xx xx xx xx
which is an indirect call to the adress xx xx xx xx. This will take the address found at xx xx xx xx and do the call there.
Example
FF 15 10 20 30 00
This will look at the adress 0x302010 :
00302010: 11 22 33 00 xx xx xx xx
where it finds the value 0x00332211 and the calls the function at that address.
With the following modifications in assembler_emit_call
the program works fine.
void assembler_emit_call(uint32_t value) {
// CALL opcode
instructionBuffer[pos++] = 0xb8; // mov eax, address
// Address as little endian
instructionBuffer[pos++] = (value >> 0) & 0xFF;
instructionBuffer[pos++] = (value >> 8) & 0xFF;
instructionBuffer[pos++] = (value >> 16) & 0xFF;
instructionBuffer[pos++] = (value >> 24) & 0xFF;
instructionBuffer[pos++] = 0xff ; // call eax
instructionBuffer[pos++] = 0xd0 ;
instructionBuffer[pos++] = 0xc3 ; // ret
}
BTW
instructionBuffer[pos++] = (value >> 0) & 0xFF;
instructionBuffer[pos++] = (value >> 8) & 0xFF;
instructionBuffer[pos++] = (value >> 16) & 0xFF;
instructionBuffer[pos++] = (value >> 24) & 0xFF;
can be replaced by
*(DWORD*)(instructionBuffer + pos) = value ;
pos += 4 ;