Guide to Making Relocatable Applications (BinReloc 2.0)
- The problem
- The solution
- Hello World
- Initialization
- Basic usage
- Glib-style API
- Useful utility functions
- Autoconf/automake build system integration
- KDE integration
- Full API reference
- More examples
- BinReloc 1.x
The problem
Autopackage supports relocation. This means that a package can be installed to any location, like how Win32 installers let you choose a directory. However, most applications are not relocatable. The paths where in they search for data files are usually hardd at compile time.
On Win32, applications and libraries are easily relocatable because
applications and DLLs can use GetModuleFilename() to obtain
their full path.
On Linux however, no easy mechanisms exist. There is no function
equivalent to GetModuleFilename().
For executables, you can still find your full location by resolving the symlink /proc/self/exe, but that won't work for libraries.
The solution
This is why we have developed BinReloc. BinReloc provides an easy-to-use API that uses dynamic linker and kernel magic to find out the full path of your application or library. Highlights:- It's meant to be statically included in your project.
- It's small, only about 20 KB of C source code (I suspect it's only about 10 KB if you remove all the inline documentation comments).
- It has absolutely no dependancies other than libc.
- It's public domain, which means you can do anything you want with the code, including relicensing it under a different license.
- Portability to other operating systems will not be affected: BinReloc will fallback to hardcoded paths if it's running on an operating system which has no executable relocation features. You can also completely disable BinReloc with one simple macro, making your program behave exactly like when you were using hardcoded paths.
- There are two versions of BinReloc: a plain C version, and glib version which even has a glib-style API.
-
-
Note to KDE developers:
As of April 21 2004, BinReloc-like functionality has been added to the KDE CVS, in theKStandardDirsclass. If your application usesKStandardDirsto lookup data files, your application will be automatically relocatable, so using BinReloc is not necessary. Libraries however will not benefit from this, and must use BinReloc directly.
Hello World
Step 1: generate BinReloc source files
Joe downloads the BinReloc SDK from the Developer Tools section of the download page. He extracts the archive in his home folder. A folder called binreloc-2.0 appears.
Joe's Hello World program doesn't use glib, so he wants the plain C version of BinReloc. Joe runs the following commands to generate the BinReloc source files:
Step 2: write the program
Now that Joe has generated the BinReloc source files, he continues with writing a Hello World program:
Step 4: compile & run
Now it is time to compile & run the program:
Yes, it's this easy!
How to disable BinReloc
The -DENABLE_BINRELOC argument enables BinReloc support. BinReloc is only enabled if this macro is defined. Let's take a look at what happens if the macro is not defined:
Initialization
BinReloc must be initialize by calling one of the BinReloc initialization functions:-
If you're using BinReloc in an application, then call
br_init(). The definition is:int br_init (BrInitError *error);This function returns 1 on success, and 0 if BinReloc failed to initialize. If BinReloc failed to initialize, then the error code will be stored in
error. The following error codes are available:typedef enum { /* Cannot allocate memory. */ BR_INIT_ERROR_NOMEM, /* Unable to open /proc/self/maps; see errno for details. */ BR_INIT_ERROR_OPEN_MAPS, /* Unable to read from /proc/self/maps; see errno for details. */ BR_INIT_ERROR_READ_MAPS, /* The file format of /proc/self/maps is invalid; kernel bug? */ BR_INIT_ERROR_INVALID_MAPS, /* BinReloc is disabled. */ BR_INIT_ERROR_DISABLED } BrInitError;If you're using BinReloc in a library, then call
br_init_lib(). The definition is:int br_init_lib (BrInitError *error);This function returns 1 on success, and 0 if BinReloc failed to initialize.
If you don't initialize BinReloc, or if initialization failed, then all BinReloc functions will return the fallback paths, so even if initialization failed, it's not fatal. Initialization will fail if BinReloc is disabled (because
ENABLE_BINRELOCis not defined), or because the application is running on a platform which doesn't support relocating executables (non-Linux platforms).Basic usage
There are more functions besides
br_find_exe(). Here is a list of all relocation functions:Function Returns br_find_exe() The full path of your application or library. br_find_exe_dir() The folder in which your application or library is located. br_find_prefix() The prefix in which your application or library is located. This function assumes that your binary is located inside an FHS-compatible directory structure ($prefix/bin/ or $prefix/lib/). Examples: - Your binary is /usr/bin/foo. It will return "/usr".
- Your library is /usr/local/lib/libfoo.so. It will return "/usr/local".
- Your binary is /Applications/CoolApp2000XP/CoolApp. It will return "/Applications".
br_find_bin_dir() prefix + "/bin" br_find_sbin_dir() prefix + "/sbin" br_find_data_dir() PREFIX + "/share" br_find_locale_dir() PREFIX + "/locale" br_find_lib_dir() PREFIX + "/lib" br_find_libexec_dir() PREFIX + "/libexec" br_find_etc_dir() PREFIX + "/etc" All functions in the above table are declared like this:
char *br_find_something (const char *default_path);default_pathis used as fallback: if the BinReloc isn't initialized, or failed to initialize, then a copy of default_path will be returned. Or if the default_path is NULL, NULL will be returned.Note that the return values of all of the above functions must be freed when no longer necessary, except if the return value is NULL.
Inline documentation
All BinReloc functions have inline documentation! So just take a look in binreloc.c if you need more info about a certain function.Glib-style API
There's also a BinReloc version with a glib-style API. Generating this version is just like generating the normal version:
[joe@localhost /home/joe/binreloc-2.0]$ ./generate.pl glib Source code written to 'binreloc.c' Header written to 'binreloc.h'The API is almost identical to the plain C version, except that it uses glib-style names and glib data types, such as GError. See the full API reference.
Useful utility functions
The plain C version of BinReloc provides some utility functions for modifying strings and paths, because many applications will need such functionality. The glib version doesn't contain these utility functions because glib already has its own utility functions. Note that these utility functions are fully portable, and can be used even when BinReloc is not enabled/initialized.
- char *br_strcat (const char *str1, const char *str2);
- str1: A string. - str2: Another string. - Returns: A newly-allocated string. This string should be freed when no longer needed.
Concatenate str1 and str2 to a newly allocated string.Example:
char *datafile; datafile = br_strcat ("/usr", "/foo/mydata.txt"); load_data_file (datafile); free (datafile);
Autoconf/Automake build system integration
Most Autoconf/Automake projects use macros that define a hardcoded path. Let's take a look at this piece of code as example.In Makefile.am:
INCLUDES = $(LIBGLADE_CFLAGS) \ -DDATADIR=\"$(datadir)\" bin_PROGRAMS = foo foo_SOURCES = main.cIn main.c:
xml = glade_xml_new (DATADIR "/foobar/glade/main.glade", NULL, NULL);
How to use BinReloc:-
Use the special BinReloc Autoconf Macro (binreloc.m4). This file can be found in the BinReloc SDK.
Append the contents of binreloc.m4 to acinclude.m4 (which is in the same folder as configure.in). Create acinclude.m4 if it doesn't exist.
In configure.in, put the command
AM_BINRELOCsomewhere.The AM_BINRELOC macro checks whether BinReloc should be enabled (whether the system supports the feature, whether the user explicitly disabled it, etc). The variable
$br_cv_binrelocwill be set to 'yes' if BinReloc is enabled, or 'no' otherwise. - Copy binreloc.c and binreloc.h to your source code directory.
-
Add
BINRELOC_CFLAGSand binreloc.c/binreloc.h to Makefile.am:AM_CPPFLAGS = $(BINRELOC_CFLAGS) ... foo_SOURCES = main.c \ binreloc.h \ binreloc.c -
At the beginning of main.c, add:
#include "binreloc.h"Somewhere in main.c:
gchar *dir, *file; gbr_init (NULL); dir = br_find_data_dir (DEFAULT_DATA_DIR); file = g_strdup_printf ("%s/foobar/glade/main.glade", dir); g_free (dir); xml = glade_xml_new (file, NULL, NULL); g_free (file);
And that was it! Your configure script will now have a --enable-binreloc=[yes/no/auto] option. The default value for this option is --enable-binreloc=auto, which will automatically check whether BinReloc support is desired. It does so by checking for the following things:
- Whether /proc/self/maps is available.
- Whether the user told configure to use a different location for a specific directory, such as by passing --bindir=/foo/bin.
KDE integration
-
-
Note to KDE developers:
As of April 21 2004, BinReloc-like functionality has been added to the KDE CVS, in theKStandardDirsclass. If your application usesKStandardDirsto lookup data files, your application will be automatically relocatable, so using BinReloc is not necessary. Libraries however will not benefit from this, and must use BinReloc directly.
KGlobal::dirs()->addPrefix(br_find_prefix(DEFAULT_PREFIX));
Make sure you useKGlobal::dirs()to lookup data files througout your entire program. If you create new instances ofKStandardDirs, you need the re-add the prefix.If you want to use
KIconLoaderto load icons from whever your program is installed, add this:KGlobal::iconLoader()->addAppDir(br_find_data_dir(DEFAULT_DATA_DIR));
Full API reference
More examples
The tests folder in the BinReloc source tarball contains more examples about how to use BinReloc.
BinReloc 1.x
The guide for BinReloc version 1.x is still available for those who need it.

