LLVM-OFFLOAD-BINARY(1)

๐Ÿ“ฆ version 23.1.3, in centos-stream 9

๐Ÿ’… llvm-offload-binary โ€” the LLVM Offload Binary Packager

okay so bestie, welcome to llvm-offload-binary(1), aka the LLVM Offload Binary Packager ๐ŸŽ€ โ€” it's a whole little tool and honestly it's giving main character energy for people who bundle device code. let's get into it โœจ


๐Ÿ˜Œ NAME

llvm-offload-binary โ€” LLVM Offload Binary Packager. that's the whole name, icon of brevity ๐Ÿซถ


๐ŸŒธ SYNOPSIS

llvm-offload-binary [options] [input files...]

literally that's it. options, then input files, no cap ๐Ÿ’…


๐Ÿ’– DESCRIPTION

so llvm-offload-binary is a utility for bundling multiple device object files into a single binary container ๐ŸŽ€. and then?? the resulting binary can be embedded into the host section table to form a fat binary containing offloading code for different targets. it's giving "one bag, many outfits" ๐Ÿ’…. and conversely โ€” yes we love a conversely โ€” it can also extract previously bundled device images from offload binaries. it does both. versatility ate ๐Ÿ”ฅ

when extracting images: if you don't specify any --image filters, all offload images are automatically extracted, each with descriptive filenames โœจ. but the second you do provide --image filters, only the matching images get extracted. picky on purpose, as she should ๐Ÿ’…

the tool also supports the nested OffloadBinary format โ€” meaning device images can be wrapped in an inner OffloadBinary container ๐Ÿช†. when extracting, the tool automatically detects and unwraps nested OffloadBinary images, making the format transparent to users. so you never have to think about it. she handles it. periodt ๐Ÿ‘‘

and the binary format itself? it begins with the magic bytes 0x10FF10AD, followed by a version and size ๐Ÿ”ฎ. each binary contains its own header, which lets tools locate offloading sections even when they get merged by a linker โ€” iconic resilience honestly. each offload entry includes metadata such as the device image kind, producer kind, and key-value string metadata ๐Ÿงš. and multiple offloading images are concatenated to form a fat binary. stacking them like a charm bracelet ๐Ÿ“


๐ŸŒˆ EXAMPLE

okay this part is so satisfying, watch:

# Package multiple device images into a fat binary:
$ llvm-offload-binary -o out.bin \
      --image=file=input.o,triple=nvptx64,arch=sm_70

# Extract all offload images from an executable (no filters):
$ llvm-offload-binary in.bin
# Output:
# Extracted: in-nvptx64-nvidia-cuda-sm_70.0.bc
# Extracted: in-spirv64-intel-unknown.0.spv

# Extract only SPIR-V images using filters:
$ llvm-offload-binary in.bin --image=triple=spirv64-intel
# Output:
# Extracted: in-spirv64-intel-unknown.0.spv

# Extract filtered images to a specific file:
$ llvm-offload-binary in.bin --image=file=output.bc,arch=sm_70

# Extract filtered images to an archive:
$ llvm-offload-binary in.bin --image=file=output.a,triple=nvptx64 --archive

see how the last three are literally the same command with different filters? the range ๐Ÿ˜ญ๐Ÿ’œ


โœจ OPTIONS โœจ

  • --archive โ€” when extracting from an input binary, write all extracted images into a static archive instead of separate files ๐ŸŽ€. one tidy little bag instead of a pile of loose files.
  • --image=<<key>=<value>,...> โ€” specify a set of arbitrary key-value arguments describing an image ๐ŸŒธ. commonly used optional keys include arch (e.g. sm_70 for CUDA) and triple (e.g. nvptx64-nvidia-cuda).

when bundling, this option specifies images to include in the output binary ๐Ÿซถ. when extracting, this option acts as a filter โ€” only images matching the specified keys are extracted. and if no --image options are provided during extraction, all images are automatically extracted with descriptive filenames โœจ.

when extracting from a static archive, the member key restricts the output to a single archive member, for cases when --archive is insufficient ๐Ÿ’… , e.g. --image=member=foo.o,triple=nvptx64,arch=sm_70,file=out.o.

  • -o <file> โ€” write output to <file> ๐Ÿ“. when bundling, this specifies the fat binary filename. when extracting, this specifies the archive or output file destination. one flag, two jobs, employed and thriving ๐Ÿ‘‘
  • --help, -h โ€” display available options ๐Ÿ’–. use --help-hidden to show hidden options ๐Ÿ‘€ (yes there are secret ones, iykyk).
  • --help-list โ€” display a list of all options ๐ŸŒŸ. use --help-list-hidden to show hidden ones ๐Ÿ™ˆ.
  • --version โ€” display the version of the llvm-offload-binary executable โญ.
  • @<FILE> โ€” read command-line options from response file <FILE> ๐Ÿฆ‹. put your options in a file like the organized fairy you are.

