If I was writing a normal compiler, I couldsimply write the desired binary to a file, to be run later by the system in whatever manner it usually would be, but how would a JIT compiler do that during runtime, running the new code within the same process that created it?
There is no reason a JIT compiler couldn't do the same thing as well. In fact, that is exactly what the MJIT compiler in the YARV Ruby VM does: it generates C code, writes it to a file, calls GCC to compile that file into a library, then links the library into the current process.
And it turns out this is actually a lot more efficient than it sounds!
Or are there some other functions available in low level languages to facilitate this?
This is not really a feature of the language, more of the host platform. Most modern platforms have a concept which allows dynamically linking code into a process, in Windows, those are called "DLL", in macOS "dyld", in Java "JAR" or "class file", and so on.
Would it simply write the raw bytes into an array of contiguous memory, cast that memory/arrays pointer as a function pointer, and call it?
That is another possibility.
A third possibility is that most modern languages have a way of calling "external" code (often called a "Foreign Function Interface" or "FFI"). So, you could just use the standard FFI API to call that generated code.
A fourth possibility is that many language implementations use native code somewhere, so they need to have a way of interacting with that code anyway. For example, in the Oracle HotSpot JVM, the low-level functionality is implemented in C++ and called as native code. (For example, the code that implements the iadd
bytecode is written in C++, not in Java, and is executed natively.) So, the language implementation must already have a way of calling native code anyway.