C++之《程序员自我修养》读书总结(5)

C++之《程序员自我修养》读书总结(5)
《程序员自我修养》读书总结(五)


Author: Once Day Date: 2026年2月12日



一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…



漫漫长路,有人对你微笑过嘛…



全系列文章可参考专栏:
书籍阅读_Once-Day的博客-ZEEKLOG博客



参考文章:《程序员的自我修养》读书笔记 | Zachary’s blog《程序员的自我修养》阅读笔记 - T0fV404 - 博客园读书笔记:《程序员的自我修养》 - 楷哥 - 博客园

文章目录

5. Windows PE/COFF 格式
5.1 发展历史

在 Windows 平台上,PE/COFF 是核心的二进制文件格式体系,其历史可以追溯到早期的 COFF(Common Object File Format)。COFF 最初用于 Unix 系统目标文件,强调可重定位目标文件的结构化组织。微软在此基础上进行扩展,形成适用于 Windows 平台的 PE(Portable Executable)格式,用于可执行文件、动态链接库以及驱动程序等。

在 Win32 时代,目标文件通常采用 COFF 格式,而最终生成的可执行文件或 DLL 则采用 PE 格式。PE 文件本质上是在 COFF 结构之上增加了装载信息与 Windows 特有的数据目录,例如导入表、导出表、资源表和重定位表等。这种分层设计使得编译器与链接器可以延续 COFF 的目标文件结构,同时满足操作系统加载器的需求。

随着 Win64 平台的出现,微软对 PE 格式进行了扩展,形成 PE32+。其核心差异在于支持 64 位地址空间,例如可选头中的 ImageBase、栈和堆大小字段从 32 位扩展为 64 位,而某些与 16 位兼容相关的字段被移除。PE32+ 保持了整体结构的一致性,从而保证工具链和加载机制的平滑过渡。

在结构组织上,PE/COFF 同样以段(Section)为核心。常见段包括 .text(代码)、.data(已初始化数据)、.rdata(只读数据)、.bss(未初始化数据)等。链接器会根据段属性为其分配不同的访问权限,例如可执行、可读或可写。这种段级别的权限划分,与操作系统的页级内存保护机制紧密配合,增强了程序运行时的安全性。

一个典型的 PE 文件加载流程可以概括如下:

读取 DOS 头

定位 PE Header

解析 Section Table

映射各 Section 到内存

处理重定位与导入表

跳转到入口点执行

通过 dumpbin /headersobjdump -x 等工具,可以直观查看这些头部与段表信息。理解 PE/COFF 的历史与结构,不仅有助于掌握 Windows 平台的程序装载机制,也为分析崩溃转储、逆向工程以及底层调试奠定了坚实基础。

5.2 mingw-w64 工具链

mingw-w64 是在原始 MinGW 基础上发展而来的 Windows 原生工具链,其核心目标是在不依赖微软编译器的前提下,使用 GCC 生成可在 Windows 上直接运行的本地程序。相较早期仅支持 32 位的 MinGW,mingw-w64 增强了对 64 位架构的支持,并补全了大量 Windows API 头文件与运行库接口,使其在现代 Windows 平台上具备更好的兼容性与可维护性。

从组成上看,mingw-w64 并非单一编译器,而是一整套工具集合。核心包括 gcc/g++ 编译器前端、binutils(如 ldasobjdump)、mingw-w64 运行时库(CRT)、Windows API 头文件与导入库,以及可选的 gdb 调试器。编译过程与类 Unix 系统一致,例如:

x86_64-w64-mingw32-g++ main.cpp -o app.exe 

这里的三元组 x86_64-w64-mingw32 表明目标平台为 64 位 Windows,体现了交叉编译工具链的命名规范。

在用途上,mingw-w64 既可以作为 Windows 本地开发环境,也可以在 Linux 或 macOS 上进行交叉编译。例如在 Linux 上构建 Windows 可执行文件:

sudoaptinstall mingw-w64 x86_64-w64-mingw32-gcc hello.c -o hello.exe 

这种能力在持续集成与跨平台发布场景中尤为重要,能够避免频繁切换操作系统环境。

常见发行版本主要包括 MSYS2WinLibsMingw-w64-builds 等。MSYS2 提供基于 pacman 的包管理系统,便于安装和升级;WinLibs 则提供预编译的压缩包,适合快速部署;部分 IDE(如 Code::Blocks)也内置特定版本工具链。不同发行版本在默认线程模型(posixwin32)以及异常处理机制(sehsjljdwarf)上可能存在差异,选择时需结合目标平台与性能需求。