๐Ÿ”ฎ BINARY FORMAT

the binary format is marked by the magic bytes 0x10FF10AD, followed by a version number ๐Ÿ”ฅ. each created binary contains its own header, and that's what allows tools to locate offloading sections even after linker operations such as relocatable linking ๐Ÿ’€ (linkers are chaotic, the header keeps its head). conceptually, this binary format is a serialization of a string map and an image buffer. that's the whole vibe check ๐Ÿงš


๐Ÿ‘‘ OFFLOADING BINARY HEADER

okay so the original had this whole gorgeous little grid and i'm gonna give it to you field by field because every single one matters, no skipping ๐Ÿ’–:

  • uint8_t โ€” magic โ€” the magic bytes for the binary format (0x10FF10AD) ๐Ÿ”ฎ
  • uint32_t โ€” version โ€” version of this format (currently version 1) ๐ŸŽ€
  • uint64_t โ€” size โ€” size of this binary in bytes ๐Ÿ“
  • uint64_t โ€” entry offset โ€” absolute offset of the offload entries in bytes ๐Ÿ“
  • uint64_t โ€” entry size โ€” size of the offload entries in bytes ๐Ÿ“

and each offload entry describes a bundled image along with its associated metadata ๐ŸŒธ โ€” like a little name tag for every image in the bag ๐Ÿซถ


(and that's where part 1 wraps... but we're not done, bestie โ€” more coming ๐Ÿ’…)

๐ŸŽ€ OFFLOADING ENTRY TABLE ๐ŸŽ€

Okay bestie, this is the glow-up table that tells you what each entry is serving ๐Ÿ’…. Every entry has a type, an identifier, and a description โ€” and yes, the way that every byte is accounted for is lowkey iconic โœจ.

  • uint16_t โ€” image kind โ€” the kind of the device image (e.g. bc, cubin) ๐Ÿซถ
  • uint16_t โ€” offload kind โ€” the producer of the image (e.g. openmp, cuda) ๐ŸŒธ
  • uint32_t โ€” flags โ€” generic flags for the image ๐ŸŽ€
  • uint64_t โ€” string offset โ€” absolute offset of the string metadata table ๐Ÿ’–
  • uint64_t โ€” num strings โ€” number of string entries in the table โœจ
  • uint64_t โ€” image offset โ€” absolute offset of the device image in bytes ๐Ÿฆ‹
  • uint64_t โ€” image size โ€” size of the device image in bytes ๐Ÿ’œ

The entry table refers to both a string table and the raw device image itself. The string table provides arbitrary key-value metadata. No cap, that's the whole vibe of this little structure ๐ŸŒˆ.


๐ŸŒธ OFFLOADING STRING ENTRY ๐ŸŒธ

And here's the string-entry moment, because metadata deserves main character energy too ๐Ÿ˜Œ. Each string entry has two fields, both uint64_t:

  • uint64_t โ€” key offset โ€” absolute byte offset of the key in the string table ๐Ÿ”‘
  • uint64_t โ€” value offset โ€” absolute byte offset of the value in the string table ๐Ÿ’ซ

The string table is a collection of null-terminated strings stored in the image. Offsets allow string entries to be interpreted as key-value pairs, enabling flexible metadata such as architecture or target triple. It's giving "little table, big flexibility" fr fr ๐Ÿงšโœจ.


๐Ÿฆ‹ IMAGE KIND ๐Ÿฆ‹

The enumerated values for image kind and offload kind are:

Here's the IMAGE KIND name / value / description breakdown, bestie ๐Ÿ‘‘:

  • IMG_None โ€” 0x00 โ€” no image information provided ๐Ÿ’€
  • IMG_Object โ€” 0x01 โ€” the image is a generic object file ๐Ÿ“ฆ
  • IMG_Bitcode โ€” 0x02 โ€” the image is an LLVM-IR bitcode file โœจ
  • IMG_Cubin โ€” 0x03 โ€” the image is a CUDA object file ๐Ÿ’…
  • IMG_Fatbinary โ€” 0x04 โ€” the image is a CUDA fatbinary file ๐Ÿ”ฅ
  • IMG_PTX โ€” 0x05 โ€” the image is a CUDA PTX file ๐ŸŒŸ

๐Ÿ’ซ OFFLOAD KIND

