ARM-Linuxシステム呼び出しツッコミ

13201 ワード

旧式x 86プラットフォーム上のシステム呼び出しはint 0 x 80によって中断されて実現され、その後、新式CPUに対してLinuxはsysenter方式を使用した.ARMプラットフォームでは,swi割り込みを用いてシステム呼び出しのジャンプを実現した.swi命令は、ユーザモードから管理モードへの変換を実現するためのソフトウェア割り込みを生成するために用いる、CPSR(Current Program Status Register、プログラム状態レジスタ、条件フラグビット、割り込み禁止ビット、現在のプロセッサモードフラグ、およびその他の制御および状態ビットを含む)は、管理モードのSPSRに保存される(Saved Program Status Register,プログラム状態保存レジスタ,CPSRの状態を保存して異常復帰後に異常発生時の動作状態を回復させるため)SWIベクトルへの移行を実行し,他のモードでもSWI命令を用いてプロセッサが同様に管理モードに切り替えることができる.命令フォーマットは以下の通りです:SWI{cond}immed_24:immed_24 24ビットの即時数.値は0~167777215の整数です.SWI命令を使用する場合、通常は2つの方法でパラメータ伝達を行い、SWI異常処理プログラムは関連するサービスを提供することができ、この2つの方法はいずれもユーザソフトウェア協定である.SWI異常割込み処理プログラムは、ソフトウェア割込みを引き起こすSWI命令を読み出して、24を即時数とする.1)命令中の24ビットの即時数はユーザ要求のサービスタイプを指定し,パラメータは汎用レジスタを介して伝達される.例えば、MOV R 0,#34 SWI 12 2)コマンドのうちの24ビットの即時数は無視され、ユーザが要求するサービスタイプはレジスタR 0のみが決定し、パラメータは他の汎用レジスタを介して伝達される.例えば、MOV R 0,#12 MOV R 1,#34 SWI 0は、SWI異常処理プログラムにおいて、SWI即時数を除去するステップは、まず、ソフトブレークしたSWI命令が一緒になったときのARM命令かThumb命令かを決定するステップであり、これはSPSRへのアクセスによって得ることができる.次に、LRレジスタにアクセスすることによって得ることができるSWI命令のアドレスを取得する.次に命令を読み出し,即時数(24ビット低い)を分解する.arch/arm/include/asmディレクトリの下でunistd.hファイルでは、Linuxカーネルでは、各システム呼び出しに一意のシステム呼び出し機能番号があり、これらの機能番号の定義はこのファイルにあり、このファイルでは、#define__のような定義が多く見られます.NR_write(_NR_SYSCALL_BASE+4)これはシステム呼び出しwriteの定義であり、機能番号は_NR_SYSCALL_BASE+4、記号__と定義NR_write. 異なるバイナリインタフェースが採用されているため、_NR_SYSCALL_BASE+4の定義は異なり、ファイルに定義が見つかります.
#ifndef __ASM_ARM_UNISTD_H
#define __ASM_ARM_UNISTD_H

#define __NR_OABI_SYSCALL_BASE	0x900000

#if defined(__thumb__) || defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE	0
#else
#define __NR_SYSCALL_BASE	__NR_OABI_SYSCALL_BASE
#endif
あのEABIに注意して、EABIは何ですか?ABI,Application Binary Interface,応用バイナリインタフェース.より新しいEABI仕様では、システム呼び出し番号をレジスタr 7に押し込み、古いOABIでは実行されるswi割り込み番号の方式、すなわち、元の呼び出し方式(Old ABI)は、swi命令に従う呼び出し番号によって行われる.
ここでは主に呼び出し番号の請求に対して異なる方法を定義している.
これらのシステム呼び出し番号に対応するシステム呼び出しリストは、arch/arm/kernelディレクトリのcallsを定義する.S.
/* 0 */	CALL(sys_restart_syscall)
		CALL(sys_exit)
		CALL(sys_fork_wrapper)
		CALL(sys_read)
		CALL(sys_write)
/* 5 */	CALL(sys_open)
		CALL(sys_close)
		CALL(sys_ni_syscall)		/* was sys_waitpid */
		CALL(sys_creat)
		CALL(sys_link)
/*………  ……….*/
ソースコードではsys_writeの関数宣言:
    asmlinkage long sys_write (unsigned int fd, const char __user *buf,size_t count);
