Linux system calls syscall_ Detail of define

Posted by bronzemonkey on Sun, 16 Jan 2022 09:07:13 +0100

Linux source code can go here https://mirrors.edge.kernel.org/pub/linux/kernel/ Download. This article is based on linux-2.6.34. The code of the old version is simpler and easier to understand.

Little friends who have learned Linux system programming should know that the entry functions of Linux system calls in the kernel are sys_xxx, but if we search with the kernel source code, we will find that we can't find sys at all_ The function definition of XXX is because all the functions corresponding to Linux system calls are defined by SYSCALL_DEFINE related macros. SYSCALL_ The macro related to define is implemented as follows:

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

You can guess the syscall here without looking at the following code_ x in definex (the seven macros SYSCALL_DEFINE0~6 here) represents the number of system call parameters. Next, continue to look at syscall_ Implementation of define macro:

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

This is another alternative macro definition. Continue to look down__ SYSCALL_ Implementation of define macro:

#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__));		\
	static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__));	\
	asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__))		\
	{								\
		__SC_TEST##x(__VA_ARGS__);				\
		return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__));	\
	}								\
	SYSCALL_ALIAS(sys##name, SyS##name);				\
	static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))

As you can see__ SYSCALL_ There are a lot of things defined in the define macro, which looks a little miscellaneous. Let's take socket as an example. sys_ The socket function is declared in linux-2.6.34 \ include \ Linux \ syscalls H, as follows:

asmlinkage long sys_socket(int, int, int);

You can see that it has three parameters, corresponding to the previous SYSCALL_DEFINE3, we are in linux-2.6.34 \ net \ socket C the following definitions can be found:

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
    code...
}

You can see that syscall is indeed used_ Define3, which ## represents a string connector in a macro__ VA_ARGS__ Represents the variable parameters in... Above. Expand the above code as follows:

SYSCALL_DEFINEx(3, _socket, int, family, int, type, int, protocol)
{
    code...
}

After further expansion, the results are as follows:

	asmlinkage long sys_socket(__SC_DECL3(__VA_ARGS__));		\
	static inline long SYSC_socket(__SC_DECL3(__VA_ARGS__));	\
	asmlinkage long SyS_socket(__SC_LONG3(__VA_ARGS__))		\
	{								\
		__SC_TEST3(__VA_ARGS__);				\
		return (long) SYSC_socket(__SC_CAST3(__VA_ARGS__));	\
	}								\
	SYSCALL_ALIAS(sys_socket, SyS_socket);				\
	static inline long SYSC_socket(__SC_DECL3(__VA_ARGS__))
	{
		code...
	}

The first line of code is the sys we expect_ Socket, but this is just a function declaration. Looking down, the definition is actually in the last line, without a semicolon at the end, and a pair of braces are added below to the corresponding implementation code (omitted here). Isn't this the definition. But there is something wrong with the name of this function. How is sysc_ What about socket? It shouldn't be sys_socket?
In fact, syscall is used here_ Alias, you can know from the name. This macro definition actually means sys_ Set the alias of socket to sys_ Socket, that is, call sys_ Socket is actually calling sys_ Socket, and we can see sys_ SYSC_ is invoked in socket. Socket. As a result, this series of tosses can finally call the real function. SYSCALL_ALIAS is defined as follows:

#define SYSCALL_ALIAS(alias, name)					\
	asm ("\t.globl " #alias "\n\t.set " #alias ", " #name "\n"	\
	     "\t.globl ." #alias "\n\t.set ." #alias ", ." #name)

To sum up, we can conclude that when we call SyS_socket, in fact, Sys is called_ Socket, and sys_ Sysc will be called again in the socket_ socket . From a normal point of view, * * * SyS_socket * * * and sys_ The incoming parameters of the socket should be the same. In fact, they are different (different macros are used). The corresponding macros for processing variable parameters are as follows:

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

#define __SC_LONG1(t1, a1)      long a1
#define __SC_LONG2(t2, a2, ...) long a2, __SC_LONG1(__VA_ARGS__)
#define __SC_LONG3(t3, a3, ...) long a3, __SC_LONG2(__VA_ARGS__)
#define __SC_LONG4(t4, a4, ...) long a4, __SC_LONG3(__VA_ARGS__)
#define __SC_LONG5(t5, a5, ...) long a5, __SC_LONG4(__VA_ARGS__)
#define __SC_LONG6(t6, a6, ...) long a6, __SC_LONG5(__VA_ARGS__)

#define __SC_CAST1(t1, a1)      (t1) a1
#define __SC_CAST2(t2, a2, ...) (t2) a2, __SC_CAST1(__VA_ARGS__)
#define __SC_CAST3(t3, a3, ...) (t3) a3, __SC_CAST2(__VA_ARGS__)
#define __SC_CAST4(t4, a4, ...) (t4) a4, __SC_CAST3(__VA_ARGS__)
#define __SC_CAST5(t5, a5, ...) (t5) a5, __SC_CAST4(__VA_ARGS__)
#define __SC_CAST6(t6, a6, ...) (t6) a6, __SC_CAST5(__VA_ARGS__)

#define __SC_TEST(type)         BUILD_BUG_ON(sizeof(type) > sizeof(long))
#define __SC_TEST1(t1, a1)      __SC_TEST(t1)
#define __SC_TEST2(t2, a2, ...) __SC_TEST(t2); __SC_TEST1(__VA_ARGS__)
#define __SC_TEST3(t3, a3, ...) __SC_TEST(t3); __SC_TEST2(__VA_ARGS__)
#define __SC_TEST4(t4, a4, ...) __SC_TEST(t4); __SC_TEST3(__VA_ARGS__)
#define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__)
#define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__)

These macros have a nested expansion process (here again highlights the importance of macros. C/C + + programmers must master the use of macros. They can't really believe it when they see that they don't use macros in the book, and then say that they can't write code anywhere...). The expansion process is as follows:

__SC_DECL3(int, family, int, type, int, protocol)
    -> int family, __SC_DECL2(int, type, int, protocol)
    -> int family, int type, __SC_DECL1(int, protocol)
    -> int family, int type, int protocol
__SC_LONG3(int, family, int, type, int, protocol)
    -> long family, __SC_LONG2(int, type, int, protocol)
    -> long family, long type , __SC_LONG1(int, protocol)
    -> long family, long type, long protocol
__SC_CAST3(int, family, int, type, int, protocol)
    -> (int) family, __SC_CAST2(int, type, int, protocol)
    -> (int) family, (int) type, __SC_CAST1(int, protocol)
    -> (int) family, (int) type, (int) protocol
__SC_TEST3(int, family, int, type, int, protocol)
    -> __SC_TEST(int); __SC_TEST2(int, type, int, protocol)
    -> __SC_TEST(int); __SC_TEST(int); __SC_TEST1(int, protocol)
    -> __SC_TEST(int); __SC_TEST(int); __SC_TEST(int);
    -> BUILD_BUG_ON(sizeof(int) > sizeof(long)); BUILD_BUG_ON(sizeof(int) > sizeof(long)); BUILD_BUG_ON(sizeof(int) > sizeof(long));

By substituting these things into the pile of code expanded earlier, you can get the following clear code:

	asmlinkage long sys_socket(int family, int type, int protocol);		\
	static inline long SYSC_socket(int family, int type, int protocol);	\
	asmlinkage long SyS_socket(long family, long type, long protocol)		\
	{								\
		BUILD_BUG_ON(sizeof(int) > sizeof(long)); BUILD_BUG_ON(sizeof(int) > sizeof(long)); BUILD_BUG_ON(sizeof(int) > sizeof(long));				\
		return (long) SYSC_socket((int) family, (int) type, (int) protocol);	\
	}								\
	SYSCALL_ALIAS(sys_socket, SyS_socket);				\
	static inline long SYSC_socket(int family, int type, int protocol)
	{
		code...
	}

I've finally seen it here. In fact, the work is to uniformly change the parameters of the system call to receive them using the long type, and then forcibly convert them to the original parameter type. It is said that this toss is to fix a loophole.

Topics: C Linux Operating System