Сканирование с stdin и печать на stdout с помощью встроенной сборки в gcc

Как читать из stdin и писать в stdout во встроенной сборке gcc, как и в NASM:

_start:
mov ecx, buffer ;buffer is a data word initialised 0h in section .data
mov edx, 03
mov eax, 03 ;read
mov ebx, 00 ;stdin
int 0x80
;Output the number entered
mov eax, 04 ;write
mov ebx, 01 ;stdout
int 0x80

Я пробовал читать из stdin в встроенной сборке, а затем назначил вход x:

#include<stdio.h>
int x;
int main()
{
    asm(" movl $5,  %%edx \n\t" " 
    movl $0,  %%ebx \n\t" " 
    movl $3,  %%eax \n\t" " 
    int $0x80 \n\t "
    mov %%ecx,x" 
    ::: "%eax", "%ebx", "%ecx", "%edx");

    printf("%d",x);  
    return 0;
}

Однако он этого не делает.

syscall из встроенной сборки GCC

Эта ссылка содержит код, который может печатать только один символ в stdout.

Ответ 1

Этот код основан исключительно на моем чтении ссылок на Linux. Я не на Linux, поэтому я не могу проверить его, но он должен быть довольно близок. Я бы проверил его с помощью перенаправления: 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;
}

Отвечая на комментарии Aanchal Dalmia ниже:

1) Как Тимоти говорит ниже, даже если вы не используете возвращаемое значение, вы должны позволить gcc знать, что регистр токов изменяется. Другими словами, небезопасно удалять "= a" (charsread), даже если он работает.

2) Я был очень смущен вашим наблюдением, что этот код не будет работать, если бафф был глобальным. Теперь, когда у меня есть установка linux для игры, я смог воспроизвести ошибку, и я подозреваю, что знаю эту проблему. Готов поспорить, вы используете int 0x80 в системе x64. Это не то, как вы должны назвать ядро ​​в 64-битной версии.

Вот несколько альтернативных кодов, которые показывают, как сделать этот вызов в x64. Обратите внимание, что номер функции и регистры изменились из приведенного выше примера (см. 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;
}

Он собирается взять лучшего эксперта linux, чем я, чтобы объяснить, почему int 0x80 на x64 не будет работать со стековыми переменными. Но использование syscall действительно работает, а syscall работает быстрее на x64, чем int.

Изменить: мне было указано, что ядро ​​clobbers rcx и r11 во время системных вызовов. Несоблюдение этого может вызвать всевозможные проблемы, поэтому я добавил их в список clobber.