Видео ролики бесплатно онлайн

Смотреть жесткое видео

Официальный сайт oriflame100 24/7/365

Смотреть видео бесплатно

dementiy 18.07.2010 00:14

CodingАссемблер в Linux. Использование системных вызовов

«Мастер ФУ! А зачем нужны системные вызовы? - Затем же, зачем тебе нужен твой сосуд
Какой сосуд? - решил уточнить ученик - А вот какой! - крикнул мастер, ударяя ученика палкой»


Системные вызовы это интерфейс для взаимодействия ядра ОС и внешних приложений. В данной заметке мы не будем рассматривать, как устроены системные вызовы, мы рассмотрим только использование некоторых из них (предназначенных для работы с файлами). Итак начнем:

Системный вызов OPEN

1
int open(const char *pathname, int flags, mode_t mode);


Системный вызов OPEN принимает три аргумента:
*pathname — путь к файлу;
flags — флаги, которые указывают способ открытия файла. Основные флаги (все флаги можно найти в файле include/asm-generic/fcntl.h) :
0 (O_RDONLY) — открыть файл только для чтения;
1 (O_WRONLY) — открыть файл только для записи;
2 (O_RDWR) — открыть файл для чтения и записи.
mode — права доступа к файлу, учитываются, если задан флаг O_CREAT. Права доступа задаются для владельца, группы и отсальных:
0700 — Владелец файла имеет право на чтение, запись и выполнение;
0400 — Владелец файла имеет право на чтение;
0200 — Владелец файла имеет право на запись
0100 — Владелец файла имеет право на выполнение;
0070 — Группа файла имеет право на чтение, запись и выполнение;
0040 — Группа файла имеет право на чтение;
0020 — Группа файла имеет право на запись;
0010 — Группа файла имеет право на выполнение;
0007 — Остальные пользователи имеют право на чтение, запись и выполнение;
0004 — Остальные пользователи имеют право на чтение;
0002 — Остальные пользователи имеют право на запись
0001 — Остальные пользователи имеют право на выполнение.
В %eax возвращается файловый дескриптор (целое неотрицательное значение) в случае успеха или отрицательное значение, если произошла ошибка.

1
2
3
4
5
6
7
...
movl $5, %eax #номер системного вызова (open) в %eax
movl $file_name, %ebx #путь к файлу (адрес) в %ebx
movl flags, %ecx #флаги в %ecx
movl mode, %edx #права доступа к файлу в %edx
int $0x80
...


Системный вызов CREAT

1
int creat(const char *pathname, mode_t mode);


Системный вызов CREAT принимает два аргумента: путь к создаваемому файлу (*pathname) и права на файл (mode). При успешном выполнении возвращает дескриптор созданного файла в регистр %eax. Функция CREAT в действительности представляет из себя обертку для вызова OPEN:

1
2
3
4
5
fs/open.c:
SYSCALL_DEFINE2(creat, const char __user *, pathname, int, mode)
{
return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
}


Пример создания файла:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#creat.s — example of creating a file
.section .data
fname:
.asciz "test.txt"
mode:
.int 0666
.section .text
.globl _start
_start:
movl $8, %eax #номер системного вызова (creat) в %eax
movl $fname, %ebx #путь к файлу в %ebx
movl mode, %ecx #права доступа в %ecx
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80


Системный вызов WRITE

1
ssize_t write(int fd, const void *buf, size_t count);


Системный вызов WRITE приниммает три параметра:
fd — файловый дескриптор:
0 — STDIN (стандартный ввод);
1 — STDOUT (стандартный вывод);
2 — STDERR (стандартный вывод ошибок).
*buf — указатель на буфер (данные подлежащие выводу);
count — количество символов подлежащих выводу.
И возвращает количество выведенных символов в регистр %eax. Пример использования:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#write.s — syscall write
.section .data
output:
.ascii "Hello world\n"
end:
.equ length, end - output
.section .text
.globl _start
_start:
movl $4, %eax #номер системного вызова (write) в %eax
movl $1, %ebx #файловый дескритор (stdout) в %ebx
movl $output, %ecx #указатель на строку в %ecx
movl $length, %edx #количество выводимых символов в %edx
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80


Системный вызов READ

1
ssize_t read(int fd, void *buf, size_t count);


Системный вызов READ аналогично WRITE принимает три аргумента, которые описаны выше. Количество прочитанных символов возвращается в %eax. Пример использования:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#read.s — syscall read
.section .bss
.lcomm buffer, 1024
.section .text
.globl _start
_start:
movl $3, %eax #номер системного вызова (read) в %eax
movl $0, %ebx #файловый дескритор (stdin) в %ebx
movl $buffer, %ecx #указатель на буфер в %ecx
movl $1024, %edx #размер буфера в %edx
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80



Системный вызов LSEEK

1
off_t lseek(int fd, off_t offset, int whence);


Системный вызов LSEEK предназначен для чтения/записи в произвольное место файла. Он принимает следующие аргументы:
fd — файловый дескриптор;
offset — количество байт, на которые нужно сместиться относительно whence;
whence — устаналивает позицию в файле, принимает следующие значения (определены в include/linux/fs.h):
1 (SEEK_SET) — начало файла;
2 (SEEK_CUR) — текущая позиция в файле;
3 (SEEK_END, SEEK_MAX) — конец файла.
Возвращает новую позицию в файле относительно его начала в %eax, либо номер ошибки в случае неудачи.

1
2
3
4
5
6
7
...
movl $0x13, %eax
movl fd, %ebx
movl offset, %ecx
movl whence, %edx
int $0x80
...


Системный вызов CLOSE

1
int close(int fd);


Cистемный вызов CLOSE принимает только один аргумент — файловый дескриптор (fd) и возврщает в %eax 0 в случае успеха или номер ошибки в случае неудачи.

1
2
3
4
5
...
movl $6, %eax #номер системного вызова (close) в %eax
movl fd, %ebx #файловый дескриптор в %ebx
int $0x80
...


Пример вывода текста из файла с использованием системных вызовов OPEN, WRITE, READ и CLOSE:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
.section .bss
.lcomm buffer, 1024
.section .data
fname:
.asciz "test.txt"
fd:
.int 0
.section .text
.globl _start
_start:
movl $5, %eax #системный вызов open
movl $fname, %ebx #имя файла
movl $0, %ecx #открыть только для чтения (0 - O_RDONLY)
movl $0, %edx #0, т.к. мы не будем создавать файл
int $0x80

movl %eax, fd #сохраняем файловый дескриптор
read:
movl $3, %eax #системный вызов read
movl fd, %ebx #дескриптор открытого файла
movl $buffer, %ecx #куда сохранять данные
movl $1024, %edx #размер буфера
int $0x80

cmpl $0, %eax #остались ли данные для чтения?
je close #если нет, то перейти к закрытию файла

movl %eax, %edx #размер буфера
movl $4, %eax #системный вызов read
movl $1, %ebx #куда осуществлять запись (1 - stdout)
movl $buffer, %ecx #откуда брать данные для записи
int $0x80
jmp read
close:
movl $6, %eax #системный вызов close
movl fd, %ebx #дескриптор файла, который следует закрыть
int $0x80

movl $1, %eax #системный вызов exit
movl $0, %ebx #возвращаем 0
int $0x80


Системный вызов UNLINK

1
int unlink(const char *pathname);


Системный вызов UNLINK принимает всего лишь один аргумент — путь к удаляемому файлу. В %eax возвращается 0 в случае успеха или номер ошибки в случае неудачи.

1
2
3
4
5
...
movl $0xa, %eax
movl $fname, %ebx
int $0x80
...


Системные вызовы TRUNCATE и FTRUNCATE

1
int truncate(const char *path, off_t length);


1
int ftruncate(int fd, off_t length);


Системный вызов TRUNCATE принимает два аргумента: путь к файлу (*path) и новый размер файла (length), и возвращает в %eax 0 в случае успеха или номер ошибки в случае неудачи. Создадим файл «test.txt» и напишем в нем «Some text before truncate». Его размер составляет 26 байт. Теперь выполним следующую программу:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#truncate.s - syscall truncate
.section .data
fname:
.asciz "test.txt"
.section .text
.globl _start
_start:
movl $0x5c, %eax #номер системного вызова (truncate) в %eax
movl $fname, %ebx #путь к файлу в %ebx
movl $9, %ecx #размер файла в %ecx
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80


Она урезает файл до 9 байт и после ее исполнения в файле останется надпись «Some text». Если указать размер файла больше текущего размера, то в файле образуются дырки. Системный вызов FTRUNCATE полностью аналогичен TRUNCATE, за исключением того, что вместо пути указывается файловый дескриптор, FTRUNCATE имеет номер 0x5d.

Системный вызов UTIME

1
int utime(const char *filename, const struct utimbuf *times);


Системный вызов UTIME принимает два параметра: имя файла, которому следует изменить время доступа и модификации и указатель на структуру utimebuf. Структура utimebuf включает в себя два поля:

1
2
3
4
5
include/linux/utime.h:
struct utimebuf {
__kernel_time_t actime; /* время доступа */
__kernel_time_t modtime; /* время модификации */
};


Время доступа и модификации указывается в секундах от 1 января 1970 года. Рассмотрим небольшой пример, создадим файл «test.txt», чтобы просмотреть его время доступа и модификации можно воспользоваться командой ls (для отображения времени доступа используется параметр --time=atime). Изменим эти значения, время доступа выставим равным 1 января 1970 года, а время модификации 1 января 2011 года:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#utime.s — syscall utime
.section .data
fname:
.asciz "test.txt"
times:
actime:
.long 0 #0 означает сброс времени на 1 января 1970 года
modtime:
.long 1293830000 #время модификации 1 января 2011 года
.section .text
.globl _start
_start:
movl $0x1e, %eax #номер системного вызова (utime) в %eax
movl $fname, %ebx #путь к файлу в %ebx
movl $times, %ecx #адрес «структуры» в %ecx
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80


После запуска программы можете проверить время доступа и модификации, они должны были измениться. Для того, чтобы установить текущее время доступа и модификации необходимо передать 0 (NULL) вместо адреса структуры (movl $0, %ecx).

Замечания

В системных вызовах в случае ошибки возвращается отрицательное значение в регистр %eax. Например в системном вызове LSEEK, если задан файловый дескриптор не открытого файла, то в %eax будет значение 0xfffffff7, что соответсвует -9 (коды ошибок можно посмотреть в файле include/asm-generic/errno-base.h), а не 4294967287. Поэтому для обработки ошибок следует использовать условные конструкции для знаковых чисел.
Системные вызовы можно просмотреть используя трассировщик strace. Например для программы utime.s:


Таким образом можно проверить себя на правильность заданных аргументов. Если системный вызов возвращает ошибку, то strace отобразит ее в удобочитаемом виде:


Общая концепция работы с системными вызовами я думаю должна быть ясна, в %eax всегда помещается номер системного вызова, в %ebx, %ecx, %edx, %esi, %edi с первого по пятый аргументы соответственно.
Вместо шлюза $0x80 можно использовать библиотеку языка С и инструкцию CALL. У такого подхода есть свои плюсы и недостатки. Главный плюс состоит в том, что будут использовать разделяемые библиотеки, а минус, как следствие, в увеличении размера исполняемого файла.
Ну и наконец, справку по системным вызовам можно получить на страницах справочного руководства man, во втором и третьем разделах.

P.S. Данная заметка не является второй частью цикла (которая появится скорее всего уже только в августе), скорее это приложение к первой части, чтобы немного пролить свет на использование системных вызовов в ассемблере. Как всегда, если заметите неточности или ошибки сообщите о них. И наконец pdf'ка (возможно, что pdf со временем будет пополняться, но это не точно).


Тэги: assembler system calls
+ 22 -
Похожие Поделиться

razum2um 18.07.2010 08:45 #
+ 3 -
Самое главное забыли объяснить по int80h
Для вызова любой системной функции используется команда INT 80h (вспомните DOS – там для этой цели использовался INT 21h). Параметры передаются через регистры. Номер функции – в АХ

