Linux (Kernel) -Quellenanalyse: Definition der Systemaufruf-Eingabefunktion

Anzahl der zu untersuchenden Versionen

linux-4.2(mainline)

Definition der Systemaufruf-Eingabefunktion

Der Titel wurde in "Definition der Systemaufruf-Eingabefunktion" geändert, aber in Wirklichkeit schreibe ich ein Makro, das die Systemaufruf-Eingabefunktion definiert. (Der Inhalt der Systemaufruf-Eingabefunktion wird nicht geschrieben.)

Wie die Definition des Makros __SYSCALL_DEFINEx, das ich im vorherigen Artikel geschrieben habe (Ich habe mir den Systemaufrufeintrag angesehen (Linux-Quellanalyse)), erweitert. Ich versuchte herauszufinden, ob es getan werden würde.

Dieses Makro definiert eine Systemaufruf-Eingabefunktion, die ziemlich esoterisch war. Ich denke jedoch, dass es einige Implementierungen gab, die das Niveau der C-Sprachkenntnisse verbessern würden.

Schauen wir uns zunächst den Code des setgid-Systemaufrufs als Beispiel an, in dem das Makro tatsächlich verwendet wird. 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;
}

In der Zeile "SYSCALL_DEFINE1 (setgid, gid_t, gid)" gibt es drei Argumente für das Makro "SYSCALL_DEFINE1". In Wirklichkeit ist das Argument von setgid eines von "gid_t gid". Wie Sie der Makrodefinition entnehmen können, ist die grundlegende Makroimplementierung für ein oder mehrere Argumente identisch.

Verwandte Makrodefinition

Die zugehörigen Makrodefinitionen lauten wie folgt. (Diese Makros sind in include \ linux \ syscalls.h definiert.)

#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 schien eine zusätzliche (?) Bedeutung zu haben, daher habe ich beschlossen, sie hier nicht anzusehen. In diesem Artikel konzentrieren wir uns darauf, wie die Eingabefunktion des Systemaufrufs erweitert wird. </ del> SYSCALL_METADATA ist ein Makro zum Einbetten von Metadaten in eine Systemaufruf-Eingabefunktion. (Der Code, der Metadaten einbettet, ist nur gültig, wenn das Makro CONFIG_FTRACE_SYSCALLS definiert ist (Konfigurationseinstellung).) In diesem Artikel konzentrieren wir uns darauf, wie die Eingabefunktion des Systemaufrufs erweitert wird, sodass SYSCALL_METADATA ausgeschlossen wird.

Vom Anfang von \ _ \ _ SYSCALL_DEFINEx, Darüber hinaus gibt es die Makros \ _ \ _ MAP, \ _ \ _ SC_DECL und \ _ \ _ stringify. Diese Definitionen lauten wie folgt.

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

Um das Makro konkret zu erweitern, betrachten wir den Fall von setgid. setgid wird als SYSCALL_DEFINE1 (setgid, gid_t, gid) geschrieben. In diesem Beispiel sehen wir uns an, wie Makros erweitert werden.

Das Erweitern des Makros SYSCALL_DEFINE1 führt zu "SYSCALL_DEFINEx (1, _setgid, gid_t, gid)" Das Erweitern des Makros SYSCALL_DEFINEx und das Ausschließen des Makroteils SYSCALL_METADATA führt zu "__SYSCALL_DEFINEx (1, _setgid, gid_t, gid)". Wenn das Makro __SYSCALL_DEFINEx erweitert wird, sieht es außerdem wie folgt aus.

/*①*/
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))	/*⑧*/

Schauen wir uns den obigen Inhalt von oben an (Link zu den eingekreisten Zahlen in den Kommentaren).

Definitionsinhalt von ①

Wenn Sie nur den Teil von __MAP (1, __SC_DECL, gid_t, gid, Es wird zu MAP1 ( SC_DECL, gid_t, gid) und wird weiter zu __SC_DECL (gid_t, gid). Schließlich wird es "gid_t gid" sein. Wenn Sie dies auf ① anwenden, asmlinkage long sys_setgid(gid_t gid)) __attribute__((alias("SyS_setgid"))); Erweitert auf.

