Skip to content

Commit

Permalink
rtdl: Add support for LD_LIBRARY_PATH
Browse files Browse the repository at this point in the history
  • Loading branch information
48cf committed Jan 23, 2024
1 parent f79fa8e commit 8545852
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 12 deletions.
19 changes: 10 additions & 9 deletions options/rtdl/generic/linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum {


#include <frg/manual_box.hpp>
#include <frg/small_vector.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/rtdl-sysdeps.hpp>
Expand All @@ -27,6 +28,7 @@ constexpr bool verbose = false;
constexpr bool stillSlightlyVerbose = false;
constexpr bool logBaseAddresses = false;
constexpr bool logRpath = false;
constexpr bool logLdPath = false;
constexpr bool eagerBinding = true;

#if defined(__x86_64__) || defined(__i386__)
Expand All @@ -42,6 +44,8 @@ constexpr inline bool tlsAboveTp = true;
extern DebugInterface globalDebugInterface;
extern uintptr_t __stack_chk_guard;

extern frg::manual_box<frg::small_vector<frg::string_view, 4, MemoryAllocator>> libraryPaths;

#if MLIBC_STATIC_BUILD
extern "C" size_t __init_array_start[];
extern "C" size_t __init_array_end[];
Expand Down Expand Up @@ -157,13 +161,6 @@ SharedObject *ObjectRepository::requestObjectWithName(frg::string_view name,
if (auto obj = findLoadedObject(name))
return obj;

const char *libdirs[4] = {
"/lib/",
"/lib64/",
"/usr/lib/",
"/usr/lib64/"
};

auto tryToOpen = [&] (const char *path) {
int fd;
if(auto x = mlibc::sys_open(path, O_RDONLY, 0, &fd); x) {
Expand Down Expand Up @@ -233,8 +230,12 @@ SharedObject *ObjectRepository::requestObjectWithName(frg::string_view name,
} else if (logRpath) {
mlibc::infoLogger() << "rtdl: no rpath set for object" << frg::endlog;
}
for(int i = 0; i < 4 && fd == -1; i++) {
auto path = frg::string<MemoryAllocator>{getAllocator(), libdirs[i]} + name + '\0';

for(size_t i = 0; i < libraryPaths->size() && fd == -1; i++) {
auto ldPath = (*libraryPaths)[i];
auto path = frg::string<MemoryAllocator>{getAllocator(), ldPath} + '/' + name;
if(logLdPath)
mlibc::infoLogger() << "rtdl: Trying to load " << name << " from ldpath " << ldPath << frg::endlog;
fd = tryToOpen(path.data());
if(fd >= 0) {
chosenPath = std::move(path);
Expand Down
31 changes: 30 additions & 1 deletion options/rtdl/generic/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <link.h>

#include <frg/manual_box.hpp>
#include <frg/small_vector.hpp>

#include <abi-bits/auxv.h>
#include <mlibc/debug.hpp>
Expand Down Expand Up @@ -47,6 +48,9 @@ frg::manual_box<Scope> globalScope;

frg::manual_box<RuntimeTlsMap> runtimeTlsMap;

// We use a small vector of size 4 to avoid memory allocation for the default library paths
frg::manual_box<frg::small_vector<frg::string_view, 4, MemoryAllocator>> libraryPaths;

static SharedObject *executableSO;
extern HIDDEN char __ehdr_start[];

Expand Down Expand Up @@ -199,11 +203,27 @@ extern "C" [[gnu::alias("dl_debug_state"), gnu::visibility("default")]] void _dl
// This symbol can be used by GDB to find the global interface structure
[[ gnu::visibility("default") ]] DebugInterface *_dl_debug_addr = &globalDebugInterface;

static void parseLibraryPaths(frg::string_view paths) {
size_t p = 0;
while(p < paths.size()) {
size_t s; // Offset of next colon or end of string.
if(size_t cs = paths.find_first(':', p); cs != size_t(-1)) {
s = cs;
}else{
s = paths.size();
}

libraryPaths->push_back(paths.sub_string(p, s - p));
p = s + 1;
}
}

extern "C" void *interpreterMain(uintptr_t *entry_stack) {
if(logEntryExit)
mlibc::infoLogger() << "Entering ld.so" << frg::endlog;
entryStack = entry_stack;
runtimeTlsMap.initialize();
libraryPaths.initialize(getAllocator());

void *phdr_pointer = 0;
size_t phdr_entry_size = 0;
Expand Down Expand Up @@ -282,16 +302,25 @@ extern "C" void *interpreterMain(uintptr_t *entry_stack) {
if(s == size_t(-1))
mlibc::panicLogger() << "rtdl: environment '" << env << "' is missing a '='" << frg::endlog;

auto name = view.sub_string(0, s);
auto value = const_cast<char *>(view.data() + s + 1);

if(view.sub_string(0, s) == "LD_SHOW_AUXV" && value && *value && *value != '0') {
if(name == "LD_SHOW_AUXV" && *value && *value != '0') {
ldShowAuxv = true;
}else if(name == "LD_LIBRARY_PATH" && *value) {
parseLibraryPaths(value);
}

aux++;
}
aux++;

// Add default library paths
libraryPaths->push_back("/lib/");
libraryPaths->push_back("/lib64/");
libraryPaths->push_back("/usr/lib/");
libraryPaths->push_back("/usr/lib64/");

// Parse the actual vector.
while(true) {
auto value = aux + 1;
Expand Down
1 change: 1 addition & 0 deletions tests/rtdl/ld_library_path/libfoo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void foo() {}
19 changes: 19 additions & 0 deletions tests/rtdl/ld_library_path/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Remove the RPATH and set the LD_LIBRARY_PATH environment variable
# instead when running tests to make sure the library can be found
# in a directory specified by the user at runtime

test_rpath = '$ORIGIN/'

test_ld_path = meson.build_root() / 'tests' / 'rtdl' / test_name
test_env += ['LD_LIBRARY_PATH=' + test_ld_path]
test_native_env += ['LD_LIBRARY_PATH=' + test_ld_path]

libfoo = shared_library('foo', 'libfoo.c',
link_with: libc, include_directories: libc_include_dirs,
link_args: test_additional_link_args,
)

libfoo_native = shared_library('native-foo', 'libfoo.c',
link_args: ['-ldl'] + test_additional_link_args,
native: true
)
16 changes: 16 additions & 0 deletions tests/rtdl/ld_library_path/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <dlfcn.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>

#ifdef USE_HOST_LIBC
#define LIBFOO "libnative-foo.so"
#else
#define LIBFOO "libfoo.so"
#endif

int main() {
void *foo = dlopen(LIBFOO, RTLD_NOW);
assert(foo);
dlclose(foo);
}
7 changes: 5 additions & 2 deletions tests/rtdl/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
rtdl_test_cases = [
'dl_iterate_phdr',
'dladdr_local',
'ld_library_path',
'noload-promote',
'rtld_next',
'soname',
Expand All @@ -17,8 +18,10 @@ foreach test_name : rtdl_test_cases
test_rpath = meson.build_root() / 'tests' / 'rtdl' / test_name / ''
test_rpath += ':$ORIGIN/' # Workaround old and buggy qemu-user on CI

test_env = []
test_link_with = []
test_depends = []
test_native_env = []
test_native_link_with = []
test_native_depends = []
test_additional_link_args = []
Expand All @@ -34,7 +37,7 @@ foreach test_name : rtdl_test_cases
c_args: test_c_args,
link_args: test_link_args + test_additional_link_args,
)
test(test_name, exec, suite: 'rtdl', depends: test_depends)
test(test_name, exec, env: test_env, suite: 'rtdl', depends: test_depends)

if build_tests_host_libc and not host_libc_excluded_test_cases.contains(test_name)
exec = executable('host-libc-' + test_name, test_name / 'test.c',
Expand All @@ -48,6 +51,6 @@ foreach test_name : rtdl_test_cases
link_args: ['-ldl'] + test_additional_link_args,
native: true,
)
test(test_name, exec, suite: ['host-libc', 'rtdl'], depends: test_native_depends)
test(test_name, exec, env: test_native_env, suite: ['host-libc', 'rtdl'], depends: test_native_depends)
endif
endforeach

0 comments on commit 8545852

Please sign in to comment.