Scan from stdin and print to stdout using inline assembly in gcc

后端 未结 1 433
后悔当初
后悔当初 2021-01-07 01:00

How to read from stdin and write to stdout in inline assembly gcc, just like we do it in NASM:

_start:
mov ecx, buffer ;buffer is a data word initialised 0h          


        
相关标签:
1条回答
  • 2021-01-07 01:46

    This code is based solely on my reading of linux references. I'm not on linux, so I cannot test it, but it should be pretty close. I would test it using redirection: a.out < foo.txt

    #include <stdio.h>
    
    #define SYS_READ 3
    
    int main()
    {
       char buff[10]; /* Declare a buff to hold the returned string. */
       ssize_t charsread; /* The number of characters returned. */
    
       /* Use constraints to move buffer size into edx, stdin handle number
          into ebx, address of buff into ecx.  Also, "0" means this value
          goes into the same place as parameter 0 (charsread).  So eax will
          hold SYS_READ (3) on input, and charsread on output.  Lastly, you
          MUST use the "memory" clobber since you are changing the contents
          of buff without any of the constraints saying that you are.
    
          This is a much better approach than doing the "mov" statements
          inside the asm.  For one thing, since gcc will be moving the 
          values into the registers, it can RE-USE them if you make a 
          second call to read more chars. */
    
       asm volatile("int $0x80" /* Call the syscall interrupt. */
          : "=a" (charsread) 
          : "0" (SYS_READ), "b" (STDIN_FILENO), "c" (buff), "d" (sizeof(buff))
          : "memory", "cc");
    
        printf("%d: %s", (int)charsread, buff);
    
        return 0;
    }
    

    Responding to Aanchal Dalmia's comments below:

    1) As Timothy says below, even if you aren't using the return value, you must let gcc know that the ax register is being modified. In other words, it isn't safe to remove the "=a" (charsread), even if it appears to work.

    2) I was really confused by your observation that this code wouldn't work unless buff was global. Now that I have a linux install to play with, I was able to reproduce the error and I suspect I know the problem. I'll bet you are using the int 0x80 on an x64 system. That's not how you are supposed to call the kernel in 64bit.

    Here is some alternate code that shows how to do this call in x64. Note that the function number and the registers have changed from the example above (see http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64):

    #include <stdio.h>
    
    #define SYS_READ 0
    #define STDIN_FILENO 0
    
    int main()
    {
       char buff[10]; /* Declare a buff to hold the returned string. */
       ssize_t charsread; /* The number of characters returned. */
    
       /* Use constraints to move buffer size into rdx, stdin handle number
          into rdi, address of buff into rsi.  Also, "0" means this value
          goes into the same place as parameter 0 (charsread).  So eax will
          hold SYS_READ on input, and charsread on output.  Lastly, I
          use the "memory" clobber since I am changing the contents
          of buff without any of the constraints saying that I am.
    
          This is a much better approach than doing the "mov" statements
          inside the asm.  For one thing, since gcc will be moving the 
          values into the registers, it can RE-USE them if you make a 
          second call to read more chars. */
    
       asm volatile("syscall" /* Make the syscall. */
          : "=a" (charsread) 
          : "0" (SYS_READ), "D" (STDIN_FILENO), "S" (buff), "d" (sizeof(buff))
          : "rcx", "r11", "memory", "cc");
    
        printf("%d: %s", (int)charsread, buff);
    
        return 0;
    }
    

    It's going to take a better linux expert than me to explain why the int 0x80 on x64 wouldn't work with stack variables. But using syscall does work, and syscall is faster on x64 than int.

    Edit: It has been pointed out to me that the kernel clobbers rcx and r11 during syscalls. Failing to account for this can cause all sorts of problems, so I have added them to the clobber list.

    0 讨论(0)
提交回复
热议问题