Skip to content

嵌入式库图谱

本页中的所有字节偏移、大小和版本字符串均适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(发布标记 libtpu_lts_20260413_b_RC00,build-id 89edbbe81c5b328a958fe628a9f2207d,磁盘上 781,691,048 bytes)。其他 wheel 会不同。

摘要

libtpu.so 是一个单一的静态链接 PJRT 插件,在一个 ELF 内携带了完整的编译器、运行时和 ML 栈。它的动态依赖列表有六项 — libmlibpthreadlibdllibrtlibcld-linux-x86-64没有 libstdc++,也没有 libgcc_s。其他所有东西,从 C++ 标准库到 LLVM/MLIR,再到 crc32 表,都被编译进来了。本页是物料清单:权威列出哪些第三方库被嵌入、证明每个库存在的二进制证据、二进制固定的版本,以及每个库占据的大致代码足迹。

对一个已发布制品来说,不寻常的是,这个二进制没有被 strip。它的 .symtab 保留了约 1.2 百万个已定义的 Itanium-mangled symbols。这完全改变了取证方法:经典 FLIRT 签名匹配 — 面对 stripped binary 时会使用的技术 — 在这里并不需要,因为每个函数已经用明文声明了自己的所属 namespace(absl::mlir::re2::tcmalloc::)。因此,本图谱通过对 demangled symbol pool 进行 namespace 分桶构建,并用烘进 .rodatathird_party/... source-path literals 以及带版本的 banners 进行佐证。全文出现三类证据:namespace marker(demangled symbol prefix,出现在某个 file offset)、version string(固定某个 release 的 banner 或 path literal)以及 symbol-family count(某个库从 IDA names sidecar 贡献多少不同名称)。引用原始计数时,它是文本包含该 family fragment 的 symbol-table names 数量。

下面的结构先给出一眼可见的物料清单,然后把细节分成四个家族:Google C++ 核心(Abseil、protobuf、tcmalloc、riegeli、RE2、farmhash)、数学/ML 层(Eigen、oneDNN)、压缩/加密层(zlib、zstd、Brotli、snappy、BoringSSL)和杂项(ICU、gRPC、RE2 的相邻工具)。LLVM 和 MLIR 是迄今最大的单体占用者;它们有自己的页面,这里只作为 line items 摘要 — commit pin 和 dialect inventory 见 LLVM/MLIR 清单

对重实现者而言,契约是:

  • 依赖姿态 — 哪些是动态链接的(六个库),哪些是静态嵌入的(其他全部,包括 C++ runtime),以及如何从 DT_NEEDED 确认。
  • 每个库的检测方法 — 哪个 namespace marker、path literal 或 banner 证明存在,以及位于哪个 file offset,使 inventory 可以复现和重新验证。
  • 二进制实际固定的 version pins(oneDNN v3.3、ICU 78、protobuf editions、LLVM trunk commit),相对于它只给出下限的版本(Abseil LTS、Eigen 3.4.x、gRPC),以及每个结论为什么落在那里。
二进制libtpu.so, 781,691,048 bytes, ELF64 DYN x86-64
Build-id89edbbe81c5b328a958fe628a9f2207d (.note.gnu.build-id)
发布标记libtpu_lts_20260413_b_RC00 @ file offset 0x84A0E80 (139,071,104)
动态依赖6: libm, libpthread, libdl, librt, libc, ld-linux-x86-64
是否 stripped?.symtab retains ~1.2 M mangled symbols;classification is by namespace, not FLIRT
工具链clang version 9999.0.0 (a70419505471…) @ 0xAF59226; Bazel r4rca-2026.04.04-1 (mainline @894239244) @ 0x84A12A0
最大占用者LLVM + MLIR(见 sibling page),然后是 Abseil、oneDNN、libc++、Eigen

物料清单

这张表是权威 inventory。Version evidence 是二进制支持的最强 pin:存在时是 path literal 或 banner;仅能观察行为时则是 feature-floor。Footprint 是库的 namespace 拥有的 code-plus-rodata symbol bytes 的数量级份额 — 粗略的大小信号,不是 linker map。Confidence 反映存在性和版本被多直接地观察到:CERTAIN = namespace 和 version string 都存在;HIGH = namespace family 存在,version 从 features 推断;MEDIUM = 存在但 version unresolved。

足迹尺度供定向参考:huge ≳ 25 MB,large 5–25 MB,medium 1–5 MB,small 0.1–1 MB,trace < 0.1 MB。

