UNKNOWN EIP issue

Programming, for all ages and all languages.
Post Reply
User avatar
BASICFreak
Member
Member
Posts: 284
Joined: Fri Jan 16, 2009 8:34 pm
Location: Louisiana, USA

UNKNOWN EIP issue

Post by BASICFreak »

Ok, so I have been writing an ELF loader, when I realized my DEBUG_print(f) somehow changes EIP to 0x80010.

So, I removed that code and did this in my main function:

Code: Select all

for(uint32_t xx = 0; xx < 0xFFFF0 ; xx++)
		outb(DEBUG_PORT, (uint8_t) 'a');
DisAsm-INTEL (with extra BS):

Code: Select all

1000ac:	bb f0 ff 0f 00       	mov    ebx,0xffff0
  1000b1:	c7 04 24 d2 53 10 00 	mov    DWORD PTR [esp],0x1053d2
  1000b8:	e8 d3 04 00 00       	call   100590 <DEBUG_printf>
  1000bd:	c7 44 24 04 00 90 10 	mov    DWORD PTR [esp+0x4],0x109000
  1000c4:	00 
  1000c5:	c7 04 24 28 50 10 00 	mov    DWORD PTR [esp],0x105028
  1000cc:	e8 bf 04 00 00       	call   100590 <DEBUG_printf>
  1000d1:	8d b4 26 00 00 00 00 	lea    esi,[esi+eiz*1+0x0]
  1000d8:	c7 44 24 04 61 00 00 	mov    DWORD PTR [esp+0x4],0x61
  1000df:	00 
  1000e0:	c7 04 24 e9 00 00 00 	mov    DWORD PTR [esp],0xe9
  1000e7:	e8 14 00 00 00       	call   100100 <outb>
  1000ec:	83 eb 01             	sub    ebx,0x1
  1000ef:	75 e7                	jne    1000d8 <kmain+0x98>
Just as a way to test, before it is through the loop (ebx says we made it to 0xDBAC8) INT 32 (0x20) is fired.
No this is not the PIT as it is masked in the PIC and if I remove the loop we end up with no issue.

outb():

Code: Select all

void outb(uint16_t port, uint8_t data)
{
	__asm__ __volatile__ ("outb %1, %0" : : "dN" (port), "a" (data));
}
DisAsm-INTEL:

Code: Select all

00100100 <outb>:
  100100:	8b 44 24 08          	mov    eax,DWORD PTR [esp+0x8]
  100104:	8b 54 24 04          	mov    edx,DWORD PTR [esp+0x4]
  100108:	ee                   	out    dx,al
  100109:	c3                   	ret    
  10010a:	8d b6 00 00 00 00    	lea    esi,[esi+0x0]
I see no reference to 0x80010 in the binary

My INT Handler reports:

Code: Select all

				UNHANDLED INTERRUPT ENCOUNTERED!
INTERRUPT # 32



FATAL ERROR: 
EAX = 0x61	EBX = 0xDBAC8	ECX = 0x1F	EDX = 0xE9
EDI = 0x26455	ESI = 0x2640D	EBP = 0x67EDC	ESP = 0x108FC6
GS = 0x10	FS = 0x62600010	ES = 0x62600010	DS = 0x47E80010
EIP = 0x80010	CS = 0x2020000	EFLAGS = 0xE90000	SS = 0x0
USER-ESP = 0x610000	ERROR-CODE = 0xEC0000

EIP RAM DUMP (EIP - 10) - (EIP + 10):
0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	[0x0]	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
ESP RAM DUMP (ESP - 10) - (ESP + 10):
0x0	0x0	0x1F	0x0	0x0	0x0	0x61	0x0	0x0	0x0	[0x20]	0x0	0x0	0x0	0x0	0x0	0xEC	0x0	0x10	0x0
This has stumped me for days now, anyone have an idea where to look or if I'm just blind and missed something obvious?

It is not Stack overflow as the stack is from 0x109000 - 0x107000.
The kernel ELF is loaded at 0x100000 - 0x10A8A0.
BOS Source Thanks to GitHub
BOS Expanded Commentary
Both under active development!
Sortie wrote:
  • Don't play the role of an operating systems developer, be one.
  • Be truly afraid of undefined [behavior].
  • Your operating system should be itself, not fight what it is.
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:

Re: UNKNOWN EIP issue

Post by Combuster »

Your regdump is broken. DS, ES and FS are rendered incorrectly, CS has the same problem and additionally is likely a bogus value, EFLAGS with this value is certainly impossible, and the same goes for SS unless it's still zero from real mode.

And in this situation, I wouldn't trust the other values either, particularly not EIP.
"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
BASICFreak
Member
Member
Posts: 284
Joined: Fri Jan 16, 2009 8:34 pm
Location: Louisiana, USA

Re: UNKNOWN EIP issue

Post by BASICFreak »

Combuster wrote:Your regdump is broken. DS, ES and FS are rendered incorrectly, CS has the same problem and additionally is likely a bogus value, EFLAGS with this value is certainly impossible, and the same goes for SS unless it's still zero from real mode.

And in this situation, I wouldn't trust the other values either, particularly not EIP.
I realize some are bogus ESP, EAX, EBX, EDX, and GS is all I trust in that output.

If I divide by 0 we get a much more trusted output:

Code: Select all

				UNHANDLED INTERRUPT ENCOUNTERED!
INTERRUPT # 0



FATAL ERROR: Division By Zero
EAX = 0x1	EBX = 0xFFFF0	ECX = 0x0	EDX = 0x0
EDI = 0x26455	ESI = 0x2640D	EBP = 0x67EDC	ESP = 0x108FC4
GS = 0x10	FS = 0x10	ES = 0x20010	DS = 0x20010
EIP = 0x1000D9	CS = 0x8	EFLAGS = 0x10246	SS = 0x109000
USER-ESP = 0x105028	ERROR-CODE = 0x0

EIP RAM DUMP (EIP - 10) - (EIP + 10):
0x0	0x0	0xB8	0x1	0x0	0x0	0x0	0x31	0xC9	0x99	[0xF7]	0xF9	0xC7	0x4	0x24	0xED	0x53	0x10	0x0	0x89	
ESP RAM DUMP (ESP - 10) - (ESP + 10):
0x0	0x0	0x0	0x0	0x0	0x0	0x1	0x0	0x0	0x0	[0x0]	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0xD9	0x0
Although I'm unsure where the extra 0x20000 come from in ES and DS.

Also with optimizer level 0 I make it almost all the way through the loop till EBX = 0x26260

I'm taking a break from this... Just wanted to show that those are the values the system thinks are there.
BOS Source Thanks to GitHub
BOS Expanded Commentary
Both under active development!
Sortie wrote:
  • Don't play the role of an operating systems developer, be one.
  • Be truly afraid of undefined [behavior].
  • Your operating system should be itself, not fight what it is.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: UNKNOWN EIP issue

Post by Brendan »

