Browse Source

DynamicLoader: Add an option to list all ELF loaded dependencies

This actually allows us to re-introduce the ldd utility as a symlink to
our dynamic loader, so now ldd behaves exactly like on Linux - it will
load all dynamic dependencies for an ELF exectuable.

This has the advantage that running ldd on an ELF executable will
provide an exact preview of how the order in which the dynamic loader
loads the executable and its dependencies.
Liav A 1 year ago
parent
commit
40a8b009db

+ 19 - 2
Userland/DynamicLoader/main.cpp

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
+#include <AK/LexicalPath.h>
 #include <AK/ScopeGuard.h>
 #include <AK/ScopeGuard.h>
 #include <Kernel/API/POSIX/sys/stat.h>
 #include <Kernel/API/POSIX/sys/stat.h>
 #include <Kernel/API/VirtualMemoryAnnotations.h>
 #include <Kernel/API/VirtualMemoryAnnotations.h>
@@ -48,6 +49,12 @@ static ErrorOr<int> open_executable(StringView path)
     return checked_fd;
     return checked_fd;
 }
 }
 
 
+static int print_loaded_libraries_callback(struct dl_phdr_info* info, size_t, void*)
+{
+    outln("{}", info->dlpi_name);
+    return 0;
+}
+
 static int _main(int argc, char** argv, char** envp, bool is_secure)
 static int _main(int argc, char** argv, char** envp, bool is_secure)
 {
 {
     Vector<StringView> arguments;
     Vector<StringView> arguments;
@@ -56,14 +63,22 @@ static int _main(int argc, char** argv, char** envp, bool is_secure)
         arguments.unchecked_append({ argv[i], strlen(argv[i]) });
         arguments.unchecked_append({ argv[i], strlen(argv[i]) });
 
 
     bool flag_dry_run { false };
     bool flag_dry_run { false };
+    bool flag_list_loaded_dependencies { false };
     Vector<StringView> command;
     Vector<StringView> command;
     StringView argv0;
     StringView argv0;
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
 
 
     args_parser.set_general_help("Run dynamically-linked ELF executables");
     args_parser.set_general_help("Run dynamically-linked ELF executables");
     args_parser.set_stop_on_first_non_option(true);
     args_parser.set_stop_on_first_non_option(true);
-    args_parser.add_option(flag_dry_run, "Run in dry-run mode", "dry-run", 'd');
-    args_parser.add_option(argv0, "Run with custom argv0", "argv0", 'E', "custom argv0");
+
+    if (LexicalPath::basename(arguments[0]) == "ldd"sv) {
+        flag_list_loaded_dependencies = true;
+        flag_dry_run = true;
+    } else {
+        args_parser.add_option(flag_dry_run, "Run in dry-run mode", "dry-run", 'd');
+        args_parser.add_option(flag_list_loaded_dependencies, "List all loaded dependencies", "list", 'l');
+        args_parser.add_option(argv0, "Run with custom argv0", "argv0", 'E', "custom argv0");
+    }
     args_parser.add_positional_argument(command, "Command to execute", "command");
     args_parser.add_positional_argument(command, "Command to execute", "command");
     // NOTE: Don't use regular PrintUsageAndExit policy for ArgsParser, as it will simply
     // NOTE: Don't use regular PrintUsageAndExit policy for ArgsParser, as it will simply
     // fail with a nullptr-dereference as the LibC exit function is not suitable for usage
     // fail with a nullptr-dereference as the LibC exit function is not suitable for usage
@@ -97,6 +112,8 @@ static int _main(int argc, char** argv, char** envp, bool is_secure)
         argv[0] = const_cast<char*>(argv0.characters_without_null_termination());
         argv[0] = const_cast<char*>(argv0.characters_without_null_termination());
 
 
     auto entry_point = ELF::DynamicLinker::linker_main(move(main_program_path), main_program_fd, is_secure, envp);
     auto entry_point = ELF::DynamicLinker::linker_main(move(main_program_path), main_program_fd, is_secure, envp);
+    if (flag_list_loaded_dependencies)
+        ELF::DynamicLinker::iterate_over_loaded_shared_objects(print_loaded_libraries_callback, nullptr);
     if (flag_dry_run)
     if (flag_dry_run)
         return 0;
         return 0;
     _invoke_entry(command.size(), argv, envp, entry_point);
     _invoke_entry(command.size(), argv, envp, entry_point);

+ 5 - 0
Userland/Libraries/LibELF/DynamicLinker.cpp

@@ -336,6 +336,11 @@ static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data)
     return 0;
     return 0;
 }
 }
 
 
+int DynamicLinker::iterate_over_loaded_shared_objects(int (*callback)(struct dl_phdr_info* info, size_t size, void* data), void* data)
+{
+    return __dl_iterate_phdr(callback, data);
+}
+
 static void initialize_libc(DynamicObject& libc)
 static void initialize_libc(DynamicObject& libc)
 {
 {
     auto res = libc.lookup_symbol("__libc_init"sv);
     auto res = libc.lookup_symbol("__libc_init"sv);

+ 1 - 0
Userland/Libraries/LibELF/DynamicLinker.h

@@ -18,6 +18,7 @@ class DynamicLinker {
 public:
 public:
     static Optional<DynamicObject::SymbolLookupResult> lookup_global_symbol(StringView symbol);
     static Optional<DynamicObject::SymbolLookupResult> lookup_global_symbol(StringView symbol);
     static EntryPointFunction linker_main(ByteString&& main_program_path, int fd, bool is_secure, char** envp);
     static EntryPointFunction linker_main(ByteString&& main_program_path, int fd, bool is_secure, char** envp);
+    static int iterate_over_loaded_shared_objects(int (*callback)(struct dl_phdr_info* info, size_t size, void* data), void* data);
 
 
     static Optional<ByteString> resolve_library(ByteString const& name, DynamicObject const& parent_object);
     static Optional<ByteString> resolve_library(ByteString const& name, DynamicObject const& parent_object);
 
 

+ 1 - 0
Userland/Utilities/CMakeLists.txt

@@ -72,6 +72,7 @@ install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/fgrep SYMBOLIC)"
 install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/rgrep SYMBOLIC)")
 install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/rgrep SYMBOLIC)")
 install(CODE "file(CREATE_LINK gzip ${CMAKE_INSTALL_PREFIX}/bin/gunzip SYMBOLIC)")
 install(CODE "file(CREATE_LINK gzip ${CMAKE_INSTALL_PREFIX}/bin/gunzip SYMBOLIC)")
 install(CODE "file(CREATE_LINK gzip ${CMAKE_INSTALL_PREFIX}/bin/zcat SYMBOLIC)")
 install(CODE "file(CREATE_LINK gzip ${CMAKE_INSTALL_PREFIX}/bin/zcat SYMBOLIC)")
+install(CODE "file(CREATE_LINK /usr/lib/Loader.so ${CMAKE_INSTALL_PREFIX}/bin/ldd SYMBOLIC)")
 
 
 target_link_libraries(abench PRIVATE LibAudio LibFileSystem)
 target_link_libraries(abench PRIVATE LibAudio LibFileSystem)
 target_link_libraries(aconv PRIVATE LibAudio LibFileSystem)
 target_link_libraries(aconv PRIVATE LibAudio LibFileSystem)