4

I implemented syscall macro to invoke software interrupt calls. It was working fine for many syscalls. However, it was not for fork(). The return pid is the same for both parent and child process. The snippet is as below:

#define SYSCALL0(N) ({                                          \
    register int ip asm("ip") = N;                              \
    register int r0 asm("r0");                                  \
    asm volatile("swi 0x80" : "=r"(r0) : "r"(ip) : "memory");   \
    r0;                                                         \
})

int main(int argc, char * argv[]) {
   NSLog(@"--beginning of program\n");
    int counter = 0;
    pid_t pid = SYSCALL0(2);
    if (pid == 0) {
        NSLog(@"pid = %d", getpid());
        // child process
        for (int i = 0; i < 3; ++i)
            NSLog(@"child process: counter=%d\n", ++counter);
    }
    else if (pid > 0)  {
        NSLog(@"pid = %d", getpid());
        // parent process
        for (int i = 0; i < 3; ++i)
            NSLog(@"parent process: counter=%d\n", ++counter);
    }
    else {
        // fork failed
        NSLog(@"fork() failed!\n");
        return 1;
    }
    NSLog(@"--end of program--\n");
}

Output:

2015-10-11 21:29:43.666 Training[2564:907] --beginning of program
2015-10-11 21:29:43.669 Training[2564:907] pid = 2650
2015-10-11 21:29:43.670 Training[2564:907] parent process: counter=1
2015-10-11 21:29:43.670 Training[2564:907] parent process: counter=2
2015-10-11 21:29:43.669 Training[2564:907] pid = 2650
2015-10-11 21:29:43.671 Training[2564:907] parent process: counter=3
2015-10-11 21:29:43.671 Training[2564:907] --end of program--
2015-10-11 21:29:43.671 Training[2564:907] parent process: counter=1
2015-10-11 21:29:43.672 Training[2564:907] parent process: counter=2
2015-10-11 21:29:43.673 Training[2564:907] parent process: counter=3
2015-10-11 21:29:43.674 Training[2564:907] --end of program--

The tested environment is a jail broken iOS (it will not run on non-jailbroken) running on armv7. I think I might not have done enough with the return of the swi call, so it could not return 0 to indicate child process. What did I miss? How do I get it work correctly?

Krypton
  • 3,337
  • 5
  • 32
  • 52

2 Answers2

2

You don't do syscalls that way. The userland wrappers for syscalls do much more work than that, as you just noticed. So either find the source code for your operating system, figure out how the system calls are wrapped and do the same, or just use the wrappers that have been conveniently provided for you.

I'm pretty sure that iOS still does syscalls the BSD way. I happen to know that fork uses the two return values syscall interface, one register contains if the process is a child process, the other one contains the return value.

Also, your wrapper probably doesn't do error handling correctly (that's usually signalled through a flag you have to handle specially). And at least some operating systems (don't know if iOS is one of them) know exactly which registers are saved in the syscall wrappers and don't bother saving them, so inlining syscalls like that may wreck your registers.

Art
  • 19,807
  • 1
  • 34
  • 60
  • The problem is I can't seem to find any source code, so I implemented it my own by look at this page https://www.theiphonewiki.com/wiki/Kernel_Syscalls – Krypton Oct 07 '15 at 07:18
  • If you dig around the web you might find some old source for Darwin which should be close enough. But yeah, the idea is that you shouldn't be doing this yourself. What's wrong with using the properly wrapped system calls your operating system provides you? – Art Oct 07 '15 at 07:56
  • I found the darwin code for it. Hopefully it will work, let's see. – Krypton Oct 07 '15 at 08:09
  • The code is here https://github.com/darwin-on-arm/xnu/blob/master/libsyscall/custom/__fork.s but I'm not sure how to convert this `MI_GET_ADDRESS(r3, __current_pid)` into inline asm code. – Krypton Oct 07 '15 at 10:23
2

I figure it out. Turns out that after svc call, r1 is set as indicator if subsequent code is child or parent. The snippet is changed as below:

#include <dlfcn.h>
int svc_fork() {
    register int r3 asm("r3") = (int)dlsym(RTLD_DEFAULT, "_current_pid");
    register int r12 asm("r12") = 2;
    register int r1 asm("r1") = 1;
    register int r0 asm("r0");
    asm volatile("swi   0x80                        \n"
                 "cmp   r1, #0                      \n"
                 "beq   Lparent                     \n"
                 "mov   r0, #0                      \n"
                 "str r0, [r3]                      \n"
                 "Lparent:                          \n"
                 : "=r"(r0)
                 : "r"(r1), "r"(r12), "r"(r3)
                 : "memory");
    return r0;
}
Krypton
  • 3,337
  • 5
  • 32
  • 52