Merge branch 'patch5' into 'master'

feat(ld.so): lazy binding and scopes

See merge request redox-os/relibc!586
This commit is contained in:
Jeremy Soller
2024-12-25 13:18:31 +00:00
16 changed files with 1052 additions and 383 deletions
+4 -4
View File
@@ -29,14 +29,14 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
tcb.dtv_mut().unwrap().len()
);
if tcb.dtv_mut().unwrap_or_default().len() < masters.len() {
if tcb.dtv_mut().len() < masters.len() {
// Reallocate DTV.
tcb.setup_dtv(masters.len());
}
let dtv_index = ti.ti_module as usize - 1;
if tcb.dtv_mut().unwrap()[dtv_index].is_null() {
if tcb.dtv_mut()[dtv_index].is_null() {
// Allocate TLS for module.
let master = &masters[dtv_index];
@@ -50,10 +50,10 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
unsafe { core::ptr::copy_nonoverlapping(master.ptr, module_tls, master.len) };
// Set the DTV entry.
tcb.dtv_mut().unwrap()[dtv_index] = module_tls;
tcb.dtv_mut()[dtv_index] = module_tls;
}
let mut ptr = tcb.dtv_mut().unwrap()[dtv_index];
let mut ptr = tcb.dtv_mut()[dtv_index];
if ptr.is_null() {
panic!(
+49 -14
View File
@@ -3,17 +3,30 @@
#![deny(unsafe_op_in_unsafe_fn)]
use core::{
ptr, str,
ptr::{self, NonNull},
str,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{c_str::CStr, ld_so::tcb::Tcb, platform::types::*};
use alloc::sync::Arc;
pub const RTLD_LAZY: c_int = 0x0001;
pub const RTLD_NOW: c_int = 0x0002;
pub const RTLD_GLOBAL: c_int = 0x0100;
use crate::{
c_str::CStr,
ld_so::{
linker::{ObjectHandle, ObjectScope, Resolve},
tcb::Tcb,
},
platform::types::*,
};
pub const RTLD_LAZY: c_int = 1 << 0;
pub const RTLD_NOW: c_int = 1 << 1;
pub const RTLD_NOLOAD: c_int = 1 << 2;
pub const RTLD_GLOBAL: c_int = 1 << 8;
pub const RTLD_LOCAL: c_int = 0x0000;
pub const RTLD_DEFAULT: *mut c_void = 0 as *mut c_void; // XXX: cbindgen doesn't like ptr::null_mut()
static ERROR_NOT_SUPPORTED: CStr = c_str!("dlfcn not supported");
#[thread_local]
@@ -42,6 +55,19 @@ pub unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int
#[no_mangle]
pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void {
//TODO support all sort of flags
let resolve = if flags & RTLD_NOW == RTLD_NOW {
Resolve::Now
} else {
Resolve::Lazy
};
let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL {
ObjectScope::Global
} else {
ObjectScope::Local
};
let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD;
let filename = if cfilename.is_null() {
None
@@ -69,19 +95,19 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
let id = match (cbs.load_library)(&mut linker, filename) {
Err(err) => {
match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) {
Ok(handle) => handle.as_ptr().cast_mut(),
Err(error) => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
ptr::null_mut()
}
Ok(id) => id,
};
id as *mut c_void
}
}
#[no_mangle]
pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void {
let handle = ObjectHandle::from_ptr(handle);
if symbol.is_null() {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
@@ -89,6 +115,9 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) };
// FIXME(andypython): just call obj.scope.get_sym() directly or search the
// global scope. The rest is unnecessary as Linker::get_sym() does not
// depend on the Linker state.
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
@@ -105,7 +134,7 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
let linker = unsafe { (&*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
match (cbs.get_sym)(&linker, handle as usize, symbol_str) {
match (cbs.get_sym)(&linker, handle, symbol_str) {
Some(sym) => sym,
_ => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
@@ -128,10 +157,16 @@ pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let Some(handle) = ObjectHandle::from_ptr(handle) else {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let mut linker = unsafe { (&*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
(cbs.unload)(&mut linker, handle as usize);
(cbs.unload)(&mut linker, handle);
0
}
+17 -10
View File
@@ -1,12 +1,13 @@
use super::linker::Linker;
use super::linker::{Linker, ObjectHandle, ObjectScope, Resolve};
use crate::platform::types::c_void;
use alloc::boxed::Box;
use goblin::error::Result;
pub struct LinkerCallbacks {
pub unload: Box<dyn Fn(&mut Linker, usize)>,
pub load_library: Box<dyn Fn(&mut Linker, Option<&str>) -> Result<usize>>,
pub get_sym: Box<dyn Fn(&Linker, usize, &str) -> Option<*mut c_void>>,
pub unload: Box<dyn Fn(&mut Linker, ObjectHandle)>,
pub load_library:
Box<dyn Fn(&mut Linker, Option<&str>, Resolve, ObjectScope, bool) -> Result<ObjectHandle>>,
pub get_sym: Box<dyn Fn(&Linker, Option<ObjectHandle>, &str) -> Option<*mut c_void>>,
}
impl LinkerCallbacks {
@@ -19,14 +20,20 @@ impl LinkerCallbacks {
}
}
fn unload(linker: &mut Linker, lib_id: usize) {
linker.unload(lib_id)
fn unload(linker: &mut Linker, handle: ObjectHandle) {
linker.unload(handle)
}
fn load_library(linker: &mut Linker, name: Option<&str>) -> Result<usize> {
linker.load_library(name)
fn load_library(
linker: &mut Linker,
name: Option<&str>,
resolve: Resolve,
scope: ObjectScope,
noload: bool,
) -> Result<ObjectHandle> {
linker.load_library(name, resolve, scope, noload)
}
fn get_sym(linker: &Linker, lib_id: usize, name: &str) -> Option<*mut c_void> {
linker.get_sym(lib_id, name)
fn get_sym(linker: &Linker, handle: Option<ObjectHandle>, name: &str) -> Option<*mut c_void> {
linker.get_sym(handle, name)
}
+94 -29
View File
@@ -1,11 +1,13 @@
//! See <https://refspecs.linuxfoundation.org/elf/elf.pdf>.
use super::{
debug::{RTLDDebug, _r_debug},
linker::Symbol,
linker::{Scope, Symbol},
tcb::Master,
};
use crate::{
header::{errno::STR_ERROR, sys_mman},
platform::{types::c_void, Pal, Sys, ERRNO},
header::sys_mman,
platform::{types::c_void, Pal, Sys},
};
use alloc::{
collections::BTreeMap,
@@ -14,7 +16,8 @@ use alloc::{
};
use core::{
mem::{size_of, transmute},
ptr, slice,
ptr::{self, NonNull},
slice,
};
#[cfg(target_pointer_width = "32")]
use goblin::elf32::{
@@ -33,12 +36,34 @@ use goblin::elf64::{
sym,
};
use goblin::{
elf::Elf,
elf::{
dynamic::DT_PLTGOT,
sym::{STB_GLOBAL, STB_WEAK},
Dynamic, Elf,
},
error::{Error, Result},
};
#[derive(Debug, PartialEq)]
#[repr(u8)]
pub enum SymbolBinding {
/// Global symbols are visible to all object files being combined. One
/// file's definition of a global symbol will satisfy another file's
/// undefined reference to the same global symbol.
Global = STB_GLOBAL,
/// Weak symbols resemble global symbols, but their definitions have lower
/// precedence.
Weak = STB_WEAK,
}
impl SymbolBinding {
#[inline]
pub fn is_global(&self) -> bool {
matches!(self, Self::Global)
}
}
/// Use to represent a library as well as all the symbols that is loaded withen it.
#[derive(Default)]
pub struct DSO {
pub name: String,
pub id: usize,
@@ -56,7 +81,13 @@ pub struct DSO {
pub fini_array: (usize, usize),
pub tls_module_id: usize,
pub tls_offset: usize,
pub use_count: usize,
pub dynamic: Option<Dynamic>,
pub dynsyms: Vec<goblin::elf::sym::Sym>,
pub scope: Scope,
/// Position Independent Executable.
pub pie: bool,
}
impl DSO {
@@ -88,33 +119,67 @@ impl DSO {
elf.header.e_entry as usize
};
let dso = DSO {
name: name,
id: id,
use_count: 1,
dlopened: dlopened,
entry_point: entry_point,
name,
id,
dlopened,
entry_point,
runpath: DSO::get_runpath(&path, &elf)?,
mmap: mmap,
global_syms: global_syms,
weak_syms: weak_syms,
mmap,
global_syms,
weak_syms,
dependencies: elf.libraries.iter().map(|s| s.to_string()).collect(),
init_array: init_array,
fini_array: fini_array,
init_array,
fini_array,
tls_module_id: if tcb_master.is_some() {
tls_module_id
} else {
0
},
tls_offset: tls_offset,
tls_offset,
pie: is_pie_enabled(&elf),
dynamic: elf.dynamic.map(|dynamic| dynamic),
dynsyms: elf.dynsyms.iter().collect(),
scope: Scope::local(),
};
return Ok((dso, tcb_master));
Ok((dso, tcb_master))
}
pub fn get_sym(&self, name: &str) -> Option<(Symbol, bool)> {
/// Global Offset Table
pub(super) fn got(&self) -> Option<NonNull<usize>> {
let Some(dynamic) = self.dynamic.as_ref() else {
return None;
};
let object_base_addr = self.mmap.as_ptr() as u64;
let got = if let Some(ptr) = {
dynamic
.dyns
.iter()
.find(|r#dyn| r#dyn.d_tag == DT_PLTGOT)
.map(|r#dyn| r#dyn.d_val)
} {
if self.pie {
(object_base_addr + ptr) as *mut usize
} else {
ptr as *mut usize
}
} else {
assert_eq!(dynamic.info.jmprel, 0);
return None;
};
Some(NonNull::new(got).expect("global offset table"))
}
pub fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding)> {
if let Some(value) = self.global_syms.get(name) {
Some((*value, true))
Some((*value, SymbolBinding::Global))
} else if let Some(value) = self.weak_syms.get(name) {
Some((*value, false))
Some((*value, SymbolBinding::Weak))
} else {
None
}
@@ -147,8 +212,8 @@ impl DSO {
Some(entry) => {
let runpath = elf
.dynstrtab
.get(entry.d_val as usize)
.ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))??;
.get_at(entry.d_val as usize)
.ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))?;
let base = dirname(path);
return Ok(Some(runpath.replace("$ORIGIN", &base)));
}
@@ -249,7 +314,7 @@ impl DSO {
// Copy data
for ph in elf.program_headers.iter() {
let voff = ph.p_vaddr % ph.p_align;
let vaddr = (ph.p_vaddr - voff) as usize;
// let vaddr = (ph.p_vaddr - voff) as usize;
let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize);
match ph.p_type {
@@ -361,8 +426,8 @@ impl DSO {
}
let name: String;
let value: Symbol;
if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
name = name_res?.to_string();
if let Some(name_res) = elf.dynstrtab.get_at(sym.st_name) {
name = name_res.to_string();
value = if is_pie_enabled(elf) {
Symbol {
base: mmap.as_ptr() as usize,
@@ -404,7 +469,7 @@ impl DSO {
.iter()
.filter(|s| s.sh_type == SHT_INIT_ARRAY || s.sh_type == SHT_FINI_ARRAY)
{
let addr = if is_pie_enabled(&elf) {
let addr = if is_pie_enabled(elf) {
mmap_addr + section.vm_range().start
} else {
section.vm_range().start
@@ -422,7 +487,7 @@ impl DSO {
impl Drop for DSO {
fn drop(&mut self) {
self.run_fini();
unsafe { Sys::munmap(self.mmap.as_mut_ptr() as *mut c_void, self.mmap.len()) };
unsafe { Sys::munmap(self.mmap.as_mut_ptr() as *mut c_void, self.mmap.len()).unwrap() };
}
}
+773 -256
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -1,5 +1,9 @@
//! Dynamic loading and linking.
// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
// dropped from src/lib.rs.
#![warn(warnings, unused_variables)]
use core::{mem, ptr};
use goblin::elf::program_header::{self, program_header32, program_header64, ProgramHeader};
@@ -73,7 +77,7 @@ pub fn static_init(sp: &'static Stack) {
let page_size = Sys::getpagesize();
let voff = ph.p_vaddr as usize % page_size;
let vaddr = ph.p_vaddr as usize - voff;
// let vaddr = ph.p_vaddr as usize - voff;
let vsize = ((ph.p_memsz as usize + voff + page_size - 1) / page_size) * page_size;
match ph.p_type {
@@ -170,7 +174,7 @@ pub unsafe fn init(sp: &'static Stack) {
pub unsafe fn fini() {
if let Some(tcb) = Tcb::current() {
if tcb.linker_ptr != ptr::null_mut() {
if !tcb.linker_ptr.is_null() {
let linker = (&*tcb.linker_ptr).lock();
linker.fini();
}
+2 -25
View File
@@ -12,7 +12,6 @@ use generic_rt::ExpectTlsFree;
use crate::{
c_str::CStr,
header::unistd,
ld_so::tcb::Master,
platform::{get_auxv, get_auxvs, types::c_char},
start::Stack,
sync::mutex::Mutex,
@@ -39,7 +38,6 @@ unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
_ => {
eprintln!("ld.so: failed to parse argv[{}]", argv.len());
unistd::_exit(1);
loop {}
}
}
ptr = ptr.add(1);
@@ -88,16 +86,6 @@ unsafe fn adjust_stack(sp: &'static mut Stack) {
if arg == 0 {
break;
}
if let Ok(arg_str) = CStr::from_ptr(arg as *const c_char).to_str() {
let mut parts = arg_str.splitn(2, '=');
if let Some(key) = parts.next() {
if let Some(value) = parts.next() {
if let "LD_LIBRARY_PATH" = key {
//library_path = value
}
}
}
}
}
// Move auxiliary vectors
@@ -152,12 +140,7 @@ fn resolve_path_name(
// TODO: Make unsafe
#[no_mangle]
pub extern "C" fn relibc_ld_so_start(
sp: &'static mut Stack,
ld_entry: usize,
self_tls_start: usize,
self_tls_end: usize,
) -> usize {
pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize {
// Setup TCB for ourselves.
unsafe {
let tcb = Tcb::new(0).expect_notls("ld.so: failed to allocate bootstrap TCB");
@@ -212,9 +195,6 @@ pub extern "C" fn relibc_ld_so_start(
crate::platform::init(auxv.clone());
}
// Some variables that will be overridden by environment and auxiliary vectors
let ld_library_path = envs.get("LD_LIBRARY_PATH").map(|s| s.to_owned());
let name_or_path = if is_manual {
// ld.so is run directly by user and not via execve() or similar systemcall
println!("argv: {:#?}", argv);
@@ -224,7 +204,6 @@ pub extern "C" fn relibc_ld_so_start(
if sp.argc < 2 {
eprintln!("ld.so [executable] [arguments...]");
unistd::_exit(1);
loop {}
}
unsafe { adjust_stack(sp) };
argv[1].to_string()
@@ -232,12 +211,11 @@ pub extern "C" fn relibc_ld_so_start(
argv[0].to_string()
};
let (path, name) = match resolve_path_name(&name_or_path, &envs) {
let (path, _name) = match resolve_path_name(&name_or_path, &envs) {
Some((p, n)) => (p, n),
None => {
eprintln!("ld.so: failed to locate '{}'", name_or_path);
unistd::_exit(1);
loop {}
}
};
@@ -260,7 +238,6 @@ pub extern "C" fn relibc_ld_so_start(
Err(err) => {
eprintln!("ld.so: failed to link '{}': {}", path, err);
unistd::_exit(1);
loop {}
}
};
if let Some(tcb) = unsafe { Tcb::current() } {
+9 -11
View File
@@ -1,6 +1,5 @@
use alloc::vec::Vec;
use core::{
arch::asm,
cell::UnsafeCell,
mem,
ops::{Deref, DerefMut},
@@ -10,7 +9,6 @@ use core::{
use generic_rt::GenericTcb;
use goblin::error::{Error, Result};
use super::ExpectTlsFree;
use crate::{
header::sys_mman,
ld_so::linker::Linker,
@@ -80,7 +78,7 @@ impl Tcb {
/// `size` is the size of the TLS in bytes.
pub unsafe fn new(size: usize) -> Result<&'static mut Self> {
let page_size = Sys::getpagesize();
let (abi_page, tls, tcb_page) = Self::os_new(size.next_multiple_of(page_size))?;
let (_abi_page, tls, tcb_page) = Self::os_new(size.next_multiple_of(page_size))?;
let tcb_ptr = tcb_page.as_mut_ptr() as *mut Self;
trace!("New TCB: {:p}", tcb_ptr);
@@ -197,7 +195,7 @@ impl Tcb {
// XXX: [`Vec::from_raw_parts`] cannot be used here as the masters were originally
// allocated by the ld.so allocator and that would violate that function's invariants.
let mut masters = self.masters().unwrap().to_vec();
masters.extend(new_masters.into_iter());
masters.extend(new_masters);
self.masters_ptr = masters.as_mut_ptr();
self.masters_len = masters.len() * mem::size_of::<Master>();
@@ -212,8 +210,8 @@ impl Tcb {
pub fn setup_dtv(&mut self, n: usize) {
if self.dtv_ptr.is_null() {
let mut dtv = vec![ptr::null_mut(); n];
let (ptr, len, cap) = dtv.into_raw_parts();
let dtv = vec![ptr::null_mut(); n];
let (ptr, len, _) = dtv.into_raw_parts();
self.dtv_ptr = ptr;
self.dtv_len = len;
@@ -222,7 +220,7 @@ impl Tcb {
//
// XXX: [`Vec::from_raw_parts`] cannot be used here as the DTV was originally allocated
// by the ld.so allocator and that would violate that function's invariants.
let mut dtv = self.dtv_mut().unwrap().to_vec();
let mut dtv = self.dtv_mut().to_vec();
dtv.resize(n, ptr::null_mut());
let (ptr, len, _) = dtv.into_raw_parts();
@@ -231,11 +229,11 @@ impl Tcb {
}
}
pub fn dtv_mut(&mut self) -> Option<&'static mut [*mut u8]> {
pub fn dtv_mut(&mut self) -> &'static mut [*mut u8] {
if self.dtv_len != 0 {
Some(unsafe { slice::from_raw_parts_mut(self.dtv_ptr, self.dtv_len) })
unsafe { slice::from_raw_parts_mut(self.dtv_ptr, self.dtv_len) }
} else {
None
&mut []
}
}
@@ -269,7 +267,7 @@ impl Tcb {
/// OS and architecture specific code to activate TLS - Linux x86_64
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
unsafe fn os_arch_activate(os: &(), tls_end: usize, _tls_len: usize) {
unsafe fn os_arch_activate(_os: &(), tls_end: usize, _tls_len: usize) {
const ARCH_SET_FS: usize = 0x1002;
syscall!(ARCH_PRCTL, ARCH_SET_FS, tls_end);
}
+1 -1
View File
@@ -348,4 +348,4 @@ pub unsafe fn init(auxvs: Box<[[usize; 2]]>) {
}
}
#[cfg(not(target_os = "redox"))]
pub fn init(auxvs: Box<[[usize; 2]]>) {}
pub unsafe fn init(auxvs: Box<[[usize; 2]]>) {}
+11 -1
View File
@@ -159,7 +159,8 @@ EXPECT_NAMES=\
BUILD?=.
DYNAMIC_ONLY_NAMES=\
dlfcn
dlfcn \
dlopen_scopes
# Binaries that may generate varied output
NAMES=\
@@ -323,10 +324,19 @@ $(BUILD)/bins_dynamic/%.so: %.c $(DEPS)
mkdir -p "$$(dirname "$@")"
$(CC) "$<" -o "$@" -shared -fpic $(FLAGS) $(DYNAMIC_FLAGS)
# foobar depends on foo
$(BUILD)/bins_dynamic/libfoobar.so: libfoobar.c $(BUILD)/bins_dynamic/libfoo.so $(DEPS)
mkdir -p "$$(dirname "$@")"
$(CC) "$<" -o "$@" -shared -fpic $(FLAGS) $(DYNAMIC_FLAGS) -L $(BUILD)/bins_dynamic -lfoo
$(BUILD)/bins_dynamic/dlfcn: dlfcn.c $(BUILD)/bins_dynamic/sharedlib.so $(DEPS)
mkdir -p "$$(dirname "$@")"
$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
$(BUILD)/bins_dynamic/dlopen_scopes: dlopen_scopes.c $(BUILD)/bins_dynamic/libfoobar.so $(BUILD)/bins_dynamic/libfoo.so $(DEPS)
mkdir -p "$$(dirname "$@")"
$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
$(BUILD)/bins_dynamic/%: %.c $(DEPS)
mkdir -p "$$(dirname "$@")"
$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
+41 -30
View File
@@ -1,18 +1,16 @@
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#define SHARED_LIB "sharedlib.so"
int add(int a, int b)
{
return a + b;
}
int add(int a, int b) { return a + b; }
void test_dlopen_null()
{
void* handle = dlopen(NULL, RTLD_LAZY);
void test_dlopen_null() {
void *handle = dlopen(NULL, RTLD_LAZY);
if (!handle) {
printf("dlopen(NULL) failed\n");
printf("dlopen(NULL) failed: %s\n", dlerror());
exit(1);
}
@@ -20,7 +18,7 @@ void test_dlopen_null()
*(void **)(&f) = dlsym(handle, "add");
if (!f) {
printf("dlsym(handle, add) failed\n");
printf("dlsym(handle, add) failed: %s\n", dlerror());
exit(2);
}
int a = 22;
@@ -29,15 +27,14 @@ void test_dlopen_null()
dlclose(handle);
}
void test_dlopen_libc()
{
void* handle = dlopen("libc.so.6", RTLD_LAZY);
void test_dlopen_libc() {
void *handle = dlopen("libc.so.6", RTLD_LAZY);
if (!handle) {
printf("dlopen(libc.so.6) failed\n");
exit(1);
}
int (*f)(const char*);
int (*f)(const char *);
*(void **)(&f) = dlsym(handle, "puts");
if (!f) {
@@ -48,10 +45,8 @@ void test_dlopen_libc()
dlclose(handle);
}
void test_dlsym_function()
{
void* handle = dlopen("sharedlib.so", RTLD_LAZY);
void test_dlsym_function() {
void *handle = dlopen(SHARED_LIB, RTLD_LAZY);
if (!handle) {
printf("dlopen(sharedlib.so) failed\n");
exit(1);
@@ -68,14 +63,13 @@ void test_dlsym_function()
dlclose(handle);
}
void test_dlsym_global_var()
{
void* handle = dlopen("sharedlib.so", RTLD_LAZY);
void test_dlsym_global_var() {
void *handle = dlopen(SHARED_LIB, RTLD_LAZY);
if (!handle) {
printf("dlopen(sharedlib.so) failed\n");
exit(1);
}
int* global_var = dlsym(handle, "global_var");
int *global_var = dlsym(handle, "global_var");
if (!global_var) {
printf("dlsym(handle, global_var) failed\n");
exit(2);
@@ -84,14 +78,13 @@ void test_dlsym_global_var()
dlclose(handle);
}
void test_dlsym_tls_var()
{
void* handle = dlopen("sharedlib.so", RTLD_LAZY);
void test_dlsym_tls_var() {
void *handle = dlopen(SHARED_LIB, RTLD_LAZY);
if (!handle) {
printf("dlopen(sharedlib.so) failed\n");
exit(1);
}
int* tls_var = dlsym(handle, "tls_var");
int *tls_var = dlsym(handle, "tls_var");
if (!tls_var) {
printf("dlsym(handle, tls_var) failed\n");
exit(2);
@@ -100,12 +93,30 @@ void test_dlsym_tls_var()
dlclose(handle);
}
int main()
{
void test_dlunload(void) {
void *handle = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_LOCAL);
void *handle2 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_LOCAL);
assert(handle == handle2 && handle);
assert(!dlclose(handle));
void *handle3 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD);
assert(handle3 == handle2);
assert(!dlclose(handle3));
assert(!dlclose(handle2));
void *handle4 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD);
assert(handle4 == NULL);
void *handle5 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_GLOBAL);
assert(handle5);
assert(!dlclose(handle5));
void *handle6 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD);
assert(handle6 == NULL);
}
int main() {
test_dlopen_null();
test_dlopen_libc();
test_dlsym_function();
test_dlsym_global_var();
test_dlsym_tls_var();
test_dlunload();
}
+38
View File
@@ -0,0 +1,38 @@
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
void *handle = dlopen("libfoobar.so", RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
printf("dlopen(libfoobar.so): %s\n", dlerror());
return EXIT_FAILURE;
}
assert(dlsym(handle, "BAR") != NULL);
assert(dlsym(handle, "FOO") != NULL);
// not in the global scope
assert(dlsym(RTLD_DEFAULT, "BAR") == NULL);
assert(dlsym(RTLD_DEFAULT, "FOO") == NULL);
void *self = dlopen(NULL, RTLD_LAZY | RTLD_LOCAL);
if (!self) {
printf("dlopen(NULL): %s\n", dlerror());
return EXIT_FAILURE;
}
assert(dlsym(self, "FOO") == NULL);
assert(dlsym(self, "BAR") == NULL);
assert(dlclose(self) == 0);
// Promote the library to the global scope.
assert(dlopen("libfoobar.so", /* RTLD_NOLOAD |*/ RTLD_NOW | RTLD_GLOBAL));
assert(dlsym(RTLD_DEFAULT, "FOO") != NULL);
assert(dlsym(RTLD_DEFAULT, "BAR") != NULL);
return EXIT_SUCCESS;
}
+3
View File
@@ -0,0 +1,3 @@
char *FOO = "foo";
void a() {}
+4
View File
@@ -0,0 +1,4 @@
char *BAR = "bar";
void a();
void b() { a(); }