Ich habe mir den Systemaufrufeintrag angesehen. (Linux-Quellenanalyse)

Anzahl der zu untersuchenden Versionen

linux-4.2(mainline)

Woher liest du?

Sehr groß. Zuerst weiß ich nicht, wo ich lesen soll.

Ich habe darüber nachgedacht, wo ich es lesen soll, aber ich habe das Gefühl, dass es sich mehr ausbreitet, wenn Sie von Fork oder Exec aus eingeben, anstatt zu booten. Ich meine, ich frage mich, ob ich von hier aus lesen möchte. Also habe ich mich entschlossen, zunächst herauszufinden, was der Systemeintrag sagt. Als ich im Internet gesucht habe, habe ich die folgende Seite gefunden.

Die Systemaufruf-Eintragstabelle scheint sys_call_table zu sein. Definition von sys_call_table


#undef __SYSCALL
#define __SYSCALL(nr, call) [nr] = (call),
void *sys_call_table[NR_syscalls] = {
        [0 ... NR_syscalls-1] = sys_ni_syscall,
#include <asm/unistd.h>
};     

Der Inhalt der Eintragstabelle befindet sich in "#include <asm / unistd.h>". Schau dir das an. Davor gibt es in der Definition von sys_call_table "[0 ... NR_syscalls-1] = sys_ni_syscall". Auch __SYSCALL (nr, call) [nr] = (call),. Dies sollte der Code sein, der die Elemente des Arrays initialisiert / festlegt. Lassen Sie uns mit einem kleinen einfachen Beispielcode experimentieren.

#include <stdio.h>

#define TBLSIZ  10
int     tbl[TBLSIZ] = {
	[0 ... TBLSIZ-1] = 123,
	[7] = 777,
};
 
int main()
{
	int     i, c = sizeof(tbl) / sizeof(tbl[0]);
	for (i = 0; i < c; i++) {
		printf("tbl[%d]=%d\n", i, tbl[i]);
	}
}      

Das Ausführungsergebnis ist wie folgt.


kou77@ubuntu:~/test$ gcc tes015.c
kou77@ubuntu:~/test$ ./a.out
tbl[0]=123
tbl[1]=123
tbl[2]=123
tbl[3]=123
tbl[4]=123
tbl[5]=123
tbl[6]=123
tbl[7]=777
tbl[8]=123
tbl[9]=123

sys_call_table ist ein Array von void *, aber da der Einstellungswert im Beispielcode leicht zu verstehen ist, habe ich ihn zu einem Array von int gemacht. Infolgedessen, wie ich erwartet hatte. (Index 7 enthält 777, andernfalls ist 123 enthalten)

Zurück zur Untersuchung, beziehen Sie sich auf die Definition von "#include <asm / unistd.h>". Es ist zu erwarten, dass jeder Eintrag das Makro __SYSCALL verwendet.

Wenn Sie versuchen, die Definition von asm / unistd.h zu überprüfen, finden Sie im Ordner arch viele Dateien von asm / unistd.h.

