为什么我用gcc编译了脚本?
题主说的gcc可以编译内核也可以编译应用,是因为GCC有-ffreestanding选项。开启后GCC就知道正在编译内核,因此禁用C标准库里的大部分库,并针对裸机环境进行调整。而不使用此选项,gcc就会针对运行在操作系统上的应用编译。
但是使用这种gcc编译内核,仅有十分局限的用途,即重新从源代码编译当前正在运行的内核,即最近版本的Linux(假如你使用的是Linux)。
如果你想编写自己的操作系统,那不能使用系统自带的GCC。你需要编译交叉编译器。
关于为什么Linux发行版的GCC不能用于开发操作系统或者编译其他操作系统内核,可以看
Why do I need a Cross Compiler?GCC工具链支持交叉编译,即GCC支持在M平台上运行,并生成N平台上的二进制文件。
工具链的目标格式为三元组 CPU架构-制造商-操作系统 有的时候操作系统后会跟上c语言库的名字或者abi。
常见的组合有:CPU架构(i686,x86_64,arm,rv64gc),操作系统(Linux,FreeBSD,mingW64),c语言库(glibc,musl,newlib)abi(sysv,arm v7的eabi,eabihf)。那些没有特定制造商的工具链有的使用unknown作为制造商标识,有的则省略。没有操作系统的工具链常使用none作为标识。
每一份GCC,都指定了CPU架构,系统的头文件(也有可能只支持裸机环境),binutils,c语言库,改变这些只能从头编译。
gcc -dumpmachine
这个命令可以显示一个编译器的目标平台。
比如,Linux发行版提供的GCC一般是x86_64-Linux-GNU,表示编译出的文件运行在x86_64平台上的Linux内核,并且使用glibc和sysv abi。
如果编译器的运行平台和目标平台一致,则称为本机(native)编译器,这种编译器生成的二进制文件一般可以直接运行。其他的称为交叉编译器,生成的文件不能直接运行。
实际上也有x86_64的Linux上运行的x86_64-Linux-GNU交叉编译器,唯一的不同是编译目标可能使用不同版本的库,其作用是自举(bootstrap)。
所谓bootstrap,就是从源码重新编译一套能运行的Linux内核,gcc本机编译器,glibc。一般这项工作由发行版维护,如果想自己尝试一下,可以阅读著名的linux from scratch
LFS Project Homepage觉得LFS软件少的可以用Gentoo Linux
Gentoo AMD64 HandbookGentoo Linux在安装时需要编译Linux内核,同时可以自己选择libc,以及编译选项。
再拿rust语言的情况来对比一下。
rust语言的编译器,rustc,其二进制文件发行支持交叉编译。
也就是rustc这个几十兆的可执行程序可以生成支持的所有CPU架构的目标文件,这得益于rustc的后端llvm。llvm在这一点上比gcc强大了一个次元。
如果使用包管理器cargo,只要项目根文件lib.rs或main.rs开头包含#![no_std],即代表不使用标准库,编译到裸机代码。这和GCC的ffreestanding选项是一个意思。
使用cargo build --target 目标 即可交叉编译。不需要重新编译rustc。根据平台的区别,有时需要外部连接器。
由于不需要重新编译编译器,因此使用rust做操作系统开发总体来说比c语言+gcc方便不少。