OS Specific Toolchain: Difference between revisions

change categorization
[unchecked revision][unchecked revision]
(change categorization)
 
(6 intermediate revisions by 4 users not shown)
Line 15:
* automake (exactly version 1.15.1)
* libtool
* The latest Binutils source code (2.3839 at time of writing).
* The latest GCC source code (1112.32.0 at time of writing).
* Knowledge of the internals of Binutils and GCC.
* Knowledge of autoconf and automake.
Line 28:
 
This tutorial currently only have instructions for adding a new x86 and x86_64 target for myos, but it serves as a good enough example that it should be trivial to add more processors by basing it on these instructions and what other operating systems have done.
 
 
== Making your changes reproducible ==
Since at some point you might have a contributor that wants participate in your project, you likely want to make your toolchain setup reproducible. Instead of downloading the tar-balls, it is therefore a good idea to clone the repositories of binutils (https://sourceware.org/git/binutils-gdb.git) and gcc (https://gcc.gnu.org/git/gcc.git) locally.
 
All release versions are tagged within these repositories, so you can use <tt>"git checkout binutils-2_39"</tt> and <tt>"git checkout releases/gcc-12.2.0"</tt> to switch to the correct versions respectively. After making the described changes, you can easily create a patch using <tt>"git diff"</tt> which you can reuse later.
Another option would be to fork one of the mirror repositories on GitHub.
 
 
== Modifying Binutils ==
Line 39 ⟶ 47:
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:
 
<sourcesyntaxhighlight lang="bash">
i[3-7]86-*-myos*)
targ_defvec=i386_elf32_vec
Line 52 ⟶ 60:
;;
#endif
</syntaxhighlight>
</source>
 
'''Note:''' If using binutils-2.24 or older, change <i>i386_elf32_vec</i> to <i>bfd_elf32_i386_vec</i> and <i>x86_64_elf64_vec</i> to <i>bfd_elf64_x86_64_vec</i>.
Line 62 ⟶ 70:
This file tells the gnu assembler what type of output to generate for each target. It automatically matches the i686 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 '<tt>Assign object format ... case ${generic_target} in</tt>' you need to add a line like
 
<sourcesyntaxhighlight lang="bash">
i386-*-myos*) fmt=elf ;;
</syntaxhighlight>
</source>
 
You should use '<tt>i386</tt>' in this line even if you are targeting x86_64. This is the only file where you should do it. It is basically because the variable 'generic_target' is not your canonical target name, but rather a variable generated further up in the configure.tgt file, and it sets the first part to <tt>i386</tt> for any <tt>i[3-7]86</tt> or <tt>x86_64</tt>.
Line 72 ⟶ 80:
=== 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 <tt>elf_i386_myos</tt>. We need to add an entry to the case statement here after '<tt>Please try to keep this table more or less in alphabeticalalphabetic order ... case "${targ}</tt>" in':
 
<sourcesyntaxhighlight lang="bash">
i[3-7]86-*-myos*)
targ_emul=elf_i386_myos
Line 84 ⟶ 92:
targ_extra_emuls="elf_i386_myos elf_x86_64 elf_i386"
;;
</syntaxhighlight>
</source>
 
* '''elf_i386_myos''' is a 32-bit target for your OS.
Line 97 ⟶ 105:
Now we need to actually define our emulation. There is a generic file called <tt>ld/genscripts.sh</tt> 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 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:
 
<sourcesyntaxhighlight lang="bash">
source_sh ${srcdir}/emulparams/elf_i386.sh
TEXT_START_ADDR=0x08000000
</syntaxhighlight>
</source>
 
This script is included by <tt>ld/genscripts.sh</tt> to customize its behavior through shell variables. We include the base <tt>elf_i386.sh</tt> script as it sets reasonable defaults. Finally, we override the variables whose defaults we disagree with.
Line 119 ⟶ 127:
This file is just like the above <tt>ld/emulparams/elf_i386_myos.sh</tt> but for x86_64.
 
<sourcesyntaxhighlight lang="bash">
source_sh ${srcdir}/emulparams/elf_x86_64.sh
</syntaxhighlight>
</source>
 
=== ld/Makefile.am ===
 
