Page 1 of 1
question about sgdt
Posted: Mon Mar 24, 2008 1:49 pm
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.
Posted: Mon Mar 24, 2008 2:34 pm
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?
Posted: Tue Mar 25, 2008 2:26 am
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.
Posted: Tue Mar 25, 2008 5:17 am
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?
Posted: Tue Mar 25, 2008 6:41 am
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.
Posted: Tue Mar 25, 2008 9:06 am
by os64dev
your declaration of gdt_ptr is fault use the one specified by 01000010
Posted: Tue Mar 25, 2008 9:47 am
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.
Posted: Tue Mar 25, 2008 2:33 pm
by 01000101
get my ****ing name right!
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.
Posted: Tue Mar 25, 2008 4:59 pm
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");
}
Posted: Tue Mar 25, 2008 5:50 pm
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.
Posted: Wed Mar 26, 2008 12:06 am
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);
}
Posted: Wed Mar 26, 2008 6:11 am
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.
Posted: Wed Mar 26, 2008 9:27 am
by devel
@Korona:
thnx for checking this.