diff options
author | Liav A <liavalb@gmail.com> | 2022-12-18 16:52:22 +0200 |
---|---|---|
committer | Andrew Kaster <andrewdkaster@gmail.com> | 2022-12-31 05:06:39 -0700 |
commit | 658f9eec6a9284b7f2a5620630d2b3d5c726fcd9 (patch) | |
tree | 8b3ae0dc5c27a0858a1850c96a1affd914ae402d | |
parent | efec3448038fccd1de7fd56c3b673f1e62e282df (diff) | |
download | serenity-658f9eec6a9284b7f2a5620630d2b3d5c726fcd9.zip |
Utilities: Introduce the ldd utility
This utility lets a user to figure out what are the dependency libraries
for an ELF dynamic object, whether it's a dynamically loaded executable
or dynamically loaded library.
-rw-r--r-- | Base/usr/share/man/man1/ldd.md | 41 | ||||
-rw-r--r-- | Userland/Utilities/ldd.cpp | 151 |
2 files changed, 192 insertions, 0 deletions
diff --git a/Base/usr/share/man/man1/ldd.md b/Base/usr/share/man/man1/ldd.md new file mode 100644 index 0000000000..d358f0b788 --- /dev/null +++ b/Base/usr/share/man/man1/ldd.md @@ -0,0 +1,41 @@ +## Name + +ldd - list dynamic dependencies + +## Synopsis + +```**sh +$ ldd [-r] [-f] <path> +``` + +## Description + +`ldd` prints all dependency libraries of an ELF object. + +## Options + +* `-f`, `--force-without-valid-interpreter`: Force library resolving on ELF + object without a valid interpreter +* `-r`, `--max-recursion`: Max library resolving recursion + +## Arguments + +* `path`: Path to ELF object + +## Security + +In contrast to other OS implementations, the `ldd` binary is completely safe for +usage on untrusted binaries - we only use the `LibELF` code for doing library +resolving, and the actual binary interpreter (when specified) is never called to +decode the dependency information. + +## Examples + +```sh +# List all dependency libraries for libc.so +$ ldd -f /usr/lib/libc.so +# List all dependency libraries for /bin/id +$ ldd /bin/id +# List all dependency libraries for /bin/WindowServer +$ ldd /bin/WindowServer +``` diff --git a/Userland/Utilities/ldd.cpp b/Userland/Utilities/ldd.cpp new file mode 100644 index 0000000000..47fa61062b --- /dev/null +++ b/Userland/Utilities/ldd.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/DeprecatedString.h> +#include <AK/LexicalPath.h> +#include <AK/StringBuilder.h> +#include <AK/StringView.h> +#include <LibCore/ArgsParser.h> +#include <LibCore/File.h> +#include <LibCore/MappedFile.h> +#include <LibCore/System.h> +#include <LibELF/DynamicLinker.h> +#include <LibELF/DynamicLoader.h> +#include <LibELF/DynamicObject.h> +#include <LibELF/Image.h> +#include <LibELF/Validation.h> +#include <LibMain/Main.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +static Vector<DeprecatedString> found_libraries; + +static ErrorOr<void> recusively_resolve_all_necessary_libraries(StringView interpreter_path, size_t recursive_iteration_max, size_t recursive_iteration, ELF::DynamicObject& object) +{ + if (recursive_iteration > recursive_iteration_max) + return ELOOP; + + Vector<DeprecatedString> libraries; + object.for_each_needed_library([&libraries](StringView entry) { + libraries.append(DeprecatedString::formatted("{}", entry)); + }); + for (auto& library_name : libraries) { + auto possible_library_path = ELF::DynamicLinker::resolve_library(library_name, object); + if (!possible_library_path.has_value()) + continue; + auto library_path = LexicalPath::absolute_path(TRY(Core::System::getcwd()), possible_library_path.value()); + if (found_libraries.contains_slow(library_path)) + continue; + auto file = TRY(Core::MappedFile::map(library_path)); + + auto elf_image_data = file->bytes(); + ELF::Image elf_image(elf_image_data); + if (!elf_image.is_valid()) { + outln("Shared library is not valid ELF: {}", library_path); + continue; + } + if (!elf_image.is_dynamic()) { + outln("Shared library is not dynamic loaded object: {}", library_path); + continue; + } + + int fd = TRY(Core::System::open(library_path, O_RDONLY)); + auto result = ELF::DynamicLoader::try_create(fd, library_path); + if (result.is_error()) { + outln("{}", result.error().text); + continue; + } + auto& loader = result.value(); + if (!loader->is_valid()) { + outln("{} is not a valid ELF dynamic shared object!", library_path); + continue; + } + + RefPtr<ELF::DynamicObject> library_object = loader->map(); + if (!library_object) { + outln("Failed to map dynamic ELF object {}", library_path); + continue; + } + outln("{} => {}", library_name, library_path); + recursive_iteration++; + found_libraries.append(library_path); + TRY(recusively_resolve_all_necessary_libraries(interpreter_path, recursive_iteration_max, recursive_iteration, *library_object)); + } + return {}; +} + +ErrorOr<int> serenity_main(Main::Arguments arguments) +{ + TRY(Core::System::pledge("stdio rpath")); + + DeprecatedString path {}; + Optional<size_t> recursive_iteration_max; + bool force_without_valid_interpreter = false; + + Core::ArgsParser args_parser; + args_parser.add_option(recursive_iteration_max, "Max library resolving recursion", "max-recursion", 'r', "max recursion-level"); + args_parser.add_option(force_without_valid_interpreter, "Force library resolving on ELF object without valid interpreter", "force-without-valid-interpreter", 'f'); + args_parser.add_positional_argument(path, "ELF path", "path"); + args_parser.parse(arguments); + + path = LexicalPath::absolute_path(TRY(Core::System::getcwd()), path); + + auto file_or_error = Core::MappedFile::map(path); + + if (file_or_error.is_error()) { + warnln("Unable to map file {}: {}", path, file_or_error.error()); + return -1; + } + + auto elf_image_data = file_or_error.value()->bytes(); + ELF::Image elf_image(elf_image_data); + + if (!elf_image.is_valid()) { + warnln("File is not a valid ELF object"); + return -1; + } + + StringBuilder interpreter_path_builder; + auto result_or_error = ELF::validate_program_headers(*(const ElfW(Ehdr)*)elf_image_data.data(), elf_image_data.size(), elf_image_data, &interpreter_path_builder); + if (result_or_error.is_error() || !result_or_error.value()) { + warnln("Invalid ELF headers"); + return -1; + } + auto interpreter_path = interpreter_path_builder.string_view(); + + RefPtr<ELF::DynamicObject> object = nullptr; + if (elf_image.is_dynamic()) { + if (interpreter_path != "/usr/lib/Loader.so"sv && !force_without_valid_interpreter) { + warnln("ELF interpreter image is invalid"); + return 1; + } + + int fd = TRY(Core::System::open(path, O_RDONLY)); + auto result = ELF::DynamicLoader::try_create(fd, path); + if (result.is_error()) { + outln("{}", result.error().text); + return 1; + } + auto& loader = result.value(); + if (!loader->is_valid()) { + outln("{} is not a valid ELF dynamic shared object!", path); + return 1; + } + + object = loader->map(); + if (!object) { + outln("Failed to map dynamic ELF object {}", path); + return 1; + } + TRY(recusively_resolve_all_necessary_libraries(interpreter_path, recursive_iteration_max.value_or(10), 0, *object)); + } else { + outln("ELF program is not dynamic loaded!"); + return 1; + } + return 0; +} |