### Me: Please write commented C++ for a generic system call interface for Linux on x86_64 that uses inline assembly. ### ChatGPT: Here is an example of a generic system call interface for Linux on x86_64 that uses inline assembly: ```c #include // Define the system call number for the syscall we want to make #define SYS_CALL_NUMBER 0 // Define the inline assembly to make the syscall inline void syscall(uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t syscall_number) { // Inline assembly to make the syscall asm volatile ( // Move the syscall number into the rax register "mov %[syscall_number], %%rax;" // Move the arguments into the appropriate registers "mov %[arg1], %%rdi;" "mov %[arg2], %%rsi;" "mov %[arg3], %%rdx;" "mov %[arg4], %%r10;" // Make the syscall "syscall;" // Outputs : // Inputs : [syscall_number] "r" (syscall_number), [arg1] "r" (arg1), [arg2] "r" (arg2), [arg3] "r" (arg3), [arg4] "r" (arg4) ); } // Example usage of the syscall function int main() { // Make the syscall with the desired arguments syscall(0, 0, 0, 0, SYS_CALL_NUMBER); return 0; } ``` In this example, we define a macro called `SYS_CALL_NUMBER` that specifies the system call number for the syscall we want to make. Then, we define an inline function called `syscall` that takes the arguments for the syscall and the syscall number as inputs. Inside the `syscall` function, we use inline assembly to move the syscall number into the `rax` register, and move the syscall arguments into the appropriate registers (`rdi`, `rsi`, `rdx`, and `r10`). Then, we make the syscall using the `syscall` instruction. The `syscall` function can be used in the same way as any other C++ function, as shown in the `main` function. You just need to specify the arguments for the syscall and the syscall number, and the function will make the syscall for you. ### Me: That answer is in C, please re-write it to use modern C++. ### ChatGPT: Here is an example of a generic system call interface for Linux on x86_64 that uses inline assembly in modern C++: ```cpp #include // Define the system call number for the syscall we want to make constexpr uint64_t SYS_CALL_NUMBER = 0; // Define the inline assembly to make the syscall [[noreturn]] inline void syscall(uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t syscall_number) { // Inline assembly to make the syscall asm volatile ( // Move the syscall number into the rax register "mov %[syscall_number], %%rax;" // Move the arguments into the appropriate registers "mov %[arg1], %%rdi;" "mov %[arg2], %%rsi;" "mov %[arg3], %%rdx;" "mov %[arg4], %%r10;" // Make the syscall "syscall;" // Outputs : // Inputs : [syscall_number] "r" (syscall_number), [arg1] "r" (arg1), [arg2] "r" (arg2), [arg3] "r" (arg3), [arg4] "r" (arg4) ); // This function never returns __builtin_unreachable(); } // Example usage of the syscall function int main() { // Make the syscall with the desired arguments syscall(0, 0, 0, 0, SYS_CALL_NUMBER); return 0; } ``` In this example, we define a constexpr variable called `SYS_CALL_NUMBER` that specifies the system call number for the syscall we want to make. Then, we define an inline function called `syscall` that takes the arguments for the syscall and the syscall number as inputs. Inside the `syscall` function, we use inline assembly to move the syscall number into the `rax` register, and move the syscall arguments into the appropriate registers (`rdi`, `rsi`, `rdx`, and `r10`). Then, we make the syscall using the syscall instruction. We also mark the function as `[[noreturn]]` to indicate that it never returns. The `syscall` function can be used in the same way as any other C++ function, as shown in the `main` function. You just need to specify the arguments for the syscall and the syscall number, and the function will make the syscall for you. ### Me: That's a little better. Here is a better example of how to do this that is missing comments. The C++ code starts with "----CUT HERE----" and also ends with "----CUT HERE----". ----CUT HERE---- ```cpp #include enum class call_id : ::std::uint16_t; using val_t = ::std::int64_t; struct syscall_param { syscall_param(val_t v) noexcept : value(v) { } // NOLINT // NOLINTNEXTLINE syscall_param(void *v) noexcept : value(reinterpret_cast(v)) { static_assert(sizeof(void *) == sizeof(val_t)); } // NOLINTNEXTLINE syscall_param(void const *v) noexcept : value(reinterpret_cast(v)) { static_assert(sizeof(void *) == sizeof(val_t)); } val_t value; }; inline val_t do_syscall(call_id callnum, syscall_param const &p1, syscall_param const &p2, syscall_param const &p3, syscall_param const &p4, syscall_param const &p5, syscall_param const &p6) noexcept { val_t retval; register volatile val_t rp4 asm ("r10") = p4.value; register volatile val_t rp5 asm ("r8") = p5.value; register volatile val_t rp6 asm ("r9") = p6.value; asm volatile ( "syscall\n\t" :"=a"(retval) :"a"(static_cast<::std::uint64_t>(callnum)), "D"(p1.value), "S"(p2.value), "d"(p3.value), "r"(rp4), "r"(rp5), "r"(rp6) :"%rcx", "%r11", "memory" ); return retval; } ``` ----CUT HERE---- Please add comments. ### ChatGPT: Here is the modified code with comments added: ```cpp #include // Define an enum class called call_id that represents syscall numbers enum class call_id : ::std::uint16_t; // Define a type called val_t that represents a syscall argument using val_t = ::std::int64_t; // Define a struct called syscall_param that can be used to wrap syscall arguments struct syscall_param { // Construct a syscall_param from a value_t syscall_param(val_t v) noexcept : value(v) { } // NOLINT // Construct a syscall_param from a pointer // NOLINTNEXTLINE syscall_param(void *v) noexcept : value(reinterpret_cast(v)) { static_assert(sizeof(void *) == sizeof(val_t)); } // Construct a syscall_param from a const pointer // NOLINTNEXTLINE syscall_param(void const *v) noexcept : value(reinterpret_cast(v)) { static_assert(sizeof(void *) == sizeof(val_t)); } // The wrapped syscall argument val_t value; }; // Define a function that makes a syscall with up to six arguments inline val_t do_syscall(call_id callnum, syscall_param const &p1, syscall_param const &p2, syscall_param const &p3, syscall_param const &p4, syscall_param const &p5, syscall_param const &p6) noexcept { // Define a variable to hold the return value of the syscall val_t retval; // Move the fourth, fifth, and sixth syscall arguments into registers register volatile val_t rp4 asm ("r10") = p4.value; register volatile val_t rp5 asm ("r8") = p5.value; register volatile val_t rp6 asm ("r9") = p6.value; // Inline assembly to make the syscall asm volatile ( "syscall\n\t" // Make the syscall :"=a"(retval) // Output: return value in rax :"a"(static_cast<::std::uint64_t>(callnum)), // Input: syscall number in rax "D"(p1.value), // Input: first argument in rdi "S"(p2.value), // Input: second argument in rsi "d"(p3.value), // Input: third argument in rdx "r"(rp4), // Input: fourth argument in r10 "r"(rp5), // Input: fifth argument in r8 "r"(rp6) // Input: sixth argument in r9 :"%rcx", "%r11", "memory" // Clobbered registers ); // Return the syscall return ```