Hi,
BASICFreak wrote:I realize some are bogus ESP, EAX, EBX, EDX, and GS is all I trust in that output.
EAX, ECX and EDX all look correct to me. DS, ES, FS and GS also look correct, except that they're 16-bit registers and you're displaying them as 32-bit values which causes the highest 16-bits (which don't exist) to look like random trash.

From there, you can reconstruct the stack contents like this:
  • 0x00EC0000 = reported error code
    0x00080010 = reported EIP
    0x02020000 = reported CS
    0x00E90000 = reported eflags
    0x00108FC6 = reported ESP
    0x00000000 = reported SS
The 0x0202 part is the only thing that looks like it might be EFLAGS, so using that as an "anchor point" and adjusting the stack contents around it you'd get this:
  • 0x0000???? = potential error code
    0x001000EC = potential EIP
    0x00000008 = potential CS
    0x00000202 = potential eflags
    0x8FC600E9 = potential ESP
    0x00000010 = potential SS
That looks quite believable to me; suggesting that your code to display registers has an "off by 2" bug.

For the original "INT 32":
  • you disabled interrupts with CLI
  • IRQ0 occurred and the IRQ wasn't masked, so the PIC chip tried to deliver it but couldn't because interrupts where disabled, so the PIC chip set a bit in its "interrupt received register" instead
  • You reprogrammed the PIC chip (I assume) and masked all the IRQs; but this does not clear the PIC chip's "interrupt received register"
  • You enabled interrupts with STI, the PIC chip noticed and checked its "interrupt received register", saw it has something it can deliver now, and sent it to you as "interrupt 32".
The correct way to mask all IRQs is:
  • Leave interrupts enabled in the CPU (STI)
  • Mask all IRQs in the PIC chips
  • Do a few NOP instructions or something (to give the hardware a chance to deliver any pending IRQs)
  • Only after all pending IRQs have been handled (by BIOS), disable interrupts with CLI if you could be bothered (there's actually no reason to use CLI at all).
  • Reprogram the PIC chip

Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
BASICFreak
Member
Member
Posts: 284
Joined: Fri Jan 16, 2009 8:34 pm
Location: Louisiana, USA

Re: UNKNOWN EIP issue

Post by BASICFreak »

Ok, let me attempt to go in order of the code

First Grub is loading my Kernel (so I won't modify boot sector or stage two)

On load the kernel sets esp to the bottom of the stack, pushes eax and ebx, then calls kmain

kmain checks multiboot flags to make sure it has the information needed.
installs GDT
installs IDT
initializes 8259:

Code: Select all

	// Send Initialize Command
	_8259_Command_Out(0, INIT);
	_8259_Command_Out(1, INIT);
	// Map IRQs (0-15) to 0x20-0x2F
	_8259_Map(0, 0x20);
	_8259_Map(1, 0x28);
	// Tell PICs how to talk
	_8259_ICW3(0);
	_8259_ICW3(1);
	// Tell PICs to use 80x86 mode
	_8259_ICW4(0);
	_8259_ICW4(1);
	// Disable All IRQs
	_8259_Disable_ALL_IRQ();
	_8259_Enable_IRQ(2);
Which breaks down into (if I followed the code correctly):

Code: Select all

outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 2);
outb(0xA1, 4);
outb(0x21, 1);
outb(0xA1, 1);
outb(0x21, 0);
outb(0xA1, 0);
uint8_t temp = inb(0x21);
temp &= ~(1 << 2);
outb(0x21, temp);
then kmain starts the ints (sti)

from this point everything will work perfectly fine no misfired INTs unless I call outb many-many times.

For testing only at this point is the loop with outb(0xE9, 'a');
NEVER Makes it this far (while loop is in place):
cli or while(true); either way same results.
hlt

I have also tried setting up the PIT before starting interrupts, but if I send ACK to the PIC after handling what it thinks to be the PIT I then run into a Double Fault, which is captured before it hits a Triple Fault. While the actual PIT IRQ works fine, so it seems to me that the software must be triggering INT 0x20.

But again in the disassembled Binary, I don't see a reference to INT 0x20 or address 0x80010.

Also I think I have fixed the register dump code here is the new output (still BS on this INT):

Code: Select all

				UNHANDLED INTERRUPT ENCOUNTERED!
INTERRUPT # 32



FATAL ERROR: 
EAX = 0x61	EBX = 0x26260	ECX = 0x1F	EDX = 0xE9
EDI = 0x26455	ESI = 0x2640D	EBP = 0x108FF0	ESP = 0x108FB6
GS = 0x10	FS = 0x10	ES = 0x10	DS = 0x10
EIP = 0x80010	CS = 0x0	EFLAGS = 0x0	SS = 0x0
USER-ESP = 0x610000	ERROR-CODE = 0x0

EIP RAM DUMP (EIP - 10) - (EIP + 10):
0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	[0x0]	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
ESP RAM DUMP (ESP - 10) - (ESP + 10):
0x0	0x0	0x1F	0x0	0x0	0x0	0x61	0x0	0x0	0x0	[0x20]	0x0	0x0	0x0	0x0	0x0	0xC6	0x0	0x10	0x0
BOS Source Thanks to GitHub
BOS Expanded Commentary
Both under active development!
Sortie wrote:
  • Don't play the role of an operating systems developer, be one.
  • Be truly afraid of undefined [behavior].
  • Your operating system should be itself, not fight what it is.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: UNKNOWN EIP issue

Post by kzinti »

int 0x20 is your PIT. You programmed the PIC to send IRQ0 (PIT) to 0x20. So I'm not sure your statement about the PIT IRQ working fine is true.
User avatar
BASICFreak
Member
Member
Posts: 284
Joined: Fri Jan 16, 2009 8:34 pm
Location: Louisiana, USA

Re: UNKNOWN EIP issue

Post by BASICFreak »

OK, so this is why I took a break for two days,

ISSUE is resolved.

changed my IDT_COMMON from:

Code: Select all

IDT_COMMON:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp
    push eax
    mov eax, IDT_HANDLER
    call eax
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
TO:

Code: Select all

IDT_COMMON:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp
    push eax
    mov eax, IDT_HANDLER
    call eax
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    pop word [tempdataw]
    add esp, 4
    iret

tempdataw dw 0
No issue at all now, my ELF phaser and the loop with outb both complete with no issue.

Now my question is how many bytes does a word actually take up on the stack?

Before IDT_COMMON is called I push byte (error code) then push word (IDT Number)

I use a word for IDT Number because anything over 0x7F in a byte causes errors due to signed nature of byte
BOS Source Thanks to GitHub
BOS Expanded Commentary
Both under active development!
Sortie wrote:
  • Don't play the role of an operating systems developer, be one.
  • Be truly afraid of undefined [behavior].
  • Your operating system should be itself, not fight what it is.
User avatar
iansjack
Member
Member
Posts: 4689
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: UNKNOWN EIP issue

Post by iansjack »

Now my question is how many bytes does a word actually take up on the stack?
Now my question is "Why are you asking here for the answer to something that is clearly described in the Programmer's Manual?".
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: UNKNOWN EIP issue

Post by Brendan »

Hi,
BASICFreak wrote:Now my question is how many bytes does a word actually take up on the stack?
It's a little bit convoluted, but depends on the operand size.

For example, "push dword 0x12345678" will consume 4 bytes of stack space, and "push word 0x1234" will consume 2 bytes of stack space (similar to "sub esp,2; mov word [esp],0x1234"). However, for an instruction with a 32-bit operand size and a 2-byte immediate operand, the immediate operand will be sign extended and that 2-byte immediate operand will consume 4 bytes of stack. The same happens with 1-byte immediate operands (which are sign extended to whatever the operand size is). Also, for segment registers if the operands size is 32-bit then the CPU will zero-extend (and not sign extend) the 16-bit value and store 4 bytes.

Finally, all of this also applies to 64-bit operand sizes (e.g. 1-byte, 2-byte and 4-byte immediate operands are extended to 8 bytes, segment registers are zero extended to 8 bytes; but something like "push word [foo]" still consumes 2 bytes of stack).

Basically, pushing a word on the stack consumes 2 bytes or 4 bytes or 8 bytes, depending on the specific instruction and operand size. :)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: UNKNOWN EIP issue

Post by Antti »

iansjack wrote:Why are you asking here for the answer to something that is clearly described in the Programmer's Manual?
Actually, this one is not very clear.
Brendan wrote:However, for an instruction with a 32-bit operand size and a 2-byte immediate operand, the immediate operand will be sign extended and that 2-byte immediate operand will consume 4 bytes of stack.
I would not want to complain... but if the operand size is 32-bit, a "push imm16" is not possible without an operand size override. It will consume 2 bytes of stack.
Brendan wrote:Finally, all of this also applies to 64-bit operand sizes (e.g. 1-byte, 2-byte and 4-byte immediate operands are extended to 8 bytes, segment registers are zero extended to 8 bytes; but something like "push word [foo]" still consumes 2 bytes of stack).
Same as above.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: UNKNOWN EIP issue

Post by Brendan »

Hi,
Antti wrote:
Brendan wrote:However, for an instruction with a 32-bit operand size and a 2-byte immediate operand, the immediate operand will be sign extended and that 2-byte immediate operand will consume 4 bytes of stack.
I would not want to complain... but if the operand size is 32-bit, a "push imm16" is not possible without an operand size override. It will consume 2 bytes of stack.
Brendan wrote:Finally, all of this also applies to 64-bit operand sizes (e.g. 1-byte, 2-byte and 4-byte immediate operands are extended to 8 bytes, segment registers are zero extended to 8 bytes; but something like "push word [foo]" still consumes 2 bytes of stack).
Same as above.
You're right- it's impossible to encode a 2-byte immediate in with 32-bit or 64-bit operand size. :oops:


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply