2

I have studied some articles where I get the information that a system call like open() invokes a wrapper function in glibc and then a trap is raised which switches the context from user space to kernel space and then cpu registers are used to invoke system call parameters/arguments in kernel space.

But still I guess I am missing a step by step procedure or detailed sequence for the system call invocation.It will be great if people can provide steps considering ARM arch as a reference.Thanks in advance.

a.saurabh
  • 1,163
  • 10
  • 15
  • "a trap is raised". This means "jump to a predefined address and switch mode to kernel". That's more or less it. What more details do you need? Which register flips which bit? – n. m. could be an AI Jun 12 '14 at 05:12
  • so is the trap handler consists of a jmp instruction to a label(that means trap handler is an assembly code) or it's a C code and where it resides exactl?Also who exactly calls the trap handler and when? – a.saurabh Jun 12 '14 at 05:19
  • 1
    [This will might be help you](http://unix.stackexchange.com/questions/125343/what-is-meant-by-a-system-call-if-not-the-implementation-in-the-programing-lan) – Jayesh Bhoi Jun 12 '14 at 05:22
  • For security reasons there's a special trap instruction that does both the jump and the switch at once. There is no way to switch to the kernel mode otherwise. There is no label, the jump address is hardwired in the CPU. – n. m. could be an AI Jun 12 '14 at 05:28
  • 1
    On the ARM, it was called *software interrupt*. You know how an interrupt works? The *syscall* is similar, but a *special assembler instruction* triggers the interrupt instead of hardware. The *special assembler* is the part in *glibc*. In [this questions](http://stackoverflow.com/questions/20369440/can-start-be-the-thumb-function), there are two examples of the user space calling `exit()`. One register is a *function number*, a jump table index to get to kernel code. [C Syscalls](http://stackoverflow.com/questions/572942/whats-the-difference-between-c-system-calls-and-c-library-routines) – artless noise Jun 13 '14 at 17:42

2 Answers2

5

Software interrupt exception is used to invoke system calls in case of ARM. It will execute function whose address is stored at physical address 0x08.

Syscall wrapper library function lands to architecture specific implementation of SYSCALL(Check sysdeps/unix directory of libc source code). In our case, syscall from sysdeps/unix/sysv/linux/arm/syscall.S file will be executed. In this function it will store the syscall number in R7 and R0-R6 are used to send arguments to the syscall.

Sample assembly code:

     mov  r7, #SYSCALL NO
     mov  r0, #ARG1
     mov  r1, #ARG2
     swi 0x0

When software exception is generated, vector_swi() <arch/arm/kernel/entry-common.S> is called. This functions gets syscall number from R7, finds and executes the registered function address from sys_call_table.

Check following kernel files for implementation details:

  1. include/linux/syscalls.h
  2. arch/arm/include/asm/unistd.h
  3. arch/arm/kernel/calls.S
  4. arch/arm/kernel/entry_common.S
  5. arch/arm/kernel/sys_arm.c
Sanket Parmar
  • 1,477
  • 12
  • 9
2

There are two parts of the handling of syscalls.

  1. From starting of the syscall and up to making processor exception to enter into previllege mode. For ARM, this mode would be SVC(Supervisor)mode. This part involves filling the general purpose registers with correct system call no, arguments to be passed in the syscall, and triggering the processor's state to privilege mode.
  2. Second(and a bit more complicated as compared to the first one) part involves processing the syscall in privilege mode, and returning from the privilege mode.

2a. In the exception table of ARM the control is directed by the exception vector(SVC in this case) to common handler for the exceptions. Following is the exception vector for SVC:

[File: arch\arm\kernel\entry-armv.S]

W(ldr)  pc, __vectors_start + 0x1000

Following is the entry to common handler of the exceptions:

[File: arch\arm\kernel\entry-armv.S]

vector_\name:
    .if \correction
    sub lr, lr, #\correction
    .endif

2b. At the finish of this common handler, r0,lr and spsr are saved on the on the stack at addresses of [SP], [SP+4] and [SP+8] respectively.

[File: arch\arm\kernel\entry-armv.S]

stmia   sp, {r0, lr}        @ save r0, lr
mrs lr, spsr
str lr, [sp, #8]        @ save spsr

2c. Then, the common handler transfers/branches the controller to SVC specific handler:

[File: arch\arm\kernel\entry-armv.S]

mrs lr, spsr 
...
and lr, lr, #0x0f
...
 ARM(   ldr lr, [pc, lr, lsl #2]    )
movs    pc, lr          @ branch to handler in SVC mode
...
.word   vector_swi

At the end of the common handler, the controller is transferred to SVC handler vector_swi.

2d. Now let's see how syscall is processed in SVC handler. Context of the caller is saved as following after advancing SP at new FRAME in follwing order:

[File: arch\arm\kernel\entry-common.S]

ENTRY(vector_swi)
...

(r0-r12,sp,lr)_usr_mode, lr_exp (exp=svc in this case)

This context will be used to return from the SVC mode to user mode by storing r0-r12,sp,lr,pc by the values of this context.

sub sp, sp, #S_FRAME_SIZE
stmia   sp, {r0 - r12}          @ Calling r0 - r12
ARM(    add r8, sp, #S_PC       )
ARM(    stmdb   r8, {sp, lr}^       )   @ Calling sp, lr
...
mrs r8, spsr            @ called from non-FIQ mode, so ok.
str lr, [sp, #S_PC]         @ Save calling PC

2e. syscall no. is fetched according to procedure calling standards, and utlimately it's stored in scno(r7 register).

[File: arch\arm\kernel\entry-common.S]

elif defined(CONFIG_AEABI)

    /*
     * Pure EABI user space always put syscall number into scno (r7).
     */
#elif defined(CONFIG_ARM_THUMB)
    /* Legacy ABI only, possibly thumb mode. */
    tst r8, #PSR_T_BIT          @ this is SPSR from save_user_regs
    addne   scno, r7, #__NR_SYSCALL_BASE    @ put OS number in
    ...

2f. Then, address of the sys_call_table is stored in tbl register(r8).

[File: arch\arm\kernel\entry-common.S]

adr tbl, sys_call_table     @ load syscall table pointer

sys_call_table contains a list of syscall according to their nos. File call.S contains the list.

[File: arch\arm\kernel\entry-common.S]

ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...

2g. Then the correct syscall handler will be involved by branching to the sys_call_table with the offset of syscall no. The reurn address is set at label "ret_fast_syscall".

[File: arch\arm\kernel\entry-common.S]

adr lr, BSYM(ret_fast_syscall)  @ return address
    ldrcc   pc, [tbl, scno, lsl #2]     @ call sys_* routine
    ...

2h. Then, the user-mode registers are restored in such a way that (r0-r12,sp,lr,pc)_usr_mode are stored from the valued stored in step 2d. This is done in macro "restore_user_regs".

[File: arch\arm\kernel\entry-common.S]

ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...

[File: arch\arm\kernel\entry-header.S]

macro   restore_user_regs, fast = 0, offset = 0
    mov r2, sp
    ...

    .if \fast
    ldmdb   sp, {r1 - r12}          @ get calling r1 - r12
    .else
    ldmdb   sp, {r0 - r12}          @ get calling r0 - r12
    .endif
    add sp, sp, #S_FRAME_SIZE - S_SP
    movs    pc, lr              @ return & move spsr_svc into cpsr
    .endm