question about sgdt

Programming, for all ages and all languages.
Post Reply
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

question about sgdt

Post by devel »

Hi there,
I am using `sgdt' instruction in my user space application and can't really
understand the results. The point is when I run application more time `sgdt'
doesn't return the same result every time. I am talking about returned base
address of GDT which is not same every time I run the instruction. See my code
below.

So my questions are:
Is that ok (I guess not)?
Might it be that linux changes GDTR content somehow?
Is the GDTR content constant all the time the linux is running?

Regards,
devel.

Code: Select all

#include <stdio.h>

typedef struct {
    unsigned short limit;
    unsigned int base __attribute__((packed));
} gdt_t;

static inline gdt_t* inl_sgdt(gdt_t* m)
{
    __asm__ __volatile__("sgdt %0":"=m"(*m)::"memory");

    return m;
}

int main(void)
{
    gdt_t gdt;

    printf("--- Checking `inl_sgdt' ---\n");
    inl_sgdt((gdt_t*)&gdt);
    printf("base: 0x%X limit: 0x%X sizeof: %d\n", gdt.base, gdt.limit, sizeof(gdt));

    return 0;
}
Now if you run the code more times you should get different values for base address.
Last edited by devel on Tue Mar 25, 2008 2:40 am, edited 1 time in total.
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Post by 01000101 »

I got a slightly modified version to return a constant memory address of the sgdt'd gdt every time.

Code: Select all

struct gdt_ptr 
{ 
    unsigned short limit; 
    unsigned int base; 
} __attribute__((packed));  

unsigned long inl_gdt(unsigned long gdt_mem_base) 
{ 
    __asm__ __volatile__("sgdt %0"
                        :"=m"(gdt_mem_base)
                        :
                        :"memory"); 

    return gdt_mem_base; 
} 

    struct gdt_ptr pGDT0;
    struct gdt_ptr pGDT1;
    unsigned long new_gdt_base0;
    unsigned long new_gdt_base1;
    new_gdt_base0 = inl_gdt((unsigned long)&pGDT0);
    new_gdt_base1 = inl_gdt((unsigned long)&pGDT1);
    printf("base0: 0x%X base1: 0x%X Base2: 0x%X Base3: 0x%X \n", (unsigned long)&pGDT0,(unsigned long)&pGDT1, new_gdt_base0, new_gdt_base1); 
    
the printed bits are the memory addresses of (in order) the first gdt, second gdt, first sgdt'd loc, second sgdt'd loc.

im not exactly sure what you are asking though, but I think you just want to make sure that the sgdt instruction pushes the gdt to a fixed location every time?
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

01000101 wrote: im not exactly sure what you are asking though, but I think you just want to make sure that the sgdt instruction pushes the gdt to a fixed location every time?
I am asking you why is value of gdt.base not constant every time I run the application.
In my case I get following results:
base: 0xC1813000 limit: 0xFF sizeof: 6
...
base: 0xC1809000 limit: 0xFF sizeof: 6

