delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/2001/11/25/08:11:01

Message-Id: <5.0.2.1.2.20011125104031.009d1910@pop.gmx.net>
X-Sender: martinSteuer AT gmx DOT de@pop.gmx.net
X-Mailer: QUALCOMM Windows Eudora Version 5.0.2
Date: Sun, 25 Nov 2001 11:34:00 +0100
To: djgpp AT delorie DOT com
From: Martin Steuer <martinSteuer AT gmx DOT de>
Subject: Re: Problems with gdb???
In-Reply-To: <1858-Sat24Nov2001212931+0200-eliz@is.elta.co.il>
References: <5 DOT 0 DOT 2 DOT 1 DOT 2 DOT 20011124192647 DOT 009e02e0 AT pop DOT gmx DOT net>
<5 DOT 0 DOT 2 DOT 1 DOT 2 DOT 20011124192647 DOT 009e02e0 AT pop DOT gmx DOT net>
Mime-Version: 1.0
Reply-To: djgpp AT delorie DOT com
Errors-To: nobody AT delorie DOT com
X-Mailing-List: djgpp AT delorie DOT com
X-Unsubscribes-To: listserv AT delorie DOT com

Pleasy reply also directly to me, thanks

At 21:29 24.11.2001 +0200, you wrote:
 >If the crashes are accompanied by the normal DJGPP crash message with
 >registers and EIP traceback, please post here one of these messages
 >in its entirety.

While composing this message i found a problem within the wrapper's code 
and fixed this but now the situation gets even more worse: Under bare DOS 
the program crashes without being debugged (SIGSEGV) in a call to getch() 
at getch()+153 (this is after the installation of the IRQ Handler) which i 
use to loop a sound until one presses a key.
DS to GS are all zero and the stack selector is invalid.
At least i'm now sure that the problem lies in my code...

 >Please tell more details; it's hard to answer that question because
 >it's too general.  Debugging programs that hook hardware interrupts
 >is a bit tricky, but should in general work.

 >To give a more specific answer, we need to know what hardware
 >interrupts does your program hook, and how (in protected mode, real
 >mode, or both).

Oh i suspected that there would be no easy way ;-)
In my test program i'm hooking the SoundBlaster interrupt, in my case IRQ5
(INT 0D). I'm handling the interrupt completly without chaining to the 
previous handler in pmode. All memory accessed by the handler is locked.

 >A source of your wrapper function might be useful.

Ok, if you have time here is it:

wrapper.S:
------
#include "irq.h"
.text

.global _irqwrapper, __immediate_addr, __old_handler_off, __old_handler_sel
.global __irqwrapper_end

/* Will call the previously installed IRQ Handler if the C-Routine returns */
/* nonzero, else the old routine wont be called */

_irqwrapper:
    pushal
    pushw %ds
    pushw %es
    pushw %fs
    pushw %gs
    cs:
    movw (___djgpp_ds_alias),%ax /* set up ds correctly */
    movw %ax,%ds
    movw %ax,%es
    movw %ax,%fs
    movw %ax,%gs

    movw %ss,%ax              /* save old stack in ax, ebx */
    movl %esp,%ebx

    movl $_st,%edx
    movl $-2,%ecx
    xor %esi,%esi

    _stack_search:            /* find a free stack */
       addl $2,%ecx
       cmpl $MAXSTACKS * 2,%ecx
       je _outofstacks

       cmpl 12(%edx,%ecx,8),%esi
    jnz _stack_search

    incl 12(%edx,%ecx,8)      /* indicate stack is in use */

    movw 4(%edx,%ecx,8),%ss   /* set up locked stack */

    movl 8(%edx,%ecx,8),%esp  /* load size of stack */
    decl %esp
    andl $0xfffffffc,%esp     /* be sure its dword aligned */


    pushl %ecx                /* save index */
    pushl %eax                /* old ss */
    pushl %ebx                /* old esp */

    .byte 0xb8                /* movw __immediate_addr,%eax */

    __immediate_addr: .long 0x0

    call *%eax                /* call the irq handler */
    popl %ebx                 /* old esp now in ebx */
    popl %ecx                 /* old ss */
    popl %esi                 /* get the old index into _st */
    cli

    movl %ebx,%esp
    movw %cx,%ss              /* DPMI IRQ Stack restored */

    movl $_st,%edx
    decl 12(%edx,%esi,8)      /* stack is free for use again */

    orl %eax,%eax             /* shell we call the previous handler? */

    jz _outofstacks           /* no */

    popw %gs
    popw %fs
    popw %es
    popw %ds
    popal

    .byte 0xea                /* far jump to previous handler */
    __old_handler_off:  .long 0x0
    __old_handler_sel:  .word 0x0

    _outofstacks:
    popw %gs
    popw %fs
    popw %es
    popw %ds
    popal
    sti
    iret
__irqwrapper_end:

------
Here comes the struct i'm using for handling the stacks:

------
struct stackInfo
{
  void* addr;     // holds address relative to ds (normal C Pointer)
  int sel;        // holds the selector used to access the stack
  int size;       // size of stack
  int free;       // 0 indicates stack is free
};

typedef struct stackInfo stacks[MAXSTACKS];

------
_st is of type stacks.

Now allocate_locked_stack() which is called MAXSTACKS-times:

------

static int firstcall_to_allocate_locked_stack = 1;

// Returns selector for this stack or otherwise -1
int allocate_locked_stack(unsigned size)
{
  int i,sel;
  long addr;
  void* h;

  if (firstcall_to_allocate_locked_stack)
  {
   firstcall_to_allocate_locked_stack = 0;
   LOCK_VARIABLE(st);
   for (i=0;i<MAXSTACKS;i++)
   {
    st[i].addr = 0;
    st[i].sel  =-1;
    st[i].size = 0;
    st[i].free = 0;
   }
  }
  if (!(h = (void*)malloc(size))) return -1;

  if ((sel = __dpmi_allocate_ldt_descriptors(1)) == -1)
  {
   free(h);
   return -1;
  }

  __dpmi_get_segment_base_address(_my_ds(),&addr);
  if (__dpmi_set_segment_base_address((unsigned short)sel,(unsigned)h + addr)\
     == -1)
  {
   __dpmi_free_ldt_descriptor(sel);
   free(h);
   return -1;
  }

  if (__dpmi_set_segment_limit(sel,size-1) == -1)
  {
   __dpmi_free_ldt_descriptor(sel);
   free(h);
   return -1;
  }

  if (__dpmi_set_descriptor_access_rights(sel,DATASEG |\
     ((_my_cs() & 3) << 5)) == -1)
  {
   __dpmi_free_ldt_descriptor(sel);
   free(h);
   return -1;
  }

  // ok, sel is now ready to be used find free slot in table st
  i = 0;
  while ((i<MAXSTACKS) && (st[i].sel != -1)) i++;
  if (i == MAXSTACKS)
  {
   __dpmi_free_ldt_descriptor(sel);
   free(h);
   return -1;
  }

  LOCK_DATA(h,size);

  st[i].addr = h;
  st[i].size = size;
  return st[i].sel = sel;
}

The install_irqHandler() Routine copies the wrappers code to a newly 
malloc'ed area, locks it and fills the immediate operands into the code, 
this could be a critical part too:

I'm using the following defines to access the immediate operands from C:

------
extern unsigned irqwrapper;
extern unsigned _irqwrapper_end;
extern unsigned _immediate_addr;
extern unsigned _old_handler_sel;
extern unsigned _old_handler_off;

#define WRAPPERSIZE     (unsigned) &_irqwrapper_end - (unsigned)
&irqwrapper
#define MY_IRQHANDLER   (unsigned) &_immediate_addr - (unsigned) &irqwrapper
#define OLDHANDLER_CS   (unsigned) &_old_handler_sel - (unsigned) &irqwrapper
#define OLDHANDLER_OFF  (unsigned) &_old_handler_off - (unsigned) &irqwrapper

------


static int firstcall_to_install_irqHandler    = 1;
static void* hand[MAXHANDLERS];

// Returns 0 if handler has been installed, else nonzero
int install_irqHandler(int vec, void* handler)
{
  int i;
  char* wrapperMem;
  _go32_dpmi_seginfo inf;

  if (firstcall_to_install_irqHandler)
  {
   firstcall_to_install_irqHandler = 0;

   // prepare_irq_stacks() simply calls allocate_locked_stack() MAXSTACKS-
   // times
   if (prepare_irq_stacks() == -1) return 1;
   for (i = 0; i < MAXHANDLERS; i++) hand[i] = (void*) 0;
  }

  i = 0;
  while ((hand[i] != (void*) 0) && (i < MAXHANDLERS)) i++;
  if (i == MAXHANDLERS) return 1;

  if ((wrapperMem = (char*) malloc(WRAPPERSIZE)) == 0) return 1; // no mem 
for wrapper

  _go32_dpmi_get_protected_mode_interrupt_vector(vec,&inf);

  memcpy(wrapperMem,(void*) &irqwrapper,WRAPPERSIZE); // copy the wrapper

  // ohh well, casting is nice ;-)
  *((unsigned*)((unsigned)(unsigned)wrapperMem + MY_IRQHANDLER)) = 
(unsigned)handler;  // this copies the addr. of the C-Routine into the wrapper

// The next two line copy the selector and the offset of the previous 
handler into
// the wrapper

  *((unsigned short*)((unsigned)(unsigned)wrapperMem + OLDHANDLER_CS)) = 
inf.pm_selector;

  *((unsigned*)((unsigned)(unsigned)wrapperMem + OLDHANDLER_OFF)) = 
(unsigned)inf.pm_offset;

  /* the code of the wrapper is in DS, in case of DJGPP this doesnt matter
     'cause DS's and CS's BASE are equal */
  LOCK_DATA(wrapperMem,WRAPPERSIZE);

  // install the wrapper for handling the interrupt
  inf.pm_selector = _my_cs();
  inf.pm_offset = (unsigned long) wrapperMem;
  _go32_dpmi_set_protected_mode_interrupt_vector(vec,&inf);

  hand[i] = (void*)wrapperMem;
  return 0;
}

I know the casting looks ugly but i didn't get it done another way and the 
generated asm-source does the right thing, but maybe someone of you may 
give me help how to do it better.

I hope this is not too confusing/ too much and you get an overview and can 
help me.
It would be even good if you could just verify that the wrapper is correct now.

Thank you!
Martin 

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019