0

So, I created a JIT code generation library for C++, and tried writing some useful code with it. I'm quite new to assembly programming, so its really hard for me to figure everything out by myself

Thing is, I created a Brainfuck-to-x64 compiler. One of Brainfuck's instructions (.) puts a character into stdout: I implemented it getting the address of putchar on C++ like this:

#include <cstdio>
uint_least64_t putchar_address = (uint_least64_t)&std::putchar;

And then generating code that would call the stored address on r10, example:

    movq   r10,     0x7ffe7ef1ab40
    ...
    call   r10

This works fine, however, I was thinking there might be a way to jump to that address without needing to waste a register through the whole program (and also having to push/pop the register before cdecl calls because these clobber r10, r11, etc)

Taking a look at compiled stdlib code shows they use things like call qword ptr [0xsomeaddress]?

Basically I need to hardcode the address on the call somehow, if that's even possible. Thanks in advance!

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Aureal
  • 77
  • 10
  • JIT your code into a buffer with 2GiB of the target so you can use `call rel32`. Otherwise use a RIP-relative addressing mode to access a function pointer in memory, like normal shared library calls. [Call an absolute pointer in x86 machine code](//stackoverflow.com/q/19552158) and [Handling calls to (potentially) far away ahead-of-time compiled functions from JITed code](//stackoverflow.com/q/54947302) BTW, use `void *` or a function pointer type for `putchar_address`. Or at least `uintptr_t`. – Peter Cordes Feb 06 '20 at 04:19
  • 1
    I don't see why you wouldn't just keep the `putchar` address in a call-preserved register like r12 across many calls, though, so you only have to set it up once. (Restore that register at the end of your JITed function. – Peter Cordes Feb 06 '20 at 04:20
  • Thanks for the info! I used `uint_least64_t` instead of an actual pointer because its supposed to be appended to the function as bytecode. And yeah, I could use a call preserved register, but its not just **one** function I would use. Brainfuck also has a `,` instruction which would call `getchar`, so there's that – Aureal Feb 06 '20 at 04:32
  • The term you're looking for is "machine code". "bytecode" implies it will be interpreted by software (e.g. Java or Python). Anyway, so what? Both major x86-64 calling conventions have several call-preserved registers, including RBX and RBP which could avoid a REX prefix on the `call` instruction. If you want compact machine code, use 2 registers. Otherwise ideally put it in range for `call rel32`. – Peter Cordes Feb 06 '20 at 04:41

0 Answers0