Под linux x86_64 не получается вывести символ через ассемблерную вставку. Делаю так:
void putc(char symbol) {
__asm__ (
"mov $4, %%eax\n\t"
"mov $1, %%ebx\n\t"
"mov %0, %%ecx\n\t"
"mov $1, %%edx\n\t"
"int $0x80\n\t"
: /* no output registers */
: "c" ((unsigned int)symbol)
);
}
GCC генерирует следующий ассемблер:
mov $4, %eax
mov $1, %ebx
mov %ecx, %ecx
mov $1, %edx
int $0x80
Видимо проблема в регистре %ecx
, что в него не вставляется адрес символа symbol
.
Компилирую gcc:
$ gcc -ffreestanding -Wall -pedantic -ansi -std=c89 main.c -o main
$ ./main
Почему не выводит символ на stdout
?
Видимо проблема в регистре %ecx
, что в него не вставляется адрес символа symbol
.
Совершенно верно. Но вы можете передать в ассемблерную вставку адрес symbol
, просто поставив оператор взятия адреса (&) перед именем переменной.
Убедитесь, что компилируете для x86, и тогда заработает. (За это отвечает флаг компилятора -m32
; вероятно, придётся допоставить 32-ух битные библиотеки).
Вот, на мой взгляд, несколько улучшенный вариант:
int putc(char symbol) {
int result;
__asm__ (
"int $0x80"
: "=a" (result) /* возвращаемое значение */
: "a" ((unsigned int) 4) /* номер системного вызова */
, "b" ((unsigned int) 1) /* номер файла для вывода */
, "c" ((unsigned int) &symbol) /* адрес буфера */
, "d" ((unsigned int) 1) /* сколько байт вывести */
);
return result;
}
int main(int argc, char* argv[]) {
return putc(argv[argc > 1][0]);
}
Использованные материалы:
Дополнительно и более подробно:
P.S.: спасибо за интересный вопрос
Вариант для AMD64 архитектуры:
int putchar_inline_assembly(char c)
{
int ret;
__asm__ volatile (
/* AssemblerTemplate */
"syscall\n\t"
/* : OutputOperands */
: "=a"( ret )
/* [ : InputOperands */
: "D"( 1 ), /* write to stdout */
"S"( &c ), /* buffer address */
"d"( 1 ), /* write 1 character from the buffer */
"a"( 1 ) /* use the write syscall */
/* [ : Clobbers ] ] */
: "rcx", "r11", "memory"
);
return ret;
}
gcc может это превратить в:
putchar_inline_assembly(char):
movb %dil, -4(%rsp)
movl $1, %edi
leaq -4(%rsp), %rsi
movl %edi, %edx
movl %edi, %eax
syscall
ret
Что похоже на ассемблер, который можно руками написать для write(1)
системного вызова:
# ssize_t write(int fildes, const void *buf, size_t nbyte);
movq $1, %rdi # write to stdout
movq $buf, %rsi # buffer address
movq $1, %rdx # write 1 character from the buffer
movq $1, %rax # use the write syscall
syscall # make syscall
Аргументы для системного вызова передаются через регистры без стека. Порядок аргументов (AMD64 ABI A.2.1.1):
Номер системного вызова write()
в Linux x86-64 равен 1
(grep write /usr/include/asm/unistd_64.h
) и передается
в syscall через %rax ("a") регистр. После syscall %rcx и %r11 регистры могут быть затёрты
(A.2.1.2).
Ассемблерная вставка выполняет аналог C кода:
ret = write(/*STDOUT_FILENO*/1, &c, /*sizeof char*/1);
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Хочу написать разложение в степенной ряд функцию ln(x+1) по известной всем формуле (можно посмотреть на сайте http://edusernam
У меня есть программа на с++ с графическим интерфейсом написанная на Visual Studio 2012Скриншот прикрепил (mal
Данное предложение объявляет два указателя или указатель и объект? Верно ли это для любого типа переменных?
Можно ли каким-либо образом посмотреть логи входа в phpmyadmin (логин, время)? Или самому реализовать логирование? Phpmyadmin находится на VPS (CentOS)