Я новичок в языке ассемблера, и мне нужно реализовать read, используя язык ассемблера x64
в MAC
. пока вот что я сделал:
;;;;;;ft_read.s;;;;;;
global _ft_read:
section .text
extern ___error
_ft_read:
mov rax, 0x2000003 ; store syscall value of read on rax
syscall ; call read and pass to it rdi , rsi, rdx ==> rax read(rdi, rsi, rdx)
cmp rax, 103 ; compare rax with 103 by subtracting 103 from rax ==> rax - 103
jl _ft_read_error ; if the result of cmp is less than 0 then jump to _ft_read_error
ret ; else return the rax value which is btw the return value of syscall
_ft_read_error:
push rax
call ___error
pop rcx
mov [rax], rcx
mov rax, -1
ret
как вы можете видеть выше, я вызываю чтение с системным вызовом, а затем сравниваю возвращаемое значение системного вызова чтения, хранящееся в rax, с 103
, я объясню, почему я сравниваю его с 103
, но перед этим позвольте мне объяснить еще кое-что, а именно errno (справочная страница Mac), вот что написано на странице руководства о errno
:
Когда системный вызов обнаруживает ошибку, он возвращает целочисленное значение, указывающее на сбой (обычно -1), и соответствующим образом устанавливает переменную errno. ‹Это позволяет интерпретировать сбой при получении -1 и принять соответствующие меры.› Успешные вызовы никогда не устанавливают errno; после установки он остается до тех пор, пока не произойдет другая ошибка. Его следует проверять только после ошибки. Обратите внимание, что ряд системных вызовов перегружает значения этих номеров ошибок и что значения должны интерпретироваться в соответствии с типом и обстоятельствами вызова.
Ниже приведен полный список ошибок и их названий, приведенных в ‹sys/errno.h›.
0
Ошибка 0. Не используется.
1
EPERM Операция не разрешена. Предпринята попытка выполнить операцию, ограниченную процессами с соответствующими привилегиями или владельцем файла или других ресурсов.
2
ENOENT Нет такого файла или каталога. Компонент указанного пути не существует, или путь представляет собой пустую строку.................................................. Я пропущу эту часть (кстати, я написал эту строку)...................................... .............
101
Тайм-аут ETIME STREAM ioctl(). Эта ошибка зарезервирована для использования в будущем.
102
EOPNOTSUPP Операция не поддерживается на сокете. Предпринятая операция не поддерживается для указанного типа сокета; например, пытаясь принять соединение через сокет дейтаграммы.
и, насколько я понимаю, и после того, как я много времени отлаживал с помощью lldb
, я заметил, что syscall
возвращает одно из тех чисел, которые показаны на странице руководства errno
, например, когда я передаю неверный файловый дескриптор, в моей функции ft_read
, используя приведенную ниже main.c
такой код:
int bad_file_des = -1337;// a file descriptor which it doesn't exist of course, you can change it with -42 as you like
ft_read(bad_file_des, buff, 300);
наш syscall
возвращает 9
, который хранится в rax
, поэтому я сравниваю, если rax
‹ 103 (поскольку значения errno от 0 до 102), затем перехожу к ft_read_error
, потому что это то, что он должен делать.
Ну, все работает, как я и планировал, но есть проблема, которая возникла из ниоткуда, когда я открываю существующий файл и передаю его файловый дескриптор моей функции ft_read
, как показано ниже main.c
, наш read syscall
возвращает "the number of bytes read is returned"
, это то, что read
системный вызов возвращается, как описано в руководстве:
В случае успеха возвращается число прочитанных байтов (ноль указывает на конец файла), и позиция в файле увеличивается на это число. Если это число меньше запрошенного числа байтов, это не является ошибкой; это может произойти, например, из-за того, что сейчас доступно меньше байтов (может быть, из-за того, что мы были близки к концу файла, или из-за того, что мы читаем из конвейера или из терминала), или из-за того, что функция read() была прервана сигнал. См. также ПРИМЕЧАНИЯ.
При ошибке возвращается -1, и errno устанавливается соответствующим образом. В этом случае остается неуказанным, изменяется ли позиция в файле (если есть).
и в моем основном это работает довольно хорошо, я передаю своей функции ft_read
хороший файловый дескриптор, буфер для хранения данных и 50 байт для чтения, поэтому syscall
вернет 50
, хранящееся в rax
, тогда сравнение делает свою работу ›› rax = 50
‹ 103, то он перейдет к ft_read_error
, даже если ошибки нет, просто потому, что 50
является одним из тех номеров ошибок errno
, которых нет в этом случае.
кто-то предлагает использовать jc
(переход, если установлен флаг переноса), а не jl
(переход, если меньше), как показано в коде ниже:
;;;;;;ft_read.s;;;;;;
global _ft_read:
section .text
extern ___error
_ft_read:
mov rax, 0x2000003 ; store syscall value of read on rax
syscall ; call read and pass to it rdi , rsi, rdx ==> rax read(rdi, rsi, rdx)
; deleted the cmp
jc _ft_read_error ; if carry flag is set then jump to _ft_read_error
ret ; else return the rax value which is btw the return value of syscall
_ft_read_error:
push rax
call ___error
pop rcx
mov [rax], rcx
mov rax, -1
ret
и угадайте, что это работает отлично, и errno
возвращает 0
, используя мой ft_read
, когда нет ошибки, и возвращает соответствующий номер ошибки, когда есть ошибка.
но проблема в том, что я не знаю, почему устанавливается carry flag
, когда нет cmp
, устанавливает ли системный вызов carry flag
, когда во время вызова возникает ошибка, или в фоновом режиме происходит что-то еще? Мне нужно подробное объяснение связи между системным вызовом и carry flag
, я все еще новичок в ассемблере и очень хочу его изучить, и заранее спасибо.
Какова связь между syscall
и carry flag
и как ее устанавливает syscall
?
это моя функция main.c
, которую я использую для компиляции ассемблерного кода выше:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
ssize_t ft_read(int fildes, void *buf, size_t nbyte);
int main()
{
/*-----------------------------------------------------------------------*/
///////////////////////////////////////////////////////////////////////////
/********************************ft_read**********************************/
int fd = open("./main.c", O_RDONLY);
char *buff = calloc(sizeof(char), 50 + 1);
int ret = ft_read(fd, buff, 50);
printf("ret value = %d, error value = %d : %s\n", ret, errno, strerror(errno));
//don't forget to free ur buffer bro, this is just a test main don't be like me.
return (0);
}
stc
и его противоположностьclc
, которые использовались рядом вызываемыми функциями для указания на ошибку или успех через флаг переноса. (Обработчики функций прерывания должны изменить кадр стекаiret
, чтобы установить или очистить CF вызывающей программы в стеке.) - person ecm   schedule 13.11.2020syscall
.errno
- это CRT. По крайней мере, так это работает в Linux, может быть и в Darwin. Системные вызовы Linux возвращают отрицательное значение при ошибке, и errno устанавливается из этих отрицательных значений. Вы должны проверить системный вызов Дарвина ABI, а не человека. - person Margaret Bloom   schedule 13.11.2020