OS Specific Toolchain: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(Added binutils commands)
(Added gcc commands)
Line 67: Line 67:
${GENSCRIPTS} myos_i386 "$(tdir_myos_i386)"
${GENSCRIPTS} myos_i386 "$(tdir_myos_i386)"
Note that some parts of the line use normal brackets () whereas other parts use curly braces {}. Also, the second line is indented with a tab, not spaces.
Note that some parts of the line use normal brackets () whereas other parts use curly braces {}. Also, the second line is indented with a tab, not spaces.
You can also add 'emyos_i386.o' to the dependencies of 'ALL_EMULATIONS' if you like, but it is not essential.


== GCC ==
=== config.sub ===
Similar modification to config.sub in binutils.

=== gcc/config.gcc ===
This file defines what needs to be built for each particular target and what to include in the final executable. There are two main sections: one which defines generic options for your operating system, and those which define options specific to your operating system on each individual machine type. For the first part, find the section starting 'Common parts for widely ported systems ... case ${target} in' and add something like:
*-*-myos*)
extra_parts="crtbegin.o crtend.o"
gas=yes
gnu_ld=yes
default_use_cxa_atexit=yes
;;
This basically says: build the static versions of crtbegin and crtend (important for various parts of libgcc), our operating system by default uses the GNU linker and assembler and that we will provide __cxa_atexit (newlib will actually provide it).
The section section we need to add to is the architecture-specific one. Find the section starting 'case ${target} in ... Support site-specific machine types' and add
i[3-7]86-*-myos*)
tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h i386/i386elf.h myos.h"
tmake_file="i386/t-i386elf t-svr4"
use_fixproto=yes
;;
Which defines some extra include files and makefile fragments to use. We will create the myos.h next.

=== gcc/config/myos.h ===
Now we create a header file which sets some os-specific stuff in gcc. This currently just sets some variables which state that the c preprocessor should #define some macros, make some asserts and set the default system name when we are compiling an application for our os. Something like the following should be fine.
#undef TARGET_OS_CPP_BUILTINS
#define TARGET_OS_CPP_BUILTINS() \
do { \
builtin_define_std ("myos"); \
builtin_define_std ("unix"); \
builtin_assert ("system=myos"); \
builtin_assert ("system=unix"); \
} while(0);

#undef TARGET_VERSION
#define TARGET_VERSION fprintf(stderr, " (i386 myos)");

=== libstdc++-v3/crossconfig.m4 ===
Only necessary if you are compiling the GNU C++ compiler AND you wish to cross-compile the standard C++ library for your os. You can experiment with adding more options (ala the Linux case) if you wish. Add a case similar to
*-myos*)
AC_CHECK_HEADERS([sys/types.h locale.h float.h])
GLIBCXX_CHECK_BUILTIN_MATH_SUPPORT
GLIBCXX_CHECK_COMPLEX_MATH_SUPPORT
GLIBCXX_CHECK_STDLIB_SUPPORT
;;

Revision as of 19:09, 12 September 2007

This tutorial will walk you through creating a toolchain comprising binutils, gcc (with libgcc), newlib and optionally g++ and libstdc++ (with libsupc++) that specifically targets your os. In this tutorial we will assume you are using a 32-bit i586 compatible, IBM PC compatible machine for a hypothetical os named 'myos' and so the gnu canonical name we will be targeting is 'i586-pc-myos'. It should not, however, be difficult to adapt this tutorial for other systems, e.g. x86-64. This tutorial will produce a setup with not much different functionality from those produced by the GCC Cross-Compiler and Porting Newlib tutorials (aside from the name). It is merely intended to demonstrate the principles behind adding a new target to these packages and provide you with a framework for incorporating your own os-specific modifications.

Prerequisites

