blob: 07ca7a071dbf6c223002fba25d59ed24d25b4e5d [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_DL_RUNTIME_DYNAMIC_LINKER_H_
#define LIB_DL_RUNTIME_DYNAMIC_LINKER_H_
#include <dlfcn.h> // for RTLD_* macros
#include <lib/elfldltl/soname.h>
#include <lib/fit/result.h>
#include <fbl/intrusive_double_list.h>
#include "diagnostics.h"
#include "error.h"
#include "module.h"
namespace dl {
enum OpenSymbolScope : int {
kLocal = RTLD_LOCAL,
kGlobal = RTLD_GLOBAL,
};
enum OpenBindingMode : int {
kNow = RTLD_NOW,
// RTLD_LAZY functionality is not supported, but keep the flag definition
// because it's a legitimate flag that can be passed in.
kLazy = RTLD_LAZY,
};
enum OpenFlags : int {
kNoload = RTLD_NOLOAD,
kNodelete = RTLD_NODELETE,
// TODO(https://fxbug.dev/323425900): support glibc's RTLD_DEEPBIND flag.
// kDEEPBIND = RTLD_DEEPBIND,
};
// Masks used to validate flag values.
inline constexpr int kOpenSymbolScopeMask = OpenSymbolScope::kLocal | OpenSymbolScope::kGlobal;
inline constexpr int kOpenBindingModeMask = OpenBindingMode::kLazy | OpenBindingMode::kNow;
inline constexpr int kOpenFlagsMask = OpenFlags::kNoload | OpenFlags::kNodelete;
class RuntimeDynamicLinker {
public:
using Soname = elfldltl::Soname<>;
// Not copyable, not movable
RuntimeDynamicLinker() = default;
RuntimeDynamicLinker(const RuntimeDynamicLinker&) = delete;
RuntimeDynamicLinker(RuntimeDynamicLinker&&) = delete;
// Attempt to find the loaded module with the given name, returning a nullptr
// if the module was not found.
ModuleHandle* FindModule(Soname name);
template <class OSImpl>
fit::result<Error, void*> Open(const char* file, int mode) {
auto already_loaded = CheckOpen(file, mode);
if (already_loaded.is_error()) [[unlikely]] {
return already_loaded.take_error();
}
// If the module for `file` was found, return a reference to it.
if (already_loaded.value()) {
return fit::ok(already_loaded.value());
}
// TODO(caslyn): Roll up ModuleHandle allocation with LoadModule allocation,
// and add comment on the allocation/relationship.
fbl::AllocChecker ac;
auto module = ModuleHandle::Create(Soname{file}, ac);
if (!ac.check()) [[unlikely]] {
return Error::OutOfMemory();
}
// TODO(https://fxbug.dev/324650368): implement file retrieval interfaces.
// Use a non-scoped diagnostics object for the main module. Because errors
// are generated on this module directly, its name does not need to be
// prefixed to the error, as is the case using ld::ScopedModuleDiagnostics.
dl::Diagnostics diag;
LoadModule<OSImpl> load_module{std::move(module)};
if (!load_module.Load(diag)) [[unlikely]] {
return diag.take_error();
}
// TODO(caslyn): these are actions needed to finish loading the module and
// return its reference back to the caller, but are not meant to be invoked
// directly by this function. These will be abstracted away eventually.
auto loaded_module = std::move(load_module).take_module();
ModuleHandle* module_ref = loaded_module.get();
loaded_modules_.push_back(std::move(loaded_module));
return diag.ok(module_ref);
}
fit::result<Error, void*> LookupSymbol(ModuleHandle* module, const char* ref);
private:
// Perform basic argument checking and check whether a module for `file` was
// already loaded. An error is returned if bad input was given. Otherwise,
// return a reference to the module if it was already loaded, or nullptr if
// a module for `file` was not found.
fit::result<Error, ModuleHandle*> CheckOpen(const char* file, int mode);
// The RuntimeDynamicLinker owns the list of all 'live' modules that have been
// loaded into the system image.
// TODO(https://fxbug.dev/324136831): support startup modules
fbl::DoublyLinkedList<std::unique_ptr<ModuleHandle>> loaded_modules_;
};
} // namespace dl
#endif // LIB_DL_RUNTIME_DYNAMIC_LINKER_H_