We now just need to tell make how to produce the emulation C file for our specific emulation. Putting the '<tt>targ_emul=elf_i386_myos</tt>' line into <tt>ld/configure.tgt</tt> above implies that your host linker will try to link your target ld executable with an object file called <tt>eelf_i386_myos.o</tt>. There is a default rule to generate this from <tt>eelf_i386_myos.c</tt>, so we just need to tell it how to make this <tt>eelf_i386_myos.c</tt> file. As stated above, we let the genscripts.sh file do the hard work. You need to add <tt>eelf_i386_myos.c</tt> to the <tt>ALL_EMULATION_SOURCES</tt> list; you also need to add <tt>eelf_x86_64_myos.c</tt> to the <tt>ALL_64_EMULATION_SOURCES</tt> list if applicable.
 
There is a complicated list of target includes (around line 648) which you need to add an entry to, aswell. Find the comment starting '<tt>It's a pity we can't generate these include</tt>'... and look for the line containing <tt>eelf_i386.Pc</tt>, duplicate it, but replace <tt>eelf_i386</tt> with <tt>eelf_i386_myos</tt>. You should have something like this now in the list:
 
<source lang="bash">
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf_i386_myos.Pc@am__quote@
</source>
 
You can also add an entry after the line with <tt>eelf_x86_64.Pc</tt> for <tt>eelf_x86_64_myos.Pc</tt>, like this:
 
<source lang="bash">
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf_x86_64_myos.Pc@am__quote@
</source>
 
'''Note''': You ''must'' run <tt>automake</tt> in the <tt>ld</tt> directory after you modify <tt>Makefile.am</tt> to regenerate <tt>Makefile.in</tt>.
Line 153 ⟶ 149:
For the first part, find the '<tt>case ${target} in</tt>' line just after '<tt># Common parts for widely ported systems</tt>' (around line 688) and add something like:
 
<sourcesyntaxhighlight lang="bash">
*-*-myos*)
gas=yes
Line 160 ⟶ 156:
use_gcc_stdint=provide
;;
</syntaxhighlight>
</source>
 
* '''gas=yes''' our operating system by default uses the GNU assembler
Line 167 ⟶ 163:
* '''use_gcc_stdint=provide''' This instructs gcc to provide you with a ''stdint.h'' appropiate for your target. Change <tt>provide</tt> to <tt>wrap</tt> if you have your own ''stdint.h'', to make GCC wrap yours.
 
The second section we need to add to is the architecture-specific one. Find the '<tt>case ${target} in</tt>' line just before '<tt>tm_file="${tm_file} dbxelf.h elfos.h newlib-stdint.h"</tt>' (around line 1094) and add something like:
 
<sourcesyntaxhighlight lang="bash">
i[34567]86-*-myos*)
tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h glibc-stdint.h i386/i386elf.h myos.h"
;;
x86_64-*-myos*)
tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h glibc-stdint.h i386/i386elf.h i386/x86-64.h myos.h"
;;
</syntaxhighlight>
</source>
 
This defines which target configuration header files gets used. You can make <tt>i386/myos32.h</tt> and <tt>i386/myos64.h</tt> files if desired.
Line 186 ⟶ 182:
You can explore <tt>gcc/defaults.h</tt> for a full list of things you can modify, and more importantly, the assumptions GCC will make about various aspects of your target. For instance, if <tt>PID_TYPE</tt> is not defined in <tt>myos.h</tt>, then GCC will default it to <tt>int</tt>, which can be problematic if that's not what your <tt>pid_t</tt> is defined as.
 
<sourcesyntaxhighlight lang="C">
/* Useful if you wish to make target-specific GCC changes. */
#undef TARGET_MYOS
Line 215 ⟶ 211:
builtin_assert ("system=posix"); \
} while(0);
</syntaxhighlight>
</source>
 
=== libstdc++-v3/crossconfig.m4 ===
Line 221 ⟶ 217:
This file describes how the libstdc++ configure file will examine your operating system and adjust the provided features of libstdc++ accordingly. Add a case similar to
 
<sourcesyntaxhighlight lang="autoconfbash">
*-myos*)
GLIBCXX_CHECK_COMPILER_FEATURES
Line 228 ⟶ 224:
GLIBCXX_CHECK_STDLIB_SUPPORT
;;
</syntaxhighlight>
</source>
 
