Программа сложения most basi c x86 - PullRequest
0 голосов
/ 01 августа 2020

Я написал следующую базовую c программу для сложения двух чисел, 1+2, как показано ниже:

.globl main

main:

    # put 1 (1 byte int/char) into accumulator register
    mov     $1,     %eax

    # add 2 (1 byte int/char), storing result in accumulator
    add     $2,     %eax

    # move the result of the accumulator into Data register (input/output)
    mov     %eax,   %edx

    ret

При компиляции это возвращает ожидаемый результат:

$ gcc d.s -o d2.out && ./d2.out; echo $?
3

У меня есть несколько вопросов по этой программе:

  • Является ли это более или менее подходящей программой, или я неправильно использую какие-либо операции, et c.?
  • Всегда ли в файле сборки должна быть одна globl функция, такая как main, или она может когда-нибудь, например, удалить части main / .globl main и просто «запускать код построчно -line "?
  • Наконец, какой ресурс лучше всего подходит для поиска кодов операций? Я обычно использую Google, и он возвращает разные результаты: было бы неплохо иметь стандартный ресурс, например Python docs, где я могу просто добавить одну страницу в закладки и посмотреть все на ней.

Ответы [ 2 ]

3 голосов
/ 01 августа 2020

mov для EDX бессмысленно, регистр возвращаемого значения - AL / AX / EAX / RAX / RDX: RAX для ширины от 1 до 16 байтов на x86-64. EDX или RDX задействованы только для широких возвращаемых значений, слишком широких, чтобы поместиться в RAX. (Или в 32-битном режиме 64-битные значения возвращаются в паре регистров EDX: EAX, потому что RAX отсутствует.)

Это верно для всех стандартных 32-битных и x86-64 соглашений о вызовах. , включая i386 и x86-64 System V ABI, используемые в GNU / Linux.

Если вы пишете main или любую функцию, которую хотите вызвать из другого файла, это должен быть символ .globl. (Если только вы не .include "foo.s" вместо построения отдельно + связывание.) Это то, что делает его видимым в таблице символов, чтобы компоновщик разрешал ссылки на него. например, из a call main в уже скомпилированном коде для _start, в crt0.o или что-то в этом роде, что вы можете увидеть ссылку g cc, если вы запустите gcc -v foo.S. (Это было чрезмерное упрощение; glib c s _start на самом деле передает основной адрес как аргумент в __libc_start_main, который находится в libc.so.6, поэтому есть код из библиотеки c, который запускается раньше main. См. Linux Запуск программы x86 или - Какого черта нам добраться до main ()? )

Если вы создаете stati c исполняемый файл без CRT (определение _start вместо main и создание собственного exit_group системного вызова), вы можете просто добавить инструкции в файл и позволить компоновщику (ld ) выберите верхнюю часть раздела .text как точку входа ELF, если он не находит символ _start. (Используйте readelf -a a.out, чтобы увидеть подобную информацию.)

Если вы планируете запускать программу под GDB только для пошагового выполнения пары инструкций, которые вам интересны, вы можете даже опустить exit-clean часть. (Для этого используйте команду GDB starti для запуска с временной точкой останова перед первой инструкцией в пространстве пользователя, поэтому вам не нужно устанавливать точку останова вручную по абсолютному адресу (потому что нет символа).)

$ cat > foo.S
mov $1 + 2, %edi     # do the math at assemble time
mov $231, %eax         # _NR_exit_group
syscall

$ gcc -static -no-pie -nostdlib foo.S      # like as + ld manually
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000

$ ./a.out ; echo $?
3

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffe0706a3c0 /* 54 vars */) = 0
exit_group(3)                           = ?
+++ exited with 3 +++

Если ваша система 32-битная, поэтому as по умолчанию работает в 32-битном режиме, используйте 32-битный int $0x80 с другими регистрами.

Наконец, какой ресурс лучше для поиска кодов операций?

Я обычно оставляю вкладку браузера открытой для https://www.felixcloutier.com/x86/, что является HTML парочкой из руководства Intel тома 2. В исходном PDF-файле есть несколько вводных глав о том, как читать записи, так что проверьте его, если вы найдете какие-либо обозначения сбивающими с толку. Существуют более старые отрывки руководств Intel, в которых нет инструкций по SIMD, так что это бесполезно для меня, но, возможно, то, что вы хотите как новичок.

Другие ресурсы связаны с x86 tag wiki , включая http://ref.x86asm.net/coder64.html, который организован по коду операции, а не по мнемоникам c, и имеет столбцы с краткой справкой, чтобы напомнить вам, читает ли инструкция или модифицирует ФЛАГИ, и если да, то какие и тому подобное.

2 голосов
/ 01 августа 2020

Это более или менее нормальная программа, или я неправильно использую какие-либо операции, et c.?

Для начала, да.

Однако сборка - это эффективность, поэтому последний оператор не нужен:

mov     %eax,   %edx

Всегда ли в файле сборки должна быть одна глобальная функция, например main

Не обязательно. Это может быть какая-то другая функция, которую вы можете вызвать, например, из вашего кода C / C ++. Но если вы хотите сделать из него исполняемый файл, вам понадобится main или _start, если вы используете ld в качестве компоновщика.

"запускайте код построчно. line "?

Для этого вам понадобится отладчик. И это будет самое важное , если вы хотите изучить сборку. Вы захотите посмотреть регистры, посмотреть, как меняются значения, что происходит с флагами и т.д. c. Я дал ответ , который немного объясняет, как настроить отладчик и пройти через ваш код. Вам понадобится флаг -g при сборке с gcc для отладки вашего кода.

Базовый c пример:

  1. Компиляция с -g
gcc -g file.s -o file
запустить gdb в режиме tui.
> gdb --tui ./file
> start           # this will automatically start the program and break at main:
> layout regs     # show registers at the top (you will need this a lot)
> n               # next instruction
> si              # step into, when you use functions, si into function

Нажатие Enter в gdb автоматически выполнит последнюю команду снова. Это избавит вас от необходимости набирать n снова и снова. Еще несколько команд:

> b 2      # break at line 2
> b func   # break at label func
> b main   # break at main

> print/x  $eax  # print value in eax in hex form, there are other /format specifiers, print/d (decimal), print/s string, print/t (binary)
> x/s $eax    # print string pointed to by eax

> info frame   # look at the current stack frame

Это наиболее распространенные инструкции, которые вам понадобятся. Вы можете набрать help command_name, чтобы получить больше информации о командах. И есть различные чит-коды и c, которые помогут вам в этом.

Вы также можете получить gui, если хотите, лично мне они не очень нравятся. Касса Немивер, что очень хорошо. gdbgui можно настроить с помощью pip, но это не очень хорошо для отладки asm, так как наблюдение за регистрами - это боль. Есть ddd, который мне нравится больше всего, но это gui из 1970-х, так что ...

Наконец, какой ресурс для поиска кодов операций является лучшим?

Лучший ресурс - это руководства Intel, однако они могут быть слишком трудными для чтения, если вы только начинаете. Я бы порекомендовал Ссылку на asm x86 Феликса Клотье . В вики-странице тегов x86 есть много информации и ссылок.

Вы также можете прочитать Соглашения о вызовах для Linux и поиск Linux Syscalls , который вам понадобится довольно много. Если вы собираетесь программировать или просто хотите узнать больше о компьютерах, я настоятельно рекомендую прочитать книгу Programming from the Ground Up , которая находится в свободном доступе и использует сборку в стиле AT&T. Однако он немного устарел, поэтому вам придется гуглить. К нему есть приложение с общими инструкциями для x86, которые будут очень полезны.

...