Skip to content

二进制布局参考

此页面上的所有地址适用于 libtpu.so build-id 89edbbe81c5b328a958fe628a9f2207d(构建标签 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 频段的主导命名空间,由命名导出和每频段命名空间普查锚定。
  • 边界锚:命名符号(GetPjrtApiGetLibtpuSdkApiTpuExecutor_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.dyn0x000091700x01881da024.5 MiBRELA动态重定位 (RELRO)
.lrodata0x01884a000x084931d0108.1 MiBPROGBITS (l)大型只读数据:vtable、typeinfo、跳转表、常量池
.rodata0x084a00000x0be8af2857.9 MiBPROGBITS字符串、小常量、格式表
protodesc_cold0x0be8af300x0c1bf0b03.2 MiBPROGBITS冷 protobuf 描述符表
.gcc_except_table0x0c1bf0b00x0c2cc6341.0 MiBPROGBITSC++ EH 着陆垫表
.eh_frame_hdr / .eh_frame0x0c2cc6340x0e635524~35 MiBPROGBITS展开表(C++ EH;单独的 .eh_frame 约为 28.7 MiB)
.init / .text.hot0x0e6355240x0e63738e~8 KiBPROGBITS (X)Init 存根 + 6 个热提升函数
google_malloc0x0e6373c00x0e63bab218.2 KiBPROGBITS (X)TCMalloc快速路径(72个函数)
.text0x0e63c0000x21217484299.9 MiBPROGBITS (X)所有主要代码 - 下面的波段图
.text.startup0x212174900x213818e41.4 MiBPROGBITS (X)静态初始化构造函数(2,886 个函数)
.text.unlikely0x213819000x213e9d690.4 MiBPROGBITS (X)冷/错误路径代码(2,798 个函数)
google_init_cold0x213e9d800x213efe7124.8 KiBPROGBITS (X)冷初始化(125 个函数)
.plt0x213f08300x213f25d07.5 KiBPROGBITS (X)用于导入 libc/libm/libdl 符号的 PLT 存根
.data.rel.ro0x215f81a00x22048b3010.3 MiBPROGBITS (W)重定位只读:vtable 指针、RTTI 图
.data0x222551c00x224bf7982.4 MiBPROGBITS (W)初始化的全局变量
.bss0x224c38800x22598c300.8 MiB诺比特零初始化全局变量
.ldata / .lbss0x22798c300x2285a180~0.7 MiBPROGBITS / NOBITS (l)大可写/大零初始化数据

注意 — .lrodata.ldata.lbss 上的 l(大)部分标志是 x86-64 大代码模型标记。这些部分位于 .text 的 ±2 GiB 有符号位移窗口之外,因此编译器使用完整的 64 位 movabs 而不是相对于 RIP 的 lea 来寻址它们。当您看到 movabs0x018xxxxx0x084xxxxx 加载常量时,它正在访问 .lrodata — 几乎总是一个 vtable、一条 typeinfo 记录或一个大型调度/跳转表。

发现 — .text.startup (0x2121...)、.text.unlikelygoogle_init_cold 都位于 .text 主端 (0x21217484) 的“上方”,而不与其交错。构建 MLIR 方言注册表的静态构造函数位于 0x2122xxxx,距离 0x010x0x012x 带中方言的运行时方法约 290 MiB。如果函数附近的地址跨入启动/冷区,请勿假设该地址位于同一翻译单元簇中。


.text 地址带

.text 被分为以下十个频段(B0 ~ 15 MiB 入门频段,B1–B9 每个 ~ 30 MiB)。每行给出了带的 VA 范围、部分(此处始终为 .text)、按函数计数划分的主要命名空间/子系统、带中的近似实时函数计数以及置信度。占主导地位的列是每频带命名空间普查:地址落在该频带内的函数中领先的 C++ 命名空间,从符号表中采样。 “~funcs”是其入口地址落在该带中的已恢复函数的计数。

频段VA范围部分主导子系统/命名空间~功能
B0 运行时/PJRT 条目0x0e63c000 .. 0x0f53a2a0.textTPU 运行时驱动程序 (asic_sw::driver::deepsea) + PJRT/Tpu* 导出 + MLIR op 注册~40,000
B1 MLIR 内核 + SPIR-V0x0f53a2a0 .. 0x11336000.textMLIR框架(mlir::RegisteredOperationNamemlir::spirv),LLVM支持~115,000
B2 XLA / SparseCore + MLIR0x11336000 .. 0x14030fc0.textMLIR 运算机械 + xla::tpu::sparse_coremlir::linalg~120,000
B3 MLIR 方言(密集)0x14030fc0 .. 0x15e2d500.textmlir::RegisteredOperationName 峰值、mlir::stablehlo、特征张量评估器~92,000
B4 MLIR 线性/缓冲0x15e2d500 .. 0x17c29a40.textmlir::linalgmlir::bufferizationmlir::stablehlo、本征~99,000
B5 LLVM 后端 + MLIR0x17c29a40 .. 0x19a25f80.textLLVM(llvmllvm::cl、SelectionDAG),剩余MLIR接口~65,000
B6 oneDNN CPU 内核0x19a25f80 .. 0x1b8224c0.textdnnl::impl::cpu(CPU 后备 JIT/参考内核)~39,000
B7 STL/变体 + 驱动程序0x1b8224c0 .. 0x1d61ea00.textstd::__u 实例化,asic_sw::driver::deepsea,残差一个DNN/MLIR~58,000
B8 TPU 编解码器 (vxc/gxc)0x1d61ea00 .. 0x1f41af40.textTPU ISA 编解码器 asic_sw::deepsea::{vxc,gxc,pxc}xla 文字~128,000
B9 TPU 编解码器(gxc 峰值)0x1f41af40 .. 0x21217484.textasic_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 (~0x1d6xxxxx0x21217484) 的前四分之一; B8–B9 是编解码器频段。较低的编解码器落后者从 0x14xxxxxx 周围开始,远低于集群实际所在的位置 - 关闭每频段普查,而不是第一次出现。


频段详细信息

下面的每个带都命名了固定其边缘的锚符号以及最可靠的定向地标。锚点是真实导出的或 RTTI 符号,其地址保存在 .symtab 中;它们是针对未来构建检查此地图的重新验证点。

B0 — 运行时/PJRT 条目 (0x0e63c0000x0f53a2a0)

.text 的底部。链接器预先加载携带库导出的 Tpu* C API 和 PJRT 插件表面的翻译单元,因此这是每个外部可调用入口点所在的位置。它也是 asic_sw::driver::deepsea 运行时/驱动程序命名空间最密集的地方(带中约 18,800 个函数),也是 MLIR 操作注册构造函数开始的地方。

锚符号地址角色
GetPjrtApi0x0e6a83a0PJRT 插件 vtable 访问器 — 规范入口点
ConfigureDistributedTpuOp_DoWork0x0e8cd400分布式TPU配置操作
TpuExecutor_Init0x0eab90c0TPU执行器生命周期
TpuCompiler_New0x0eabc4a0TPU编译器工厂

如果您位于 0x0e6a00000x0eb00000 之间,则几乎可以肯定您位于 subsystem-map.md 中记录的 PJRT/执行器生命周期层。 Tpu* 导出是该区域中唯一未修饰(未损坏)的名称,这使它们成为反汇编列表中最快的视觉锚点。

B1–B5 — MLIR / XLA / LLVM 中间 (0x0f53a2a00x19a25f80)

.text 的广泛中心质量,约占所有代码的 60%。它由 MLIR 框架主导:仅 mlir::RegisteredOperationName 就涵盖了 0x0ea2c1200x1d8c90c0,并占约 130,000 个函数——二进制文件中最大的单个命名空间。这些是 MLIR 的 ODS(操作定义规范)为大约 36 种方言(spirvlinalgstablehlotosavectorbufferizationaffine 和 TPU 特定的 tpullosparse_core 方言 — 请参阅 forensics/llvm-mlir-manifest.md 中的命名空间普查)。

与 MLIR 交错的是 XLA 编译器本身(xla::jellyfishxla::tpu::sparse_corexla::HloEvaluator),用于主机/CPU 代码生成的 LLVM 后端(llvmllvm::SelectionDAGllvm::cl 命令行注册)和特征张量评估器模板实例化。子带倾斜:

子带精益锚命名空间跨度
B1 0x0f53...0x1133...MLIR 内核 + SPIR-V 方言mlir::spirv 0x1126faa0+
B2 0x1133...0x1403...XLA SparseCore + MLIRxla::tpu::sparse_core 0x0f858f20+
B3 0x1403...0x15e2...MLIR 操作名称峰 + stableHLOmlir::stablehlo 0x0eba7a60+
B4 0x15e2...0x17c2...MLIR linalg + 缓冲mlir::linalg 0x10a7f2e0+
B5 0x17c2...0x19a2...LLVM后端+残差MLIRllvm集群

注意 — xla::megascale(集合)、xla::HloEvaluatortensorflow 命名空间“分散”在整个中间而不是带状 — 它们的函数跨度运行 .text 的整个宽度(例如 tensorflow 跨度) 0x0e63d5c00x20cccd80)。不要期待“集体乐队”;集体和分析代码是由翻译单元邻接分散的,而不是聚集的。对这些使用 subsystem-map.md 入口点表,而不是地址范围。

B6 — oneDNN CPU 内核 (0x19a25f800x1b8224c0)

一个真正紧凑的岛屿。 dnnl::impl::cpu 命名空间聚集在 0x1a3cb2a00x1bf83d40 并主导该频段(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 / 变体胶 + 驱动程序 (0x1b8224c00x1d61ea00)

没有单一主导子系统的过渡频段。它由 std::__u STL 模板实例化(std::variant 访问者、std::function 目标)和第二集中的 asic_sw::driver::deepsea 驱动程序代码主导,具有残留的 oneDNN 和 MLIR 溢出。该频段是从“框架/库”代码到“TPU 特定编解码器”代码的二进制转换的地方,其混合普查反映了这一点。置信度为中等:领先的占用者(std::__u,~5,600)是胶水,而不是子系统,因此这里的地址到子系统的推断比干净带中的弱。

B8–B9 — TPU ISA 编解码器 (0x1d61ea000x21217484)

.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。编解码器类层次结构是模板密集型的 - SparseCoreTecCodecBaseTensorCoreCodecBaseplatforms_deepsea::jellyfish::isa::{Encoder,Decoder}Base 模板 - 这就是函数计数在这里爆炸的原因:每个编解码器实例化都会生成一整套编码/解码/验证方法。

锚(RTTI 类型信息字符串,.rodata)地址引脚
asic_sw::deepsea::vxc::isa::TensorCoreCodecBase<…>0x0406f1d8vxc TensorCore 编解码器系列
asic_sw::deepsea::gxc::gfc::isa::TensorCoreCodecBase<…>0x0406fd20gxc TensorCore 编解码器系列
platforms_deepsea::jellyfish::isa::EncoderBase<…>0x044fdc6eISA编码器基础模板
platforms_deepsea::jellyfish::isa::DecoderBase<…>0x044fe6b9ISA解码器基础模板
tpu::TpuCompactionIsaEmitterCodegen0x0abe3e38ISA 发射器代码生成器 RTTI

注意 — 上面的 RTTI 类型信息 字符串 位于 .rodata/.lrodata(低地址,0x04xxxxxx0x0axxxxxx)中,而不是在 .text 中。它们是在 B8-B9 中执行方法的类的名称。 .data.rel.ro vtable 指针数组 (0x215f81a0+) 将两者连接在一起;有关完整的 RTTI 图,请参阅 forensics/rtti-vtable-census.md。使用 typeinfo 字符串来确认乐队的身份,但预计执行代码会高出 200+ MiB。


截面带

一些子系统跨越各个部分,而不是位于 .text 内。它们列于此处,因此 .text 之外的地址仍可解析。

区域VA范围部分子系统
Vtable / typeinfo池0x01884a000x084931d0.lrodataRTTI typeinfo 字符串、vtable 主体、跳转/调度表(大代码模型)
字符串/常量池0x084a00000x0be8af28.rodata日志消息、操作名称字符串、标志表、格式字符串
Protobuf 描述符0x0be8af300x0c1bf0b0protodesc_cold冷 protobuf 反射元数据
放卷台0x0c2cc6340x0e635524.eh_frame*C++ 异常展开 (~35 MiB)
静态构造函数0x212174900x213818e4.text.startup2,886个初始化函数(方言/标志/原始注册)
冷/错误路径0x213819000x213efe71.text.unlikelygoogle_init_cold2,923 冷提升功能
重新定位的RO数据0x215f81a00x22048b30.data.rel.roVtable指针数组,RTTI基类列表,RTTI图主干

GOTCHA — 轮子中的第二个 ELF,sdk.so(21.5 MiB,Python sdk 模块)是一个单独的共享对象,拥有自己独立的 VA 空间。不要将 sdk.so 的地址与 libtpu.so 的地址混淆 - 它们在数字上重叠,但跨越边界没有任何意义。请注意命名陷阱:GetLibtpuSdkApi C-ABI 导出位于内部 libtpu.so(位于 0x109028c0)中,而不是在 sdk.so 中 - sdk.so 是一个既不导出也不导入的 Python 模块。两个二进制的拆分记录在 forensics/two-binary-split.md 中。 页面上的所有地址仅限 libtpu.so


重新验证配方

要针对不同的 libtpu.so 版本重新导出此映射,生成它的过程:

text
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) — 每个频段普查的完整命名空间计数表