OSDev.org
https://forum.osdev.org/

Global Descriptor Table location
https://forum.osdev.org/viewtopic.php?f=1&t=30005
Page 1 of 1

Author:  yushill [ Thu Jan 21, 2016 9:05 am ]
Post subject:  Global Descriptor Table location

I'm confused with the location of the Global Descriptor Table (GDT). According to Intel Manuals from i386 to earlier ones, the GDTR register contains a base address of the GDT table which is pretended to be a linear address. Following Intel conventions, linear addresses are subject to paging.

Nevertheless, I'm wondering which address space is considered. Ring 3 (user-land) programs are perfectly allowed to modify some segment selectors (ES for example). This modification should trigger the processor to load segment descriptor from corresponding entry in the GDT which base address is computed using the linear address given by the GDTR register.

Because linear address are subject to paging, I understand from Intel manuals, that segment descriptor loads go through the memory paging of current process. Because Linux certainly doesn't want to expose the GDT structure to user-land programs, I thought that it somehow managed to introduce a hole in the address space of user-land processes; preventing these processes to read the GDT, while allowing the processor to read it for segment reloads.

I checked by using the following code which showed I'm completely wrong about the GDTR's base linear address.

Code:
int
main()
{
  struct
  {
    uint16_t  pad;
    uint16_t  size;
    uintptr_t base;
  } gdt_info;

  __asm__ volatile ("sgdt %0" : "=m" (gdt_info.size) );

  void* try_mmgdt = (void*)( gdt_info.base & ~0xfff );
  void* chk_mmgdt = mmap(try_mmgdt, 0x4000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

  std::cout << "gdt size: \t" << std::dec << gdt_info.size << std::endl;
  std::cout << "gdt base: \t" << std::hex << gdt_info.base << std::endl;
  std::cout << "mmgdt try:\t" << std::hex << uintptr_t(try_mmgdt) << std::endl;
  std::cout << "mmgdt chk:\t" << std::hex << uintptr_t(chk_mmgdt) << std::endl;

  return 0;
}


The program output (i386-compiled) on my machine is:

Code:
gdt size:       127
gdt base:       1dd89000
mmgdt try:      1dd89000
mmgdt chk:      1dd89000


The linear addresses of GDT entries and linear addresses of the mmap chunk perfectly overlap. Nevertheless the mmap chunk has obviously no relation with the GDT.

So my question finally is: which Intel/linux mechanism makes the linear address of the GDTR and the linear address of the current process point to different memory region ?

Author:  Brendan [ Thu Jan 21, 2016 3:56 pm ]
Post subject:  Re: Global Descriptor Table location

Hi,

yushill wrote:
Because linear address are subject to paging, I understand from Intel manuals, that segment descriptor loads go through the memory paging of current process. Because Linux certainly doesn't want to expose the GDT structure to user-land programs, I thought that it somehow managed to introduce a hole in the address space of user-land processes; preventing these processes to read the GDT, while allowing the processor to read it for segment reloads.


When software accesses something the CPU asks itself "is this software allowed to access it?" (which is effected by CPL, etc). When the CPU accesses something itself (e.g. GDT table lookups, IDT table lookups, setting the accessed and dirty flags in page table entries, etc) the CPU asks itself "am I allowed to access it" instead (which has nothing to do with CPL).

When the CPU is accessing the GDT (for whatever reason), the CPU only checks GDT limit - an OS doesn't need to ensure the GDT can be accessed at CPL=3.


Cheers,

Brendan

Author:  gerryg400 [ Thu Jan 21, 2016 5:14 pm ]
Post subject:  Re: Global Descriptor Table location

Firstly you really should use __attribute__ ((packed)) on the struct especially in 64 bit land. I would not trust the result until you do.

I would expect to see something like the following on a 64 bit kernel

Code:
gdt size:    127
gdt base:    ffff88021fd09000
mmgdt try:   0xffff88021fd09000
mmgdt chk:   0x7fe7dcead000
I would expect you to see that the gdt base is actually in the kernel memory area near the top of memory and then I would expect mmap to refuse to map that area and place your request somewhere in user space. That's what happens on my OS ;)

Author:  yushill [ Fri Jan 22, 2016 7:04 am ]
Post subject:  Re: Global Descriptor Table location

Quote:
Firstly you really should use __attribute__ ((packed)) on the struct especially in 64 bit land. I would not trust the result until you do.


Your perfectly right about the packed attribute, I definitly should have used it. Nevertheless I compiled all this stuff with proper -m32 flags and that led to the correct offset (byte 0-1 for the limit and 2-5 for the base, I've checked the asm).

*But*, and this is where your answer enlightened me :D. Though my code is running in 32 bits it's not using legacy 32 bit mode but compat mode.
Thus, current process' linear addresses are 32 bits but GDTR's linear address is 64bits (even in compat mode).
Using LGDT in compat mode only gives you the lower 32bits of the true GDTR linear address.

Edit: @Brendan: regarding the "Protection" part, you're right, during its lookup the processor doesn't check access rights as it would do for software, but my confusion was rather on the "Translation" part. By the way, its one of these few differences between "legacy 32-bits protected mode" and "32-bits compat-mode" that I'm still discovering...

Thanks for helping me understand that.

Y.

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/