'''TODO''': Examine this design and find out what actually needs to be done here.
 
'''TODO''': This hasn't been updated since the article was about GCC 7.2.0. It's probably very wrong now. It needs to be updated.
 
'''Note''': You need to run <tt>autoconf</tt> in the <tt>libstdc++-v3</tt> directory.
Line 240 ⟶ 232:
Find the '<tt>case ${host} in</tt>' just prior to '<tt>extra_parts="$extra_parts crtbegin.o crtend.o crti.o crtn.o"</tt>' (around line 368) and add the cases:
 
<sourcesyntaxhighlight lang="bash">
i[34567]86-*-myos*)
extra_parts="$extra_parts crti.o crtbegin.o crtend.o crtn.o"
Line 249 ⟶ 241:
tmake_file="$tmake_file i386/t-crtstuff t-crtstuff-pic t-libgcc-pic"
;;
</syntaxhighlight>
</source>
 
=== fixincludes/mkfixinc.sh ===
Line 255 ⟶ 247:
You should disable fixincludes for your operating system. Find the case statement and add a pattern for your operating system. For instance:
 
<sourcesyntaxhighlight lang="bash">
# Check for special fix rules for particular targets
case $machine in
Line 267 ⟶ 259:
(echo "#! /bin/sh" ; echo "exit 0" ) > ${target}
;;
</syntaxhighlight>
</source>
 
A number of operating systems (especially older and obscure ones) provide troublesome systems headers that fail to strictly comply with various standards. The GCC developers consider it their job to fix these headers. GCC will look into your system root, apply a bunch of patterns to detect headers it doesn't like, then it copies that header into a private GCC system directory (that overrides your standard system directory) and attempts to fix the header. Sometimes fixincludes even break working headers (some people refer to it as breakincludes).
Line 287 ⟶ 279:
If you wish to change the default library directory from <tt>/usr/lib</tt>, you can change it to <tt>/lib</tt> by adding the following block of code to the case just below the declaration of <tt>NATIVE_LIB_DIRS</tt> in <tt>binutils/ld/configure.tgt</tt> (around line 1056).
 
<sourcesyntaxhighlight lang="cbash">
*-*-myos*)
NATIVE_LIB_DIRS='/lib /local/lib'
;;
</syntaxhighlight>
</source>
 
=== Start Files Directory ===
Line 297 ⟶ 289:
You can modify which directory GCC looks for the crt0.o, crti.o and crtn.o in. The path to that directory is stored in <tt>STANDARD_STARTFILE_PREFIX</tt>. For instance, if you change the library directory to <tt>/lib</tt> in Binutils and want GCC to match, you can add the following to <tt>gcc/config/myos.h</tt>:
 
<sourcesyntaxhighlight lang="C">
#undef STANDARD_STARTFILE_PREFIX
#define STANDARD_STARTFILE_PREFIX "/lib/"
</syntaxhighlight>
</source>
 
Note that the trailing slash is important as the raw crt*.o names are appended without first adding a slash.
Line 310 ⟶ 302:
* Set <tt>LINK_SPEC</tt> in your "gcc/config/myos.h" so the gcc driver will pass the correct flags to the linker:
 
<sourcesyntaxhighlight lang="C">
#undef LINK_SPEC
#define LINK_SPEC "%{shared:-shared} %{static:-static} %{!shared: %{!static: %{rdynamic:-export-dynamic}}}"
</syntaxhighlight>
</source>
 
=== Linker Options ===
Line 319 ⟶ 311:
You can modify the arguments passed to the linker using <tt>LINK_SPEC</tt>. This can be used to force 4KB alignment of sections on 64 bit systems, as ld defaults to 2MB alignment.
 
<sourcesyntaxhighlight lang="C">
/* Tell ld to force 4KB pages*/
#undef LINK_SPEC
#define LINK_SPEC "-z max-page-size=4096"
</syntaxhighlight>
</source>
 
== Selecting a C Library ==
Line 362 ⟶ 354:
* [[GCC]]
 
[[Category:ToolsToolchains]]
[[Category:Tutorials]]