TCC: Difference between revisions

From OSDev Wiki
Jump to navigation Jump to search
Line 1: Line 1:
{{In Progress}}
{{In Progress}}


(削除) This article describes how to make a sample ELF kernel with [[FASM]] and [[ (削除ここまで)TCC(削除) | (削除ここまで)Tiny C Compiler(削除) ]] (aka TCC). (削除ここまで)
TCC (追記) ( (追記ここまで)Tiny C Compiler) is a (追記) single-pass, (追記ここまで)small and fast (追記) [[ (追記ここまで)C(追記) ]] (追記ここまで)compiler, which (追記) can produce (追記ここまで)x86, x86_64(追記) , (追記ここまで)ARM (追記) and RISC-V (追記ここまで)code, and (追記) generate [[ (追記ここまで)PE(追記) ]], [[ (追記ここまで)ELF(追記) ]] and [[Mach-O]] (追記ここまで)executables.
(削除) It is also possible to use [[NASM]] ([[Bare_Bones_with_NASM]] (削除ここまで))(削除) . (削除ここまで)
TCC is heading toward full ISOC99 compliance, and (追記) is able to (追記ここまで)compile itself.
(削除) 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
TCC includes also a linker (追記) (supporting a subset of GNU's [[LD|ld]] [[Linker Script|linker script]]) (追記ここまで)and an assembler (追記) with [[GAS]]-like syntax (追記ここまで)(only x86). But this assembler is limited: no
16/64-bit support, instructions up to MMX are supported.
16/64-bit support, instructions up to (追記) [[ (追記ここまで)MMX(追記) ]] (追記ここまで)are supported(追記) , with no [[SSE]] support (追記ここまで).


Note: The Windows version of TCC doesn't produce ELF executables, but only object files. You
(追記) This article describes how to make a simple ELF kernel with [[FASM]] and TCC. (追記ここまで)
need to recompile TCC without PE support, if you want to use this tutorial on Windows. You can
(追記) It is also possible to use [[NASM]] ([[Bare Bones with NASM]]). (追記ここまで)
Note: The Windows version of TCC doesn't produce (追記) [[ (追記ここまで)ELF(追記) ]] (追記ここまで)executables, but only (追記) [[Object Files| (追記ここまで)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.
skip this step if you aren't using Windows.
(追記) (追記ここまで)


==Building TCC with ELF support==
==Building TCC with ELF support==
Line 19: Line 21:
2. Extract the source folder tcc-0.9.26.
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.
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:
4. Open Notepad or another text editor, and enter this:
Line 104: Line 106:


===Linux===
===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?
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==


This little example builds a small kernel in ELF format, which can be booted by (削除) Grub (削除ここまで).
This little example builds a small kernel in ELF format, which can be booted by (追記) [[GRUB]] (追記ここまで).


===start32.asm===
===start32.asm===
Line 201: Line 203:


==Inline Assembly==
==Inline Assembly==
TCC supports inline GAS syntax assembly like GCC:
TCC supports inline (追記) [[ (追記ここまで)GAS(追記) ]] (追記ここまで)syntax assembly like (追記) [[ (追記ここまで)GCC(追記) ]] (追記ここまで):
<source lang="c">
<source lang="c">
__asm__ __volatile__("hlt");
__asm__ __volatile__("hlt");
</source>
</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">
<source lang="c">
#define breakpoint() __asm__ __volatile__("xchg %bx, %bx");
#define breakpoint() __asm__ __volatile__("xchg %bx, %bx");
Line 222: Line 224:
magic_break: enabled=1
magic_break: enabled=1
</source>
</source>
And from (削除) boch's (削除ここまで)install location, executing bochsdbg.exe instead of bochs.exe.
And from (追記) [[Bochs]] (追記ここまで)install location, executing bochsdbg.exe instead of bochs.exe.


==GDT and struct warning==
==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."
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.
So if you use structs to store your (追記) [[ (追記ここまで)GDT(追記) ]] (追記ここまで)entries or (追記) [[GDT#GDTR| (追記ここまで)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">
<source lang="c">
Line 235: Line 237:
} __attribute__((packed));
} __attribute__((packed));
// 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 (追記) compatibility (追記ここまで)with GCC, as long as the above attributes don't interfere with GCC.
</source>
</source>
Instead of this:
Instead of this:
Line 245: Line 247:
} __attribute__((packed));
} __attribute__((packed));
</source>
</source>
(削除) (削除ここまで)


==Inline Function Warning==
==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.
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==
==(追記) Note on (追記ここまで)stdint.h==
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">
<source lang="c">
/* stdint.h */
/* stdint.h */
Line 264: Line 265:
#endif
#endif
</source>
</source>
(追記) (追記ここまで)
(追記) ==See Also== (追記ここまで)
(追記) *[[Smaller C]] - Another simple single-pass compiler. Unlike TCC it can output 16-bit code (that also works on [[Unreal Mode]]). (追記ここまで)
(追記) (追記ここまで)
(追記) ===External Links=== (追記ここまで)
(追記) *[https://bellard.org/tcc/ TCC project homepage] (追記ここまで)
(追記) *[https://repo.or.cz/w/tinycc.git TCC source code repo] (追記ここまで)
(追記) *[https://github.com/TinyCC/tinycc Github mirror of the above repo] (追記ここまで)


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

Latest revision as of 16:42, 16 September 2025

This page is under construction! This page or section is a work in progress and may thus be incomplete. Its content may be changed in the near future.

TCC (Tiny C Compiler) is a single-pass, small and fast C compiler, which can produce x86, x86_64, ARM and RISC-V code, and generate PE, ELF and Mach-O executables. TCC is heading toward full ISOC99 compliance, and is able to compile itself.

TCC includes also a linker (supporting a subset of GNU's ld linker script) and an assembler with GAS-like syntax (only x86). But this assembler is limited: no 16/64-bit support, instructions up to MMX are supported, with no SSE support.

This article describes how to make a simple ELF kernel with FASM and TCC. It is also possible to use NASM (Bare Bones with NASM).

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.

formatelf
use32
;
; Equates
;
MULTIBOOT_PAGE_ALIGNequ(1shl0)
MULTIBOOT_MEMORY_INFOequ(1shl1)
MULTIBOOT_AOUT_KLUDGEequ(1shl16)
MULTIBOOT_HEADER_MAGICequ0x1BADB002
MULTIBOOT_HEADER_FLAGSequMULTIBOOT_PAGE_ALIGNorMULTIBOOT_MEMORY_INFO
MULTIBOOT_CHECKSUMequ-(MULTIBOOT_HEADER_MAGIC+MULTIBOOT_HEADER_FLAGS)
section'.text'executable
;
; Multiboot header
;
ddMULTIBOOT_HEADER_MAGIC
ddMULTIBOOT_HEADER_FLAGS
ddMULTIBOOT_CHECKSUM
;
; Kernel entry point.
;
public_start
extrnkmain
_start:
; Call the main kernel function.
callkmain

@@:
jmp@b

kernel.c

/* Tutorial: A small kernel with Fasm & TCC
 * By Tommy.
 */
/*
 * Main kernel function.
 */
void
kmain(void)
{
*((unsignedchar*)0xB8000)='H';
*((unsignedchar*)0xB8001)=0x1F;
*((unsignedchar*)0xB8002)='E';
*((unsignedchar*)0xB8003)=0x1F;
*((unsignedchar*)0xB8004)='L';
*((unsignedchar*)0xB8005)=0x1F;
*((unsignedchar*)0xB8006)='L';
*((unsignedchar*)0xB8007)=0x1F;
*((unsignedchar*)0xB8008)='O';
*((unsignedchar*)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");
voidbochs_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 Bochs 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.
structsome_struct{
unsignedchara__attribute__((packed));
unsignedcharb__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 compatibility 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.
structsome_struct{
unsignedchara;
unsignedcharb;
}__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.

Note on 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

See Also

  • Smaller C - Another simple single-pass compiler. Unlike TCC it can output 16-bit code (that also works on Unreal Mode).

External Links

Retrieved from "https://wiki.osdev.org/index.php?title=TCC&oldid=29656"