mod attr;
mod error;
use self::attr::LinkSegment;
use self::error::Error;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{ToTokens, quote};
#[proc_macro_attribute]
pub fn portable_link_section(attr: TokenStream, item: TokenStream) -> TokenStream {
match portable_link_section_impl(attr.into(), item.into()) {
Ok(tt) => tt.into_token_stream().into(),
Err(error) => error.into_token_stream().into(),
}
}
fn portable_link_section_impl(
attr: TokenStream2,
item: TokenStream2,
) -> Result<TokenStream2, Error> {
let attrs = attr.into_iter();
let params = LinkSegment::try_from(attrs)?;
let elf_segment = Elf(¶ms);
let macho_segment = Macho(¶ms);
let coff_segment = Coff(¶ms);
let elf_link_section = quote! {
#[cfg_attr(
all(target_family = "unix", not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))),
unsafe(link_section = #elf_segment),
)]
};
let macho_link_section = quote! {
#[cfg_attr(
any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"),
unsafe(link_section = #macho_segment),
)]
};
let coff_link_section = quote! {
#[cfg_attr(
target_os = "windows",
unsafe(link_section = #coff_segment),
)]
};
Ok(quote! {
#elf_link_section
#macho_link_section
#coff_link_section
#item
})
}
struct Elf<'a, T>(&'a T);
struct Macho<'a, T>(&'a T);
struct Coff<'a, T>(&'a T);
impl ToTokens for Elf<'_, LinkSegment> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let toks = match self.0 {
LinkSegment::Text(group) => {
quote!(concat!(".text.", stringify!(#group)))
}
LinkSegment::Data(group) => {
quote!(concat!(".data.", stringify!(#group)))
}
LinkSegment::Rodata => quote! { ".rodata" },
LinkSegment::Bss => quote! { ".bss" },
};
tokens.extend(toks);
}
}
impl ToTokens for Macho<'_, LinkSegment> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let toks = match self.0 {
LinkSegment::Text(group) => {
quote!(concat!("__TEXT,__", stringify!(#group)))
}
LinkSegment::Data(group) => {
quote!(concat!("__DATA,__", stringify!(#group)))
}
LinkSegment::Rodata => quote! { "__TEXT,__const" },
LinkSegment::Bss => quote! { "__DATA,__bss" },
};
tokens.extend(toks);
}
}
impl ToTokens for Coff<'_, LinkSegment> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let toks = match self.0 {
LinkSegment::Text(group) => {
quote!(concat!(".text$", stringify!(#group)))
}
LinkSegment::Data(group) => {
quote!(concat!(".data$", stringify!(#group)))
}
LinkSegment::Rodata => quote! { ".rdata" },
LinkSegment::Bss => quote! { ".bss" },
};
tokens.extend(toks);
}
}