LINUX.ORG.RU

Боль, безысходность, работа с ассемблером из под 64х разрядной системы.

 ,


0

1

Здравствуйте уважаемые форумчане, недавно начал ознакомление с ассемблером, и ввиду пока что слабой осведомленности в данном вопросе сразу столкнулся с следующей проблемой: в университете у нас на всех машинах стоят 32х разрядные системы и соответственно преподают нам язык именно с расчетом на учебные компьютеры, дома же у меня стоит 64х разрядная система, а так как синтаксис для данных, как мне удалось разузнать(спасибо дедушке гуглу за это). Теперь перейду к конкретным примерам.

Функция для поиска среднего арифметического трех чисел:

.intel_syntax noprefix
.globl avg3_asm
.type avg3_asm, @function
avg3_asm:
  push rdx
  push rbx
  mov rax, [rsp + 12]
  add rax, [rsp + 16]
  add rax, [rsp + 20]
  mov rbx, 3
  cltd
  idiv rbx
  pop rbx
  pop rdx
  ret  
//Функция для запуска на си:
# include <stdio.h>

extern int avg3_asm(int, int, int);

int main(void)
{
  int a, b, c;
  scanf("%d%d%d", &a, &b, &c);
  printf("%d\n", avg3_asm(a, b, c));
  return 0;
} 

Ввод: 1,2,3 Вывод: 4195845

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



Последнее исправление: beastie (всего исправлений: 1)

для архитектуры amd64 используется другой ABI, в частности соглашение о вызовах функций, аргументы сначала передаются через регистры, а если их не хватает, то потом через стек

Harald ★★★★★
()

дома же у меня стоит 64х разрядная система

Ну так заюзай в виртуалке любую 32-битную систему, прогай там и не парься.

Всё равно сессию завалишь.

EXL ★★★★★
()

Аргументы в x86_64 передауются сначала через регистры (п. 3.2.3) и только потом через стек.

mashina ★★★★★
()
Последнее исправление: mashina (всего исправлений: 1)

варианты такие:

1) не выпендриваться и писать на 32 битном ассемблере и компилировать под 32 бита

2) изучить матчасть 64 битной архитектуры

Harald ★★★★★
()

и стек на 64 битах пушится/попится кусками по 8 байтов, а не по 4

Harald ★★★★★
()

Архитектура x86_64 обратно совместима с x86, поэтому ты можешь невозбранно писать и запускать 32-битные программы, в т.ч. и на асме

annulen ★★★★★
()
Ответ на: комментарий от annulen

В том-то и дело, что при написании кода:

.intel_syntax noprefix
.globl avg3_asm
.type avg3_asm, @function
avg3_asm:
  push edx
  push ebx
  mov eax, [esp + 12]
  add eax, [esp + 16]
  add eax, [esp + 20]
  mov ebx, 3
  cltd
  idiv ebx
  pop ebx
  pop edx
  ret
Возвращает ошибки:

  • test.s:5: Error: operand type mismatch for `push'
  • test.s:6: Error: operand type mismatch for `push'
  • test.s:13: Error: operand type mismatch for `pop'
  • test.s:14: Error: operand type mismatch for `pop'
DLamer
() автор топика
Ответ на: комментарий от Deleted

Для статического ассемблерного бинаря не нужен мультилиб.

anonymous
()
Ответ на: комментарий от DLamer

Укажи ассемблеру чтобы он собирал 32x битный elf.

anonymous
()
Ответ на: комментарий от DLamer

Это не вопрос был. Я имею в виду, что тебе нужен ассемблер, умеющий в 32-битный код. Брось вообще этот GAS и используй лучше YASM или NASM.

Deleted
()
Последнее исправление: romeo250501 (всего исправлений: 1)
Ответ на: комментарий от FeyFre

для чего это нужно делать? я понимаю что проц выдаст исключение и программа не будет дальше работать. почему он без этого не хочет работать, ему то какая разница...

SmilePlz
()
Ответ на: комментарий от Deleted

Ну тогда binutils придется собрать самому, это не проблема

annulen ★★★★★
()
Ответ на: комментарий от SmilePlz

Потому что гладиолус. Маны пишут idiv делит RDX:RAX(EDX:EAX/DX:AX) на что попросили, а не просто *AX. Как пить дать в RDX не ноль, вот и результат не то что ждали.
Это конечно не отменяет других косяков(callconv).

FeyFre ★★★★
()
Ответ на: комментарий от DLamer

Или лучше поставь gcc-multilib и g++-multilib

anonymous
()
Ответ на: комментарий от anonymous

Пфф, что за бред вы советуете? 32-битному ассемблеру не нужны никакие libc. И вообще, зачем тут Си? Топикстартер пусть без Си на голом ассемблере все пишет, дергая вызовы write-read

