Guide to Making Relocatable Applications
- The problem
- The solution
- Hello World
- Basic usage
- Useful utility functions
- Implementation details
- Autoconf/automake build system integration
- KDE integration
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 hardcoded 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 aplication or library. Highlights:- It's meant to be statically included in your project.
- It's small, only about 15 KB of C source code.
- It's only has two dependancies: libc and libpthread (this latter is used for thread safety; it's optional and can be disabled)
- 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; you can disable BinReloc with one simple macro.
Historical note: BinReloc is the successor of libprefixdb, Autopackage's previous solution to the relocation problem. BinReloc is more automatic and easier to use.
-
-
Note to KDE developers:
As of April 21 2004, BinReloc-like functionality has been added to the KDE CVS, in theKStandardDirs
class. If your application usesKStandardDirs
to 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
Let's begin with a BinReloc "Hello World" tutorial.
Step 1:Download BinReloc from the Developer Tools section of the download page. Extract the archive, and you will find the following files:
|
![]() |
Step 2:Create the folder ~/helloworld. Copy prefix.c and prefix.h to that folder. Open a terminal and change to that folder. |
|
Step 3:Open your favorite text editor and save the following code to ~/helloworld/hello.c:#include <stdio.h> #include "prefix.h" int main () { printf ("The full path of this application is: %s\n", SELFPATH); return 0; } |
![]() |
Step 4:Compile the program and run it:[bash ~/helloworld]$ gcc -DENABLE_BINRELOC hello.c prefix.c -o hello -lpthread [bash ~/helloworld]$ ./hello The full path of this application is: /home/example/helloworld/hello |
Yes, it's this easy! Now let's take a look at what the code does:
- The -DENABLE_BINRELOC argument enables BinReloc support. By default this macro is not defined, and thus BinReloc is disabled.
SELFPATH
is a macro which returns the filename of the application or the library that has called it. If you callSELFPATH
in hello, it will return "/home/example/helloworld/hello". If you callSELFPATH
in libfoo, it will return (for example) "/usr/lib/libfoo.so.0.0.0".
Basic usage
There are more macros available. One of them isPREFIX
.It returns the prefix of your binary. This macro 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".
Other similar macros are:
Macro | Value |
---|---|
BINDIR | PREFIX + "/bin" |
SBINDIR | PREFIX + "/sbin" |
DATADIR | PREFIX + "/share" |
LIBDIR | PREFIX + "/lib" |
LIBEXECDIR | PREFIX + "/libexec" |
ETCDIR | PREFIX + "/etc" |
SYSCONFDIR | Alias for ETCDIR. |
CONFDIR | Alias for ETCDIR. |
LOCALEDIR | DATADIR + "/locale" |
There are also a set of macros that are named exactly the same as the
above macros, but are prepended with BR_. For example, BR_SELFPATH
and
BR_DATADIR
.
These macros are convenience macros for contatenating paths.
BR_SELFPATH("/lib")
is like SELFPATH + "/lib", BR_DATADIR("/data.png")
is like DATADIR + "/data.png", etc.
Note: The return values must not be freed. See Implementation details for information about why.
All of the afore mentioned macros (including SELFPATH
, and PREFIX
) can be disabled by defining BR_NO_MACROS
.
Useful utility functions
BinReloc provides the utility functionbr_strcat
. Most applications that deal with paths will probably need
to concatenate strings somewhere in the source code. For that reason, this function is provided by BinReloc as a utility function.br_strcat
is 100% portable across all operating systems! You can use it even when ENABLE_BINRELOC
isn't defined.
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. This is a convenience function since many applications need to concatenate DATADIR with another path.
Example:
char *datafile;
/* Note: this is kind of redundant because you can also just do
BR_DATADIR("/foo/mydata.txt") but this is just an example ;) */
datafile = br_strcat (DATADIR, "/foo/mydata.txt");
load_data_file (datafile);
free (datafile);
Implementation details
All macros usebr_thread_local_store()
internally, so that you don't have
to worry about freeing the string returned by those macros.
It's defined as follows: const char *br_thread_local_store (char *str);
The first time you call br_thread_local_store()
, str
is saved in an thread
local internal variable (refer to the pthreads manual for more information about
this), or a normal internal variable if pthread support is disabled. str
remains unmodified and is returned as const char *
.
The next times you call this function, the previous value of str
(that was
saved in the internal variable) is freed. Then the current str
is
stored in that variable again, and str is returned as const char *
.
The internal variable is also freed on application exit.
This means that you should not free a string once it is passed to
br_thread_local_store()
, it already does that for you. You also cannot
pass static strings to br_thread_local_store()
.
So what does this mean for all the macros?
Example:
char *foo, *bar, *saved; /* => "/usr/share/mydata.txt" */ foo = BR_DATADIR("mydata.txt"); saved = strdup(foo); /* => "/usr/share/moredata.png"; "/usr/share/mydata.txt" is now freed */ bar = BR_DATADIR("moredata.png"); /* This is wrong! You can't do this because the value of foo is freed by the 2nd BR_DATADIR call */ printf("%s\n", foo); /* This is good; by calling strdup() the value is saved. But you have to free this variable yourself. */ printf("%s\n", saved);
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 source code.
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_BINRELOC
somewhere.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_binreloc
will be set to 'yes' if BinReloc is enabled, or 'no' otherwise. - Copy prefix.c and prefix.h to your source code directory.
-
Add
BINRELOC_CFLAGS
and prefix.c/prefix.h to Makefile.am, as well as additional linker flags:AM_CPPFLAGS = $(BINRELOC_CFLAGS) ... foo_SOURCES = main.c \ prefix.h \ prefix.c foo_LDFLAGS = -lpthread
If the pthread dependancy is unacceptable, then you can disable pthread support by defining
BR_PTHREAD=0
:AM_CPPFLAGS = $(BINRELOC_CFLAGS) -DBR_PTHREAD=0
-
At the beginning of main.c, add:
#include "prefix.h"
Somewhere in main.c:
xml = glade_xml_new (BR_DATADIR("/foobar/glade/main.glade"), NULL, NULL);
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 theKStandardDirs
class. If your application usesKStandardDirs
to 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(strdup(PREFIX));Make sure you use
KGlobal::dirs()
to lookup data files througout your entire program.
If you create new instances of KStandardDirs
, you need the re-add the prefix.
If you want to use KIconLoader
to load icons from whever your program is installed, add this:
KGlobal::iconLoader()->addAppDir(strdup(DATADIR));