analyse de source linux (noyau): définition de la fonction d'entrée d'appel système

Nombre de versions à étudier

linux-4.2(mainline)

Définition de la fonction d'entrée d'appel système

Le titre a été changé en "Définition de la fonction d'entrée d'appel système", mais en réalité, j'écris une macro qui définit la fonction d'entrée d'appel système. (Le contenu de la fonction d'entrée d'appel système n'est pas écrit)

Comment la définition de la macro __SYSCALL_DEFINEx que j'ai écrite dans l'article que j'ai posté plus tôt (J'ai regardé l'entrée d'appel système. (Analyse des sources Linux)) est étendue. J'ai essayé de savoir si cela serait fait.

Cette macro définit une fonction d'entrée d'appel système, qui était assez ésotérique. Cependant, je pense qu'il y avait pas mal d'implémentations qui augmenteraient le niveau de compétences en langage C.

Tout d'abord, examinons le code de l'appel système setgid comme un exemple dans lequel la macro est réellement utilisée. kernel\sys.c(386): SYSCALL_DEFINE1(setgid, gid_t, gid)

/*
 * setgid() is implemented like SysV w/ SAVED_IDS
 *
 * SMP: Same implicit races as above.
 */
SYSCALL_DEFINE1(setgid, gid_t, gid)
{
	struct user_namespace *ns = current_user_ns();
	const struct cred *old;
	struct cred *new;
	int retval;
	kgid_t kgid;

	kgid = make_kgid(ns, gid);
	if (!gid_valid(kgid))
		return -EINVAL;

	new = prepare_creds();
	if (!new)
		return -ENOMEM;
	old = current_cred();

	retval = -EPERM;
	if (ns_capable(old->user_ns, CAP_SETGID))
		new->gid = new->egid = new->sgid = new->fsgid = kgid;
	else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid))
		new->egid = new->fsgid = kgid;
	else
		goto error;

	return commit_creds(new);

error:
	abort_creds(new);
	return retval;
}

En regardant la ligne SYSCALL_DEFINE1 (setgid, gid_t, gid), il y a trois arguments pour la macro SYSCALL_DEFINE1, En réalité, l'argument de setgid est l'un de gid_t gid. Comme vous pouvez le voir dans la définition de macro, l'implémentation de base de la macro est la même pour un ou plusieurs arguments.

Définition de macro associée

Les définitions de macro associées sont les suivantes. (Ces macros sont définies dans include \ linux \ syscalls.h)

#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__)

