Это третья часть серии
- Часть первая: Начало работы с языком ассемблера
- Часть вторая: Поиск эффективного цикла разработки для написания языка ассемблера
Теперь, когда мы создали эффективную среду разработки, пришло время написать реальный код на языке ассемблера. В этом руководстве я расскажу о ресурсах и процессах, которым я следовал при создании простой программы на языке ассемблера.
Печать аргументов командной строки
Чтобы продемонстрировать концепции, давайте напишем ассемблерную программу, которая подсчитывает количество уникальных слов в текстовом файле, имя которого предоставляется в качестве аргумента.
В качестве первого шага к этой цели программе потребуется доступ к аргументам командной строки. Эта первая программа посвящена пониманию того, как получить доступ к аргументам командной строки с помощью сборки Linux x86_64.
Если вы новичок в программировании на ассемблере, я предлагаю это руководство в качестве отправной точки для изучения некоторых основ. Я ссылаюсь на некоторые концепции здесь без дополнительных пояснений.
Что такое аргумент командной строки
Аргументы командной строки - это текстовые входные данные, которые пользователи программы включают в командную строку при выполнении программы. Например, если есть исполняемая программа с именем a.out, ее можно запустить в системе Linux из командной строки с помощью команды
$ ./a.out
Кроме того, после имени исполняемого файла можно указать «аргументы командной строки». Например,
$ ./a.out 1 2 3
В этом случае есть три аргумента: 1, 2 и 3.
Стек вызовов
Это поможет понять стек вызовов, поскольку именно здесь хранятся аргументы командной строки, когда программа начинает выполняться.
Если вы не знакомы, стек - это структура данных Last In First Out (LIFO), которая находится в оперативной памяти компьютера. Указатель стека отслеживает верх стека. В x86_64 указателем стека является регистр $rpi. $rpi - это указатель на последний адрес памяти стека. Указатель стека может использоваться для доступа к стеку или могут использоваться две команды языка ассемблера: push и pop. push можно использовать для добавления значения в стек и обновления $rpi, чтобы указать на это новое добавленное значение. pop можно использовать для получения значения Последний вход из стека и обновления $rpi, чтобы он указывал на следующее значение в стеке. Формат этих команд (обратите внимание, что точка с запятой используется для обозначения комментариев на языке ассемблера, поэтому все, что находится после ;, игнорируется компилятором):
; add the value of $rcx to the stack push rcx; pop the "Last In" stack value into $rbx. ; In this case, the value of $rcx pop rbx
Доступ к аргументам командной строки на языке ассемблера X86–64
Когда программа начинает выполнение, все аргументы командной строки сохраняются в стеке. Верх стека будет содержать количество аргументов. Если вы программировали в c или c++, это обозначается как argc в main(), что означает количество аргументов.
Второе значение в стеке - это имя функции. Это считается первым аргументом и включается в общее количество аргументов. Следовательно, если вы не укажете аргументов при выполнении программы, argc будет равно единице.
Любые предоставленные аргументы будут следующими значениями в стеке. Например, если вы выполнили программу с командой
./a.out arg1 arg2 arg3
стек будет выглядеть следующим образом
Стек
Указатель стека ($ rpi) будет указывать на вершину стека, то есть на адрес памяти значения argc, 4. Конечно, все эти значения будут закодированы в двоичном формате.
Эта программа выводит argc и имя программы с новой строкой между ними:
Обратите внимание на использование push и pop для извлечения argc и имени программы из стека.
Также важно использование команды call. Вот как можно вызвать функцию в сборке, сохранив адрес строки, следующей за строкой, которая вызвала функцию. Затем можно использовать команду ret для возврата к этой строке. Указатель на ячейку памяти места возврата сохраняется в стеке. Обратите внимание, что это значение удаляется из стека, чтобы разрешить доступ к «скрытым» значениям, затем оно помещается обратно в стек, потому что это то место, где команда ret ожидает адрес памяти.
Спасибо за прочтение! В следующей части этой серии вы найдете несколько советов по написанию вашей первой программы на ассемблере (скоро)!