Files
RedBear-OS/src/macros.rs
T
Mateusz Tabaka a7480ea656 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.
2021-01-03 16:04:40 +01:00

292 lines
7.4 KiB
Rust

#[macro_export]
macro_rules! c_str {
($lit:expr) => {
#[allow(unused_unsafe)]
unsafe {
$crate::c_str::CStr::from_bytes_with_nul_unchecked(concat!($lit, "\0").as_bytes())
}
};
}
/// Print to stdout
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
let _ = write!($crate::platform::FileWriter(1), $($arg)*);
});
}
/// Print with new line to stdout
#[macro_export]
macro_rules! println {
() => (print!("\n"));
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
/// Print to stderr
#[macro_export]
macro_rules! eprint {
($($arg:tt)*) => ({
use core::fmt::Write;
let _ = write!($crate::platform::FileWriter(2), $($arg)*);
});
}
/// Print with new line to stderr
#[macro_export]
macro_rules! eprintln {
() => (eprint!("\n"));
($fmt:expr) => (eprint!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*));
}
/// Lifted from libstd
#[macro_export]
macro_rules! dbg {
() => {
eprintln!("[{}:{}]", file!(), line!());
};
($val:expr) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
eprintln!(
"[{}:{}] {} = {:#?}",
file!(),
line!(),
stringify!($val),
&tmp
);
tmp
}
}
};
}
#[macro_export]
#[cfg(not(feature = "trace"))]
macro_rules! trace {
($($arg:tt)*) => {};
}
#[macro_export]
#[cfg(feature = "trace")]
macro_rules! trace {
($($arg:tt)*) => ({
use $crate::{Pal, Sys};
eprintln!($($arg)*);
});
}
#[macro_export]
#[cfg(not(feature = "trace"))]
macro_rules! trace_expr {
($expr:expr, $($arg:tt)*) => {
$expr
};
}
#[macro_export]
#[cfg(feature = "trace")]
macro_rules! trace_expr {
($expr:expr, $($arg:tt)*) => ({
use $crate::header::errno::STR_ERROR;
use $crate::platform;
trace!("{}", format_args!($($arg)*));
#[allow(unused_unsafe)]
let trace_old_errno = unsafe { platform::errno };
#[allow(unused_unsafe)]
unsafe { platform::errno = 0; }
let ret = $expr;
#[allow(unused_unsafe)]
let trace_errno = unsafe { platform::errno } as isize;
if trace_errno == 0 {
#[allow(unused_unsafe)]
unsafe { platform::errno = trace_old_errno; }
}
let trace_strerror = if trace_errno >= 0 && trace_errno < STR_ERROR.len() as isize {
STR_ERROR[trace_errno as usize]
} else {
"Unknown error"
};
trace!("{} = {} ({}, {})", format_args!($($arg)*), ret, trace_errno, trace_strerror);
ret
});
}
#[macro_export]
macro_rules! strto_impl {
(
$rettype:ty, $signed:expr, $maxval:expr, $minval:expr, $s:ident, $endptr:ident, $base:ident
) => {{
// ensure these are constants
const CHECK_SIGN: bool = $signed;
const MAX_VAL: $rettype = $maxval;
const MIN_VAL: $rettype = $minval;
let set_endptr = |idx: isize| {
if !$endptr.is_null() {
// This is stupid, but apparently strto* functions want
// const input but mut output, yet the man page says
// "stores the address of the first invalid character in *endptr"
// so obviously it doesn't want us to clone it.
*$endptr = $s.offset(idx) as *mut _;
}
};
let invalid_input = || {
platform::errno = EINVAL;
set_endptr(0);
};
// only valid bases are 2 through 36
if $base != 0 && ($base < 2 || $base > 36) {
invalid_input();
return 0;
}
let mut idx = 0;
// skip any whitespace at the beginning of the string
while ctype::isspace(*$s.offset(idx) as c_int) != 0 {
idx += 1;
}
// check for +/-
let positive = match is_positive(*$s.offset(idx)) {
Some((pos, i)) => {
idx += i;
pos
}
None => {
invalid_input();
return 0;
}
};
// convert the string to a number
let num_str = $s.offset(idx);
let res = match $base {
0 => detect_base(num_str)
.and_then(|($base, i)| convert_integer(num_str.offset(i), $base)),
8 => convert_octal(num_str),
16 => convert_hex(num_str),
_ => convert_integer(num_str, $base),
};
// check for error parsing octal/hex prefix
// also check to ensure a number was indeed parsed
let (num, i, overflow) = match res {
Some(res) => res,
None => {
invalid_input();
return 0;
}
};
idx += i;
let overflow = if CHECK_SIGN {
overflow || (num as c_long).is_negative()
} else {
overflow
};
// account for the sign
let num = num as $rettype;
let num = if overflow {
platform::errno = ERANGE;
if CHECK_SIGN {
if positive {
MAX_VAL
} else {
MIN_VAL
}
} else {
MAX_VAL
}
} else {
if positive {
num
} else {
// not using -num to keep the compiler happy
num.overflowing_neg().0
}
};
set_endptr(idx);
num
}};
}
#[macro_export]
macro_rules! strto_float_impl {
($type:ident, $s:expr, $endptr:expr) => {{
let mut s = $s;
let endptr = $endptr;
while ctype::isspace(*s as c_int) != 0 {
s = s.offset(1);
}
let mut result: $type = 0.0;
let mut radix = 10;
let negative = match *s as u8 {
b'-' => {
s = s.offset(1);
true
}
b'+' => {
s = s.offset(1);
false
}
_ => false,
};
if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' {
s = s.offset(2);
radix = 16;
}
while let Some(digit) = (*s as u8 as char).to_digit(radix) {
result *= radix as $type;
result += digit as $type;
s = s.offset(1);
}
if *s as u8 == b'.' {
s = s.offset(1);
let mut i = 1.0;
while let Some(digit) = (*s as u8 as char).to_digit(radix) {
i *= radix as $type;
result += digit as $type / i;
s = s.offset(1);
}
}
if !endptr.is_null() {
// This is stupid, but apparently strto* functions want
// const input but mut output, yet the man page says
// "stores the address of the first invalid character in *endptr"
// so obviously it doesn't want us to clone it.
*endptr = s as *mut _;
}
if negative {
-result
} else {
result
}
}};
}