PCSX2 内置的 Zycore-C:一套支撑 Zydis 反汇编引擎的跨平台 C 基础库深度解析
【免费下载链接】pcsx2PCSX2 - The Playstation 2 Emulator项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2
导读
Zycore(Zyan Core Library for C)是一套以平台无关为核心目标的 C 语言基础库:它为上层代码统一提供整型/布尔类型、编译器与平台与架构检测宏、断言与工具宏、位集(Bitset)、字符串(String)以及 Vector/List 等容器类型,并提供了面向无 LibC 环境的函数回退实现。在 PCSX2 仓库中,Zycore 以 Zydis 反汇编器内嵌依赖的形式存在(源码位于 3rdparty/zydis/dependencies/zycore,并被 3rdparty/zydis/CMakeLists.txt 直接编译进 zydis 库),是调试器与动态重编译器(如 pcsx2/x86/ix86-32/iR5900.cpp)正确解析 x86/x86-64 指令的前提。读完本文,你将掌握 Zycore 的类型体系、宏机制、容器设计与无 LibC 回退策略,并能看懂 PCSX2 中 Zydis/Zycore 相关代码的底层原理。
一、Zycore 在 PCSX2 中的定位
Zycore 在官方 README 中的自我定位是:
Internal library providing platform independent types, macros and a fallback for environments without LibC.
(面向内部使用的库,提供平台无关的类型、宏,以及针对无 LibC 环境的回退方案。)
它不是独立对外发布的工具库,而是为 Zydis(x86/x86-64 反汇编器)这类底层解析引擎提供"地基"的支撑库。在 PCSX2 中,它的落点非常具体:
- 3rdparty/zydis/CMakeLists.txt 把
dependencies/zycore/src/下的Allocator.c、ArgParse.c、Bitset.c、Format.c、List.c、String.c、Vector.c、Zycore.c以及API/下的Memory.c、Process.c、Synchronization.c、Terminal.c、Thread.c全部直接编译进zydis静态库,并把dependencies/zycore/include/Zycore/*.h作为公开头文件暴露; - PCSX2 的 x86 重编译器/调试器代码通过
#include "Zydis/Zydis.h"间接引入 Zycore 的头文件(见 pcsx2/x86/ix86-32/iR5900.cpp)。例如该文件中自定义的反汇编 Formatter 回调直接使用ZYAN_CHECK(...)、ZyanStatus等 Zycore 提供的宏与类型来拼接符号与绝对地址输出。
也就是说,Zycore 表面上"隐身",但 PCSX2 的指令反汇编、绝对地址计算、符号格式化等调试能力,都依赖这套基础类型与宏体系才能跨 MSVC/GCC/Clang 正确编译运行。
二、平台无关类型体系(Types.h)
Zycore 的全部类型定义集中在 include/Zycore/Types.h 中,其设计原则是:无论底层是标准 LibC、还是完全没有 LibC 的裸机环境,上层代码看到的类型签名永远一致。
2.1 整型家族
| 类型 | 含义 | 备注 |
|---|---|---|
ZyanU8/ZyanU16/ZyanU32/ZyanU64 | 无符号整型 | 宽度固定,与uint8_t等对齐 |
ZyanI8/ZyanI16/ZyanI32/ZyanI64 | 有符号整型 | 宽度固定 |
ZyanUSize/ZyanISize | 与指针等宽的尺寸/差值类型 | 等价于size_t/ptrdiff_t |
ZyanUPointer/ZyanIPointer | 指针相关整型 | 等价于uintptr_t/intptr_t |
类型映射分两条路径实现(Types.h):
- 有 LibC 时:直接
#include <stdint.h>/<stddef.h>,将uint8_t、size_t、uintptr_t等一一 typedef 为Zyan*类型,保证与标准库完全一致; - 无 LibC 时(
ZYAN_NO_LIBC):MSVC/ICC 路径使用unsigned __int8等编译器内建类型,GNU 路径优先使用__UINT8_TYPE__、__UINT64_TYPE__、__SIZE_TYPE__等编译器预定义宏,仅在缺少这些宏时才回退到unsigned char等基础 C 类型。对于ZyanU64,在__x86_64__且非__ILP32__时使用unsigned long int,否则使用unsigned long long int。
这套路径还顺带定义了与stdint.h平行的极值宏(ZYAN_INT8_MIN、ZYAN_UINT32_MAX、ZYAN_UINT64_MAX等),确保无 LibC 环境下也能写出可移植的边界判断。
有趣的是,Types.h 用ZYAN_STATIC_ASSERT在编译期硬性校验所有类型的大小与符号性假设,例如sizeof(ZyanU64) == 8、sizeof(ZyanUPointer) == sizeof(void*),任何平台上类型宽度不符都会直接编译失败,从根源上杜绝"移植了但类型错了"的隐患。
2.2 逻辑类型与指针
ZyanBool:底层就是ZyanU8,0视为假、非 0 视为真,配合ZYAN_FALSE (0u)与ZYAN_TRUE (1u)使用;ZyanTernary:ZyanI8实现的三态类型,取值为ZYAN_TERNARY_FALSE (-1)、ZYAN_TERNARY_UNKNOWN (0)、ZYAN_TERNARY_TRUE (1),用于表达"未知"这种既非真也非假的中间状态;ZyanVoidPointer/ZyanConstVoidPointer:void*/const void*的别名;ZYAN_NULL:定义为((void*)0);ZyanCharPointer/ZyanConstCharPointer:C 风格(NUL 结尾)字符串指针。
三、宏体系:检测、断言与工具(Defines.h)
include/Zycore/Defines.h 是 Zycore 的"元编程中枢",它让同一份 C 代码可以在 MSVC、GCC、Clang、Intel ICC 下获得一致的宏接口。
3.1 编译器 / 平台 / 架构三级检测
三级检测逐层递进,全部基于预定义宏,优先级与语义如下(Defines.h):
- 编译器:
ZYAN_CLANG、ZYAN_GCC、ZYAN_ICC、ZYAN_MSVC、ZYAN_BORLAND,其中 Clang 同时定义ZYAN_GNUC以获得 GCC 兼容行为,且 Clang-cl 还会额外定义ZYAN_CLANG_CL与ZYAN_MSVC; - 平台:
ZYAN_WINDOWS、ZYAN_EMSCRIPTEN、ZYAN_WASI、ZYAN_APPLE、ZYAN_LINUX、ZYAN_FREEBSD、ZYAN_NETBSD、ZYAN_SOLARIS、ZYAN_HAIKU、ZYAN_UNIX,其中所有类 Unix 平台会统一追加ZYAN_POSIX; - 架构:
ZYAN_X64、ZYAN_X86、ZYAN_AARCH64、ZYAN_ARM、ZYAN_WASM、ZYAN_LOONGARCH、ZYAN_PPC64、ZYAN_PPC、ZYAN_RISCV64,无法识别时直接#error "Unsupported architecture detected"强制终止编译; - 内核/用户态:检测到
_KERNEL_MODE、KERNEL、__KERNEL__等时定义ZYAN_KERNEL,否则定义ZYAN_USER; - 调试/发布:MSVC/Borland 依据
_DEBUG,GCC/ICC/Clang 依据NDEBUG,分别定义ZYAN_DEBUG或ZYAN_RELEASE。
3.2 断言与调试宏
ZYAN_ASSERT(condition):运行时断言。无 LibC 时退化为(void)(condition)空操作;Windows 内核模式映射到NT_ASSERT;其余情况映射到标准assert;ZYAN_STATIC_ASSERT(x):编译期断言,按标准依次尝试 C11_Static_assert、C++11static_assert,最终退化为"用typedef声明一个长度依赖条件真假(1 或 -1)的数组"这一经典技巧,失败即编译错误(Defines.h);ZYAN_UNREACHABLE:按平台/编译器映射到__builtin_unreachable()、__assume(0)、__fastfail(0)或{ assert(0); abort(); },向编译器声明"代码路径不可达"。
3.3 常用工具宏
| 宏 | 作用 |
|---|---|
ZYAN_MACRO_CONCAT(x, y)/ZYAN_MACRO_CONCAT_EXPAND(x, y) | 用##拼接标识符;EXPAND 版先展开参数再拼接 |
ZYAN_UNUSED(x) | 消除"未使用参数"告警 |
ZYAN_FALLTHROUGH | 标注 switch 中有意穿透的分支 |
ZYAN_BITFIELD(x) | 声明位域的: x语法包装 |
ZYAN_ARRAY_LENGTH(a) | sizeof(a)/sizeof((a)[0])求数组元素个数 |
ZYAN_MIN/ZYAN_MAX/ZYAN_ABS | 极值与绝对值 |
ZYAN_IS_POWER_OF_2(x) | 判断 2 的幂(注意x == 0恒真) |
ZYAN_IS_ALIGNED_TO(x, align)/ZYAN_ALIGN_UP/ZYAN_ALIGN_DOWN | 对齐判断与向上/向下取整(仅支持 2 的幂对齐) |
ZYAN_NEEDS_BIT(n, b)/ZYAN_BITS_TO_REPRESENT(n) | 判断"表示 n 是否需要第 b 位"、计算表示 n 所需的最少位数 |
ZYAN_DIV64(n, divisor) | Linux 内核态映射到内核的do_div,否则普通除法 |
此外还有一批编译器属性宏:ZYAN_DEPRECATED、ZYAN_DLLEXPORT/ZYAN_DLLIMPORT、ZYAN_INLINE、ZYAN_NOINLINE、ZYAN_NO_SANITIZE(what)、ZYAN_PRINTF_ATTR/ZYAN_WPRINTF_ATTR(printf 风格格式化函数检查)等。
3.4 符号导出与版本控制
ZYCORE_EXPORT/ZYCORE_NO_EXPORT:静态构建(ZYCORE_STATIC_BUILD)时为空宏;共享库构建时依据ZYCORE_SHOULD_EXPORT映射到__declspec(dllexport/dllimport)或 GCC 的__attribute__((visibility(...)))。Defines.h 中还保留了旧宏名ZYCORE_STATIC_DEFINE、Zycore_EXPORTS的兼容转换并打印改名警告(Defines.h);ZYCORE_VERSION是 64 位版本号常量,配合 include/Zycore/Zycore.h 中的ZYCORE_VERSION_MAJOR/MINOR/PATCH/BUILD四个宏可按位拆分出主/次/补丁/构建号;ZycoreGetVersion()在运行时返回该值。
四、LibC 抽象与无 LibC 回退(LibC.h)
include/Zycore/LibC.h 是 README 中标注为 "WiP(进行中)" 的部分,但它的设计已经非常清晰:
- 未定义
ZYAN_NO_LIBC时,直接把标准库函数/类型逐一定义为ZYAN_*别名:ZYAN_MALLOC→malloc、ZYAN_FREE→free、ZYAN_MEMCPY→memcpy、ZYAN_STRLEN→strlen、ZYAN_VSNPRINTF→vsnprintf,以及ZyanVAList→va_list、ZyanFile→FILE、ZYAN_STDIN/STDOUT/STDERR等; - 定义
ZYAN_NO_LIBC时,则提供一组内联实现作为"最后防线"。作者在注释中明确承认这些实现"完全不追求性能优化,任何标准库实现都能轻松胜过它们",其目的是"为没有可用 libc 的环境提供兜底"。这部分目前覆盖了全部string.h类函数(ZYAN_MEMCHR、ZYAN_MEMCMP、ZYAN_MEMCPY、ZYAN_MEMMOVE(正确处理源/目标重叠)、ZYAN_MEMSET、ZYAN_STRCAT、ZYAN_STRCHR、ZYAN_STRCMP、ZYAN_STRCPY、ZYAN_STRLEN、ZYAN_STRTOK、ZYAN_STRSTR等),而malloc/free与vsnprintf的兜底实现目前仍是注释占位(无法在无内存管理器的环境里凭空实现); - 开发者也可以定义
ZYAN_CUSTOM_LIBC,自行提供整套替换函数,绕过内置路径。
五、容器类型:Vector、List、Bitset 与 String
Zycore 把最常见的容器以"结构体 + 操作函数"的 C 风格实现出来,这也是 README 中列出的重点能力。
5.1 ZyanVector:动态数组
include/Zycore/Vector.h 定义的ZyanVector结构包含:内存分配器指针、增长因子(growth_factor)、收缩阈值(shrink_threshold)、当前元素数(size)、容量(capacity)、单元素字节数(element_size)、元素析构回调(destructor)与数据指针。它提供三类关键行为:
- 容量策略常量:最小容量为
ZYAN_VECTOR_MIN_CAPACITY (1),默认增长因子为 2(成倍扩容),默认收缩阈值为 4(元素数降到容量的 1/4 时收缩),见 Vector.h; - 构造方式:支持栈上静态初始化与基于分配器的动态初始化(
ZyanVectorInit/ZyanVectorInitCustomBuffer等),以及ZyanVectorDestroy释放; - 操作接口:
ZyanVectorPushBack、ZyanVectorPopBack、ZyanVectorGet、ZyanVectorSet、ZyanVectorInsert、ZyanVectorDelete、ZyanVectorResize、ZyanVectorClear、ZyanVectorReserve等,构成完整的动态数组语义。
5.2 ZyanList:链表
include/Zycore/List.h 提供双向链表实现,支持头插/尾插/中间插入、按节点遍历(ZyanListNode)、节点析构回调以及整表销毁,适用于需要频繁插入删除、不要求随机访问的场景。
5.3 ZyanBitset:位集
include/Zycore/Bitset.h 实现的ZyanBitset结构非常简洁——一个size字段记录位集大小,内部数据则直接内嵌一个ZyanVector(ZyanVector bits),即"用动态数组承载位数据"。它提供的核心能力包括:
- 按位读写:
ZyanBitsetSet、ZyanBitsetGet、ZyanBitsetFlip; - 批量清零/置位:
ZyanBitsetClear、ZyanBitsetSetAll; - 集合运算:
ZyanBitsetAND、ZyanBitsetOR、ZyanBitsetXOR,这些运算通过函数指针类型ZyanBitsetByteOperation(对两个字节执行操作的回调)逐字节作用于两个位集,见 Bitset.h; - 枚举已置位索引:
ZyanBitsetEnumeratorInit+ZyanBitsetEnumeratorNext,支持高效遍历所有为 1 的位。
5.4 ZyanString / ZyanStringView:字符串
include/Zycore/String.h 提供两类字符串抽象:
ZyanString:拥有缓冲区的动态字符串(内嵌ZyanVector管理字符数据),支持ZyanStringInit、ZyanStringAppend、ZyanStringInsert、ZyanStringReplace、ZyanStringToLowerCase/UpperCase以及格式化为子串视图等操作;ZyanStringView:只读视图,仅指向已有字符串的一段区间,不拥有数据,适合作为函数参数避免拷贝。
六、配套设施:分配器、状态码与原子操作
除 README 列出的能力外,Zycore 还提供了一批支撑性模块,共同构成完整的基础设施:
- 分配器抽象(include/Zycore/Allocator.h):
ZyanAllocator将malloc/calloc/realloc/free包装成可替换的分配器接口,容器可通过它实现自定义内存管理; - 状态码体系(include/Zycore/Status.h):
ZyanStatus是 32 位状态码,ZYAN_MAKE_STATUS(error, module, code)把"是否错误(1 位) + 模块 ID(11 位) + 具体错误码(20 位)"打包成单一值;ZYAN_SUCCESS/ZYAN_FAILED用于判断成败,ZYAN_CHECK(status)则是在失败时直接return状态码的"快速失败"惯用法。模块 ID 已预定义ZYAN_MODULE_ZYCORE、ZYAN_MODULE_ARGPARSE等。PCSX2 中 Zydis 的 Formatter 回调就大量使用ZYAN_CHECK链式传播错误; - 原子操作(include/Zycore/Atomic.h 及其
Internal/AtomicGNU.h、AtomicMSVC.h):分别基于 GCC 内建原子与 MSVC 的Interlocked*函数族提供跨平台原子读写/加减; - OS API 封装(include/Zycore/API/):
Memory.h、Process.h、Synchronization.h、Terminal.h、Thread.h将内存映射、进程、同步原语、终端 IO、线程等系统能力统一封装,Windows 走 Win32 API、POSIX 平台走 pthread/mmap 等; - 命令行解析(include/Zycore/ArgParse.h):提供参数解析器;格式化(include/Zycore/Format.h)提供格式化输出能力;对象操作(include/Zycore/Object.h)与比较器(include/Zycore/Comparison.h)则为容器提供元素析构回调与比较回调的通用定义。
七、编译集成方式与使用前提
Zycore 不是一个需要单独安装的系统库,而是作为 Zydis 的源码依赖直接参与构建:
- 在 PCSX2 中,zycore 的全部
.c与.h被 3rdparty/zydis/CMakeLists.txt 显式列为zydis库的源文件与头文件,与 Zydis 自身的src/Decoder.c、src/Formatter*.c等编译进同一目标,最终由 PCSX2 的 CMake 体系(参见根目录 CMakeLists.txt)链接进模拟器; - 使用方只需
#include <Zycore/Zycore.h>(主入口,包含类型定义并声明ZycoreGetVersion)或按需引入Zycore/Types.h、Zycore/Vector.h等细分头文件; - 由于 zycore 的所有头文件都带有完善的
extern "C"保护(如 Zycore.h),C++ 项目(如 PCSX2 的重编译器与调试器代码)可以直接包含而不必担心名字修饰问题; - 需要无 LibC 环境时,编译期定义
ZYAN_NO_LIBC即可切换到内建回退实现;如需完全自定义替换函数,则定义ZYAN_CUSTOM_LIBC并提供自己的实现。
八、源码阅读路线图
如果希望深入理解 Zycore 的每个模块,可以沿以下路径在仓库内继续探索(全部路径相对于仓库根目录):
- include/Zycore/Types.h:类型体系 + 编译期大小/符号性校验;
- include/Zycore/Defines.h:检测宏、断言、工具宏、导出宏;
- include/Zycore/LibC.h:LibC 别名与无 LibC 内联回退实现;
- include/Zycore/Vector.h、include/Zycore/List.h、include/Zycore/Bitset.h、include/Zycore/String.h:四类容器/字符串;
- include/Zycore/Status.h、include/Zycore/Allocator.h、include/Zycore/Atomic.h:状态码、分配器、原子操作;
- 落地示例:pcsx2/x86/ix86-32/iR5900.cpp 中 Zydis Formatter 回调对
ZyanStatus、ZYAN_CHECK的调用,可以直观看到这些基础设施在生产代码中的真实用法。
结语
Zycore 的定位决定了它的价值不在于功能花哨,而在于稳定与可移植:从整型宽度、布尔语义到编译器/平台/架构检测,再到无 LibC 环境下的内联回退,它把"写一次、处处编译"这件事做到了 C 语言能实现的极致。在 PCSX2 中,正是这套基础库支撑着 Zydis 反汇编器在多编译器、多平台下稳定工作,从而为模拟器的调试与动态重编译功能提供了可靠底座。
【免费下载链接】pcsx2PCSX2 - The Playstation 2 Emulator项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考