и еще на той же странице wasm`а есть нехилая, но грустная метафора:

когда только начинаешь писать на асме под *nix то возникает интересное ощущение: вроде бы ты попал в грязный пятибаксовый мотель (из тех, возле которых обязательно проходит метро и когда едет поезд на потолке дрожит дешевая люстра и мигает свет); здесь давно нет горячей воды, обои уродливыми клочьями свисают со стен, с потолка капает какая то мерзкая гадость и пахнет плесенью, все удобства – во дворе... На мотеле (подпертые кем-то неизвестным) стоят уже давно покосившиеся со временем неоновые буквы «*NIX для ассемблерщиков» (половина букв давно не горит, а половина с треском догорает). У мотеля нет своих постояльцев. Сюда заезжают лишь переночевать, чтобы на следующее утро убраться подальше...

Самое мерзкое во всем этом то, что через единственное окно в этой конуре, через дорогу, как будто специально, вырос семизвездочный отель, весь в рекламе, бассейнах и пальмах... Прямо над входом (к которому то и дело поминутно подъезжают все более и более крутые тачки) сверкает золотом надпись: “*NIX для сишников”. Вон видно как по террасам ходят пузатые мужики в обнимку с дорогими бабами, потягивая коктейли и куря сигары, им прислуживает армия официантов и слуг; все они смеются и живут.

И еще интересно, правда, что до сих пор
ассеблерщик, сунувшийся в *nix не найдет практически никакой документации, описывающей системные вызовы на низком уровне

dementiy 18.07.2010 09:42 #
+ 0 -
На счет документации. Есть документация по системным вызовам для С, а для ассемблера можно найти примеры (и то не много), которые разбросаны по всей сети, а документации нет. Хорошо это или плохо я не знаю, с одной стороны дублировалась бы информация, которая есть в документации к С, с другой стороны было бы неплохо иметь описание всех системных вызовов с примерами на ассемблере.
razum2um 18.07.2010 09:55 #
+ 1 -
короче реально подбирать параметры в регистры...
но я не отстану ;)..
вот что можно такое зафигачить на асме, что тяжелее или просто нельзя на сях?
самогенерирующийся код? а это сильно сложно, чтоб хотя бы понять, сейчас?
dementiy 18.07.2010 10:12 #
+ 0 -
Нет, подбирать-то ничего не надо, я же в конце написал "... в %eax всегда помещается номер системного вызова, в %ebx, %ecx, %edx, %esi, %edi с первого по пятый аргументы соответственно.", а номера системных вызовов можно взять тут. На счет сложности, всегда все зависит от алгоритма.
digiwhite 18.07.2010 17:51 #
+ 0 -
Зафигарить можно оптимизацию. Да, если у вас конечно на запасных путях стоит N товарных поездов, заполненных свободным временем, то можете написать какое-нибудь приложение.
razum2um 18.07.2010 09:03 #
+ 0 -
И да, hello world заработал (хз, че в пролый раз было)
так:
$ as -s -o hello.o hello.s
$ ld -s -o hello hello.o
zhevak 18.07.2010 23:27 #
+ -2 -
Крошка-сын к отцу пришел,
и спросила кроха:
Что такое "хорошо"
И что такое "плохо"
...

Мастер ФУ! А зачем нужны системные вызовы? - Затем же, зачем тебе нужен твой сосуд
Какой сосуд? - решил уточнить ученик - А вот какой! - крикнул мастер, ударяя ученика палкой

Это только у меня вызывает омерзение этот, с позволения сказать, -- "мастер", которого вообще следовало бы изолировать?

И ушла рыдая кроха,
Вытирая кровь рубашкой.
Нет, не ждите от ребенка
Уважения к "папашке".
dementiy 19.07.2010 12:14 #
+ 0 -
А чем Вам не нравится вступление? Метафора, как метафора, и ничего в ней плохого я не вижу.
zhevak 19.07.2010 19:45 #
+ -2 -
Ай, не обращайте внимание!

Жаль, что Вы не видите в ней агрессии сумасшедшего идиота. Если это так, то я не смогу Вам объяснить, что мне там не нравиться. Это как слепому от рождения рассказать какого цвета небо, какого цвета трава, снег, одуванчики?

Как говориться, ничего личного. Просто не люблю когда проявляют жестокость. Даже иносказательно.

Куда ходит ваша энергия? -- Она уходит в след за вашими мыслями. Думайте позитивно, созидательно и мир вокруг вас начнет меняться в эту же сторону.

Извините за лишнюю мораль. И в мыслях не было -- учить кого-то! Моя реплика -- это всего лишь моя позиция, но не правила для всех.
kstep 19.07.2010 18:52 #
+ -1 -
Ох уж мне эти моралисты...

В хорошем качестве hd видео

Онлайн видео бесплатно


Смотреть русское с разговорами видео

Online video HD

Видео скачать на телефон

Русские фильмы бесплатно

Full HD video online

Смотреть видео онлайн

Смотреть HD видео бесплатно

School смотреть онлайн