14

Under Windows, when I compile C/C++ code in a DLL project in MSVC I am getting 2 files:

  1. MyDll.dll
  2. MyDll.lib

where as far as I understand MyDll.lib contains some kind of pointers table indicating functions locations in the dll. When using this dll, say in an exe file, MyDll.lib is embedded into the exe file during linkage so in runtime it "knows" where the functions are located in MyDll.dll and can use them.

But if I compile the same code under Linux I am getting only one file MySo.so without MySo.a (the equivalent to lib file in Linux) so how does an executable file under Linux knows where the functions are located in MySo.so if nothing is embedded into it during linking?

Benny K
  • 1,957
  • 18
  • 33

5 Answers5

7

The MSVC linker can link together object files (.obj) and object libraries (.lib) to produce an .EXE or a .DLL.

To link with a DLL, the process in MSVC is to use a so-called import library (.LIB) that acts as a glue between the C function names and the DLL's export table (in a DLL a function can be exported by name or by ordinal - the latter was often used for undocumented APIs).

However, in most cases the DLL export table has all the function names and thus the import library (.LIB) contains largely redundant information ("import function ABC -> exported function ABC", etc).
It is even possible to generate a .LIB from an existing .DLL.

Linkers on other platforms don't have this "feature" and can link with dynamic libraries directly.

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • "Linkers on other platforms don't have this feature" - it's easy to implement though (e.g. [Implib.so](https://github.com/yugr/Implib.so) does this for Linux) to achieve delayed loading and other goodies. – yugr Oct 29 '19 at 14:34
  • @yugr: that's why "feature" is in quotes -- it not something you generally want to do and is extra work you must do on Windows. – Chris Dodd Oct 29 '19 at 20:20
  • For completeness, I would add that dynamic linking in MSVC can be performed _implicitly_, i.e., with both the .dll file and an import file, and _explicitly_, i.e. just the .dll without any .lib, but this requires using something like `LoadLibrary`. Reference: [Link an executable to a DLL](https://learn.microsoft.com/en-us/cpp/build/linking-an-executable-to-a-dll) – Breno May 04 '22 at 19:02
2

On Linux, the linker (not the dynamic linker) searches through the shared libraries specified at link time and creates references to them inside the executable. When the dynamic linker loads these executables it loads the shared libraries they require into memory and resolves the symbols, which allows the binaries to be run.

MySo.a, if created, would actually include the symbols to be linked directly into the binary instead of the "symbol lookup tables" used on Windows.

rustyx's answer explains the process on Windows more thoroughly than I can; it's been a long time since I've used Windows.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
  • 1
    "Windows takes a different approach ... specify to the operating system exactly where the symbols are in the DLL" - this contradicts [wiki](https://en.wikipedia.org/wiki/Dynamic-link_library#Symbol_resolution_and_binding), which says that function names are still resolved (at startup or on first call to library function) even when you use ordinals (unless direct address binding is used which noone would does because it forces library users to recompile and redeploy their code whenever library changes). – yugr Oct 29 '19 at 14:28
  • @yugr Removed that part, I was grasping at straws anyway. – S.S. Anne Oct 29 '19 at 15:43
1

The difference you are seeing is more of an implementation detail - under the hood both Linux and Windows work similarly - you code calls a stub function which is statically linked in your executable and this stub then loads DLL/shlib if necessary (in case of delayed loading, otherwise library is loaded when program starts) and (on first call) resolves symbol via GetProcAddress/dlsym.

The only difference is that on Linux the these stub functions (which are called PLT stubs) are generated dynamically when you link your app with dynamic library (library contains enough information to generate them), whereas on Windows they are instead generated when DLL itself is created, in a separate .lib file.

The two approaches are so similar that it's actually possible to mimic Windows import libraries on Linux (see Implib.so project).

yugr
  • 19,769
  • 3
  • 51
  • 96
0

On Linux, you pass MySo.so to the linker and it is able to extract only what is needed for the link phase, putting in a reference that MySo.so is needed at run time.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
-4

.dll or .so are shared libs (linked in runtime), while .a and .lib is a static library (linked in compile time). This is no difference between Windows and Linux.

The difference is, how are they handled. Note: the difference is only in the customs, how are they used. It wouldn't be too hard to make Linux builds on the Windows way and vice versa, except that practically no one does this.

If we use a dll, or we call a function even from our own binary, there is a simple and clear way. For example, in C, we see that:

int example(int x) {
  ...do_something...
}

int ret = example(42);

However, on the asm level, there could be many differences. For example, on x86, a call opcode is executed, and the 42 is given on the stack. Or in some registers. Or anywhere. No one knows that before writing the dll, how it will be used. Or how the projects will want to use it, possible written with a compiler (or in a language!) which doesn't even exist now (or is it unknown for the developers of the dll).

For example, by default, both C and Pascal puts the arguments (and gets the return values) from the stack - but they are doing it in different order. You can also exchange arguments between your functions in the registers by some - compiler-dependent - optimization.

As you see correctly, the Windows custom is that building a dll, we also create a minimal .a/.lib with it. This minimal static library is only a wrapper, the symbols (functions) of that dll are reached through it. This makes the required asm-level calling conversions.

Its advantage is the compatibility. Its disadvantage is that if you have only a .dll, you can have a hard time to figure out, how its functions want to be called. This makes the usage of dlls a hacking task, if the developer of the dll does not give you the .a. Thus, it serves mainly closedness purposes, for example so is it easier to get extra cash for the SDKs.

Its another disadvantage is than even if you use a dynamical library, you need to compile this little wrapper statically.

In Linux, the binary interface of the dlls is standard and follows the C convention. Thus, no .a is required and there is binary compatibility between the shared libs, in exchange we don't have the advantages of the microsoft custom.

peterh
  • 11,875
  • 18
  • 85
  • 108
  • 1
    Please provide a prooflink that stub functions can change argument order. I've never heard about this before and it's hard to believe, given how big performance overhead would be. – yugr Oct 29 '19 at 14:00
  • @yugr A simple register/stack reordering is not a performance overhead. If you use msvc-compiled dlls from msvc-compiled binaries, then obviously not too much will happen, but it could. – peterh Oct 29 '19 at 14:14
  • 1
    We could argue on this but in case you are right it should be easy to provide prooflinks that stub functions are capable non-trivial processing of arguments (and be more than just dummy trampolines). – yugr Oct 29 '19 at 14:21
  • @yugr The stubs have access to the function signatures of the dll, that makes the non-trivial processing trivial. – peterh Oct 29 '19 at 14:27
  • 1
    I only suggest you complete your answer with few prooflinks regarding what import library do (because some of the claims are questionable). – yugr Oct 29 '19 at 14:30