#define SYSCALL_DEFINEx(x, sname, ...)				\
	SYSCALL_METADATA(sname, x, __VA_ARGS__)			\
	__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
		__attribute__((alias(__stringify(SyS##name))));		\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

SYSCALL_METADATA semblait avoir une signification auxiliaire (?), j'ai donc décidé de ne pas l'examiner ici. Dans cet article, nous allons nous concentrer sur la façon dont la fonction d'entrée de l'appel système est étendue. </ del> SYSCALL_METADATA est une macro pour incorporer des métadonnées dans une fonction d'entrée d'appel système. (Le code qui intègre les métadonnées n'est valide que lorsque la macro CONFIG_FTRACE_SYSCALLS est définie (paramètre de configuration)) Dans cet article, nous nous concentrerons sur la façon dont la fonction d'entrée de l'appel système est étendue, nous allons donc exclure SYSCALL_METADATA.

En regardant depuis le début de \ _ \ _ SYSCALL_DEFINEx, En outre, il existe des macros \ _ \ _ MAP, \ _ \ _ SC_DECL et \ _ \ _ stringify. Ces définitions sont les suivantes.

#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)

#define __SC_DECL(t, a)	t a
#define __TYPE_IS_L(t)	(__same_type((t)0, 0L))
#define __TYPE_IS_UL(t)	(__same_type((t)0, 0UL))
#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL))
#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
#define __SC_CAST(t, a)	(t) a
#define __SC_ARGS(t, a)	a
#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))
include\linux\stringify.h(9): #define __stringify_1(x...)	#x
include\linux\stringify.h(10): #define __stringify(x...)	__stringify_1(x)

Afin de développer concrètement la macro, regardons le cas de setgid. setgid s'écrit SYSCALL_DEFINE1 (setgid, gid_t, gid). Dans cet exemple, voyons comment les macros sont développées.

Le développement de la macro SYSCALL_DEFINE1 entraîne SYSCALL_DEFINEx (1, _setgid, gid_t, gid) Le développement de la macro SYSCALL_DEFINEx et l'exclusion de la partie macro SYSCALL_METADATA aboutit à «__SYSCALL_DEFINEx (1, _setgid, gid_t, gid)». En outre, lorsque la macro __SYSCALL_DEFINEx est développée, elle devient comme suit.

/*①*/
asmlinkage long sys_setgid(__MAP(1,__SC_DECL,gid_t,gid))
	__attribute__((alias(__stringify(SyS_setgid))));
/*②*/
static inline long SYSC_setgid(__MAP(1,__SC_DECL,gid_t,gid));	
/*③*/
asmlinkage long SyS_setgid(__MAP(1,__SC_LONG,gid_t,gid));
/*④*/
asmlinkage long SyS_setgid(__MAP(1,__SC_LONG,gid_t,gid))
{
	/*⑤*/
	long ret = SYSC_setgid(__MAP(1,__SC_CAST,gid_t,gid));
	/*⑥*/
	__MAP(1,__SC_TEST,gid_t,gid);
	/*⑦*/
	__PROTECT(1, ret,__MAP(1,__SC_ARGS,gid_t,gid));
	return ret;
}
static inline long SYSC_setgid(__MAP(1,__SC_DECL,gid_t,gid))	/*⑧*/

Regardons le contenu ci-dessus du haut (lien vers les nombres encerclés écrits dans les commentaires).

Contenu de la définition de ①

Si vous développez uniquement la partie de __MAP (1, __SC_DECL, gid_t, gid, Il devient __MAP1 (__ SC_DECL, gid_t, gid), et devient ensuite __SC_DECL (gid_t, gid), Finalement, ce sera gid_t gid. Si vous appliquez ceci à ①, asmlinkage long sys_setgid(gid_t gid)) __attribute__((alias("SyS_setgid"))); Développé à.

Considérons maintenant la macro \ _ \ _MAP. La macro \ _ \ _MAP développe le type et le nom de la variable avec la macro du deuxième argument, les sépare par des virgules et développe le contenu développé du nombre de combinaisons du premier argument. C'est connecté. Dans le cas de setgid, le nombre de combinaisons est de 1, il est donc difficile à comprendre comme exemple, La deuxième macro d'argument __SC_DECL relie le type et le nom de la variable séparés par des blancs, donc (si le nombre de combinaisons est supérieur à 1) les connecte séparés par des virgules. Par conséquent, cela s'étend à la partie de définition de variable de la fonction. (Les modèles énumérés développés par la macro du deuxième changement d'argument)

② Extension de macro

La ligne suivante (2) est basée sur ce que nous avons vu ci-dessus. Développé en static inline long SYSC_setgid (gid_t gid);.

③ Extension de macro

Dans ③, la macro du deuxième argument de la macro \ _ \ _MAP est \ _ \ _ SC_LONG. Le contenu de cette macro \ _ \ _ SC_LONG est le suivant.

\ _ \ _ SC \ _LONG Définition de macro

#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL))
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

La macro \ _ \ _ same_type est une macro qui renvoie si les types d'arguments a et b correspondent. La macro \ _ \ _TYPE_IS_LL correspond au type de 0LL avec le cast de 0 avec l'argument t, ou Une macro qui renvoie si le type de 0ULL correspond au cast de 0 avec l'argument t. LL et ULL sont des représentations entières de «long long» et «unsigned long long», respectivement. Donc \ _ \ _TYPE_IS_LL retourne true si le type d'argument t est soit long long ou ʻunigned long long, sinon il retourne false. En regardant jusqu'ici, enfin la définition de \ _ \ _ SC_LONG. \ _ \ _ Builtin_choose_expr est une fonction intégrée de gcc qui renvoie la valeur du deuxième argument si la valeur d'évaluation du premier argument est vraie et la valeur du troisième argument si elle est fausse. <del> (je pense que la différence avec l'opérateur ternaire est probablement de savoir s'il est évalué au moment de la construction ou à l'exécution) </ del> <del> (\ _ \ _ builtin_choose_expr semble également être évalué au moment de l'exécution. La différence avec l'opérateur ternaire est inconnue) </ del> (\ _ \ _ Builtin_choose_expr reste jusqu'à ce qu'il passe par le préprocesseur, mais seul le vrai code (deuxième argument) est compilé, le faux code (troisième argument) n'est pas compilé ⇒ La condition du premier argument est déterminée par le compilateur Seule la valeur const qui peut être spécifiée peut être spécifiée) De ces choses, la macro \ _ \ _ SC_LONG est de type 0LL (long long) si l'argument t est soit long long ou ʻunigned long long, sinon il est de type 0L. Définissez l'argument a avec (long).

Sur la base de ce qui précède, en regardant la définition de ③, gid_t est ʻunigned int, donc __SC_LONG (gid_t, gid) ʻest Développé en "long gid". À ce stade, ce n'est plus juste comme chasser des macros.

Enfin, lorsque la macro de ③ est développée, ʻAsm linkage long SyS_setgid (long gid); `

④ Extension de macro

C'est la même chose que le développement de ③, Développé comme ʻasmlinkage long SyS_setgid (long gid) `.

⑤ Extension de macro

En ⑤, la macro du deuxième argument de la macro \ _ \ _MAP est __SC_CAST. Le contenu de cette macro \ _ \ _ SC_CAST est le suivant.

#define __SC_CAST(t, a)	(t) a

La macro \ _ \ _ SC_CAST est développée en une syntaxe qui convertit l'argument a avec le premier argument t. Puisqu'il s'agit du deuxième argument de \ _ \ _MAP, ⑤ est développé comme suit. long ret = SYSC_setgid((gid_t)gid); La fonction SyS_setgid reçoit un gid en tant que type long et le convertit en gid_t en appelant SYSC_setgid.

⑥ Extension de macro

En ⑥, la macro du deuxième argument de la macro \ _ \ _MAP est __SC_TEST. Le contenu de cette macro \ _ \ _ SC_TEST est le suivant.

\ _ \ _ Définition de macro SC_TEST

#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))

\ _ \ _TYPE_IS_LL est comme je l'ai écrit ci-dessus. (Dans la macro "\ _ \ _TYPE_IS_LL, le type de 0LL correspond au cast de 0 avec l'argument t, ou Une macro qui renvoie si le type de 0ULL correspond au cast de 0 avec l'argument t. ")

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))

La macro BUILD_BUG_ON_ZERO est extrêmement élaborée. Je ne peux pas écrire cela facilement. Premièrement, la partie !! (e) est le premier refus de la valeur de e, et si la valeur de e est différente de 0, tout sera 1. Avec deux dénégations, si la valeur de e est différente de 0, elle devient 1, et si elle est 0, elle devient 0. struct {int: - !! (e);} définit un champ de bits dans struct. Lorsque e est 0, la taille du champ de bits est 0, mais lorsque e est différent de 0, la taille du champ de bits est -1. Une erreur de compilation se produira. BUILD_BUG_ON_ZERO vérifie les erreurs au moment de la compilation comme ceci. Puisqu'il ne définit pas réellement la structure, il est inclus dans sizeof.

Si vous développez de force la macro de ⑥ (développez sous une forme simplifiée), (sizeof(struct { int:-!!(0); }))。 Si vous essayez d'écrire \ _ \ _builtin_types_compatible_p dans le contenu développé, ce sera incompréhensible, alors pardonnez-moi. Après tout, si vous écrivez ce que fait la macro \ _ \ _ SC_TEST, Lorsque le type de l'argument t est autre que «long long» et «unsigned long long» et que la taille est supérieure à sizeof (long) Faites une erreur de compilation. C'est comme vérifier les modifications / ajouts aux entrées des appels système au moment de la compilation.

⑦ Extension de macro

\ _ \ _ SC_ARGS est spécifié dans le deuxième argument de la macro \ _ \ _MAP. La définition de la macro \ _ \ _ SC_ARGS est la suivante.

#define __SC_ARGS(t, a)	a

Seul le nom de l'argument est développé. La définition de la macro \ _ \ _ PROTECT est la suivante.

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define asmlinkage_protect(n, ret, args...) \
	__asmlinkage_protect##n(ret, ##args)
#define __asmlinkage_protect_n(ret, args...) \
	__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args)
#define __asmlinkage_protect0(ret) \
	__asmlinkage_protect_n(ret)
#define __asmlinkage_protect1(ret, arg1) \
	__asmlinkage_protect_n(ret, "m" (arg1))
#define __asmlinkage_protect2(ret, arg1, arg2) \
	__asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2))
