Search code examples
c++c++17powerpc

Generic C/C++ function for execution system calls in PowerPC


I want to write a C/C++ function to perform a syscall which takes the syscall value as an argument as well as optional additional arguments.

void execute_system_call(short value, const int *arguments, int argument_count) {
    // TODO: Use arguments
    asm volatile (
    "li 0, %0\n\t"
    "sc\n\t"
    :
    : "i"(0x1337) // TODO: Use variable
    );
}

Compiling the code with a C variable as syscall value instead of hard-coding one results in the following compilation error (so this is something which already doesn't work as intended):

assembly.cpp: In function 'void execute_system_call(short int, const int*, int)':
assembly.cpp:62:3: warning: asm operand 0 probably doesn't match constraints
  );
   ^
assembly.cpp:62:3: error: impossible constraint in 'asm'
make[1]: *** [assembly.o] Error 1
make: *** [build] Error 2

Furthermore, I also need to pass the arguments into the respective registers. In PowerPC, this would usually be r3, r4, ... which appears troublesome as well since I don't want to explicitly specify this and leave register assignment according to the calling conventions to the compiler instead. Maybe the latter isn't possible so I have to write it more like the following pseudo code:

lis 3, $arguments[0]@h # Load higher 16-bit
ori 3, 3, $arguments[0]@l # Load lower 16-bit
lis 4, $arguments[1]@h
ori 4, 4, $arguments[1]@l
lis 5, $arguments[2]@h
ori 5, 5, $arguments[2]@l
...
li 0, $value
sc

Note that it is okay if the argument_count is assumed to always be e.g. 8 and does not have to be variable so the method signature might as well be

void execute_system_call(short value, const int *arguments)

How can this be done?


Solution

  • Assuming your altered code looks like this:

    void execute_system_call(short value, const int *arguments, int argument_count) {
        // TODO: Use arguments
        asm volatile (
        "li 0, %0\n\t"
        "sc\n\t"
        :
        : "i"(value)
        );
    }
    

    - then you're asking gcc to pass the value variable as an immediate (since you're still using "i" as the constraint). Since value is a variable, and not an immediate, this won't work, which is the reason for the error you're seeing there.

    Instead, you'd need to use the "r" input constraint, to refer to the register value:

    void execute_system_call(short value, const int *arguments, int argument_count) {
        // TODO: Use arguments
        asm volatile (
        "li 0, %0\n\t"
        "sc\n\t"
        :
        : "r"(value)
        );
    }
    

    - but this time, the constraints are correct, but the assembly isn't - li takes a register and an immediate, but we want two registers.

    So, we probably want mr ("move register") rather than li:

    void execute_system_call(short value, const int *arguments, int argument_count) {
        // TODO: Use arguments
        asm volatile (
        "mr 0, %0\n\t"
        "sc\n\t"
        :
        : "r"(value)
        );
    }
    

    This should perform the sc instruction with r0 containing value.

    However, it's not quite complete yet. Since your system call probably returns something, you'd also need to describe that in the output constraints of the inline asm. The way that return value is stored will depend on the ABI of the system you're working with.

    Also depending on your system call ABI, the sc instruction may alter other registers, and/or memory. You'd also need to list those in the clobber constriants of the asm statement.

    Then, we have the input arguments. The actual implementation of this will really depend on the function call ABI of your program, and the system call ABI of the supervisor code.

    If these are fairly similar, you may find that it's much easier to implement the execute_system_call as actual ASM rather than C-with-inline-ASM. This way, you don't have to unpack the arguments from C to inline ASM. Otherwise, you'll need to create similar code to place the elements of the arguments array into specific registers.

    And just as a sanity check, does your platform not provide the syscall function? That sounds exactly what you're looking for here.