Page 1 of 2

Executing flat binaries

Posted: Sun Oct 09, 2005 5:05 pm
by TheChuckster
I would like to add program execution to my OS.

I wrote a simple C program with this code:

Code: Select all

char *vidmem = (char*)0xb8000;

void main()
{
        int i=0;
        vidmem[i]='A';
        i++;
        vidmem[i]=0x07;
        for(;;);
}
and compiled with this link script:

Code: Select all

OUTPUT_FORMAT("binary")
phys = 0x00200000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}
to get a flat binary with code beginning at the 2 MB mark in RAM.

I used a program called raw2c that generates a string from the binary and gives me a pointer to it and the size of the string in order to include that binary in my kernel (no filesystems yet).

Getting it into RAM was just a matter of this code:

Code: Select all

unsigned char *destptr = (unsigned char*)0x200000;
memcpy(destptr, test, test_size);
Now, the killer begins when I try to execute this thing. One would think a simple jmp to the 2 MB mark would be enough.

Code: Select all

__asm__ ("jmp 200000");
Instead I get an "illegal opcode" exception. Yuck. I tried writing up a simple ASM program instead to ensure I was actually jumping to code at that address but no go. I think my inline assembly is wrong. I am not to keen on x86 assembly, let alone AT&T syntax. Google didn't give me much help either, and I spent the last hour or so looking.

Can any of you spot what is wrong? If not, are there any comprehensive documents on loading user programs within an OS? As far as I've seen, there aren't any, and this is tricky stuff.

Re:Executing flat binaries

Posted: Sun Oct 09, 2005 6:48 pm
by GLneo
mabey it has something to do with your ALIGN(4096);, try ALIGN(0); ???

Re:Executing flat binaries

Posted: Sun Oct 09, 2005 7:14 pm
by TheChuckster
Well I changed it around by removing the alignment and made my C code to branch out to "native" (as opposed to inline) ASM code in a .asm file.

Now I'm getting an out of bounds exception. Is that because of my GDT? I haven't set one up yet; I'm using GRUB's because I'm using paging memory instead of segmented, but a bad GDT could be preventing execution nonetheless, no? What is an out of bounds exception and how can I fix it?

EDIT: Okay. Something's definitely wrong with the way I'm jumping. Changed the jmp line to jmp 100000h -- the beginning of my kernel and added a 5 second delay before it using the timer. One would think that the kernel would keep restarting itself but instead it triple faulted.

Re:Executing flat binaries

Posted: Sun Oct 09, 2005 10:52 pm
by AR
That code is invalid GAS Syntax, without a dollar sign prefix it is taken as a memory access IIRC (ie. it reads 200000 then jumps to the address pointed to by it which is most likely be 0 in Bochs), not to mention the lack of a hexadecimal prefix (ie. "0x"), try:

Code: Select all

__asm__ volatile ("jmp $0x200000");

Re:Executing flat binaries

Posted: Mon Oct 10, 2005 4:48 am
by TheChuckster

Code: Select all

