Assembler hello world linux

Ассемблер для Linux. Для начала Hello, world! (книга)

Приветствую всех на моем канале Old Programmer . Продолжаем публиковать материалы из книги об ассемблере GAS .

Пишем первую программу Hello, world!

Ну вот настало время, когда нужно и можно написать программу«Hello, world!». Это не сложно ведь у нас имеется шаблон программы. И мы просто должны его правильно заполнить. Но это еще не все мы должны знать как вывести информацию на консоль. Как уже понятно из предыдущего параграфа одним из инструментов, который широко используется при программировании на ассемблере в операционной системе Linux являются системные вызовы. Мы вызываем функцию ядра, которая выполняет то или иное системное действие. В предыдущем параграфе мы вызывали функцию, которая заканчивает выполнение текущей программы (процесса). И как легко догадаться мы должны теперь использовать системный вызов, который выводит строку текста на консоль. В дальнейшем мы дадим список системных вызовов операционной системы Linux, а пока будем знакомить с ними последовательно, по мере их поступления. В данном параграфе это системный вызов с номером 1 — вывод на консоль.

Как и говорили, мы возьмем программу из листинга 3 и дополним ее. Мы дополним ее еще одним системным вызовом. Кроме этого добавим в программу еще одну секцию для хранения там данных, а точнее строки, которую будем выводить. Эта программа представлена в листинге 4.

Давайте подробно разберем программу из листинга 4, с учетом того, что уже было объяснено для листинга 3.

1. Как мы видим у нас появился еще один системный вызов с номером 1 (число, помещаемое в регистр rax ). Но у этого системного вызова есть еще и другие параметры, которые прокомментированы в программе. rdi — стандартный поток вывода, rsi — адрес (метка) начала выводимой строки, rdx — длина строки в байтах.

2. В программе имеются две секции: .data — для данных (глобальных переменных), .text — для программного кода. В большинстве случаев нам будет вполне хватать именно двух секций.

3. В программе имеются две метки. С одной, глобальной меткой _start мы уже знакомы. Метка msg указывает на начало строки. Для нас смысл обеих меток равнозначен — обе указывают на некоторый адрес.

4. Давайте посмотрим еще на один важный элемент. Это len . Это не команда процессора. Ее можно назвать макропеременной или переменной времени трансляции. Ее значение определяется до того, как кодируются команда процессора. Точка в ассемблере GAS принимает значение текущего адреса, поэтому . — msg это просто длина строки. Конечно команду процессора mov $len, %rdx можно заменить другой: mov $n, %rdx, где n количество байтов в строке. Но с использованием переменной len мы реализовали более общий подход. Можно брать строки разной длины и не считать, сколько там на самом деле байтов. Подчеркнем лишний раз, что считать нужно количество байтов, при кодировке UTF-8, которую мы используем, а это может быть проблематично.

Читайте также:  Convert file to hex linux

5. Обратим внимание еще на один элемент программы, точнее выводимой на консоль строки. Это символ ‘ \n ‘. Конечно, вы с ним знакомы из языка C. Это символ перевода строки. Для вывода на консоль с помощью системного вызова с номером 1 этот символ работает ровно также.

Трансляции программы производится уже известным на способом:

as —64 l4.s -o l4.o
ld -s l4.o -o l4

Подписываемся на мой канал Old Programmer и пишем свои комментарии.

Источник

Записки программиста

Написание и отладка кода на ассемблере x86/x64 в Linux

Сегодня мы поговорим о программировании на ассемблере. Вопрос «зачем кому-то в третьем тысячелетии может прийти в голову писать что-то на ассемблере» раскрыт в заметке Зачем нужно знать всякие низкоуровневые вещи, поэтому здесь мы к нему возвращаться не будем. Отмечу, что в рамках поста мы сосредоточимся на вопросе компиляции и отладки программ на ассемблере. Сам же язык ассемблера заслуживает отдельного большого поста, а то и серии постов.

