Я довольно новичок в программировании на ассемблере. Я использую платформу 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
1 ответ
По крайней мере, один из операндов cmp
должен быть регистром. Если вы пытаетесь сравнить содержимое двух ячеек памяти, вам нужно поместить одну из них в регистр. Вы спросите, как внести его в реестр? Ну, вы уже сделали это со своим примером кода. Эта строка:
movl 8(%esp), %ecx
Берет 4 байта в %esp+8 и помещает их в %ecx. В C-подобном псевдокоде:
ecx = *(esp + 8);
Надеюсь, это имеет смысл. Вы можете выполнять аналогичные операции, чтобы вывести указатели из стека в регистры, а затем разыменовать их, сравнить разыменованные значения и так далее. Дайте мне знать, если у вас есть еще вопросы!
Изменить - ваши разбитые вопросы:
как передать указатель и сохранить его как указатель?
Вы уже делаете это, и ваша инструкция
movl 8(%esp), %ecx
или что-то в этом роде сделает все, что вам нужно.как использовать значение указанного указателя в сборке? (например, как *ptr в C)
Вам нужно снова использовать
()
- чтобы загрузить первый байт из указателя в%ecx
из вашей инструкции выше, например:movb (%ecx), %edx
В C-подобном псевдокоде, аналогичном тому, как я использовал его выше, эта инструкция выглядит так:
edx = *(unsigned char *)ecx;
Хочу ли я посмотреть операнд 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:
Ваш пример программы кажется почти правильным, но вы пытаетесь разыменовать указатели в памяти - сначала поместите эти указатели в регистры, и все будет в порядке.
Похожие вопросы
Новые вопросы
assembly
Вопросы по языку ассемблера. Отметьте процессор и/или набор инструкций, которые вы используете, а также ассемблер, допустимый набор должен быть таким: ([assembly] [x86] [gnu-assembler] или [att]). Вместо этого используйте тег [.net-assembly] для сборок .NET, [cil] для языка ассемблера .NET и вместо байт-кода Java используйте тег java-bytecode-asm.