13

Would you mind to leave your comment on this if you have really experienced which relates to the title above? I have tried to make a shared object to be delay loaded with both Clang and GCC on Ubuntu (I actually don't mind which compiler is used), but they do not look really support any delay loading feature (I expected the delay loading feature put a stub in a parent object, which was trying to load another object on demand, at a moment when the functionality is required, but it actually did not). The following commands show that I tried to make libbar.so to be delay loaded against to libfoo.so:

clang bar.c -fPIC -shared -o libbar.so
clang foo.c -Wl,-zlazy,lL'/path/to/where/lib/is',-lbar -o foo

You'll see the libfoo.so raise an exception before entering to the entry if libbar.so does not exist. Anyway, I don't mind if there were any typo in the commands above, but want to know Clang/GCC really supports a delay loading feature or not.

Personally, however, I can't believe if Linux program developers have been required to invoke dlopen() or dlsym() to make a shared object to be delay loaded if Clang/GCC did not support any delay loading feature. It could be okay if the object was written in C, but if it were written in C++, the situation must be completely complicated :(

I believe a solution which is realized with a help from compiler or linker is the best because I have successfully done it with Windows and Mac OS. So I feel it would be a natural reaction where citizen wants to dream to have a delay loading feature even on Clang/GCC. I'd also be appreciate if you have any comment on my feeling.

PS. I know Solaris supports a delay loading feature but that's not a way to go for me because I will don't develop anything on it.

Anyway, thank you very much in advance.

Doofah
  • 384
  • 3
  • 12
  • Yes, this can be done on Linux. – kec May 06 '14 at 03:37
  • @ kec, thanks. Do you mean GCC/Clang can make a shared object delay loaded by passing some options to them? Does any official document describe how to use it? It'd be nice to know if you could explain why I got the result above. – Doofah May 06 '14 at 03:42
  • Actually, after re-reading, I'm not sure I know what you are talking about. Are you talking about dlopen()? What do you want to happen? – kec May 06 '14 at 03:46
  • @ kec, sorry for making you confused. I know a delay loading feature can be realized with dlopen() and dlsum() but **don't want to invoke them in the source code**. Instead, I want to realize by **just passing some option flags to a linker or compiler** and **keep the source code as is**. – Doofah May 06 '14 at 03:52
  • Hm...lazy binding is the default. Whether or not the loading is actually lazy, I'm not sure, but is there really a practical difference? If you really want to know, you could use `strace` to find out when the library is `mmap`'ped in. What are you trying to achieve? – kec May 06 '14 at 03:57
  • @ kec, thanks again. I want make a shared object to be delay loaded by passing some option flags to a compiler or linker. Have you ever made a DLL delay loaded on Windows? MSVC++ supports it and you can make it just passing **/DELAYLOAD:DLL_NAME** to the linker. I'm expecting to have such a way even on Linux because I don't want to make the source code messy and looks not easy to invoke dlsym() for a method written in C++. I hope this makes clear for your understanding :) – Doofah May 06 '14 at 04:02
  • @ kec, in addition, I want to make the delay loaded object will not loaded until its function is actually called. If you create a couple of simple programs and invoke the shell commands above, then libfoo.so will automatically search and load libber.so immediately before entering to the libfoo.so's entry point. That's not what I want to have. – Doofah May 06 '14 at 04:07
  • I have never created one with Windows. I looked into it a bit more. The lazy option to the linker is lazy binding, but...there's really not that much difference. A library isn't really "loaded" per se, it's just mapped in with `mmap`. So I don't understand what the code looking messy has to do with delayed loading. – kec May 06 '14 at 04:11
  • I had to look up what delay loading is, because I'd never heard of it. Sounds horrible. Anyway, my understanding of the way things work on Linux is that all symbols must be resolved by the dynamic linker at run-time. Lazy loading (the default) means that the DLL won't actually be opened and mapped into memory until or unless you call a function from it (including constructors of C++ static objects), but the dynamic linker still needs to know that the libraries exist, or your programme won't run. So as I understand it, you're probably out of luck. – Tristan Brindle May 06 '14 at 04:12
  • Okay, so it seems that Linux does not support "true" lazy loading, only lazy binding. I'm not sure if that is good enough for you or not. And I still don't understand your use case, or why you need this. There might be other ways of achieving the same effect. – kec May 06 '14 at 04:16
  • @ kec, thanks for keeping conversation :) Sorry, perhaps I could have been sticking with wrong idea, but could you explain why libfoo.so raised an exception which inform libbar.so was not found **before invoking it's functionality**? If libbar.so had been actually made delay loaded, the exception should have been raised at the moment when it's function was called. That's why I have suspected that Clang/GCC does not support any delay loading feature. What do you think? – Doofah May 06 '14 at 04:18
  • @ kec, another short question: So do you invoke dlopen() or dlsym() if you need to make a shared object to be delay loaded? Even if the object is written in C++? – Doofah May 06 '14 at 04:20
  • @ Tristan, thank you for leaving your comment. Perhaps the confusion must be coming from a difference on its terminology. Anyway, your comment seems to imply GCC/Clang can postpone to resolve the symbols under the name of 'lazy binding' but it must know where the library is at least. If it is true, as you told above, probably I'd be out of luck :( – Doofah May 06 '14 at 04:28
  • Yes, you can use dlopen() to load a shared library in later. I have done that before. The shared library can contain C++ code. What was the error message that you got with your example? – kec May 06 '14 at 04:33
  • @ kec, thanks, as you can see in the harmic's comment below, I passed a option flags to make the symbols delay bound, but not delay loading of the library (although I was expecting to make the object to be delay loaded). So I got an exception which inform that libbar.so was not found. I've understand what I had done from his comment. Thanks for your help anyway :) – Doofah May 06 '14 at 04:42
  • If you got a message saying that the library was not found, then that's a different problem. You need to set your LD_LIBRARY_PATH env var. – kec May 06 '14 at 04:43
  • @ kec, sorry, I told you incorrect information. Please excuse me. Yes, I have actually set LD_LIBRARY_PATH and confirmed a segmentation fault (not an exception which says the shared object not found) happened at a function of the lazy bound library is called. Thanks. – Doofah May 06 '14 at 04:55