Betrachten Sie nun das Makro \ _ \ _MAP. Das Makro \ _ \ _MAP erweitert den Typ- und Variablennamen um das Makro des zweiten Arguments, trennt sie durch Kommas und erweitert den erweiterten Inhalt um die Anzahl der Kombinationen des ersten Arguments. Es ist verbunden. Im Fall von setgid beträgt die Anzahl der Kombinationen 1, so dass es als Beispiel schwer zu verstehen ist: Das zweite Argument, Makro __SC_DECL, verbindet den Typ- und Variablennamen mit einem leeren Trennzeichen. Wenn die Anzahl der Kombinationen größer als 1 ist, verbinden Sie sie mit einem Komma-Trennzeichen. Infolgedessen wird dies auf den Teil der Variablendefinition der Funktion erweitert. (Die durch das Makro der zweiten Argumentänderung erweiterten Aufzählungsmuster)

② Makroerweiterung

Die nächste Zeile (2) basiert auf dem, was wir oben gesehen haben. Erweitert auf static inline long SYSC_setgid (gid_t gid);.

③ Makroerweiterung

In ③ ist das Makro des zweiten Arguments des Makros \ _ \ _MAP \ _ \ _ SC_LONG. Der Inhalt dieses \ _ \ _ SC_LONG-Makros lautet wie folgt.

\ _ \ _ SC \ _LONG Makrodefinition

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

