2

I found many examples to run Hello World syscall using asm but none that talked about calling a syscall with return values. My code is

int my_syscall(int sn)
{
    int output_variable = 0;
    int input_variable = sn;
    __asm__ volatile(
    "mov r7,%1\n\t"
    "svc 0\n\t"
    "mov %0,r0\n\t"
    :"=r"(output_variable)
    :"r"(input_variable)
    );
    return output_variable;
}

Running this on ARM linux(BeagleBone Black) gives me a segmentation fault.

old_timer
  • 69,149
  • 8
  • 89
  • 168
Arjun K V
  • 29
  • 3
  • Which syscall is that supposed to be? Run it through `strace`. Also, you clobber (at least) `r0` and `r7` without telling the compiler. If you need things in specific registers, use register variables, not `mov` instructions. – Jester Oct 31 '17 at 23:01
  • As Jester says, using `register int retval asm("r0")` to control which register `"=r"` picks [is a supported use of `asm` register variables](https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html) for machines where there is no specific-register constraint. You don't need any `mov` instructions inside your asm, and should try to avoid them. – Peter Cordes Oct 31 '17 at 23:27
  • did you try real assembly first? start there get that working then if absolutely necessary then use inline – old_timer Nov 01 '17 at 14:34
  • [Start as thumb](https://stackoverflow.com/questions/20369440/can-start-be-the-thumb-function), [klibc common](https://git.kernel.org/pub/scm/libs/klibc/klibc.git/tree/usr/klibc/arch/arm/syscall.S) and [shims](https://git.kernel.org/pub/scm/libs/klibc/klibc.git/tree/usr/klibc/arch/arm/sysstub.ph), [static library arm cross compile](https://stackoverflow.com/questions/24616226/how-can-i-select-a-static-library-to-be-linked-while-arm-cross-compiling)... A working example of `write()` is in the last link which you can make a crude 'print' facility with. `write("Hello World")` returns a value. – artless noise Nov 03 '17 at 20:07

1 Answers1

0

Fix : The fix is to use "-fomit-frame-pointer" directive when compiling using arm-linux-gnueabihf-gcc (probably for other flavors as well).

$arm-linux-gnueabihf-gcc {YOUR FILE NAME}.c -fomit-frame-pointer -o {YOUR OUTPUT FILE NAME}

Thank you guys for all the comments. So, for the record, the above code logic in the question, works. As far as I understand it, the issue was that the compiler for some reason uses r7 as frame pointer. You can keep adding arguments as input and create a more general purpose syscall implementation using inline assembly. Consider what people have said in the comments for better implementations.

Pseudocode

returntype my_syscall(int syscallnumber,type arg1,type arg2,..)
{
    int output_variable = 0;
    __asm__ volatile
    (
        "mov r7,[sysnum]\n\t"
        "mov r0,[var1]\n\t"
        "mov r1,[var2]\n\t"
                             //continue similarly for more input arguments etc 
        "svc 0\n\t"
        "mov %0,r0\n\t"      //Return value of syscall will be available in r0 for ARM EABI
        :"=r"(output_variable)
        :[sysnum]"r"(syscallnumber),[var1]"r"(arg1),[var2]"r"(arg2) //and so on for other arguments
        :"memory" //clobber others based on usage
    );
    return output_variable;
}

Backstory (For anyone interested) : The same code(the one which is present in the question) with slight variations worked when I implemented a simple "Hello World" string write (changes included different syscall number and arguments). But it wouldn't work when I called my custom syscall which returned a needed value (Alternatively, you can design the syscall to return values using arguments instead of return instruction to avoid this whole issue. I didn't do it because then I would have to recompile the whole kernel but I digress). I tested my syscall with inbuilt syscall() function and it worked perfectly. But MY implementation of the syscall() function was unable to call MY custom syscall. I tried register variables , I tried different instructions and clobbered r0, memory etc.(before I asked the question here) but nothing seemed to work. Then I tried clobbering r7 and the compiler said you can't do that. So that is when I found out the r7 register was being used for something else as well. When I googled the error I found the above fix. Ironically, after the fix it worked without clobbering r7 or anything else. lol.

Arjun K V
  • 29
  • 3
  • This inline assembly is incorrect: the compiler might decide to use `r7` for `arg1` and then you will overwrite it with the system call number. It probably won't but you haven't told the compiler that you're using it in the assembly. See http://www.ethernut.de/en/documents/arm-inline-asm.html for a good overview of inline assembly in ARM and how to use it safely. – ayke Feb 05 '19 at 17:36
  • You're missing clobbers on `r7`,`r0`, and `r1`. See [ARM inline asm: exit system call with value read from memory](https://stackoverflow.com/q/37358451) and [How to specify an individual register as constraint in ARM GCC inline assembly?](https://stackoverflow.com/q/3929442) for ways to do this which don't suck. You call it pseudocode, but it would be better to just show the correct clobbers for this example instead of expecting beginners to know how to expand it "based on usage". – Peter Cordes Aug 11 '23 at 00:35