[SDL] Embed SDL.dll in the .exe

Sylvain Beucler beuc at beuc.net
Thu Sep 27 12:54:18 PDT 2007


On Wed, Sep 26, 2007 at 03:57:30PM +0200, Sylvain Beucler wrote:
> On Wed, Sep 26, 2007 at 03:14:25PM +0200, Gerry JJ wrote:
> > Den Wed, 26 Sep 2007 12:02:28 +0200
> > skrev Sylvain Beucler <beuc at beuc.net>:
> > > I look for a kind of "partially static" compilation, as if SDL was
> > > part of my own code, but I don't have enough knowledge of GCC to know
> > > how that can be done.
> > > 
> > > Do you know if that's possible?
> > 
> > Yes, it's possible, you just have to be a bit more specific when
> > telling the linker what you want to do, since -static affects the
> > entire linking process and not just part of it like you want.  The
> > linker has some options called -Bstatic and -Bdynamic that can be used
> > to do exactly what you want, and you can pass args to the linker with
> > gcc's -Wl arg, with commas in stead of spaces.  So, just surround the
> > libraries you want to link statically with -Wl,-Bstatic and
> > -Wl,-Bdynamic:
> > 
> > gcc stuff -Wl,-Bstatic -lSDL etc -Wl,-Bdynamic
> > 
> > You can do this as many times as you want in a link.  A -Wl,-Bdynamic
> > at the end ensures that gcc won't try to statically link system libs
> > and such.  If you still get a dynamically linked SDL after this, you
> > probably just have to sort out the order of the link args (a request
> > for a lib in a dynamic part after that lib's already been statically
> > linked will pull it in dynamically as well if possible.  If you've got
> > false dependencies interfering with your link, -Wl,--as-needed might
> > help (makes the target only depend on first-level dependencies)).
> 
> Thanks, I'll dig more into those options.
> 
> I'm also having a first clean build by simply adding -lwinmm - I
> thought that --static-libs would give me this kind of dependencies
> automatically :)
> 
> i586-mingw32msvc-gcc -static joytest.c \
>   `/usr/local/cross-tools/i386-mingw32msvc/bin/sdl-config --cflags --static-libs` \
>   -lwinmm

OK, I managed to build the stand-alone executable.

I cross-compiled all SDL libraries manually because the binary
releases don't provide static libs. I also cross-compiled freetype2
for SDL_ttf. There was a couple libtool fixes to do on SDL_gfx as
well.

Then I just add to specific '-static' and '-lfreetype -lwinmm' to gcc.

No need to specfic 'disable-shared' for me when building the
libraries, using '-static' for my .exe takes care of using the static
libs appropriately.


Doing the same thing for GNU/Linux is quite more difficult, because
SDL's dependencies require dlopen(), which in turn requires a
non-static build, so we have to use the scary -Wl,-Bstatic and
-Wl,-Bdynamic options. Moreover, some versions of SDL conflicts with
X11 when compiled statically (SDL_x11dyn.o). And of course, GNU/Linux
has numerous backends which can make your dependencies list grow very
large!

Building a dynamic executable that includes SDL + its dependencies
*except X11* is easy enough though, once you listed the dependencies
and the matching distro packages.


I attach 2 files from my documentation for people interested in doing
the same thing for their project :)
http://git.sv.gnu.org/gitweb/?p=freedink.git;a=blob;f=doc/cross.txt
http://git.sv.gnu.org/gitweb/?p=freedink.git;a=blob;f=doc/static-build.txt


Thanks to the list for the tips :)

-- 
Sylvain
-------------- next part --------------
Statically compiling for GNU/Linux
==================================

Thread "[SDL] Embed SDL.dll in the .exe"
http://lists.libsdl.org/pipermail/sdl-libsdl.org/2007-September/062841.html


Distros compile libSDL with a number of backends (such as X11 and
AAlib for graphics, ALSA and OSS for sounds, etc.) which will trigger
a lot of dependencies.

You may need to statically compile against a manually-compiled libSDL
which fewer backends enabled.

Another solution may be to have SDL use relaytool (check
http://autopackage.org/docs/tutorials/glb-binport.html).


Technical note: maybe use -Wl,--as-needed, it's supposed to reduce the
number of NEEDED symbols (objdump -x src/freedink | grep NEEDED). The
SDL list says it "makes the target only depend on first-level
dependencies".


Compiling with the distro's SDL
===============================

Here's an attempt to list the minimum dependencies to statically
compile against Debian Etch's libsdl1.2-dev:

  ##
  # Dynamic
  ##

  # glibc-related, better keep them dynamic I think
  -ldl # dlopen
  -lpthreads # threads

  # X11 - conflicts with SDL_x11dyn.o if statically
  # compiled. Something to do with ./configure --enable-x11-shared ?
  -lX11

  ##
  # Static
  ##

  # Maths
  -lm

  # AAlib text-mode graphics backend
  aptitude install libaa1-dev
  aptitude install libgpmg1-dev # mouse support
  -laa -lgpm
  # caca text-mode graphics backend
  aptitude install libcaca-dev
  aptitude install libslang2-dev
  aptitude install libncurses5-dev
  -lcaca -lslang -lncurses -lcucul

  # Direct frame buffer graphics backend
  aptitude install libdirectfb-dev
  aptitude install libfusionsound-0.9-25
  -ldirectfb -ldirect -lfusion
  # SVGA graphics backend
  aptitude install libsvga1-dev
  -lvga

  # ALSA sound backend
  aptitude install libasound2-dev
  -lasound
  # aRts sound backend
  aptitude install libarts1-dev
  -lartsc
  # ESD sound backend
  aptitude install libesd0-dev
  -lesd
  # NAS sound backend
  aptitude install libaudio-dev
  -laudio

$ gcc -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT joytest.c \
  -L/usr/lib -Wl,--as-needed -Wl,-Bstatic -lSDL -lcaca -lslang -laa \
  -laudio -lesd -lartsc -lvga -ldirectfb -ldirect -lfusion -lcurses \
  -lcucul -lasound -lgpm -lm -Wl,-Bdynamic -lX11 -ldl -lpthread -o \
  joytest
# --as-needed doesn't seem useful here.
$ ls -lh joytest
-rwxr-xr-x 1 me me 2,5M 2007-09-26 22:24 joytest
$ strip joytest
$ ls -lh joytest
-rwxr-xr-x 1 me me 2,0M 2007-09-26 22:24 joytest
me at dmc:~/freedink/test/sdl$ ldd joytest
        linux-gate.so.1 =>  (0xffffe000)
        libX11.so.6 => /usr/lib/libX11.so.6 (0xb7ee2000)
        libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb7ede000)
        libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7ecc000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7d9b000)
        libXau.so.6 => /usr/lib/libXau.so.6 (0xb7d98000)
        /lib/ld-linux.so.2 (0xb7fe8000)
        libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0xb7d92000)

Here we still have a dynamic executable, with the usual glibc
issues. Compiling with apgcc from Apbuild (Autopackage) may help.


Compiling with a custom simpler SDL
===================================

We can try to recompile SDL ourself and get rid of the X11 conflicts,
allowing for a truly static executable:

$ cd SDL-1.2.12
# not sure if both options are mandatory:
$ ./configure --disable-x11-shared --disable-shared
$ make && make install
...

$ gcc -static -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT \
  joytest.c -L/usr/local/lib -L/usr/lib -lSDL -laudio -lvga -ldirectfb \
  -ldirect -lfusion -lm -lXrandr -lXrender -lX11 -lXau -lXdmcp \
  -lXext -ldl -lpthread -o joytest
/usr/local/lib/libSDL.a(SDL_alsa_audio.o): In function `LoadALSALibrary':
./src/audio/alsa/SDL_alsa_audio.c:139: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/libdirect.a(stream.o): In function `tcp_open':
(.text+0x891): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/libaudio.a(ConnSvr.o): In function `MakeTCPConnection':
/home/steve/debian/nas/nas-1.8/lib/audio/ConnSvr.c:981: warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/libX11.a(x11_trans.o): In function `_X11TransSocketINETConnect':
(.text+0x1da4): warning: Using 'getservbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
$ ls -lh joytest
-rwxr-xr-x 1 me me 4,4M 2007-09-26 23:52 joytest
$ strip joytest
$ ls -lh joytest
-rwxr-xr-x 1 me me 2,3M 2007-09-26 23:52 joytest
$ ldd joytest
        not a dynamic executable
