2014-06-26

Embed a binary file into your executable

The GTK+ authors had the good idea of providing an API to create About dialog boxes. It may not look the way you want but it is good enough for ordinary needs and it increases the consistency of your application with the rest of GTK+ based applications.

You just need to call a function called gtk_about_dialog_new() to create the dialog box and then call several setter functions to set the different parts of the dialog box. One of them is the application logo which you set with gtk_about_dialog_set_logo(), giving that function a GdkPixbuf.

The easiest method to set the content of a GdkPixbuf is to use an external image file and call gdk_pixbuf_new_from_file:

bool AboutDlg::SetupUI()
{
	auto dialog = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
	GError* error = nullptr;
	auto logo = gdk_pixbuf_new_from_file("gtk/logo48.png", &error);
	gtk_about_dialog_set_logo(dialog, logo);
	...
}

However this is not a method I am fond of because it creates a weak link between the application executable and the resource file. To ensure my application will always be able to display the logo, I wanted to embed the image file in the executable and give it to the GTK dialog box.

There is no gdk_pixbuf_new_from_memory() function but you can easily write it:

GdkPixbuf* gdk_pixbuf_new_from_memory(const void* data, gsize len, GError** error)
{
	auto inputStream = g_memory_input_stream_new_from_data(data, len, nullptr);
	return gdk_pixbuf_new_from_stream(inputStream, nullptr, error);
}

(Yes, I tend to love that new C++11 auto keyword, not sure yet if I am abusing it :))

This is the first step, now how do you provide the memory buffer to that function? I used to convert the binary file into C++ source code, and then compile this source code into binary again. However, I felt like it was a waste of CPU cycles and my own time to make a binary file of something that was already binary in the first place. Moreover it would complicate the build by adding an intermediate step.

Fortunately there is a way to directly embed the binary file into a .o file, ready to be linked to your executable. You do that by using the build tools coming along with GCC, "ld" (the linker) and "objcopy":

ld -r -o gtk/logo48.png -z noexecstack --format=binary logo48.png.o
objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents logo48.png.o

ld will automatically create two symbols to retrieve the file content in the .o files, named after the file path you gave him. In you C++ code, you just need to reference those symbols:

extern "C" const char _binary_gtk_logo48_png_start[];
extern "C" const char _binary_gtk_logo48_png_end[];

and use them as input for gdk_pixbuf_new_from_memory:

auto logo = gdk_pixbuf_new_from_memory(_binary_gtk_logo48_png_start,
                _binary_gtk_logo48_png_end - _binary_gtk_logo48_png_start, &error);

this method is probably already used by IDEs to embed different kind of resources, but if you like coding quite down to the metal that can be useful to know :)