asmlinkageはgccラベルで、関数の読み取りによって伝達されたパラメータがスタックにあることを示しています.具体的な役割はリンクを参照してください.http://blog.chinaunix.net/uid-20585891-id-1919646.html
しかし、具体的な実装コードはマクロで定義される.
Linuxはすでに各システム呼び出しに対して唯一のシステム呼び出し機能番号を設定しており、システム呼び出しを実行すると、システム呼び出し番号に従ってシステム呼び出しをインデックスし、いくつかのパラメータ付きマクロで実現され、syscallsに位置する.hファイルには、次のように表示されます.
#define SYSCALL_DEFINE0(name)	   asmlinkage long sys_##name(void)
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
ここでDEFINEnはパラメータの個数を表し、パラメータのあるシステム呼び出しは最終的にこのようなマクロを指す.
#define SYSCALL_DEFINEx(x, sname, ...)  __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
めくり_SYSCALL_DEFINExの定義:
#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
コードを検索すると、次のことがわかります.
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
		size_t, count)
このマクロを展開すると、「
asmlinkage long sys_write (unsigned int fd, const char __user *buf,size_t count);”に表示されます.
PS:~~どうしてこのような形式で定義を説明するのか分からないが、意図不明で憂鬱だ.
    PS:__SC_DECLXのマクロ定義は以下の通りです(/include/linux/syscals.h),_VA_ARGS__可変パラメータマクロです.
#define __SC_DECL1(t1, a1)	t1 a1
#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
#define __SC_DECL4(t4, a4, ...) t4 a4, __SC_DECL3(__VA_ARGS__)
#define __SC_DECL5(t5, a5, ...) t5 a5, __SC_DECL4(__VA_ARGS__)
#define __SC_DECL6(t6, a6, ...) t6 a6, __SC_DECL5(__VA_ARGS__)
では、システムがどのように関数を見つけたのか.
システム呼び出しテーブルの各エントリをcallsに一致させるオフセット量であるシステム呼び出し番号が先に送信される.Sファイルには各エントリに対する宣言があり、システム呼び出しテーブルの定義は、ファイルarch/arm/kernel/entry-armv.S中です.
/*
 * This is the syscall table declaration for native ABI syscalls.
 * With EABI a couple syscalls are obsolete and defined as sys_ni_syscall.
 */
#define ABI(native, compat) native
#ifdef CONFIG_AEABI
#define OBSOLETE(syscall) sys_ni_syscall
#else
#define OBSOLETE(syscall) syscall
#endif

	.type	sys_call_table, #object
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
#undef OBSOLETE
そして
/*
 * Let's declare a second syscall table for old ABI binaries
 * using the compatibility syscall entries.
 */
#define ABI(native, compat) compat
#define OBSOLETE(syscall) syscall

	.type	sys_oabi_call_table, #object
ENTRY(sys_oabi_call_table)
#include "calls.S"
#undef ABI
#undef OBSOLETE
    sys_call_tableはカーネル内でジャンプテーブルであり、システム呼び出し関数のポインタ(sys_open)のような一連の関数ポインタが格納されている.カーネルは、EABIにとってシステム呼び出しテーブルのインデックスであるシステム呼び出し番号に基づいて、実際に呼び出されたカーネルのどの関数を見つけ、その関数を実行することによってシステム呼び出しを完了します.
