can't read bottom of stack (esp), causes 'ret' to fail

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
aggieben
Posts: 4
Joined: Wed Jul 13, 2005 11:00 pm

can't read bottom of stack (esp), causes 'ret' to fail

Post by aggieben »

I have a function, called "flush_gdt" (ok, actually memmgr::_flush_gdt()) that loads gdtr, ds, es, and ss, does a far jump to load cs (to a label on the next line), and then returns. the ret instruction causes a triple-fault (because I don't have an IDT working yet), and it's because when the 'leave' instruction pops into %ebp, %ebp ends up being 0. What's weird is that gdb things that the value in the location contained in %esp is not 0.

Here's my code:

Code: Select all

void
memmgr::_flush_gdt(gdtdesc& gdtr)
{
  int *p=reinterpret_cast<int*>(&gdtr);
  asm("lgdt %0" : : "m"(*p));

  asm("movw %0, %%ds\n\t"
      "movw %1, %%es\n\t"
      "movw %2, %%ss"
      : /* no output */
      : "r"(ds), "r"(es), "r"(ss)
      );
  asm("ljmp $0x8, $1f\n"
      "1:\n"
      : : "r"(cs)
      );
}
I tried inserting a couple of lines like this:

Code: Select all

movl (%esp), %edi
movl %edi, %ebp
but %edi gets a zero also. doing a

Code: Select all

(gdb) x /1wx
on the value in %esp shows it not to be 0.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Could you please post a dissassembly of the resulting function?

Code: Select all

objdump -d kernel.bin
and just grab where the function is.

I'm a little confused by the syntax here:

Code: Select all

asm("ljmp $0x8, $1f\n" 
      "1:\n" 
      : : "r"(cs) 
      );
That seems to be to be jumping to the CS 0x8, and location 0x1f (note the $ prefix = immediate). I may be wrong (I use intel syntax normally) - but post the dissassembly anyway so we can see what the compiler is doing :)
aggieben
Posts: 4
Joined: Wed Jul 13, 2005 11:00 pm

Post by aggieben »

JamesM wrote:Could you please post a dissassembly of the resulting function?

Code: Select all

objdump -d kernel.bin
and just grab where the function is.
This is better:

Code: Select all

objdump -S kernel.elf | c++filt

memmgr::_flush_gdt(gdtdesc& gdtr)
  10124a:       55                      push   %ebp
  10124b:       89 e5                   mov    %esp,%ebp
  10124d:       83 ec 10                sub    $0x10,%esp
{
  int *p=reinterpret_cast<int*>(&gdtr);
  101250:       8b 45 0c                mov    0xc(%ebp),%eax
  101253:       89 45 fc                mov    %eax,-0x4(%ebp)
  asm("lgdt %0" : : "m"(*p));
  101256:       8b 45 fc                mov    -0x4(%ebp),%eax
  101259:       0f 01 10                lgdtl  (%eax)
  asm("movw %0, %%ds\n\t"
      "movw %1, %%es\n\t"
      "movw %2, %%ss"
      : /* no output */
      : "r"(ds), "r"(es), "r"(ss)
      );
  10125c:       0f b7 0d 36 00 20 00    movzwl 0x200036,%ecx
  101263:       0f b7 15 38 00 20 00    movzwl 0x200038,%edx
  10126a:       0f b7 05 34 00 20 00    movzwl 0x200034,%eax
  101271:       8e d9                   mov    %ecx,%ds
  101273:       8e c2                   mov    %edx,%es
  101275:       8e d0                   mov    %eax,%ss
  asm("ljmp $0x8, $1f\n"
      "1:\n"
      : : "r"(cs)
      );
  101277:       0f b7 05 32 00 20 00    movzwl 0x200032,%eax
  10127e:       ea 85 12 10 00 08 00    ljmp   $0x8,$0x101285

  // test doing leave stuff manually to see what happens to ebp
  asm("movl %ebp, %esp\n\t"
      "movl (%esp), %edi\n\t"
      "mov %edi, %ebp");
  101285:       89 ec                   mov    %ebp,%esp
  101287:       8b 3c 24                mov    (%esp),%edi
  10128a:       89 fd                   mov    %edi,%ebp
}
  10128c:       c9                      leave
  10128d:       c3                      ret
[/quote]
I'm a little confused by the syntax here:

Code: Select all

asm("ljmp $0x8, $1f\n" 
      "1:\n" 
      : : "r"(cs) 
      );
That seems to be to be jumping to the CS 0x8, and location 0x1f (note the $ prefix = immediate). I may be wrong (I use intel syntax normally) - but post the dissassembly anyway so we can see what the compiler is doing :)
Yes, that's for loading CS - but that's not the problem (that I know of, anyway). I'm beginning to think it's actually that there is a 0 in my stack, but gdb is misleading me. (FWIW, bochs also thinks there's not a zero in (%esp)). Could there be something wrong with my stack segment? Here it is:

Code: Select all

struct __attribute__ ((packed)) segdesc
{
  unsigned int limit0:16;
  unsigned int base0:16;
  unsigned int base1:8;
  unsigned int type:4;
  unsigned int sflag:1;
  unsigned int priv:2;
  unsigned int pflag:1;
  unsigned int limit1:4;
  unsigned int avail:1;
  unsigned int lflag:1;
  unsigned int dub:1;
  unsigned int gran:1;
  unsigned int base2:8;
};

  segdesc gdt[] __attribute__ ((aligned(8))) =
    {
      // null descriptor
/* 0x0 */      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      // code descriptor for kernel [0x0 - 0x200000] (really only 1MiB
      // for xok, but no reason to segment off the first 1MiB)
/* 0x08 */     {0x0200, 0x0000, 0x00, 0xa, 1, 0, 1, 0, 0, 0, 1, 1, 0},
      // stack descriptor (512KB, growing down)
/* 0x10 */     {0x0080, 0x0000, 0x28, 0x6, 1, 0, 1, 0, 0, 0, 1, 1, 0},
      // heap descriptor
/* 0x18 */     {0x0400, 0x0000, 0x28, 0x2, 1, 0, 1, 0, 0, 0, 1, 1, 0},
      // extra data descriptor for < 0x100000
/* 0x20 */     {0x0100, 0x0000, 0x00, 0x2, 1, 0, 1, 0, 0, 0, 1, 1, 0}
    };
  uint16_t gdt_limit = 0x27; // 8n - 1

  uint16_t cs = 0x8;  // 0..001000
  uint16_t ss = 0x10; // 0..010000
  uint16_t ds = 0x18; // 0..011000
  uint16_t es = 0x20; // 0..100000
Post Reply