Overv / MineAssemble

A tiny bootable Minecraft clone written partly in x86 assembly
MIT License
1.04k stars 69 forks source link

Makefile for cross-compiling, debuggable elf image #5

Closed rikusalminen closed 11 years ago

rikusalminen commented 11 years ago

Improved Makefile using a cross compiler toolchaing and dependency generation.

A linker script for building debuggable ELF files.

Instructions for building a cross compiler and remote debugging with QEMU and GDB.

rikusalminen commented 11 years ago

@Overv I wrote a better makefile for cross compiling and added instructions for building the toolchain to README.md. The new makefile does not put binary files under bin/, that doesn't work too well with GNU make if you want to take advantage of implicit build rules.

The elf linker script file does not seem to work correctly, it needs a little fine tuning. It crashes qemu with this message:

qemu: fatal: Trying to execute code outside RAM or ROM at 0x000a0000

I already had the elf files working properly in QEMU + gdb remote debugger at one point but I don't know what I did to break it. Maybe the linker script is fragile. I'll see if I can fix the issue later.

Overv commented 11 years ago

Okay, I'll play around with it tomorrow and then merge if it's stable enough.

rikusalminen commented 11 years ago

There's no hurry to get this merged.

On the other hand, it this change shouldn't break anything. edit: The old patch didn't really link the .bin file properly but it still booted ok because the bin file is so small that the multiboot header is close enough to beginning of file.

I will poke around with the debugger/objdump a little bit and see if I can make the elf files work, I think the address space set up by the linker does not quite match what the bootloader and init code expects.

@Overv did you try building a cross compiler toolchain? Did the instructions I wrote work?

rikusalminen commented 11 years ago

I have solved the issue. I can now run MineAssemble on QEMU i368-softmmu with remote debugging using i686-pc-elf-gdb. I also tested the ELF file on real hardware using GRUB.

The problem was that the init code and multiboot header were not placed in the beginning of the elf executable.

The old Makefile had init.o as the first object file in the (hand-written) linker command line. This makes init.o text section the first object in the output binary and that makes it work.

I changed the linker scripts to put src/init.o text section in the beginning of the text section. That puts the multiboot header in the right place (close enough to beginning of executable binary).

The new Makefile works a little better, but it's still not perfect. It uses automatic dependency generation (using gcc -MMD and nasm -MD) but it works only after make has been run twice. It should, however, reliably rebuild everything when needed (and only when needed).

The latest patch should be good enough to merge. Test and review, please!

Overv commented 11 years ago

@rikusalminen I managed to install the toolchain. I did have to fix two typos (?) in your script:

- mkdir ~/i686-pc-elf-build/gcc ; cd ~/gcc-pc-elf-build/gcc
+ mkdir ~/i686-pc-elf-build/gcc ; cd ~/i686-pc-elf-build/gcc

- mkdir ~/i686-pc-elf-build/qemu ; cd ~/gcc-pc-elf-build/qemu
+ mkdir ~/i686-pc-elf-build/qemu ; cd ~/i686-pc-elf-build/qemu

The binutils, gcc and qemu projects all compiled successfully, but they each gave some strange errors when running make install. It doesn't seem like it affected anything however. I'm now going to try your makefile.

Overv commented 11 years ago

Hmm, currently getting this when compiling with your latest makefile:

overv@overv-linux:~/Code/MineAssemble$ make
i686-pc-elf-gcc -std=c99 -MMD -m32 -g -ggdb -O3 -ffast-math -ffreestanding -nostdlib -nostdinc -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-stack-protector -static -fno-pic   -c -o src/reference.o src/reference.c
/tmp/cc2nyW1b.s: Assembler messages:
/tmp/cc2nyW1b.s:269: Error: invalid instruction suffix for `push'
/tmp/cc2nyW1b.s:271: Error: invalid instruction suffix for `push'
/tmp/cc2nyW1b.s:273: Error: invalid instruction suffix for `push'
/tmp/cc2nyW1b.s:275: Error: invalid instruction suffix for `push'
...

Seems like it's using the assembler in 64-bit mode for some reason.

rikusalminen commented 11 years ago

The latest revision is just a few touches to the toolchain build instructions in the README.

I fixed the copy/paste/typos you mention.

rikusalminen commented 11 years ago

@Overv I don't know what your build problem could be. Do you have the right binutils in your $PATH? Is GCC trying to use a wrong version of as to assemble? It needs to find i686-pc-elf-as to work. "invalid instruction suffix" often means that there's a 64 bit instruction in 32 bit code.

For debugging, here's the assembly output for reference.c from my compiler: http://www.pasteall.org/43406

Generated using this:

i686-pc-elf-gcc -std=c99 -m32 -g -ggdb -O3 -ffast-math -ffreestanding -nostdlib -nostdinc -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-stack-protector -static -fno-pic   -S -o reference.s src/reference.c

Can you spot a difference?

Overv commented 11 years ago

@rikusalminen Ah, you're right, I currently don't have a i686-pc-elf-as executable.

rikusalminen commented 11 years ago

@Overv something has gone wrong with your binutils installation then, you'll need to have i686-pc-elf-as and i686-pc-elf-ld in your $PATH. Try running the install again and see if there were any meaningful errors.

Overv commented 11 years ago

@rikusalminen Ah, the binutils building got stuck because I didn't have texinfo installed, it finished building now and I can successfully make and run it (although the QEMU test still runs at 0.3 frames per second)

rikusalminen commented 11 years ago

Good that you can build it.

Running this under QEMU is really slow, but it's the easiest way to attach a debugger to a kernel image.