Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Statically link libfuse.so.2 into runtime #45

Closed
probonopd opened this issue Nov 27, 2015 · 11 comments
Closed

Statically link libfuse.so.2 into runtime #45

probonopd opened this issue Nov 27, 2015 · 11 comments
Labels

Comments

@probonopd
Copy link
Member

probonopd commented Nov 27, 2015

Is it possible to statically link libfuse.so.2 into runtime and still have a binary small enough to be embedded into the header padding of an ISO file?

This was suggested by @frerich in the discussion thread to Linus Torvalds' "This is just very cool" blog post.

If we attempt this, we should do so in a separate feature branch.

EDIT: Done, you can skip this thread right to #45 (comment)

I tried adding

set(BUILD_SHARED_LIBS ON)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(LINK_SEARCH_START_STATIC TRUE)
set(LINK_SEARCH_END_STATIC TRUE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")

to the CMakeLists.txt but this did not work, as I get

[ 83%] Linking C executable runtime
/usr/bin/ld: attempted static link of dynamic object `libfuseiso.so'
collect2: error: ld returned 1 exit status
CMakeFiles/runtime.dir/build.make:96: recipe for target 'runtime' failed
make[3]: *** [runtime] Error 1
CMakeFiles/Makefile2:292: recipe for target 'CMakeFiles/runtime.dir/all' failed
make[2]: *** [CMakeFiles/runtime.dir/all] Error 2
CMakeFiles/Makefile2:304: recipe for target 'CMakeFiles/runtime.dir/rule' failed
make[1]: *** [CMakeFiles/runtime.dir/rule] Error 2
Makefile:196: recipe for target 'runtime' failed
make: *** [runtime] Error 2
@probonopd probonopd added the idea label Nov 29, 2015
@RazZziel
Copy link

RazZziel commented Dec 1, 2015

According to #5 (comment), the dynamic runtime was 28K, so there's around 4K left to get to 32,768 unused bytes, although that was before removing Elficon. On my system, compiling runtime produces a 30828 binary right now, which leaves even less space.

Besides, at least in Archlinux there's no static libfuse library to link against:

$ yaourt -Ql fuse
(...)
fuse /usr/lib/libfuse.so
fuse /usr/lib/libfuse.so.2
fuse /usr/lib/libfuse.so.2.9.4
(...)

according to https://patchwork.ozlabs.org/patch/223175/ this is because

libfuse uses dl functions exclusively so it can't be used for static builds

and

If you've just got libfuse.a in staging and fuse-using programs link/use it it breaks anyway because libfuse hardcodes dlopen

so it looks like it's not feasible

@probonopd
Copy link
Member Author

musl is an alternative c standard library (a leaner replacement for glibc). The musl team is more static-linking-friendly than the glibc team. Also, binaries compiled with musl are supposed to be significantly smaller.

http://sourceforge.net/p/fuse/mailman/message/31326476/

Chat on #musl:

<probono> I want to get rid of: linux-vdso.so.1 libfuse.so.2 libpthread.so.0 libglib-2.0.so.0 libz.so.1 libc.so.6 libdl.so.2 /lib64/ld-linux-x86-64.so.2 
<landley> probono: in theory?
* tridactyla hat die Verbindung getrennt (Ping timeout: 244 seconds)
<probono> in practice, landley 
<probono> let's say "pragmatically small"
<landley> You won't get of linux-vdso.so.1, that's a virtual library added by the linux ABI.
<probono> that's ok then
<probono> the binary goes into the header padding of an ISO so every byte counts ;-)
<nsz> it should be possile
<nsz> *possible
<landley> If you have static versions of libz.a, libfuse.a, and so on.
<probono> I guess I have to compile these first using musl?
<nsz> glib is hard to static link probably
<landley> libglib is a special case horror from the FSF they'd put effort into making work, but it's crap.
(...)
<landley> You can't mix static and dynamic linking in the same program.
<landley> You really don't want to go there.
<landley> Imagine if your library uses malloc, and statically links in a copy.
<landley> Then another library dynamically links in glibc and uses its malloc.
<landley> Each has its own start-of-heap pointer.
<probono> from a user point of view, that'd be useful: dynamically link stuff that can be assumed comes with the base OS and statically link the rest
<landley> One calls strdup() or something, returns a pointer to memory, and it gets freed with the _other_ one's heap.
<landley> At _best_ that's a memory leak. At worst it's heap corruption.
<probono> but I can imagine it's asking for trouble
<landley> They tried to make it work. Can't be done.
<landley> It's the same problem with making dlopen() work from static binaries.
<landley> Mixing static and dynamic. Two heaps.
(...)
<landley> Oh static works great if you statically link the whole binary.
<landley> Musl supports static linking very well.
<landley> Endorses it even.
(...)
<landley> Again, you'll want to wait for dalias to get back and ask him.
<landley> You can statically link qt. People have done it.
<landley> Or you could use a fully musl system. Ask the alpine linux guys about that.
<landley> You can statically link C++ resources. You just need to make sure your compiler supplies the .a versions of things.

@RazZziel
Copy link

If the only problem is dlopen(), we can try removing dlopen() from libfuse

https://groups.google.com/forum/#!topic/exfat/2rLN-yhLnoU

As far as I can see, libfuse uses dlopen() to load some plugins when
the "modules=..." mount parameter is specified. I have no idea what it
is for, but fuse-exfat does not use this parameter. So I think you can
just remove this feature from libfuse.

I haven't deeply understood the comments below yet, but looks like they were trying to remove libdl rather than removing dlopen. But then there's the pthread issue, which seems to be problematic to link statically as well

http://stackoverflow.com/a/10390911/674713

While linking libpthread.a into a shared library is theoretically possible, it is a really bad idea. The reason is that libpthead is part of glibc, and all parts of glibc must match exactly, or you'll see strange and un-explainable crashes.

So linking libpthread.a into your shared library will:

  • Cause your program to crash when moved to a machine with a different version of glibc
  • Cause your existing program to crash when your current machine's glibc is upgraded, but your module is not re-linked against updated libpthread.a.

Spare yourself aggravation, and don't do that.

@probonopd
Copy link
Member Author

So I agree that libdl should not be linked statically. A dependency on libdl is entirely OK. Btw, I don't think the statement "You can't mix static and dynamic linking in the same program" above is correct. I have done it. just provide .a libraries instead of .so libraries and they will be linked staticallly while the rest is still linked dynamically.

Also see http://www.murga-linux.com/puppy/viewtopic.php?p=370694&sid=f925b44171e8372377c00c6208e4814c#370694

@RazZziel
Copy link

Yes, I do it as well on industrial C++ software, as many hardware providers will only provide you with SDKs that only include a header and a static library, so you can integrate their products without actually disclosing their internal workings.

I think what they actually meant is that you can't mix static and dynamic implementations of the same symbol. For instance, you can't have part of your program using the system's malloc() implementation, and other part of the program using a static version of malloc(), because you may into memory corruption if those two implementations differ significantly. It may work on your system, but not on other system. dlopen() also seems to be that kind of function, and pthreads as well. Besides, linking a static .a library is completely fine, as long as that library uses the system's malloc() and not a statically linked version.

@probonopd
Copy link
Member Author

Good news: It is possible.

cc CMakeFiles/runtime.dir/runtime.c.o -o runtime -rdynamic libfuseiso.a libisofs.a /home/me/libfuse.a -lpthread -lglib-2.0 -lz with libfuse.a obtained from http://snapshot.debian.org/archive/debian/20070101T000000Z/pool/main/f/fuse/libfuse-dev_2.5.3-4.1_amd64.deb

Bad news: The resulting binary is too large to fit into the ISO header, even if compressed with upx.

@probonopd
Copy link
Member Author

probonopd commented Feb 18, 2017

With type 2 AppImages, we don't have this tight restriction on AppImage runtime size anymore, so we might reconsider it. What do people think?

@probonopd
Copy link
Member Author

probonopd commented Feb 18, 2017

Just for fun, here is an AppImage that has libfuse statically linked in.
f72fb3c

./appimagetool-x86_64.AppImage --appimage-offset
333608

Runtime is now 3x as large as before. Do you think it is worth it?
Also the resulting runtime would then be GPLv2 rather than MIT, https://github.com/libfuse/libfuse/blob/master/COPYING

appimagetool-x86_64.AppImage.zip

Note that this breaks ldd which now thinks the runtime is a static binary, probably something is not quite right with this line:
-L../xz-5.2.3/build/lib -Wl,-Bdynamic -lpthread -lz -Wl,-Bstatic -lfuse -llzma -Wl,-Bdynamic -ldl
Probably this can be fixed (experimentation needed).

@probonopd
Copy link
Member Author

probonopd commented Feb 18, 2017

I still think that #123 may be the better approach, but I'd need help on implementing it. @frerich what do you think?

@TheAssassin
Copy link
Member

Since we've implemented #123 already, I think we can close this feature proposal now.

@probonopd
Copy link
Member Author

Agree!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants