Autor Thema: Probleme mit aarch64  (Gelesen 17850 mal)

RW2003

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« am: 11. July 2021, 12:17 »
Hallo allerseits.
Ich wollte mich mal etwas an arm versuchen und habe mich an das ARM-OS-Dev Tutorial aus dem Wiki gehalten.
Naja, mit ein paar Änderungen, da ich nciht für 32-bit sondern für armv8 bzw. aarch64 entwickeln möchte.

Nun ja, der kernel wird kompiliert und von qemu gestartet. Ausgaben auf den Bildschirm bzw. seriellen Schnittstelle funktionieren halbwegs.
Es scheint Probleme mit den Variablen bzw. Speicherzugriffen zu geben...

Einzelne Zeichen auszugeben funktioniert.
Wenn ich das aber in einer Schleife machen möchte, dann tut er sich mit der Zählervariablen schwer...
Im ersten Durchlauf funktioniert es. Dann wird die Schleife aber nciht mehr fortgeführt, weil s == 0 sei...
irgendwie verkackt er hier den Zugriff über die Variable i.
Wenn man die Zeichen per Zahl, also s[1], s[2], etc. ausgibt dann bekommt man auch das Zeichen und kein '\0'.
Gleiches gilt auch, wenn man den Wert in der Schleife auf einen konkreten Wert setzt: z.b. i = 4;.
Das Inkrementieren von i hingegen scheint einfach nciht zu funktionieren. Danach bekommt er aus s == '\0' raus und beendet die Schleife.

Vllt. könnt ihr mir helfen und mir meine Fehler aufzeigen; und mir sagen ob ich die Adresse für den Stack richtig zugewiesen habe, oder dort vllt. das Problem liegt...


Da man hier leider keine zip archive hochladen kann, gibt es hier meine Dateien als Text.

Start.S
/* Init ist eine Funktion aus init.c */
.extern init
.org 0x0

/* Hier befinden sich die Einsprungspunkte für Exceptions, der erste wird beim
   Reset aufgerufen. Da die Einsprungspunkte nur vier Byte voneinander entfernt
   sind, bleibt jeweils nur Platz für einen Opcode – und damit springen wir
   woanders hin. */
b       _start
/* Hier folgen jetzt die Einsprungspunkte für Undefined Instruction, SWI,
   Prefetch Abort, Data Abort, eine bisher reservierte Exception, IRQ und FIQ.
   Da all diese Exceptions noch nicht behandelt werden können, legen wir dort
   einfach Endlosschleifen hin. */
b       end
b       end
b       end
b       end
b       end
b       end
b       end

.global start
_start:
/* Stack initialisieren  */
mov x0, #kernel_stack
mov     sp, x0


sub  sp, sp, #8
mov  x0, #17
str  x0, [sp]

/* C-Code aufrufen */
bl      init

/* Falls wir jemals aus init zurueckkommen sollten, gehen wir in eine
   Endlosschleife */
b       end

end:
ldr x0, =0x84000008
hvc #0


/* Enthält die Adresse des Kernelstacks */
kernel_stack_addr:
.8byte kernel_stack

/* 8 kB Stack für den Kernel. Das Label steht hinter dem freien Speicher,
   weil der Stack nach unten wächst */
.section .bss
.space 8192
kernel_stack:


init.c
// Data register of first UART (PL011)
volatile unsigned int * const UART0_DR = (unsigned int *) 0x09000000;


// Stop guest so Qemu terminates
void system_off() {
__asm__("ldr x0, =0x84000008;hvc #0;");
}


void print(const char *s) {
    short int i = 0;
    while(s[i] != '\0' || i <5){
        //*UART0_DR = 48 + i;

        *UART0_DR = s[i];

        *UART0_DR = 48 + i;
        *UART0_DR = 48 + i;
       
        *UART0_DR = s[i];
        *UART0_DR = s[i + 1];

        i = i + 1;

        *UART0_DR = 48 + i;
        *UART0_DR = '\n';
    }
}

void init(int value) {
   const char *hw = "Hello World!\n";
   char text[25];

   *UART0_DR = (48 + value);
   *UART0_DR = '\n';
   *UART0_DR = '\n';

   print(hw);
   //print(text);
   print("U");

   system_off();
}


makefile
SRCS = $(shell find -name '*.[cS]')
OBJS = $(addsuffix .o,$(basename $(SRCS)))

CC = aarch64-linux-gnu-gcc
LD = aarch64-linux-gnu-ld

ASFLAGS = -march=armv8-a
CFLAGS = -march=armv8-a -O3 -ffreestanding -nostartfiles -nostdinc -nodefaultlibs -Wall -Wextra -fno-stack-protector
LDFLAGS = -e 0x00000000 -T link.ld