If I understand well `sgdt' instruction should store 6 bytes content of GDTR at given
address (in my case &gdt). I don't understand why the result is not constant.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

I noticed one interesting difference between the two pieces of inline assembly (note the pointer dereference):
... :"=m"(*m)
... :"=m"(gdt_mem_base)

I suck at GCC inlines, but have you checked that the correct assembly is generated, instead of having it read some garbage off the stack?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

Well, regarding my code I think assembly seems to be correct:

Code: Select all

<inl_sgdt>:
push   %ebp
mov    %esp,%ebp
mov    0x8(%ebp),%eax
sgdtl  (%eax)  <--- store the 6bytes content at given address
mov    0x8(%ebp),%eax
pop    %ebp
ret
Note that regardless the code is inlined or not result varies between two values.
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

Post by os64dev »

your declaration of gdt_ptr is fault use the one specified by 01000010
Author of COBOS
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

Did you try 01000010's code? .
I don't think that 01000010's inline function is correct and using 01000010's declaration makes no different. See assembly below:

Code: Select all

<inl_gdt>:
push   %ebp
mov    %esp,%ebp
sgdtl  0x8(%ebp)     <---- this is faulty
mov    0x8(%ebp),%eax
pop    %ebp
ret
Btw I tried my implementation under qemu and it works fine I've problem only in linux
environment.
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Post by 01000101 »

get my ****ing name right! :twisted:

the code I supplied was tested on a real machine and I double checked the base and limits after using the sgdt and then lgdt instructions.

btw, The return value in the inline function (both mine and yours) is unnecessary and should just become a void function as the return value is the input value, therefor proving nothing.
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Post by Korona »

The correct GCC statement to store the gdt description struct is:

Code: Select all

void store_gdt_desc(struct gdt_ptr *location) {
__asm__ __volatile__("sgdt %0" : : "m"(location) : "memory");
}
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Post by 01000101 »

? that's what my function did.
it took the memory location of the gdt struct and was then used with the SGDT instruction.
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

@01000101:
Your code gives following output on my machine:
First run:
base0: 0xBFCAAB86 base1: 0xBFCAAB80 Base2: 0x300000FF Base3: 0x300000FF
pGDT0.base: 0xBFCAABA8 pGDT0.limit: 0x804
pGDT1.base: 0x9FF4B7FC pGDT1.limit: 0x3FF4

Second run:
base0: 0xBFA6A146 base1: 0xBFA6A140 Base2: 0x900000FF Base3: 0x900000FF
pGDT0.base: 0xBFA6A168 pGDT0.limit: 0x804
pGDT1.base: 0x9FF4B7F8 pGDT1.limit: 0x2FF4

Third run:
base0: 0xBFDB8C96 base1: 0xBFDB8C90 Base2: 0x300000FF Base3: 0x300000FF
pGDT0.base: 0xBFDB8CB8 pGDT0.limit: 0x804
pGDT1.base: 0x9FF4B7F2 pGDT1.limit: 0x3FF4

Is the output really correct?


your code:

Code: Select all

struct gdt_ptr
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed));

unsigned long inl_gdt(unsigned long gdt_mem_base)
{
    __asm__ __volatile__("sgdt %0"
                        :"=m"(gdt_mem_base)
                        :
                        :"memory");

    return gdt_mem_base;
}
int main(void)
{
    struct gdt_ptr pGDT0;
    struct gdt_ptr pGDT1;
    unsigned long new_gdt_base0;
    unsigned long new_gdt_base1;
    new_gdt_base0 = inl_gdt((unsigned long)&pGDT0);
    new_gdt_base1 = inl_gdt((unsigned long)&pGDT1);
    printf("base0: 0x%X base1: 0x%X Base2: 0x%X Base3: 0x%X \n", (unsigned long)&pGDT0,(unsigned long)&pGDT1, new_gdt_base0, new_gdt_base1);
    printf("pGDT0.base: 0x%X pGDT0.limit: 0x%X \n", pGDT0.base, pGDT0.limit);
    printf("pGDT1.base: 0x%X pGDT1.limit: 0x%X \n", pGDT1.base, pGDT1.limit);
}
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Post by Korona »

01000101 wrote:? that's what my function did.
it took the memory location of the gdt struct and was then used with the SGDT instruction.
No, your function did a

Code: Select all

asm volatile ("sgdt %0" : "=m" (location) : : "memory");
Your inline assembly statement specifies location as an output operand. The sgdt instruction takes location as an input operand.

EDIT: It seems my code is not correct either.
The following code gives the expected results on linux 2.6.22-14-386.

Code: Select all

#include "stdio.h"

struct gdt_desc
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed)); 

void store_gdt_desc(struct gdt_desc *location) {
	asm volatile ("sgdt %0" : : "m"(*location) : "memory");
}

int main(int argc, char** args) {
	struct gdt_desc desc;
	
	store_gdt_desc(&desc);
	printf("First run, gdt base: 0x%X, limit: 0x%X\n", desc.base, desc.limit);
	store_gdt_desc(&desc);
	printf("Second run, gdt base: 0x%X, limit: 0x%X\n", desc.base, desc.limit);
	store_gdt_desc(&desc);
	printf("Third run, gdt base: 0x%X, limit: 0x%X\n", desc.base, desc.limit);
}
The result on my machine is:

Code: Select all

First run, gdt base: 0xC039B000, limit: 0xFF
Second run, gdt base: 0xC039B000, limit: 0xFF
Third run, gdt base: 0xC039B000, limit: 0xFF
These values do not change if I run the program several times. It seems linux does not change the gdtr register.
User avatar
devel
Member
Member
Posts: 62
Joined: Wed Nov 28, 2007 4:15 am
Contact:

Post by devel »

@Korona:
thnx for checking this.
Post Reply