Carbon Language - C++20/C++23 - Sanitizer

书接上一篇 ## 🟡 Carbon Language,在 Windows 系下运行失败。目前只能通过 Windows WSL 系统体验 Carbon。
Carbon 语言当前的实现称为 Explorer,其主要目的是作为语言的明确规范,官方提供在线体验网站,它是基于 LLVM 现代编译器框架开发的一个前端编译器。
作为该目标的一个扩展,Explorer 还可以用作原型设计和验证语言更改的平台。因此,它优先于展示直接可读的代码,其次是性能、诊断质量和其他常规实现。换言之,它的目标受众是从事 Carbon 设计的人,而不是任何Carbon 编程终端用户。
- [Building on Windows](https://github.com/carbon-language/carbon-lang/issues/298)
- [Carbon on Windows WSL](https://cristianopizzamiglio.com/2022/08/14/carbon-wsl.html)
- [C++ Standard Libraries](https://docs.brew.sh/C++-Standard-Libraries)
- [So I tried Carbon... Aryan Garg](https://aryan401.hashnode.dev/carbon)
注意安装基础编译工具套件或 clang 编译器,Brew 需要用它来编译 glibc 等基础库:
GCC 10.9 开始基础运行库命名为 libc++,早期的版本则默认为 libstdc,可以按需要安装指定版本。
brew install gcc@7
brew install --cc=gcc-7 <formula>
最后编译时通过了,也生成了 explorer,但是执行时,未能通过 Sanizer 内存安全检查:
# https://github.com/carbon-language/carbon-lang/issues/2236
总之,安装过程非常不顺利,还是官方说的那样,如果可以使用 Rust,那就不必考虑 Carbon!

这个项目由谷歌内部 C++ 和开发工具组开启,因为 C++ 标准委员会无法在 C++ ABI 上达成共识。目标是不用重写谷歌内部巨大的 C++ 代码,同时逐步提高安全和执行效率。资源上,最多是主管级的支持。大公司里一个小组放出一个天马行空的开源项目很常见,Google 开源玩具 demo 也是老传统。
演化论有个“生态位”的概念,即在一个稳定的环境下,一个生态位一旦被先来者抢占,那后来者是没有机会的,这个先来者就会一直占着这个生态位,直到环境发生变化。而软件工业中的 C++ 就是这样的一个地位。
自操作系统诞生以来,编写内存安全的代码一直是一个比较困难的问题,另一个问题则是保证线程安全。2004 年以来,微软安全响应中心(MSRC)已对所有报告过的微软安全漏洞进行了分类,据统计数据,所有微软年度补丁中约有 70% 是针对内存安全漏洞的修复程序。
- https://github.com/google/sanitizers
- https://gcc.gnu.org/onlinedocs/gcc-11.3.0/gcc/Option-Index.html
- https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind
- [Definitions and One Definition Rule (ODR)](https://en.cppreference.com/w/cpp/language/definition)
- [Constraints and concepts (since C++20)](https://en.cppreference.com/w/cpp/language/constraints)
- [C++ compiler support](https://en.cppreference.com/w/cpp/compiler_support)
Sanitizers 是谷歌发起的开源内存安全检查工具,项目本是 LLVM 项目的一部分,GNU GCC 编译器也集成了。GCC 4.8 版本开始支持地址、线程 Sanitizer,GCC 4.9 版本开始支持 Leak Sanitizer 和 UB Sanitizer,这些都是查找隐藏 Bug 的利器。通过编译器参数启用,如 -fsanitize=address 使用地址检查器。
- AddressSanitizer 检查地址相关问题,包括释放后使用、重复释放、堆溢出、栈溢出等等问题。
- LeakSanitizer 检查内存泄漏问题。
- ThreadSanitizer 检查线程数据竞争和死锁问题。
- MemorySanitizer 检查使用未初始化内存问题。
- Undefied Behavior Sanitizer 未定义行为问题。
Address Sanitize 支持功能:
01. Use after free (dangling pointer dereference) 使用悬浮指针
02. Heap buffer overflow 堆缓冲区溢出
03. Stack buffer overflow 栈缓冲区溢出
04. Global buffer overflow 全局缓冲区溢出
05. Use after return 通过返回值访问局部变量的内存
06. Use after scope 访问已经释放的局部变量的内存
07. Initialization order bugs 使用未初始化的内存
08. Memory leaks 内存泄漏
C++20 是有史以来最大的 C++ 版本更新,但是不知什么原因它又没有完全完工,是疫情版完成了后续的工作,C++23 “Pandemic Edition” is complete。要使用最新的功能,需要 GCC 或 CLang 13 这样的版本。
C++20 引入了一个新概念,真的是概念,它的名字就叫 `concept`,其实是一个语法糖,它的本质可以认为是一个模板类型的 bool 变量。
template < template-parameter-list >
concept concept-name = constraint-expression;
其中,constraint-expression 是一个可以被 eval 为 bool 的表达式或者编译期函数。使用定义好的 concept 时,constraint-expression 会根据上面 template-parameter-list 传入的类型,执行编译期计算,判断使用该concept的模板定义是否满足。 如果不满足,则编译期会给定一个具有明确语义的错误,即这个 concept 没有匹配成功。 注意到,上述匹配的行为都是在编译期完成的,因此 concept 是 zero-cost 表达式。
Multiple declarations, Only one definition。
对于编译器来说,任何一个翻译单元中,只允许对任何变量、函数、类类型、枚举类型、`concept` 或模板进行一个定义。其中一些可能有多个声明,但是定义只允许一个,这就是 ODR 单一定义规则。
在整个程序中,包括标准库和用户定义库,ODR 规则使用的 non-inline 函数或变量只需要出现一个定义。编译器不需要诊断这种违反,但违反它的程序的行为是未定义的,即 ODR Violation。
遵循 ODR 是构建良好 C++ 程序的基础。编写 C++ 程序时,代码写在文件中,然而对于 ODR 原则来说,文件之间的边界并不很重要,真正重要的是编译单元,Translation Units。本质上来说,一个编译单元是对程序员提交给编译器的文件执行预处理后的结果,即一个源文件经过预处理后,产生一个目标文件。参考 C++ template:the complete guide。
预处理器按照条件编译命令,#if,#ifdef 等等,去除掉未被选中的代码块,去除注释,递归地插入 "#include" 指令所引用的文件,并将宏展开。
Valgrind 可以检测非常多的内存错误,但相对于 Sanitizer,对程序性能影响更大。
以下测试程序一般编译通过并正常运行,但是使用内存检查工具后,就可以发现潜在的越界问题: