This is my C/C++ kernel.
[tt]
unsigned char *textaddress = (char *)0xB8000;
void main();
void main() {
*textaddress++ = '!';
*textaddress++ = 7;
}
[/tt]
Reverse Engineering the kernel showed:
[tt]
startup (assembler):
00: fa cli
01: e8 c7 00 00 00 call 0xcd
06: fa cli
07: f4 hlt
kernel::main()
cd: 53 push %ebx
ce: 51 push %ecx
cf: 52 push %edx
d0: 56 push %esi
d1: 57 push %edi
d2: f4 hlt
d3: e8 eb ff fe ff call 0xffff00c3
d8: e8 ea 00 ff ff call 0xffff01c7
dd: e8 26 ff fe ff call 0xffff0008
e2: e8 70 ff fe ff call 0xffff0057
e7: e8 89 ff fe ff call 0xffff0075
ec: e8 85 ff fe ff call 0xffff0076
f1: 5f pop %edi
f2: 5e pop %esi
f3: 5a pop %edx
f4: 59 pop %ecx
f5: 5b pop %ebx
f6: c3 ret
[/tt]
I'll let GAF explain:
I always thought it was Watcom but it appears its JLOC but Im not an expert when it comes to JLOC.Let's have a look at the call at address 0xd3 in the hex dump (e8 eb ff fe ff). The first byte is the opcode of the relative call, while the other 4 bytes define the value to be added to EIP (0xfffeff). As it's an ordinary signed 32bit number, we can get the absolute value by doing a two's complement:
0xfffeffeb -invert-> 0x10014 -add-> 0x10013
The resulting value (-0x10013) gets now added to the address following the call instruction (0x100d8), so that we eventually end up with 0xc5 and not 0x100C5 as intended.
In my opinion this behaviour is really weird: The linker now seems to have understood that it's currently at a location greater than 0x10000 - otherwise the calls wouldn't be broken. On the other hand it however doesn't seem to know that all the other procedures, aswell as the data-section, are also located at 0x10000+ - thus global variables still don't get accessed correctly.
This is my linker script.
[tt]
startup: 0 10000
,,,text,*
code: 0 startup.after startup.i_after
,,,_TEXT,*
const: 0 code.after code.i_after
,,,_CONST,*
data: 0 const.after const.i_after
,,,*,*
[/tt]
Can anyone help me with this?