版本证据足迹
LLVM(见 相邻页面LLVM version g3_____-trunk 8918319853fbdf… @ 0xB1FA070 — trunk commit,无 upstream taghuge (~84 MB)
MLIR(见 相邻页面与同一个 LLVM trunk 捆绑;mlir:: namespacehuge (~72 MB)
Abseilabsl::log_internal::SetTimeZone @ 0x8714E5B; absl::AnyInvocable @ 0x188C785≥ LTS 20240722,ceiling openhuge (~29 MB)
Intel oneDNN (DNNL)path third_party/intel_dnnl/v3_3/src/common @ 0x85BEAE8v3.3large (~25 MB)
libc++std::__u:: ABI tag — google3 libc++,匹配 LLVM commitlarge (~18 MB)
EigenEigen namespace,28,702 names;Eigen::bfloat16 tensor ABI → 3.4.x branch,exact tag openlarge (~12 MB)
protobufEDITION_2024 @ 0x5EE8C1E, EDITION_2026 @ 0x5EE8C31; descriptor_table_protodef @ 0x23DF7736v32+ editions-eramedium (~8 MB)
gRPCgrpc_core:: @ 0x188C4DE; chaotic_good transport → google3-tip(无 tagged release),≥ v1.66 devmedium (~3 MB)
BoringSSLBoringSSL banner @ 0x857D84C; bssl:: / EVP_/SSL_/X509_ C symbolsmedium (~1.3 MB)
BrotliBrotliDecoderDecompress @ 0xA2869A1; static-dictionary tables → ≥ 1.1,通过 riegeli 到达small (~0.85 MB)
zstdZSTD_compressBound @ 0x270D623C, ZSTD_createDCtx @ 0x8716BDF; bundled as third_party/zippy/small (~0.6 MB)
ICUdata file icudt78 @ 0x83FE04C; namespace icu_78ICU 78small (~0.5 MB)
riegelipath third_party/riegeli/ @ 0x85D7066; riegeli:: namespacesmall (~0.3 MB)
snappysnappy:: namespace @ 0xA1251E5trace (~18 KB)
RE2re2:: @ 0x86679B9, re2::RegexpFlag, third_party/re2small (~0.12 MB)
zlibzlib_rs Rust-port symbols @ 0x852F25E; deflate/inflate/crc32 C symbolssmall (~0.17 MB)
Xbyak (oneDNN JIT)Xbyak namespace @ 0xB38846Dtrace (~90 KB)
tcmalloctcmalloc:: namespace;custom google_malloc ELF sectiontrace (~55 KB code, allocator-only)
farmhashfarmhash family(farmhashna/uo/te/xo/mk/cc)@ 0xfdce579trace (~7 KB)

NOTE — footprint 数字是 named symbol bytes 的份额,而不是 segment sizes。它们能正确排列库之间的相对大小,但不应被当作精确的 .text 贡献;一个失去 namespace prefix 的 Abseil TU-local helper 会被计入 anonymous residue,而不是 Abseil。应把 scale buckets 视为可靠信号,而不是括号内数字。

QUIRK — DT_NEEDED 中缺少 libstdc++.so.6libgcc_s.so.1 是本页最具后果的事实。标准 C++ runtime 以 google3 的 libc++ 构建形式静态链接,可通过每个 std:: 符号上的 __u inline-namespace ABI tag 识别(std::__u::basic_stringstd::__u::vector)。假设加载了系统 libstdc++ 的重实现者会在 string 和 exception ABI 上不匹配。相比之下,配套的 sdk.so 确实声明 libstdc++.so.6 — 两个二进制做出了相反的链接选择;见 双二进制拆分


Google C++ 核心

这些库构成其他每个 google3 组件的基础:collections、strings、status propagation、logging、allocator 和 serialization stack。它们无处不在 — 单是 Abseil 就比任何非 LLVM 库贡献了更多 named symbols。

Abseil

Abseil 是基底。它的 absl:: namespace 出现在 109,189 个 symbol-table names 中(nm -C libtpu.so | grep -c 'absl::'),是该二进制中最大的非 LLVM/MLIR family,覆盖 absl::Status/StatusOrabsl::Cordabsl::flat_hash_map、flags library 和 structured-logging stack。

版本有下限但没有上限。offset 0x8714E5B(141,643,355)处存在 absl::log_internal::SetTimeZone0x188C785(25,741,189)处存在 absl::AnyInvocable,这把构建放在 LTS 20240722 或之后 — 两者都是在该 cut 或之后落地的功能。.rodata 中没有留下 ABSL_LTS_RELEASE_VERSIONABSL_OPTION_* literal,因此无法仅从字符串固定上界。要闭合它,需要对 tagged Abseil trees 做逐符号 diff(例如检查 SwissTable container internals 是否匹配 LTS 20250127 重组)。

text
absl::log_internal::SetTimeZone          @ 0x8714E5B   →  ≥ LTS 20240722
absl::AnyInvocable                       @ 0x188C785   →  ≥ LTS 20240722
absl:: (symbol family)                   109,189 names →  pervasive
```text

### protobuf

Protobuf 处于现代 **editions** 时代。enum literals `EDITION_2024`(offset `0x5EE8C1E`,99,519,518)和 `EDITION_2026`(`0x5EE8C31`,99,519,537)在 `.rodata` enum-name pool 中相邻出现,生成的 descriptor 机制则以 `descriptor_table_protodef_*` symbols(首个在 `0x23DF7736`,601,847,606)以及 `TableStruct_*` reflection metadata 的形式出现。`EDITION_2024`-production / `EDITION_2026`-declared 组合与 protobuf **v32 或之后**一致(editions support 在 v32 发布)。确切 minor version 没有以 banner 暴露;固定它需要把 serialized descriptor-pool wire format 与候选源码树匹配。

> **NOTE —** 760 条 serialized `FileDescriptorProto` records 位于专用的 `protodesc_cold` ELF section 中,被构建从 `.rodata` 其余部分隔离出来。这种隔离是一个编译时提示:descriptor pool 是 cold-path data;它记录在 [自定义 Sections](custom-sections.md) 下,而不是在这里重复。

### tcmalloc

allocator 是 google3 的 TCMalloc,而不是系统 `malloc`。两项证据具有决定性:`tcmalloc::` / `tcmalloc_internal::` namespace family(107 names;裸 `tcmalloc` fragment 为 110),以及构建专门为它发出的一组 **custom ELF sections** — `google_malloc`(allocator code body)、`google_malloc_bss`(其 `.bss` state)、`malloc_hook`(`MallocHook` lifecycle thunks)和 `__rseq_cs`(per-CPU caches 的 restartable-sequence critical-section metadata)。`__rseq_cs` section 是 post-2024 TCMalloc 的 rseq-accelerated per-CPU cache design 的明显标记。named code footprint 很小(约 55 KB),因为 allocator 大多是紧凑的 hot path;标记它的是 section-level isolation,而不是 byte count。

### riegeli、RE2、farmhash

三个较小的 google3 常用组件补齐了 core:

- **riegeli** — record-IO format。`0x85D7066`(140,341,350)处的 path literal `third_party/riegeli/` 和 `riegeli::` namespace 确认它存在。Riegeli 是拉入 Brotli(见下)的消费者,用其作为 block codec。
- **RE2** — regex engine。`0x86679B9`(140,933,561)处的 namespace `re2::`,加上 internal `re2::RegexpFlag` symbol 和 `third_party/re2` path。约 18,722 个 mangled names 携带 `re2` fragment(`nm libtpu.so | grep -c re2`),不过该计数包括生成的 automaton tables,不只是 engine code;demangled `re2::` namespace 自身只占 605 names。
- **farmhash** — non-cryptographic hash。完整的子变体 family 均存在(`farmhashna`、`farmhashuo`、`farmhashte`、`farmhashxo`、`farmhashmk`、`farmhashcc`,从 `0xfdce579` 开始),这具有决定性:偶然字符串不会复现全部六个 dispatch variants。Footprint 只是少数几个函数(约 7 KB)。

---

## 数学 / ML 层

这是 XLA 和 TPU runtime 依赖的数值核心:来自 Eigen 的 dense linear algebra 和 tensor expression templates,以及用于 host-side fallback paths 的 x86 JIT-kernel library oneDNN。

### Eigen

Eigen 贡献 28,702 个 symbol-table names(`nm libtpu.so | grep -c Eigen`)— 是 Abseil 和 LLVM/MLIR 组合之后第三大的 C++ family — 几乎全部是 unsupported `Tensor` module 的 template instantiations(`Eigen::Tensor`、`ThreadPoolDevice`、async executor)。`Eigen::bfloat16` 作为 first-class scalar 存在(而不只是 legacy `Eigen::half`),把该构建放在 **3.4.x branch 或之后**。`.rodata` 中没有留下 `EIGEN_WORLD_VERSION` / `EIGEN_MAJOR_VERSION` macro literal,因此无法用字符串证据区分 3.4.0 和 3.4-series snapshot;pin 指向 branch,而不是 tag。

> **GOTCHA —** Eigen 的 footprint 几乎全是 template bloat。29 K names 并不意味着 29 K 个不同的 engine logic 函数 — 它们是同一小组 expression templates 按 type、device 逐一实例化的结果。给“真实” Eigen surface 估算大小的重实现者应统计不同 algorithm bodies,而不是 symbol names。

### Intel oneDNN (DNNL)

oneDNN 是该二进制**精确**固定的数学库。offset `0x85BEAE8`(140,241,640)处的 source-path literal `third_party/intel_dnnl/v3_3/src/common` 把版本固定为 **v3.3**,无需推断 — version 被直接编码在 vendored path 中。oneDNN 带来了自己的 JIT assembler **Xbyak**(namespace 在 `0xB38846D`),用来在运行时发出 CPU kernels;因此 Xbyak 的存在是 oneDNN 的结果,而不是独立依赖。组合 footprint 很大(约 25 MB named symbols),反映了 oneDNN 发布的 primitive implementations(convolution、matmul、normalization)的广度。

---

## 压缩 / 加密层

嵌入了四种 compression codecs 和一个 TLS/crypto stack。codec 选择映射到不同消费者:zstd 用于 wheel-internal payload 和 runtime block compression,Brotli 作为 riegeli 的 record codec,snappy 和 zlib 用于 legacy compatibility paths,BoringSSL 用于 transport security。

### zstd

zstd 存在并承担两个角色。runtime-codec 角色显示为完整的 `ZSTD_*` C API — offset `0x270D623C`(655,188,540)处的 `ZSTD_compressBound`、`0x8716BDF` 处的 `ZSTD_createDCtx`,以及 `ZSTD_CCtx`/`ZSTD_c*` context machinery — 通过 google3 的 `third_party/zippy/` wrapper 捆绑。第二个角色是解压 wheel-internal trailing blob,这由自己的页面说明,这里不重复。

> **NOTE —** `.rodata` 中没有留下 `ZSTD_versionNumber` banner 或 `1.5.x` literal,因此无法从字符串固定确切 zstd release — 只能确认 API surface。两种 zstd 用途(runtime codec 与 trailing payload)分别记录;trailing-blob 叙事见 [尾部 zstd Blob](trailing-zstd-blob.md)。

### Brotli 和 snappy

- **Brotli** 通过 riegeli 作为 record-block codec 到达。`0xA2869A1`(170,420,641)处的 `BrotliDecoderDecompress` 和嵌入的 static-dictionary tables(Brotli 发布的大型 `.rodata` arrays)确认它存在;static-dictionary 形式指向 Brotli ≥ 1.1。symbol table 中有 16 个 `BrotliDecoder*` names — 一个薄的、偏 decode 的 surface,与 read-path use 一致。
- **snappy** 作为 `0xA1251E5`(168,972,773)处的 `snappy::` namespace 存在 — trace footprint(约 18 KB),是 legacy fast-compression path。

### zlib

zlib 以一种不寻常的形式存在:主要实现是 **Rust port** `libz-rs`,其 mangled symbols 携带 `zlib_rs` fragment(首个在 `0x852F25E`,139,653,726),同时还有经典 C `deflate`/`inflate`/`crc32` entry points 和 `crc_braid_table` lookup。这是二进制中唯一由 Rust crate 提供 C-ABI compression primitive 的地方;它与 C zlib symbols 共存(而不是取代它们)。

### BoringSSL

crypto 和 TLS stack 是 **BoringSSL**,不是系统 OpenSSL。literal `BoringSSL` banner 出现在 offset `0x857D84C`(139,974,732),并且 C symbol families `EVP_*`、`SSL_*`、`X509_*`、`BN_*`、`RSA_*`、`AES_*`、`SHA256_*`、`ED25519_*`、`X25519_*` 全部存在(9 个 names 直接携带 `BoringSSL` fragment;algorithm families 远大得多)。它 vendored 在 `third_party/openssl/` 下,这是 BoringSSL fork 的常规 google3 位置。BoringSSL 是嵌入式 gRPC stack 背后的 transport-security provider。

---

## 杂项

### ICU

International Components for Unicode 以固定版本存在:offset `0x83FE04C`(138,403,916)处的 data-file marker `icudt78` 和 versioned C++ namespace `icu_78` 一起把它固定为 **ICU 78**(`icudt78` 是 ICU 78 data-bundle 命名约定,`icu_78::` 是 ICU 78 namespace-versioning macro expansion)。ICU 把自己的 trie-based property tables(`propsTrie`、`ucase_props`、`ubidi_props`、`norm2_nfc_data`)作为 `.rodata` blobs 拉入。

### gRPC

gRPC 作为 google3-tip 嵌入,而不是 tagged release。`grpc_core::` namespace(首个在 `0x188C4DE`,25,740,510)以及 **`chaotic_good`** transport 的存在 — 它只存在于 gRPC trunk,直到 v1.71 都没有 tagged release 包含 — 将其放在 **≥ v1.66 dev** 的开发分支上。`grpc_version_string` symbol 存在,但其 build-time `gRPC C++ x.y.z` literal 被 Google 内部 release tagging 替换,因此无法读出 upstream semver。gRPC 的 TLS 由上面的 BoringSSL stack 提供;serialization 由 protobuf stack 提供。

> **Note:** **double-conversion**(wheel 的 `THIRD_PARTY_NOTICES.txt` 中命名的 float↔string library)**未**列入物料清单:对 `double_conversion` / `double-conversion` markers 的直接探测在 `.rodata` 或 symbol table 中没有返回任何内容。它可能只作为没有残留 namespace marker 的内联 `absl::str_format` internals 存在,也可能完全不在此构建中。没有 binary marker 就不计为存在 — 相信二进制,没有 marker 就没有 entry。
>
> **NOTE —** wheel 的 `THIRD_PARTY_NOTICES.txt`(731,537 bytes)命名了比本图谱多得多的组件。notices 文件是一个*法律超集*:它覆盖构建配置中可能被链接的所有内容,包括从这个具体 artifact 中编译出去的组件。上方物料清单中的每个库都独立于 notices 文件、从 binary evidence 重新推导而来;只出现在 `THIRD_PARTY_NOTICES.txt` 中,而二进制里没有 namespace marker 或 path literal 的名称,不计为存在。

---

## 复现

因为二进制未 strip,inventory 无需专门工具即可复现。端到端方法如下:

```bash
# 1. Confirm the dependency posture — six entries, no libstdc++/libgcc.
readelf -d libtpu.so | grep NEEDED

# 2. Pin the build identity (version-bearing banners, with offsets).
grep -aboE 'LLVM version g3[_]+-trunk [0-9a-f]{40}' libtpu.so
grep -aboE 'libtpu_lts_[0-9]+_b_RC[0-9]+'           libtpu.so

# 3. Per-library presence: namespace marker / path literal / banner, at an offset.
grep -aboE 'third_party/intel_dnnl/v3_3/src/common' libtpu.so   # oneDNN v3.3
grep -aboE 'icudt78'                                libtpu.so   # ICU 78
grep -aboE 'EDITION_2024|EDITION_2026'              libtpu.so   # protobuf editions
grep -aboE 'BoringSSL|ZSTD_compressBound|re2::'     libtpu.so   # crypto / codec / regex

# 4. Symbol-family counts: how many names a library contributes.
#    Bucket the demangled symbol pool by namespace prefix.
nm -SC --defined-only libtpu.so | grep -c 'absl::'

物料清单中的每一行都对应一个这样的 probe;上面引用的 file offsets 是字面结果,因此独立验证者会落在同一个 byte 上。

QUIRK — 因为二进制未 strip,诱惑在于绝对相信名称并跳过 path-literal 佐证。对 version claims 不要这样做。re2:: 这样的 namespace 能证明库被链接,但不能说明其版本;只有 path literal(third_party/intel_dnnl/v3_3/...)或 data-file marker(icudt78)能固定 release。存在性和版本是两个不同的 claims,具有两种不同的证据等级 — Confidence column 跟踪两者中较弱的那个。

交叉引用

  • LLVM/MLIR 清单 — 负责两个最大占用者(LLVM trunk 8918319853fbdf…、MLIR dialects);本图谱只把它们列为 line items
  • 二进制取证概览 — 取证工作的 top-level map,以及本图谱在其中的位置
  • 双二进制拆分sdk.so 确实声明 libstdc++.so.6;与 libtpu.so 嵌入的 libc++ 做出相反链接选择
  • 尾部 zstd Blob — wheel-internal zstd payload;本页记录 zstd 的存在,那个页面讲述它的故事
  • 自定义 Sectionsgoogle_mallocprotodesc_cold__rseq_cs 以及其他 build-emitted sections,它们隔离了 tcmalloc 和 protobuf descriptor pool
  • ELF 剖析 — section-size accounting,为 per-library footprint figures 提供框架