A bare-metal x86-cross-compiler on Mountain Lion

I wanted to create a bare-metal program for an Intel-x86 processor. The program is used in a chapter called The Bare Metal in the book Into Embedded.

On an Ubuntu Linux host I could use gcc, together with a linker script, to accomplish the task. I could then run the program using Bochs.

On a Mac Mountain Lion host, the above method did not work out of the box, due to ld not being the GNU linker.

In addition, gcc was not GNU gcc either.

I searched the net and found this interesting page by M3 Operating System Development where it was described how to build an i386 cross-compiler. I thought that this could be a way to create a compiler for bare-metal programs with no OS-dependencies – which was desirable since the bare-metal program was to be evolved into an OS by itself.

Problem arose, however, since the pre-installed gcc on my Mac could not build the i386-cross-gcc.

I searched again, and found this eminent page by Solarian Programmer where it is described how to build a GNU gcc compiler.

I downloaded the gcc 4.7.2 sources from the GNU ftp repository.

Then, following the instructions, however leaving out the choice of building a compiler for Fortran and also changing the gcc version from 4.7.1 to gcc 4.7.2, I was able to build a native GNU gcc compiler.

The newly built native gcc could then be set to the default gcc by issuing the command

export PATH=/usr/gcc-4.7.2/bin:$PATH

Then, returning to the instructions for building an i386-cross-gcc, I started with downloading

binutils-2.23.tar.gz

from the GNU binutils repository.

I created a destination directory for the cross compiler, by doing

sudo mkdir /usr/local/i386elfgcc/

The binutils could then be unpacked, configured, and built, using the commands

tar zxvf binutils-2.23.tar.gz
mkdir build-binutils
cd build-binutils/
../binutils-2.23/configure --target=i386-elf --prefix=/usr/local/i386elfgcc
make
sudo make install

I then built gcc, using inspiration also from this OS development wiki, by issuing the commands

tar xvjf gcc-4.7.2.tar.bz2
mkdir build-gcc
cd build-gcc
../gcc-4.7.2/configure --target=i386-elf --prefix=/usr/local/i386elfgcc --with-gnu-as --with-gnu-ld --disable-libssp --enable-languages=c --without-headers
make all-gcc
sudo make install-gcc

After having added /usr/local/i386elfgcc/bin to the PATH environment variable I was able to compile and link a bare metal program.

The program consists of three C-files, which I could compile using the commands

i386-elf-gcc -Wall -c screen_output.c
i386-elf-gcc -c -Wall -DBUILD_X86_FD_TARGET src/bare_metal.c -o obj/bare_metal_x86_fd_target.o
i386-elf-gcc -c -Wall -DBUILD_X86_FD_TARGET src/console.c -o obj/console_x86_fd_target.o

The program could then be linked, using a command where also startup code, written in assembly and assembled using NASM resulting in the object file start_code.o, as

i386-elf-ld -T arch/x86_fd_target/link.ld --oformat=elf32-i386 -melf_i386 arch/x86_fd_target/start_code.o arch/x86_fd_target/screen_output.o -o prog_x86_fd_target.elf obj/console_x86_fd_target.o obj/bare_metal_x86_fd_target.o

Finally, the file prog_x86_fd_target.elf needs to be converted from ELF format to raw binary format. This can be done using the command

i386-elf-objcopy -O binary prog_x86_fd_target.elf prog_x86_fd_target.bin

As a last step, a binary bootable image for a floppy-disc drive can be created, by concatenating a FAT12 boot sector, a defined number of empty FAT12-sectors, and the binary file created by the i386-elf-objcopy command.

The concatenation is done as

cat arch/x86_fd_target/boot.bin arch/x86_fd_target/b_32_512.bin prog_x86_fd_target.bin > arch/x86_fd_target/a.img

The program can now be run, using Bochs, by giving the command

bochs -f arch/x86_fd_target/bochsrc.txt -q

Advertisements

Installing Bochs

Bochs is an x86-emulator that can be installed on different platforms. Here I describe my experiences of installing Bochs on Linux and on MacOS Mountain Lion.

Ubuntu Linux

Installing Bochs on Ubuntu was simple. I did

sudo apt-get install bochs

Then I located the Bochs configuration file. It was found, and copied to the directory where I intended to run Bochs, as

cp /usr/local/share/doc/bochs/bochsrc-sample.txt bochsrc.txt

My goal was to simulate an x86-computer with a floppy-disc unit. Changes were therefore done, in the file bochsrc.txt. The changes can be seen from the diff-command

diff /usr/local/share/doc/bochs/bochsrc-sample.txt bochsrc.txt

as

387c387
< floppya: 1_44=/dev/fd0, status=inserted
---
> #floppya: 1_44=/dev/fd0, status=inserted
392c392
< #floppya: 1_44=a.img, status=inserted, write_protected=1
---
> floppya: 1_44=arch/x86_fd_target/a.img, status=inserted, write_protected=1
474c474
< ata0-master: type=disk, mode=flat, path="30M.sample"
---
> #ata0-master: type=disk, mode=flat, path="30M.sample"
494,495c494,495
< #boot: floppy
< boot: disk
---
> boot: floppy
> #boot: disk

Then I am ready to run Bochs, using the command

bochs -f arch/x86_fd_target/bochsrc.txt -q

resulting in a simulated PC screen, as

bochs hello

Linux

The Bochs project is found at http://bochs.sourceforge.net.

After reading information about the latest release, I navigated to the releases page. I then downloaded release 2.6 in source format, resulting in download of the file

bochs-2.6.tar.gz

I then unpacked the source and navigated to the newly created bochs source directory, as

tar zxvf bochs-2.6.tar.gz
cd bochs-2.6

Then the configure script shall be run. Since I was not root on the machine, I used a prefix to configure, indicating the directory where I wanted Bochs to be installed. The configure command used was

./configure --prefix=/nobackup/local/prog/bochs

Bochs was then built, using the command

make

and installed using the command

make install

Then I located the Bochs configuration file. It was found, and copied to the directory where I intended to run Bochs, as

cp /nobackup/local/prog/bochs/share/doc/bochs/bochsrc-sample.txt arch/x86_fd_target/bochsrc.txt

My goal was to simulate an x86-computer with a floppy-disc unit. Changes were therefore done, in the file bochsrc.txt. The changes can be seen from the diff-command

diff /nobackup/local/prog/bochs/share/doc/bochs/bochsrc-sample.txt arch/x86_fd_target/bochsrc.txt

as

190c190
< cpu: model=core2_penryn_t9600, count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def"
---
> cpu: count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def"
415c415
< floppya: 1_44=/dev/fd0, status=inserted
---
> #floppya: 1_44=/dev/fd0, status=inserted
420c420
< #floppya: 1_44=a.img, status=inserted, write_protected=1
---
> floppya: 1_44=arch/x86_fd_target/a.img, status=inserted, write_protected=1
502c502
< ata0-master: type=disk, mode=flat, path="30M.sample"
---
> #ata0-master: type=disk, mode=flat, path="30M.sample"
522,523c522,523
< #boot: floppy
< boot: disk
---
> boot: floppy
> #boot: disk
636c636
< debug: action=ignore, pci=report # report BX_DEBUG from module 'pci'
---
> debug: action=ignore, # pci=report # report BX_DEBUG from module 'pci'

I also added the following changes

export PATH=/nobackup/local/prog/bochs/bin:$PATH
export BXSHARE=/nobackup/local/prog/bochs/share/bochs

to my setup script, where I also set up other environment variables.

Then I am ready to run Bochs, using the command

bochs -f arch/x86_fd_target/bochsrc.txt -q

resulting in a simulated PC screen as shown above.

Mac Mountain Lion

I navigated to the releases page. I then downloaded release 2.6 in source format, resulting in download of the file

bochs-2.6.tar.gz

I then unpacked the source and navigated to the newly created bochs source directory, as

tar zxvf bochs-2.6.tar.gz
cd bochs-2.6

The configure command used was

./configure --with-x11

The –with-x11 indicates that X11 shall be used. For this purpose I had installed XQuartz, as described by Apple in this support note.

Bochs was then built, using the command

make

and installed using the command

sudo make install

I used the same configuration file as described above for Ubuntu, and I could then run Bochs using the command

bochs -f arch/x86_fd_target/bochsrc.txt -q

resulting in a simulated PC screen, as shown above.

Installing NASM

NASM is an assembler for x86-based computers. Here I describe my experiences from installing NASM on Linux and on Mac Mountain Lion.

Ubuntu Linux

NASM was installed using the command

sudo apt-get install nasm

Linux

Starting from the NASM site I navigated to the NASM download page.

I downloaded NASM version 2.10.05 in source format, by downloading the file

nasm-2.10.05.tar.bz2

I unpacked the file and navigated to the newly created directory, using the commands

tar xvjf nasm-2.10.05.tar.bz2
cd nasm-2.10.05

As a preparation for building NASM, a configure command shall be issued. Since I was not root for the machine, I used a prefix, indicating the place where I wanted NASM to be installed. The command used was

./configure --prefix=/nobackup/local/prog/nasm

Then I could build and install NASM, using the commands

make
make install

I also added the following changes

export PATH=/nobackup/local/prog/nasm/bin:$PATH

to my setup script, where I also set up other environment variables.

Mac Mountain Lion

Starting from the NASM site I navigated to the NASM download page. From there I continued to the Mac OS download page, from where I downloaded the file

nasm-2.10.05-macosx.zip

Unzipping this file resulted in a directory named nasm-2.10.05 being created.

I now noticed that I already had nasm on my computer, as could be seen from

Olas-MacBook-Air:nasm-2.10.05 oladahl$ nasm -v
NASM version 0.98.40 (Apple Computer, Inc. build 11) compiled on Aug 4 2012

That version was however older than the newly downloaded, as could be seen by doing

Olas-MacBook-Air:nasm-2.10.05 oladahl$ ./nasm -v
NASM version 2.10.05 compiled on Sep 9 2012

I decided to put the downloaded nasm in a directory called prog/nasm inside my home directory. I moved it there using

mkdir -p ~/prog/nasm
mv * ~/prog/nasm

I then added the following changes

export PATH=~/prog/nasm:$PATH

to my setup script, where I also set up other environment variables. After having rerun the setup script I could use the downloaded nasm, after first having verified that it was found using

Olas-MacBook-Air:i1_bare_metal oladahl$ nasm -v
NASM version 2.10.05 compiled on Sep 9 2012