$ ./joytest
# Runs fine until I exit the app:
*** glibc detected *** double free or corruption (out): 0xb7ba4230 ***
Abandon

The resulting binary also crashes with a floating point exception
under Fedora 7 :/

Maybe it can work with a different SDL setup, namely one that wouldn't
use dlopen.


Something in-between works. The improvement over using the distro's
SDL is that X11 is now statically linked:
$ gcc -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT joytest.c \
  -L/usr/local/lib -L/usr/lib -Wl,-Bstatic -lSDL -laudio -lvga \
  -ldirectfb -ldirect -lfusion -lm -lXrandr -lXrender -lX11 -lXau \
  -lXdmcp -lXext -o joytest -Wl,-Bdynamic -ldl -lpthread
$ ls -lh joytest
-rwxr-xr-x 1 me me 3,6M 2007-09-26 23:50 joytest
$ strip joytest
me at dmc:~/freedink/test/sdl$ ls -lh joytest
-rwxr-xr-x 1 me me 1,8M 2007-09-26 23:50 joytest
$ ldd joytest
        linux-gate.so.1 =>  (0xffffe000)
        libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb7f5a000)
        libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7f48000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e17000)
        /lib/ld-linux.so.2 (0xb7f78000)


Try switching from dlopen to relaytool
======================================

Left as exercise to the reader ;)
-------------- next part --------------
Cross-compilation
=================

# Install a cross-compiler version of GCC
aptitude install mingw32


# Prepare directory to store cross-compiled libraries
mkdir -p -m 775 /usr/local/cross-tools/i386-mingw32msvc
cd /usr/local/cross-tools
ln -s i386-mingw32msvc i386-mingw32

##
# Install precompiled SDL binaries
##