安装方式通常分为三类:下载安装包直接解压配置环境变量,通过 MSYS2 包管理安装,或在类 Unix 系统中通过系统仓库获取交叉编译版本。

配置完成后,将 bin 目录加入 PATH,即可在命令行或 IDE 中调用 gccg++ 等工具。掌握 mingw-w64 的工具链结构与变种差异,有助于在跨平台构建和 ABI 兼容问题上做出更合理的技术决策。

5.3 COFF 文件结构

PE 文件结构中,映像头(Image Header)承担着描述整体布局与装载属性的职责。它位于 DOS Header 之后,由 File HeaderOptional Header 组成。File Header 包含机器类型、段数量、时间戳等基本信息,而 Optional Header(在可执行文件中实际上是必选的)则定义入口点地址、映像基址 ImageBase、节对齐方式以及数据目录表等关键字段。操作系统加载器正是依据这些信息,判断文件类型并完成内存映射。

在这里插入图片描述

从语义上看,映像头更像是“全局配置中心”。例如 AddressOfEntryPoint 决定程序从何处开始执行,Subsystem 指明是控制台程序还是 GUI 程序,而数据目录中的导入表、导出表、重定位表等条目,则提供后续解析所需的偏移位置。使用 dumpbin /headers 可以清晰查看这些字段,它们共同决定了映像文件的运行方式与系统交互模式。

段表(Section Table)紧随映像头之后,是对各个段(Section)的结构化描述。每个段表项记录段名、虚拟地址、文件偏移、大小以及访问属性等信息。加载器根据这些描述,将文件中的数据按页对齐映射到进程虚拟地址空间。例如 .text 通常标记为可执行和只读,而 .data 则具有读写权限。

_IMAGE_FILE_HEADERPE/COFF 文件结构中的核心组成部分,位于 IMAGE_NT_HEADERS 中的 File Header 区域。它用于描述目标文件或可执行文件的基本属性,例如目标架构、节数量以及符号表信息。该结构在目标文件(.obj)与可执行文件(.exe/.dll)中均存在,是链接器与加载器识别文件类型的重要依据。

其典型定义如下(来自 Windows SDK):

typedefstruct_IMAGE_FILE_HEADER{ WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics;} IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

各字段含义如下:

字段名类型含义说明
MachineWORD指定目标 CPU 架构,如 IMAGE_FILE_MACHINE_I386(0x014c)表示 x86,IMAGE_FILE_MACHINE_AMD64(0x8664)表示 x64。加载器据此判断是否与当前系统架构匹配。
NumberOfSectionsWORD文件中节(Section)的数量,对应后续 Section Table 中的条目数。
TimeDateStampDWORD文件创建时间戳,通常为自 1970 年 1 月 1 日以来的秒数,可用于调试与版本追踪。
PointerToSymbolTableDWORD指向 COFF 符号表的文件偏移,仅在目标文件中有效;在可执行文件中通常为 0。
NumberOfSymbolsDWORD符号表中的符号数量,主要用于 .obj 文件,供链接器解析。
SizeOfOptionalHeaderWORD后续 Optional Header 的大小。对于可执行文件,该值非 0;而纯 COFF 目标文件中通常为 0。
CharacteristicsWORD文件属性标志位,如是否为可执行文件、是否为 DLL、是否支持大地址空间等,使用按位组合的宏定义表示。

_IMAGE_SECTION_HEADER 用于描述 PE 文件中的每一个段(Section),是段表(Section Table)的基本单元。链接器在生成可执行文件时,会为每个段生成一个对应的段头;操作系统加载器则依据这些信息,将文件中的段正确映射到进程虚拟地址空间,并设置相应的访问权限。

其典型结构定义如下(来自 Windows SDK):

typedefstruct_IMAGE_SECTION_HEADER{ BYTE Name[8];union{ DWORD PhysicalAddress; DWORD VirtualSize;} Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics;} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;

各字段含义如下:

字段名类型含义说明
NameBYTE[8]段名,最长 8 字节,如 .text.data.rdata 等,不足部分以 \0 填充。
Misc.VirtualSizeDWORD段在内存中的实际大小(字节数)。若大于 SizeOfRawData,多余部分在内存中补零。
VirtualAddressDWORD段在内存中的相对虚拟地址(RVA),基于 ImageBase 计算实际加载地址。
SizeOfRawDataDWORD段在文件中的大小,通常按文件对齐值对齐。
PointerToRawDataDWORD段内容在文件中的偏移位置。
PointerToRelocationsDWORD指向重定位表的文件偏移,主要用于 COFF 目标文件,在可执行文件中通常为 0。
PointerToLinenumbersDWORD指向行号信息表的偏移,已较少使用,多数情况下为 0。
NumberOfRelocationsWORD重定位项数量,仅在目标文件中有效。
NumberOfLinenumbersWORD行号条目数量,现代编译环境中通常为 0。
CharacteristicsDWORD段属性标志,如是否包含代码、是否可执行、是否可读写等,以位标志形式组合。

