Skip to content

Instantly share code, notes, and snippets.

@Sturmlilie
Last active November 13, 2020 00:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sturmlilie/1965f10c35b56149ee35c5700fe88803 to your computer and use it in GitHub Desktop.
Save Sturmlilie/1965f10c35b56149ee35c5700fe88803 to your computer and use it in GitHub Desktop.
rpath shenanigans on win32

(Draft; this ended up working for a big Qt5 app, but not a simple test exe for whatever reason, so beware)

When creating a stand alone application on windows, the problem of bundling dynamically linked libraries arises, because by default, the linker only checks a system wide path and the folder containing the executable. Most people don't concern themselves with this too much and just dump all their dlls into the folder together with the exe, but this makes for a really miserable user experience, especially when the number of dlls is more than a handful. Ideally, we want to stash all libraries into a subdirectoy, everything else (assets etc.) into separate subdirectories, and only leave the executable at the top. On Linux, this would be very easy to achieve: we could just pass -rpath=$ORIGIN/lib to ld, and the dynamic linker would always check the "lib" folder relative to the executable when resolving .so's. In fact, this mechanism is the bread-and-butter of independently distributed proprietary games on Linux. But this is actually doable on Windows as well, and it surprised me that it's so rarely known and used.

I will illustrate an example of an executable "foo.exe" depending on "bar.dll" and "baz.dll". First, we need to determine the name of our lib subfolder. I think this should be a unique one (not just "lib"), so let's go with "foolibs". Now we will create an "assembly manifest", which is just an xml file that references our dlls:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <noInheritable></noInheritable>
    
    <assemblyIdentity type="win32"
                      name="foolibs"
                      version="1.0.0.0"
                      processorArchitecture="x86"/>
    
    <file name="bar.dll"></file>
    <file name="baz.dll"></file>
</assembly>

Note how the element references the name of our lib folder in the "name" tag, and our dlls are enumerated using elements. Everything else is not unique to our application (you might want to change the "processorArchitecture" tag if you created a 64bit executable for whatever reason). This file must be named "foolibs.manifest" and put into foolibs together with the dlls.

Next we need to create the application manifest, which will actually reference foolibs.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="Foo.Application"
                    version="1.0.0.0"
                    processorArchitecture="x86"/>
                    
  <description>Foo.Application</description>
  
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32"
                        name="foolibs"
                        version="1.0.0.0"
                        language="*"
                        processorArchitecture="x86"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

As you can see, the same element describing foolibs resides below /. I am not if the element is needed, but it doesn't seem to hurt. We can name this file anything we want, because it will be compiled into the executable using Microsoft's rc/windres mechanism, which I'm not going to explain in detail as it can be googled. But let's go with "foo.manifest" for now.

We create "foo.rc" and add this entry:
1 RT_MANIFEST foo.manifest

The integer identifier is not really important from what I have gathered. If you already have an rc file (e.g. for executable icons) just append the above entry.

Compile the rc (I use Fedora's mingw32-* packages):
i686-w64-mingw32-windres foo.rc -o foo.res --output-format=coff

And link the resulting foo.res with the rest of your object files:
i686-w64-mingw32-gcc foo.c foo.res -o foo.exe -lbar -lbaz

When you run "foo.exe", the linker should now automatically look in "foolibs" and find "bar.dll" and "baz.dll" without further intervention. Note that this only works on Windows XP and up.

Full credits go to Carsten Teibes (https://github.com/carstene1ns) for teaching me about this method.

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