Я довольно новичок в программировании на ассемблере. Я использую платформу x86 с GCC (Linux).

У меня есть функция, которую я хочу вызвать из C как:

myfunc ( unsigned char * s1, unsigned char * s2, int someint );

Функция возьмет ячейки памяти s1 и s2 и сравнит их, затем увеличит и сравнит и т. д., выполняя некоторую обработку по ходу дела. Это похоже на memcmp, но я делаю больше.

Мой вопрос: если я передам указатель в функцию сборки? И тогда как мне сказать «дайте мне значение, хранящееся по этому адресу памяти»?

Вот что у меня есть на данный момент:

Чтобы получить первую функцию arg ("s1") из стека, я делаю это (someaddress - это 32-битное целое число, и я работаю на 32-битном процессоре):

movl  8(%esp), %ecx
movl  %ecx, someaddress

Если я помещу somevar в %eax (или %ebx и т. д.), а затем напечатаю его с помощью %p, я увижу, что его адрес и адрес указателя на беззнаковый символ " s1" Я прошел то же самое. Но я подозреваю, что на самом деле я взял адрес памяти, преобразовал его в целое число, а затем поместил это целое число в какой-то адрес.

Например, если я сделаю это:

movl  pos1, %eax
movl  pos2, %ebx
cmp   (%eax),(%ebx)

Я получаю сообщение "Ошибка: слишком много ссылок на память для `cmp'". Я не совсем уверен, что это значит, кроме "ты облажался" ;-)

Так...

  • как передать указатель и сохранить его как указатель?
  • как использовать значение указанного указателя в сборке? (например, как *ptr в C)

Хочу ли я посмотреть на операнд LEA?

Я использую «Профессиональное программирование сборки» Ричарда Блюма в качестве руководства, но Блум, похоже, не освещает этот случай.

Обновить

Большое спасибо за ваш ученый ответ!

К сожалению, я все еще не могу разыменовать.

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

first_ptr points to 81 (should be 81) <-- from C program
the value is -1543299247 <-- printf called from within assembler
the value is -6028513 <-- printf called from within assembler
my function returned -6028513 <-- return value printed from C program

Программа C:

#include <stdio.h>
#include <string.h>

int main (void) {
        unsigned char first;
        unsigned char * first_ptr;

        first = 'Q';
        first_ptr = &first;

        printf ("first_ptr points to %i (should be 81)\n",*first_ptr);

        printf ("my function returned %i\n", myfunc(first_ptr));
        return 0;
}

Программа сборки:

.section .data

msg:
  .asciz "the value is %i\n"

.section .bss
.lcomm str, 8

.section .text
.type myfunc, @function
.globl myfunc
myfunc:

  # save stack
  pushl %ebp
  movl  %esp, %ebp

  # save string arg from stack to "str"
  movl  8(%esp), %ecx
  movl  %ecx, str

  # let's try printing the ecx dereference

  pushl (%ecx)
  pushl $msg
  call printf

  # put the value of str on the stack 
  # and call printf

  pushl (str)
  pushl $msg
  call printf

  # now return the character at pos1
  movl  (str), %eax

  # restore the stack
  movl  %ebp, %esp
  popl  %ebp

  ret
10
raindog308 4 Мар 2011 в 03:12
Проблемы с вашим обновленным кодом: для печати значения вы вводите 32 бита, а переменная - всего 8 бит. Вы можете либо расширить его до 32 бит (что и делает C), либо изменить строку формата. Обратите внимание, что младшие 8 битов -1543299247 фактически оцениваются как 81, как и ожидалось. Для второго вывода и возврата: вы пытаетесь использовать двойное разыменование, написав (str), а в x86 такого нет. Если вы спросите меня, ассемблер должен выдать за это ошибку, но вместо этого он молча опускает круглые скобки.
 – 
Jester
4 Мар 2011 в 19:00

1 ответ

По крайней мере, один из операндов cmp должен быть регистром. Если вы пытаетесь сравнить содержимое двух ячеек памяти, вам нужно поместить одну из них в регистр. Вы спросите, как внести его в реестр? Ну, вы уже сделали это со своим примером кода. Эта строка:

movl  8(%esp), %ecx

Берет 4 байта в %esp+8 и помещает их в %ecx. В C-подобном псевдокоде:

ecx = *(esp + 8);

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

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

  1. как передать указатель и сохранить его как указатель?

    Вы уже делаете это, и ваша инструкция movl 8(%esp), %ecx или что-то в этом роде сделает все, что вам нужно.

  2. как использовать значение указанного указателя в сборке? (например, как *ptr в C)

    Вам нужно снова использовать () - чтобы загрузить первый байт из указателя в %ecx из вашей инструкции выше, например:

    movb (%ecx), %edx
    

    В C-подобном псевдокоде, аналогичном тому, как я использовал его выше, эта инструкция выглядит так:

    edx = *(unsigned char *)ecx;
    
  3. Хочу ли я посмотреть операнд LEA?

    Вероятно, нет, исходя из описания вашей проблемы, которое вы предоставили. Впрочем, это всегда возможно. lea работает примерно так же, как оператор & в C. Например, эта инструкция:

    lea 12(%ecx), %edx
    

    можно перевести в наш псевдокод как:

    edx = &(*(ecx + 12))
    

    или проще:

    edx = ecx + 12
    

    Этот пример немного глуповат, так как мы используем относительно простой режим адресации, но как насчет чего-то вроде этого:

    lea 1(%edx,%ecx,4), %eax
    

    что значит:

    eax = &(edx[ecx * 4] + 1)
    

Часто самым простым решением подобных проблем является написание подпрограммы на C, затем ее компиляция и дизассемблирование результатов.

Изменить 2:

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

8
Carl Norum 4 Мар 2011 в 04:52