use std::fmt::Write as _;
use rucc_target::ObjectFormat;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Directives {
Elf,
MachO,
Coff,
}
impl Directives {
#[must_use]
pub const fn of(format: ObjectFormat) -> Directives {
match format {
ObjectFormat::Elf => Directives::Elf,
ObjectFormat::MachO => Directives::MachO,
ObjectFormat::Coff => Directives::Coff,
}
}
#[must_use]
pub const fn symbol(self) -> &'static str {
match self {
Directives::Elf | Directives::Coff => "",
Directives::MachO => "_",
}
}
#[must_use]
pub const fn local(self) -> &'static str {
match self {
Directives::Elf | Directives::Coff => ".L",
Directives::MachO => "L",
}
}
#[must_use]
pub const fn text(self) -> &'static str {
match self {
Directives::Elf | Directives::Coff => "\t.text",
Directives::MachO => "\t.section\t__TEXT,__text,regular,pure_instructions",
}
}
pub fn open(self, out: &mut String, name: &str) {
let symbol = self.symbol();
out.push_str("\t.p2align\t4, 0x90\n");
let _ = writeln!(out, "\t.globl\t{symbol}{name}");
match self {
Directives::Elf => {
let _ = writeln!(out, "\t.type\t{name}, @function");
}
Directives::Coff => {
let _ = writeln!(out, "\t.def\t{name}\n\t.scl\t2\n\t.type\t32\n\t.endef");
}
Directives::MachO => {}
}
let _ = writeln!(out, "{symbol}{name}:");
}
pub fn close(self, out: &mut String, name: &str) {
if self == Directives::Elf {
let _ = writeln!(out, "\t.size\t{name}, .-{name}");
}
}
pub fn end(self, out: &mut String) {
match self {
Directives::Elf => out.push_str("\t.section\t.note.GNU-stack,\"\",@progbits\n"),
Directives::MachO => out.push_str("\t.subsections_via_symbols\n"),
Directives::Coff => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_mach_o_symbol_is_the_c_name_with_an_underscore_in_front_of_it() {
let mut out = String::new();
Directives::MachO.open(&mut out, "main");
assert!(out.contains("\t.globl\t_main\n"), "{out}");
assert!(out.contains("\n_main:\n"), "{out}");
assert!(!out.contains(".type"), "{out}");
let mut close = String::new();
Directives::MachO.close(&mut close, "main");
assert_eq!(close, "");
}
#[test]
fn an_elf_function_says_what_it_is_and_how_long_it_is() {
let mut out = String::new();
Directives::Elf.open(&mut out, "main");
Directives::Elf.close(&mut out, "main");
assert!(out.contains("\t.type\tmain, @function\n"), "{out}");
assert!(out.contains("\t.size\tmain, .-main\n"), "{out}");
}
#[test]
fn an_elf_file_says_the_stack_is_not_executable() {
let mut out = String::new();
Directives::Elf.end(&mut out);
assert!(out.contains(".note.GNU-stack"), "{out}");
}
#[test]
fn every_object_format_has_directives() {
for format in [ObjectFormat::Elf, ObjectFormat::MachO, ObjectFormat::Coff] {
let directives = Directives::of(format);
assert!(directives.text().starts_with('\t'));
let mut out = String::new();
directives.open(&mut out, "f");
directives.close(&mut out, "f");
directives.end(&mut out);
assert!(out.ends_with('\n'), "{format:?} left a line unfinished");
}
}
}