This is a stripped-down version of Fabrice Bellard's Tiny CC compiler.
This version generates 64-bit PE files only, as used on 64-bit versions of Windows.
It will compile and link BootExecute (native) programs, console and GUI apps, SYS files (drivers), and DLLs (shared libraries).
You can build it on Linux (with Clang or GCC), build it on Windows (with itself, CL or Clang), or simply install the pre-built package on Windows. When built on Linux, you end up with both a Linux-hosted cross-compiler and the Windows package as a ZIP file.
To install on Windows, simply extract the petcc64-winbin.zip
file (see the Releases page) into a directory that's on your path.
Tiny CC automatically finds its default libraries and include files
relative to the location of PETCC64.EXE
.
In fact, the only file you absolutely require is PETCC64.EXE
, if
you can do without the standard include files and libraries (see
below).
To compile on Linux, get into the source code directory and run:
$ sh bootstrap-exes.sh
To choose a specific C compiler, try something like:
$ CC=clang sh bootstrap-exes.sh
$ CC=gcc sh bootstrap-exes.sh
To compile on Windows, open a command prompt with environment variables configured for your compiler of choice (we've tried CL from the latest Visual Studio and Clang for Windows) and use one of:
> build-exes-with-cl.bat
> clang-build-exes.bat
Note that by default, PETCC64
ignores both its standard libraries
and its standard include files when compiling and linking.
So, compiling this mini.c
file:
#include <stdio.h>
int main(void) {
printf("Hello, Tiny World.\n");
return 0;
}
...produces:
> petcc64 mini.c
-> mini.c
mini.c:1: error: include file 'stdio.h' not found
You can tell the compiler to use its own collection of standard includes (fairly broad, and often sufficient) like this:
> petcc64 -stdinc mini.c
Of course, without the standard libraries and startup code, you'll now get something like this:
-> mini.c
-> c:/users/duck/petccinc/stdio.h
-> c:/users/duck/petccinc/_mingw.h
-> c:/users/duck/petccinc/stddef.h
-> c:/users/duck/petccinc/stdarg.h
-> c:/users/duck/petccinc/vadefs.h
-> c:/users/duck/petccinc/sec_api/stdio_s.h
-> c:/users/duck/petccinc/stdio.h
tcc: error: undefined symbol 'printf'
tcc: error: undefined symbol '_start'
...because the linker can't find the low-level entry point function
_start
.
You need to be explicit about what other compilers do by default:
> petcc64 -stdinc -stdlib mini.c
-> mini.c
-> c:/users/duck/petccinc/stdio.h
-> c:/users/duck/petccinc/_mingw.h
-> c:/users/duck/petccinc/stddef.h
-> c:/users/duck/petccinc/stdarg.h
-> c:/users/duck/petccinc/vadefs.h
-> c:/users/duck/petccinc/sec_api/stdio_s.h
-> c:/users/duck/petccinc/stdio.h
-> c:/users/duck/petcclib/libpetcc1_64.a
-> C:/Windows/system32/msvcrt.dll
-> C:/Windows/system32/kernel32.dll
-------------------------------
virt file size section
1000 200 118 .text
2000 400 144 .data
3000 600 18 .pdata
-------------------------------
<- mini.exe (2048 bytes)
PETCC64
is fairly verbose by default, in order to show you exactly
which header files, libraries and so on are used. Note above that
the linker automatically looks in msvcrt.dll
and kernel32.dll
for
common system functions (no need for .DEF
or .LIB
files!), adding
user32.dll
and gdi32.dll
if you use the option -pegui
to create
a Windows GUI app instead of a default console executable.
The reason for having -nostdinc
and -nostdlib
as defaults is for
minimalism. You can simply provide your own _start()
function and
treat it as a standard Windows entry point.
Save this as plain.c
:
int MessageBoxA(void* hwnd, char* msg, char* hdr, unsigned dat);
void ExitProcess(unsigned code);
void _start(void) {
MessageBoxA(0,"Hello","Tiny World",0);
ExitProcess(0);
}
...and you can build a 2048-byte demo program like this:
> petcc64 -pegui plain.c -lkernel32 -luser32
-> plain.c
-> c:/users/duck/petcclib/libpetcc1_64.a
-> C:/Windows/system32/kernel32.dll
-> C:/Windows/system32/user32.dll
-------------------------------
virt file size section
1000 200 68 .text
2000 400 d0 .data
3000 600 c .pdata
-------------------------------
<- plain.exe (2048 bytes)
Don't forget to use -pegui
when creating a non-console app, or the
compiled EXE will run command-line style, as a sub-process of the
program you started it from, rather than as a standalone process in
its own right.
You can strip the EXE down even further by using -unwind
to omit
the .pdata
section, which contains the "function unwind" data
needed for debugging and exception handling. Note, however, that
you must have unwind data in 64-bit Windows programs that use C's
longjmp()
function or else your code will exit when you longjmp
.
To show only the filenames compiled (no headers and build summary),
us the -v
(regular verbosity) option; for a quiet build, use -v0
.
This minimalist approach means that the only file you actually need
in order to build useful, working, C programs is the PETCC64.EXE
binary itself.
Explore, experiment, and enjoy!