SZT ★★★★★
()

желаю открыть AT&T синтаксис а так же все то множество ISA под которое придется переписывать асмокод.

exception13 ★★★★★
()
Ответ на: комментарий от anonymous

Я знаю, но дело, возможно, в том, что собранная версия не для i686.

Deleted
()
Последнее исправление: romeo250501 (всего исправлений: 1)
Ответ на: комментарий от anonymous

А ты следил?

Боль, безысходность, работа с ассемблером из под 64х разрядной системы. (комментарий)

И вообще, зачем тут Си? Топикстартер пусть без Си на голом ассемблере все пишет, дергая вызовы write-read

SZT ★★★★★
()
Ответ на: комментарий от anonymous

Я это пишу в ответ на советы ставить какие-то хедеры к glibc для ассемблера, а не конкретно тебе. И ТС-у я советую пойти http://stolyarov.info/books/asm_unix скачать, там все достаточно понятно изложено, с прицелом на Linux и без всяких glibc.

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)

gdb показывает, что первый аргумент передается в rax, второй в rcx, третий в rdx.

anonymous
()

мне показалось возможно, но с таким уровнем компетенции студента, достаточно вопрос решить самостоятельно и да, смахивает на толстоту...

виртуалки, контейнеры никто не отменял ...

anonymous
()
Ответ на: комментарий от FeyFre

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

SmilePlz
()

а так как синтаксис для данных, как мне удалось разузнать(спасибо дедушке гуглу за это).

А так как синтаксис что?

andreyu ★★★★★
()
Ответ на: комментарий от SmilePlz

Потому что «EDX:EAX» — это не «разделить EDX на EAX», а конкатенация. Делится 64-битное значение на 32-битное. Очевидно, что если ты хочешь разделить 32-битное значение на 32-битное, то тебе надо делить EAX, а не (EAX + мусор << 32).

anonymous
()

myfunc.asm:

global  myfunc

section .text
myfunc: 
        mov     rax, rdi        ; Первый пареметр
        add     rax, rsi        ; Второй параметр
        add     rax, rdx        ; Третий параметр
        cqo                     ; Расширяем RAX до RDX:RAX с учётом знака
        mov     rcx, 3          ; Делитель в RCX, что бы не сохранять RBX 
        idiv    rcx             ; RDX:RAX делим на RCX, частное в RAX (результат), остаток в EDX (побоку)
        ret

test.c:

#include <stdio.h>
#include <inttypes.h>

extern int64_t myfunc(int64_t a, int64_t b, int64_t c);

void test_myfunc(int64_t a, int64_t b, int64_t c) {
  printf("myfunc(%" PRId64 ", %" PRId64 ", %" PRId64 ")=%" PRId64 "\n", a, b, c, myfunc(a,b,c));
}

int main() {
  test_myfunc(1, 2, 3);
  test_myfunc(-1,0,1);
  test_myfunc(-1,-2,-3);
  return 0;
}

Компиляция и сборка:

$ nasm -f elf64 -o myfunc.o myfunc.asm
$ gcc -std=c99 test.c myfunc.o

З.Ы.: Аккуратно смотри под какую платформу компилируешь и пишешь, самое главное понять что есть такое «соглашение о вызове». Это позволит понять в каком порядке передаются параметры, какие регистры можно менять, а какие надо восстановить. Для 64-битных Linux - это System V Application Binary Interface AMD64 Architecture Processor Supplement (на странице 21 про использование регистров).

AlexVR ★★★★★
()
Последнее исправление: AlexVR (всего исправлений: 1)
Ответ на: комментарий от DLamer

Попробовал добавить xor rdx,rdx перед делением: было- 4195845, стало- 1433054375, ожидалось- 2

На какой ещё. у тебя же есть cltd в коде, слушай больше бред. Твоя проблема, что ты брал числа не от туда, куда они были положены компилятором Си.

AlexVR ★★★★★
()
Ответ на: комментарий от SmilePlz

Xdx - не обязан быть 0, Xdx - старшее слово делимого, может быть что угодно.
В данном конкретном случае ТС не вкурсе этого(как и его преподаватели) и явно работает со словами(а не с двойными словами), и ему необходимо просто занулять DX.

Перейдите по ссылке - много вопросов отпадет.

FeyFre ★★★★
()
Ответ на: комментарий от FeyFre

и ему необходимо просто занулять DX.

БРЕД. У него знаковое деление, там должно быть расширение знакового через CLTD или CQO.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Да, да. Расширить знаково. На практике не встречал необходимости делить знаковые, ну совсем, поэтому xor rdx,rdx и в почете.

FeyFre ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.