Как данные распределяются на языке ассемблера с помощью набора инструкций?

Рассмотрим следующий фрагмент ассемблерного кода, написанного для встроенной архитектуры процессора Nios II :

    .section .data
    .align 2
va:   .long 0x0
vb:   .long 0x11223344
vc:   .long 0x55667788

Необходимо сделать следующее:

  • Выделите достаточно памяти для всех трех переменных
  • Убедитесь, что выделенная память непрерывна
  • Преобразование инструкций, использующих имя переменной, в инструкции, использующие адрес переменной.

Я не понимаю, как это можно сделать с помощью набора инструкций, и надеялся получить некоторое представление о том, как происходит перевод приведенного выше кода в набор инструкций.

Компоновщик не выполняет первые два пункта.
@ IgnacioVazquez-Abrams Это работа сборщика?
Нет. Это ничья работа. Директивы ассемблера не выделяют память, и в любом случае память не должна быть непрерывной.
@ IgnacioVazquez-Abrams Что вы подразумеваете под «директивами ассемблера не выделяют память»? Я думал, что весь смысл приведенного выше кода заключался в выделении памяти. Во-вторых, меня учили, что память выделяется непрерывно.
Это не совсем точно. В этих директивах указано, что произвольные байты должны быть размещены в текущей и последующей ячейках памяти; «распределение» происходит потому, что к сборке добавлено больше байтов, а не потому, что эти директивы используются.
Что касается темы «непрерывный», то, хотя каждая директива определяет непрерывный блок памяти, нет необходимости иметь непрерывную память по всем директивам.

Ответы (2)

Это выделение памяти, и это не имеет ничего общего с набором инструкций. Ассемблеры невероятно просты — строки в ассемблерном файле почти точно соответствуют выходному бинарному файлу, делается очень мало перестановок. Строка «.long 0x0» просто означает «дайте мне четыре байта прямо здесь и загрузите их со значением 0x0». Данные «распределяются» так же, как и инструкции. Строка «MOV R12, R11» просто означает «дайте мне четыре байта прямо здесь и загрузите их двоичной кодировкой для MOV R12, R11». Данные являются смежными по той же причине, что и ваши инструкции, — вы ввели их на смежных строках в файле сборки. «va:» просто означает «назовите расположение этих четырех байтов «va»». Это используется только внутри ассемблера. Везде, где вы ссылаетесь на «ва» в коде ассемблер вставит адрес четырех байтов данных, на которые ссылается «va», что является просто числом. Обратите внимание, что это может сделать не ассемблер, а компоновщик. Однако строка 'va' никогда не появится в выходном двоичном файле.

По сути, ассемблер — это почти прямое представление содержимого памяти. Последовательные линии сборки заканчиваются последовательными адресами. Единственным исключением является изменение разделов (.section). Любые данные, явно расположенные в коде, будут непрерывными, если они непрерывны в файле сборки. Теперь секции немного возятся с этим. Возможно, что все .data попадает в одну часть памяти, а все .text — в другую. Однако блоки не будут разбиты — если они являются смежными в файле сборки, они будут смежными и в выходных данных.

Подобные данные не «распределяются» совершенно одинаково.

Что происходит, так это то, что когда вы запускаете программу, эта программа загружается в память. Если эта программа где-то содержит 0x000000001122334455667788, то эти ячейки памяти (всего 12, если я не ошибся) будут содержать эти значения. Когда вы компилируете свой код, все ИМЕНА переводятся в адреса памяти. С этого момента для этого фактически не выполняются никакие инструкции — память есть, она «выделена» (выделено достаточно памяти для хранения всей программы, статических переменных, таких как эта и все такое), и поскольку вся программа размещается в этой памяти инициализируются переменные.

Затем, когда вы говорите «загрузить переменную va в регистр 'X'», скомпилированный код (который говорит «загрузить адрес 0x84573412 в регистр 'X'») получит правильный бит данных.

Имена переменных приведены только для вашего удобства, иначе вам пришлось бы самостоятельно выделять конкретные адреса.