C - вывести символ через ассемблерную вставку

381
10 февраля 2017, 04:12

Под 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?

Answer 1

Видимо проблема в регистре %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]);
}

Использованные материалы:

  • Находим код системного вызова mkdir (Habrahabr)
  • Ассемблерные вставки в GCC (GameDev)

Дополнительно и более подробно:

  • https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html

P.S.: спасибо за интересный вопрос

Answer 2

Вариант для 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
  • What are the calling conventions for UNIX & Linux system calls on x86-64
  • How to invoke a system call via sysenter in inline assembly (x86/amd64 linux)?

Аргументы для системного вызова передаются через регистры без стека. Порядок аргументов (AMD64 ABI A.2.1.1):

  1. %rdi ("D")
  2. %rsi ("S")
  3. %rdx ("d")
  4. %r10
  5. %r8
  6. %r9

Номер системного вызова 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);
READ ALSO
Не могу понять, где подвох

Не могу понять, где подвох

Хочу написать разложение в степенной ряд функцию ln(x+1) по известной всем формуле (можно посмотреть на сайте http://edusernam

272
Как вывести вывод командной строки в CheckedListBox?

Как вывести вывод командной строки в CheckedListBox?

У меня есть программа на с++ с графическим интерфейсом написанная на Visual Studio 2012Скриншот прикрепил (mal

305
Объявление указателей через запятую

Объявление указателей через запятую

Данное предложение объявляет два указателя или указатель и объект? Верно ли это для любого типа переменных?

298
Логирование входа в phpmyadmin

Логирование входа в phpmyadmin

Можно ли каким-либо образом посмотреть логи входа в phpmyadmin (логин, время)? Или самому реализовать логирование? Phpmyadmin находится на VPS (CentOS)

399