1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::{env, fmt};

use anyhow::*;
use xmas_elf::sections::{SectionData, ShType};
use xmas_elf::{symbol_table, ElfFile};

pub const VAR_SYMBOLS_FILE: &str = "EMBUILD_GENERATED_SYMBOLS_FILE";

pub fn run(elf: impl AsRef<Path>, start_addr: u64) -> Result<()> {
    let output_file = PathBuf::from(env::var("OUT_DIR")?).join("symbols.rs");

    run_for_file(elf, start_addr, &output_file)?;

    println!(
        "cargo:rustc-env={}={}",
        VAR_SYMBOLS_FILE,
        output_file.display()
    );

    Ok(())
}

pub fn run_for_file(
    elf: impl AsRef<Path>,
    start_addr: u64,
    output_file: impl AsRef<Path>,
) -> Result<()> {
    let output_file = output_file.as_ref();

    eprintln!("Output: {:?}", output_file);

    write(elf, start_addr, &mut File::create(output_file)?)
}

pub fn write(elf: impl AsRef<Path>, start_addr: u64, output: &mut impl Write) -> Result<()> {
    eprintln!("Input: {:?}", elf.as_ref());

    let elf_data = fs::read(elf.as_ref())?;
    let elf = ElfFile::new(&elf_data).map_err(Error::msg)?;

    for symtable in get_symtables(&elf) {
        match symtable {
            SectionData::SymbolTable32(entries) => {
                write_symbols(&elf, start_addr, entries.iter(), output)?
            }
            SectionData::SymbolTable64(entries) => {
                write_symbols(&elf, start_addr, entries.iter(), output)?
            }
            _ => panic!(),
        }
    }

    Ok(())
}

fn write_symbols<'a, W: Write>(
    elf: &ElfFile<'a>,
    start_addr: u64,
    symbols: impl Iterator<Item = &'a (impl symbol_table::Entry + fmt::Debug + 'a)>,
    output: &mut W,
) -> Result<()> {
    for sym in symbols {
        eprintln!("Found symbol: {:?}", sym);

        if sym.get_type().map_err(Error::msg)? == symbol_table::Type::Object {
            let name = sym.get_name(elf).map_err(Error::msg)?;
            if !name.contains('.') {
                eprintln!("Writing symbol: {:?}", sym);
                write!(
                    output,
                    "#[allow(dead_code)]\npub const {name}: *{mut} core::ffi::c_void = 0x{addr:x} as *{mut} core::ffi::c_void;\n",
                    name = name,
                    mut = "mut", // TODO
                    addr = start_addr + sym.value()
                )?;
            } else {
                eprintln!("Skipping symbol: {:?}", sym);
            }
        }
    }

    Ok(())
}

fn get_symtables<'a, 'b>(elf: &'b ElfFile<'a>) -> impl Iterator<Item = SectionData<'a>> + 'b {
    elf.section_iter()
        .filter(|header| header.get_type().map_err(Error::msg).unwrap() == ShType::SymTab)
        .map(move |header| header.get_data(elf).unwrap())
}