/tmp/ccwCikph.s:39: Error: Unrecognized token `$0x200000'
/tmp/ccwCikph.s:39: Error: Unrecognized token ''
Think I'm going to have to move the address into a register and then jump to the pointer register? If so, how do I do that in GSA syntax?

Re:Executing flat binaries

Posted: Mon Oct 10, 2005 6:11 am
by Pype.Clicker
i feel like a bit of FAQ is needed here ...

If "$<some_number>" should indeed be used for moves, etc. when a constant is used (comparatively, "<some_number>" is an address base for GAS), it appears by giving a look at some disassembled code that jumps do _not_ require immediates ... after all, jump targets are memory addresses, no ?

Very honnestly, i never managed to get how to write a proper jump with GAS, so what i usually do is to write down NASM code, assemble, disassemble with an AT&T-syntax tool and copy the result. That's silly but it works rather well.

Re:Executing flat binaries

Posted: Mon Oct 10, 2005 6:38 am
by Solar
Pype.Clicker wrote:
Very honnestly, i never managed to get how to write a proper jump with GAS, so what i usually do is to write down NASM code, assemble, disassemble with an AT&T-syntax tool and copy the result. That's silly but it works rather well.
It is also excellent debugging advice for those despairing on GAS syntax in general. If you would know how to write something in Intel lingo, write that, run NASM over it, and objdump it to get a good hint at the AT&T lingo.

Re:Executing flat binaries

Posted: Mon Oct 10, 2005 8:35 am
by Candy
Solar wrote:
Pype.Clicker wrote:
Very honnestly, i never managed to get how to write a proper jump with GAS, so what i usually do is to write down NASM code, assemble, disassemble with an AT&T-syntax tool and copy the result. That's silly but it works rather well.
It is also excellent debugging advice for those despairing on GAS syntax in general. If you would know how to write something in Intel lingo, write that, run NASM over it, and objdump it to get a good hint at the AT&T lingo.
Ever heard of intel2gas ?

Re:Executing flat binaries

Posted: Mon Oct 10, 2005 4:03 pm
by TheChuckster
It works, sort of. I added this to my bootloader:

Code: Select all

global load
load:
        jmp 0x200000
When I call load from my C, it works. Now I want some more flexible inline assembly so I run that through intel2gas. The output is:

jmp 0x200000

so I put that in my code as:

__asm__ volatile ("jmp 0x200000");

It compiles but when I run it I get an illegal opcode exception. Surely I'm doing the exact same thing as the assembly, no? What's different about this that's causing it to crash?

Re:Executing flat binaries

Posted: Tue Oct 11, 2005 1:38 am
by Candy
TheChuckster wrote: It works, sort of. I added this to my bootloader:

Code: Select all

global load
load:
        jmp 0x200000
When I call load from my C, it works. Now I want some more flexible inline assembly so I run that through intel2gas. The output is:

jmp 0x200000

so I put that in my code as:

__asm__ volatile ("jmp 0x200000");

It compiles but when I run it I get an illegal opcode exception. Surely I'm doing the exact same thing as the assembly, no? What's different about this that's causing it to crash?
0x200000 isn't intel syntax in the first place. IIRC it requires it to be 0200000h instead. Try to run it through intel2gas with that.

Re:Executing flat binaries

Posted: Tue Oct 11, 2005 1:58 am
by Pype.Clicker
okay. After checking the good old nasm-then-objdump thing, it appears that "jmp 0x200000" (and even "jmp 200000" as far as i can tell) are what AT&T likes.

Now, you say you still have illegal opcode
- at what address does that occur. If EIP==0x200000, then your jump was successful but there is simply no valid code there
- what do you have there. Did you check the memcpy worked properly ? afaik even QEMU has a monitor tool that helps you seeing content of memory
- do your code segment and your data segment have the same base address. If not, data written at 0x200000 may not appear at the same offset in the code segment or they might not appear at all...
- did you checked the string produced by "raw2c" was actually your code ? You might wish to compare the codes produced with a table of opcode and actually check by hand the first few instructions do what they're supposed to do.

In fewer words, i think your initial problem is a bit too complex and that you should try something simpler first such like using "call" instead of "jmp", and use a couple of very simple opcodes such as "mov eax,0xcafebabe ; ret" to see if your code executed.

Re:Executing flat binaries

Posted: Tue Oct 11, 2005 2:47 am
by bubach
>0x200000 isn't intel syntax in the first place.
Yes it is.
>IIRC it requires it to be 0200000h instead.
No it doesn't. :P

Re:Executing flat binaries

Posted: Tue Oct 11, 2005 4:28 am
by Solar
Pype.Clicker wrote: In fewer words, i think your initial problem is a bit too complex and that you should try something simpler first such like using "call" instead of "jmp", and use a couple of very simple opcodes such as "mov eax,0xcafebabe ; ret" to see if your code executed.
Also called "reducing the problem to the minimum set required to reproduce the problem", or "debugging". Hint: consider what you would have to do if you were not to ask for advice, but to write a bug report to an extremly busy developer who doesn't know zilch about your problem and couldn't be bothered to check for many details.

That gets you to the problem rather quick, usually.

Re:Executing flat binaries

Posted: Tue Oct 11, 2005 5:17 pm
by TheChuckster
I think I got it! But I don't know how to fix it. Looking at the disassembly of main.o (the C code) with objdump:

53: e8 fc ff 1f 00 call 200054 <k_main+0x200054>

The instruction call 0x200000 is for some reason being compiled as calling 0x200054. Why is it doing that? It SAYS 200000 in the C code! Is this one of GCC's weird misoptimizations? -nostdinc -fno-builtin are the only compiler flags that I am using. Perhaps it's a stack problem? Or it's being misinterpreted as a relative jump? (Ah! I think that's it! But how to fix?)

Well, I caught on to its tricks and I replaced it with call 1FFFAC -- 54 bytes less since it likes to be 54 bytes off for some reason. That didn't work either. call 200023 was the result. Weird.

Re:Executing flat binaries

Posted: Wed Oct 12, 2005 1:07 am
by Candy
TheChuckster wrote: I think I got it! But I don't know how to fix it. Looking at the disassembly of main.o (the C code) with objdump:

53: e8 fc ff 1f 00 call 200054 <k_main+0x200054>

The instruction call 0x200000 is for some reason being compiled as calling 0x200054. Why is it doing that? It SAYS 200000 in the C code! Is this one of GCC's weird misoptimizations? -nostdinc -fno-builtin are the only compiler flags that I am using. Perhaps it's a stack problem? Or it's being misinterpreted as a relative jump? (Ah! I think that's it! But how to fix?)

Well, I caught on to its tricks and I replaced it with call 1FFFAC -- 54 bytes less since it likes to be 54 bytes off for some reason. That didn't work either. call 200023 was the result. Weird.
Well.. the location specified in the instruction:

Code: Select all

e8 fc ff 1f 00 -> 00 1f ff fc -> 200000 - 4
is in fact your 200000 minus four. My guess is that you are using a relative jump of 200000 (0xE8 or 0xEB), whereas you are trying to do an absolute jump of 200000 (which is 0xEA).