Three methods implement the Linux system call

  • 2020-05-14 05:44:08
  • OfStack

System calls (System Call) are a set of interfaces that the operating system provides for processes running in user mode to interact with hardware devices such as CPU, disks, printers, and so on. When a user process needs to make a system call, CPU switches to kernel state via soft interrupts to start executing kernel system call functions. Here are three ways to make a system call to Linux:

1. Library functions provided by glibc
glibc is the open source standard C library used under Linux, which is the libc library, or runtime library, published by GNU. glibc provides programmers with rich API (Application Programming Interface). In addition to user-mode services such as string processing and mathematical operations, the most important thing is to encapsulate the system services provided by the operating system, that is, the encapsulation of system calls. So what is the relationship between system calls provided by glibc, API, and kernel-specific system calls?

In general, each specific system call corresponds to at least one library function encapsulated by glibc. For example, the system-provided open file system call sys_open corresponds to the open function in glibc. Second, a single glibc API may call multiple system calls, such as sys_open, sys_mmap, sys_write, sys_close and so on. In addition, multiple API may only correspond to the same system call, for example, malloc, calloc, free and other functions implemented under glibc are used to allocate and free memory, all using the system call of sys_brk of the kernel.

For example, we used the chmod function provided by glibc to change the property of etc/passwd to 444:


#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>

int main()
{
  int rc;

  rc = chmod("/etc/passwd", 0444);
  if (rc == -1)
    fprintf(stderr, "chmod failed, errno = %d\n", errno);
  else
    printf("chmod success!\n");
  return 0;
}

Compiled and applied under ordinary users, the output result is:

chmod failed, errno = 1
The value returned by the above system call is -1, indicating that the system call failed and the error code is 1. In the file /usr/include/ asm-generic/errno-base.h, there is the following error code:

#define EPERM 1 /* Operation not permitted */
We cannot modify the properties of /etc/passwd file with normal user permission. The result is correct.

2. Call directly using syscall
There are many benefits to using the above method. First of all, you don't need to know more details, such as the chmod system call number. You only need to know the prototype of API provided by glibc. Second, the method is more portable, you can easily migrate the program to another platform, or replace the glibc library with another library, with only a few changes to the program.
The downside is that if glibc doesn't encapsulate a system call provided by a kernel, I won't be able to call the system call using the above method. If I added a system call by compiling the kernel, then glibc cannot have the encapsulation API of your new system call. At this time, we can directly call syscall functions provided by glibc. This function is defined in the unistd.h header file, and the function prototype is as follows:

long int syscall (long int sysno, ...)

sysno is the system call number, and each system call is identified by a unique system call number of 1. There are macro definitions for all possible system call Numbers in sys/ syscall.h. . Is the parameter of the remaining variable length, and is the parameter taken by the system call. According to the difference of the system call, it can take 0~5 different parameters. If it exceeds the parameter of the specific system call energy band, the extra parameters will be ignored. The return value of this function is the return value of a particular system call. After the system call is successful, you can convert the return value to a specific type. If the system call fails, it returns -1. The error code is stored in errno.

Also take the above modification /etc/passwd file properties for example, this time using syscall directly call:


#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
  int rc;
  rc = syscall(SYS_chmod, "/etc/passwd", 0444);

  if (rc == -1)
    fprintf(stderr, "chmod failed, errno = %d\n", errno);
  else
    printf("chmod succeess!\n");
  return 0;
}

Compile and execute under normal users, and the output is the same as in the above example.

3. Get caught by int instruction
If we know the whole process of the system call, should you know the user mode application by soft interrupt instruction int 0 x80 into kernel mode (in Intel Pentium II and sysenter instruction) is introduced, parameters passed by register, eax transfer is the system call number, ebx, ecx, edx, esi and edi is passed up to five parameters, when the system call returns. The return value is stored in eax.

As an example of modifying the file properties above, write inline assembly code for the system call section:


#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
  long rc;
  char *file_name = "/etc/passwd";
  unsigned short mode = 0444;

  asm(
    "int $0x80"
    : "=a" (rc)
    : "0" (SYS_chmod), "b" ((long)file_name), "c" ((long)mode)
  );

  if ((unsigned long)rc >= (unsigned long)-132) {
    errno = -rc;
    rc = -1;
  }

  if (rc == -1)
    fprintf(stderr, "chmode failed, errno = %d\n", errno);
  else
    printf("success!\n");

  return 0;
}

If the return value stored in the eax register (stored in the variable rc) is between -1 and -132, it must be interpreted as an error code (the maximum error code defined in the /usr/include/ asm-generic/errno.h file is 132). Otherwise you return the value in eax.

The above program compiled and ran with normal user privileges under 32-bit Linux with the same results as the previous two!

The above is the detailed content of this article, hope you enjoy it.


Related articles: