查看“TCC”的源代码
←
TCC
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{In Progress}} 本文描述了如何使用[[FASM]]和[[TCC|Tiny C Compiler]](又名TCC)制作一个示例ELF内核。 也可以使用[[NASM]]([[Bare_Bones_With_NASM]])。 TCC是一个小型且快速的C编译器,它产生x86,x86_64或ARM代码,并生成PE或ELF可执行文件。 TCC正朝着完全符合ISOC99的方向发展,并且可以像FASM一样自我编译。(译者注:自我编译,指用自身的源代码和编译器可以编译出一个新的编译器) TCC还包括一个链接器和一个汇编器(仅x86)。 但是此汇编器是有限的: 不支持16/64位,支持到MMX的指令。 注: Windows版本的TCC不生成ELF可执行文件,而只生成目标文件。 如果要在Windows上使用本教程,则需要在没有PE支持的情况下重新编译TCC。 如果不使用Windows,可以跳过此步骤。 ==构建TCC带ELF支持== ===Windows=== 1. 下载 [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26.tar.bz2 TCC源代码]和[http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26-win32-bin.zip 32位TCC]和(如果你有64位操作系统) [http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26-win64-bin.zip 64位TCC]。 2. 解压源代码文件夹tcc-0.9.26。 3. 将32位TCC文件保存到TCC-0.9.26所在位置的名为“Win32”的文件夹中。 如果你有64位操作系统,请创建一个win64文件夹,并将64位文件保存到同一位置的名为 “win64” 的文件夹中。 4. 打开记事本或其他文本编辑器,然后输入以下内容: <source lang="winbatch"> @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&#10;@if _%PROCESSOR_ARCHITEW6432%_==_AMD64_ goto x86_64 @if _%PROCESSOR_ARCHITECTURE%_==_AMD64_ goto x86_64 :finished </source> 5. 使用你想要的名称保存到TCC-0.9.26,但扩展名必须是.bat。 如果使用记事本,则必须将保存对话框的类型从 “文本文档” 更改为 “所有文件”。 6. 运行脚本,确保所有内容都正确编译(请注意,可能会有来自不兼容指针类型和绑定检查的分配的警告,在特定环境中不支持malloc,但这些都可以接受),并且在win32-elf中,你应该有一个工作的elf编译器。 你还应该拥有用于32位和64位PE编译器的Win32和Win64,以及Win64-ELF中的64位ELF编译器。 注意: 64位编译器不会在非64位操作系统上编译。 如果编译TCC时出现错误,只需将“@echo off”更改为“@echo on”,然后再次运行脚本,看看哪里出了问题。 ===Linux=== 你的发行版可能已经提供了TCC的包。 如果没有,请从https://download.savannah.gnu.org/releases/tinycc/下载源代码,然后从那里继续。 你知道如何从头开始构建程序,对吗? ==一个小内核示例== 这个小例子以ELF格式构建了一个小内核,可以通过Grub引导。 ===start32.asm=== <source lang="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 </source> ===kernel.c=== <source lang="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; } </source> ==编译和链接== 使用以下选项汇编start32.asm: <source lang="bash"> fasm start32.asm </source> 编译kernel.c用: <source lang="bash"> tcc -c kernel.c </source> 然后将整件东西链接起来: <source lang="bash"> tcc -nostdlib -Wl,-Ttext,0x100000 start32.o kernel.o -o kernel-i386.elf </source> 如果你更喜欢二进制形式,例如,如果你正在使用自己的不支持ELF的引导加载程序,请使用以下链接方式: <source lang="bash"> tcc -nostdlib -Wl,-Ttext,0x100000 -Wl,--oformat,binary -static start32.o kernel.o -o kernel-i386.bin </source> 就这样! ==内联汇编== TCC支持内联GAS语法汇编,如GCC: <source lang="c"> __asm__ __volatile__("hlt"); </source> 你可以用这个特性为你处理好很多事情,比如在Bochs调试: <source lang="c"> #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++; } } </source> 然后将其添加到你的bochsrc中。文本编辑器中的bxrc文件: <source lang="text"> port_e9_hack: enabled=1 magic_break: enabled=1 </source> 并从Boch的安装位置执行bochsdbg.exe,而不是bochs.exe。 == 关于GDT和结构体的警告 == 根据TCC的创始人法布里斯·贝拉德(Fabrice Bellard)的说法,并且被(痛苦地)证明是正确的: "在TCC中,只有结构字段或变量声明支持‘packed’属性,整个结构体不支持。 因此,你的解决方案是将其添加到打包结构体的每个字段中。" 因此,如果使用结构体存储GDT条目或GDTR,请注意,如果没有正确指定结构的打包,那么在加载GDT时会遇到问题。 在创建结构体时,请使用类似以下内容: <source lang="c"> // 我们使用属性 'packed' 告诉TCC不要更改结构中的任何对齐方式。 struct some_struct { unsigned char a __attribute__((packed)); unsigned char b __attribute__((packed)); } __attribute__((packed)); // 最后一个属性可以保留,它不会干扰编译或输出,它可能对 // 保持与GCC的兼容性有用,只要上述属性不干扰GCC即可。 </source> 而不是这样: <source lang="c"> //我们使用属性“packed”告诉GCC不要更改结构中的任何对齐方式。 struct some_struct { unsigned char a; unsigned char b; } __attribute__((packed)); </source> ==内联函数警告== TCC不支持函数内联,因为 'inline' 关键字会被忽略,所以如果一个函数需要内联,你必须使用defins预定义代替。 ==stdint.h== TCC不包括stdint.h、 但stddef.h中提供了所需的所有typedefs类型定义 要使用stdint.h,将以下代码作为stdint.h放入你的内核包含路径中。 这将使你的代码与gcc和tcc兼容。 <source lang="c"> /* stdint.h */ #ifdef __TINYC__ /* tcc */ #include <stddef.h> #else /* assume gcc */ #include_next <stdint.h> #endif </source>
本页使用的模板:
模板:In Progress
(
查看源代码
)
返回至“
TCC
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息