Das Makro \ _ \ _ same_type ist ein Makro, das zurückgibt, ob die Argumenttypen a und b übereinstimmen. Das Makro \ _ \ _TYPE_IS_LL hat den gleichen Typ von 0LL wie die Umwandlung von 0 mit dem Argument t oder Ein Makro, das zurückgibt, ob der Typ 0ULL mit dem Argument t mit der Umwandlung von 0 übereinstimmt. LL und ULL sind ganzzahlige Darstellungen von "long long" bzw. "unsigned long long". \ _ \ _TYPE_IS_LL gibt also true zurück, wenn der Argumenttyp t entweder "long long" oder "unsigned long long" ist, andernfalls wird false zurückgegeben. Soweit bisher endlich die Definition von \ _ \ _ SC_LONG. \ _ \ _ Builtin_choose_expr ist eine integrierte Funktion von gcc, die den Wert des zweiten Arguments zurückgibt, wenn der Bewertungswert des ersten Arguments wahr ist, und den Wert des dritten Arguments, wenn er falsch ist. (Ich denke, der Unterschied zum ternären Operator besteht wahrscheinlich darin, ob er zur Erstellungszeit oder zur Laufzeit ausgewertet wird.) </ del> (\ _ \ _ builtin_choose_expr scheint auch zur Laufzeit ausgewertet zu werden. Der Unterschied zum ternären Operator ist unbekannt) </ del> (\ _ \ _ Builtin_choose_expr bleibt bestehen, bis es den Präprozessor durchläuft, aber nur der wahre Code (zweites Argument) wird kompiliert, der gefälschte Code (drittes Argument) wird nicht kompiliert. ⇒ Die Bedingung des ersten Arguments wird vom Compiler bestimmt Es kann nur der konstante Wert angegeben werden, der angegeben werden kann. Aus diesen Gründen ist das Makro \ _ \ _ SC_LONG vom Typ 0LL ("long long"), wenn das Argument t entweder "long long" oder "unsigned long long" ist, andernfalls ist es vom Typ 0L. Definieren Sie das Argument a mit (long).

Basierend auf dem oben Gesagten ist gid_t bei Betrachtung der Definition von ③ "unsigned int", also "__SC_LONG (gid_t, gid)" Erweitert auf "Long Gid". Ab diesem Zeitpunkt ist es nicht mehr nur so, als würde man Makros jagen.

Wenn schließlich das Makro von ③ erweitert wird, Es sieht aus wie asmlinkage long SyS_setgid (long gid);.

④ Makroerweiterung

Dies ist das gleiche wie die Entwicklung von ③, Erweitert wie "asmlinkage long SyS_setgid (long gid)".

⑤ Makroerweiterung

In ⑤ ist das Makro des zweiten Arguments des Makros \ _ \ _MAP __SC_CAST. Der Inhalt dieses \ _ \ _ SC_CAST-Makros lautet wie folgt.

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

Das \ _ \ _ SC_CAST-Makro wird zu einer Syntax erweitert, die das Argument a mit dem ersten Argument t umsetzt. Da dies das zweite Argument von \ _ \ _MAP ist, ⑤ wird wie folgt erweitert. long ret = SYSC_setgid((gid_t)gid); Die Funktion SyS_setgid empfängt eine GID als langen Typ und wandelt sie durch Aufrufen von SYSC_setgid in gid_t um.

⑥ Makroerweiterung

In ⑥ ist das Makro des zweiten Arguments des Makros \ _ \ _MAP __SC_TEST. Der Inhalt dieses \ _ \ _ SC_TEST-Makros lautet wie folgt.

\ _ \ _ SC_TEST Makrodefinition

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

\ _ \ _TYPE_IS_LL ist wie oben geschrieben. (Im Makro "\ _ \ _TYPE_IS_LL entspricht der Typ 0LL der Umwandlung von 0 mit dem Argument t oder Ein Makro, das zurückgibt, ob der Typ 0ULL mit dem Argument t mit der Umwandlung von 0 übereinstimmt. ")

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

Das Makro BUILD_BUG_ON_ZERO ist äußerst aufwendig. Ich kann das nicht einfach schreiben. Erstens ist der Teil "!! (e)" die erste Ablehnung des Wertes von e, und wenn der Wert von e nicht 0 ist, ist alles 1. Wenn bei zwei Ablehnungen der Wert von e nicht 0 ist, wird er 1, und wenn er 0 ist, wird er 0. struct {int: - !! (e);} definiert ein Bitfeld in struct. Wenn e 0 ist, ist die Größe des Bitfelds 0, aber wenn e nicht 0 ist, ist die Größe des Bitfelds -1. Ein Kompilierungsfehler wird auftreten. BUILD_BUG_ON_ZERO sucht beim Kompilieren wie folgt nach Fehlern. Da es die Struktur nicht wirklich definiert, ist es in sizeof eingeschlossen.

Wenn Sie das Makro von ⑥ zwangsweise erweitern (in vereinfachter Form erweitern), (sizeof(struct { int:-!!(0); }))。 Wenn Sie versuchen, \ _ \ _builtin_types_compatible_p in den erweiterten Inhalt zu schreiben, ist dies unverständlich. Bitte verzeihen Sie mir. Wenn Sie schreiben, was das Makro \ _ \ _ SC_TEST tut, Wenn der Typ des Arguments t nicht "long long" und "unsigned long long" ist und die Größe größer als sizeof (long) ist Machen Sie einen Kompilierungsfehler. Es ist so, als würden Sie beim Kompilieren nach Änderungen / Ergänzungen an Systemaufrufeinträgen suchen.

⑦ Makroerweiterung

\ _ \ _ SC_ARGS wird im zweiten Argument des Makros \ _ \ _MAP angegeben. Die Definition des Makros \ _ \ _ SC_ARGS lautet wie folgt.

#define __SC_ARGS(t, a)	a

Nur der Argumentname wird erweitert. Die Definition des Makros \ _ \ _ PROTECT lautet wie folgt.

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

Wenn Sie im Fall von setgid mit der Erweiterung fortfahren, __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));

Ich konnte es bereitstellen, habe aber keine Ahnung, was dies bedeutet. Als ich im Internet gesucht habe, habe ich die folgende Seite gefunden.

Gemäß diesem Artikel wird das Schreiben von asm-Anweisungen wie der oben genannten als erweiterte Assemblysyntax bezeichnet. Die Beschreibung lautet wie folgt.

\ _ \ _ Asm \ _ \ _ (Baugruppenvorlage : Ausgabeoperand / * Option * / : Eingabeoperand / * Option * / : Liste der zu zerstörenden Register / * Optionen * / ) In der Assembly-Vorlage werden Assembler-Anweisungen geschrieben. Bei erweiterter Assembly-Syntax unterscheidet sich das Schreiben von Registern usw. jedoch geringfügig vom Schreiben grundlegender Assembler-Anweisungen. Jeder Operand kann ein C-Sprachausdruck sein, der in Operandeneinschränkungszeichen und Klammern eingeschlossen ist.

