Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

turn x86 in a test-only target #2

Merged
merged 1 commit into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
---

TODO
- [ ] decoder
- [ ] terminal printer
- [ ] float support
- [ ] test in QEMU
- [ ] work on macro interface

Expand All @@ -24,6 +21,7 @@ TODO
- Custom linking (linker script) is required
- Single, global logger instance
- The `@` character is not allowed in strings but otherwise UTF-8 is supported &#x1F44D
- No x86 support. This architecture is exclusively used for testing at the moment.
- ???

## Intended use
Expand Down
9 changes: 4 additions & 5 deletions decoder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,8 @@ fn parse_args<'t>(bytes: &mut &[u8], format: &str, table: &'t Table) -> Result<V
args.push(Arg::Uxx(data as u64));
}

Type::BitField(_) => {}
Type::Bool => {}
// {:?}
Type::BitField(_) => todo!(),
Type::Bool => todo!(),
Type::Format => {
let index = leb128::read::unsigned(bytes).map_err(drop)?;
let (level, format) = table.get(index as usize)?;
Expand All @@ -194,7 +193,7 @@ fn parse_args<'t>(bytes: &mut &[u8], format: &str, table: &'t Table) -> Result<V
let data = bytes.read_i8().map_err(drop)?;
args.push(Arg::Ixx(data as i64));
}
Type::Str => {}
Type::Str => todo!(),
Type::U16 => {
let data = bytes.read_u16::<LE>().map_err(drop)?;
args.push(Arg::Uxx(data as u64));
Expand All @@ -213,7 +212,7 @@ fn parse_args<'t>(bytes: &mut &[u8], format: &str, table: &'t Table) -> Result<V
let data = bytes.read_u32::<LE>().map_err(drop)?;
args.push(Arg::F32(f32::from_bits(data)));
}
Type::Slice => {}
Type::Slice => todo!(),
}
}

Expand Down
102 changes: 69 additions & 33 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,9 @@ pub fn format(ts: TokenStream) -> TokenStream {
))
}

