c++assemblyarm64asmjit

Illegal Instruction Error on AArch64 Linux Using AsmJit for a quite Simple C++ Code


I am attempting to dynamically generate and execute ARM64 assembly code using GCC and the AsmJit library on an aarch64 Linux system. I consistently encounter an "illegal instruction" error, even with very basic programs. Here is an example of the code that's causing the issue:

#include <stdio.h>
#include "asmjit.h"
#include "a64.h"
using namespace asmjit;

typedef int (*SumFunc)(int, int);

int main() {
  // Signature of the generated function.

  for (int nop_num = 0; nop_num < 10; nop_num += 1) {
    JitRuntime rt;    // Create a runtime specialized for JIT.
    CodeHolder code;  // Create a CodeHolder.

    code.init(
        rt.environment());    // Initialize code to match the JIT environment.
    a64::Assembler a(&code);  // Create and attach a64::Assembler to code.

    // a64::Gp s1 = a64::w0;
    // a64::Gp s2 = a64::w1;
    // a64::Gp result = a64::w11;
    a.add(a64::w0, a64::w0, a64::w1);
    a.ret(a64::w0);

    SumFunc fn;
    Error err = rt.add(&fn, &code);  // Add the generated code to the runtime.
    if (err) return 1;  // Handle a possible error returned by AsmJit.
    int final_result = fn(10, 20);  // Execute our constructed function fn
    printf("%d", final_result);
    rt.release(fn);  // Explicitly remove the function from the runtime
    code.reset();
  }
  return 0;
}

Why does even this simple ARM64 assembly code trigger an "illegal instruction" error on my aarch64 Linux system? Do I need to configure or modify any settings in AsmJit to adapt it to the aarch64 architecture?

Steps I've Tried I have confirmed that my system is aarch64 architecture and that the GCC compiler settings are correct. I've tried other code snippets from the AsmJit examples and encountered the same issue. I've consulted AsmJit's documentation for relevant configuration options or known compatibility issues but haven't found any clear leads. I'm hoping to find a way to properly use AsmJit on aarch64 Linux or to understand the potential reasons behind this issue. Thanks for any help you can provide!


Solution

  • The problem is in your ret instruction:

    a.ret(a64::w0);
    

    This is invalid. Firstly, because you are using a 32-bit register to specify a return address (it must be 64-bit), and secondly ret instruction expects a register holding the address where to return, not a return value.

    You need to first study calling conventions if you want to generate code for AArch64. And one note from my experience - Apple did some changes to the default AArch64 calling convention so you will need to handle that as well.

    So the correct return instruction would be:

    a.ret(a64::x30)
    

    Maybe you got confused by AsmJit's Compiler tool, which usually uses ret() as an intrinsic, but here you are using the Assembler directly, which doesn't provide any intrinsics (what you emit is what you get).

    BTW: There is is a recommendations section in AsmJit documentation (https://asmjit.com/doc/index.html) which suggests to use Logger and ErrorHandler. When you emit an invalid instruction the error handler would have it nicely formatted, so you will know exactly what it is, and if you put a breakpoint to your error handler, you will know exactly which part of your code triggered the error. AsmJit has a lot of tooling that can make your life much easier when you use it.