./arch/unicore32/include/uapi/asm/unistd.h
./arch/powerpc/include/asm/unistd.h
./arch/powerpc/include/uapi/asm/unistd.h
./arch/tile/include/asm/unistd.h
./arch/tile/include/uapi/asm/unistd.h
./arch/nios2/include/uapi/asm/unistd.h
./arch/openrisc/include/uapi/asm/unistd.h
./arch/microblaze/include/asm/unistd.h
./arch/microblaze/include/uapi/asm/unistd.h
./arch/arm/include/asm/unistd.h
./arch/arm/include/uapi/asm/unistd.h
./arch/c6x/include/uapi/asm/unistd.h
./arch/xtensa/include/asm/unistd.h
./arch/xtensa/include/uapi/asm/unistd.h
./arch/parisc/include/asm/unistd.h
./arch/parisc/include/uapi/asm/unistd.h
./arch/mips/include/asm/unistd.h
./arch/mips/include/uapi/asm/unistd.h
./arch/x86/include/asm/unistd.h
./arch/x86/include/uapi/asm/unistd.h
./arch/m32r/include/asm/unistd.h
./arch/m32r/include/uapi/asm/unistd.h
./arch/h8300/include/uapi/asm/unistd.h
./arch/s390/include/asm/unistd.h
./arch/s390/include/uapi/asm/unistd.h
./arch/hexagon/include/uapi/asm/unistd.h
./arch/mn10300/include/asm/unistd.h
./arch/mn10300/include/uapi/asm/unistd.h
./arch/metag/include/asm/unistd.h
./arch/metag/include/uapi/asm/unistd.h
./arch/avr32/include/asm/unistd.h
./arch/avr32/include/uapi/asm/unistd.h
./arch/sparc/include/asm/unistd.h
./arch/sparc/include/uapi/asm/unistd.h
./arch/sh/include/asm/unistd.h
./arch/sh/include/uapi/asm/unistd.h
./arch/blackfin/include/asm/unistd.h
./arch/blackfin/include/uapi/asm/unistd.h
./arch/m68k/include/asm/unistd.h
./arch/m68k/include/uapi/asm/unistd.h
./arch/frv/include/asm/unistd.h
./arch/frv/include/uapi/asm/unistd.h
./arch/score/include/uapi/asm/unistd.h
./arch/arm64/include/asm/unistd.h
./arch/arm64/include/uapi/asm/unistd.h
./arch/cris/include/asm/unistd.h
./arch/cris/include/arch-v32/arch/unistd.h
./arch/cris/include/uapi/asm/unistd.h
./arch/cris/include/arch-v10/arch/unistd.h
./arch/ia64/include/asm/unistd.h
./arch/ia64/include/uapi/asm/unistd.h
./arch/alpha/include/asm/unistd.h
./arch/alpha/include/uapi/asm/unistd.h
./arch/arc/include/uapi/asm/unistd.h
./include/asm-generic/unistd.h
./include/uapi/asm-generic/unistd.h
./include/uapi/linux/unistd.h

Da arch wahrscheinlich architekturabhängige Dateien enthält, werde ich sie anhand von Dateien suchen, auf die häufig verwiesen wird. Erstens ist asm / unistd.h der Inhalt der Definition von sys_call_table, und es scheint, dass die Beschreibung mit dem Makro __SYSCALL aufgelistet ist. Wenn Sie sich ./include/asm-generic/unistd.h ansehen,

#include <uapi/asm-generic/unistd.h>
#include <linux/export.h>
 
/*
 * These are required system calls, we should
 * invert the logic eventually and let them
 * be selected by default.
 */
#if __BITS_PER_LONG == 32
#define __ARCH_WANT_STAT64
#define __ARCH_WANT_SYS_LLSEEK
#endif 

Fahren Sie mit ./include/uapi/asm-generic/unistd.h und ./include/linux/export.h fort. ./Include/uapi/asm-generic/unistd.h ist etwas groß, da Systemaufrufeinträge definiert werden sollten. Es gibt Definitionen wie Makros in ./include/linux/export.h, und es scheint keine Beschreibung des Inhalts von sys_call_table zu geben. Abgesehen davon finden Sie hier einen Auszug aus dem Code für ./include/uapi/asm-generic/unistd.h.

#include <asm/bitsperlong.h>
 
/*
 * This file contains the system call numbers, based on the
 * layout of the x86-64 architecture, which embeds the
 * pointer to the syscall in the table.
 *
 * As a basic principle, no duplication of functionality
 * should be added, e.g. we don't use lseek when llseek
 * is present. New architectures should use this file
 * and implement the less feature-full calls in user space.
 */
 
#ifndef __SYSCALL
#define __SYSCALL(x, y)
#endif
 
#if __BITS_PER_LONG == 32 || defined(__SYSCALL_COMPAT)
#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _32)
#else
#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _64)
#endif
 
#ifdef __SYSCALL_COMPAT
#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _comp)
#define __SC_COMP_3264(_nr, _32, _64, _comp) __SYSCALL(_nr, _comp)
#else
#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _sys)
#define __SC_COMP_3264(_nr, _32, _64, _comp) __SC_3264(_nr, _32, _64)
#endif
/*Unterwegs weggelassen ...*/
#define __NR_uselib 1077
__SYSCALL(__NR_uselib, sys_uselib)
#define __NR__sysctl 1078
__SYSCALL(__NR__sysctl, sys_sysctl)
 
#define __NR_fork 1079
#ifdef CONFIG_MMU
__SYSCALL(__NR_fork, sys_fork)
#else
__SYSCALL(__NR_fork, sys_ni_syscall)
#endif /* CONFIG_MMU */
/*Folgendes wird weggelassen ...*/     

In der Definition des Fork-Eintrags sehen wir define in CONFIG_MMU. Ich weiß nicht, wie die (make) -Konfiguration aussehen wird, aber es ist unwahrscheinlich, dass sich die Verzweigung nicht im Systemaufrufeintrag befindet. Für normal funktionierendes Linux kann CONFIG_MMU definiert werden.

Die Definition von sys_ni_syscall lautet übrigens wie folgt. kernel\sys_ni.c(14): asmlinkage long sys_ni_syscall(void)

/*
 * Non-implemented system calls get redirected here.
 */
asmlinkage long sys_ni_syscall(void)
{
        return -ENOSYS;
}

Das Obige ist die Funktion, die aufgerufen wird, wenn der Eintrag nicht implementiert ist. Lassen Sie uns die registrierte Funktion anhand des Gabeleintrags im obigen Code-Auszug als Beispiel überprüfen.

Definition von sys_fork

Ich habe mit sys_fork gesucht, aber ich kann nichts finden, was eine Funktionsdefinition zu sein scheint.

kou77@ubuntu:~/linux-4.2$ find . \( -name \*.c -o -name \*.h \) -exec grep 'sys_fork' {} /dev/null \;
./arch/openrisc/include/asm/syscalls.h:asmlinkage long __sys_fork(void);
./arch/openrisc/include/asm/syscalls.h:#define sys_fork __sys_fork
./arch/mips/kernel/syscall.c:save_static_function(sys_fork);
./arch/x86/um/sys_call_table_64.c:#define stub_fork sys_fork
./arch/x86/include/generated/asm/syscalls_32.h:__SYSCALL_I386(2, sys_fork, stub32_fork)
./arch/sparc/kernel/process_32.c: *       sys_fork invocation and when we reach here
./arch/arm64/include/asm/unistd32.h:__SYSCALL(__NR_fork, sys_fork)
./include/linux/syscalls.h:asmlinkage long sys_fork(void);
./include/uapi/asm-generic/unistd.h:__SYSCALL(__NR_fork, sys_fork)

Es gibt eine Definition unter arch, aber es gibt keine Definition des Funktionskörpers. Bogen wird anders sein. Als Ergebnis der Suche wurde angenommen, dass dies bei Makros usw. wahrscheinlich nicht direkt sichtbar ist, und Folgendes wurde gefunden. ./kernel/fork.c:SYSCALL_DEFINE0(fork)

Die ganze Definition

#ifdef __ARCH_WANT_SYS_FORK
SYSCALL_DEFINE0(fork)
{
#ifdef CONFIG_MMU
        return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);
#else
        /* can not support in nommu mode */
        return -EINVAL;
#endif
}
#endif 

Die Definition des Makros SYSCALL_DEFINE0 lautet wie folgt. ./include/linux/syscalls.h:178:#define SYSCALL_DEFINE0(sname) \