kernel: $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^

%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $^

%.o: %.S
$(CC) $(ASFLAGS) -c -o $@ $^

clean:
rm $(OBJS)

.PHONY: clean


link.ld
/* Wir nehmen einen ARMv8-Prozessor an */
OUTPUT_ARCH("aarch64")
/* Diese Datei muss im Speicher nach 0x00000000 geladen werden */
STARTUP(start.o)

/*
 * Hier wird festgelegt, in welcher Reihenfolge welche Sektionen in die Binary
 * geschrieben werden sollen
 */
SECTIONS
{
    /* Die Standardsektionen einfach hintereinander weg ab 0x00000000 einbinden. */
    .text 0x00000000 : AT(0x00000000) {
        *(.text)
    }
    .data ALIGN(4096) : {
        *(.data)
    }
    .rodata ALIGN(4096) : {
        *(.rodata)
    }
    .bss ALIGN(4096) : {
        *(.bss)
    }
}


Aso.. wenn ich hier schon dabei bin zu posten..
vllt. kann mir jemand das .global in Assembler erklären.
Ich wollte die system_off(); funktion in C benutzen und hatte sie davor in der Assemblerdatei wie folgt stehen:
.global _system_off
_system_off:
ldr x0, =0x84000008
hvc #0
In C hab ich das als extern void _system_off(); deklariert.
Damit konnte der Linker aber nichts anfangen, und warf mir doppelte Definierung vor.
Deshalb hab ich den Code dann in C kopiert...
Wie hätte ich das definieren müssen?


Ich hoffe ihr könnt mir dabei helfen  :-)


Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 12. July 2021, 01:45 »
Vorweg: Mit arm64 kenne ich mich nicht besonders aus.

Versuche mal, statt "short int" einen ganz normalen int oder long zu benutzen (also 32 oder 64 Bit). Vielleicht ist irgendwas mit Adressierung oder Alignment komisch. Für mehr müsstest du einen Debugger auspacken, du kannst qemu an einen gdb flanschen und einfach mal single-steppen.

Das ".global" im Assemblerquelltext macht ein Symbol für andere Dateien sichtbar. Zum Beispiel hast du in start.S das Symbol "start" global verfügbar gemacht (".global _start"). Wenn sich der Linker über mehrfache Definition beschwert, dann ist aus irgendeinem Grund das Symbol mehrfach vorhanden. Ohne das ".global" sind die Symbole auch mehrfach vorhanden, aber die stören sich nicht, weil sie lokal sind.

Dein Makefile greift sich sämtliche C- und S-Dateien. Das habe ich früher auch mal so gemacht, aber das ist kein guter Stil. Wenn du eine C-Datei mit "system_off" rumliegen hast - muss ja nichtmal deine Wunschdatei sein, sondern z.B. eine Kopie - dann erzeugt ein ".global _system_off" im Assembler einen Konflikt. Teste das mal aus; was du beschreibst, sollte funktionieren.

RW2003

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 12. July 2021, 14:46 »
danke für die Antwort.
Mein Makefile hab ich geändert  :-D
anstatt short int etwas anderes zu versuchen, hab ich schon versucht.
Tatsächlich macht es jetzt aber einen Unterschied.

Bei short oder unsigned short und unsigned int bekomme ich folgende Ausgabe:
A

H00  1
 11  2
 22  3
 33  4
 44  5
U00  1
 11  2
 22  3
 33  4
 44  5

Mit int, long, long long oder unsigned long und unsigned long long folgendes:
A

H00He1
e11el2
l22ll3
l33lo4
o44o 5
 55 W6
W66  7
U00U 1
 11  2
 22  3
 33  4
 44  5


Wenn man das i < 5 aus der While-Schleife entfernt gibt es in allen Fällen nur den ersten Buchstaben aus, und verlässt dann die Schleife...

ich werde es mal mit dem Debugger versuchen ._.

Das mit dem .global versteh ich; was ich nciht verstehe, ist die Verwendung davon bzw. wieso dies nciht so funktiniert wie ich das erwarten würde...
wenn ich in start.S folgendes schreibe:
.global _test
_test:
...
bekomme ich beim Linken folgenden Fehler:
aarch64-linux-gnu-ld: start.o: in function `_test':
(.text+0x3c): multiple definition of `_test'; start.o:(.text+0x3c): first defined here
demnach hab ich es ohne _ versucht.. sowohl:
.global test
_test:
als auch
.global _test
test:

scheint der linker auch erstmal so hinzunehmen.
Möchte ich nun in C darauf zugreifen:
extern void _test();
...
_test();

