Merge branch 'globalsyms' into 'master'
Fix global symbols relocations See merge request redox-os/relibc!325
This commit is contained in:
@@ -4,3 +4,5 @@ sysroot/
|
||||
**/target/
|
||||
.gdb_history
|
||||
*.patch
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
+11
-15
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ unsafe fn inner_scanf(
|
||||
r.commit();
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
return Ok(matched);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-28
@@ -1,15 +1,12 @@
|
||||
use super::linker::{Linker, Symbol, DSO};
|
||||
use super::linker::Linker;
|
||||
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 link:
|
||||
Box<dyn Fn(&mut Linker, Option<&str>, Option<DSO>, Option<usize>) -> Result<Option<usize>>>,
|
||||
pub get_sym: Box<dyn Fn(&Linker, &str, Option<usize>) -> Option<Symbol>>,
|
||||
pub run_init: Box<dyn Fn(&Linker, Option<usize>) -> Result<()>>,
|
||||
pub run_fini: Box<dyn Fn(&Linker, Option<usize>) -> Result<()>>,
|
||||
pub get_sym: Box<dyn Fn(&Linker, usize, &str) -> Option<*mut c_void>>,
|
||||
}
|
||||
|
||||
impl LinkerCallbacks {
|
||||
@@ -17,37 +14,19 @@ impl LinkerCallbacks {
|
||||
LinkerCallbacks {
|
||||
unload: Box::new(unload),
|
||||
load_library: Box::new(load_library),
|
||||
link: Box::new(link),
|
||||
get_sym: Box::new(get_sym),
|
||||
run_init: Box::new(run_init),
|
||||
run_fini: Box::new(run_fini),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unload(linker: &mut Linker, libspace: usize) {
|
||||
linker.unload(libspace)
|
||||
fn unload(linker: &mut Linker, lib_id: usize) {
|
||||
linker.unload(lib_id)
|
||||
}
|
||||
|
||||
fn load_library(linker: &mut Linker, name: Option<&str>) -> Result<usize> {
|
||||
linker.load_library(name)
|
||||
}
|
||||
|
||||
fn link(
|
||||
linker: &mut Linker,
|
||||
primary_opt: Option<&str>,
|
||||
dso: Option<DSO>,
|
||||
libspace: Option<usize>,
|
||||
) -> Result<Option<usize>> {
|
||||
linker.link(primary_opt, dso, libspace)
|
||||
}
|
||||
|
||||
fn get_sym(linker: &Linker, name: &str, libspace: Option<usize>) -> Option<Symbol> {
|
||||
linker.get_sym(name, libspace)
|
||||
}
|
||||
fn run_init(linker: &Linker, libspace: Option<usize>) -> Result<()> {
|
||||
linker.run_init(libspace)
|
||||
}
|
||||
fn run_fini(linker: &Linker, libspace: Option<usize>) -> Result<()> {
|
||||
linker.run_fini(libspace)
|
||||
fn get_sym(linker: &Linker, lib_id: usize, name: &str) -> Option<*mut c_void> {
|
||||
linker.get_sym(lib_id, name)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
use super::{
|
||||
debug::{RTLDDebug, _r_debug},
|
||||
linker::Symbol,
|
||||
tcb::{round_up, Master},
|
||||
};
|
||||
use crate::{
|
||||
header::{errno::STR_ERROR, sys_mman},
|
||||
platform::{errno, types::c_void},
|
||||
};
|
||||
use alloc::{
|
||||
collections::BTreeMap,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
mem::{size_of, transmute},
|
||||
ptr, slice,
|
||||
};
|
||||
use goblin::{
|
||||
elf::{
|
||||
header::ET_DYN,
|
||||
program_header,
|
||||
r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH},
|
||||
section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY},
|
||||
sym, Elf,
|
||||
},
|
||||
error::{Error, Result},
|
||||
};
|
||||
|
||||
/// 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,
|
||||
pub dlopened: bool,
|
||||
pub entry_point: usize,
|
||||
pub runpath: Option<String>,
|
||||
/// Loaded library in-memory data
|
||||
pub mmap: &'static mut [u8],
|
||||
pub global_syms: BTreeMap<String, Symbol>,
|
||||
pub weak_syms: BTreeMap<String, Symbol>,
|
||||
pub dependencies: Vec<String>,
|
||||
/// .init_array addr and len
|
||||
pub init_array: (usize, usize),
|
||||
/// .fini_array addr and len
|
||||
pub fini_array: (usize, usize),
|
||||
pub tls_module_id: usize,
|
||||
pub tls_offset: usize,
|
||||
pub use_count: usize,
|
||||
}
|
||||
|
||||
impl DSO {
|
||||
pub fn new(
|
||||
path: &str,
|
||||
data: &Vec<u8>,
|
||||
base_addr: Option<usize>,
|
||||
dlopened: bool,
|
||||
id: usize,
|
||||
tls_module_id: usize,
|
||||
tls_offset: usize,
|
||||
) -> Result<(DSO, Option<Master>)> {
|
||||
let elf = Elf::parse(data)?;
|
||||
let (mmap, tcb_master) = DSO::mmap_and_copy(&path, &elf, &data, base_addr, tls_offset)?;
|
||||
let (global_syms, weak_syms) = DSO::collect_syms(&elf, &mmap)?;
|
||||
let (init_array, fini_array) = DSO::init_fini_arrays(&elf, mmap.as_ptr() as usize);
|
||||
|
||||
let name = match elf.soname {
|
||||
Some(soname) => soname.to_string(),
|
||||
_ => basename(&path),
|
||||
};
|
||||
let tls_offset = match tcb_master {
|
||||
Some(ref master) => master.offset,
|
||||
_ => 0,
|
||||
};
|
||||
let entry_point = if is_pie_enabled(&elf) {
|
||||
mmap.as_ptr() as usize + elf.header.e_entry as usize
|
||||
} else {
|
||||
elf.header.e_entry as usize
|
||||
};
|
||||
let dso = DSO {
|
||||
name: name,
|
||||
id: id,
|
||||
use_count: 1,
|
||||
dlopened: dlopened,
|
||||
entry_point: entry_point,
|
||||
runpath: DSO::get_runpath(&path, &elf)?,
|
||||
mmap: mmap,
|
||||
global_syms: global_syms,
|
||||
weak_syms: weak_syms,
|
||||
dependencies: elf.libraries.iter().map(|s| s.to_string()).collect(),
|
||||
init_array: init_array,
|
||||
fini_array: fini_array,
|
||||
tls_module_id: tls_module_id,
|
||||
tls_offset: tls_offset,
|
||||
};
|
||||
return Ok((dso, tcb_master));
|
||||
}
|
||||
|
||||
pub fn get_sym(&self, name: &str) -> Option<Symbol> {
|
||||
if let Some(value) = self.global_syms.get(name) {
|
||||
Some(*value)
|
||||
} else if let Some(value) = self.weak_syms.get(name) {
|
||||
Some(*value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_init(&self) {
|
||||
unsafe {
|
||||
let (addr, size) = self.init_array;
|
||||
for i in (0..size).step_by(8) {
|
||||
let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i);
|
||||
(*func).map(|x| x());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_fini(&self) {
|
||||
unsafe {
|
||||
let (addr, size) = self.fini_array;
|
||||
for i in (0..size).step_by(8).rev() {
|
||||
let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i);
|
||||
(*func).map(|x| x());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_runpath(path: &str, elf: &Elf) -> Result<Option<String>> {
|
||||
if let Some(dynamic) = &elf.dynamic {
|
||||
let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH);
|
||||
match entry {
|
||||
Some(entry) => {
|
||||
let runpath = elf
|
||||
.dynstrtab
|
||||
.get(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)));
|
||||
}
|
||||
_ => return Ok(None),
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
fn mmap_and_copy(
|
||||
path: &str,
|
||||
elf: &Elf,
|
||||
data: &Vec<u8>,
|
||||
base_addr: Option<usize>,
|
||||
tls_offset: usize,
|
||||
) -> Result<(&'static mut [u8], Option<Master>)> {
|
||||
// data for struct LinkMap
|
||||
let mut l_ld = 0;
|
||||
// Calculate virtual memory bounds
|
||||
let bounds = {
|
||||
let mut bounds_opt: Option<(usize, usize)> = None;
|
||||
for ph in elf.program_headers.iter() {
|
||||
let voff = ph.p_vaddr % ph.p_align;
|
||||
let vaddr = (ph.p_vaddr - voff) as usize;
|
||||
let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize);
|
||||
|
||||
match ph.p_type {
|
||||
program_header::PT_DYNAMIC => {
|
||||
l_ld = ph.p_vaddr;
|
||||
}
|
||||
program_header::PT_LOAD => {
|
||||
trace!(" load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph);
|
||||
if let Some(ref mut bounds) = bounds_opt {
|
||||
if vaddr < bounds.0 {
|
||||
bounds.0 = vaddr;
|
||||
}
|
||||
if vaddr + vsize > bounds.1 {
|
||||
bounds.1 = vaddr + vsize;
|
||||
}
|
||||
} else {
|
||||
bounds_opt = Some((vaddr, vaddr + vsize));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
bounds_opt.ok_or(Error::Malformed(
|
||||
"Unable to find PT_LOAD section".to_string(),
|
||||
))?
|
||||
};
|
||||
trace!(" bounds {:#x}, {:#x}", bounds.0, bounds.1);
|
||||
// Allocate memory
|
||||
let mmap = unsafe {
|
||||
if let Some(addr) = base_addr {
|
||||
let size = if is_pie_enabled(&elf) {
|
||||
bounds.1
|
||||
} else {
|
||||
bounds.1 - bounds.0
|
||||
};
|
||||
_r_debug.insert_first(addr as usize, path, addr + l_ld as usize);
|
||||
slice::from_raw_parts_mut(addr as *mut u8, size)
|
||||
} else {
|
||||
let (start, end) = bounds;
|
||||
let size = end - start;
|
||||
let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE;
|
||||
if start != 0 {
|
||||
flags |= sys_mman::MAP_FIXED_NOREPLACE;
|
||||
}
|
||||
trace!("mmap({:#x}, {:x}, {:x})", start, size, flags);
|
||||
let ptr = sys_mman::mmap(
|
||||
start as *mut c_void,
|
||||
size,
|
||||
//TODO: Make it possible to not specify PROT_EXEC on Redox
|
||||
sys_mman::PROT_READ | sys_mman::PROT_WRITE,
|
||||
flags,
|
||||
-1,
|
||||
0,
|
||||
);
|
||||
if ptr as usize == !0
|
||||
/* MAP_FAILED */
|
||||
{
|
||||
return Err(Error::Malformed(format!(
|
||||
"failed to map {}. errno: {}",
|
||||
path, STR_ERROR[errno as usize]
|
||||
)));
|
||||
}
|
||||
if start as *mut c_void != ptr::null_mut() {
|
||||
assert_eq!(
|
||||
ptr, start as *mut c_void,
|
||||
"mmap must always map on the destination we requested"
|
||||
);
|
||||
}
|
||||
ptr::write_bytes(ptr as *mut u8, 0, size);
|
||||
_r_debug.insert(ptr as usize, path, ptr as usize + l_ld as usize);
|
||||
slice::from_raw_parts_mut(ptr as *mut u8, size)
|
||||
}
|
||||
};
|
||||
|
||||
let skip_load_segment_copy = base_addr.is_some();
|
||||
let mut tcb_master = None;
|
||||
|
||||
// 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 vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize);
|
||||
|
||||
match ph.p_type {
|
||||
program_header::PT_LOAD => {
|
||||
if skip_load_segment_copy {
|
||||
continue;
|
||||
}
|
||||
let obj_data = {
|
||||
let range = ph.file_range();
|
||||
match data.get(range.clone()) {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
return Err(Error::Malformed(format!(
|
||||
"failed to read {:x?}",
|
||||
range
|
||||
)))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mmap_data = {
|
||||
let range = if is_pie_enabled(elf) {
|
||||
let addr = ph.p_vaddr as usize;
|
||||
addr..addr + obj_data.len()
|
||||
} else {
|
||||
let addr = ph.p_vaddr as usize - mmap.as_ptr() as usize;
|
||||
addr..addr + obj_data.len()
|
||||
};
|
||||
match mmap.get_mut(range.clone()) {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
return Err(Error::Malformed(format!(
|
||||
"failed to write {:x?}",
|
||||
range
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
trace!(
|
||||
" copy {:#x}, {:#x}: {:#x}, {:#x}",
|
||||
vaddr,
|
||||
vsize,
|
||||
voff,
|
||||
obj_data.len()
|
||||
);
|
||||
mmap_data.copy_from_slice(obj_data);
|
||||
}
|
||||
program_header::PT_TLS => {
|
||||
let ptr = unsafe {
|
||||
if is_pie_enabled(elf) {
|
||||
mmap.as_ptr().add(ph.p_vaddr as usize)
|
||||
} else {
|
||||
ph.p_vaddr as *const u8
|
||||
}
|
||||
};
|
||||
tcb_master = Some(Master {
|
||||
ptr: ptr,
|
||||
len: ph.p_filesz as usize,
|
||||
offset: tls_offset + vsize,
|
||||
});
|
||||
trace!(" tcb master {:x?}", tcb_master);
|
||||
}
|
||||
program_header::PT_DYNAMIC => {
|
||||
// overwrite DT_DEBUG if exist in DYNAMIC segment
|
||||
// first we identify the location of DYNAMIC segment
|
||||
let dyn_start = ph.p_vaddr as usize;
|
||||
let mut debug_start = None;
|
||||
// next we identify the location of DT_DEBUG in .dynamic section
|
||||
if let Some(dynamic) = elf.dynamic.as_ref() {
|
||||
let mut i = 0;
|
||||
for entry in &dynamic.dyns {
|
||||
if entry.d_tag == DT_DEBUG {
|
||||
debug_start = Some(i as usize);
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if let Some(i) = debug_start {
|
||||
let bytes: [u8; size_of::<Dyn>() / 2] =
|
||||
unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) };
|
||||
let start = if is_pie_enabled(elf) {
|
||||
dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
|
||||
} else {
|
||||
dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
|
||||
- mmap.as_mut_ptr() as usize
|
||||
};
|
||||
mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
return Ok((mmap, tcb_master));
|
||||
}
|
||||
|
||||
fn collect_syms(
|
||||
elf: &Elf,
|
||||
mmap: &[u8],
|
||||
) -> Result<(BTreeMap<String, Symbol>, BTreeMap<String, Symbol>)> {
|
||||
let mut globals = BTreeMap::new();
|
||||
let mut weak_syms = BTreeMap::new();
|
||||
for sym in elf.dynsyms.iter() {
|
||||
let bind = sym.st_bind();
|
||||
if sym.st_shndx == SHN_UNDEF as usize
|
||||
|| ![sym::STB_GLOBAL, sym::STB_WEAK].contains(&bind)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let name: String;
|
||||
let value: Symbol;
|
||||
if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
|
||||
name = name_res?.to_string();
|
||||
value = if is_pie_enabled(elf) {
|
||||
Symbol {
|
||||
base: mmap.as_ptr() as usize,
|
||||
value: sym.st_value as usize,
|
||||
size: sym.st_size as usize,
|
||||
}
|
||||
} else {
|
||||
Symbol {
|
||||
base: 0,
|
||||
value: sym.st_value as usize,
|
||||
size: sym.st_size as usize,
|
||||
}
|
||||
};
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
match sym.st_bind() {
|
||||
sym::STB_GLOBAL => {
|
||||
trace!(" global {}: {:x?} = {:p}", &name, sym, value.as_ptr());
|
||||
globals.insert(name, value);
|
||||
}
|
||||
sym::STB_WEAK => {
|
||||
trace!(" weak {}: {:x?} = {:p}", &name, sym, value.as_ptr());
|
||||
weak_syms.insert(name, value);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
return Ok((globals, weak_syms));
|
||||
}
|
||||
|
||||
fn init_fini_arrays(elf: &Elf, mmap_addr: usize) -> ((usize, usize), (usize, usize)) {
|
||||
let mut init_array: (usize, usize) = (0, 0);
|
||||
let mut fini_array: (usize, usize) = (0, 0);
|
||||
for section in elf
|
||||
.section_headers
|
||||
.iter()
|
||||
.filter(|s| s.sh_type == SHT_INIT_ARRAY || s.sh_type == SHT_FINI_ARRAY)
|
||||
{
|
||||
let addr = if is_pie_enabled(&elf) {
|
||||
mmap_addr + section.vm_range().start
|
||||
} else {
|
||||
section.vm_range().start
|
||||
};
|
||||
if section.sh_type == SHT_INIT_ARRAY {
|
||||
init_array = (addr, section.sh_size as usize);
|
||||
} else {
|
||||
fini_array = (addr, section.sh_size as usize);
|
||||
}
|
||||
}
|
||||
return (init_array, fini_array);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DSO {
|
||||
fn drop(&mut self) {
|
||||
self.run_fini();
|
||||
unsafe { sys_mman::munmap(self.mmap.as_mut_ptr() as *mut c_void, self.mmap.len()) };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pie_enabled(elf: &Elf) -> bool {
|
||||
return elf.header.e_type == ET_DYN;
|
||||
}
|
||||
|
||||
fn basename(path: &str) -> String {
|
||||
return path.split("/").last().unwrap_or(path).to_string();
|
||||
}
|
||||
|
||||
fn dirname(path: &str) -> String {
|
||||
let mut parts: Vec<&str> = path.split("/").collect();
|
||||
parts.truncate(parts.len() - 1);
|
||||
return parts.join("/");
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use super::linker::Symbol;
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct DepTree {
|
||||
pub name: String,
|
||||
pub deps: Vec<DepTree>,
|
||||
}
|
||||
|
||||
impl DepTree {
|
||||
pub fn new(name: String) -> DepTree {
|
||||
DepTree {
|
||||
name,
|
||||
deps: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use to represnt a library as well as all th symbols that is loaded withen it.
|
||||
#[derive(Default)]
|
||||
pub struct Library {
|
||||
/// Global symbols
|
||||
pub globals: BTreeMap<String, Symbol>,
|
||||
/// Weak symbols
|
||||
pub weak_syms: BTreeMap<String, Symbol>,
|
||||
/// Loaded library raw data
|
||||
pub objects: BTreeMap<String, Box<[u8]>>,
|
||||
/// Loaded library in-memory data
|
||||
pub mmaps: BTreeMap<String, (usize, &'static mut [u8])>,
|
||||
/// Each object will have its children called once with no repetition.
|
||||
pub dep_tree: DepTree,
|
||||
/// A set used to detect circular dependencies in the Linker::load function
|
||||
pub cir_dep: BTreeSet<String>,
|
||||
pub runpath: Option<String>,
|
||||
}
|
||||
impl Library {
|
||||
pub fn new() -> Library {
|
||||
Default::default()
|
||||
}
|
||||
pub fn get_sym(&self, name: &str) -> Option<Symbol> {
|
||||
if let Some(value) = self.globals.get(name) {
|
||||
Some(*value)
|
||||
} else if let Some(value) = self.weak_syms.get(name) {
|
||||
Some(*value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
+275
-873
File diff suppressed because it is too large
Load Diff
+8
-2
@@ -6,10 +6,16 @@ use crate::{header::sys_auxv::AT_NULL, start::Stack};
|
||||
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub const PATH_SEP: char = ';';
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const PATH_SEP: char = ':';
|
||||
|
||||
mod access;
|
||||
pub mod callbacks;
|
||||
pub mod debug;
|
||||
mod library;
|
||||
mod dso;
|
||||
pub mod linker;
|
||||
pub mod start;
|
||||
pub mod tcb;
|
||||
@@ -73,7 +79,7 @@ pub fn static_init(sp: &'static Stack) {
|
||||
unsafe {
|
||||
STATIC_TCB_MASTER.ptr = ph.p_vaddr as usize as *const u8;
|
||||
STATIC_TCB_MASTER.len = ph.p_filesz as usize;
|
||||
STATIC_TCB_MASTER.offset = vsize - valign;
|
||||
STATIC_TCB_MASTER.offset = valign;
|
||||
|
||||
let tcb = Tcb::new(vsize).expect("failed to allocate TCB");
|
||||
tcb.masters_ptr = &mut STATIC_TCB_MASTER;
|
||||
|
||||
+9
-41
@@ -17,13 +17,9 @@ use crate::{
|
||||
ALLOCATOR,
|
||||
};
|
||||
|
||||
use super::{
|
||||
access::accessible,
|
||||
debug::_r_debug,
|
||||
linker::{Linker, DSO, PATH_SEP},
|
||||
tcb::Tcb,
|
||||
};
|
||||
use super::{access::accessible, debug::_r_debug, linker::Linker, tcb::Tcb, PATH_SEP};
|
||||
use crate::header::sys_auxv::{AT_ENTRY, AT_PHDR};
|
||||
use goblin::elf::header::header64::SIZEOF_EHDR;
|
||||
|
||||
unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
|
||||
//traverse the stack and collect argument vector
|
||||
@@ -214,53 +210,25 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
|
||||
// if we are not running in manual mode, then the main
|
||||
// program is already loaded by the kernel and we want
|
||||
// to use it. on redox, we treat it the same.
|
||||
let program = {
|
||||
let mut pr = None;
|
||||
let base_addr = {
|
||||
let mut base = None;
|
||||
if !is_manual && cfg!(not(target_os = "redox")) {
|
||||
let phdr = *auxv.get(&AT_PHDR).unwrap();
|
||||
if phdr != 0 {
|
||||
let p = DSO {
|
||||
name: path.to_owned(),
|
||||
entry_point: *auxv.get(&AT_ENTRY).unwrap(),
|
||||
// The 0x40 is the size of Elf header not a good idea for different bit size
|
||||
// compatiablility but it will always work on 64 bit systems,
|
||||
base_addr: phdr - 0x40,
|
||||
};
|
||||
pr = Some(p);
|
||||
base = Some(phdr - SIZEOF_EHDR);
|
||||
}
|
||||
}
|
||||
pr
|
||||
base
|
||||
};
|
||||
let mut linker = Linker::new(ld_library_path, false);
|
||||
match linker.load(&path, &path) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
eprintln!("ld.so: failed to load '{}': {}", path, err);
|
||||
unistd::_exit(1);
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
let entry = match linker.link(Some(&path), program, None) {
|
||||
Ok(ok) => match ok {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
eprintln!("ld.so: failed to link '{}': missing entry", path);
|
||||
unistd::_exit(1);
|
||||
loop {}
|
||||
}
|
||||
},
|
||||
let mut linker = Linker::new(ld_library_path);
|
||||
let entry = match linker.load_program(&path, base_addr) {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => {
|
||||
eprintln!("ld.so: failed to link '{}': {}", path, err);
|
||||
unistd::_exit(1);
|
||||
loop {}
|
||||
}
|
||||
};
|
||||
if let Err(e) = linker.run_init(None) {
|
||||
eprintln!("ld.so: failed to run .init_array");
|
||||
unistd::_exit(1);
|
||||
loop {}
|
||||
}
|
||||
if let Some(tcb) = unsafe { Tcb::current() } {
|
||||
tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
|
||||
tcb.mspace = ALLOCATOR.get_book_keeper();
|
||||
|
||||
+44
-24
@@ -1,5 +1,5 @@
|
||||
use alloc::boxed::Box;
|
||||
use core::{mem, ops::Range, ptr, slice};
|
||||
use alloc::vec::Vec;
|
||||
use core::{mem, ptr, slice};
|
||||
use goblin::error::{Error, Result};
|
||||
|
||||
use crate::{header::sys_mman, ld_so::linker::Linker, sync::mutex::Mutex};
|
||||
@@ -7,6 +7,7 @@ use crate::{header::sys_mman, ld_so::linker::Linker, sync::mutex::Mutex};
|
||||
use super::PAGE_SIZE;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct Master {
|
||||
/// Pointer to initial data
|
||||
pub ptr: *const u8,
|
||||
@@ -21,11 +22,6 @@ impl Master {
|
||||
pub unsafe fn data(&self) -> &'static [u8] {
|
||||
slice::from_raw_parts(self.ptr, self.len)
|
||||
}
|
||||
|
||||
/// The region of TLS that the master will initialize
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.offset..self.offset + self.len
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -43,6 +39,8 @@ pub struct Tcb {
|
||||
pub masters_ptr: *mut Master,
|
||||
/// Size of the masters list in bytes (multiple of mem::size_of::<Master>())
|
||||
pub masters_len: usize,
|
||||
/// Index of last copied Master
|
||||
pub last_master_copied: usize,
|
||||
/// Pointer to dynamic linker
|
||||
pub linker_ptr: *const Mutex<Linker>,
|
||||
/// pointer to rust memory allocator structure
|
||||
@@ -52,10 +50,10 @@ pub struct Tcb {
|
||||
impl Tcb {
|
||||
/// Create a new TCB
|
||||
pub unsafe fn new(size: usize) -> Result<&'static mut Self> {
|
||||
let (tls, tcb_page) = Self::os_new(size)?;
|
||||
let (tls, tcb_page) = Self::os_new(round_up(size, PAGE_SIZE))?;
|
||||
|
||||
let tcb_ptr = tcb_page.as_mut_ptr() as *mut Self;
|
||||
// println!("New TCB: {:p}", tcb_ptr);
|
||||
trace!("New TCB: {:p}", tcb_ptr);
|
||||
ptr::write(
|
||||
tcb_ptr,
|
||||
Self {
|
||||
@@ -65,6 +63,7 @@ impl Tcb {
|
||||
tcb_len: tcb_page.len(),
|
||||
masters_ptr: ptr::null_mut(),
|
||||
masters_len: 0,
|
||||
last_master_copied: 0,
|
||||
linker_ptr: ptr::null(),
|
||||
mspace: 0,
|
||||
},
|
||||
@@ -109,26 +108,34 @@ impl Tcb {
|
||||
}
|
||||
|
||||
/// Copy data from masters
|
||||
pub unsafe fn copy_masters(&self) -> Result<()> {
|
||||
pub unsafe fn copy_masters(&mut self) -> Result<()> {
|
||||
//TODO: Complain if masters or tls exist without the other
|
||||
if let Some(tls) = self.tls() {
|
||||
if let Some(masters) = self.masters() {
|
||||
for (i, master) in masters.iter().enumerate() {
|
||||
let range = master.range();
|
||||
let data = master.data();
|
||||
for (i, master) in masters
|
||||
.iter()
|
||||
.skip(self.last_master_copied)
|
||||
.filter(|m| m.len > 0)
|
||||
.enumerate()
|
||||
{
|
||||
let range =
|
||||
self.tls_len - master.offset..self.tls_len - master.offset + master.len;
|
||||
if let Some(tls_data) = tls.get_mut(range) {
|
||||
// println!(
|
||||
// "tls master {}: {:p}, {:#x}: {:p}, {:#x}",
|
||||
// i,
|
||||
// data.as_ptr(), data.len(),
|
||||
// tls_data.as_mut_ptr(), tls_data.len()
|
||||
// );
|
||||
|
||||
let data = master.data();
|
||||
trace!(
|
||||
"tls master {}: {:p}, {:#x}: {:p}, {:#x}",
|
||||
i,
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
tls_data.as_mut_ptr(),
|
||||
tls_data.len()
|
||||
);
|
||||
tls_data.copy_from_slice(data);
|
||||
} else {
|
||||
return Err(Error::Malformed(format!("failed to copy tls master {}", i)));
|
||||
}
|
||||
}
|
||||
self.last_master_copied = masters.len();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,10 +143,19 @@ impl Tcb {
|
||||
}
|
||||
|
||||
/// The initial images for TLS
|
||||
pub unsafe fn set_masters(&mut self, mut masters: Box<[Master]>) {
|
||||
self.masters_ptr = masters.as_mut_ptr();
|
||||
self.masters_len = masters.len() * mem::size_of::<Master>();
|
||||
mem::forget(masters);
|
||||
pub unsafe fn append_masters(&mut self, mut new_masters: Vec<Master>) {
|
||||
if self.masters_ptr.is_null() {
|
||||
self.masters_ptr = new_masters.as_mut_ptr();
|
||||
self.masters_len = new_masters.len() * mem::size_of::<Master>();
|
||||
mem::forget(new_masters);
|
||||
} else {
|
||||
let len = self.masters_len / mem::size_of::<Master>();
|
||||
let mut masters = Vec::from_raw_parts(self.masters_ptr, len, len);
|
||||
masters.extend(new_masters.into_iter());
|
||||
self.masters_ptr = masters.as_mut_ptr();
|
||||
self.masters_len = masters.len() * mem::size_of::<Master>();
|
||||
mem::forget(masters);
|
||||
}
|
||||
}
|
||||
|
||||
/// Activate TLS
|
||||
@@ -217,3 +233,7 @@ impl Tcb {
|
||||
//TODO: Consider setting FS offset to TCB pointer
|
||||
}
|
||||
}
|
||||
|
||||
pub fn round_up(value: usize, alignment: usize) -> usize {
|
||||
return (value + alignment - 1) & (!(alignment - 1));
|
||||
}
|
||||
|
||||
@@ -78,7 +78,6 @@ macro_rules! trace {
|
||||
($($arg:tt)*) => ({
|
||||
use $crate::{Pal, Sys};
|
||||
eprintln!($($arg)*);
|
||||
Sys::fsync(2);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// From https://www.remlab.net/op/futex-misc.shtml
|
||||
//TODO: improve implementation
|
||||
|
||||
use crate::platform::{types::*, Pal, Sys};
|
||||
use super::AtomicLock;
|
||||
use crate::platform::{types::*, Pal, Sys};
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
pub struct Semaphore {
|
||||
@@ -29,7 +29,7 @@ impl Semaphore {
|
||||
value,
|
||||
value - 1,
|
||||
Ordering::Acquire,
|
||||
Ordering::Relaxed
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(ok) => return,
|
||||
Err(err) => {
|
||||
|
||||
+35
-40
@@ -12,6 +12,7 @@ EXPECT_NAMES=\
|
||||
fcntl/create \
|
||||
fcntl/fcntl \
|
||||
fnmatch \
|
||||
futimens \
|
||||
libgen \
|
||||
locale \
|
||||
math \
|
||||
@@ -22,18 +23,25 @@ EXPECT_NAMES=\
|
||||
setjmp \
|
||||
sigaction \
|
||||
signal \
|
||||
stdio/all \
|
||||
stdio/buffer \
|
||||
stdio/fgets \
|
||||
stdio/fputs \
|
||||
stdio/fread \
|
||||
stdio/freopen \
|
||||
stdio/fseek \
|
||||
stdio/fwrite \
|
||||
stdio/getc_unget \
|
||||
stdio/mutex \
|
||||
stdio/popen \
|
||||
stdio/printf \
|
||||
stdio/rename \
|
||||
stdio/scanf \
|
||||
stdio/setvbuf \
|
||||
stdio/sprintf \
|
||||
stdio/printf_space_pad \
|
||||
stdio/ungetc_ftell \
|
||||
stdio/ungetc_multiple \
|
||||
stdio/fscanf_offby1 \
|
||||
stdio/fscanf \
|
||||
stdio/printf_neg_pad \
|
||||
@@ -69,6 +77,7 @@ EXPECT_NAMES=\
|
||||
sys_mman \
|
||||
time/asctime \
|
||||
time/gmtime \
|
||||
time/localtime \
|
||||
time/macros \
|
||||
time/mktime \
|
||||
time/strftime \
|
||||
@@ -82,6 +91,8 @@ EXPECT_NAMES=\
|
||||
unistd/fork \
|
||||
unistd/fsync \
|
||||
unistd/ftruncate \
|
||||
unistd/getopt \
|
||||
unistd/getopt_long \
|
||||
unistd/pipe \
|
||||
unistd/rmdir \
|
||||
unistd/sleep \
|
||||
@@ -98,34 +109,20 @@ EXPECT_NAMES=\
|
||||
wchar/wcsrchr \
|
||||
wchar/wcsstr \
|
||||
wchar/wcstod \
|
||||
wchar/wcstok \
|
||||
wchar/wcstol \
|
||||
wchar/wcscasecmp \
|
||||
wchar/wcsncasecmp \
|
||||
wctype/towlower \
|
||||
wctype/towupper
|
||||
# TODO: Fix these
|
||||
# mkfifo
|
||||
# netdb/netdb \
|
||||
|
||||
# issues with linking stdin, stdout, stderr
|
||||
STATIC_ONLY_NAMES=\
|
||||
futimens \
|
||||
stdio/all \
|
||||
stdio/buffer \
|
||||
stdio/fgets \
|
||||
stdio/freopen \
|
||||
stdio/getc_unget \
|
||||
stdio/setvbuf \
|
||||
stdio/ungetc_multiple \
|
||||
time/localtime \
|
||||
wchar/wcstok \
|
||||
wctype/towlower \
|
||||
wctype/towupper \
|
||||
# need to call fini in ld_so's _start
|
||||
STATIC_ONLY_NAMES+=\
|
||||
destructor \
|
||||
# issues with linking optarg, optind etc.
|
||||
STATIC_ONLY_NAMES+=\
|
||||
unistd/getopt \
|
||||
unistd/getopt_long \
|
||||
STATIC_ONLY_NAMES=\
|
||||
destructor
|
||||
|
||||
|
||||
DYNAMIC_ONLY_NAMES=\
|
||||
dlfcn
|
||||
@@ -205,9 +202,15 @@ FLAGS=\
|
||||
-I .
|
||||
|
||||
STATIC_FLAGS=\
|
||||
../sysroot/lib/libc.a \
|
||||
-static
|
||||
|
||||
DYNAMIC_FLAGS=\
|
||||
-Wl,--enable-new-dtags \
|
||||
-Wl,-export-dynamic
|
||||
|
||||
../sysroot:
|
||||
$(MAKE) -C .. sysroot
|
||||
|
||||
NATIVE_RELIBC?=0
|
||||
ifeq ($(NATIVE_RELIBC),0)
|
||||
FLAGS+=\
|
||||
@@ -218,32 +221,24 @@ FLAGS+=\
|
||||
../sysroot/lib/crti.o \
|
||||
../sysroot/lib/crtn.o
|
||||
|
||||
../sysroot:
|
||||
$(MAKE) -C .. sysroot
|
||||
|
||||
bins_static/%: %.c ../sysroot
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS)
|
||||
|
||||
SYSROOT_LIB=$(shell realpath ../sysroot/lib/)
|
||||
|
||||
DYNAMIC_FLAGS=\
|
||||
STATIC_FLAGS+=\
|
||||
$(SYSROOT_LIB)/libc.a
|
||||
|
||||
DYNAMIC_FLAGS+=\
|
||||
-Wl,-dynamic-linker=$(SYSROOT_LIB)/ld64.so.1 \
|
||||
-Wl,--enable-new-dtags \
|
||||
-Wl,-rpath=$(SYSROOT_LIB) \
|
||||
-Wl,-export-dynamic \
|
||||
-Wl,-rpath=$(SYSROOT_LIB):\$$ORIGIN \
|
||||
-L $(SYSROOT_LIB) \
|
||||
-lc
|
||||
|
||||
bins_dynamic/%: %.c ../sysroot
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
|
||||
else
|
||||
bins_static/%: %.c
|
||||
DEPS=../sysroot
|
||||
endif
|
||||
|
||||
bins_static/%: %.c $(DEPS)
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS)
|
||||
|
||||
bins_dynamic/%: %.c
|
||||
bins_dynamic/%: %.c $(DEPS)
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) "$<" -o "$@" $(FLAGS)
|
||||
endif
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ int main(int argc, char** argv) {
|
||||
char temp[] = "/tmp/stattest-XXXXXX";
|
||||
const char file[] = "/mkfifo_fifo";
|
||||
int len = sizeof(temp) + sizeof(int);
|
||||
char* path = malloc(len * sizeof(char));
|
||||
char* path = calloc(len, sizeof(char));
|
||||
|
||||
if (path == NULL) {
|
||||
fprintf(stderr, "Could not allocate: %s\n", strerror(errno));
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ void test(char* fmt_in, char* input, struct params *p, ...) {
|
||||
int main(void) {
|
||||
struct params p = { .c = 'a' };
|
||||
|
||||
test("%hhd %d", "12 345", &p, &p.sa, &p.ia);
|
||||
test("%hd %d", "12 345", &p, &p.sa, &p.ia);
|
||||
test("%x %i %i", "12 0x345 010", &p, &p.ia, &p.ib, &p.ic);
|
||||
test("%f.%lf", "0.1.0.2", &p, &p.fa, &p.da);
|
||||
test("%p", "0xABCDEF", &p, &p.ptr);
|
||||
|
||||
Reference in New Issue
Block a user