Almost every Linux system (and non-Linux system, but this is for Linux) tries to be efficient and reliable by making available a unified set of libraries which any application can use for some standard function. One could compile that content into every executable, but it would be a waste of space if the content of shared libraries could be put on disk once, and each application copy that subset of its program into RAM as it starts. That’s what dynamic libraries are.
As such, a standardized mechanism has to be available for finding and loading that content. Think of the security issues and performance problems if every program implemented its own search for that content. The mechanism for finding and assisting in loading shared content is the linker. The linker itself has a search path, and if a specification for a library is presented to the linker, then it finds the best choice within that search path. If the library in question is not found, then it fails.
ld_library_path is one specification of where to search. You can append to it each compile, or if the content is in a location which will be accessed by several programs, then that directory can be appended to the default search path to make it more convenient.
That’s all “runtime”. At the time you compile a program there are additional problems. The function calls to libraries have to follow a predefined application binary interface scheme (the ABI). This basically matches the called function signature to the library’s provided function signatures. The binary compiled program will work with library calls with a signature that matches the actual location and signature in memory address which the program expects. So during compile time the program sets aside an address span for the library to load at (with the help of the linker). This becomes etched into the binary executable.
In the case of a cross linker it adds yet another issue. The linker program itself needs libraries to operate which are native to the platform. However, the code of the function call is a different ABI than what is native (think of arm64/aarch64 assembler instead of amd64/x86_64 assembler). So now you need:
- The linker’s execution environment library calls for it to function.
- The signature search in arm64/aarch64 ABI.
- A compiler creating an address span compliant with the same arm64/aarch64 ABI.
The linker ties all of that together. If you lack the native libraries (or lack the ability to find those libraries), then the linker fails. If you try to link a library which a compiler is looking for in order to design the load address, then the linker works, but tells you it didn’t find the content. If your linker works, and it finds the library being linked, but it is the wrong signature (in this case due to wrong architecture), then it once more fails to set up linking. If all of these are found, and ABIs are correct, then the binary executable creates that address span to load the signature of that library function.
When you set up your Jetson for development, and have all development content plus libraries on it, then your linker and cross linker have everything they need, but only if it can find them. You still have to tell the PC where the cross architecture libraries are found.