Записать байт по адресу в сегменте.data в сборке RISC-V
Я пишу программу сборки RISC-V, в которой нужно сохранить слово (сохраненное в регистре) в сегменте.data:
.section .rodata
msg:
.string "Hello World\n"
.section .data
num:
.word 97
.section .text
.global _start
_start:
li a1, 100
sw a1, num
loop:
j loop
Но когда программа достигает sw a1, num
Я получаю ошибку "недопустимые операнды`sw a1,num'". Как я могу хранить данные в памяти внутри сегмента.data? не могли бы вы дать мне несколько советов?
1 ответ
Как общее практическое правило, сборка, которую вы пишете на ассемблере, является собственным языком программирования, который, похоже, очень похож на то, что написано в руководстве по RISC-V ISA. RISC-V - довольно простой ISA, поэтому для большинства инструкций фактически нет разницы между синтаксисом руководства ISA и синтаксисом, принятым ассемблером. Место, где это начинает разрушаться, - это ссылка на символы, потому что, хотя вы можете заполнять непосредственное непосредственно в коде сборки, вы, вероятно, захотите сделать так, чтобы компоновщик делал это, потому что вы не будете знать фактический адрес символа до времени ссылки (и это может измениться, как вы измените свою программу).
Чтобы позволить компоновщику заполнять адреса символов в вашем коде, вам нужно передать перемещения из ассемблера, чтобы компоновщик мог позже заполнить их. У меня есть целое сообщение в блоге о том, как это работает в блоге SiFive, но мы просто обновили сайт, и я не могу понять, как его найти:).
В этом случае вы, по сути, пытаетесь написать сборку, которая реализует следующий код C
int num = 97;
void func(int val) { num = val; }
В вашем исходном ответе все данные правильные, поэтому единственное, о чем нужно беспокоиться, это как правильно составлять инструкции. У вас есть несколько вариантов того, как их испускать. Одним из вариантов является явное написание каждой инструкции и перемещения в ваш исходный код, который будет выглядеть следующим образом
func:
lui t0, %hi(num)
sw a0, %lo(num)(a0)
ret
Вы можете создать эту сборку из моего кода на C выше, скомпилировав -mcmodel=medlow -mexplicit-relocs -O3
, Руководство GCC определяет другие специфические опции RISC-V, которые управляют генерацией кода.
Если вас интересует более подробная информация, у нас есть руководство по сборке, доступное на GitHub: https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md. Это далеко не завершено, но мы хотели бы получить помощь, чтобы указать на проблемы или предоставить больше контента.
Синтаксис запасного слова (sw
) инструкция
sw rs2, offset(rs1)
где смещение - это 12-битный непосредственный операнд.
Когда ты пишешь sw a1, num
вы получаете синтаксическую ошибку, и ассемблер не работает:
foo.s: : Assembler messages:
foo.s::13: Error: illegal operands `sw a1,num'
Возможно, самый простой способ решить эту проблему - использовать адрес загрузки (la
) псевдо-инструкция:
li a1, 100
la t0, num
sw a1, 0(t0)
Поскольку la
инструкция полностью загружает адрес в регистр, который мы должны использовать 0
как смещение.
В la
псевдо-инструкция расширяется до относительной адресации программного счетчика (ПК), т.е. objdump
:
00000000000100b0 <_start>:
100b0: 06400593 addi a1,zero,100
100b4: 00001297 auipc t0,0x1
100b8: 01028293 addi t0,t0,16 # 110c4 <__DATA_BEGIN__>
100bc: 00b2a023 sw a1,0(t0)
В качестве альтернативы вы можете использовать абсолютную адресацию:
li a1, 100
lui t0, %hi(num)
sw a1, %lo(num)(t0)
Обратите внимание, что %hi()
а также %lo()
Макросы ассемблера разделяют 32-битный адрес на его старшие 20-битные и младшие 12-битные части (т.е. %hi(num) + sign_ext(%lo(num)) = num
).