Okay so every offload binary carries a little "who made me" tag called the offload kind, and it's literally just a number. Here's the whole roster, no skips, bestie:

  • OFK_None = 0x00 โ€” no offloading info was provided at all. It's giving blank slate, nothing to see here. ๐Ÿซฅ
  • OFK_OpenMP = 0x01 โ€” the producer was OpenMP offloading. Team OpenMP, rise. ๐Ÿ”ฅ
  • OFK_CUDA = 0x02 โ€” the producer was CUDA. Green team represent. ๐Ÿ’š
  • OFK_HIP = 0x03 โ€” the producer was HIP. AMD girlies stay winning. โค๏ธ
  • OFK_SYCL = 0x04 โ€” the producer was SYCL. One API, one vibe. ๐Ÿ’™

That's the whole list โ€” if you see a value past 0x04, that's not on the guest list, iykyk. ๐Ÿ‘‘


๐ŸŽ€ COMMON WORKFLOWS

These are the six little rituals, in order, exactly as written. Screenshot them, bookmark them, tattoo them, whatever works. โœจ

๐ŸŒธ Workflow 1: Explore Executable Contents

Extract all embedded offload images to see what's inside:

$ clang++ -fopenmp -fopenmp-targets=nvptx64,spirv64-intel app.cpp -o myapp
$ llvm-offload-binary myapp
# Output:
# Extracted: myapp-nvptx64-nvidia-cuda-sm_70.0.bc
# Extracted: myapp-spirv64-intel-unknown.1.spv

Literally just dump everything out and go "ohhh so that's what was hiding in there." Open the hood, no cap. ๐Ÿ•ต๏ธโ€โ™€๏ธ

๐Ÿฆ‹ Workflow 2: Extract Specific Target

Extract only images for a specific target:

$ llvm-offload-binary myapp --image=triple=spirv64-intel
# Output:
# Extracted: myapp-spirv64-intel-unknown.0.spv

Filtering? We love a selective queen. Only the spirv64-intel image gets pulled, everything else stays tucked in. ๐Ÿ’…

๐ŸŒˆ Workflow 3: Create Device Image Archive

Extract filtered images into a static archive:

$ llvm-offload-binary myapp --image=file=nvptx.a,triple=nvptx64 --archive
$ ar t nvptx.a
# Shows extracted CUDA images

So like โ€” the --archive flag means instead of loose files everywhere (chaos, we hate her), everything lands in a tidy static archive. Then ar t lists it back at you like a little server handing you your order. ๐Ÿ“ฆ

โญ Workflow 4: Validate SPIR-V

Extract and validate SPIR-V binaries:

$ llvm-offload-binary myapp --image=triple=spirv64-intel
$ spirv-val myapp-spirv64-intel-unknown.0.spv
$ spirv-dis myapp-spirv64-intel-unknown.0.spv -o kernel.spvasm

Validating first, disassembling second โ€” that's the correct order, bestie, don't @ me. spirv-val does the vibe check, spirv-dis drops the readable kernel.spvasm so you can actually look at it. ๐Ÿ”ฌ

๐Ÿงš Workflow 5: Bundle Multiple Targets

Create a fat binary from multiple device images:

$ clang++ -fopenmp -fopenmp-targets=nvptx64 --offload-device-only kernel.cpp -o kernel_nvptx.bc
$ clang++ -fopenmp -fopenmp-targets=spirv64-intel --offload-device-only kernel.cpp -o kernel_spirv.bc
$ llvm-offload-binary -o bundle.bin \
    --image=file=kernel_nvptx.bc,triple=nvptx64,arch=sm_70 \
    --image=file=kernel_spirv.bc,triple=spirv64-intel

Two separate device-only compiles, then one -o bundle.bin to rule them all. That's a fat binary and honestly? It ate. ๐Ÿฝ๏ธ

๐Ÿ‘‘ Workflow 6: Extract and Rebundle

Extract images from one binary and rebundle with modifications:

$ llvm-offload-binary old_app
$ llvm-offload-binary -o new_bundle.bin \
    --image=file=old_app-nvptx64-nvidia-cuda-sm_70.0.bc,triple=nvptx64,arch=sm_70 \
    --image=file=new_kernel.bc,triple=nvptx64,arch=sm_80

Out with the sm_70, in with the sm_80 โ€” glow-up achieved, main character energy confirmed. ๐Ÿ’–


๐Ÿ”— SEE ALSO

Go read those if you're nosy (affectionate). ๐Ÿซถ


โœ๏ธ AUTHOR

Maintained by the LLVM Team (https://llvm.org/). Shoutout to the maintainers, they're carrying the whole ecosystem on their backs fr fr. ๐Ÿ˜ญ๐Ÿ’œ


2003-2026, LLVM Project


Okay that's the whole page, bestie โ€” go forth and offload responsibly. ๐Ÿ’…โœจ

Peace out from page 23 of LLVM-OFFLOAD-BINARY(1), dated 2026-10-06, brought to you by the LLVM Project. Slay, bye! ๐ŸŽ€๐Ÿฆ‹๐ŸŒˆ

๐Ÿ“ฆ where this page comes from