old ABIの場合、カーネルが与える処理はsys_という単独のsystem call tableを確立することである.oabi_call_table.これにより、互換方式では2つのsystem call tableがあり、old ABI方式のシステム呼び出しでold_が実行されるsyscall_tableテーブルのシステム呼び出し関数、EABI方式のシステム呼び出しはsys_call_tableの関数ポインタ.
構成は以下の4の中から除外されません.
第一に、二つのマクロの配置行為は上記の通りです. 
第二に、CONFIGのみを配置するOABI_COMPAT、それではoldABI方式で呼び出すのはsys_を使うことができますoabi_call_table,EABI方式で呼び出されたsys_call_tableは、1と実質的に同じです.ただ状況1はもっと明確です.
第三に、CONFIGのみを配置するAEABIシステムにsys_が存在しないoabi_call_table、old ABI方式呼び出しに互換性がありません.EABIでしか呼び出せませんsys_call_table.
4つ目と2つ目は構成されていません.システムのデフォルトではold ABI方式しか許可されませんが、old_は存在しません.syscall_table、最終的にsys_を通過しますcall_tableは関数呼び出しを完了します.
ABIに応じて、対応するシステム呼び出しテーブルのベースアドレスがtblレジスタにロードされます.次に検索するプロシージャ.ARM-Linuxカーネル起動時、start_kernel(/init/main.c)->setup_arch(/arch/arm/kernel/setup.c)->paging_init(/arch/arm/mm/nommu.c)->early_trap_Init(/arch/arm/kernel/traps.c)、割り込み異常ベクトルテーブルを初期化します.
void __init early_trap_init(void *vectors_base)
{
	unsigned long vectors = (unsigned long)vectors_base;
	extern char __stubs_start[], __stubs_end[];
	extern char __vectors_start[], __vectors_end[];
	extern char __kuser_helper_start[], __kuser_helper_end[];
	int kuser_sz = __kuser_helper_end - __kuser_helper_start;

	vectors_page = vectors_base;

	/*
	 * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
	 * into the vector page, mapped at 0xffff0000, and ensure these
	 * are visible to the instruction stream.
	 */
	memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
	memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
	memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

	/*
	 * Do processor specific fixups for the kuser helpers
	 */
	kuser_get_tls_init(vectors);

	/*
	 * Copy signal return handlers into the vector page, and
	 * set sigreturn to be a pointer to these.
	 */
	memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
	       sigreturn_codes, sizeof(sigreturn_codes));
	memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
	       syscall_restart_code, sizeof(syscall_restart_code));

	flush_icache_range(vectors, vectors + PAGE_SIZE);
	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
    paging_Init関数で呼び出される伝達パラメータは、early_です.trap_init((void *)CONFIG_VECTORS_BASE);
    early_trap_Initは主に割り込みベクトルテーブル(_vectors_start,_vectors_end)と割り込みエントリ関数テーブル(_stubs_start,_stubs_end)の関連コードcopyをメモリ0 xffff 0000または0 x 0000000に完了する.
この関数はarch/arm/kernel/entry-armvに定義する.Sにおける異常ベクトルテーブルと異常ハンドラのstubを行う
再配置:異常ベクトルテーブルを0 xFFFF_にコピー0000,異常ベクトルハンドラのstubコピー0 xFFFFFF_0200.
modify_を呼び出しますdomain()は、例外ベクトルテーブルが占有するページへのアクセス権を変更し、ユーザーステータスを変更できません.
このページにアクセスするには、コア・ステータスのみがアクセスできます.
異常ベクトルテーブル、ファイルarch/arm/kernel/entry-armv.S中:
   
__vectors_start:
 ARM(	swi	SYS_ERROR0	)
 THUMB(	svc	#0		)
 THUMB(	nop			)
 W(b)	vector_und + stubs_offset
 W(ldr)	pc, .LCvswi + stubs_offset
 W(b)	vector_pabt + stubs_offset
 W(b)	vector_dabt + stubs_offset
 W(b)	vector_addrexcptn + stubs_offset
 W(b)	vector_irq + stubs_offset
 W(b)	vector_fiq + stubs_offset
 .globl	__vectors_end
__vectors_end:
が充填された後、ベクトルテーブルは次のようになります.
    
バーチャルアドレス異常処理コード
    0xffff0000      reset              swi SYS_ERROR0
    0xffff0004      undefined        b __real_stubs_start + (vector_undefinstr - __stubs_start)
0 xffff 0008ソフトウェア中断ldr pc,_real_stubs_start + (.LCvswi - __stubs_start)
0 xffff 000 c取指令異常b_real_stubs_start + (vector_prefetch - __stubs_start)
0 xffff 0010データ異常b_real_stubs_start + (vector_data - __stubs_start)
    0xffff0014      reserved         b __real_stubs_start + (vector_addrexcptn - __stubs_start)
    0xffff0018      irq                 b __real_stubs_start + (vector_IRQ - __stubs_start)
    0xffff001c      fiq                  b __real_stubs_start + (vector_FIQ - __stubs_start)
ソフトブレークswiが発生すると、ジャンプする.LCvswi + stubs_offsetで実行し、
LCvswi定義は、
.LCvswi:
	.word	vector_swi
が最終的にインスタンスvector_を実行するswiはシステム呼び出しの処理を完了し、/arch/arm/kernel/entry-commonを参照する.S下vector_swiの定義.
ENTRY(vector_swi)
	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
 THUMB(	mov	r8, sp			)
 THUMB(	store_user_sp_lr r8, r10, S_SP	)	@ calling sp, lr
	mrs	r8, spsr			@ called from non-FIQ mode, so ok.
	str	lr, [sp, #S_PC]			@ Save calling PC
	str	r8, [sp, #S_PSR]		@ Save CPSR
	str	r0, [sp, #S_OLD_R0]		@ Save OLD_R0
	zero_fp

	/*
	 * Get the system call number.
	 */

#if defined(CONFIG_OABI_COMPAT)
	/*
	 * If we have CONFIG_OABI_COMPAT then we need to look at the swi
	 * value to determine if it is an EABI or an old ABI call.
	 */
#ifdef CONFIG_ARM_THUMB
	tst	r8, #PSR_T_BIT
	movne	r10, #0				@ no thumb OABI emulation
	ldreq	r10, [lr, #-4]			@ get SWI instruction
#else
	ldr	r10, [lr, #-4]			@ get SWI instruction
  A710(	and	ip, r10, #0x0f000000		@ check for SWI		)
  A710(	teq	ip, #0x0f000000						)
  A710(	bne	.Larm710bug						)
#endif //endif "CONFIG_ARM_THUMB"

#ifdef CONFIG_CPU_ENDIAN_BE8
	rev	r10, r10			@ little endian instruction
#endif //endif "CONFIG_CPU_ENDIAN_BE8"

#elif defined(CONFIG_AEABI)

	/*
	 * Pure EABI user space always put syscall number into scno (r7).
	 */
  A710(	ldr	ip, [lr, #-4]			@ get SWI instruction	)
  A710(	and	ip, ip, #0x0f000000		@ check for SWI		)
  A710(	teq	ip, #0x0f000000						)
  A710(	bne	.Larm710bug						)

#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
	ldreq	scno, [lr, #-4]

#else

	/* Legacy ABI only. */
	ldr	scno, [lr, #-4]			@ get SWI instruction
  A710(	and	ip, scno, #0x0f000000		@ check for SWI		)
  A710(	teq	ip, #0x0f000000						)
  A710(	bne	.Larm710bug						)

#endif //endif "CONFIG_OABI_COMPAT"

#ifdef CONFIG_ALIGNMENT_TRAP
	ldr	ip, __cr_alignment
	ldr	ip, [ip]
	mcr	p15, 0, ip, c1, c0		@ update control register
#endif
	enable_irq

	get_thread_info tsk
        //tbl r8      , arch/arm/kernel/entry-header.S   :
        // tbl  .req   r8     @syscall table pointer,
        //             ,          
        adr	tbl, sys_call_table		@ load syscall table pointer

#if defined(CONFIG_OABI_COMPAT)
	/*
	 * If the swi argument is zero, this is an EABI call and we do nothing.
	 *
	 * If this is an old ABI call, get the syscall number into scno and
	 * get the old ABI syscall table address.
	 */
	bics	r10, r10, #0xff000000
	eorne	scno, r10, #__NR_OABI_SYSCALL_BASE
	ldrne	tbl, =sys_oabi_call_table
#elif !defined(CONFIG_AEABI)
        // scno    r7   
	bic	scno, scno, #0xff000000		@ mask off SWI op-code
	eor	scno, scno, #__NR_SYSCALL_BASE	@ check OS number
#endif

	ldr	r10, [tsk, #TI_FLAGS]		@ check for syscall tracing
	stmdb	sp!, {r4, r5}			@ push fifth and sixth args

#ifdef CONFIG_SECCOMP
	tst	r10, #_TIF_SECCOMP
	beq	1f
	mov	r0, scno
	bl	__secure_computing	
	add	r0, sp, #S_R0 + S_OFF		@ pointer to regs
	ldmia	r0, {r0 - r3}			@ have to reload r0 - r3
1:
#endif

	tst	r10, #_TIF_SYSCALL_WORK		@ are we tracing syscalls?
	bne	__sys_trace

	cmp	scno, #NR_syscalls		@ check upper syscall limit
	adr	lr, BSYM(ret_fast_syscall)	@ return address
        //       
	ldrcc	pc, [tbl, scno, lsl #2]		@ call sys_* routine

	add	r1, sp, #S_OFF
        // why r8      
2:	mov	why, #0				@ no longer a real syscall
	cmp	scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
	eor	r0, scno, #__NR_SYSCALL_BASE	@ put OS number back
	bcs	arm_syscall	
	b	sys_ni_syscall			@ not private func
ENDPROC(vector_swi)

その後、関数エントリに移行し、システム呼び出しを実行します.
参照先:http://blog.csdn.net/xiyangfan/article/details/5701673
http://blog.csdn.net/hongjiujing/article/details/6831192
http://blog.chinaunix.net/uid-26316047-id-3402198.html