解析CUDA FATBIN格式

本文探讨了CUDA程序编译后生成的特殊二进制文件——fatbin,其中包含.nv_fatbin,__nv_module_id,和nvFatBinSegment等段。nv_fatbin用于存储运行时只读的元数据,__nv_module_id是描述信息,而nvFatBinSegment记录CUDA程序在fatbin中的布局。文章还揭示了fatbin如何包含多个ELF文件,以及它们在CUDA执行流程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文档:

https://pdfs.semanticscholar.org/5096/25785304410039297b741ad2007e7ce0636b.pdf

CUDA Pro Tip: Understand Fat Binaries and JIT Caching | NVIDIA Technical Blog

cuda二进制文件中到底有什么 - 知乎

NVIDIA CUDA Compiler Driver

NVIDIA CUDA Compiler Driver

https://docs.nvidia.com/cuda/pdf/CUDA_Compiler_Driver_NVCC.pdf

cuda二进制文件中到底有些什么_真是使用gpu的二进制文件-优快云博客

分析过程

首先编译一个CUDA程序,编译结果为标准的ELF文件,所以可以用binutils工具分析,查看可执行文件的section有那些。

$ readelf -S a.out 
There are 35 section headers, starting at offset 0xa0fd8:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000270  00000270
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000000028c  0000028c
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             00000000000002ac  000002ac
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         00000000000002d0  000002d0
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002f0  000002f0
       0000000000000eb8  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000000011a8  000011a8
       0000000000000796  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000000193e  0000193e
       000000000000013a  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000001a78  00001a78
       0000000000000150  0000000000000000   A       6     8     8
  [ 9] .rela.dyn         RELA             0000000000001bc8  00001bc8
       00000000000037e0  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000000053a8  000053a8
       0000000000000e10  0000000000000018  AI       5    27     8
  [11] .init             PROGBITS         00000000000061b8  000061b8
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000000061d0  000061d0
       0000000000000970  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000006b40  00006b40
       0000000000000008  0000000000000008  AX       0     0     8
  [14] .text             PROGBITS         0000000000006b50  00006b50
       00000000000547fe  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         000000000005b350  0005b350
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         000000000005b360  0005b360
       000000000000916c  0000000000000000   A       0     0     32
  [17] .nv_fatbin        PROGBITS         00000000000644d0  000644d0
       0000000000000d28  0000000000000000   A       0     0     8
  [18] __nv_module_id    PROGBITS         00000000000651f8  000651f8
       000000000000000f  0000000000000000   A       0     0     8
  [19] .eh_frame_hdr     PROGBITS         0000000000065208  00065208
       0000000000002a8c  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000067c98  00067c98
       00000000000093e8  0000000000000000   A       0     0     8
  [21] .gcc_except_table PROGBITS         0000000000071080  00071080
       0000000000000010  0000000000000000   A       0     0     1
  [22] .tbss             NOBITS           0000000000271da0  00071da0
       0000000000000298  0000000000000000 WAT       0     0     8
  [23] .init_array       INIT_ARRAY       0000000000271da0  00071da0
       0000000000000098  0000000000000008  WA       0     0     8
  [24] .fini_array       FINI_ARRAY       0000000000271e38  00071e38
       0000000000000008  0000000000000008  WA       0     0     8
  [25] .data.rel.ro      PROGBITS         0000000000271e40  00071e40
       0000000000002a70  0000000000000000  WA       0     0     32
  [26] .dynamic          DYNAMIC          00000000002748b0  000748b0
       0000000000000260  0000000000000010  WA       6     0     8
  [27] .got              PROGBITS         0000000000274b10  00074b10
       00000000000004f0  0000000000000008  WA       0     0     8
  [28] .data             PROGBITS         0000000000275000  00075000
       0000000000000058  0000000000000000  WA       0     0     32
  [29] .nvFatBinSegment  PROGBITS         0000000000275058  00075058
       0000000000000030  0000000000000000  WA       0     0     8
  [30] .bss              NOBITS           00000000002750a0  00075088
       00000000000014c0  0000000000000000  WA       0     0     32
  [31] .comment          PROGBITS         0000000000000000  00075088
       00000000000003ef  0000000000000000           0     0     1
  [32] .symtab           SYMTAB           0000000000000000  00075478
       000000000000f0c0  0000000000000018          33   1561     8
  [33] .strtab           STRTAB           0000000000000000  00084538
       000000000001c952  0000000000000000           0     0     1
  [34] .shstrtab         STRTAB           0000000000000000  000a0e8a
       000000000000014e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

出现了三个普通ELF文件中没有的段:

它们分别是.nv_fatbin,__nv_module_id和.nvFatBinSegment三个段.进一步分析,.nv_fatbin和__nv_module_id属性是READONLY,而.nvFatBinSegment则是DATA,说明运行时.nv_fatbin和__nv_module_id为只读,而.nvFatBinSegment支持读写,说明CUDA的代码保存在nv_fatbin中,而nvFatBinSegment则放在数据段。

查看program header确实如此,.nv_fatbin __nv_module_id被放在只读R属性的segment,而.nvFatBinSegment和BSS,.data之类放在一起,是可读写的。

CUDA运行时的秘密就隐藏在这三个段中,分别解析,先从简单的入手,__nv_module_id只有15个字节,其内容是一组字符串,内容如其名,是描述信息,执行时的作用可能不大。

nvFatBinSegment 段文档中这样描述:

根据描述,nvFatBinSegment中保存的是CUDA程序的元数据,元数据是数据的数据,究竟是什么数据的数据,看来只能是描述CUDA程序的数据了,而前面分析CUDA程序在fatbin中,所以nvFatBinSegment段应该就是对fatbin的描述。

根据文档的描述,nvFatBinSegment 每6个word(24字节)为一组,其中每组第三个word为代码段在fatbin中的地址,并且nvFatBinSegment中的描述和fatbin要始终对应一致。

按照文档解析nvFatBinSegment,一共有两组,分别描述了fatbin中的两个代码区,地址能够对应的上。

仔细观察发现,每个GROUP内似乎都有一个ELF文件的,将其抠出来:

如下命令进行ELF hack,可以看到扣出的两个文件可以被解析,说明其确实是ELF文件,并且是CUDA ARCH:

objcopy -O binary --only-section .nv_fatbin a.out fat.bin
dd if=./fat.bin bs=1 skip=80 count=696 of=fat1.elf
dd if=./fat.bin bs=1 skip=776 of=fat2.elf

查看符号信息,fat1.elf没有有效内容,而FAT2.ELF中则存在写的KERNEL ADD函数的代码段:

反编译elf,fat1.elf没有指令,fat2.elf中包含了ADD的机器指令,从反编译的二进制可以看到这里的CUDA ISA是8个字节,也就是64BIT指令。

分析KERNEL流程:

$ nvdisasm -cfg fat2.elf|dot -ocfg.png -Tpng

文档中对FATBIN的描述,可见其确实包含多组ELF文件,至于为何多组ELF,多组的目的是什么,目前还是不甚了解。


End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值