C- Inline assembly to implement puts function

The puts() function implemented using inline assembly is as follows:

#define SYS_WRITE 1

#define CALL2(n) "movq $"#n", %%rax\n"
#define CALL(n) CALL2(n)

int puts(char *s) {
    
    
    long n = strlen(s);
    long r;
    asm(CALL(SYS_WRITE)
       "movq $1, %%rdi\n"
       "movq %1, %%rsi\n"
       "movq %2, %%rdx\n"
       "syscall\n"
       "movq %%rax, %0\n"
       : "=r"(r)
       : "r"(s), "r"(n)
       : "%rax", "%rdi", "%rsi", "%rdx");
    return (int)r;
}

This code provides a simplified version of putsa function that uses inline assembly to directly execute writea system call on Linux to output a string to standard output. We will analyze this function in detail step by step:

  1. Function definition and variable initialization :
int puts(char *s) {
    
    
    long n = strlen(s);
    long r;

This defines a putsfunction that receives a string sas argument. Internally the function first calculates sthe length and stores the result in a variable n.

  1. Inline assembly part :

This is a block of inline assembly code written in GCC extended syntax. It directly uses assembly instructions on the x86-64 architecture to call Linux system calls.

    asm(CALL(SYS_WRITE)

This part refers to a macro or external definition CALL(SYS_WRITE), which should resolve to writethe number of a system call. But in this code, we don't see the definition of these macros.

The next assembly instructions writeset parameters for the system call:

  • "movq $1, %%rdi\n": Set file descriptor 1 (standard output) as the first argument.

  • "movq %1, %%rsi\n": Set the address of the string as the second parameter.

  • "movq %2, %%rdx\n": Set the string length as the third parameter.

Next comes the actual system call instructions:

  • "syscall\n": Execute system call.

Finally, the return value of the system call (in raxa register) is moved into a variable r.

  1. Placeholders for input, output and assembly code :
  • : "=r"(r): Output section indicating that rthe variable receives the value of a register (in particular rax, the return value of a system call).

  • : "r"(s), "r"(n): Input part, specify sand ntwo variables are passed to rsiand rdxregister respectively.

  • : "%rax", "%rdi", "%rsi", "%rdx": Clobbered register list, tells the compiler that the values ​​of these registers have been modified in the assembly code block.

  1. Return value :
    return (int)r;
}

The function finally returns the number of bytes written, or a negative value on error.

To summarize, this putsfunction uses inline assembly to interact directly with the Linux kernel and swrite a string to standard output.

Guess you like

Origin blog.csdn.net/weixin_43844521/article/details/133365414