#define SYSCALL_DEFINE0(sname)                                  \
        SYSCALL_METADATA(_##sname, 0);                          \
        asmlinkage long sys_##sname(void)      

Mit Ausnahme von SYSCALL_METADATA ist der asmlinkage long sys _ ## sname (void) Teil Am Beispiel von SYSCALL_DEFINE0 (Fork) wird asmlinkage long sys_fork (void) ersetzt. Schließlich fand ich die Definition der Funktion sys_fork. asmlinkage kann von einer im C-Compiler geschriebenen Quelle aufgerufen und referenziert werden, wenn diese in C ++ kompiliert wird. Es scheint eine Beschreibung zu sein, mit der das Symbol ausgeschrieben werden kann. Die Definition von asmlinkage lautet wie folgt.

./tools/lib/lockdep/uinclude/linux/lockdep.h:13:#define asmlinkage
./arch/x86/include/asm/linkage.h:10:#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
./arch/mn10300/include/asm/linkage.h:15:#define asmlinkage
./arch/ia64/include/asm/linkage.h:6:#define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage))
./include/linux/linkage.h:21:#define asmlinkage CPP_ASMLINKAGE

Beurteilt, dass die Definition von ./include/linux/linkage.h allgemein gültig ist. Für x86 ist die Definition von #define asmlinkage CPP_ASMLINKAGE attribute ((regparm (0))) möglicherweise gültig. Ich weiß es nicht. regparm scheint Argumente an Register zu übergeben. (Lassen Sie uns separat prüfen)

Die Definition von CPP_ASMLINKAGE lautet wie folgt.

#ifdef __cplusplus
#define CPP_ASMLINKAGE extern "C"
#else
#define CPP_ASMLINKAGE
#endif 

Gabel (Körper) Code

Werfen wir einen kurzen Blick auf den Code des Hauptteils der Gabel. Die Definition von _do_fork ist wie folgt. ./kernel/fork.c:1679:long _do_fork(unsigned long clone_flags,

/*
 *  Ok, this is the main fork-routine.
 *
 * It copies the process, and if successful kick-starts
 * it and waits for it to finish using the VM if required.
 */
long _do_fork(unsigned long clone_flags,
              unsigned long stack_start,
              unsigned long stack_size,
              int __user *parent_tidptr,
              int __user *child_tidptr,
              unsigned long tls)
{
        struct task_struct *p;
        int trace = 0;
        long nr;
 
        /*
         * Determine whether and which event to report to ptracer.  When
         * called from kernel_thread or CLONE_UNTRACED is explicitly
         * requested, no event is reported; otherwise, report if the event
         * for the type of forking is enabled.
         */
        if (!(clone_flags & CLONE_UNTRACED)) {
                if (clone_flags & CLONE_VFORK)
                        trace = PTRACE_EVENT_VFORK;
                else if ((clone_flags & CSIGNAL) != SIGCHLD)
                        trace = PTRACE_EVENT_CLONE;
                else
                        trace = PTRACE_EVENT_FORK;
 
                if (likely(!ptrace_event_enabled(current, trace)))
                        trace = 0;
        }
 
        p = copy_process(clone_flags, stack_start, stack_size,
                         child_tidptr, NULL, trace, tls);
        /*
         * Do this prior waking up the new thread - the thread pointer
         * might get invalid after that point, if the thread exits quickly.
         */
        if (!IS_ERR(p)) {
                struct completion vfork;
                struct pid *pid;
 
                trace_sched_process_fork(current, p);
 
                pid = get_task_pid(p, PIDTYPE_PID);
                nr = pid_vnr(pid);
 
                if (clone_flags & CLONE_PARENT_SETTID)
                        put_user(nr, parent_tidptr);
 
                if (clone_flags & CLONE_VFORK) {
                        p->vfork_done = &vfork;
                        init_completion(&vfork);
                        get_task_struct(p);
                }
 
                wake_up_new_task(p);
 
                /* forking complete and child started to run, tell ptracer */
                if (unlikely(trace))
                        ptrace_event_pid(trace, pid);
 
                if (clone_flags & CLONE_VFORK) {
                        if (!wait_for_vfork_done(p, &vfork))
                                ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
                }
 
                put_pid(pid);
        } else {
                nr = PTR_ERR(p);
        }
        return nr;
}

Dies scheint sicherlich der Hauptteil zu sein. In diesem Artikel ist die Gabelanalyse nicht das Ziel, daher werde ich nicht weiter gehen. Fork hat keine Argumente, aber welche Funktion ist im Eintrag des Systemaufrufs registriert, der ein Argument enthält?

Nehmen wir ein Beispiel für setgid, das eine relativ einfache Implementierung zu sein scheint.

/*
 * setgid() is implemented like SysV w/ SAVED_IDS
 *
 * SMP: Same implicit races as above.
 */
SYSCALL_DEFINE1(setgid, gid_t, gid)
{  
/*Folgendes wird weggelassen ...*/    
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)    
#define SYSCALL_DEFINEx(x, sname, ...)                          \
        SYSCALL_METADATA(sname, x, __VA_ARGS__)                 \
        __SYSCALL_DEFINEx(x, sname, __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__))  

Das ist zäh. Um kurz in den untersuchten Bereich zu schreiben, wird im Fall von setgid das Makro mit dem folgenden Gefühl erweitert.

Ich habe es noch nicht im Detail betrachtet, aber die Art und Weise, __MAP zu definieren, ist sehr interessant. Die Implementierung hier ist interessant, daher kann ich sie in einem anderen Artikel erläutern, nachdem ich den Inhalt etwas genauer verstanden habe.

Recommended Posts

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