VERSION=1.2.12
# Cf. http://libsdl.org/download-1.2.php
wget http://libsdl.org/release/SDL-devel-$VERSION-mingw32.tar.gz
tar xzf SDL-devel-$VERSION-mingw32.tar.gz
mv SDL-$VERSION/* i386-mingw32msvc/
rmdir SDL-$VERSION

# Install precompiled SDL_mixer binaries
VERSION=1.2.8
wget http://libsdl.org/projects/SDL_mixer/release/SDL_mixer-devel-$VERSION-VC8.zip
unzip SDL_mixer-devel-$VERSION-VC8.zip
cp -r SDL_mixer-$VERSION/include/* i386-mingw32msvc/include/SDL/
cp -r SDL_mixer-$VERSION/lib/* i386-mingw32msvc/lib/
rm -rf SDL_mixer-$VERSION/

# Install precompiled SDL_mixer binaries
VERSION=2.0.9
wget http://libsdl.org/projects/SDL_ttf/release/SDL_ttf-devel-$VERSION-VC8.zip
unzip SDL_ttf-devel-$VERSION-VC8.zip
cp -r SDL_ttf-$VERSION/include/* i386-mingw32msvc/include/SDL/
cp -r SDL_ttf-$VERSION/lib/* i386-mingw32msvc/lib/
rm -rf SDL_ttf-$VERSION/


# Cross-compile SDL_gfx (no binaries available)
cd /usr/src
VERSION=2.0.16
wget http://www.ferzkopp.net/Software/SDL_gfx-2.0/SDL_gfx-$VERSION.tar.gz
tar xzf SDL_gfx-$VERSION.tar.gz
cd SDL_gfx-$VERSION
# Refresh and mark as DLL-compliant (patch sent)
rm -f acinclude.m4
sed -i -e 's/-version-info/-no-undefined -version-info/' Makefile.am
autoreconf --force --install --symlink
patch -p1 < /tmp/SDL_gfx-libtool.diff # see below
export SDL_CONFIG=/usr/local/cross-tools/i386-mingw32msvc/bin/sdl-config
./configure --host=i586-mingw32msvc --build=i686-pc-linux-gnu
make
make install


##
# Cross-compile FreeDink
##
cd ~/freedink/
mkdir cross
cd cross/
SDL_CONFIG=/usr/local/cross-tools/i386-mingw32msvc/bin/sdl-config \
  ../configure --host=i586-mingw32msvc --build=i686-pc-linux-gnu
make clean # just in case
make
make install-strip DESTDIR=/tmp/distribute/



Alternative: cross-compile yourself
===================================

Among others, necessary for static builds (not static libraries with
the official releases).

# SDL
VERSION=1.2.12
wget http://libsdl.org/release/SDL-$VERSION.tar.gz
tar xzf SDL-$VERSION.tar.gz
cd SDL-$VERSION
./configure --host=i586-mingw32msvc --build=i686-pc-linux-gnu
make
make install # /usr/local/cross-tools/i386-mingw32/ by default

# SDL_mixer
VERSION=1.2.8
wget http://libsdl.org/projects/SDL_mixer/release/SDL_mixer-$VERSION.tar.gz
tar xzf SDL_mixer-$VERSION.tar.gz
cd SDL_mixer-$VERSION
# Disable MP3 support (not needed in FreeDink and avoid a dependency)
SDL_CONFIG=/usr/local/cross-tools/i386-mingw32msvc/bin/sdl-config ./configure \
  --host=i586-mingw32msvc --build=i686-pc-linux-gnu \
  --disable-music-mp3
make
make install # /usr/local/cross-tools/i386-mingw32/ by default

# FreeType (SDL_ttf dependency)
VERSION=2.3.5
wget http://download.savannah.gnu.org/releases/freetype/freetype-$VERSION.tar.bz2
tar xjf freetype-$VERSION.tar.bz2
cd freetype-$VERSION
./configure --host=i586-mingw32msvc --build=i686-pc-linux-gnu --prefix=/usr/local/cross-tools/i386-mingw32msvc
make
make install

# SDL_ttf
VERSION=2.0.9
wget http://libsdl.org/projects/SDL_ttf/release/SDL_ttf-$VERSION.tar.gz
tar xzf SDL_ttf-$VERSION.tar.gz
cd SDL_ttf-$VERSION
SDL_CONFIG=/usr/local/cross-tools/i386-mingw32msvc/bin/sdl-config \
  FREETYPE_CONFIG=/usr/local/cross-tools/i386-mingw32msvc/bin/freetype-config \
  ./configure --host=i586-mingw32msvc --build=i686-pc-linux-gnu

make
make install # /usr/local/cross-tools/i386-mingw32/ by default


Static build
============

I want to cross-compile statically, to provide a single .exe that
includes SDL and SDL_* :)

- You need to add -lwinmm

i586-mingw32msvc-gcc mousetest.c \
  `/usr/local/cross-tools/i386-mingw32msvc/bin/sdl-config --cflags \
  --static-libs` -lwinmm

- If you have some troubles with SDL_gfx which tries to use
  __imp__SDL_setFramerate, you need a build system patch (see below) -
  maybe it will make it to 2.0.17 ;)

- You need to specify -lfreetype, a dependency of SDL_ttf

You get a 1.1MB standalone stripped executable.

Now we need to integrate the additional library in our build system
and provide ./configure option to build statically (LIBS+="-lfreetype
-lwinmm", LDFLAGS+="-static")


diff -ru SDL_gfx-2.0.16/SDL_framerate.h SDL_gfx-2.0.16.beuc/SDL_framerate.h
--- SDL_gfx-2.0.16/SDL_framerate.h	2006-12-22 13:36:10.000000000 +0100
+++ SDL_gfx-2.0.16.beuc/SDL_framerate.h	2007-09-27 01:46:10.000000000 +0200
@@ -39,21 +39,24 @@
 /* --------- Function prototypes */
 
 #ifdef WIN32
