Взаимодействие с Linux

Модератор, закрепи тему, пожалуйста.

Сделал перевод малюсенькой статьи. А то раздел, по программированию на Ассемблере под Linux, как-то обделили.

X86 Assembly/Взаимодействие с Linux.

Автор перевода: XiloManX.

Дата перевода: 02.11.2016

Перевод статьи из электронной книги "x86 Assembly":

https://en.wikibooks.org/wiki/... with_Linux

От автора перевода:

Это, мой первый технический перевод. Поэтому, заранее прошу прощение, за возможные грамматические ошибки и возможную неточность перевода.

Системные вызовы.

Системные вызовы являются интерфейсом между пользовательскими программами и ядром Linux. Они используются, чтобы позволить ядру выполнять различные системные задачи, такие как: доступ к файлам, управление процессами и создание сетей. В языке программирования С, вы, как правило, вызываете функцию-обертку, которая выполняет все необходимые действия, или даже используете функции высокого уровня из стандартной библиотеки ввода-вывода.

В Linux, есть несколько способов сделать системный вызов. Данная страница, будет сосредоточена на создании системных вызовов, путем вызова программного прерывания, с помощью:

int $0x80

или

syscall

. Это простой и интуитивно понятный способ, для того, чтобы делать системные вызовы только в ассемблерных программах.

Создание системного вызова.

Для того, чтобы сделать системный вызов с использованием прерывания, вы должны передать всю необходимую информацию ядру, копируя её в регистры общего назначения.

Каждый системный вызов имеет фиксированный номер (примечание: между собой

$0x80

и

syscall

отличаются номерами!). Вы указываете системный вызов, путем записи номера в регистр

eax

/

rax

.

Чтобы выполнить свою задачу, большинство системных вызовов принимают параметры. Перед выполнением вызова, эти параметры, передаются путем записи их в соответствующие регистры.

Каждый индекс параметра имеет специальный регистр. Для того, чтобы увидеть различия, между

int $0x80

и

syscall

, см. таблицы в подразделах. Параметры передаются в порядке, в котором они определены в описании функции, соответствующей обертке функции C. В каждой документации по

Linux API

, для справки, вы можете найти функции системных вызовов и их описание (например: чтобы увидеть описание системного вызова

open

, напечатайте в терминале

man 2 open

).

После того, как все настроено правильно, вы вызываете прерывание используя

int $0x80

или

syscall

, и ядро выполняет задачу. Возвращаемое значение системного вызова, об удаче или ошибке, записывается в регистр

eax

/

rax

.

Для выполнения действий, ядро Linux использует свой собственный стек, и никаким образом не затрагивает стек пользователя.

int $0x80

На обоих системах Linux, x86 и x86_64, вы можете сделать системный вызов, вызвав прерывание 0x80 с помощью команды

int $0x80

. Параметры передаются путем установки регистров общего назначения следующим образом:

| | | | | | | |
| --- | --- | --- | --- | --- | --- | --- |
| № сис. вызова | Парам 1 | Парам 2 | Парам 3 | Парам 4 | Парам 5 | Парам 6 |
| eax | ebx | ecx | rdx | esi | edi | ebp |

Номера системных вызовов описаны в исходном файле

Linux: arch/x86/include/asm/unistd_32.h

.

Во время системного вызова, все регистры сохраняются.

syscall

Для того, чтобы сделать системный вызов, в архитектуру x86_64 введена специальная инструкция. Она быстрее и не имеет доступа к таблице дескрипторов прерываний.

| | | | | | | |
| --- | --- | --- | --- | --- | --- | --- |
| № сис. вызова | Парам 1 | Парам 2 | Парам 3 | Парам 4 | Парам 5 | Парам 6 |
| rax | rdi | rsi | rdx | r10 | r8 | r9 |

Номера системных вызовов описаны в исходном файле

Linux: arch/x86/include/asm/unistd_64.h

.

Во время системного вызова, все регистры сохраняются, за исключением

rcx

и

r11

.

Примеры.

Для того, чтобы подвести итог и уяснить информацию, давайте посмотрим на очень простой пример: программа привет мир. Она будет писать текст "Hello World" на стандартный вывод, используя системный вызов

write

, а выход из программы, с помощью системного вызова

_exit

.

Описание системных вызовов:

CВыделить код

1
2

ssize_t write(int fd, const void buf, size_t count);
void _exit(int status);

Это программа на языке С, которая ниже реализована на ассемблере:

CВыделить код

1
2
3
4
5
6
7

#include <unistd.h>
 
int main(int argc, char argv[])
{
    write(1, "Hello Worldn", 12); / написать в ст.вывод "Hello World". /
    _exit(0);                      / выход с кодом ошибки 0 (нет ошибки) /
}

Оба примера, начинаются так: строка, хранится в сегменте данных и

_start

, в качестве глобального символа.

AssemblerВыделить код

1
2
3
4
5

.data
msg: .ascii "Hello Worldn"
 
.text
.global _start

int 0x80

Как определено в:

arch/x86/include/asm/unistd_32.h

, номера системных вызовов для

write

и

_exit

, будут такими:

Параметры передаются в той же последовательности, что и в программе на языке C, но с использованием правильных регистров. После чего, системный вызов выполняется с использованием

int $0x80

.

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10

_start:
    movl $4, %eax   ; использовать сис. вызов write.
    movl $1, %ebx   ; печатать в стандарный вывод.
    movl $msg, %ecx ; использовать строку "Hello World".
    movl $12, %edx  ; печатать 12 символов.
    int $0x80       ; сделать системный вызов.
    
    movl $1, %eax   ; использовать сис. вызов _exit.
    movl $0, %ebx   ; код ошибки 0.
    int $0x80       ; сделать системный вызов.

syscall

Номера системных вызовов определённы в файле -

arch/x86/include/asm/unistd_64.h

, следующим образом:

Параметры переданы так же, как в примере с

int $0x80

, только порядок регистров другой. A системный вызов, сделан при помощи инструкции

syscall

.

AssemblerВыделить код

1
2
3
4
5
6
7
8
9
10

_start:
    movq $1, %rax   ; использовать сис. вызов write.
    movq $1, %rdi   ; печатать в стандарный вывод.
    movq $msg, %rsi ; использовать строку "Hello World".
    movq $12, %rdx  ; печатать 12 символов.
    syscall         ; сделать системный вызов.
    
    movq $60, %rax  ; использовать сис. вызов _exit.
    movq $0, %rdi   ; код ошибки 0.
    syscall         ; сделать системный вызов.