Если вы знаете ассемблер, то любая программа для вас — open source.

Народная мудрость.

Введение

Существует два широко используемых ассемблерных синтаксиса — так называемые AT&T-синтаксис и Intel-синтаксис. Они не сильно друг от друга отличаются и легко переводятся один в другой. В мире Windows принято использовать синтаксис Intel. В мире *nix систем, наоборот, практически всегда используется синтаксис AT&T, а синтаксис Intel встречается крайне редко (например, он используется в утилите perf). Поскольку Windows, как известно, не существует, далее мы сосредоточимся на правильном AT&T-синтаксисе 🙂

Компиляторов ассемблера существует много. Мы будем использовать GNU Assembler (он же GAS, он же /usr/bin/as). Скорее всего, он уже есть вашей системе. К тому же, если вы пользуетесь GCC и собираетесь писать ассемблерные вставки в коде на C, то именно с этим ассемблером вам предстоит работать. Из достойных альтернатив GAS можно отметить NASM и FASM.

Наконец, язык ассемблера отличается в зависимости от архитектуры процессора. Пока что мы сосредоточимся на ассемблере для x86 (он же i386) и x64 (он же amd64), так как именно с этими архитектурами приходится чаще всего иметь дело. Впрочем, ARM тоже весьма распространен, главным образом на телефонах и планшетах. Еще из сравнительно популярного есть SPARC и PowerPC, но шансы столкнуться с ними весьма малы. Отмечу, что x86 и x64 можно было бы рассматривать отдельно, но эти архитектуры во многом похожи, поэтому я не вижу в этом большого смысла.

«Hello, world» на int 0 x80

Рассмотрим типичный «Hello, world» для архитектуры x86 и Linux:

.data
msg :
. ascii «Hello, world!\n»
. set len , . — msg

. globl _start
_start :
# write
mov $ 4 , % eax
mov $ 1 , % ebx
mov $msg , % ecx
mov $len , % edx
int $ 0x80

# exit
mov $ 1 , % eax
xor % ebx , % ebx
int $ 0x80

# Или: gcc -m32 -c hello-int80.s
as —32 hello-int80.s -o hello-int80.o
ld -melf_i386 -s hello-int80.o -o hello-int80

Читайте также:  Red hat linux logo

Коротко рассмотрим первые несколько действий, выполняемых программой: (1) программа начинает выполнение с метки _start, (2) в регистр eax кладется значение 4, (3) в регистр ebx помещается значение 1, (4) в регистр ecx кладется адрес строки, (5) в регистр edx кладется ее длина, (6) происходит прерывание 0 x80. Так в мире Linux традиционно происходит выполнение системных вызовов. Конкретно int 0 x80 считается устаревшим и медленным, но из соображений обратной совместимости он все еще работает. Далее мы рассмотрим и более новые механизмы.

Нетрудно догадаться, что eax — это номер системного вызова, а ebx, ecx и edx — его аргументы. Какой системный вызов имеет какой номер можно подсмотреть в файлах:

# для x86
/ usr / include / x86_64-linux-gnu / asm / unistd_32.h
# для x64
/ usr / include / x86_64-linux-gnu / asm / unistd_64.h

Следующая строчка из файла unistd_32.h:

… как бы намекает нам, что производится вызов write. В свою очередь, из man 2 write мы можем узнать, какие аргументы этот системный вызов принимает:

ssize_t write ( int fd , const void * buf , size_t count ) ;

То есть, рассмотренный код эквивалентен:

Затем аналогичным образом производится вызов:

В общем случае системный вызов через 0 x80 производится по следующим правилам. Регистру eax присваивается номер системного вызова из unistd_32.h. До шести аргументов помещаются в регистры ebx, ecx, edx, esi, edi и ebp. Возвращаемое значение помещается в регистр eax. Значения остальных регистров при возвращении из системного вызова остаются прежними.

Выполнение системного вызова через sysenter

Начиная с i586 появилась инструкция sysenter, специально предназначенная (чего нельзя сказать об инструкции int) для выполнения системных вызовов.