2 Answers2

19

This is more a question of functionality provided by the run time linker, ld-linux.so.

This linker does support lazy binding of symbols, but not lazy loading of libraries. What this means is that each of the shared objects which an executable requires are loaded when the program starts, but the symbols within the program are not resolved to the loaded libraries until they are first referenced.

The reason for this is performance. A library may contain many thousands of symbols for functions that never get called in a single execution of a program. Resolving them all would be a waste of time.

For this reason, if a library does not contain the expected symbols, you can get 'undefined symbol' errors well after the program has started running, but if a library is missing altogether, you will get an error before the program starts.

The -zlazy option which you are quoting controls lazy symbol binding only. In fact it is enabled by default (at least for GCC, I did not check for clang).

The only way to have a library loaded after program startup, for example in response to some command line option, configuration or other dynamic condition, is to call dlopen.

You might want to look around for a good plugin framework - for references see:

Community
  • 1
  • 1
harmic
  • 28,606
  • 5
  • 67
  • 91
  • @ harmic, thank you for leaving your comment. It answered all things what I had incorrectly done. I'll read the references you posted above. Thank you again! :) – Doofah May 06 '14 at 04:44
  • @ harmic, the reason I hesitated to use the dlopen way was it looked very complicated to implement. I have actually confirmed it works with a very simple C++ program (it calls a member function of a class which was obtained by a class pointer returned from dlsym()). But if I tried to apply the technique to a third party library (it's more and more complicated compared to the tutorial program I tested), everything goes crazy... – Doofah May 06 '14 at 04:50
  • @user3591878 hard to know what the problem is from that description! I suggest you post that as a separate question. Include the smallest sample program you can that causes the 'crazy' behavior. – harmic May 06 '14 at 05:10
  • @ harmic, please never mind my comment above and thank you for your suggestion. I'll try to separate comments per question unit and put a sample code if it helps the readers to understand the issue :) – Doofah May 06 '14 at 06:00
  • One can build delay loading on top of existing linker functionality by generating stub library which will call `dlopen` on first call (see another answer). – yugr Feb 13 '18 at 16:21
  • I also wonder if MSVC `/DELAYLOAD` has a specific kernel support in Windows. I don't know the performance of their approach, but it's something that one can immagine to be doable without any specific OS support, like @yugr says – ceztko Mar 21 '21 at 09:27
  • @ceztko MSVC's `/DELAYLOAD` does not need specific kernel support, it's just a bunch of wrapping code around `LoadLibrary` API (analog of `dlopen` in Windows world). – yugr Mar 21 '21 at 09:40
5

Linux does not support delay loading of libraries out of the box but it can easily be implemented using the same mechanism that is used on Windows i.e. by linking against small stub static library which dlopens main shared library on first call to any of it's functions.

You can implement such stub library by hand, via custom script tailored for your project or use Implib.so to generate it automatically:

$ clang bar.c -fPIC -shared -o libbar.so
$ implib-gen.py libbar.so
$ clang foo.c libbar.tramp.S libbar.init.c -o foo
yugr
  • 19,769
  • 3
  • 51
  • 96
  • I love your efforts but we should have something similar directly in the linker. I think LLVM LLD guys could be more open minded than GNU ld ones. Did you try to contact them? – ceztko Mar 21 '21 at 09:27
  • @ceztko You are right and actually in Sun's shlibs (after which GNU shlibs are implemented) delayed loading did work. So it's unclear why it wasn't implemented in GNU libc. There have been [discussions in mailing list](https://sourceware.org/legacy-ml/libc-help/2013-02/msg00017.html) which didn't get any response. I just took the path of least resistance with my tool. – yugr Mar 21 '21 at 09:43
  • I just read the message you linked and in both replies people basically didn't understand the feature :) – ceztko Mar 21 '21 at 10:30
  • @ceztko LLD might indeed work out, I'll try to talk to them. – yugr Mar 21 '21 at 13:39
  • I got subscribed to [llvm-dev](https://lists.llvm.org/mailman/listinfo/llvm-dev) mailing list, which is currently moderated but they still manually accept all subscriptions. It's the right place where to ask for LLD. I can send an introductory post, also citing your project, asking for a `/DELAYLOAD` equivalent in LLD and showing some interest. After you can intervene from a technical point of view, since of your expertise. Tell me if it's ok for you – ceztko Mar 22 '21 at 16:22
  • @ceztko Sure, you can cc me from start (my gmail is tetra2005) but please mention them to use reply-to-all (the list is too active so I don't want to subscribe). – yugr Mar 22 '21 at 18:45