Получение адреса

Последнее обновление: 30.07.2023

Инструкция adr

Ранее мы рассмотрели, что для получения адреса метки применяется инструкция LDR, которой через знак "равно" (=) передается название метки:

LDR Xn, =label

Также получуть адрес можно с помощью инструкции ADR. Она принимает два операнда - регистр и метка, адрес которой будет загружаться в регистр:

ADR Xn, label

Например:

.global _start
 
_start: mov X0, #1
 adr X1, hello          // загружаем в X1 адрес строки hello
 mov X2, #18
 mov X8, #64 
 svc 0 
 
 mov X0, 0                  
 mov X8, #93
 svc 0
 
.data
hello: .ascii "Hello METANIT.COM\n"    // данные для вывода

Здесь в регистр X1 помещается адрес строки hello (адрес первого ее символа).

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

.global _start
 
_start: 
    adr X1, hello
    ldr X0, [X1]               // в X0 данные из адреса в X1 - символ "H"
    mov X8, #93 
    svc 0 
 
.data
hello: .ascii "Hello METANIT.COM\n"

Здесь в X1 загружается адрес строки hello, а в X0 помещаются те данные, которые располагаются по этому адресу. Поскольку адрес строки - это адрес первого ее символа, то в X1 помещается числовой код символа "H", то есть число 72.

Возможность получения адресов позволяет нам манипулировать адресами, производить с ними различные операции. Например:

.global _start
 
_start: mov X0, #1          // 1 = StdOut - поток вывода
 adr X1, hellometanit       // строка для вывода на экран
 adr X2, helloworld 
 sub X2, X2, X1               // длина строки - X2 = X2-X1
 mov X8, #64                
 svc 0                      
 
 mov X0, X2               // в X0 данные из адреса в X1 - символ "H"
 mov X8, #93 
 svc 0 
 
.data
hellometanit: .ascii "Hello METANIT.COM\n"
helloworld: .ascii "Hello World\n"

Здесь у нас две строки. Поскольку данные в секции .data располагаются последовательно, то чтобы найти количество символов (байтов) первой строки, нам надо от адреса второй строки отнять адрес первой строки:

 adr X1, hellometanit       // строка для вывода на экран
 adr X2, helloworld 
 sub X2, X2, X1               // длина строки - X2 = X2-X1

Или, к примеру, переместимся в строке на 1 символ вперед:

adr X1, hellometanit
add X1, X1, #1

Здесь к адресу в X1 прибавляется 1, то есть адрес смещается вперед на 1 байт, а в X1 окажется адрес второго символа.

Текущий адрес и динамическое вычисление адреса

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

MOV X4, .

То есть в регистре X4 будет храниться адрес текущей инструкции.

Можно прибавлять или вычитать определенное количество байт, получая новые адреса. Например:

. - 5      // на 16 байт от текущего адреса назад
. + 5      // на 8 байт от текущего адреса вперед

В ряде ситуаций это может упростить программу. Например, возьмем следующую программу:

.global _start 
_start:
// вывод строки на консоль
    mov x0, #1           // 1  - стандартный поток вывода
    ldr x1, =message     // загружаем адрес выводимой строки message
    mov x2, count        // в регистр x2 - количество символов
    mov x8, #64         // функция Linux для вывода в поток
    svc 0               // вызываем функцию Linux
// выход из программы
    mov x0, count      // для теста в качестве кода возврата помещаем в Х0 количество символов
    mov x8, #93       // устанавливаем функцию Linux для выхода из программы
    svc 0             // Вызываем функцию Linux

.data
message: .ascii "Hello METANIT.COM!\n"
.equ count, .-message

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

.equ count, .-message

Директива .equ определяет константу count, значение которой равно текущий_адрес - адрес_метки_message. Поскольку в данном случае 1 символ эквивалентен 1 байту, то count будет содержать количество символов.

Чтобы поместить констату в регистр, применяется инструкция mov:

mov x2, count

Таким образом, на консоль будет выведено

Hello METANIT.COM!
19

где 19 будет представлять количество символов.

Подобные динамические значения можно присваивать и переменным. Правда, конкретно в случае с подсчетом длины строки лучше использовать констату, потому что в случае с переменной нам нужно учитывать выравнивание по 4 байтам, которое требуется для загрузки переменной с помощью инструкции ldr:

.global _start 
_start:
// вывод строки на консоль
    mov x0, #1           // 1  - стандартный поток вывода
    ldr x1, =message     // загружаем адрес выводимой строки message
    ldr x2, count        // загружаем переменную count
    mov x8, #64         // функция Linux для вывода в поток
    svc 0               // вызываем функцию Linux
// выход из программы
    mov x8, #93       // устанавливаем функцию Linux для выхода из программы
    svc 0             // Вызываем функцию Linux

.data
message: .ascii "Hello METANIT.COM!\n"
.align 2    // из-за выравнивания размер переменная count будет некорректной
count: .quad .-message

Здесь в строке message 19 символов, то есть 19 байт. Для загрузки переменной в регистр с помощью инструкции ldr требуется выравнивание по 4 байтам. Соответственно строка message фактически будет занимать 20 байт. А переменная count будет хранить число 20. Хотя строка корректно будет выведена на консоль, но само значение count будет некорректным, что может негативно сказаться на каких-то других вычислениях.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850