The latest binutils, gcc-core and newlib packages, and optionally gcc-g++. This tutorial was developed with binutils-2.18, gcc-4.2.1 and newlib-1.15.0. A build environment that can successfully build a GCC Cross-Compiler. This tutorial was developed with Cygwin. The os you are targeting should be able to handle syscalls, with a well defined interface. Knowledge of the internals of binutils, gcc and newlib, along with a working knowledge of autoconf and automake.

Preparation

Extract the source tarballs into your /usr/src directory. Set up the TARGET and PREFIX environment variables:

 export TARGET=i586-pc-myos
 export PREFIX=/usr/cross

Create the build directories:

 mkdir build-binutils build-gcc build-newlib

Modify the source directories to add your target

Now you can start adding your own target to each package. Some of the changes are quite similar between each package.

Binutils

config.sub

This is a file you will modify in the same way for each package. It is a gnu standard file produced by including the line 'AC_CANONICAL_SYSTEM' in a configure.in that is processed by autoconf, and is designed to convert a canonical name of the form 'i586-pc-myos' into separate variables for the processor, vendor and os, and also rejects systems it doesn't know about. We simply need to add 'myos' to the list of acceptable operating systems. Find the section that begins with the comment "First accept the basic system types" (it begins '-gnu*') and add '-myos*' to the list. I typically add it after -aos* simply because there is some free room on the line there.

bfd/config.bfd

This file is part of the configuration for libbfd, the back-end to binutils which provides a consistent interface for many object file formats. Generally, each platform-specific version of binutils contains a libbfd which only supports the object files normally in use on that system, as otherwise the library would be massive (libbfd can support a _lot_ of object types). We need to associate our os with some particular object types. There is a long list starting 'WHEN ADDING ENTRIES TO THIS MATRIX' with the first line as 'case "${targ}" in'. We need to add our full canonical name to this list, by adding some cases such as:

 i[3-7]86-*-myos*)
   targ_defvec=bfd_elf32_i386_vec
   targ_selvecs=i386_coff_vec
   ;;

If you like, you could support different object formats (look at other entries in the list, and the contents of 'bfd' for hints) and also provide more than one to the 'targ_selvecs' line. The example here lets you deal with coff object files, but defaults to elf for object files and executables (similar to the i586-elf GCC Cross-Compiler).

gas/configure.tgt

This file tells the gnu assembler what type of output to generate for each target. It automatically matches the i586 part of your target and generates the correct output for that. We just need to tell it what type of object file to generate for myos. In the section starting 'Assign object file format ... case ${generic_target} in' you need to add a line like

 i[3-7]86-*-myos*)    fmt=elf ;;

ld/configure.tgt

This file tells the gnu linker what 'emulation' to use for each target. An emulation is basically a combination of linker script and executable file format. We are going to define our own emulation called myos_i386. We need to add an entry to the case statement here beginning 'Please try to keep this table in alphabetical order ... case "${targ}" in'

 i[3-7]86-*-myos*)    targ_emul=myos_i386 ;;

ld/emulparams/myos_i386.sh

Now we need to actually define our emulation. There is a generic file called ld/genscripts.sh which creates the required linker scripts for our target (you need more than one, depending on shared object usage and the like: I have 13 for a single target). It uses a linker script template (from the ld/scripttempl directory) to do this, and it creates the actual emulation c file from an emulation template (from the ld/emultempl directory). These templates are customised by running a script in the ld/emulparams directory which sets various variables. You are more than welcome to define your own emulation and linker templates, but I find the elf ones adequate, given that they can be customised by simply adding a file to the emulparams directory. This is what we are going to do now. The content of the file could be something like:

 SCRIPT_NAME=elf

(defines the scripttempl file to use, in this case the elf one)

 OUTPUT_FORMAT=elf32-i386

(create our executables in elf32 format)

 TEXT_START_ADDR=0x40000000

(start of the .text section and therefore the entire executable image. I set to this because my kernel occupies 0x0-0x3fffffff)

 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
 COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"

