简体   繁体   中英

Cannot understand enabling GDT segmentation, namely updating CS register

Im currently following a guide to enabling GDT segmentation. I am using GNU Assembler, with Bochs for the emulation.

I understand that I need to load the GDT register with the GDT descriptor. This I have done, and the following step is to now load all the segment registers with offsets, relative to the GDT, to the individual location of the code/data segment descriptors. The code to do so is stated as the following:

reloadSegments:
   ; Reload CS register containing code selector:
   JMP   0x08:reload_CS ; 0x08 points at the new code selector
.reload_CS:
   ; Reload data segment registers:
   MOV   AX, 0x10 ; 0x10 points at the new data selector
   MOV   DS, AX
   MOV   ES, AX
   MOV   FS, AX
   MOV   GS, AX
   MOV   SS, AX
   RET

I cannot, however, understand how it is possible to implicitly load the CS register with the offset without the apparently inevitable outcome of jumping to whatever memory location is pointed by the CS:IP pair - ie, if the code segment descriptor is located at GDT_start+0x10, and I attempt to load 0x10 to the CS register, the virtual machine jumps to 0x10:IP, and I never enter the .reload_CS label.

My version of the routine (at&t syntax):

_start:
    // Disable interrupts
    cli
    // Load GDT register with location of GDT
    lgdt    0x3c

    // Load location of Code segment descriptor into cs
    ljmp    $0x2c, $reload_cs

reload_cs:
    mov $0x34, %ax
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs
    mov %ax, %ss

loop:
    jmp loop

Ps: I am not sure why ljmp $0x2c, reload_cs does not work - prefixing reload_cs with $ compiles but labels generally didn't need this syntax in my experience...

You seem to have a number of misconceptions about how the GDT works.

  • The LGDT instruction doesn't load the GDTR with a selector. It's operand is a location in memory that holds a structure containing a 16-bit limit and a 32-bit linear base address. It's normally executed in real mode before entering protected mode.

  • The GDT only works in protected mode. To use it, you must switch from real mode to protected mode by setting the PE bit in CR0.

  • The GDT is a table in memory containing a number of 8 byte long segment descriptors. The location and limit of the GDT in memory is determined by the base and limit loaded into the GDTR by the LGDT instruction as described above. Each descriptor contains various type and permission bits and for the basic descriptor types also contains the linear address of the base of the segment along with the limit of the segment.

  • Protected mode addressing works by taking the selector value contained in the relevant segment register and using that as in index into the GDT or LDT. The indexed segment descriptor provides the base address of segment being addressed. That base is added to the relevant offset to determine the linear address being referenced. Your far jump instruction ( ljmp $0x2c, $reload_cs ) loads the values 0x2c and reload_cs into CS and EIP respectively. The next instruction to be executed is determined taking the base from the segment descriptor referred to by 0x2c and adding the value of reload_cs to it.

  • The segment selector 0x2c isn't an index into the GDT, it's an index into the LDT. The least significant three bits of a selector are special. Bits 0 and 1 are the requested privilege level and should be 0 here. Bit 2 is the table indicator, if its 0 then the selector references the GDT, if it's 1 then it uses the LDT. The rest of the bits, 3-15, provide the index into the GDT/LDT.

  • The value of the symbol reload_cs is determined by the assembler and/or linker. You need to ensure that its value is correct. As you're using it as an offset into a protected mode code segment, that means that offset into that segment must be where the instruction following reload_cs: is actually located in memory. The assembler and linker have no idea where your loading your code into memory and they don't know how you've set up your code segment.

Since you're using the GNU assembler and presumably GNU linker the easiest way to ensure reload_cs has the correct value is to use a protected mode code segment with a base of 0, tell the assembler to put everything into the .text section and then tell the linker to locate the .text section at the actual linear address where you're loading it into memory. That way 0 + reload_cs will equal actual linear address in memory of the instruction following the reload_cs label.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM