TCC: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Added Note about stdint.h)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(4 intermediate revisions by 4 users not shown)
Line 9: Line 9:
16/64-bit support, instructions up to MMX are supported.
16/64-bit support, instructions up to MMX are supported.


Note: The Windows version of TCC doesn't produces ELF executables, but only object files. You
Note: The Windows version of TCC doesn't produce ELF executables, but only object files. You
need to recompile TCC without PE support, if you want to use this tutorial on Windows. You can
need to recompile TCC without PE support, if you want to use this tutorial on Windows. You can
skip this step if you aren't using Windows.
skip this step if you aren't using Windows.


==Building TCC with ELF support==
==Building TCC with ELF support==
===Windows===
1. Download [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26.tar.bz2 TCC sources] and [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26-win32-bin.zip 32-bit TCC] and (if you have a 64-bit OS) [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26-win64-bin.zip 64-bit TCC].
1. Download [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26.tar.bz2 TCC sources] and [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26-win32-bin.zip 32-bit TCC] and (if you have a 64-bit OS) [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26-win64-bin.zip 64-bit TCC].


Line 22: Line 23:
4. Open Notepad or another text editor, and enter this:
4. Open Notepad or another text editor, and enter this:


<source lang="winbatch">
<syntaxhighlight lang="winbatch">
@echo off
@echo off


Line 95: Line 96:


:finished
:finished
</syntaxhighlight>
</source>


5. Save to tcc-0.9.26 with any name you want, but the extension MUST be .bat. If you use notepad, you will have to change the type from "Text Documents" to "All Files".
5. Save to tcc-0.9.26 with any name you want, but the extension MUST be .bat. If you use notepad, you will have to change the type from "Text Documents" to "All Files".
Line 101: Line 102:
6. Run the script, make sure everything compiled correctly (note that there may be warnings about assignments from incompatible pointer types and bound checking not supporting malloc in a certain environment, but these are okay), and inside win32-elf you should have a working ELF compiler. You should also have win32 and win64 for 32-bit and 64-bit PE compilers, and a 64-bit ELF compiler in win64-elf. Note: The 64-bit compilers won't be compiled on a non-64 bit OS.
6. Run the script, make sure everything compiled correctly (note that there may be warnings about assignments from incompatible pointer types and bound checking not supporting malloc in a certain environment, but these are okay), and inside win32-elf you should have a working ELF compiler. You should also have win32 and win64 for 32-bit and 64-bit PE compilers, and a 64-bit ELF compiler in win64-elf. Note: The 64-bit compilers won't be compiled on a non-64 bit OS.
If there are errors compiling TCC, just change the "@echo off" to "@echo on" and run the script again to see where things went wrong.
If there are errors compiling TCC, just change the "@echo off" to "@echo on" and run the script again to see where things went wrong.

===Linux===
Your distribution may already provide a package for tcc. If not, download the sources from https://download.savannah.gnu.org/releases/tinycc/ and go on from there. You know how to build a program from scratch, right?


==A small kernel example==
==A small kernel example==
Line 107: Line 111:


===start32.asm===
===start32.asm===
<source lang="asm">
<syntaxhighlight lang="asm">
; Tutorial: A small kernel with Fasm & TCC
; Tutorial: A small kernel with Fasm & TCC
; By Tommy.
; By Tommy.
Line 145: Line 149:
jmp @b
jmp @b


</syntaxhighlight>
</source>


===kernel.c===
===kernel.c===
<source lang="c">
<syntaxhighlight lang="c">
/* Tutorial: A small kernel with Fasm & TCC
/* Tutorial: A small kernel with Fasm & TCC
* By Tommy.
* By Tommy.
Line 171: Line 175:
}
}


</syntaxhighlight>
</source>


==Compiling and linking==
==Compiling and linking==
Assemble start32.asm with:
Assemble start32.asm with:
<source lang="bash">
<syntaxhighlight lang="bash">
fasm start32.asm
fasm start32.asm
</syntaxhighlight>
</source>


Compile kernel.c with:
Compile kernel.c with:
<source lang="bash">
<syntaxhighlight lang="bash">
tcc -c kernel.c
tcc -c kernel.c
</syntaxhighlight>
</source>


Then link the whole thing with:
Then link the whole thing with:
<source lang="bash">
<syntaxhighlight lang="bash">
tcc -nostdlib -Wl,-Ttext,0x100000 start32.o kernel.o -o kernel-i386.elf
tcc -nostdlib -Wl,-Ttext,0x100000 start32.o kernel.o -o kernel-i386.elf
</syntaxhighlight>
</source>


If you would prefer it in binary form, for example, if you're using your own bootloader that doesn't support ELF, link it with this:
If you would prefer it in binary form, for example, if you're using your own bootloader that doesn't support ELF, link it with this:
<source lang="bash">
<syntaxhighlight lang="bash">
tcc -nostdlib -Wl,-Ttext,0x100000 -Wl,--oformat,binary -static start32.o kernel.o -o kernel-i386.bin
tcc -nostdlib -Wl,-Ttext,0x100000 -Wl,--oformat,binary -static start32.o kernel.o -o kernel-i386.bin
</syntaxhighlight>
</source>


That's all!
That's all!
Line 198: Line 202:
==Inline Assembly==
==Inline Assembly==
TCC supports inline GAS syntax assembly like GCC:
TCC supports inline GAS syntax assembly like GCC:
<source lang="c">
<syntaxhighlight lang="c">
__asm__ __volatile__("hlt");
__asm__ __volatile__("hlt");
</syntaxhighlight>
</source>
You can use this to your benefit for many things, such as debugging in Bochs:
You can use this to your benefit for many things, such as debugging in Bochs:
<source lang="c">
<syntaxhighlight lang="c">
#define breakpoint() __asm__ __volatile__("xchg %bx, %bx");
#define breakpoint() __asm__ __volatile__("xchg %bx, %bx");


Line 212: Line 216:
}
}
}
}
</syntaxhighlight>
</source>
Then adding this to your bochsrc.bxrc file in a text editor:
Then adding this to your bochsrc.bxrc file in a text editor:
<source lang="text">
<syntaxhighlight lang="text">
port_e9_hack: enabled=1
port_e9_hack: enabled=1
magic_break: enabled=1
magic_break: enabled=1
</syntaxhighlight>
</source>
And from boch's install location, executing bochsdbg.exe instead of bochs.exe.
And from boch's install location, executing bochsdbg.exe instead of bochs.exe.


Line 224: Line 228:
So if you use structs to store your GDT entries or GDTR, beware, you will encounter issues loading your GDT if you don't specify the packing of structures correctly.
So if you use structs to store your GDT entries or GDTR, beware, you will encounter issues loading your GDT if you don't specify the packing of structures correctly.
When creating structures, use something like this:
When creating structures, use something like this:
<source lang="c">
<syntaxhighlight lang="c">
// We use the attribute 'packed' to tell TCC not to change any of the alignment in the structure.
// We use the attribute 'packed' to tell TCC not to change any of the alignment in the structure.
struct some_struct {
struct some_struct {
Line 232: Line 236:
// This last attribute can be kept, it won't interfere with the compilation or output, so it may be
// This last attribute can be kept, it won't interfere with the compilation or output, so it may be
// useful to retain compatilbity with GCC, as long as the above attributes don't interfere with GCC.
// useful to retain compatilbity with GCC, as long as the above attributes don't interfere with GCC.
</syntaxhighlight>
</source>
Instead of this:
Instead of this:
<source lang="c">
<syntaxhighlight lang="c">
// We use the attribute 'packed' to tell GCC not to change any of the alignment in the structure.
// We use the attribute 'packed' to tell GCC not to change any of the alignment in the structure.
struct some_struct {
struct some_struct {
Line 240: Line 244:
unsigned char b;
unsigned char b;
} __attribute__((packed));
} __attribute__((packed));
</syntaxhighlight>
</source>




Line 249: Line 253:
TCC doesn't include stdint.h, but all typedefs required are provided in stddef.h.
TCC doesn't include stdint.h, but all typedefs required are provided in stddef.h.
To use stdint.h place the following code in your kernels include path as stdint.h. This will make your code compatible with both gcc and tcc.
To use stdint.h place the following code in your kernels include path as stdint.h. This will make your code compatible with both gcc and tcc.
<source lang="c">
<syntaxhighlight lang="c">
/* stdint.h */
/* stdint.h */


Line 259: Line 263:
#include_next <stdint.h>
#include_next <stdint.h>
#endif
#endif
</syntaxhighlight>
</source>

[[Category:C]]
[[Category:Compilers]]

Latest revision as of 05:40, 9 June 2024

This page is a work in progress.
This page may thus be incomplete. Its content may be changed in the near future.

This article describes how to make a sample ELF kernel with FASM and Tiny C Compiler (aka TCC). It is also possible to use NASM (Bare_Bones_with_NASM). TCC is a small and fast C compiler, which produces x86, x86_64 or ARM code, and generates PE or ELF executables. TCC is heading toward full ISOC99 compliance, and can compile itself, like FASM.

TCC includes also a linker and an assembler (only x86). But this assembler is limited: no 16/64-bit support, instructions up to MMX are supported.

Note: The Windows version of TCC doesn't produce ELF executables, but only object files. You need to recompile TCC without PE support, if you want to use this tutorial on Windows. You can skip this step if you aren't using Windows.

Building TCC with ELF support

Windows

1. Download TCC sources and 32-bit TCC and (if you have a 64-bit OS) 64-bit TCC.

2. Extract the source folder tcc-0.9.26.

3. Save the 32-bit tcc files to a folder called "win32" in the location containing tcc-0.9.26. If you have a 64-bit OS, also create a win64 folder, and save the 64-bit files to a folder called "win64" in the same location.

4. Open Notepad or another text editor, and enter this:

@echo off

set \p VERSION = < .VERSION
echo > config.h #define TCC_VERSION "%VERSION%"

set targetP=I386
set B=32
goto begin

:x86_64
set targetP=X86_64
set B=64
goto begin

:begin
set targetF=PE
set CC=..\win%B%\tcc.exe -O0 -s -fno-strict-aliasing
set P=%B%

:start
if %targetF%==ELF set P=%B%-elf
set target=-DTCC_TARGET_%targetF% -DTCC_TARGET_%targetP%

:tools
%CC% %target% win%P%\tools\tiny_impdef.c -o win%P%\tiny_impdef.exe
%CC% %target% win%P%\tools\tiny_libmaker.c -o win%P%\tiny_libmaker.exe

:libtcc
if not exist win%P%\libtcc\nul mkdir win%P%\libtcc
copy libtcc.h win%P%\libtcc\libtcc.h
%CC% %target% -shared -DLIBTCC_AS_DLL -DONE_SOURCE libtcc.c -o win%P%\libtcc.dll
win%P%\tiny_impdef win%P%\libtcc.dll -o win%P%\libtcc\libtcc.def

:tcc
%CC% %target% tcc.c -o win%P%\tcc.exe -ltcc -Lwin%P%\libtcc

:copy_std_includes
copy include\*.h win%P%\include

:libtcc1.a
win%B%\tcc %target% -c lib\libtcc1.c -o win%P%\libtcc1.o
win%B%\tcc %target% -c win%P%\lib\crt1.c -o win%P%\crt1.o
win%B%\tcc %target% -c win%P%\lib\wincrt1.c -o win%P%\wincrt1.o
win%B%\tcc %target% -c win%P%\lib\dllcrt1.c -o win%P%\dllcrt1.o
win%B%\tcc %target% -c win%P%\lib\dllmain.c -o win%P%\dllmain.o
win%B%\tcc %target% -c win%P%\lib\chkstk.S -o win%P%\chkstk.o
goto lib%B%

:lib32
win%B%\tcc %target% -c lib\alloca86.S -o win%P%\alloca86.o
win%B%\tcc %target% -c lib\alloca86-bt.S -o win%P%\alloca86-bt.o
win%B%\tcc %target% -c lib\bcheck.c -o win%P%\bcheck.o
win%P%\tiny_libmaker win%P%\lib\libtcc1.a win%P%\libtcc1.o win%P%\alloca86.o win%P%\alloca86-bt.o win%P%\crt1.o win%P%\wincrt1.o win%P%\dllcrt1.o win%P%\dllmain.o win%P%\chkstk.o win%P%\bcheck.o
@goto the_end

:lib64
win%P%\tcc %target% -c lib\alloca86_64.S -o win%P%\alloca86_64.o
win%P%\tiny_libmaker win%P%\lib\libtcc1.a win%P%\libtcc1.o win%P%\alloca86_64.o win%P%\crt1.o win%P%\wincrt1.o win%P%\dllcrt1.o win%P%\dllmain.o win%P%\chkstk.o

:the_end
del win%P%\*.o

@if %targetF%==PE (
	@set targetF=ELF
	@goto start
)

@if %B%==64 goto finished
@if _%PROCESSOR_ARCHITEW6432%_==_AMD64_ goto x86_64
@if _%PROCESSOR_ARCHITECTURE%_==_AMD64_ goto x86_64

:finished

5. Save to tcc-0.9.26 with any name you want, but the extension MUST be .bat. If you use notepad, you will have to change the type from "Text Documents" to "All Files".

6. Run the script, make sure everything compiled correctly (note that there may be warnings about assignments from incompatible pointer types and bound checking not supporting malloc in a certain environment, but these are okay), and inside win32-elf you should have a working ELF compiler. You should also have win32 and win64 for 32-bit and 64-bit PE compilers, and a 64-bit ELF compiler in win64-elf. Note: The 64-bit compilers won't be compiled on a non-64 bit OS. If there are errors compiling TCC, just change the "@echo off" to "@echo on" and run the script again to see where things went wrong.

Linux

Your distribution may already provide a package for tcc. If not, download the sources from https://download.savannah.gnu.org/releases/tinycc/ and go on from there. You know how to build a program from scratch, right?

A small kernel example

This little example builds a small kernel in ELF format, which can be booted by Grub.

start32.asm

;  Tutorial: A small kernel with Fasm & TCC
;  By Tommy.
 
        format elf
        use32

;
; Equates
;
MULTIBOOT_PAGE_ALIGN	    equ (1 shl 0)
MULTIBOOT_MEMORY_INFO	    equ (1 shl 1)
MULTIBOOT_AOUT_KLUDGE	    equ (1 shl 16)
MULTIBOOT_HEADER_MAGIC	    equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS	    equ MULTIBOOT_PAGE_ALIGN or MULTIBOOT_MEMORY_INFO
MULTIBOOT_CHECKSUM          equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

        section '.text' executable
;
; Multiboot header
;
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM


;
; Kernel entry point.
;
        public  _start
        extrn   kmain
_start:
        ; Call the main kernel function.
        call    kmain
 
@@:
        jmp     @b

kernel.c

/*  Tutorial: A small kernel with Fasm & TCC
 *  By Tommy.
 */

/*
 * Main kernel function.
 */
void
kmain (void)
{
    *((unsigned char *) 0xB8000) = 'H';
    *((unsigned char *) 0xB8001) = 0x1F;
    *((unsigned char *) 0xB8002) = 'E';
    *((unsigned char *) 0xB8003) = 0x1F;
    *((unsigned char *) 0xB8004) = 'L';
    *((unsigned char *) 0xB8005) = 0x1F;
    *((unsigned char *) 0xB8006) = 'L';
    *((unsigned char *) 0xB8007) = 0x1F;
    *((unsigned char *) 0xB8008) = 'O';
    *((unsigned char *) 0xB8009) = 0x1F;
}

Compiling and linking

Assemble start32.asm with:

fasm start32.asm

Compile kernel.c with:

tcc -c kernel.c

Then link the whole thing with:

tcc -nostdlib -Wl,-Ttext,0x100000 start32.o kernel.o -o kernel-i386.elf

If you would prefer it in binary form, for example, if you're using your own bootloader that doesn't support ELF, link it with this:

tcc -nostdlib -Wl,-Ttext,0x100000 -Wl,--oformat,binary -static start32.o kernel.o -o kernel-i386.bin

That's all!

Inline Assembly

TCC supports inline GAS syntax assembly like GCC:

__asm__ __volatile__("hlt");

You can use this to your benefit for many things, such as debugging in Bochs:

#define breakpoint() __asm__ __volatile__("xchg %bx, %bx");

void bochs_print (char *string) {
	char *c = string;
	while (*c != '\0') {
		outb(0xE9, *c); // may be outportb
		c++;
	}
}

Then adding this to your bochsrc.bxrc file in a text editor:

port_e9_hack: enabled=1
magic_break: enabled=1

And from boch's install location, executing bochsdbg.exe instead of bochs.exe.

GDT and struct warning

According to Fabrice Bellard, the creator of TCC, and (painfully) tested to be true: "In TCC 'packed' is supported only for structure fields or variable declarations, not for a whole structure. So a solution for you is to add it to each field of the packed structure." So if you use structs to store your GDT entries or GDTR, beware, you will encounter issues loading your GDT if you don't specify the packing of structures correctly. When creating structures, use something like this:

// We use the attribute 'packed' to tell TCC not to change any of the alignment in the structure.
struct some_struct {
   unsigned char a __attribute__((packed));
   unsigned char b __attribute__((packed));
} __attribute__((packed));
// This last attribute can be kept, it won't interfere with the compilation or output, so it may be
// useful to retain compatilbity with GCC, as long as the above attributes don't interfere with GCC.

Instead of this:

// We use the attribute 'packed' to tell GCC not to change any of the alignment in the structure.
struct some_struct {
   unsigned char a;
   unsigned char b;
} __attribute__((packed));


Inline Function Warning

TCC doesn't support function inlining, because the 'inline' keyword is ignored, so if a function needs to be inlined, you must use defines instead.

stdint.h

TCC doesn't include stdint.h, but all typedefs required are provided in stddef.h. To use stdint.h place the following code in your kernels include path as stdint.h. This will make your code compatible with both gcc and tcc.

/* stdint.h */

#ifdef __TINYC__
/* tcc */
#include <stddef.h>
#else
/* assume gcc */
#include_next <stdint.h>
#endif