Лисья Нора

Оглавление


§ GCC

Стандартный шаблон для компилятора gcc:
RC=riscv64-unknown-elf
LB=-nostartfiles -nostdlib -nodefaultlibs
 
all: comp dump
comp:
$(RC)-gcc -march=rv32i_zicsr -mabi=ilp32 $(LB) -T linker.ld -Os crt.s main.c -o main.elf -lgcc
$(RC)-objcopy -O binary main.elf main.bin
dump:
$(RC)-objdump -S main.elf > dump.lst
Пояснения к компиляции:
Опционально:

§ crt.s

Код запуска, инициализации.
.section .text.init # Название данной секции
.global _start # Метка _start будет видна везде
.align 4 # Указать что выровнено по 4 байта
 
_start: la sp, _stack_top
call main
loop: j loop

§ main.c

Ниже представлен код, который выведет "Hello World" по адресу IBM VGA Textmode 0xB8000:
void main()
{
heaph(vm, 0xB8000);
char *str = "Hello World!";
while (*str) {
*vm = (*str | 0x1700);
str++;
vm++;
}
}
Могут потребоваться макросы:
// Объявление разных региона памяти
#define heap(A,B,C) volatile unsigned A* B = (unsigned A*) C
#define heapb(A,B) heap(char,A,B)
#define heaph(A,B) heap(short,A,B)
#define heapw(A,B) heap(int,A,B)
#define brk asm("ebreak")
 
// Работа с CSR, reg-номер регистра CSR, val-значение на запись
#define csr_read(reg) ({ unsigned int __v; asm volatile ("csrr %0, " #reg : "=r"(__v)); __v; })
#define csr_swap(reg,val) ({ unsigned int __v; asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__v) : "rK" (val)); __v; })
#define csr_write(reg, val) asm volatile ("csrw " #reg ", %0" : : "rK"(val))
#define csr_set(reg, mask) asm volatile ("csrs " #reg ", %0" : : "rK" (mask))
#define csr_clr(reg, mask) asm volatile ("csrrc " #reg ", %0" : : "rK" (mask))

§ Linker.ld

Общий шаблон линкера.
PHDRS
{
text_pt PT_LOAD FLAGS(5); /* 101 RX для кода */
data_pt PT_LOAD FLAGS(6); /* 110 RW для данных */
}
 
SECTIONS
{
. = 0x0; /* Стартовый адрес */
 
.text : { *(.text.init) *(.text) *(.text.*) } : text_pt
.rodata : { *(.rodata .rodata.*) } : text_pt
.data : { *(.data) } : data_pt
.bss : { *(.bss) } : data_pt
 
_stack_top = 0x10000;
 
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) }
}
Указываются важные точки:
Для каждой секции (.text, .rodata, ...) дополнительно указываются права доступа – данные или код.

§ Пример ECALL

В GCC необходимо правильно пользоваться системными вызовами, чтобы избежать падения системы при компиляции. Регистры должны быть правильно распределены и записаны. Ниже пример, как пользоваться вызовом ECALL. Всего можно задать до 7 аргументов и в регистре a7 будет номер системного вызова.
int syscall(int fd, const void *buf, int count)
{
register int r_a0 __asm__("a0") = fd; // Аргумент 1
register int r_a1 __asm__("a1") = (int) buf; // Аргумент 2
register int r_a2 __asm__("a2") = count; // Аргумент 3
register int r_a7 __asm__("a7") = 64; // Номер вызова
 
__asm__ volatile(
"ecall"
: "+r" (r_a0) // a0 используется и для ввода, и для вывода
: "r" (r_a1), "r" (r_a2), "r" (r_a7)
: "memory" // Указываем, что вызов может менять память
);
 
return r_a0;
}
Вот только для самого обработчика исключений придется делать ассемблерный код, который будет:
Скачать полный шаблон