LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Tool like Dependency Walker for Linux that can show which funtion is imported from which .so file? (https://www.linuxquestions.org/questions/linux-newbie-8/tool-like-dependency-walker-for-linux-that-can-show-which-funtion-is-imported-from-which-so-file-4175735155/)

DerPropeller 03-21-2024 07:31 PM

Tool like Dependency Walker for Linux that can show which funtion is imported from which .so file?
 
On Windows, tools like Dependency Walker, or its modern rewrite Dependencies, can be used to show which DLL files (shared libraries) a given EXE or DLL file depends on. These tools even show recursive dependencies. And, most important, these tools nicely show which exact function is imported from which exact DLL file. Is there any tool that can provide this information on Linux?

https://i.imgur.com/IsTmC1q.png

I know that a somewhat similar functionality is available on Linux via the ldd tool. Or, even better, lddtree from the pax-utils package, which shows recursive dependencies as a tree diagram. Those tools are nice, but they only show which .so files the given executable (or shared library) depends on. They do not show which exact functions are imported from each of those .so files! Meanwhile, objdump -T (or, alternatively, nm -D) can be used to show a list of all functions that the given executable (or shared library) imports from shared libraries, but it does not show from which exact .so file each function is imported.

Is there any tool available on Linux that shows both information combined :confused:

I searched on Google for a Linux tool that can show which exact function is imported from which exact .so file, but couldn't find anything. People either recommend using ldd or objdump -T (or, alternatively, nm -D). Unfortunately, as explained before, those tools only show half of the required information, but not the full picture. Since both information are available separately, it shouldn't be too hard to combine them... :rolleyes:

jmgibson1981 03-21-2024 08:45 PM

I haven't messed with either tool directly but if you cannot find something specific you can probably script something.

DerPropeller 03-21-2024 08:54 PM

Quote:

Originally Posted by jmgibson1981 (Post 6491137)
but if you cannot find something specific you can probably script something.

But how? ldd only outputs a list of the required .so files, with no information about the functions that are supposed to be imported from each file.

Meanwhile, objdump (or nm) only outputs a list of the required function names, with no information about the .so file from which each function is supposed to be imported.

So, there appears to be no way to match up the separate outputs :confused:

In theory it should be possible to parse the import table directly from the executable file, but I have no real idea how to do approach this...

___ 03-21-2024 09:09 PM

Maybe like https://www.linuxquestions.org/quest...4/#post6490781

DerPropeller 03-21-2024 09:16 PM

Quote:

Originally Posted by ___ (Post 6491143)

I don't think so. If I understand it right, the script in question only shows the dependencies, in terms of .so files, differentiating between direct and indirect dependencies, just like lddtree from pax-utils.

It apparently does not show the functions that are to be imported from each .so file at all.

Again, what I'm looking for is a list of all the .so files that are required by an executable (or shared library), and for each of those .so files the list of functions that are supposed to be imported from the individual file.

TTBOMK, in the executable file, there is an "import table" that tells the loader which specific function shall be imported from which specific .so file. It must be possible to dump that table... :scratch:

teckk 03-22-2024 03:59 PM

Best way to do that is, get the source code for the software that you are using.
Then look at the tree and see what it is doing.

What are you trying to accomplish? .so files are compiled code. You need to get the source tree and walk through it. Are you trying to reverse engineer an executable? That would be the real hard way to go.

DerPropeller 03-22-2024 04:14 PM

Quote:

Originally Posted by teckk (Post 6491299)
Best way to do that is, get the source code for the software that you are using.
Then look at the tree and see what it is doing.

No, looking at the source code does not help, I think :scratch:

In the source code you'd see that a certain function gets called by the program. That function probably is declared in some header (.h) file. But that's it. You can not see at all, in the source code, which library actually provides the implementation of the function! It is the linker that figures out where each required function is located. If any required function can not be located by the linker, it will error out and the build process fails. Also, it is the linker that generates the "import table" of the executable file. At runtime, the loader reads the "import table" to know which specific function must be imported from which specific shared library (.so file).

Quote:

Originally Posted by teckk (Post 6491299)
What are you trying to accomplish? .so files are compiled code. You need to get the source tree and walk through it. Are you trying to reverse engineer an executable? That would be the real hard way to go.

Given an existing executable file (or shared library), I want to check/analyze its dependencies.

On Windows, we can simply use Dependency Walker to see which DLL files a given executable file depends on, and also which specific functions are imported from each specific DLL file :thumbsup:
https://i.imgur.com/IsTmC1q.png

On Linux, ldd shows which .so files a given executable file depends on, but it does not tell you which functions are imported!

Meanwhile, objdump or nm shows a list of all the functions that a given executable file imports, but it does not tell you from which specific .so file each of those functions is imported :(

mysterious X 03-23-2024 09:35 AM

Quote:

Originally Posted by DerPropeller (Post 6491127)
know that a somewhat similar functionality is available on Linux via the ldd tool. Or, even better, lddtree from the pax-utils package, which shows recursive dependencies as a tree diagram. Those tools are nice, but they only show which .so files the given executable (or shared library) depends on. They do not show which exact functions are imported from each of those .so files! Meanwhile, objdump -T (or, alternatively, nm -D) can be used to show a list of all functions that the given executable (or shared library) imports from shared libraries, but it does not show from which exact .so file each function is imported.

Is there any tool available on Linux that shows both information combined :confused:

Quote:

Originally Posted by DerPropeller (Post 6491145)
TTBOMK, in the executable file, there is an "import table" that tells the loader which specific function shall be imported from which specific .so file. It must be possible to dump that table... :scratch:

Not quite. Linux uses the ELF (Extensible Linking Format) format for executable files (and shared libraries).

In the ".dynamic" section of the ELF file, there is, amongst other things, one DT_NEEDED entry for each shared library that is needed. The DT_NEEDED entry just contains (a pointer to) the name of the shared library (*.so) file that needs to be loaded, but it does not indicate at all which symbols are to be "imported" from that shared library. To know which symbols need to be imported, we have to look at the ".dynsym" section.

The ".dynsym" section of the ELF file, contains the dynamic linking symbol table. Any symbols that are "undefined" (U) here will need to be resolved by the dynamic linker/loader. In other words, those "undefined" symbols are the ones that will be "imported" at runtime, e.g. from shared libraries. But the dynamic linking symbol table does not indicate at all which shared library contains a particular "undefined" symbol.

So, ldd pretty much dumps the DT_NEEDED entries from the ".dynamic" section. And nm -D dumps the ".dynsym" section; filter lines that start with U to get "imported" symbols.

Alternatively, readelf -d and readelf --dyn-syms provide the same information. Use readelf -a to dump all sections at once.

In conclusion, I think the information you are seeking can not be determined by just looking at the ELF file, because it is not specified from which shared library each "undefined" symbol will be resolved at runtime.

DerPropeller 03-23-2024 02:25 PM

Now, that is very interesting information :doh:

After reading your post, I conducted a little test: I created a test program that calls foo() from libfoo.so and then calls bar() from libbar.so. As expected, ldd showed that my executable depends on both, libfoo.so and libbar.so. Then I changed my libfoo.so to additionally contain a "fake" function named bar(), which prints a different text than the original one. The executable file and the libbar.so were totally unchanged. Only the libfoo.so was re-built. Guess what, the program now suddenly calls the "fake" bar() from libfoo.so and does not use libbar.so at all! Interestingly, ldd still shows both library dependencies.

From this result I can only conclude that, indeed, Linux does not store which specific function shall be imported from which specific shared library, but only stores a "unspecific" list of shared libraries that need to be loaded. Apparently, the dynamic linker/loader searches all the loaded shared libraries and simply imports the required function from the first library where it is found – which is very different from Windows!

At least this explains why no tool shows the information that I was looking for. It just doesn't exist in Linux. Lesson learned :rolleyes:

DerPropeller 03-24-2024 10:13 AM

Small update:

After reading some more on the topic, I found that while Linux executables apparently do not store from which specific shared library a certain function shall be imported, we can still see from which specific shared library each function actually does get imported, at runtime, by setting the LD_DEBUG environment variable to "bindings":
Code:

$ LD_DEBUG=bindings ./a.out
      [...]
      4541:        binding file ./a.out [0] to /home/propeller/Desktop/libbar.so [0]: normal symbol `bar'
      4541:        binding file ./a.out [0] to /home/propeller/Desktop/libfoo.so [0]: normal symbol `foo'
      4541:        calling init: /home/propeller/Desktop/libfoo.so
      4541:        calling init: /home/propeller/Desktop/libbar.so
      [...]
Hello from foo!
Hello from bar!

Versus the modified libfoo.so file:
Code:

$ LD_DEBUG=bindings ./a.out
      [...]
      4722:        binding file ./a.out [0] to /home/propeller/Desktop/libfoo.so [0]: normal symbol `bar'
      4722:        binding file ./a.out [0] to /home/propeller/Desktop/libfoo.so [0]: normal symbol `foo'
      4722:        calling init: /home/propeller/Desktop/libfoo.so
Hello from foo!
Fake bar() funnction has been invoked !!!


DerPropeller 03-28-2024 03:15 PM

For what it's worth:
https://github.com/dEajL3kA/dependencies.py


All times are GMT -5. The time now is 09:40 AM.