-#ifdef BUILD_DLL
-#define DLLINTERFACE __declspec(dllexport)
-#else
-#define DLLINTERFACE __declspec(dllimport)
+#  ifdef DLL_EXPORT
+#    define SDL_FRAMERATE_SCOPE __declspec(dllexport)
+#  else
+#    ifdef LIBSDL_GFX_DLL_IMPORT
+#      define SDL_FRAMERATE_SCOPE __declspec(dllimport)
+#    endif
+#  endif
 #endif
-#else
-#define DLLINTERFACE
+#ifndef SDL_FRAMERATE_SCOPE
+#  define SDL_FRAMERATE_SCOPE extern
 #endif
 
 /* Functions return 0 or value for sucess and -1 for error */
 
-    DLLINTERFACE void SDL_initFramerate(FPSmanager * manager);
-    DLLINTERFACE int SDL_setFramerate(FPSmanager * manager, int rate);
-    DLLINTERFACE int SDL_getFramerate(FPSmanager * manager);
-    DLLINTERFACE void SDL_framerateDelay(FPSmanager * manager);
+    SDL_FRAMERATE_SCOPE void SDL_initFramerate(FPSmanager * manager);
+    SDL_FRAMERATE_SCOPE int SDL_setFramerate(FPSmanager * manager, int rate);
+    SDL_FRAMERATE_SCOPE int SDL_getFramerate(FPSmanager * manager);
+    SDL_FRAMERATE_SCOPE void SDL_framerateDelay(FPSmanager * manager);
 
 /* --- */
 
diff -ru SDL_gfx-2.0.16/SDL_rotozoom.h SDL_gfx-2.0.16.beuc/SDL_rotozoom.h
--- SDL_gfx-2.0.16/SDL_rotozoom.h	2006-12-22 13:36:10.000000000 +0100
+++ SDL_gfx-2.0.16.beuc/SDL_rotozoom.h	2007-09-27 01:45:58.000000000 +0200
@@ -45,13 +45,16 @@
 /* ---- Prototypes */
 
 #ifdef WIN32
-#ifdef BUILD_DLL
-#define DLLINTERFACE __declspec(dllexport)
-#else
-#define DLLINTERFACE __declspec(dllimport)
+#  ifdef DLL_EXPORT
+#    define SDL_ROTOZOOM_SCOPE __declspec(dllexport)
+#  else
+#    ifdef LIBSDL_GFX_DLL_IMPORT
+#      define SDL_ROTOZOOM_SCOPE __declspec(dllimport)
+#    endif
+#  endif
 #endif
-#else
-#define DLLINTERFACE
+#ifndef SDL_ROTOZOOM_SCOPE
+#  define SDL_ROTOZOOM_SCOPE extern
 #endif
 
 /* 
@@ -65,17 +68,17 @@
 
 */
 
-    DLLINTERFACE SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth);
+    SDL_ROTOZOOM_SCOPE SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth);
 
-    DLLINTERFACE SDL_Surface *rotozoomSurfaceXY
+    SDL_ROTOZOOM_SCOPE SDL_Surface *rotozoomSurfaceXY
     (SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth);
 
 /* Returns the size of the target surface for a rotozoomSurface() call */
 
-    DLLINTERFACE void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth,
+    SDL_ROTOZOOM_SCOPE void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth,
 					  int *dstheight);
 
-    DLLINTERFACE void rotozoomSurfaceSizeXY
+    SDL_ROTOZOOM_SCOPE void rotozoomSurfaceSizeXY
     (int width, int height, double angle, double zoomx, double zoomy, 
      int *dstwidth, int *dstheight);
 
@@ -90,11 +93,11 @@
 
 */
 
-    DLLINTERFACE SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth);
+    SDL_ROTOZOOM_SCOPE SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth);
 
 /* Returns the size of the target surface for a zoomSurface() call */
 
-    DLLINTERFACE void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight);
+    SDL_ROTOZOOM_SCOPE void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight);
 
 
 /* 
@@ -107,7 +110,7 @@
     or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
 */     
     
-    DLLINTERFACE SDL_Surface *shrinkSurface(SDL_Surface * src, int factorx, int factory);
+    SDL_ROTOZOOM_SCOPE SDL_Surface *shrinkSurface(SDL_Surface * src, int factorx, int factory);
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus


More information about the SDL mailing list