(so that ld knows page sizes so it can properly align sections to page boundaries, just use the defaults here)

 ARCH=i386
 MACHINE=

(self explanatory)

 NOP=0x90909090

(what ld pads sections with, basically the i386 NOP instruction 4 times to fill a 32-bit value)

 TEMPLATE_NAME=elf32

(defines the emultempl file to use, again, stick with elf)

 GENERATE_SHLIB_SCRIPT=yes
 GENERATE_PIE_SCRIPT=yes

(should we generate linker scripts to produced shared libraries and position-independent executables respectively)

 NO_SMALL_DATA=yes
 SEPARATE_GOTPLT=12

(unsure as to these, just copied from the elf one, seems to work okay)

ld/Makefile.in

We now just need to tell make how to produce the emulation c file for our specific emulation. Putting the 'targ_emul=myos_i386' line into ld/configure.tgt above implies that your host linker will try to link your target ld executable with an object file called emyos_i386.o. There is a default rule to generate this from emyos_i386.c, so we just need to tell it how to make this emyos_i386.c file. As stated above, we let the genscripts.sh file do the hard work. You need to add a makefile rule (I add it after the one to build eelf_i386.c) similar to:

 emyos_i386.c: $(srcdir)/emulparams/myos_i386.sh $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
          ${GENSCRIPTS} myos_i386 "$(tdir_myos_i386)"

Note that some parts of the line use normal brackets () whereas other parts use curly braces {}. Also, the second line is indented with a tab, not spaces. You can also add 'emyos_i386.o' to the dependencies of 'ALL_EMULATIONS' if you like, but it is not essential.


GCC

config.sub

Similar modification to config.sub in binutils.

gcc/config.gcc

This file defines what needs to be built for each particular target and what to include in the final executable. There are two main sections: one which defines generic options for your operating system, and those which define options specific to your operating system on each individual machine type. For the first part, find the section starting 'Common parts for widely ported systems ... case ${target} in' and add something like:

 *-*-myos*)
   extra_parts="crtbegin.o crtend.o"
   gas=yes
   gnu_ld=yes
   default_use_cxa_atexit=yes
   ;;

This basically says: build the static versions of crtbegin and crtend (important for various parts of libgcc), our operating system by default uses the GNU linker and assembler and that we will provide __cxa_atexit (newlib will actually provide it). The section section we need to add to is the architecture-specific one. Find the section starting 'case ${target} in ... Support site-specific machine types' and add

 i[3-7]86-*-myos*)
   tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h i386/i386elf.h myos.h"
   tmake_file="i386/t-i386elf t-svr4"
   use_fixproto=yes
   ;;

Which defines some extra include files and makefile fragments to use. We will create the myos.h next.

gcc/config/myos.h

Now we create a header file which sets some os-specific stuff in gcc. This currently just sets some variables which state that the c preprocessor should #define some macros, make some asserts and set the default system name when we are compiling an application for our os. Something like the following should be fine.

 #undef TARGET_OS_CPP_BUILTINS
 #define TARGET_OS_CPP_BUILTINS()      \
   do {                                \
     builtin_define_std ("myos");      \
     builtin_define_std ("unix");      \
     builtin_assert ("system=myos");   \
     builtin_assert ("system=unix");   \
   } while(0);
 #undef TARGET_VERSION
 #define TARGET_VERSION fprintf(stderr, " (i386 myos)");

libstdc++-v3/crossconfig.m4

Only necessary if you are compiling the GNU C++ compiler AND you wish to cross-compile the standard C++ library for your os. You can experiment with adding more options (ala the Linux case) if you wish. Add a case similar to

 *-myos*)
   AC_CHECK_HEADERS([sys/types.h locale.h float.h])
   GLIBCXX_CHECK_BUILTIN_MATH_SUPPORT
   GLIBCXX_CHECK_COMPLEX_MATH_SUPPORT
   GLIBCXX_CHECK_STDLIB_SUPPORT
   ;;