# System call entry: SVC, SYSCALL과 ECALL

v6.18.37 / arch/arm64/kernel/syscall.c

사용자 함수가 커널 함수를 일반 call로 직접 호출하는 것은 아닙니다. arm64의 SVC, x86의 SYSCALL, RISC-V의 ECALL이 권한 경계를 넘긴 뒤 번호를 검사해 서비스 함수를 고릅니다. 아래는 arm64 시스템 호출 번호를 테이블에 연결하고 반환값을 기록하는 함수입니다.

## invoke_syscall

```c

static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
			   unsigned int sc_nr,
			   const syscall_fn_t syscall_table[])
{
	long ret;

	add_random_kstack_offset();

	if (likely(scno < sc_nr)) {
		syscall_fn_t syscall_fn;
		syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];
		ret = __invoke_syscall(regs, syscall_fn);
	} else {
		ret = do_ni_syscall(regs, scno);
	}

	syscall_set_return_value(current, regs, 0, ret);

	/*
	 * This value will get limited by KSTACK_OFFSET_MAX(), which is 10
	 * bits. The actual entropy will be further reduced by the compiler
	 * when applying stack alignment constraints: the AAPCS mandates a
	 * 16-byte aligned SP at function boundaries, which will remove the
	 * 4 low bits from any entropy chosen here.
	 *
	 * The resulting 6 bits of entropy is seen in SP[9:4].
	 */
	choose_random_kstack_offset(get_random_u16());
}

```

### 38행

```c

static void invoke_syscall(struct pt_regs *regs, unsigned int scno,

```

저장 레지스터 프레임과 사용자 시스템 호출 번호를 받습니다.

### 39행

```c

			   unsigned int sc_nr,

```

유효한 테이블 항목 수를 인수로 받습니다. 번호가 이 범위를 넘지 않는지 검사할 기준입니다.

### 40행

```c

			   const syscall_fn_t syscall_table[])

```

현재 ABI에 대응하는 시스템 호출 함수 포인터 테이블을 받습니다. 호환 ABI에는 다른 표가 사용될 수 있습니다.

### 42행

```c

	long ret;

```

서비스 함수의 결과를 보관할 long 변수를 선언합니다.

### 44행

```c

	add_random_kstack_offset();

```

이번 진입에 사용할 커널 스택 offset 무작위화를 적용합니다. 사용자 인수 버퍼의 주소를 바꾸는 작업은 아닙니다.

### 46행

```c

	if (likely(scno < sc_nr)) {

```

번호가 테이블의 항목 수 미만인지 확인합니다. likely는 정상 호출이 흔하다는 컴파일러 힌트이지 검사를 생략하는 명령이 아닙니다.

### 47행

```c

		syscall_fn_t syscall_fn;

```

선택할 함수 포인터를 보관할 변수를 선언합니다.

### 48행

```c

		syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];

```

범위가 확인된 번호에도 array_index_nospec을 적용해 추측 실행의 테이블 범위 밖 접근을 제한합니다.

### 49행

```c

		ret = __invoke_syscall(regs, syscall_fn);

```

선택한 시스템 호출 wrapper에 regs를 전달하고 결과를 받습니다.

### 50행

```c

	} else {

```

번호가 유효한 범위를 벗어난 경우 처리 경로로 갈립니다.

### 51행

```c

		ret = do_ni_syscall(regs, scno);

```

미구현 또는 호환 처리 경로를 통해 해당 번호의 결과를 얻습니다. 임의 주소를 함수로 실행하지 않습니다.

### 54행

```c

	syscall_set_return_value(current, regs, 0, ret);

```

현재 task의 사용자 복귀 레지스터 상태에 결과를 기록합니다. 실제 사용자 레지스터 복원은 뒤의 진입 복귀 코드가 맡습니다.

### 65행

```c

	choose_random_kstack_offset(get_random_u16());

```

다음 호출에서 쓸 무작위 스택 offset을 선택합니다. 주석처럼 정렬 제약 때문에 난수 모든 비트가 유효한 위치 변화로 남는 것은 아닙니다.

