Moin,
ich nehm das mal auseinander, ohne es zu testen:
Die Fehlermeldung wird davon verursacht, dass du nicht lgdt (%eax) schreibst. Der Befehl lgdt erwartet nämlich eine Speicherreferenz. Zum Beispiel wäre einfach lgdt loader_addr unter den meisten Umständen auch möglich gewesen.
Anschließend sollte dir dein Assembler eine Fehlermeldung geben, dass die $-Zeichen in den .quad-Anweisungen nicht funktionieren. Diese sind ebenso wie die bei .word $0x107 und .int $gdt fehlerhaft. Die $'s müssen in diesen Befehlen weg.
Wie bist du auf die Deskriptoren in der GDT gekommen? Für Kernel und User Space 4 GB Flat Memory musst du folgendes nehmen:
.quad 0x00000000
.quad 0x00cf9a000000ffff
.quad 0x00cf92000000ffff
.quad 0x00cffa000000ffff
.quad 0x00cff2000000ffff
Das hat dann diese Einteilung, die deiner GDT am nächsten kommt:
0x08 - Kernel Code
0x10 - Kernel Data
0x18 - User Code
0x20 - User Data
Wenn du die User Deskriptoren verwendest, musst du auf beide 3 addieren (weil User Mode = Ring 3), dann musst du CS und die anderen Segmentregister mit 0x1b bzw. 0x23 laden.
Dann ist noch das Limit der GDT zu groß. Der Wert sollte 5*8-1=39 (bzw. 0x27) sein.
Das Laden von $0x0b in die Datensegmente ist nicht korrekt. Vermutlich willst du erstmal eine Weile im Kernel Mode bleiben. Dann ist der korrekte Wert $0x10. (Wenn du irgendwann in den User Mode wechseln willst, ist 0x23 richtig.)
Was du mit dem folgenden Abschnitt bezweckst, ist mir nicht klar. mov $loader, %al
shl $2, %al
mov %al, loader_addr
Du dividierst da das untere Byte der Adresse der GDT durch 4 und schreibst es wieder irgendwo hin. Der Linker wird das vermutlich nicht akzeptieren.