二进制布局参考
此页面上的所有地址适用于
libtpu.sobuild-id89edbbe81c5b328a958fe628a9f2207d(构建标签libtpu_lts_20260413_b_RC00),来自libtpu-0.0.40-cp314轮(781,691,048 字节,884,832 个函数)。其他构建将改变每个边界。
摘要
libtpu.so 是一个 745 MiB 的静态链接整体:一个共享对象,吸收了 PJRT 插件入口层、完整的 XLA 编译器、具有三打方言的 MLIR 框架、TPU 指令集编解码器、oneDNN CPU 后备内核、分析和集合子系统以及 Abseil/protobuf/LLVM/gRPC运行时 — 所有链接时间合并到一个 .text 中。单步执行此二进制文件的逆向工程师会落在平坦的 299.9 MiB 指令区域内的某个位置,没有模块边界,也没有每个库段。 (此构建确实保留了其完整的 .symtab,因此符号名称通常可用 - 但仅靠名称并不能告诉您位于哪个链接合并子系统中。)此页面是回答当时唯一重要问题的地图:给定一个地址,我在哪个子系统中?
组织观察是链接顺序不是随机的。来自同一翻译单元集群的功能,以及来自同一子系统集群的翻译单元到 .text 内广泛、连续的“地址带”。 PJRT/运行时入口代码位于 .text 的最底部(链接器首先放置导出符号翻译单元); MLIR 方言机制填补了广阔的中间地带; oneDNN CPU 内核形成一个紧密的岛屿; TPU ISA 编解码器 (asic_sw::deepsea::{gxc,vxc,pxc}) 在顶部四分之一中堆积得最密集。这些带在其边缘重叠——单个命名空间可以有距离其质心 200 MiB 的离散者——所以这张地图描述的是密度质心和主要占用者,而不是硬分区。将带分配视为“此处大多数代码是 X”,通过采样进行验证,而不是保证每个字节都是 X。
这是地址带导航附录。它补充但不复制 ELF 节表:forensics/elf-anatomy.md 拥有权威的节头转储,而 subsystem-map.md 拥有子系统到入口点目录。此页面是它们之间的桥梁 - VA 到子系统的查找,将原始地址转换为起始假设。
对于导航,合约是:
- 节骨架:哪些节是可分配代码,哪些是大模型只读数据,以及每个节的开始位置。在您知道它属于哪个部分之前,您持有的地址毫无意义。
.text频段分区:每个 ~15-30 MiB 频段的主导命名空间,由命名导出和每频段命名空间普查锚定。- 边界锚:命名符号(
GetPjrtApi、GetLibtpuSdkApi、TpuExecutor_Init、RTTI typeinfo 字符串),其地址固定带边缘,以便可以针对任何未来的构建重新验证地图。
| 二进制 | libtpu.so,构建 ID 89edbbe81c5b328a958fe628a9f2207d,构建标签 libtpu_lts_20260413_b_RC00 |
| 文件大小 | 781,691,048 B (745.5 MiB),51 个命名部分(48 个可分配) |
| 功能数 | 总计 884,832 · .text 内 877,976 |
.text 范围 | 0x0e63c000 .. 0x21217484 (299.9 MiB) |
.lrodata 范围 | 0x01884a00 .. 0x084931d0(108.1 MiB,大只读数据) |
.rodata 范围 | 0x084a0000 .. 0x0be8af28 (57.9 MiB) |
| 最低代码地址 | 0x0e635524 (.init) |
| 最高代码地址 | 0x21217480(.text结束) |
| PJRT 入口锚 | GetPjrtApi @ 0x0e6a83a0(低.text) |
| SDK入口锚点 | GetLibtpuSdkApi @ 0x109028c0(中.text) |
截面骨架
在任何波段查找之前,将地址放置在其部分中。链接器以严格的升序排列文件 - 首先是只读数据,然后是一个小的辅助代码簇,然后是巨大的 .text,然后是可写的预告片。完整的 51 行标头转储位于 forensics/elf-anatomy.md;下面的行是绑定地址带的行。
| 部分 | VA 启动 | VA端 | 尺寸 | 型 | 持有 |
|---|---|---|---|---|---|
.rela.dyn | 0x00009170 | 0x01881da0 | 24.5 MiB | RELA | 动态重定位 (RELRO) |
.lrodata | 0x01884a00 | 0x084931d0 | 108.1 MiB | PROGBITS (l) | 大型只读数据:vtable、typeinfo、跳转表、常量池 |
.rodata | 0x084a0000 | 0x0be8af28 | 57.9 MiB | PROGBITS | 字符串、小常量、格式表 |
protodesc_cold | 0x0be8af30 | 0x0c1bf0b0 | 3.2 MiB | PROGBITS | 冷 protobuf 描述符表 |
.gcc_except_table | 0x0c1bf0b0 | 0x0c2cc634 | 1.0 MiB | PROGBITS | C++ EH 着陆垫表 |
.eh_frame_hdr / .eh_frame | 0x0c2cc634 | 0x0e635524 | ~35 MiB | PROGBITS | 展开表(C++ EH;单独的 .eh_frame 约为 28.7 MiB) |
.init / .text.hot | 0x0e635524 | 0x0e63738e | ~8 KiB | PROGBITS (X) | Init 存根 + 6 个热提升函数 |
google_malloc | 0x0e6373c0 | 0x0e63bab2 | 18.2 KiB | PROGBITS (X) | TCMalloc快速路径(72个函数) |
.text | 0x0e63c000 | 0x21217484 | 299.9 MiB | PROGBITS (X) | 所有主要代码 - 下面的波段图 |
.text.startup | 0x21217490 | 0x213818e4 | 1.4 MiB | PROGBITS (X) | 静态初始化构造函数(2,886 个函数) |
.text.unlikely | 0x21381900 | 0x213e9d69 | 0.4 MiB | PROGBITS (X) | 冷/错误路径代码(2,798 个函数) |
google_init_cold | 0x213e9d80 | 0x213efe71 | 24.8 KiB | PROGBITS (X) | 冷初始化(125 个函数) |
.plt | 0x213f0830 | 0x213f25d0 | 7.5 KiB | PROGBITS (X) | 用于导入 libc/libm/libdl 符号的 PLT 存根 |
.data.rel.ro | 0x215f81a0 | 0x22048b30 | 10.3 MiB | PROGBITS (W) | 重定位只读:vtable 指针、RTTI 图 |
.data | 0x222551c0 | 0x224bf798 | 2.4 MiB | PROGBITS (W) | 初始化的全局变量 |
.bss | 0x224c3880 | 0x22598c30 | 0.8 MiB | 诺比特 | 零初始化全局变量 |
.ldata / .lbss | 0x22798c30 | 0x2285a180 | ~0.7 MiB | PROGBITS / NOBITS (l) | 大可写/大零初始化数据 |
注意 —
.lrodata、.ldata和.lbss上的l(大)部分标志是 x86-64 大代码模型标记。这些部分位于.text的 ±2 GiB 有符号位移窗口之外,因此编译器使用完整的 64 位movabs而不是相对于 RIP 的lea来寻址它们。当您看到movabs从0x018xxxxx–0x084xxxxx加载常量时,它正在访问.lrodata— 几乎总是一个 vtable、一条 typeinfo 记录或一个大型调度/跳转表。发现 —
.text.startup(0x2121...)、.text.unlikely和google_init_cold都位于.text主端 (0x21217484) 的“上方”,而不与其交错。构建 MLIR 方言注册表的静态构造函数位于0x2122xxxx,距离0x010x–0x012x带中方言的运行时方法约 290 MiB。如果函数附近的地址跨入启动/冷区,请勿假设该地址位于同一翻译单元簇中。
.text 地址带
.text 被分为以下十个频段(B0 ~ 15 MiB 入门频段,B1–B9 每个 ~ 30 MiB)。每行给出了带的 VA 范围、部分(此处始终为 .text)、按函数计数划分的主要命名空间/子系统、带中的近似实时函数计数以及置信度。占主导地位的列是每频带命名空间普查:地址落在该频带内的函数中领先的 C++ 命名空间,从符号表中采样。 “~funcs”是其入口地址落在该带中的已恢复函数的计数。
| 频段 | VA范围 | 部分 | 主导子系统/命名空间 | ~功能 |
|---|---|---|---|---|
| B0 运行时/PJRT 条目 | 0x0e63c000 .. 0x0f53a2a0 | .text | TPU 运行时驱动程序 (asic_sw::driver::deepsea) + PJRT/Tpu* 导出 + MLIR op 注册 | ~40,000 |
| B1 MLIR 内核 + SPIR-V | 0x0f53a2a0 .. 0x11336000 | .text | MLIR框架(mlir::RegisteredOperationName、mlir::spirv),LLVM支持 | ~115,000 |
| B2 XLA / SparseCore + MLIR | 0x11336000 .. 0x14030fc0 | .text | MLIR 运算机械 + xla::tpu::sparse_core、mlir::linalg | ~120,000 |
| B3 MLIR 方言(密集) | 0x14030fc0 .. 0x15e2d500 | .text | mlir::RegisteredOperationName 峰值、mlir::stablehlo、特征张量评估器 | ~92,000 |
| B4 MLIR 线性/缓冲 | 0x15e2d500 .. 0x17c29a40 | .text | mlir::linalg、mlir::bufferization、mlir::stablehlo、本征 | ~99,000 |
| B5 LLVM 后端 + MLIR | 0x17c29a40 .. 0x19a25f80 | .text | LLVM(llvm、llvm::cl、SelectionDAG),剩余MLIR接口 | ~65,000 |
| B6 oneDNN CPU 内核 | 0x19a25f80 .. 0x1b8224c0 | .text | dnnl::impl::cpu(CPU 后备 JIT/参考内核) | ~39,000 |
| B7 STL/变体 + 驱动程序 | 0x1b8224c0 .. 0x1d61ea00 | .text | std::__u 实例化,asic_sw::driver::deepsea,残差一个DNN/MLIR | ~58,000 |
| B8 TPU 编解码器 (vxc/gxc) | 0x1d61ea00 .. 0x1f41af40 | .text | TPU ISA 编解码器 asic_sw::deepsea::{vxc,gxc,pxc}、xla 文字 | ~128,000 |
| B9 TPU 编解码器(gxc 峰值) | 0x1f41af40 .. 0x21217484 | .text | asic_sw::deepsea::gxc 密度峰值,pxc/vxc,protobuf arena | ~146,000 |
QUIRK — 命名空间
asic_sw::deepsea::gxc(TPU“core-X”ISA 编解码器系列,.text中的约 60,700 个函数)出现在约 197 MiB 代码跨度(0x1391cd40..0x1fe6f7a0)中,但其密度向.text的顶部:~13.7k 函数落在 B8 中,~46k 函数落在 B9 中。如果重新实现者在第一个gxc函数中键入“编解码器从 X 开始”,则速度会太低约 150 MiB。使用密度质心(高.text,B8–B9),而不是第一次出现。注意 — 编解码器命名空间 (
gxc/vxc/pxc) 集群升序,密度峰值位于.text(~0x1d6xxxxx–0x21217484) 的前四分之一; B8–B9 是编解码器频段。较低的编解码器落后者从0x14xxxxxx周围开始,远低于集群实际所在的位置 - 关闭每频段普查,而不是第一次出现。
频段详细信息
下面的每个带都命名了固定其边缘的锚符号以及最可靠的定向地标。锚点是真实导出的或 RTTI 符号,其地址保存在 .symtab 中;它们是针对未来构建检查此地图的重新验证点。
B0 — 运行时/PJRT 条目 (0x0e63c000–0x0f53a2a0)
.text 的底部。链接器预先加载携带库导出的 Tpu* C API 和 PJRT 插件表面的翻译单元,因此这是每个外部可调用入口点所在的位置。它也是 asic_sw::driver::deepsea 运行时/驱动程序命名空间最密集的地方(带中约 18,800 个函数),也是 MLIR 操作注册构造函数开始的地方。
| 锚符号 | 地址 | 角色 |
|---|---|---|
GetPjrtApi | 0x0e6a83a0 | PJRT 插件 vtable 访问器 — 规范入口点 |
ConfigureDistributedTpuOp_DoWork | 0x0e8cd400 | 分布式TPU配置操作 |
TpuExecutor_Init | 0x0eab90c0 | TPU执行器生命周期 |
TpuCompiler_New | 0x0eabc4a0 | TPU编译器工厂 |
如果您位于 0x0e6a0000 和 0x0eb00000 之间,则几乎可以肯定您位于 subsystem-map.md 中记录的 PJRT/执行器生命周期层。 Tpu* 导出是该区域中唯一未修饰(未损坏)的名称,这使它们成为反汇编列表中最快的视觉锚点。
B1–B5 — MLIR / XLA / LLVM 中间 (0x0f53a2a0–0x19a25f80)
.text 的广泛中心质量,约占所有代码的 60%。它由 MLIR 框架主导:仅 mlir::RegisteredOperationName 就涵盖了 0x0ea2c120–0x1d8c90c0,并占约 130,000 个函数——二进制文件中最大的单个命名空间。这些是 MLIR 的 ODS(操作定义规范)为大约 36 种方言(spirv、linalg、stablehlo、tosa、vector、 bufferization、affine 和 TPU 特定的 tpu、llo、sparse_core 方言 — 请参阅 forensics/llvm-mlir-manifest.md 中的命名空间普查)。
与 MLIR 交错的是 XLA 编译器本身(xla::jellyfish、xla::tpu::sparse_core、xla::HloEvaluator),用于主机/CPU 代码生成的 LLVM 后端(llvm、llvm::SelectionDAG、llvm::cl 命令行注册)和特征张量评估器模板实例化。子带倾斜:
| 子带 | 精益 | 锚命名空间跨度 |
|---|---|---|
B1 0x0f53...–0x1133... | MLIR 内核 + SPIR-V 方言 | mlir::spirv 0x1126faa0+ |
B2 0x1133...–0x1403... | XLA SparseCore + MLIR | xla::tpu::sparse_core 0x0f858f20+ |
B3 0x1403...–0x15e2... | MLIR 操作名称峰 + stableHLO | mlir::stablehlo 0x0eba7a60+ |
B4 0x15e2...–0x17c2... | MLIR linalg + 缓冲 | mlir::linalg 0x10a7f2e0+ |
B5 0x17c2...–0x19a2... | LLVM后端+残差MLIR | llvm集群 |
注意 —
xla::megascale(集合)、xla::HloEvaluator和tensorflow命名空间“分散”在整个中间而不是带状 — 它们的函数跨度运行.text的整个宽度(例如tensorflow跨度)0x0e63d5c0–0x20cccd80)。不要期待“集体乐队”;集体和分析代码是由翻译单元邻接分散的,而不是聚集的。对这些使用subsystem-map.md入口点表,而不是地址范围。
B6 — oneDNN CPU 内核 (0x19a25f80–0x1b8224c0)
一个真正紧凑的岛屿。 dnnl::impl::cpu 命名空间聚集在 0x1a3cb2a0–0x1bf83d40 并主导该频段(B6 中约 16,000 个函数,跨度约 28,000 个函数)。这是 Intel oneDNN (DNNL) CPU 后端 — XLA 回退到参考和 JIT 生成的卷积/matmul/重排序内核以进行主机端执行。由于 oneDNN 是一个独立的第三方库,几乎没有交叉调用 libtpu 的其余部分,因此它的翻译单元连续链接,从而在二进制文件中产生最干净的带边界。
QUIRK — 密集的
dnnl::impl::cpu簇使 B6 成为最容易仅通过内容识别的频段:大量使用 AVX-512 寄存器阻塞内部循环、std::__u::__function蹦床(约 6,200 个带内,oneDNN 内核调度闭包),并且几乎没有mlir/xla符号。如果反汇编窗口是全面的矢量化 GEMM 微内核,那么您就处于 B6 中。
B7 — STL / 变体胶 + 驱动程序 (0x1b8224c0–0x1d61ea00)
没有单一主导子系统的过渡频段。它由 std::__u STL 模板实例化(std::variant 访问者、std::function 目标)和第二集中的 asic_sw::driver::deepsea 驱动程序代码主导,具有残留的 oneDNN 和 MLIR 溢出。该频段是从“框架/库”代码到“TPU 特定编解码器”代码的二进制转换的地方,其混合普查反映了这一点。置信度为中等:领先的占用者(std::__u,~5,600)是胶水,而不是子系统,因此这里的地址到子系统的推断比干净带中的弱。
B8–B9 — TPU ISA 编解码器 (0x1d61ea00–0x21217484)
.text 的前四分之一和 TPU 专用机器的核心:为三个核心系列(vxc(矢量核心)、gxc(通用/网格核心)和 pxc(处理核心))编码/解码 TPU 包的指令集编解码器。这些是 asic_sw::deepsea::{vxc,gxc,pxc} 命名空间,它们在这里最密集:B8 包含 ~20k vxc + ~14k gxc + ~1.5k pxc; B9 持有约 46k gxc(其峰值)+ 约 4.9k pxc + 约 4.4k vxc。编解码器类层次结构是模板密集型的 - SparseCoreTecCodecBase、TensorCoreCodecBase 和 platforms_deepsea::jellyfish::isa::{Encoder,Decoder}Base 模板 - 这就是函数计数在这里爆炸的原因:每个编解码器实例化都会生成一整套编码/解码/验证方法。
锚(RTTI 类型信息字符串,.rodata) | 地址 | 引脚 |
|---|---|---|
asic_sw::deepsea::vxc::isa::TensorCoreCodecBase<…> | 0x0406f1d8 | vxc TensorCore 编解码器系列 |
asic_sw::deepsea::gxc::gfc::isa::TensorCoreCodecBase<…> | 0x0406fd20 | gxc TensorCore 编解码器系列 |
platforms_deepsea::jellyfish::isa::EncoderBase<…> | 0x044fdc6e | ISA编码器基础模板 |
platforms_deepsea::jellyfish::isa::DecoderBase<…> | 0x044fe6b9 | ISA解码器基础模板 |
tpu::TpuCompactionIsaEmitterCodegen | 0x0abe3e38 | ISA 发射器代码生成器 RTTI |
注意 — 上面的 RTTI 类型信息 字符串 位于
.rodata/.lrodata(低地址,0x04xxxxxx–0x0axxxxxx)中,而不是在.text中。它们是在 B8-B9 中执行方法的类的名称。.data.rel.rovtable 指针数组 (0x215f81a0+) 将两者连接在一起;有关完整的 RTTI 图,请参阅forensics/rtti-vtable-census.md。使用 typeinfo 字符串来确认乐队的身份,但预计执行代码会高出 200+ MiB。
截面带
一些子系统跨越各个部分,而不是位于 .text 内。它们列于此处,因此 .text 之外的地址仍可解析。
| 区域 | VA范围 | 部分 | 子系统 |
|---|---|---|---|
| Vtable / typeinfo池 | 0x01884a00–0x084931d0 | .lrodata | RTTI typeinfo 字符串、vtable 主体、跳转/调度表(大代码模型) |
| 字符串/常量池 | 0x084a0000–0x0be8af28 | .rodata | 日志消息、操作名称字符串、标志表、格式字符串 |
| Protobuf 描述符 | 0x0be8af30–0x0c1bf0b0 | protodesc_cold | 冷 protobuf 反射元数据 |
| 放卷台 | 0x0c2cc634–0x0e635524 | .eh_frame* | C++ 异常展开 (~35 MiB) |
| 静态构造函数 | 0x21217490–0x213818e4 | .text.startup | 2,886个初始化函数(方言/标志/原始注册) |
| 冷/错误路径 | 0x21381900–0x213efe71 | .text.unlikely,google_init_cold | 2,923 冷提升功能 |
| 重新定位的RO数据 | 0x215f81a0–0x22048b30 | .data.rel.ro | Vtable指针数组,RTTI基类列表,RTTI图主干 |
GOTCHA — 轮子中的第二个 ELF,
sdk.so(21.5 MiB,Pythonsdk模块)是一个单独的共享对象,拥有自己独立的 VA 空间。不要将sdk.so的地址与libtpu.so的地址混淆 - 它们在数字上重叠,但跨越边界没有任何意义。请注意命名陷阱:GetLibtpuSdkApiC-ABI 导出位于内部libtpu.so(位于0x109028c0)中,而不是在sdk.so中 -sdk.so是一个既不导出也不导入的 Python 模块。两个二进制的拆分记录在forensics/two-binary-split.md中。 此页面上的所有地址仅限libtpu.so。
重新验证配方
要针对不同的 libtpu.so 版本重新导出此映射,生成它的过程:
1. readelf -SW libtpu.so
-> .text VA start/end, .lrodata/.rodata bounds, large-section flags
2. Histogram recovered function entry addresses into N equal .text sub-bands
-> raw function-density profile (the ~Funcs column)
3. For each sub-band, census the leading demangled C++ namespace token
-> dominant-subsystem column
4. Pin band edges with named anchors:
readelf -sW libtpu.so | grep -E 'GetPjrtApi|GetLibtpuSdkApi|TpuExecutor_Init'
readelf -sW libtpu.so | grep -iE 'CodecBase|EncoderBase|DecoderBase|IsaEmitter'
5. Cross-check density centroids vs first/last occurrence of each namespace
-> distinguishes the cluster (centroid) from stragglers (span edges)
```text
跨构建应该存在的不变量(即使绝对地址发生变化):PJRT/运行时是*最低*,MLIR 填充*中间*,oneDNN 是靠近下三分之一的*紧密岛*,TPU 编解码器集群*最高*。此页面上的绝对边界是特定于构建的; *排序*是结构性的,遵循子系统的链接顺序。
---
## 交叉引用
- [精灵解剖](../forensics/elf-anatomy.md)——拥有权威的51行节头表;该页面的部分骨架是导航子集
- [取证概述](../forensics/overview.md) — 二进制形状方向:大小、符号计数、剥离状态
- [定制型材](../forensics/custom-sections.md) — `protodesc_cold`、`google_malloc`、`google_init_cold`、`linkarr_upb_AllExts`等非标型材
- [子系统图](../subsystem-map.md) — 拥有子系统到入口点目录;将其用于分散而不是带状的集体/分析代码
- [LLVM/MLIR 清单](../forensics/llvm-mlir-manifest.md) — 填充 B1–B5 频段的方言普查
- [嵌入式库图集](../forensics/embedded-library-atlas.md) — 静态链接到 `.text` 的第三方库(oneDNN、abseil、protobuf、gRPC)
- [RTTI / Vtable普查](../forensics/rtti-vtable-census.md) — 将 `.lrodata` 类型信息连接到 B8–B9 编解码器代码的 `.data.rel.ro` vtable 图
- [两二进制分割](../forensics/two-binary-split.md) — 为什么 `sdk.so` 是与 `libtpu.so` 不同的 VA 空间
- [符号命名空间索引](symbol-namespace-index.md) — 每个频段普查的完整命名空间计数表