bekomme ich nur:
aarch64-linux-gnu-ld: init.o: in function `init':
init.c:(.text+0x194): undefined reference to `_test'
das gleiche auch wenn ich es als test(); ohne _ deklariere.

kannst du mir erklären wie ich das .global konkret einsetzen kann?
Bei meinem vorherigen Projekt ging es mit:
.global _name
_name:
...

Was ist denn die korrekte Syntax für .global?


RW2003

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 12. July 2021, 15:30 »
da ich nun gdb an der Reihe ist, hab ich gcc mit dem Argument -O0 versucht.
Dabei komm ich in eine Endlosschleife mit der Ausgabe von ganz vielen Zeilen von 0 0  .
mit -O1 udn -O2 bekomm ich lediglich ein A ausgegeben.

Ich finde es sehr seltsam dass es nur mit -O3 läuft, und selbst damit hat er ja offenbar Probleme ._.

RW2003

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 13. July 2021, 17:12 »
ich hab nun etwa mit gdb rum probiert... und, es verwirrt mich noch mehr..
ich hab immerhin geschafft dass die Schleife nun funktioniert (auch ohne i < 5).
Hello World wird also ganz ausgegeben. yeay.

Wenn man sich die Zeile i = i + 1; mit gdb anguckt bzw einen Breakpoint setzt, bekomm ich folgendes:

Breakpoint 4, print (s=0x2008 "Hello World\n") at init.c:16
16
(gdb) x/gx i
0x3 <KERNEL_START+3>:    0x00000d1400000e14
(gdb) c
Continuing.

Breakpoint 4, print (s=0x2008 "Hello World\n") at init.c:16
16
(gdb) x/gx i
0x4 <KERNEL_START+4>:    0x1400000d1400000e
(gdb) c
Continuing.

Breakpoint 4, print (s=0x2008 "Hello World\n") at init.c:16
16
(gdb) x/gx i
0x5 <KERNEL_START+5>:    0x0c1400000d140000
(gdb) c
Continuing.

Breakpoint 4, print (s=0x2008 "Hello World\n") at init.c:16
16
(gdb) x/gx i
0x6 <KERNEL_START+6>:    0x000c1400000d1400
(gdb) c
Continuing.


Das Format sollte doch Speicheradresse: Wert sein, oder nciht?
Wieso verändert sich denn beides beim inkrementieren?


Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 08. August 2021, 23:27 »
Zitat
Das mit dem .global versteh ich; was ich nciht verstehe, ist die Verwendung davon bzw. wieso dies nciht so funktiniert wie ich das erwarten würde...
Okay, dann gehe ich da mal durch...

Zitat
.global _test
_test:
...
aarch64-linux-gnu-ld: start.o: in function `_test':
(.text+0x3c): multiple definition of `_test'; start.o:(.text+0x3c): first defined here

In deiner start.o gibt es mindestens zwei Definitionen von "_test". Oder du gibst dem Linker die start.o mindestens zweimal, das hat den gleichen Effekt. Du hast in deinem System nicht zufällig eine "start.c" und eine "start.s", oder?

Zitat
.global test
_test:
als auch.global _test
test:
scheint der linker auch erstmal so hinzunehmen.
Das sind unterschiedliche Symbole. Im ersten Fall meldest du die Existenz von "test", aber es existiert nicht. Im zweiten Fall meldest du die Existenz von "_test", aber es existiert nicht. Die jeweils anderen Symbole sind rein lokal und daher von außen nicht sichtbar.

Zitat
Was ist denn die korrekte Syntax für .global?
Du machst das schon richtig, aber ich vermute, dass du deine Datei zweimal dem Linker übergibst. Dann hast du natürlich jedes Symbol doppelt...

Mit dem gdb-Output kann ich nichts anfangen, mit dem habe ich nicht viel Erfahrung.

Wenn dein Problem noch aktuell ist, geh ich mal in den Code rein.

RW2003

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 16. August 2021, 15:50 »
is ne Weile her, aber danke für die Antwort  :-D
immerhin einer der seinen Senf dazugeben will  :-D

Ich habe nur eine Datei: start.S, welche dann start.o erzeugt; also keine weitere start.c oder so.
Die Objektdateien werden mittels:
SRCS = start.S init.c
OBJS = $(addsuffix .o,$(basename $(SRCS)))

gesammelt.

Das ganze "Projekt" hatte ich erstmal auf Eis gelegt, da ich eifnach nciht weiter kam....
Also wenn du denn möchtest, kannst du den Code gerne durchgehen :-D
Ansonsten bleibt das Projekt erstmal auf Eis.


 

Einloggen