Das Folgende ist ein Beispiel, das auf derselben Site geschrieben wurde.

int in1=10,in2=5,out1; \ _ \ _ asm \ _ \ _ ("movl% 1, %% eax; / * Baugruppenvorlage * / addl %2, %%eax; movl %%eax, %0;" : "= r" (out1) / * Ausgabe * / : "r" (in1), "r" (in2) / * Eingabe * / : "% eax" / * Zu zerstörendes Register * / );

Basierend auf dem oben Gesagten, wenn Sie sich __asm__ __volatile__ (" ":" = r "(ret):" 0 "(ret)," m "(gid)) ansehen;, Erstens ist der Teil "Baugruppenvorlage" leer. "Output" ist ret und "Input" ist ret und gid. Was sind "= r", "0" und "m"? Dies wurde auch auf der Referenzseite zuvor erläutert. Diese werden als Einschränkungszeichen bezeichnet.

Selbst wenn Sie es sich bisher ansehen, bin ich mir immer noch nicht sicher. Da der Teil "Baugruppenvorlage" leer ist, sollte keine asm-Anweisung ausgeführt werden. Daher sollte es sinnvoll sein, die Variablen in den Operanden "output" und "input" anzugeben. Außerdem: "Durch das Schreiben flüchtiger Anweisungen können asm-Anweisungen entfernt, erheblich verschoben oder zu einer zusammengefasst werden. Sie können das verhindern. ", Ich fühle mich also wie eine Implementierung im Zusammenhang mit der gcc-Optimierung. Als ich online weitere Informationen sammelte, fand ich die folgende Website.

Die Informationen, die ich nur nachschlagen wollte, wurden geschrieben. Ich verstehe es jedoch immer noch nicht ganz. In dem Beispiel, dem wir bisher gefolgt sind, wird der Aufruf der Funktion SYSC_setgid optimiert, um sicherzustellen, dass auf den Inhalt des Arguments gid verwiesen werden kann. Ich denke, es ist eine Abschreckung. Lassen Sie uns etwas mehr Nachforschungen anstellen und diesem Artikel zusätzliche Informationen hinzufügen.

Endlich konnte ich die Entwicklung des Makros sehen, aber es scheint unverdaulich zu sein.

Das nächste Mal werde ich mir den Inhalt des Hauptkörpers der Gabel ansehen, aber vielleicht nehme ich mir etwas Zeit.

... Nach ein wenig mehr Recherche fand ich die folgende Seite.

Auf dieser Seite

Normalerweise ist die Funktion vfs_systemcall Stuck wird verwendet, um Argumente wie eine normale Funktion zu übergeben, jedoch in Systemaufrufen Es wird im Register übergeben. Da es als Register übergeben wird, befindet sich das Argument wie eine normale Funktion auf dem Stapel. Benimmt sich wie gestapelt. Im Falle eines Systemaufrufs hat der Stapel also keine Argumente Das Aufrufen einer normalen Funktion führt zu undefiniertem Verhalten. Dies wird als Tail-Call-Problem bezeichnet. Asmlinkage_protect, um dieses Problem zu vermeiden und sicherzustellen, dass Argumente an vfs_systemcall übergeben werden Ich werde.

Es wurde vielleicht auf der vorherigen Seite geschrieben, aber ich konnte es verstehen, wenn ich mir diese Beschreibung ansah. Wenn Sie SYSC_setgid direkt aufrufen, ohne SyS_setgid zu durchlaufen, werden keine Argumente übergeben. Im Fall von Argument 0 wie fork wurde asmlinkage_protect sicherlich nicht ausgeführt.

Recommended Posts

Linux (Kernel) -Quellenanalyse: Definition der Systemaufruf-Eingabefunktion
Quellanalyse von Linux (Kernel): Systemaufruf
Ich habe mir den Systemaufrufeintrag angesehen. (Linux-Quellenanalyse)