在运行时,加载器依据 VirtualAddressVirtualSize 将段映射到内存,并根据 Characteristics 设置页面权限。例如 .text 通常具有 IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ 属性,而 .data 则具有 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE。通过理解该结构,可以清晰区分文件布局与内存布局之间的映射关系。

5.4 COFF 符号表

COFF 目标文件中,符号表(Symbol Table)是链接阶段的核心数据结构,用于描述函数、全局变量及外部引用等符号信息。编译器在生成 .obj 文件时,会为每个可见符号生成一条记录;链接器则遍历这些记录,完成符号解析与重定位。与可执行文件不同,COFF 符号表主要存在于目标文件中,最终链接生成的 PE 文件通常不再保留完整符号信息。

一个典型的 COFF 符号表项结构如下:

typedefstruct_IMAGE_SYMBOL{union{ BYTE ShortName[8];struct{ DWORD Short;// 若为0,则使用Long DWORD Long;// 指向字符串表的偏移} Name;} N; DWORD Value; SHORT SectionNumber; WORD Type; BYTE StorageClass; BYTE NumberOfAuxSymbols;} IMAGE_SYMBOL;

各字段含义可概括如下:

字段含义说明
Name符号名。若长度不超过 8 字节,直接存储;否则通过偏移索引到字符串表。
Value符号在所属段中的偏移地址。对于函数或变量,表示相对段起始的偏移。
SectionNumber符号所属段编号;为 0 表示未定义(外部符号),为负值表示特殊符号。
Type符号类型,通常低位表示基本类型,高位表示派生类型(如函数)。
StorageClass存储类别,如 IMAGE_SYM_CLASS_EXTERNAL 表示外部符号。
NumberOfAuxSymbols后续附加符号数量,用于补充信息(如函数大小等)。

结合一个简单示例进行说明。假设存在如下代码:

int global_var =10;intadd(int a,int b){return a + b + global_var;}

编译为 .obj 后,可通过 dumpbin /symbols 查看符号表。global_var 通常会显示为已定义外部符号,SectionNumber 指向 .data 段;add 位于 .text 段,其 Value 表示函数在代码段内的偏移。若另一个文件中声明 extern int global_var;,则在该文件的符号表中会出现同名但 SectionNumber 为 0 的未定义符号,等待链接器解析。

从链接器视角看,符号解析流程大致如下:

读取目标文件符号表

建立全局符号映射

匹配未定义符号

执行重定位修正

因此,COFF 符号表不仅是编译产物,更是模块化构建机制的基础。理解其结构与字段语义,有助于分析“未定义符号”“重复定义”等常见链接错误,并深入掌握 Windows 平台目标文件的内部组织方式。

5.5 PE 文件格式

PE 文件格式在设计之初就承载了向后兼容的历史使命,因此其文件开头并不是直接出现 COFF 文件头,而是以一个 DOS MZ 头部开始。该头部结构源自早期的 MS-DOS 可执行文件格式,其前两个字节为 MZ 签名。紧随其后的,是一段称为 DOS Stub 的桩代码。这种布局并非偶然,而是为了保证在 DOS 环境下执行时不会导致系统崩溃。

DOS Stub 的典型功能是在 DOS 系统中输出一段提示信息,例如 "This program cannot be run in DOS mode.",随后正常退出。其本质是一段合法的 16 位 DOS 程序。当 DOS 加载器发现该文件时,会按照 MZ 格式执行这段代码;而在 Windows 环境中,加载器会读取 e_lfanew 字段,跳转到真正的 PE 头部位置,忽略 Stub 内容。这种双重结构体现了早期操作系统演进过程中的兼容策略。

Image Header(即 COFF File Header)基础之上,PE 引入了更为复杂的 Optional Header 结构。尽管名为“可选”,但对于可执行文件和 DLL 来说它是必不可少的。

该结构包含入口点地址 AddressOfEntryPoint、映像基址 ImageBase、段对齐参数、子系统类型以及数据目录数组等信息。尤其是数据目录表,定义了导入表、导出表、资源表、重定位表等关键数据结构的定位方式,是 Windows 加载机制的重要支撑。

从整体结构上看,PE 文件可以抽象为如下层次:

DOS Header + DOS Stub

PE Signature

COFF File Header

PE Optional Header

Section Table

Section Data

这种结构体现了历史与现实的叠加。虽然 DOS 早已退出主流舞台,但 MZ 头部与桩代码依然保留在每一个 Windows 可执行文件中,成为 PE 格式的历史印记。这种兼容性设计既增加了文件结构的复杂度,也展示了操作系统生态在长期演进中对稳定性的高度重视。

Read more

KingbaseES数据库:ksql 命令行用户与权限全攻略,从创建到删除

KingbaseES数据库:ksql 命令行用户与权限全攻略,从创建到删除

KingbaseES数据库:ksql 命令行用户与权限全攻略,从创建到删除 本文聚焦 KingbaseES 数据库的 ksql 命令行用户与权限管理,先明确操作前需用管理员账号(如 system)连接数据库,并确认依赖的数据库、模式、表等对象存在。接着解析权限 “数据库→模式→表” 的层级关系,再分步讲解用户管理全流程:创建用户时需设置用户名、密码及属性,可通过 \du 命令查看用户信息,用 ALTER USER 修改密码、权限属性及默认配置,按层级用 GRANT 授予、REVOKE 回收权限,删除用户需谨慎,存在依赖对象时需加 CASCADE 参数。还列举了登录、查表、删用户的常见报错及解决办法,强调最小权限、层级清晰等核心原则,助力新手掌握数据安全控制要点。 前言     中电科金仓(北京)

By Ne0inhk
Flutter 组件 activity_files 适配鸿蒙 HarmonyOS 实战:文件活动流治理,构建高性能存储沙箱访问与资产全生命周期管理架构

Flutter 组件 activity_files 适配鸿蒙 HarmonyOS 实战:文件活动流治理,构建高性能存储沙箱访问与资产全生命周期管理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 activity_files 适配鸿蒙 HarmonyOS 实战:文件活动流治理,构建高性能存储沙箱访问与资产全生命周期管理架构 前言 在鸿蒙(OpenHarmony)生态迈向全场景分布式协同、涉及海量多媒体资产处理及严苛应用沙箱(Sandbox)隔离的背景下,如何实现一套既能穿透复杂的层级目录、又能实时追踪文件变更活动且具备极高 I/O 吞吐能力的存储治理架构,已成为决定应用性能广度与数据安全深度。在鸿蒙设备这类强调 AOT 极致性能与受限文件权限周期的环境下,如果应用依然采用陈旧的同步文件读取或缺乏活动追踪的直接 I/O,由于由于频繁的磁盘竞争,极易由于由于“主线程阻塞”或“资产状态不同步”导致用户在管理大型媒体库时发生明显的感知性卡顿。 我们需要一种能够解耦文件路径、支持异步流式追踪(Activity Tracking)且符合鸿蒙分布式文件系统安全范式的操作框架。 activity_files 为 Flutter 开发者引入了“

By Ne0inhk
Flutter 组件 chopper_built_value 适配鸿蒙 HarmonyOS 实战:强类型网络层架构,构建不可变模型与高性能序列化闭环

Flutter 组件 chopper_built_value 适配鸿蒙 HarmonyOS 实战:强类型网络层架构,构建不可变模型与高性能序列化闭环

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 chopper_built_value 适配鸿蒙 HarmonyOS 实战:强类型网络层架构,构建不可变模型与高性能序列化闭环 前言 在鸿蒙(OpenHarmony)生态迈向大规模企业级应用、涉及高频网络数据交互、复杂业务模型及严苛运行时稳定性的背景下,如何确保网络请求返回的数据在进入 UI 层前具备绝对的类型安全,已成为衡量应用架构“护城河”深度的核心标准。在鸿蒙设备这类强调 AOT 极致性能与低容错率的环境下,如果应用依然依赖动态类型的 Map<String, dynamic> 进行数据传递,由于由于后端字段变更或类型溢出,极易由于由于运行时强转失败导致应用在关键业务路径上的红屏崩溃。 我们需要一种能够实现自动化代码生成、支持不可变(Immutable)模型且具备拦截器解耦能力的序列化粘合层。 chopper_built_value 为 Flutter 开发者引入了将 Chopper

By Ne0inhk
淘宝超市卡TopAPI接入实战:Spring Boot + Lombok完整实现指南

淘宝超市卡TopAPI接入实战:Spring Boot + Lombok完整实现指南

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[[email protected]] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? * 专栏导航: 码农阿豪系列专栏导航 面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️ Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻 Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡 全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀 目录 * 淘宝超市卡TopAPI接入实战:Spring Boot + Lombok完整实现指南 * 引言 * 一、淘宝超市卡API概述 * 1.1 核心API接口 * 二、开发环境准备 * 2.

By Ne0inhk