let id = rand::random::<u64>();
let section = format!(".binfmt.fmt.{}", id);
let sym = format!("{}@{}", fs, id);
let sym = mksym(&fs, "fmt");
exprs.push(quote!(
#[link_section = #section]
#[export_name = #sym]
static S: u8 = 0;

f.str(&binfmt::export::str(&S));
f.str(&binfmt::export::str(#sym));
));
exprs.push(quote!(match self {
#(#arms)*
Expand Down Expand Up @@ -212,6 +206,7 @@ fn fields(fields: &Fields, format: &mut String, mut kind: Kind) -> Vec<TokenStre
list
}

// TODO other logging levels
#[proc_macro]
pub fn info(ts: TokenStream) -> TokenStream {
let log = parse_macro_input!(ts as Log);
Expand All @@ -235,20 +230,14 @@ pub fn info(ts: TokenStream) -> TokenStream {
Err(e) => return e.to_compile_error().into(),
};

let id = rand::random::<u64>();
let section = format!(".binfmt.info.{}", id);
let sym = format!("{}@{}", ls, id);
let sym = mksym(&ls, "info");
quote!({
if cfg!(feature = "binfmt-on") {
if binfmt::export::Level::Info >= binfmt::export::threshold() {
if let Some(mut _fmt_) = binfmt::export::acquire() {
match (binfmt::export::timestamp(), #(#args),*) {
(ts, #(ref #pats),*) => {
#[link_section = #section]
#[export_name = #sym]
static S: u8 = 0;

_fmt_.str(&binfmt::export::str(&S));
match (binfmt::export::timestamp(), #(&#args),*) {
(ts, #(#pats),*) => {
_fmt_.str(&binfmt::export::str(#sym));
_fmt_.leb64(ts);
#(#exprs;)*
binfmt::export::release(_fmt_)
Expand All @@ -261,6 +250,44 @@ pub fn info(ts: TokenStream) -> TokenStream {
.into()
}

// TODO share more code with `info`
#[proc_macro]
pub fn winfo(ts: TokenStream) -> TokenStream {
let write = parse_macro_input!(ts as Write);
let ls = write.litstr.value();
let params = match Param::parse(&ls) {
Ok(args) => args,
Err(e) => {
return parse::Error::new(write.litstr.span(), e)
.to_compile_error()
.into()
}
};

let args = write
.rest
.map(|(_, exprs)| exprs.into_iter().collect())
.unwrap_or(vec![]);

let (pats, exprs) = match Codegen::new(&params, args.len(), write.litstr.span()) {
Ok(cg) => (cg.pats, cg.exprs),
Err(e) => return e.to_compile_error().into(),
};

let f = &write.fmt;
let sym = mksym(&ls, "info");
quote!({
match (&mut #f, binfmt::export::timestamp(), #(&#args),*) {
(_fmt_, ts, #(#pats),*) => {
_fmt_.str(&binfmt::export::str(#sym));
_fmt_.leb64(ts);
#(#exprs;)*
}
}
})
.into()
}

struct Log {
litstr: LitStr,
rest: Option<(Token![,], Punctuated<Expr, Token![,]>)>,
Expand Down Expand Up @@ -292,18 +319,14 @@ pub fn intern(ts: TokenStream) -> TokenStream {
.into();
}

let id = rand::random::<u64>();
let section = format!(".binfmt.str.{}", id);
let sym = format!("{}@{}", ls, id);
let sym = mksym(&ls, "str");
quote!({
#[link_section = #section]
#[export_name = #sym]
static S: u8 = 0;
binfmt::export::str(&S)
binfmt::export::str(#sym)
})
.into()
}

// TODO(likely) remove
#[proc_macro]
pub fn internp(ts: TokenStream) -> TokenStream {
let lit = parse_macro_input!(ts as LitStr);
Expand Down Expand Up @@ -353,20 +376,33 @@ pub fn write(ts: TokenStream) -> TokenStream {
};

let fmt = &write.fmt;
let sym = mksym(&ls, "fmt");
quote!(match (#fmt, #(&#args),*) {
(ref mut _fmt_, #(#pats),*) => {
_fmt_.str(&binfmt::export::str(#sym));
#(#exprs;)*
}
})
.into()
}

fn mksym(string: &str, section: &str) -> TokenStream2 {
let id = rand::random::<u64>();
let section = format!(".binfmt.fmt.{}", id);
let sym = format!("{}@{}", ls, id);
quote!(match (#fmt, #(#args),*) {
(ref mut _fmt_, #(ref #pats),*) => {
let section = format!(".binfmt.{}.{}", section, string);
let sym = format!("{}@{}", string, id);
quote!(match () {
#[cfg(target_arch = "x86_64")]
() => {
binfmt::export::fetch_add_string_index()
}
#[cfg(not(target_arch = "x86_64"))]
() => {
#[link_section = #section]
#[export_name = #sym]
static S: u8 = 0;

_fmt_.str(&binfmt::export::str(&S));
#(#exprs;)*
&S as *const u8 as usize
}
})
.into()
}

struct Write {
Expand Down
41 changes: 40 additions & 1 deletion src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,72 @@ use crate::{Formatter, Str};

pub use common::Level;

#[cfg(target_arch = "x86_64")]
thread_local! {
static I: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
static T: core::sync::atomic::AtomicU64 =
core::sync::atomic::AtomicU64::new(0);
}

#[cfg(target_arch = "x86_64")]
pub fn fetch_string_index() -> usize {
I.with(|i| i.load(core::sync::atomic::Ordering::Relaxed))
}

#[cfg(target_arch = "x86_64")]
pub fn fetch_add_string_index() -> usize {
I.with(|i| i.fetch_add(1, core::sync::atomic::Ordering::Relaxed))
}

#[cfg(target_arch = "x86_64")]
pub fn fetch_timestamp() -> u64 {
T.with(|i| i.load(core::sync::atomic::Ordering::Relaxed))
}

pub fn threshold() -> Level {
// TODO add Cargo features
Level::Info
}

#[cfg(target_arch = "x86_64")]
pub fn acquire() -> Option<Formatter> {
None
}

#[cfg(not(target_arch = "x86_64"))]
pub fn acquire() -> Option<Formatter> {
extern "Rust" {
fn _binfmt_acquire() -> Option<Formatter>;
}
unsafe { _binfmt_acquire() }
}

#[cfg(target_arch = "x86_64")]
pub fn release(_: Formatter) {}

#[cfg(not(target_arch = "x86_64"))]
pub fn release(fmt: Formatter) {
extern "Rust" {
fn _binfmt_release(fmt: Formatter);
}
unsafe { _binfmt_release(fmt) }
}

#[cfg(target_arch = "x86_64")]
pub fn timestamp() -> u64 {
T.with(|i| i.fetch_add(1, core::sync::atomic::Ordering::Relaxed))
}

#[cfg(not(target_arch = "x86_64"))]
pub fn timestamp() -> u64 {
extern "Rust" {
fn _binfmt_timestamp() -> u64;
}
unsafe { _binfmt_timestamp() }
}

pub fn str(address: &'static u8) -> Str {
pub fn str(address: usize) -> Str {
Str {
// NOTE address is limited to 14 bits in the linker script
#[cfg(not(test))]
Expand Down
34 changes: 20 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![allow(warnings)] // FIXME
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(target_arch = "x86_64"), no_std)]

use core::{mem::MaybeUninit, ptr::NonNull};

#[cfg(not(test))]
pub use binfmt_macros::intern;
#[doc(hidden)]
pub use binfmt_macros::winfo;
pub use binfmt_macros::{info, write, Format};

use crate as binfmt;
Expand All @@ -31,11 +33,11 @@ pub struct Str {
address: u16,
}

/// Handler that owns the global logger
pub struct Formatter {
/// This pointer owns the global logger
#[cfg(not(test))]
#[cfg(not(target_arch = "x86_64"))]
writer: NonNull<dyn Write>,
#[cfg(test)]
#[cfg(target_arch = "x86_64")]
bytes: Vec<u8>,
}

Expand Down Expand Up @@ -102,24 +104,28 @@ unsafe fn leb64(x: u64, buf: &mut [u8]) -> usize {
}

impl Formatter {
#[cfg(test)]
fn new() -> Self {
/// Only for testing
#[cfg(target_arch = "x86_64")]
pub fn new() -> Self {
Self { bytes: vec![] }
}

#[cfg(test)]
fn bytes(&self) -> &[u8] {
/// Only for testing
#[cfg(target_arch = "x86_64")]
pub fn bytes(&self) -> &[u8] {
&self.bytes
}

#[cfg(not(test))]
fn write(&mut self, bytes: &[u8]) {
unsafe { self.writer.as_mut().write(bytes) }
#[doc(hidden)]
#[cfg(target_arch = "x86_64")]
pub fn write(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes)
}

#[cfg(test)]
fn write(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes)
#[doc(hidden)]
#[cfg(not(target_arch = "x86_64"))]
pub fn write(&mut self, bytes: &[u8]) {
unsafe { self.writer.as_mut().write(bytes) }
}

// TODO turn these public methods in `export` free functions
Expand Down