Рассмотрим пример использования ее на Linux:

.data
msg :
. ascii «Hello, world!\n»
len = . — msg

_start :
# write
mov $ 4 , % eax
mov $ 1 , % ebx
mov $msg , % ecx
mov $len , % edx
push $write_ret
push % ecx
push % edx
push % ebp
mov % esp , % ebp
sysenter

write_ret :
# exit
mov $ 1 , % eax
xor % ebx , % ebx
push $exit_ret
push % ecx
push % edx
push % ebp
mov % esp , % ebp
sysenter

Сборка осуществляется аналогично сборке предыдущего примера.

Как видите, принцип тот же, что при использовании int 0 x80, только перед выполнением sysenter требуются поместить в стек адрес, по которому следует вернуть управление, а также совершить кое-какие дополнительные манипуляции с регистрами. Причины этого более подробно объясняются здесь.

Инструкция sysenter работает быстрее int 0 x80 и является предпочтительным способом совершения системных вызовов на x86.

Выполнение системного вызова через syscall

До сих пор речь шла о 32-х битных программах. На x64 выполнение системных вызовов осуществляется так:

.data
msg :
. ascii «Hello, world!\n»
. set len , . — msg

. globl _start
_start :
# write
mov $ 1 , % rax
mov $ 1 , % rdi
mov $msg , % rsi
mov $len , % rdx
syscall

# exit
mov $ 60 , % rax
xor % rdi , % rdi
syscall

Собирается программа таким образом:

Принцип все тот же, но есть важные отличия. Номера системных вызовов нужно брать из unistd_64.h, а не из unistd_32.h. Как видите, они совершенно другие. Так как это 64-х битный код, то и регистры мы используем 64-х битные. Номер системного вызова помещается в rax. До шести аргументов передается через регистры rdi, rsi, rdx, r10, r8 и r9. Возвращаемое значение помещается в регистр rax. Значения, сохраненные в остальных регистрах, при возвращении из системного вызова остаются прежними, за исключением регистров rcx и r11.

Интересно, что в программе под x64 можно одновременно использовать системные вызовы как через syscall, так и через int 0 x80.

Читайте также:  Lemp linux mysql php

Отладка ассемблерного кода в GDB

Статья была бы не полной, если бы мы не затронули вопрос отладки всего этого хозяйства. Так как мы все равно очень плотно сидим на GNU-стэке, в качестве отладчика воспользуемся GDB. По большому счету, отладка не сильно отличается от отладки обычного кода на C, но есть нюансы.

Например, вы не можете так просто взять и поставить брейкпоинт на процедуру main. Как минимум, у вас попросту нет отладочных символов с информацией о том, где эту main искать. Решение заключается в том, чтобы самостоятельно определить адрес точки входа в программу и поставить брейкпоинт на этот адрес:

Источник

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

A simplistic Hello World for x64 nasm w/ intel syntax

KyleNehman/Linux-ASM-Hello-World

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

A simplistic Hello World for nasm w/ intel syntax

The makefile assumes you’ve got nasm and ld installed, and uses the ‘-m elf_i386’ linker flag for my own pc, you’ll probably have to adjust this.

git clone https://github.com/KyleNehman/Linux-ASM-Hello-World.git cd Linux-ASM-Hello-World/ make make run 

What’s this crazy code do? (Breakdown)

This program is more or less the equivalent of the following python:

Let’s go line by line, since this is for total beginners.

 section .data msg: db "Hello, world!", 0x0a msgLen: equ $-msg 
 section .text global _start _start: mov ecx, msg mov edx, msgLen call print call exit 
 print: push eax push ebx mov eax, 4 mov ebx, 1 int 0x80 pop ebx pop eax ret 
 exit: mov eax, 1 mov ebx, 0 int 0x80 

About

A simplistic Hello World for x64 nasm w/ intel syntax

Источник

Оцените статью
Adblock
detector