Работа с данными и памятью

Типы данных

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

Перед загрузкой данных в/из памяти сначала надо определить память, с которой будет идти работа. Ассемблер GNU предоставляет ряд директив для выделения памяти для использования в программе.

  • .ascii: строка в двойных кавычках

  • .asciz: строка ascii, которая заканчивается 0-вым байтом

  • .byte: целое число размером в 1 байт

  • .short: целое число размером в 2 байта (так называемое полуслово - hlf-word)

  • .word: целое число размером в 4 байта (слово)

  • .quad: целое число размером в 8 байт (двойное слово)

  • .octa: целое число размером в 16 байт (четверное слово)

  • .float: число с плавающей точкой одинарной точности

  • .double: число с плавающей точкой двойной точности

Мы можем определить числа в одном из следующих форматов:

  • Число в десятичной системе, которое может содержать цифры от 0 до 9 и должно начинаться с любой цифры кроме 0

  • Число в восьмеричной системе, которое может содержать цифры от 0 до 7 и должно начинаться с нуля

  • Число в двоичной системе, которое может содержать цифры 0 и 1 и должно начинаться с символов 0b или 0B

  • Число в шестнадцатеричной системе, которое может содержать шестнадцатеричные цифры от 0 до F и должно начинаться с символов 0x или 0X

  • Число с плавающей точкой, которое начинается с символов 0f или 0e, за которым идет число с плавающей точкой

Для определения памяти в программе предназначена секция .data. Например:

.global _start 
_start:
// выход из программы
    MOV X0, 0         // код возврата - 0
    MOV X8, #93       // устанавливаем функцию Linux для выхода из программы
    SVC 0             // Вызываем функцию Linux

.data
    mybyte: .byte 1            // определяем один байт, который равен 1
    myword: .word 18            // определяем одно слово (4 байта), которое равно 18
    myshort: .short 3           // определяем двухбайтное число, которое равно 3
    myquad: .quad 1248          // определяем двойное слово (8 байт), которое равно 1248
    myocta: .octa 12            // определяем четверное слово (16 байт), которое равно 12
    hello: .ascii "Hello\n"     // определяем строку 
    message: .asciz "Hi Wordl"   // определяем строку, которая заканчивается нулевым байтом

В данном случае для каждого отдельного кусочка данных определена метка. Например, на метку mybyte проецируется байт, который имеет значение 15. Определенные в секции .data еще можно назвать глобальными переменными, поскольку они доступны глобально - в любом месте прогаммы, а инструкции могут динамически изменять их значения.

Перед целыми числами можно использовать два знака:

  • - (отрицательное значение) определяет дополнение числа до двух

  • ~ определяет дополнение числа до единицы

Например

byte1: .byte -0x45
byte1: .byte  -33, 
byte1: .byte ~0b00111001

Расположение данных

Стоит учитывать, что данные, определенные в секции .data, в памяти располагаются сплошняком. Например, возьмем следующее определение данных:

.data
    n1: .word 12    // 4 байта
    n2: .short 13    // 2 байта
    n3: .byte 14    // 1 байт
    n4: .byte 15    // 1 байт

В памяти для секции .data выделяется некоторая область, где вначале располагается число n1, которое занимает 4 байта. Сразу ним располагается число n2, которое занимает 2 байта, и далее идут числа n3 и n4, которые занимают по 1 байту. Допустим, секция .data располагается в памяти по адресу 0xff00, тогда расположение данных упрощенно могло выглядеть следующим образом:

Расположение данных в секции .data в ассембдеое в ARM64

Это важное обстоятельство, поскольку позволяет нам, используя адрес одной переменной в памяти, обращаться к другим данным, которые располагаются до или после переменной.

Массив данных

Можно определять сразу набор или массив значений через запятую:

.data
    bytes: .byte 74, 0112, 0b00101010, 0x4A, 0X4a

Здесь определяется 5 байт, каждый из которых имеет одно и то же значение. Мы можем определить значения в десятичной, восьмеричной, двоичной, шестнадцатеричной системах. При этом значения могут представлять результат выражений, которые вычисляет ассемблер, когда компилирует программу.

Определение наборов данных

Для упрошения определения наборов большего размера можно использовать инструкцию .fill, которая имеет следующую форму:

.fill repeat, size, value

Эта инструкция повторяет значение (value) определенного размера (size) определенное количество раз (repeat):

.data
    zeros: .fill 10, 4, 0

Эта инструкция создает блок памяти из 10-ти 4-байтовых чисел (слов - тип .word), каждое из которых равно нулю.

Еще одна конструкция - .rept:

.rept count
...
.endr

Повторяет выражения между .rept и .endr столько раз, сколько указано в параметре count. Например:

rpn: .rept 3
    .byte 0, 1, 2
    .endr

Здесь создается 3 раза по 3 байта - 0, 1, 2. То есть этот код будет эквивалентен следующему:

.byte 0, 1, 2
.byte 0, 1, 2
.byte 0, 1, 2
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850