Fix global symbols relocations

Instead of a single source of symbols, now linker keeps a list of DSO (former Library) objects
with their own symbols map. That helps to process R_X86_64_COPY relocations correctly.
For example, if 'a.out' executable with dependencies ['libstdc++.so', 'libc.so'] is being loaded
and 'a.out' uses 'stdout' symbol from 'libc.so', its relocation process goes as follows:
- linker processes relocation entry 'stdout' of type R_X86_64_GLOB_DAT from 'libc.so',
- it goes through object list ['a.out', 'libstdc++.so', 'libc.so'] to find first object
  that exports 'stdout' symbol. The symbol is in 'a.out' with the value e.g. '0x404070',
- linker sets 'stdout' symbol GOT entry in 'libc.so' to '0x404070',
....
- linker processes relocation entry 'stdout' of type R_X86_64_COPY from 'a.out',
- it goes through object list excluding 'a.out': ['libstdc++.so', 'libc.so']. The symbol is found in 'libc.so',
- linker copies the 'stdout' symbol content from 'libc.so' to memory at address '0x404070' (in 'a.out' object).

Objects are relocated in reverse order they were loaded. So in the example above, linker starts with relocating
'libc.so' and ends with 'a.out'. It is necessary e.g. when linking with 'libstdc++.so' - there are many
relocations which symbols are found in 'libstdc++.so', so they need to be resolved before their contents are
copied to 'a.out'. That also matches GNU ld.so behavior.
This commit is contained in:
Mateusz Tabaka
2020-12-29 13:58:45 +01:00
parent 1a0edd8eeb
commit a7480ea656
14 changed files with 829 additions and 1113 deletions
+11 -15
View File
@@ -17,21 +17,17 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
(*ti).ti_offset
);
if let Some(tcb) = Tcb::current() {
if let Some(tls) = tcb.tls() {
if let Some(masters) = tcb.masters() {
if let Some(master) = masters.get((*ti).ti_module as usize) {
let addr = tls
.as_mut_ptr()
.add(master.offset + (*ti).ti_offset as usize);
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
ti,
(*ti).ti_module,
(*ti).ti_offset,
addr
);
return addr as *mut c_void;
}
if let Some(masters) = tcb.masters() {
if let Some(master) = masters.get((*ti).ti_module as usize) {
let addr = tcb.tls_end.sub(master.offset).add((*ti).ti_offset as usize);
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
ti,
(*ti).ti_module,
(*ti).ti_offset,
addr
);
return addr as *mut c_void;
}
}
}
+6 -32
View File
@@ -50,13 +50,11 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut
let tcb = match Tcb::current() {
Some(tcb) => tcb,
None => {
eprintln!("dlopen: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlopen: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
@@ -67,28 +65,12 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut
let id = match (cbs.load_library)(&mut linker, filename) {
Err(err) => {
eprintln!("dlopen: failed to load {:?}", filename);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
Ok(id) => id,
};
if let Some(fname) = filename {
if let Err(err) = (cbs.link)(&mut linker, None, None, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", fname, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
if let Err(err) = (cbs.run_init)(&mut linker, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", fname, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
}
id as *mut c_void
}
@@ -104,14 +86,12 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
let tcb = match Tcb::current() {
Some(tcb) => tcb,
None => {
eprintln!("dlsym: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlsym: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
@@ -119,12 +99,12 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
let linker = (&*tcb.linker_ptr).lock();
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Some(global) = (cbs.get_sym)(&linker, symbol_str, Some(handle as usize)) {
global.as_ptr()
} else {
eprintln!("dlsym: symbol not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
match (cbs.get_sym)(&linker, handle as usize, symbol_str) {
Some(sym) => sym,
_ => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
}
}
}
@@ -133,24 +113,18 @@ pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
let tcb = match Tcb::current() {
Some(tcb) => tcb,
None => {
eprintln!("dlclose: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlclose: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let mut linker = (&*tcb.linker_ptr).lock();
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Err(err) = (cbs.run_fini)(&mut linker, Some(handle as usize)) {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
(cbs.unload)(&mut linker, handle as usize);
0
}