Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Last active May 12, 2025 04:08
Show Gist options
  • Save mmozeiko/ed9655cf50341553d282 to your computer and use it in GitHub Desktop.
Save mmozeiko/ed9655cf50341553d282 to your computer and use it in GitHub Desktop.
Include binary file with gcc/clang
#include <stdio.h>
#define STR2(x) #x
#define STR(x) STR2(x)
#ifdef _WIN32
#define INCBIN_SECTION ".rdata, \"dr\""
#else
#define INCBIN_SECTION ".rodata"
#endif
// this aligns start address to 16 and terminates byte array with explict 0
// which is not really needed, feel free to change it to whatever you want/need
#define INCBIN(name, file) \
__asm__(".section " INCBIN_SECTION "\n" \
".global incbin_" STR(name) "_start\n" \
".balign 16\n" \
"incbin_" STR(name) "_start:\n" \
".incbin \"" file "\"\n" \
\
".global incbin_" STR(name) "_end\n" \
".balign 1\n" \
"incbin_" STR(name) "_end:\n" \
".byte 0\n" \
); \
extern __attribute__((aligned(16))) const char incbin_ ## name ## _start[]; \
extern const char incbin_ ## name ## _end[]
INCBIN(foobar, "binary.bin");
int main()
{
printf("start = %p\n", &incbin_foobar_start);
printf("end = %p\n", &incbin_foobar_end);
printf("size = %zu\n", (char*)&incbin_foobar_end - (char*)&incbin_foobar_start);
printf("first byte = 0x%02hhx\n", incbin_foobar_start[0]);
}
@Ionizing
Copy link

Ionizing commented Mar 24, 2023

This solution works fine on both Linux and Windows, but failed on macOS. I modified a bit and make it run on macOS (x86).

  • Support macOS (x86)
  • Add optional prefix for variable name
#include <stdio.h>

#define STR2(x) #x
#define STR(x) STR2(x)

#ifdef __APPLE__
#define USTR(x) "_" STR(x)
#else
#define USTR(x) STR(x)
#endif

#ifdef _WIN32
#define INCBIN_SECTION ".rdata, \"dr\""
#elif defined __APPLE__
#define INCBIN_SECTION "__TEXT,__const"
#else
#define INCBIN_SECTION ".rodata"
#endif

// this aligns start address to 16 and terminates byte array with explict 0
// which is not really needed, feel free to change it to whatever you want/need
#define INCBIN(prefix, name, file) \
    __asm__(".section " INCBIN_SECTION "\n" \
            ".global " USTR(prefix) "_" STR(name) "_start\n" \
            ".balign 16\n" \
            USTR(prefix) "_" STR(name) "_start:\n" \
            ".incbin \"" file "\"\n" \
            \
            ".global " STR(prefix) "_" STR(name) "_end\n" \
            ".balign 1\n" \
            USTR(prefix) "_" STR(name) "_end:\n" \
            ".byte 0\n" \
    ); \
    extern __attribute__((aligned(16))) const char prefix ## _ ## name ## _start[]; \
    extern                              const char prefix ## _ ## name ## _end[];

INCBIN(incbin, foobar, "binary.bin");

int main()
{
    printf("start = %p\n", &incbin_foobar_start);
    printf("end = %p\n", &incbin_foobar_end);
    printf("size = %zu\n", (char*)&incbin_foobar_end - (char*)&incbin_foobar_start);
    printf("first byte = 0x%02hhx\n", incbin_foobar_start[0]);
}

The code above can also be found in my fork.

@eXory2024
Copy link

Thank you so much! ❤️ I was looking for a way to make it work on macOS! The way described on http://elm-chan.org/junk/32bit/binclude.html unfortunately does not work on macOS but yours does!

@novicearts
Copy link

is there an equivalent version of this approach for wasm/emscripten?

It looks like if I try to use it directly it errors out on the INCBIN_SECTION being .rodata. Currently trying to find possible alternatives for the sectionn name, but still searching

@mmozeiko
Copy link
Author

mmozeiko commented May 12, 2025

Nowadays on clang I would not bother with .incbin inline assembly anymore, and instead use #embed directive.

See wasm example here: https://godbolt.org/z/Ke6bjxfTr

@novicearts
Copy link

novicearts commented May 12, 2025

Ah that's neat! Did not know that existed before. I'll be looking at using the #embed directive.

That being said, there are 2 reasons I'm still looking at the .incbin approach, mostly for reference at this point:

  1. In my studio I have an older build of emscripten, and I have that version locked for some projects I'm working on. I'll update the compiler most likely after I'm done, but in the meantime I dont want to break those projects should something go wrong with migration.
  2. I'm working on a library that is ideally c99 compatible, b/c I've got legacy platforms I support. Thus far .incbin seems to work very well in those areas while keeping the build process lean.

That being said, it's possible that inline wasm support may just be really limited, so I might have to try alternate methods, like objcopy or the like.

@mmozeiko
Copy link
Author

mmozeiko commented May 12, 2025

You don't need to switch to C23. Clang supports #embed as extension even in C99 or whatever standard. It'll just be extra warning, but that you can silence it with pragma or just globally.

If you want to figure out how to make .incbin work, then you can enable assembly output with -S for some const uint8_t data[] = { ... } array and then look how variable declaration looks in assembly, then replicate the same thing in inline asm. Or just simply look in godoblt - there you need turn off "Directives" under "Filter".

@novicearts
Copy link

ohh those are some good tips. I'll try them out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment