Ответ 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.