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 puts
a function that uses inline assembly to directly execute write
a system call on Linux to output a string to standard output. We will analyze this function in detail step by step:
- Function definition and variable initialization :
int puts(char *s) {
long n = strlen(s);
long r;
This defines a puts
function that receives a string s
as argument. Internally the function first calculates s
the length and stores the result in a variable n
.
- 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 write
the number of a system call. But in this code, we don't see the definition of these macros.
The next assembly instructions write
set 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 rax
a register) is moved into a variable r
.
- Placeholders for input, output and assembly code :
-
: "=r"(r)
: Output section indicating thatr
the variable receives the value of a register (in particularrax
, the return value of a system call). -
: "r"(s), "r"(n)
: Input part, specifys
andn
two variables are passed torsi
andrdx
register 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.
- Return value :
return (int)r;
}
The function finally returns the number of bytes written, or a negative value on error.
To summarize, this puts
function uses inline assembly to interact directly with the Linux kernel and s
write a string to standard output.