#define __asmlinkage_protect3(ret, arg1, arg2, arg3) \
	__asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3))
#define __asmlinkage_protect4(ret, arg1, arg2, arg3, arg4) \
	__asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \
			      "m" (arg4))
#define __asmlinkage_protect5(ret, arg1, arg2, arg3, arg4, arg5) \
	__asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \
			      "m" (arg4), "m" (arg5))
#define __asmlinkage_protect6(ret, arg1, arg2, arg3, arg4, arg5, arg6) \
	__asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \
			      "m" (arg4), "m" (arg5), "m" (arg6))

Si vous procédez à l'expansion dans le cas de setgid, __PROTECT(1, ret,__MAP(1,__SC_ARGS,gid_t,gid));__PROTECT(1, ret,gid);asmlinkage_protect(1,ret,gid);asmlinkage_protect_1(ret,gid);asmlinkage_protect_n(ret, "m" (gid));__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), "m" (gid));

J'ai pu le déployer, mais je n'ai aucune idée de ce que cela signifie. Lorsque j'ai cherché sur le net, j'ai trouvé le site suivant.

Selon cet article, l'écriture d'instructions asm comme celle ci-dessus est appelée syntaxe d'assemblage étendue. La description est la suivante.

\ _ \ _ asm \ _ \ _ (modèle d'assemblage : Opérande de sortie / * option * / : Opérande d'entrée / * option * / : Liste des registres à détruire / * Options * / ) Les instructions de l'assembleur sont écrites dans le modèle d'assemblage, mais dans le cas d'une syntaxe d'assemblage étendue, la façon d'écrire des registres, etc. est légèrement différente de la façon d'écrire des instructions d'assemblage de base. Chaque opérande peut être une expression en langage C entre des caractères de contrainte d'opérande et des parenthèses.

Ce qui suit est un exemple écrit sur le même site.

int in1=10,in2=5,out1; \ _ \ _ asm \ _ \ _ ("movl% 1, %% eax; / * Modèle d'assemblage * / addl %2, %%eax; movl %%eax, %0;" : "= r" (out1) / * Sortie * / : "r" (in1), "r" (in2) / * Entrée * / : "% eax" / * Registre à détruire * / );

Sur la base de ce qui précède, si vous regardez __asm__ __volatile__ (" ":" = r "(ret):" 0 "(ret)," m "(gid));, Tout d'abord, la partie "gabarit d'assemblage" est vide. "Output" est ret et "Input" est ret et gid. De plus, que signifient "= r", "0" et "m"? Cela a également été expliqué dans le site de référence plus tôt. Celles-ci sont appelées caractères de contrainte.

Même si vous le regardez jusqu'à présent, je ne suis toujours pas sûr. Puisque la partie "modèle d'assemblage" est vide, il ne doit y avoir aucune instruction asm à exécuter. Il devrait donc être logique de spécifier les variables dans les opérandes "sortie" et "entrée". De plus, «En écrivant des instructions volatiles, les instructions asm peuvent être supprimées, déplacées de manière significative ou combinées en une seule. Vous pouvez empêcher cela. ", Je me sens donc comme une implémentation liée à l'optimisation gcc. Lorsque j'ai rassemblé plus d'informations en ligne, j'ai trouvé le site suivant.

Les informations que je voulais juste rechercher étaient écrites. Cependant, je ne le comprends toujours pas pleinement. Dans le cas de l'exemple que nous avons suivi jusqu'à présent, l'appel à la fonction SYSC_setgid est optimisé pour s'assurer que le contenu de l'argument gid peut être référencé. Je pense que c'est un élément dissuasif. Faisons un peu plus de recherche et ajoutons des informations supplémentaires à cet article.

J'ai enfin pu voir le développement de la macro, mais cela me semble indigeste.

La prochaine fois, j'examinerai le contenu du corps principal de fork, mais je vais peut-être prendre un peu de temps.

... Après un peu plus de recherche, j'ai trouvé le site suivant.

Dans ce site

Normalement, la fonction vfs_systemcall est Stuck est utilisé pour passer des arguments comme une fonction normale, mais dans les appels système Il sera passé dans le registre. Puisqu'il est passé en tant que registre, l'argument est sur la pile comme une fonction normale. Se comporte comme s'ils étaient empilés. Dans le cas d'un appel système, la pile n'a pas d'arguments, donc L'appel d'une fonction normale entraînera un comportement indéfini. C'est ce qu'on appelle le problème de l'appel de queue. Asmlinkage_protect pour éviter ce problème et s'assurer que les arguments sont passés à vfs_systemcall Je le ferai.

Il a peut-être été écrit sur le site précédent, mais je pourrais le comprendre en regardant cette description. Si vous appelez SYSC_setgid directement sans passer par SyS_setgid, aucun argument ne sera passé. Certes, dans le cas de l'argument 0 comme fork, asmlinkage_protect n'a pas été fait.

Recommended Posts

analyse de source linux (noyau): définition de la fonction d'entrée d'appel système
analyse de la source linux (noyau): appel système
J'ai jeté un coup d'œil à l'entrée d'appel système. (analyse des sources Linux)