Add `elf split`; rework asm generation
`elf split` allows splitting an ELF directly into relocatable object files
This commit is contained in:
parent
54f2abd35f
commit
9f4cc2f542
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use anyhow::{Context, Error, Result};
|
use anyhow::{Context, Error, Result};
|
||||||
use argh::FromArgs;
|
use argh::FromArgs;
|
||||||
use object::{Object, ObjectSymbol};
|
use object::{Object, ObjectSymbol, SymbolScope};
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// Commands for processing static libraries.
|
/// Commands for processing static libraries.
|
||||||
|
@ -52,7 +52,7 @@ fn create(args: CreateArgs) -> Result<()> {
|
||||||
Some(rsp_file) => {
|
Some(rsp_file) => {
|
||||||
let reader = BufReader::new(
|
let reader = BufReader::new(
|
||||||
File::open(rsp_file)
|
File::open(rsp_file)
|
||||||
.with_context(|| format!("Failed to open file '{}'", rsp_file))?,
|
.with_context(|| format!("Failed to open file '{rsp_file}'"))?,
|
||||||
);
|
);
|
||||||
for result in reader.lines() {
|
for result in reader.lines() {
|
||||||
let line = result?;
|
let line = result?;
|
||||||
|
@ -92,7 +92,7 @@ fn create(args: CreateArgs) -> Result<()> {
|
||||||
.with_context(|| format!("Failed to mmap object file: '{}'", path.to_string_lossy()))?;
|
.with_context(|| format!("Failed to mmap object file: '{}'", path.to_string_lossy()))?;
|
||||||
let obj = object::File::parse(map.as_ref())?;
|
let obj = object::File::parse(map.as_ref())?;
|
||||||
for symbol in obj.symbols() {
|
for symbol in obj.symbols() {
|
||||||
if symbol.is_global() {
|
if symbol.scope() == SymbolScope::Dynamic {
|
||||||
entries.push(symbol.name_bytes()?.to_vec());
|
entries.push(symbol.name_bytes()?.to_vec());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
216
src/cmd/elf.rs
216
src/cmd/elf.rs
|
@ -1,7 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{btree_map::Entry, BTreeMap},
|
collections::{btree_map, hash_map, BTreeMap, HashMap},
|
||||||
fs,
|
fs,
|
||||||
fs::File,
|
fs::{DirBuilder, File},
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,12 @@ use object::{
|
||||||
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
|
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::util::{asm::write_asm, elf::process_elf};
|
use crate::util::{
|
||||||
|
asm::write_asm,
|
||||||
|
elf::{process_elf, write_elf},
|
||||||
|
obj::ObjKind,
|
||||||
|
split::split_obj,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// Commands for processing ELF files.
|
/// Commands for processing ELF files.
|
||||||
|
@ -29,6 +34,7 @@ pub struct Args {
|
||||||
enum SubCommand {
|
enum SubCommand {
|
||||||
Disasm(DisasmArgs),
|
Disasm(DisasmArgs),
|
||||||
Fixup(FixupArgs),
|
Fixup(FixupArgs),
|
||||||
|
Split(SplitArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
@ -39,8 +45,8 @@ pub struct DisasmArgs {
|
||||||
/// input file
|
/// input file
|
||||||
elf_file: PathBuf,
|
elf_file: PathBuf,
|
||||||
#[argh(positional)]
|
#[argh(positional)]
|
||||||
/// output directory
|
/// output file (.o) or directory (.elf)
|
||||||
out_dir: PathBuf,
|
out: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
@ -55,55 +61,132 @@ pub struct FixupArgs {
|
||||||
out_file: PathBuf,
|
out_file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
/// Splits an executable ELF into relocatable objects.
|
||||||
|
#[argh(subcommand, name = "split")]
|
||||||
|
pub struct SplitArgs {
|
||||||
|
#[argh(positional)]
|
||||||
|
/// input file
|
||||||
|
in_file: PathBuf,
|
||||||
|
#[argh(positional)]
|
||||||
|
/// output directory
|
||||||
|
out_dir: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(args: Args) -> Result<()> {
|
pub fn run(args: Args) -> Result<()> {
|
||||||
match args.command {
|
match args.command {
|
||||||
SubCommand::Disasm(c_args) => disasm(c_args),
|
SubCommand::Disasm(c_args) => disasm(c_args),
|
||||||
SubCommand::Fixup(c_args) => fixup(c_args),
|
SubCommand::Fixup(c_args) => fixup(c_args),
|
||||||
|
SubCommand::Split(c_args) => split(c_args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disasm(args: DisasmArgs) -> Result<()> {
|
fn disasm(args: DisasmArgs) -> Result<()> {
|
||||||
let obj = process_elf(&args.elf_file)?;
|
let obj = process_elf(&args.elf_file)?;
|
||||||
write_asm(&args.out_dir, &obj)?;
|
match obj.kind {
|
||||||
for unit in obj.link_order {
|
ObjKind::Executable => {
|
||||||
let name = format!("$(OBJ_DIR)/asm/{}", file_name_from_unit(&unit));
|
let split_objs = split_obj(&obj)?;
|
||||||
println!(" {name: <70}\\");
|
|
||||||
|
let asm_dir = args.out.join("asm");
|
||||||
|
let include_dir = args.out.join("include");
|
||||||
|
DirBuilder::new().recursive(true).create(&include_dir)?;
|
||||||
|
fs::write(&include_dir.join("macros.inc"), include_bytes!("../../assets/macros.inc"))?;
|
||||||
|
|
||||||
|
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
|
||||||
|
let out_path = asm_dir.join(file_name_from_unit(unit, ".s"));
|
||||||
|
if let Some(parent) = out_path.parent() {
|
||||||
|
DirBuilder::new().recursive(true).create(parent)?;
|
||||||
|
}
|
||||||
|
let mut w = BufWriter::new(File::create(out_path)?);
|
||||||
|
write_asm(&mut w, split_obj)?;
|
||||||
|
|
||||||
|
let name = format!("$(OBJ_DIR)/asm/{}", file_name_from_unit(unit, ".o"));
|
||||||
|
println!(" {name: <70}\\");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjKind::Relocatable => {
|
||||||
|
if let Some(parent) = args.out.parent() {
|
||||||
|
DirBuilder::new().recursive(true).create(parent)?;
|
||||||
|
}
|
||||||
|
let mut w = BufWriter::new(File::create(args.out)?);
|
||||||
|
write_asm(&mut w, &obj)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_name_from_unit(str: &str) -> String {
|
fn split(args: SplitArgs) -> Result<()> {
|
||||||
|
let obj = process_elf(&args.in_file)?;
|
||||||
|
|
||||||
|
let mut file_map = HashMap::<String, object::write::Object>::new();
|
||||||
|
|
||||||
|
let split_objs = split_obj(&obj)?;
|
||||||
|
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
|
||||||
|
let out_obj = write_elf(split_obj)?;
|
||||||
|
match file_map.entry(unit.clone()) {
|
||||||
|
hash_map::Entry::Occupied(_) => {
|
||||||
|
return Err(Error::msg(format!("Duplicate file {unit}")));
|
||||||
|
}
|
||||||
|
hash_map::Entry::Vacant(e) => e.insert(out_obj),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rsp_file = BufWriter::new(File::create("rsp")?);
|
||||||
|
for unit in &obj.link_order {
|
||||||
|
let object = file_map
|
||||||
|
.get(unit)
|
||||||
|
.ok_or_else(|| Error::msg(format!("Failed to find object file for unit '{unit}'")))?;
|
||||||
|
let out_path = args.out_dir.join(file_name_from_unit(unit, ".o"));
|
||||||
|
writeln!(rsp_file, "{}", out_path.to_string_lossy())?;
|
||||||
|
if let Some(parent) = out_path.parent() {
|
||||||
|
DirBuilder::new().recursive(true).create(parent)?;
|
||||||
|
}
|
||||||
|
let mut file = BufWriter::new(File::create(out_path)?);
|
||||||
|
object.write_stream(&mut file).map_err(|e| Error::msg(format!("{e:?}")))?;
|
||||||
|
file.flush()?;
|
||||||
|
}
|
||||||
|
rsp_file.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_name_from_unit(str: &str, suffix: &str) -> String {
|
||||||
|
let str = str.strip_suffix(ASM_SUFFIX).unwrap_or(str);
|
||||||
let str = str.strip_prefix("C:").unwrap_or(str);
|
let str = str.strip_prefix("C:").unwrap_or(str);
|
||||||
let str = str
|
let str = str
|
||||||
.strip_suffix(".c")
|
.strip_suffix(".c")
|
||||||
.or_else(|| str.strip_suffix(".cp"))
|
.or_else(|| str.strip_suffix(".cp"))
|
||||||
.or_else(|| str.strip_suffix(".cpp"))
|
.or_else(|| str.strip_suffix(".cpp"))
|
||||||
.or_else(|| str.strip_suffix(".s"))
|
.or_else(|| str.strip_suffix(".s"))
|
||||||
|
.or_else(|| str.strip_suffix(".o"))
|
||||||
.unwrap_or(str);
|
.unwrap_or(str);
|
||||||
let str = str.replace('\\', "/");
|
let str = str.replace('\\', "/");
|
||||||
format!("{}.o", str.strip_prefix('/').unwrap_or(&str))
|
let str = str.strip_prefix('/').unwrap_or(&str);
|
||||||
|
format!("{str}{suffix}")
|
||||||
}
|
}
|
||||||
|
|
||||||
const ASM_SUFFIX: &[u8] = " (asm)".as_bytes();
|
const ASM_SUFFIX: &str = " (asm)";
|
||||||
|
|
||||||
fn fixup(args: FixupArgs) -> Result<()> {
|
fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
let in_buf = fs::read(&args.in_file).context("Failed to open input file")?;
|
let in_buf = fs::read(&args.in_file).with_context(|| {
|
||||||
|
format!("Failed to open input file: '{}'", args.in_file.to_string_lossy())
|
||||||
|
})?;
|
||||||
let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?;
|
let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?;
|
||||||
let mut out_file =
|
let mut out_file =
|
||||||
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
||||||
|
|
||||||
// Write file symbol(s) first
|
// Write file symbol first
|
||||||
let mut file_symbol_found = false;
|
let mut file_symbol_found = false;
|
||||||
for symbol in in_file.symbols() {
|
for symbol in in_file.symbols() {
|
||||||
if symbol.kind() != SymbolKind::File {
|
if symbol.kind() != SymbolKind::File {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut out_symbol = to_write_symbol(&symbol, &[])?;
|
let mut out_symbol = to_write_symbol(&symbol, &[])?;
|
||||||
out_symbol.name.append(&mut ASM_SUFFIX.to_vec());
|
out_symbol.name.append(&mut ASM_SUFFIX.as_bytes().to_vec());
|
||||||
out_file.add_symbol(out_symbol);
|
out_file.add_symbol(out_symbol);
|
||||||
file_symbol_found = true;
|
file_symbol_found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Create a file symbol if not found
|
||||||
if !file_symbol_found {
|
if !file_symbol_found {
|
||||||
let file_name = args.in_file.file_name().ok_or_else(|| {
|
let file_name = args.in_file.file_name().ok_or_else(|| {
|
||||||
Error::msg(format!("'{}' is not a file path", args.in_file.to_string_lossy()))
|
Error::msg(format!("'{}' is not a file path", args.in_file.to_string_lossy()))
|
||||||
|
@ -112,7 +195,7 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
Error::msg(format!("'{}' is not valid UTF-8", file_name.to_string_lossy()))
|
Error::msg(format!("'{}' is not valid UTF-8", file_name.to_string_lossy()))
|
||||||
})?;
|
})?;
|
||||||
let mut name_bytes = file_name.as_bytes().to_vec();
|
let mut name_bytes = file_name.as_bytes().to_vec();
|
||||||
name_bytes.append(&mut ASM_SUFFIX.to_vec());
|
name_bytes.append(&mut ASM_SUFFIX.as_bytes().to_vec());
|
||||||
out_file.add_symbol(object::write::Symbol {
|
out_file.add_symbol(object::write::Symbol {
|
||||||
name: name_bytes,
|
name: name_bytes,
|
||||||
value: 0,
|
value: 0,
|
||||||
|
@ -163,11 +246,11 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
symbol_ids.push(Some(symbol_id));
|
symbol_ids.push(Some(symbol_id));
|
||||||
if symbol.size() != 0 {
|
if symbol.size() != 0 {
|
||||||
if let Some(section_id) = section_id {
|
if let Some(section_id) = section_id {
|
||||||
let map = match addr_to_sym.entry(section_id) {
|
match addr_to_sym.entry(section_id) {
|
||||||
Entry::Vacant(e) => e.insert(BTreeMap::new()),
|
btree_map::Entry::Vacant(e) => e.insert(BTreeMap::new()),
|
||||||
Entry::Occupied(e) => e.into_mut(),
|
btree_map::Entry::Occupied(e) => e.into_mut(),
|
||||||
};
|
}
|
||||||
map.insert(symbol.address() as u32, symbol_id);
|
.insert(symbol.address() as u32, symbol_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,53 +262,42 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
for (addr, reloc) in section.relocations() {
|
for (addr, reloc) in section.relocations() {
|
||||||
let mut symbol = match reloc.target() {
|
let mut target_symbol_id = match reloc.target() {
|
||||||
RelocationTarget::Symbol(idx) => match symbol_ids[idx.0] {
|
RelocationTarget::Symbol(idx) => match symbol_ids[idx.0] {
|
||||||
Some(id) => id,
|
Some(id) => Ok(id),
|
||||||
None => {
|
None => {
|
||||||
let in_symbol = in_file.symbol_by_index(idx)?;
|
let in_symbol = in_file.symbol_by_index(idx)?;
|
||||||
match in_symbol.kind() {
|
match in_symbol.kind() {
|
||||||
SymbolKind::Section => {
|
SymbolKind::Section => in_symbol
|
||||||
let section_idx = match in_symbol.section_index() {
|
.section_index()
|
||||||
Some(id) => id,
|
.ok_or_else(|| Error::msg("Section symbol without section"))
|
||||||
None => {
|
.and_then(|section_idx| {
|
||||||
return Err(Error::msg("Missing section for relocation"))
|
section_ids[section_idx.0].ok_or_else(|| {
|
||||||
}
|
Error::msg("Relocation against stripped section")
|
||||||
};
|
})
|
||||||
let section_id = match section_ids[section_idx.0] {
|
})
|
||||||
Some(id) => id,
|
.map(|section_idx| out_file.section_symbol(section_idx)),
|
||||||
None => {
|
_ => Err(Error::msg("Missing symbol for relocation")),
|
||||||
return Err(Error::msg("Missing section for relocation"))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
out_file.section_symbol(section_id)
|
|
||||||
}
|
|
||||||
_ => return Err(Error::msg("Missing symbol for relocation")),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RelocationTarget::Section(idx) => {
|
RelocationTarget::Section(section_idx) => section_ids[section_idx.0]
|
||||||
let section_id = match section_ids[idx.0] {
|
.ok_or_else(|| Error::msg("Relocation against stripped section"))
|
||||||
Some(id) => id,
|
.map(|section_id| out_file.section_symbol(section_id)),
|
||||||
None => return Err(Error::msg("Missing section for relocation")),
|
target => Err(Error::msg(format!("Invalid relocation target '{target:?}'"))),
|
||||||
};
|
}?;
|
||||||
out_file.section_symbol(section_id)
|
|
||||||
}
|
|
||||||
RelocationTarget::Absolute => todo!("Absolute relocation target"),
|
|
||||||
_ => return Err(Error::msg("Invalid relocation target")),
|
|
||||||
};
|
|
||||||
let mut addend = reloc.addend();
|
|
||||||
|
|
||||||
// Attempt to replace section symbols with direct symbol references
|
// Attempt to replace section symbols with direct symbol references
|
||||||
let target_sym = out_file.symbol(symbol);
|
let mut addend = reloc.addend();
|
||||||
|
let target_sym = out_file.symbol(target_symbol_id);
|
||||||
if target_sym.kind == SymbolKind::Section {
|
if target_sym.kind == SymbolKind::Section {
|
||||||
if let Some(new_symbol_id) = target_sym
|
if let Some(&new_symbol_id) = target_sym
|
||||||
.section
|
.section
|
||||||
.id()
|
.id()
|
||||||
.and_then(|id| addr_to_sym.get(&id))
|
.and_then(|id| addr_to_sym.get(&id))
|
||||||
.and_then(|map| map.get(&(addend as u32)))
|
.and_then(|map| map.get(&(addend as u32)))
|
||||||
{
|
{
|
||||||
symbol = *new_symbol_id;
|
target_symbol_id = new_symbol_id;
|
||||||
addend = 0;
|
addend = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,14 +314,15 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
size: reloc.size(),
|
size: reloc.size(),
|
||||||
kind,
|
kind,
|
||||||
encoding: reloc.encoding(),
|
encoding: reloc.encoding(),
|
||||||
symbol,
|
symbol: target_symbol_id,
|
||||||
addend,
|
addend,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut out =
|
let mut out = BufWriter::new(File::create(&args.out_file).with_context(|| {
|
||||||
BufWriter::new(File::create(&args.out_file).context("Failed to create out file")?);
|
format!("Failed to create output file: '{}'", args.out_file.to_string_lossy())
|
||||||
|
})?);
|
||||||
out_file.write_stream(&mut out).map_err(|e| Error::msg(format!("{e:?}")))?;
|
out_file.write_stream(&mut out).map_err(|e| Error::msg(format!("{e:?}")))?;
|
||||||
out.flush()?;
|
out.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -259,24 +332,25 @@ fn to_write_symbol_section(
|
||||||
section: SymbolSection,
|
section: SymbolSection,
|
||||||
section_ids: &[Option<SectionId>],
|
section_ids: &[Option<SectionId>],
|
||||||
) -> Result<object::write::SymbolSection> {
|
) -> Result<object::write::SymbolSection> {
|
||||||
Ok(match section {
|
match section {
|
||||||
SymbolSection::None => object::write::SymbolSection::None,
|
SymbolSection::None => Ok(object::write::SymbolSection::None),
|
||||||
SymbolSection::Absolute => object::write::SymbolSection::Absolute,
|
SymbolSection::Absolute => Ok(object::write::SymbolSection::Absolute),
|
||||||
SymbolSection::Common => object::write::SymbolSection::Common,
|
SymbolSection::Common => Ok(object::write::SymbolSection::Common),
|
||||||
SymbolSection::Section(idx) => match section_ids.get(idx.0).and_then(|opt| *opt) {
|
SymbolSection::Section(idx) => section_ids
|
||||||
Some(section_id) => object::write::SymbolSection::Section(section_id),
|
.get(idx.0)
|
||||||
None => return Err(Error::msg("Missing symbol section")),
|
.and_then(|&opt| opt)
|
||||||
},
|
.map(object::write::SymbolSection::Section)
|
||||||
_ => object::write::SymbolSection::Undefined,
|
.ok_or_else(|| Error::msg("Missing symbol section")),
|
||||||
})
|
_ => Ok(object::write::SymbolSection::Undefined),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_write_symbol_flags(flags: SymbolFlags<SectionIndex>) -> Result<SymbolFlags<SectionId>> {
|
fn to_write_symbol_flags(flags: SymbolFlags<SectionIndex>) -> Result<SymbolFlags<SectionId>> {
|
||||||
Ok(match flags {
|
match flags {
|
||||||
SymbolFlags::Elf { st_info, st_other } => SymbolFlags::Elf { st_info, st_other },
|
SymbolFlags::Elf { st_info, st_other } => Ok(SymbolFlags::Elf { st_info, st_other }),
|
||||||
SymbolFlags::None => SymbolFlags::None,
|
SymbolFlags::None => Ok(SymbolFlags::None),
|
||||||
_ => return Err(Error::msg("Unexpected symbol flags")),
|
_ => Err(Error::msg("Unexpected symbol flags")),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_write_symbol(
|
fn to_write_symbol(
|
||||||
|
|
594
src/util/asm.rs
594
src/util/asm.rs
|
@ -1,18 +1,15 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{min, Ordering},
|
cmp::{min, Ordering},
|
||||||
collections::{btree_map, hash_map::Entry, BTreeMap, HashMap},
|
collections::{btree_map, BTreeMap},
|
||||||
fs,
|
io::Write,
|
||||||
fs::{DirBuilder, File},
|
|
||||||
io::{BufWriter, Write},
|
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use ppc750cl::{disasm_iter, Argument, Ins, Opcode};
|
use ppc750cl::{disasm_iter, Argument, Ins, Opcode};
|
||||||
|
|
||||||
use crate::util::obj::{
|
use crate::util::obj::{
|
||||||
ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags,
|
nested_push, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||||
ObjSymbolKind,
|
ObjSymbolFlags, ObjSymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
@ -28,106 +25,110 @@ struct SymbolEntry {
|
||||||
kind: SymbolEntryKind,
|
kind: SymbolEntryKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_asm<P: AsRef<Path>>(path: P, obj: &ObjInfo) -> Result<()> {
|
pub fn write_asm<W: Write>(w: &mut W, obj: &ObjInfo) -> Result<()> {
|
||||||
let mut file_map = HashMap::<String, BufWriter<File>>::new();
|
writeln!(w, ".include \"macros.inc\"")?;
|
||||||
|
if !obj.name.is_empty() {
|
||||||
let asm_dir = path.as_ref().join("asm");
|
writeln!(w, ".file \"{}\"", obj.name.replace('\\', "\\\\"))?;
|
||||||
let include_dir = path.as_ref().join("include");
|
|
||||||
DirBuilder::new().recursive(true).create(&include_dir)?;
|
|
||||||
fs::write(&include_dir.join("macros.inc"), include_bytes!("../../assets/macros.inc"))?;
|
|
||||||
|
|
||||||
for unit in &obj.link_order {
|
|
||||||
let w = match file_map.entry(unit.clone()) {
|
|
||||||
Entry::Occupied(_) => {
|
|
||||||
return Err(Error::msg(format!("Duplicate file {unit}")));
|
|
||||||
}
|
|
||||||
Entry::Vacant(e) => {
|
|
||||||
let file_path = asm_dir.join(file_name_from_unit(unit));
|
|
||||||
if let Some(parent) = file_path.parent() {
|
|
||||||
DirBuilder::new().recursive(true).create(parent)?;
|
|
||||||
}
|
|
||||||
e.insert(BufWriter::new(File::create(file_path)?))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
writeln!(w, ".include \"macros.inc\"")?;
|
|
||||||
writeln!(w, ".file \"{}\"", unit.replace('\\', "\\\\"))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut symbols = Vec::<ObjSymbol>::new();
|
// We'll append generated symbols to the end
|
||||||
let mut addr_sym = BTreeMap::<u32, Vec<SymbolEntry>>::new();
|
let mut symbols: Vec<ObjSymbol> = obj.symbols.clone();
|
||||||
for section in &obj.sections {
|
let mut section_entries: Vec<BTreeMap<u32, Vec<SymbolEntry>>> = vec![];
|
||||||
for symbol in §ion.symbols {
|
let mut section_relocations: Vec<BTreeMap<u32, ObjReloc>> = vec![];
|
||||||
let symbol_index = symbols.len();
|
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||||
symbols.push(symbol.clone());
|
// Build symbol start/end entries
|
||||||
let symbol_start = symbol.address as u32;
|
let mut entries = BTreeMap::<u32, Vec<SymbolEntry>>::new();
|
||||||
let symbol_end = (symbol.address + symbol.size) as u32;
|
for (symbol_index, symbol) in obj.symbols_for_section(section_idx) {
|
||||||
|
if symbol.kind == ObjSymbolKind::Section {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nested_push(&mut entries, symbol.address as u32, SymbolEntry {
|
||||||
|
index: symbol_index,
|
||||||
|
kind: SymbolEntryKind::Start,
|
||||||
|
});
|
||||||
if symbol.size > 0 {
|
if symbol.size > 0 {
|
||||||
match addr_sym.entry(symbol_start) {
|
nested_push(&mut entries, (symbol.address + symbol.size) as u32, SymbolEntry {
|
||||||
btree_map::Entry::Occupied(mut e) => {
|
index: symbol_index,
|
||||||
e.get_mut().push(SymbolEntry {
|
kind: SymbolEntryKind::End,
|
||||||
index: symbol_index,
|
});
|
||||||
kind: SymbolEntryKind::Start,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut relocations = section.build_relocation_map()?;
|
||||||
|
|
||||||
|
// Generate local jump labels
|
||||||
|
if section.kind == ObjSectionKind::Code {
|
||||||
|
for ins in disasm_iter(§ion.data, section.address as u32) {
|
||||||
|
if let Some(address) = ins.branch_dest() {
|
||||||
|
if ins.field_AA()
|
||||||
|
|| (address as u64) < section.address
|
||||||
|
|| (address as u64) >= section.address + section.size
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace section-relative jump relocations (generated by GCC)
|
||||||
|
// These aren't possible to express accurately in GNU assembler
|
||||||
|
if matches!(relocations.get(&ins.addr), Some(reloc) if reloc.addend == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let vec = match entries.entry(address) {
|
||||||
|
btree_map::Entry::Occupied(e) => e.into_mut(),
|
||||||
|
btree_map::Entry::Vacant(e) => e.insert(vec![]),
|
||||||
|
};
|
||||||
|
let mut target_symbol_idx = vec
|
||||||
|
.iter()
|
||||||
|
.find(|e| e.kind == SymbolEntryKind::Label)
|
||||||
|
.or_else(|| vec.iter().find(|e| e.kind == SymbolEntryKind::Start))
|
||||||
|
.map(|e| e.index);
|
||||||
|
if target_symbol_idx.is_none() {
|
||||||
|
let display_address = address as u64 + section.original_address;
|
||||||
|
let symbol_idx = symbols.len();
|
||||||
|
symbols.push(ObjSymbol {
|
||||||
|
name: format!(".L_{display_address:08X}"),
|
||||||
|
address: display_address,
|
||||||
|
section: Some(section_idx),
|
||||||
|
size_known: true,
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
vec.push(SymbolEntry { index: symbol_idx, kind: SymbolEntryKind::Label });
|
||||||
|
target_symbol_idx = Some(symbol_idx);
|
||||||
}
|
}
|
||||||
btree_map::Entry::Vacant(e) => {
|
if let Some(symbol_idx) = target_symbol_idx {
|
||||||
e.insert(vec![SymbolEntry {
|
relocations.insert(ins.addr, ObjReloc {
|
||||||
index: symbol_index,
|
kind: ObjRelocKind::PpcRel24,
|
||||||
kind: SymbolEntryKind::Start,
|
address: ins.addr as u64,
|
||||||
}]);
|
target_symbol: symbol_idx,
|
||||||
}
|
addend: 0,
|
||||||
}
|
|
||||||
match addr_sym.entry(symbol_end) {
|
|
||||||
btree_map::Entry::Occupied(mut e) => {
|
|
||||||
// Always push first
|
|
||||||
e.get_mut().insert(0, SymbolEntry {
|
|
||||||
index: symbol_index,
|
|
||||||
kind: SymbolEntryKind::End,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
btree_map::Entry::Vacant(e) => {
|
|
||||||
e.insert(vec![SymbolEntry {
|
|
||||||
index: symbol_index,
|
|
||||||
kind: SymbolEntryKind::End,
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match addr_sym.entry(symbol_start) {
|
|
||||||
btree_map::Entry::Occupied(mut e) => {
|
|
||||||
e.get_mut().push(SymbolEntry {
|
|
||||||
index: symbol_index,
|
|
||||||
kind: SymbolEntryKind::Label,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
btree_map::Entry::Vacant(e) => {
|
|
||||||
e.insert(vec![SymbolEntry {
|
|
||||||
index: symbol_index,
|
|
||||||
kind: SymbolEntryKind::Label,
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate labels for .text relocations
|
section_entries.push(entries);
|
||||||
|
section_relocations.push(relocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate labels for jump tables & relative data relocations
|
||||||
|
for section in &obj.sections {
|
||||||
|
if !matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for reloc in §ion.relocations {
|
for reloc in §ion.relocations {
|
||||||
let target_section = match &reloc.target_section {
|
if reloc.addend == 0 {
|
||||||
Some(v) => v,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let section = match obj.sections.iter().find(|s| &s.name == target_section) {
|
|
||||||
Some(v) => v,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
match section.kind {
|
|
||||||
ObjSectionKind::Code => {}
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
if reloc.target.addend == 0 {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let address = (reloc.target.address as i64 + reloc.target.addend) as u64;
|
let target = &obj.symbols[reloc.target_symbol];
|
||||||
let vec = match addr_sym.entry(address as u32) {
|
let target_section_idx = match target.section {
|
||||||
|
Some(v) => v,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let target_section = &obj.sections[target_section_idx];
|
||||||
|
let address = (target.address as i64 + reloc.addend) as u64;
|
||||||
|
let vec = match section_entries[target_section_idx].entry(address as u32) {
|
||||||
btree_map::Entry::Occupied(e) => e.into_mut(),
|
btree_map::Entry::Occupied(e) => e.into_mut(),
|
||||||
btree_map::Entry::Vacant(e) => e.insert(vec![]),
|
btree_map::Entry::Vacant(e) => e.insert(vec![]),
|
||||||
};
|
};
|
||||||
|
@ -135,207 +136,100 @@ pub fn write_asm<P: AsRef<Path>>(path: P, obj: &ObjInfo) -> Result<()> {
|
||||||
.iter()
|
.iter()
|
||||||
.any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start)
|
.any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start)
|
||||||
{
|
{
|
||||||
let symbol_index = symbols.len();
|
let display_address = address + target_section.original_address;
|
||||||
|
let symbol_idx = symbols.len();
|
||||||
symbols.push(ObjSymbol {
|
symbols.push(ObjSymbol {
|
||||||
name: format!(".L_{address:8X}"),
|
name: format!(".L_{display_address:08X}"),
|
||||||
demangled_name: None,
|
address: display_address,
|
||||||
address,
|
section: Some(target_section_idx),
|
||||||
section_address: address - section.address,
|
|
||||||
size: 0,
|
|
||||||
size_known: true,
|
size_known: true,
|
||||||
flags: Default::default(),
|
..Default::default()
|
||||||
addend: 0,
|
|
||||||
kind: ObjSymbolKind::Unknown,
|
|
||||||
});
|
});
|
||||||
vec.push(SymbolEntry { index: symbol_index, kind: SymbolEntryKind::Label });
|
vec.push(SymbolEntry { index: symbol_idx, kind: SymbolEntryKind::Label });
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate local jump labels
|
|
||||||
for ins in disasm_iter(§ion.data, section.address as u32) {
|
|
||||||
if let Some(address) = ins.branch_dest() {
|
|
||||||
let section =
|
|
||||||
match obj.sections.iter().find(|s| {
|
|
||||||
s.address <= address as u64 && (s.address + s.size) > address as u64
|
|
||||||
}) {
|
|
||||||
Some(s) => s,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let vec = match addr_sym.entry(address) {
|
|
||||||
btree_map::Entry::Occupied(e) => e.into_mut(),
|
|
||||||
btree_map::Entry::Vacant(e) => e.insert(vec![]),
|
|
||||||
};
|
|
||||||
if !vec
|
|
||||||
.iter()
|
|
||||||
.any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start)
|
|
||||||
{
|
|
||||||
let symbol_index = symbols.len();
|
|
||||||
symbols.push(ObjSymbol {
|
|
||||||
name: format!(".L_{address:8X}"),
|
|
||||||
demangled_name: None,
|
|
||||||
address: address as u64,
|
|
||||||
section_address: address as u64 - section.address,
|
|
||||||
size: 0,
|
|
||||||
size_known: true,
|
|
||||||
flags: Default::default(),
|
|
||||||
addend: 0,
|
|
||||||
kind: ObjSymbolKind::Unknown,
|
|
||||||
});
|
|
||||||
vec.push(SymbolEntry { index: symbol_index, kind: SymbolEntryKind::Label });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for section in &obj.sections {
|
for section in &obj.sections {
|
||||||
log::info!(
|
let entries = §ion_entries[section.index];
|
||||||
"Writing section {} ({:#10X} - {:#10X})",
|
let relocations = §ion_relocations[section.index];
|
||||||
section.name,
|
|
||||||
section.address,
|
|
||||||
section.address + section.size
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut current_address = section.address as u32;
|
let mut current_address = section.address as u32;
|
||||||
let section_end = (section.address + section.size) as u32;
|
let section_end = (section.address + section.size) as u32;
|
||||||
let mut file_iter = obj.splits.range(current_address..).peekable();
|
let subsection =
|
||||||
|
obj.sections.iter().take(section.index).filter(|s| s.name == section.name).count();
|
||||||
|
|
||||||
let mut relocations = BTreeMap::<u32, ObjReloc>::new();
|
|
||||||
for reloc in §ion.relocations {
|
|
||||||
let address = reloc.address as u32;
|
|
||||||
match relocations.entry(address) {
|
|
||||||
btree_map::Entry::Vacant(e) => {
|
|
||||||
e.insert(reloc.clone());
|
|
||||||
}
|
|
||||||
btree_map::Entry::Occupied(_) => {
|
|
||||||
return Err(Error::msg(format!("Duplicate relocation @ {address:#10X}")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut subsection = 0;
|
|
||||||
let mut current_unit = String::new();
|
|
||||||
loop {
|
loop {
|
||||||
if current_address >= section_end {
|
if current_address >= section_end {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (file_addr, unit) = match file_iter.next() {
|
write_section_header(w, section, subsection, current_address, section_end)?;
|
||||||
Some((addr, unit)) => (*addr, unit),
|
|
||||||
None => return Err(Error::msg("No file found")),
|
|
||||||
};
|
|
||||||
if file_addr > current_address {
|
|
||||||
return Err(Error::msg(format!(
|
|
||||||
"Gap in files: {} @ {:#10X}, {} @ {:#10X}",
|
|
||||||
section.name, section.address, unit, file_addr
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
let mut file_end = section_end;
|
|
||||||
if let Some((next_addr, _)) = file_iter.peek() {
|
|
||||||
file_end = min(**next_addr, section_end);
|
|
||||||
}
|
|
||||||
if unit == ¤t_unit {
|
|
||||||
subsection += 1;
|
|
||||||
} else {
|
|
||||||
current_unit = unit.clone();
|
|
||||||
subsection = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let w = write_section_header(
|
|
||||||
&mut file_map,
|
|
||||||
unit,
|
|
||||||
section,
|
|
||||||
subsection,
|
|
||||||
current_address,
|
|
||||||
file_end,
|
|
||||||
)?;
|
|
||||||
match section.kind {
|
match section.kind {
|
||||||
ObjSectionKind::Code | ObjSectionKind::Data => {
|
ObjSectionKind::Code | ObjSectionKind::Data | ObjSectionKind::ReadOnlyData => {
|
||||||
write_data(
|
write_data(
|
||||||
w,
|
w,
|
||||||
&symbols,
|
&symbols,
|
||||||
&addr_sym,
|
entries,
|
||||||
&relocations,
|
relocations,
|
||||||
section,
|
section,
|
||||||
current_address,
|
current_address,
|
||||||
file_end,
|
section_end,
|
||||||
|
§ion_entries,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Bss => {
|
ObjSectionKind::Bss => {
|
||||||
write_bss(w, &symbols, &addr_sym, current_address, file_end)?;
|
write_bss(w, &symbols, entries, current_address, section_end)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current_address = file_end;
|
|
||||||
|
// Write end of symbols
|
||||||
|
if let Some(entries) = entries.get(§ion_end) {
|
||||||
|
for entry in entries {
|
||||||
|
if entry.kind != SymbolEntryKind::End {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
write_symbol_entry(w, &symbols, entry)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_address = section_end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, mut w) in file_map {
|
w.flush()?;
|
||||||
w.flush()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_code_chunk(
|
fn write_code_chunk<W: Write>(
|
||||||
w: &mut BufWriter<File>,
|
w: &mut W,
|
||||||
symbols: &[ObjSymbol],
|
symbols: &[ObjSymbol],
|
||||||
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
|
_entries: &BTreeMap<u32, Vec<SymbolEntry>>,
|
||||||
relocations: &BTreeMap<u32, ObjReloc>,
|
relocations: &BTreeMap<u32, ObjReloc>,
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
address: u32,
|
address: u32,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for ins in disasm_iter(data, address) {
|
for ins in disasm_iter(data, address) {
|
||||||
let mut reloc = relocations.get(&ins.addr);
|
let reloc = relocations.get(&ins.addr);
|
||||||
let mut generated_reloc: Option<ObjReloc> = None;
|
|
||||||
|
|
||||||
// HACK: GCC-built objects generate section-relative jump relocations,
|
|
||||||
// which aren't always possible to express in GNU assembler accurately,
|
|
||||||
// specifically when dealing with multiple sections with the same name.
|
|
||||||
// Use a (hacky) heuristic to clear them so we generate a local label jump below.
|
|
||||||
if let Some(rel) = reloc {
|
|
||||||
if rel.target.addend != 0
|
|
||||||
&& matches!(rel.kind, ObjRelocKind::PpcRel14 | ObjRelocKind::PpcRel24)
|
|
||||||
{
|
|
||||||
reloc = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a branch instruction, automatically "relocate" to a label.
|
|
||||||
// Local branch labels are generated above.
|
|
||||||
if reloc.is_none() {
|
|
||||||
if let Some(symbol_entry) =
|
|
||||||
ins.branch_dest().and_then(|dest| sym_map.get(&dest)).and_then(|entries| {
|
|
||||||
entries
|
|
||||||
.iter()
|
|
||||||
.find(|e| e.kind == SymbolEntryKind::Label)
|
|
||||||
.or_else(|| entries.iter().find(|e| e.kind == SymbolEntryKind::Start))
|
|
||||||
})
|
|
||||||
{
|
|
||||||
let symbol = &symbols[symbol_entry.index];
|
|
||||||
generated_reloc = Some(ObjReloc {
|
|
||||||
kind: ObjRelocKind::Absolute,
|
|
||||||
address: ins.addr as u64,
|
|
||||||
target: symbol.clone(),
|
|
||||||
target_section: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_offset = section.file_offset + (ins.addr as u64 - section.address);
|
let file_offset = section.file_offset + (ins.addr as u64 - section.address);
|
||||||
write_ins(w, ins, reloc.or(generated_reloc.as_ref()), file_offset)?;
|
write_ins(w, symbols, ins, reloc, file_offset, section.original_address)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_ins(
|
fn write_ins<W: Write>(
|
||||||
w: &mut BufWriter<File>,
|
w: &mut W,
|
||||||
|
symbols: &[ObjSymbol],
|
||||||
ins: Ins,
|
ins: Ins,
|
||||||
reloc: Option<&ObjReloc>,
|
reloc: Option<&ObjReloc>,
|
||||||
file_offset: u64,
|
file_offset: u64,
|
||||||
|
section_address: u64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
write!(
|
write!(
|
||||||
w,
|
w,
|
||||||
"/* {:08X} {:08X} {:02X} {:02X} {:02X} {:02X} */\t",
|
"/* {:08X} {:08X} {:02X} {:02X} {:02X} {:02X} */\t",
|
||||||
ins.addr,
|
ins.addr as u64 + section_address,
|
||||||
file_offset,
|
file_offset,
|
||||||
(ins.code >> 24) & 0xFF,
|
(ins.code >> 24) & 0xFF,
|
||||||
(ins.code >> 16) & 0xFF,
|
(ins.code >> 16) & 0xFF,
|
||||||
|
@ -364,14 +258,14 @@ fn write_ins(
|
||||||
match arg {
|
match arg {
|
||||||
Argument::Uimm(_) | Argument::Simm(_) | Argument::BranchDest(_) => {
|
Argument::Uimm(_) | Argument::Simm(_) | Argument::BranchDest(_) => {
|
||||||
if let Some(reloc) = reloc {
|
if let Some(reloc) = reloc {
|
||||||
write_reloc(w, reloc)?;
|
write_reloc(w, symbols, reloc)?;
|
||||||
} else {
|
} else {
|
||||||
write!(w, "{arg}")?;
|
write!(w, "{arg}")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::Offset(_) => {
|
Argument::Offset(_) => {
|
||||||
if let Some(reloc) = reloc {
|
if let Some(reloc) = reloc {
|
||||||
write_reloc(w, reloc)?;
|
write_reloc(w, symbols, reloc)?;
|
||||||
} else {
|
} else {
|
||||||
write!(w, "{arg}")?;
|
write!(w, "{arg}")?;
|
||||||
}
|
}
|
||||||
|
@ -393,8 +287,8 @@ fn write_ins(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_reloc(w: &mut BufWriter<File>, reloc: &ObjReloc) -> Result<()> {
|
fn write_reloc<W: Write>(w: &mut W, symbols: &[ObjSymbol], reloc: &ObjReloc) -> Result<()> {
|
||||||
write_symbol(w, &reloc.target)?;
|
write_reloc_symbol(w, symbols, reloc)?;
|
||||||
match reloc.kind {
|
match reloc.kind {
|
||||||
ObjRelocKind::Absolute | ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
ObjRelocKind::Absolute | ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
||||||
// pass
|
// pass
|
||||||
|
@ -415,32 +309,27 @@ fn write_reloc(w: &mut BufWriter<File>, reloc: &ObjReloc) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_symbol_entry(
|
fn write_symbol_entry<W: Write>(
|
||||||
w: &mut BufWriter<File>,
|
w: &mut W,
|
||||||
symbols: &[ObjSymbol],
|
symbols: &[ObjSymbol],
|
||||||
entry: &SymbolEntry,
|
entry: &SymbolEntry,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let symbol = &symbols[entry.index];
|
let symbol = &symbols[entry.index];
|
||||||
assert_eq!(symbol.addend, 0);
|
|
||||||
|
|
||||||
// Skip writing certain symbols
|
// Skip writing certain symbols
|
||||||
if is_skip_symbol(&symbol.name) {
|
if symbol.kind == ObjSymbolKind::Section {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment out linker-generated symbols
|
|
||||||
let mut start_newline = true;
|
|
||||||
if entry.kind == SymbolEntryKind::Start && is_linker_symbol(&symbol.name) {
|
|
||||||
writeln!(w, "\n/* Linker generated")?;
|
|
||||||
start_newline = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let symbol_kind = match symbol.kind {
|
let symbol_kind = match symbol.kind {
|
||||||
ObjSymbolKind::Function => "fn",
|
ObjSymbolKind::Function => "fn",
|
||||||
ObjSymbolKind::Object => "obj",
|
ObjSymbolKind::Object => "obj",
|
||||||
ObjSymbolKind::Unknown => "sym",
|
ObjSymbolKind::Unknown => "sym",
|
||||||
|
ObjSymbolKind::Section => {
|
||||||
|
return Err(Error::msg(format!("Attempted to write section symbol: {symbol:?}")))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let visibility = if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
let scope = if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
||||||
"weak"
|
"weak"
|
||||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
||||||
"global"
|
"global"
|
||||||
|
@ -456,11 +345,11 @@ fn write_symbol_entry(
|
||||||
} else {
|
} else {
|
||||||
write!(w, ".sym ")?;
|
write!(w, ".sym ")?;
|
||||||
write_symbol_name(w, &symbol.name)?;
|
write_symbol_name(w, &symbol.name)?;
|
||||||
writeln!(w, ", {visibility}")?;
|
writeln!(w, ", {scope}")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SymbolEntryKind::Start => {
|
SymbolEntryKind::Start => {
|
||||||
if start_newline {
|
if symbol.kind != ObjSymbolKind::Unknown {
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
}
|
}
|
||||||
if let Some(name) = &symbol.demangled_name {
|
if let Some(name) = &symbol.demangled_name {
|
||||||
|
@ -468,7 +357,7 @@ fn write_symbol_entry(
|
||||||
}
|
}
|
||||||
write!(w, ".{symbol_kind} ")?;
|
write!(w, ".{symbol_kind} ")?;
|
||||||
write_symbol_name(w, &symbol.name)?;
|
write_symbol_name(w, &symbol.name)?;
|
||||||
writeln!(w, ", {visibility}")?;
|
writeln!(w, ", {scope}")?;
|
||||||
}
|
}
|
||||||
SymbolEntryKind::End => {
|
SymbolEntryKind::End => {
|
||||||
write!(w, ".end{symbol_kind} ")?;
|
write!(w, ".end{symbol_kind} ")?;
|
||||||
|
@ -477,34 +366,39 @@ fn write_symbol_entry(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.kind == SymbolEntryKind::End && is_linker_symbol(&symbol.name) {
|
if matches!(entry.kind, SymbolEntryKind::Start | SymbolEntryKind::Label)
|
||||||
writeln!(w, "*/")?;
|
&& symbol.flags.0.contains(ObjSymbolFlags::Hidden)
|
||||||
|
{
|
||||||
|
write!(w, ".hidden ")?;
|
||||||
|
write_symbol_name(w, &symbol.name)?;
|
||||||
|
writeln!(w)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data(
|
fn write_data<W: Write>(
|
||||||
w: &mut BufWriter<File>,
|
w: &mut W,
|
||||||
symbols: &[ObjSymbol],
|
symbols: &[ObjSymbol],
|
||||||
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
|
entries: &BTreeMap<u32, Vec<SymbolEntry>>,
|
||||||
relocations: &BTreeMap<u32, ObjReloc>,
|
relocations: &BTreeMap<u32, ObjReloc>,
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
start: u32,
|
start: u32,
|
||||||
end: u32,
|
end: u32,
|
||||||
|
section_entries: &[BTreeMap<u32, Vec<SymbolEntry>>],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut sym_iter = sym_map.range(start..end);
|
let mut entry_iter = entries.range(start..end);
|
||||||
let mut reloc_iter = relocations.range(start..end);
|
let mut reloc_iter = relocations.range(start..end);
|
||||||
|
|
||||||
let mut current_address = start;
|
let mut current_address = start;
|
||||||
let mut current_symbol_kind = ObjSymbolKind::Unknown;
|
let mut current_symbol_kind = ObjSymbolKind::Unknown;
|
||||||
let mut sym = sym_iter.next();
|
let mut entry = entry_iter.next();
|
||||||
let mut reloc = reloc_iter.next();
|
let mut reloc = reloc_iter.next();
|
||||||
let mut begin = true;
|
let mut begin = true;
|
||||||
loop {
|
loop {
|
||||||
if current_address == end {
|
if current_address == end {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Some((sym_addr, vec)) = sym {
|
if let Some((sym_addr, vec)) = entry {
|
||||||
if current_address == *sym_addr {
|
if current_address == *sym_addr {
|
||||||
for entry in vec {
|
for entry in vec {
|
||||||
if entry.kind == SymbolEntryKind::End && begin {
|
if entry.kind == SymbolEntryKind::End && begin {
|
||||||
|
@ -513,7 +407,7 @@ fn write_data(
|
||||||
write_symbol_entry(w, symbols, entry)?;
|
write_symbol_entry(w, symbols, entry)?;
|
||||||
}
|
}
|
||||||
current_symbol_kind = find_symbol_kind(current_symbol_kind, symbols, vec)?;
|
current_symbol_kind = find_symbol_kind(current_symbol_kind, symbols, vec)?;
|
||||||
sym = sym_iter.next();
|
entry = entry_iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
begin = false;
|
begin = false;
|
||||||
|
@ -521,7 +415,9 @@ fn write_data(
|
||||||
let symbol_kind = if current_symbol_kind == ObjSymbolKind::Unknown {
|
let symbol_kind = if current_symbol_kind == ObjSymbolKind::Unknown {
|
||||||
match section.kind {
|
match section.kind {
|
||||||
ObjSectionKind::Code => ObjSymbolKind::Function,
|
ObjSectionKind::Code => ObjSymbolKind::Function,
|
||||||
ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object,
|
ObjSectionKind::Data | ObjSectionKind::ReadOnlyData | ObjSectionKind::Bss => {
|
||||||
|
ObjSymbolKind::Object
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
current_symbol_kind
|
current_symbol_kind
|
||||||
|
@ -531,18 +427,19 @@ fn write_data(
|
||||||
reloc = reloc_iter.next();
|
reloc = reloc_iter.next();
|
||||||
match symbol_kind {
|
match symbol_kind {
|
||||||
ObjSymbolKind::Object => {
|
ObjSymbolKind::Object => {
|
||||||
current_address = write_data_reloc(w, symbols, sym_map, r)?;
|
current_address =
|
||||||
|
write_data_reloc(w, symbols, entries, r, section_entries)?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ObjSymbolKind::Function => {
|
ObjSymbolKind::Function => {
|
||||||
// handled in write_code_chunk
|
// handled in write_code_chunk
|
||||||
}
|
}
|
||||||
ObjSymbolKind::Unknown => unreachable!(),
|
ObjSymbolKind::Unknown | ObjSymbolKind::Section => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let until = match (sym, reloc) {
|
let until = match (entry, reloc) {
|
||||||
(Some((sym_addr, _)), Some((reloc_addr, _))) => min(*reloc_addr, *sym_addr),
|
(Some((sym_addr, _)), Some((reloc_addr, _))) => min(*reloc_addr, *sym_addr),
|
||||||
(Some((addr, _)), None) | (None, Some((addr, _))) => *addr,
|
(Some((addr, _)), None) | (None, Some((addr, _))) => *addr,
|
||||||
(None, None) => end,
|
(None, None) => end,
|
||||||
|
@ -552,27 +449,18 @@ fn write_data(
|
||||||
if symbol_kind == ObjSymbolKind::Function {
|
if symbol_kind == ObjSymbolKind::Function {
|
||||||
if current_address & 3 != 0 || data.len() & 3 != 0 {
|
if current_address & 3 != 0 || data.len() & 3 != 0 {
|
||||||
return Err(Error::msg(format!(
|
return Err(Error::msg(format!(
|
||||||
"Unaligned code write @ {:#010X} size {:#X}",
|
"Unaligned code write @ {} {:#010X} size {:#X}",
|
||||||
|
section.name,
|
||||||
current_address,
|
current_address,
|
||||||
data.len()
|
data.len()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
write_code_chunk(w, symbols, sym_map, relocations, section, current_address, data)?;
|
write_code_chunk(w, symbols, entries, relocations, section, current_address, data)?;
|
||||||
} else {
|
} else {
|
||||||
write_data_chunk(w, data)?;
|
write_data_chunk(w, data)?;
|
||||||
}
|
}
|
||||||
current_address = until;
|
current_address = until;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write end of symbols
|
|
||||||
if let Some(entries) = sym_map.get(&end) {
|
|
||||||
for entry in entries {
|
|
||||||
if entry.kind != SymbolEntryKind::End {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
write_symbol_entry(w, symbols, entry)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,7 +475,7 @@ fn find_symbol_kind(
|
||||||
match entry.kind {
|
match entry.kind {
|
||||||
SymbolEntryKind::Start => {
|
SymbolEntryKind::Start => {
|
||||||
let new_kind = symbols[entry.index].kind;
|
let new_kind = symbols[entry.index].kind;
|
||||||
if new_kind != ObjSymbolKind::Unknown {
|
if !matches!(new_kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section) {
|
||||||
if found && new_kind != kind {
|
if found && new_kind != kind {
|
||||||
return Err(Error::msg(format!(
|
return Err(Error::msg(format!(
|
||||||
"Conflicting symbol kinds found: {kind:?} and {new_kind:?}"
|
"Conflicting symbol kinds found: {kind:?} and {new_kind:?}"
|
||||||
|
@ -603,7 +491,7 @@ fn find_symbol_kind(
|
||||||
Ok(kind)
|
Ok(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data_chunk(w: &mut BufWriter<File>, data: &[u8]) -> Result<()> {
|
fn write_data_chunk<W: Write>(w: &mut W, data: &[u8]) -> Result<()> {
|
||||||
let remain = data;
|
let remain = data;
|
||||||
for chunk in remain.chunks(4) {
|
for chunk in remain.chunks(4) {
|
||||||
match chunk.len() {
|
match chunk.len() {
|
||||||
|
@ -626,24 +514,27 @@ fn write_data_chunk(w: &mut BufWriter<File>, data: &[u8]) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data_reloc(
|
fn write_data_reloc<W: Write>(
|
||||||
w: &mut BufWriter<File>,
|
w: &mut W,
|
||||||
symbols: &[ObjSymbol],
|
symbols: &[ObjSymbol],
|
||||||
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
|
_entries: &BTreeMap<u32, Vec<SymbolEntry>>,
|
||||||
reloc: &ObjReloc,
|
reloc: &ObjReloc,
|
||||||
|
section_entries: &[BTreeMap<u32, Vec<SymbolEntry>>],
|
||||||
) -> Result<u32> {
|
) -> Result<u32> {
|
||||||
Ok(match reloc.kind {
|
match reloc.kind {
|
||||||
ObjRelocKind::Absolute => {
|
ObjRelocKind::Absolute => {
|
||||||
// Attempt to use .rel macro for relative relocations
|
// Attempt to use .rel macro for relative relocations
|
||||||
if reloc.target.addend != 0 {
|
if reloc.addend != 0 {
|
||||||
let target_addr = (reloc.target.address as i64 + reloc.target.addend) as u32;
|
let target = &symbols[reloc.target_symbol];
|
||||||
if let Some(entry) = sym_map
|
let target_addr = (target.address as i64 + reloc.addend) as u32;
|
||||||
.get(&target_addr)
|
if let Some(entry) = target
|
||||||
|
.section
|
||||||
|
.and_then(|section_idx| section_entries[section_idx].get(&target_addr))
|
||||||
.and_then(|entries| entries.iter().find(|e| e.kind == SymbolEntryKind::Label))
|
.and_then(|entries| entries.iter().find(|e| e.kind == SymbolEntryKind::Label))
|
||||||
{
|
{
|
||||||
let symbol = &symbols[entry.index];
|
let symbol = &symbols[entry.index];
|
||||||
write!(w, "\t.rel ")?;
|
write!(w, "\t.rel ")?;
|
||||||
write_symbol_name(w, &reloc.target.name)?;
|
write_symbol_name(w, &target.name)?;
|
||||||
write!(w, ", ")?;
|
write!(w, ", ")?;
|
||||||
write_symbol_name(w, &symbol.name)?;
|
write_symbol_name(w, &symbol.name)?;
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
|
@ -651,31 +542,31 @@ fn write_data_reloc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(w, "\t.4byte ")?;
|
write!(w, "\t.4byte ")?;
|
||||||
write_symbol(w, &reloc.target)?;
|
write_reloc_symbol(w, symbols, reloc)?;
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
(reloc.address + 4) as u32
|
Ok((reloc.address + 4) as u32)
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => Err(Error::msg(format!("Unsupported data relocation type {:?}", reloc.kind))),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bss(
|
fn write_bss<W: Write>(
|
||||||
w: &mut BufWriter<File>,
|
w: &mut W,
|
||||||
symbols: &[ObjSymbol],
|
symbols: &[ObjSymbol],
|
||||||
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
|
entries: &BTreeMap<u32, Vec<SymbolEntry>>,
|
||||||
start: u32,
|
start: u32,
|
||||||
end: u32,
|
end: u32,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut sym_iter = sym_map.range(start..end);
|
let mut entry_iter = entries.range(start..end);
|
||||||
|
|
||||||
let mut current_address = start;
|
let mut current_address = start;
|
||||||
let mut sym = sym_iter.next();
|
let mut entry = entry_iter.next();
|
||||||
let mut begin = true;
|
let mut begin = true;
|
||||||
loop {
|
loop {
|
||||||
if current_address == end {
|
if current_address == end {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Some((sym_addr, vec)) = sym {
|
if let Some((sym_addr, vec)) = entry {
|
||||||
if current_address == *sym_addr {
|
if current_address == *sym_addr {
|
||||||
for entry in vec {
|
for entry in vec {
|
||||||
if entry.kind == SymbolEntryKind::End && begin {
|
if entry.kind == SymbolEntryKind::End && begin {
|
||||||
|
@ -683,116 +574,92 @@ fn write_bss(
|
||||||
}
|
}
|
||||||
write_symbol_entry(w, symbols, entry)?;
|
write_symbol_entry(w, symbols, entry)?;
|
||||||
}
|
}
|
||||||
sym = sym_iter.next();
|
entry = entry_iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
begin = false;
|
begin = false;
|
||||||
|
|
||||||
let until = sym.map(|(addr, _)| *addr).unwrap_or(end);
|
let until = entry.map(|(addr, _)| *addr).unwrap_or(end);
|
||||||
let size = until - current_address;
|
let size = until - current_address;
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
writeln!(w, "\t.skip {size:#X}")?;
|
writeln!(w, "\t.skip {size:#X}")?;
|
||||||
}
|
}
|
||||||
current_address = until;
|
current_address = until;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write end of symbols
|
|
||||||
if let Some(entries) = sym_map.get(&end) {
|
|
||||||
for entry in entries {
|
|
||||||
if entry.kind != SymbolEntryKind::End {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
write_symbol_entry(w, symbols, entry)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_name_from_unit(str: &str) -> String {
|
fn write_section_header<W: Write>(
|
||||||
let str = str.strip_prefix("C:").unwrap_or(str);
|
w: &mut W,
|
||||||
let str = str
|
|
||||||
.strip_suffix(".c")
|
|
||||||
.or_else(|| str.strip_suffix(".cp"))
|
|
||||||
.or_else(|| str.strip_suffix(".cpp"))
|
|
||||||
.or_else(|| str.strip_suffix(".s"))
|
|
||||||
.unwrap_or(str);
|
|
||||||
let str = str.replace('\\', "/");
|
|
||||||
format!("{}.s", str.strip_prefix('/').unwrap_or(&str))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_section_header<'a>(
|
|
||||||
file_map: &'a mut HashMap<String, BufWriter<File>>,
|
|
||||||
unit: &String,
|
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
subsection: usize,
|
subsection: usize,
|
||||||
start: u32,
|
start: u32,
|
||||||
end: u32,
|
end: u32,
|
||||||
) -> Result<&'a mut BufWriter<File>> {
|
) -> Result<()> {
|
||||||
let w = file_map
|
writeln!(
|
||||||
.get_mut(unit)
|
w,
|
||||||
.ok_or_else(|| Error::msg(format!("Failed to locate file for {unit}")))?;
|
"\n# {:#010X} - {:#010X}",
|
||||||
writeln!(w, "\n# {start:#10X} - {end:#10X}")?;
|
start as u64 + section.original_address,
|
||||||
let alignment = match section.name.as_str() {
|
end as u64 + section.original_address
|
||||||
|
)?;
|
||||||
|
match section.name.as_str() {
|
||||||
".text" if subsection == 0 => {
|
".text" if subsection == 0 => {
|
||||||
write!(w, "{}", section.name)?;
|
write!(w, "{}", section.name)?;
|
||||||
4
|
|
||||||
}
|
}
|
||||||
".data" | ".bss" | ".rodata" if subsection == 0 => {
|
// .bss excluded to support < r40 devkitPro
|
||||||
|
".data" | ".rodata" if subsection == 0 => {
|
||||||
write!(w, "{}", section.name)?;
|
write!(w, "{}", section.name)?;
|
||||||
8
|
|
||||||
}
|
}
|
||||||
".text" | ".init" => {
|
".text" | ".init" => {
|
||||||
write!(w, ".section {}", section.name)?;
|
write!(w, ".section {}", section.name)?;
|
||||||
write!(w, ", \"ax\"")?;
|
write!(w, ", \"ax\"")?;
|
||||||
4
|
|
||||||
}
|
}
|
||||||
".data" | ".sdata" => {
|
".data" | ".sdata" => {
|
||||||
write!(w, ".section {}", section.name)?;
|
write!(w, ".section {}", section.name)?;
|
||||||
write!(w, ", \"wa\"")?;
|
write!(w, ", \"wa\"")?;
|
||||||
8
|
|
||||||
}
|
}
|
||||||
".rodata" | ".sdata2" => {
|
".rodata" | ".sdata2" => {
|
||||||
write!(w, ".section {}", section.name)?;
|
write!(w, ".section {}", section.name)?;
|
||||||
write!(w, ", \"a\"")?;
|
write!(w, ", \"a\"")?;
|
||||||
8
|
|
||||||
}
|
}
|
||||||
".bss" | ".sbss" => {
|
".bss" | ".sbss" => {
|
||||||
write!(w, ".section {}", section.name)?;
|
write!(w, ".section {}", section.name)?;
|
||||||
write!(w, ", \"wa\", @nobits")?;
|
write!(w, ", \"wa\", @nobits")?;
|
||||||
8
|
|
||||||
}
|
}
|
||||||
".sbss2" => {
|
".sbss2" => {
|
||||||
write!(w, ".section {}", section.name)?;
|
write!(w, ".section {}", section.name)?;
|
||||||
write!(w, ", \"a\", @nobits")?;
|
write!(w, ", \"a\", @nobits")?;
|
||||||
8
|
|
||||||
}
|
}
|
||||||
".ctors" | ".dtors" | "extab" | "extabindex" => {
|
".ctors" | ".dtors" | "extab" | "extabindex" => {
|
||||||
write!(w, ".section {}", section.name)?;
|
write!(w, ".section {}", section.name)?;
|
||||||
write!(w, ", \"a\"")?;
|
write!(w, ", \"a\"")?;
|
||||||
4
|
|
||||||
}
|
}
|
||||||
name => todo!("Section {}", name),
|
name => return Err(Error::msg(format!("Unknown section {name}"))),
|
||||||
};
|
};
|
||||||
if subsection != 0 {
|
if subsection != 0 {
|
||||||
write!(w, ", unique, {subsection}")?;
|
write!(w, ", unique, {subsection}")?;
|
||||||
}
|
}
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
if alignment != 0 {
|
if section.align != 0 {
|
||||||
writeln!(w, ".balign {alignment}")?;
|
writeln!(w, ".balign {}", section.align)?;
|
||||||
}
|
}
|
||||||
Ok(w)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_symbol(w: &mut BufWriter<File>, sym: &ObjSymbol) -> std::io::Result<()> {
|
fn write_reloc_symbol<W: Write>(
|
||||||
write_symbol_name(w, &sym.name)?;
|
w: &mut W,
|
||||||
match sym.addend.cmp(&0i64) {
|
symbols: &[ObjSymbol],
|
||||||
Ordering::Greater => write!(w, "+{:#X}", sym.addend),
|
reloc: &ObjReloc,
|
||||||
Ordering::Less => write!(w, "-{:#X}", -sym.addend),
|
) -> std::io::Result<()> {
|
||||||
|
write_symbol_name(w, &symbols[reloc.target_symbol].name)?;
|
||||||
|
match reloc.addend.cmp(&0i64) {
|
||||||
|
Ordering::Greater => write!(w, "+{:#X}", reloc.addend),
|
||||||
|
Ordering::Less => write!(w, "-{:#X}", -reloc.addend),
|
||||||
Ordering::Equal => Ok(()),
|
Ordering::Equal => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_symbol_name(w: &mut BufWriter<File>, name: &str) -> std::io::Result<()> {
|
fn write_symbol_name<W: Write>(w: &mut W, name: &str) -> std::io::Result<()> {
|
||||||
// TODO more?
|
// TODO more?
|
||||||
if name.contains('@') || name.contains('<') {
|
if name.contains('@') || name.contains('<') {
|
||||||
write!(w, "\"{name}\"")?;
|
write!(w, "\"{name}\"")?;
|
||||||
|
@ -802,17 +669,6 @@ fn write_symbol_name(w: &mut BufWriter<File>, name: &str) -> std::io::Result<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_skip_symbol(name: &str) -> bool {
|
|
||||||
// Avoid generating these, they span across files
|
|
||||||
matches!(name, "_ctors" | "_dtors")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_linker_symbol(name: &str) -> bool {
|
|
||||||
matches!(name, "_eti_init_info" | "_rom_copy_info" | "_bss_init_info")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_illegal_instruction(code: u32) -> bool {
|
fn is_illegal_instruction(code: u32) -> bool {
|
||||||
matches!(code, 0x43000000 /* bc 24, lt, 0x0 */ | 0xB8030000 /* lmw r0, 0(r3) */)
|
matches!(code, 0x43000000 /* bc 24, lt, 0x0 */ | 0xB8030000 /* lmw r0, 0(r3) */)
|
||||||
|
|
381
src/util/elf.rs
381
src/util/elf.rs
|
@ -7,16 +7,18 @@ use indexmap::IndexMap;
|
||||||
use memmap2::MmapOptions;
|
use memmap2::MmapOptions;
|
||||||
use object::{
|
use object::{
|
||||||
elf::{
|
elf::{
|
||||||
R_PPC_ADDR16_HA, R_PPC_ADDR16_HI, R_PPC_ADDR16_LO, R_PPC_EMB_SDA21, R_PPC_REL14,
|
R_PPC_ADDR16_HA, R_PPC_ADDR16_HI, R_PPC_ADDR16_LO, R_PPC_ADDR32, R_PPC_EMB_SDA21,
|
||||||
R_PPC_REL24,
|
R_PPC_REL14, R_PPC_REL24,
|
||||||
},
|
},
|
||||||
Architecture, Object, ObjectKind, ObjectSection, ObjectSymbol, Relocation, RelocationKind,
|
write::{Mangling, SectionId, SymbolId},
|
||||||
RelocationTarget, Section, SectionKind, Symbol, SymbolKind, SymbolSection,
|
Architecture, BinaryFormat, Endianness, Object, ObjectKind, ObjectSection, ObjectSymbol,
|
||||||
|
Relocation, RelocationEncoding, RelocationKind, RelocationTarget, SectionKind, Symbol,
|
||||||
|
SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::util::obj::{
|
use crate::util::obj::{
|
||||||
ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind,
|
||||||
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
|
@ -31,13 +33,15 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||||
arch => return Err(Error::msg(format!("Unexpected architecture: {arch:?}"))),
|
arch => return Err(Error::msg(format!("Unexpected architecture: {arch:?}"))),
|
||||||
};
|
};
|
||||||
if obj_file.is_little_endian() {
|
if obj_file.endianness() != Endianness::Big {
|
||||||
return Err(Error::msg("Expected big endian"));
|
return Err(Error::msg("Expected big endian"));
|
||||||
}
|
}
|
||||||
match obj_file.kind() {
|
let kind = match obj_file.kind() {
|
||||||
ObjectKind::Executable => {}
|
ObjectKind::Executable => ObjKind::Executable,
|
||||||
|
ObjectKind::Relocatable => ObjKind::Relocatable,
|
||||||
kind => return Err(Error::msg(format!("Unexpected ELF type: {kind:?}"))),
|
kind => return Err(Error::msg(format!("Unexpected ELF type: {kind:?}"))),
|
||||||
}
|
};
|
||||||
|
let mut obj_name = String::new();
|
||||||
|
|
||||||
let mut stack_address: Option<u32> = None;
|
let mut stack_address: Option<u32> = None;
|
||||||
let mut stack_end: Option<u32> = None;
|
let mut stack_end: Option<u32> = None;
|
||||||
|
@ -45,7 +49,36 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
let mut arena_lo: Option<u32> = None;
|
let mut arena_lo: Option<u32> = None;
|
||||||
let mut arena_hi: Option<u32> = None;
|
let mut arena_hi: Option<u32> = None;
|
||||||
|
|
||||||
let mut common: Vec<ObjSymbol> = vec![];
|
let mut sections: Vec<ObjSection> = vec![];
|
||||||
|
let mut section_indexes: Vec<Option<usize>> = vec![];
|
||||||
|
for section in obj_file.sections() {
|
||||||
|
let section_kind = match section.kind() {
|
||||||
|
SectionKind::Text => ObjSectionKind::Code,
|
||||||
|
SectionKind::Data => ObjSectionKind::Data,
|
||||||
|
SectionKind::ReadOnlyData => ObjSectionKind::ReadOnlyData,
|
||||||
|
SectionKind::UninitializedData => ObjSectionKind::Bss,
|
||||||
|
_ => {
|
||||||
|
section_indexes.push(None);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
section_indexes.push(Some(sections.len()));
|
||||||
|
sections.push(ObjSection {
|
||||||
|
name: section.name()?.to_string(),
|
||||||
|
kind: section_kind,
|
||||||
|
address: section.address(),
|
||||||
|
size: section.size(),
|
||||||
|
data: section.uncompressed_data()?.to_vec(),
|
||||||
|
align: section.align(),
|
||||||
|
index: sections.len(),
|
||||||
|
relocations: vec![],
|
||||||
|
original_address: 0, // TODO load from abs symbol
|
||||||
|
file_offset: section.file_range().map(|(v, _)| v).unwrap_or_default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut symbols: Vec<ObjSymbol> = vec![];
|
||||||
|
let mut symbol_indexes: Vec<Option<usize>> = vec![];
|
||||||
let mut current_file: Option<String> = None;
|
let mut current_file: Option<String> = None;
|
||||||
let mut section_starts = IndexMap::<String, Vec<(u64, String)>>::new();
|
let mut section_starts = IndexMap::<String, Vec<(u64, String)>>::new();
|
||||||
for symbol in obj_file.symbols() {
|
for symbol in obj_file.symbols() {
|
||||||
|
@ -54,23 +87,18 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
match symbol_name {
|
match symbol_name {
|
||||||
"_stack_addr" => {
|
"_stack_addr" => {
|
||||||
stack_address = Some(symbol.address() as u32);
|
stack_address = Some(symbol.address() as u32);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
"_stack_end" => {
|
"_stack_end" => {
|
||||||
stack_end = Some(symbol.address() as u32);
|
stack_end = Some(symbol.address() as u32);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
"_db_stack_addr" => {
|
"_db_stack_addr" => {
|
||||||
db_stack_addr = Some(symbol.address() as u32);
|
db_stack_addr = Some(symbol.address() as u32);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
"__ArenaLo" => {
|
"__ArenaLo" => {
|
||||||
arena_lo = Some(symbol.address() as u32);
|
arena_lo = Some(symbol.address() as u32);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
"__ArenaHi" => {
|
"__ArenaHi" => {
|
||||||
arena_hi = Some(symbol.address() as u32);
|
arena_hi = Some(symbol.address() as u32);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +107,9 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
// Detect file boundaries
|
// Detect file boundaries
|
||||||
SymbolKind::File => {
|
SymbolKind::File => {
|
||||||
let file_name = symbol_name.to_string();
|
let file_name = symbol_name.to_string();
|
||||||
|
if kind == ObjKind::Relocatable {
|
||||||
|
obj_name = file_name.clone();
|
||||||
|
}
|
||||||
match section_starts.entry(file_name.clone()) {
|
match section_starts.entry(file_name.clone()) {
|
||||||
indexmap::map::Entry::Occupied(_) => {
|
indexmap::map::Entry::Occupied(_) => {
|
||||||
return Err(Error::msg(format!("Duplicate file name: {file_name}")));
|
return Err(Error::msg(format!("Duplicate file name: {file_name}")));
|
||||||
|
@ -86,15 +117,13 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
indexmap::map::Entry::Vacant(e) => e.insert(Default::default()),
|
indexmap::map::Entry::Vacant(e) => e.insert(Default::default()),
|
||||||
};
|
};
|
||||||
current_file = Some(file_name);
|
current_file = Some(file_name);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
// Detect sections within a file
|
// Detect sections within a file
|
||||||
SymbolKind::Section => {
|
SymbolKind::Section => {
|
||||||
if let Some(file_name) = ¤t_file {
|
if let Some(file_name) = ¤t_file {
|
||||||
let sections = match section_starts.get_mut(file_name) {
|
let sections = section_starts
|
||||||
Some(entries) => entries,
|
.get_mut(file_name)
|
||||||
None => return Err(Error::msg("Failed to create entry")),
|
.ok_or_else(|| Error::msg("Failed to create entry"))?;
|
||||||
};
|
|
||||||
let section_index = symbol
|
let section_index = symbol
|
||||||
.section_index()
|
.section_index()
|
||||||
.ok_or_else(|| Error::msg("Section symbol without section"))?;
|
.ok_or_else(|| Error::msg("Section symbol without section"))?;
|
||||||
|
@ -102,30 +131,27 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
let section_name = section.name()?.to_string();
|
let section_name = section.name()?.to_string();
|
||||||
sections.push((symbol.address(), section_name));
|
sections.push((symbol.address(), section_name));
|
||||||
};
|
};
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
// Sometimes, the section symbol address is 0,
|
// Sometimes, the section symbol address is 0,
|
||||||
// so attempt to detect it from first symbol within section
|
// so attempt to detect it from first symbol within section
|
||||||
SymbolKind::Data | SymbolKind::Text => {
|
SymbolKind::Data | SymbolKind::Text => {
|
||||||
if let Some(file_name) = ¤t_file {
|
if let Some(file_name) = ¤t_file {
|
||||||
let section_map = match section_starts.get_mut(file_name) {
|
let sections = section_starts
|
||||||
Some(entries) => entries,
|
.get_mut(file_name)
|
||||||
None => return Err(Error::msg("Failed to create entry")),
|
.ok_or_else(|| Error::msg("Failed to create entry"))?;
|
||||||
};
|
let section_index = symbol.section_index().ok_or_else(|| {
|
||||||
let section_index = symbol
|
Error::msg(format!("Section symbol without section: {symbol:?}"))
|
||||||
.section_index()
|
})?;
|
||||||
.ok_or_else(|| Error::msg("Section symbol without section"))?;
|
|
||||||
let section = obj_file.section_by_index(section_index)?;
|
let section = obj_file.section_by_index(section_index)?;
|
||||||
let section_name = section.name()?;
|
let section_name = section.name()?;
|
||||||
if let Some((addr, _)) =
|
if let Some((addr, _)) =
|
||||||
section_map.iter_mut().find(|(_, name)| name == section_name)
|
sections.iter_mut().find(|(_, name)| name == section_name)
|
||||||
{
|
{
|
||||||
if *addr == 0 {
|
if *addr == 0 {
|
||||||
*addr = symbol.address();
|
*addr = symbol.address();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
_ => match symbol.section() {
|
_ => match symbol.section() {
|
||||||
// Linker generated symbols indicate the end
|
// Linker generated symbols indicate the end
|
||||||
|
@ -133,82 +159,60 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
current_file = None;
|
current_file = None;
|
||||||
}
|
}
|
||||||
SymbolSection::Section(_) | SymbolSection::Undefined => {}
|
SymbolSection::Section(_) | SymbolSection::Undefined => {}
|
||||||
_ => todo!("Symbol section type {:?}", symbol),
|
_ => return Err(Error::msg(format!("Unsupported symbol section type {symbol:?}"))),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of common symbols
|
// Generate symbols
|
||||||
if !symbol.is_common() {
|
if matches!(symbol.kind(), SymbolKind::Null | SymbolKind::File) {
|
||||||
|
symbol_indexes.push(None);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
common.push(to_obj_symbol(&obj_file, &symbol, 0)?);
|
symbol_indexes.push(Some(symbols.len()));
|
||||||
|
symbols.push(to_obj_symbol(&obj_file, &symbol, §ion_indexes)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link order is trivially deduced
|
|
||||||
let mut link_order = Vec::<String>::new();
|
let mut link_order = Vec::<String>::new();
|
||||||
for file_name in section_starts.keys() {
|
|
||||||
link_order.push(file_name.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a map of address -> file splits
|
|
||||||
let mut splits = BTreeMap::<u32, String>::new();
|
let mut splits = BTreeMap::<u32, String>::new();
|
||||||
for (file_name, sections) in section_starts {
|
if kind == ObjKind::Executable {
|
||||||
for (address, _) in sections {
|
// Link order is trivially deduced
|
||||||
splits.insert(address as u32, file_name.clone());
|
for file_name in section_starts.keys() {
|
||||||
|
link_order.push(file_name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a map of address -> file splits
|
||||||
|
for (file_name, sections) in section_starts {
|
||||||
|
for (address, _) in sections {
|
||||||
|
splits.insert(address as u32, file_name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO rebuild common symbols
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sections: Vec<ObjSection> = vec![];
|
for (section_idx, section) in obj_file.sections().enumerate() {
|
||||||
for section in obj_file.sections() {
|
let out_section = match section_indexes[section_idx].and_then(|idx| sections.get_mut(idx)) {
|
||||||
let section_index = section.index();
|
Some(s) => s,
|
||||||
let section_kind = match section.kind() {
|
None => continue,
|
||||||
SectionKind::Text => ObjSectionKind::Code,
|
|
||||||
SectionKind::Data => ObjSectionKind::Data,
|
|
||||||
SectionKind::ReadOnlyData => ObjSectionKind::Data,
|
|
||||||
SectionKind::UninitializedData => ObjSectionKind::Bss,
|
|
||||||
_ => continue,
|
|
||||||
};
|
};
|
||||||
let name = section.name()?;
|
|
||||||
log::info!("Processing section {}", name);
|
|
||||||
let data = section.uncompressed_data()?.to_vec();
|
|
||||||
|
|
||||||
// Generate symbols
|
|
||||||
let mut symbols: Vec<ObjSymbol> = vec![];
|
|
||||||
for symbol in obj_file.symbols() {
|
|
||||||
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if symbol.address() == 0 || symbol.name()?.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
symbols.push(to_obj_symbol(&obj_file, &symbol, 0)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate relocations
|
// Generate relocations
|
||||||
let mut relocations: Vec<ObjReloc> = vec![];
|
|
||||||
for (address, reloc) in section.relocations() {
|
for (address, reloc) in section.relocations() {
|
||||||
relocations.push(to_obj_reloc(&obj_file, §ion, &data, address, reloc)?);
|
out_section.relocations.push(to_obj_reloc(
|
||||||
|
&obj_file,
|
||||||
|
&symbol_indexes,
|
||||||
|
&out_section.data,
|
||||||
|
address,
|
||||||
|
reloc,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_offset = section.file_range().map(|(v, _)| v).unwrap_or_default();
|
|
||||||
sections.push(ObjSection {
|
|
||||||
name: name.to_string(),
|
|
||||||
kind: section_kind,
|
|
||||||
address: section.address(),
|
|
||||||
size: section.size(),
|
|
||||||
data,
|
|
||||||
index: sections.len(),
|
|
||||||
symbols,
|
|
||||||
relocations,
|
|
||||||
file_offset,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ObjInfo {
|
Ok(ObjInfo {
|
||||||
|
kind,
|
||||||
architecture,
|
architecture,
|
||||||
path: path.as_ref().to_path_buf(),
|
name: obj_name,
|
||||||
|
symbols,
|
||||||
sections,
|
sections,
|
||||||
common,
|
|
||||||
entry: obj_file.entry() as u32,
|
entry: obj_file.entry() as u32,
|
||||||
stack_address,
|
stack_address,
|
||||||
stack_end,
|
stack_end,
|
||||||
|
@ -220,10 +224,107 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_elf(obj: &ObjInfo) -> Result<object::write::Object> {
|
||||||
|
let mut out_obj =
|
||||||
|
object::write::Object::new(BinaryFormat::Elf, Architecture::PowerPc, Endianness::Big);
|
||||||
|
out_obj.set_mangling(Mangling::None);
|
||||||
|
if !obj.name.is_empty() {
|
||||||
|
out_obj.add_file_symbol(obj.name.as_bytes().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut section_idxs: Vec<SectionId> = Vec::with_capacity(obj.sections.len());
|
||||||
|
for section in &obj.sections {
|
||||||
|
let section_id =
|
||||||
|
out_obj.add_section(vec![], section.name.as_bytes().to_vec(), match section.kind {
|
||||||
|
ObjSectionKind::Code => SectionKind::Text,
|
||||||
|
ObjSectionKind::Data => SectionKind::Data,
|
||||||
|
ObjSectionKind::ReadOnlyData => SectionKind::ReadOnlyData,
|
||||||
|
ObjSectionKind::Bss => SectionKind::UninitializedData,
|
||||||
|
});
|
||||||
|
section_idxs.push(section_id);
|
||||||
|
let out_section = out_obj.section_mut(section_id);
|
||||||
|
match section.kind {
|
||||||
|
ObjSectionKind::Bss => {
|
||||||
|
out_section.append_bss(section.size, section.align);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
out_section.set_data(section.data.clone(), section.align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate section symbol
|
||||||
|
out_obj.section_symbol(section_id);
|
||||||
|
|
||||||
|
// Add original addresses
|
||||||
|
if section.original_address != 0 {
|
||||||
|
// TODO write to metadata?
|
||||||
|
}
|
||||||
|
if section.file_offset != 0 {
|
||||||
|
// TODO write to metadata?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add symbols
|
||||||
|
let mut symbol_idxs: Vec<SymbolId> = Vec::with_capacity(obj.symbols.len());
|
||||||
|
for symbol in &obj.symbols {
|
||||||
|
let symbol_id = out_obj.add_symbol(object::write::Symbol {
|
||||||
|
name: symbol.name.as_bytes().to_vec(),
|
||||||
|
value: symbol.address,
|
||||||
|
size: symbol.size,
|
||||||
|
kind: match symbol.kind {
|
||||||
|
ObjSymbolKind::Unknown => SymbolKind::Null,
|
||||||
|
ObjSymbolKind::Function => SymbolKind::Text,
|
||||||
|
ObjSymbolKind::Object => SymbolKind::Data,
|
||||||
|
ObjSymbolKind::Section => SymbolKind::Section,
|
||||||
|
},
|
||||||
|
scope: if symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
||||||
|
SymbolScope::Linkage
|
||||||
|
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
||||||
|
SymbolScope::Compilation
|
||||||
|
} else {
|
||||||
|
SymbolScope::Dynamic
|
||||||
|
},
|
||||||
|
weak: symbol.flags.0.contains(ObjSymbolFlags::Weak),
|
||||||
|
section: match symbol.section {
|
||||||
|
None => object::write::SymbolSection::Undefined,
|
||||||
|
Some(idx) => object::write::SymbolSection::Section(section_idxs[idx]),
|
||||||
|
},
|
||||||
|
flags: SymbolFlags::None,
|
||||||
|
});
|
||||||
|
symbol_idxs.push(symbol_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add relocations
|
||||||
|
for section in &obj.sections {
|
||||||
|
let section_id = section_idxs[section.index];
|
||||||
|
for reloc in §ion.relocations {
|
||||||
|
let symbol_id = symbol_idxs[reloc.target_symbol];
|
||||||
|
out_obj.add_relocation(section_id, object::write::Relocation {
|
||||||
|
offset: reloc.address,
|
||||||
|
size: 0,
|
||||||
|
kind: RelocationKind::Elf(match reloc.kind {
|
||||||
|
ObjRelocKind::Absolute => R_PPC_ADDR32,
|
||||||
|
ObjRelocKind::PpcAddr16Hi => R_PPC_ADDR16_HI,
|
||||||
|
ObjRelocKind::PpcAddr16Ha => R_PPC_ADDR16_HA,
|
||||||
|
ObjRelocKind::PpcAddr16Lo => R_PPC_ADDR16_LO,
|
||||||
|
ObjRelocKind::PpcRel24 => R_PPC_REL24,
|
||||||
|
ObjRelocKind::PpcRel14 => R_PPC_REL14,
|
||||||
|
ObjRelocKind::PpcEmbSda21 => R_PPC_EMB_SDA21,
|
||||||
|
}),
|
||||||
|
encoding: RelocationEncoding::Generic,
|
||||||
|
symbol: symbol_id,
|
||||||
|
addend: reloc.addend,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out_obj)
|
||||||
|
}
|
||||||
|
|
||||||
fn to_obj_symbol(
|
fn to_obj_symbol(
|
||||||
obj_file: &object::File<'_>,
|
obj_file: &object::File<'_>,
|
||||||
symbol: &Symbol<'_, '_>,
|
symbol: &Symbol<'_, '_>,
|
||||||
addend: i64,
|
section_indexes: &[Option<usize>],
|
||||||
) -> Result<ObjSymbol> {
|
) -> Result<ObjSymbol> {
|
||||||
let section = match symbol.section_index() {
|
let section = match symbol.section_index() {
|
||||||
Some(idx) => Some(obj_file.section_by_index(idx)?),
|
Some(idx) => Some(obj_file.section_by_index(idx)?),
|
||||||
|
@ -255,31 +356,31 @@ fn to_obj_symbol(
|
||||||
if symbol.is_weak() {
|
if symbol.is_weak() {
|
||||||
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
||||||
}
|
}
|
||||||
let section_address = if let Some(section) = §ion {
|
if symbol.scope() == SymbolScope::Linkage {
|
||||||
symbol.address() - section.address()
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Hidden);
|
||||||
} else {
|
}
|
||||||
symbol.address()
|
let section_idx = section.as_ref().and_then(|section| section_indexes[section.index().0]);
|
||||||
};
|
|
||||||
Ok(ObjSymbol {
|
Ok(ObjSymbol {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
demangled_name: demangle(name, &Default::default()),
|
demangled_name: demangle(name, &Default::default()),
|
||||||
address: symbol.address(),
|
address: symbol.address(),
|
||||||
section_address,
|
section: section_idx,
|
||||||
size: symbol.size(),
|
size: symbol.size(),
|
||||||
size_known: symbol.size() != 0,
|
size_known: true,
|
||||||
flags,
|
flags,
|
||||||
addend,
|
|
||||||
kind: match symbol.kind() {
|
kind: match symbol.kind() {
|
||||||
SymbolKind::Text => ObjSymbolKind::Function,
|
SymbolKind::Text => ObjSymbolKind::Function,
|
||||||
SymbolKind::Data => ObjSymbolKind::Object,
|
SymbolKind::Data => ObjSymbolKind::Object,
|
||||||
_ => ObjSymbolKind::Unknown,
|
SymbolKind::Unknown => ObjSymbolKind::Unknown,
|
||||||
|
SymbolKind::Section => ObjSymbolKind::Section,
|
||||||
|
_ => return Err(Error::msg(format!("Unsupported symbol kind: {:?}", symbol.kind()))),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_obj_reloc(
|
fn to_obj_reloc(
|
||||||
obj_file: &object::File<'_>,
|
obj_file: &object::File<'_>,
|
||||||
_section: &Section<'_, '_>,
|
symbol_indexes: &[Option<usize>],
|
||||||
section_data: &[u8],
|
section_data: &[u8],
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: Relocation,
|
reloc: Relocation,
|
||||||
|
@ -305,17 +406,10 @@ fn to_obj_reloc(
|
||||||
return Err(Error::msg(format!("Unhandled relocation target: {:?}", reloc.target())));
|
return Err(Error::msg(format!("Unhandled relocation target: {:?}", reloc.target())));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let target_section = match symbol.section() {
|
let target_symbol = symbol_indexes[symbol.index().0]
|
||||||
SymbolSection::Common => Some(".comm".to_string()),
|
.ok_or_else(|| Error::msg(format!("Relocation against stripped symbol: {symbol:?}")))?;
|
||||||
SymbolSection::Section(idx) => {
|
let addend = match symbol.kind() {
|
||||||
obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok()
|
SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => Ok(reloc.addend()),
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let target = match symbol.kind() {
|
|
||||||
SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => {
|
|
||||||
to_obj_symbol(obj_file, &symbol, reloc.addend())
|
|
||||||
}
|
|
||||||
SymbolKind::Section => {
|
SymbolKind::Section => {
|
||||||
let addend = if reloc.has_implicit_addend() {
|
let addend = if reloc.has_implicit_addend() {
|
||||||
let addend = u32::from_be_bytes(
|
let addend = u32::from_be_bytes(
|
||||||
|
@ -323,70 +417,23 @@ fn to_obj_reloc(
|
||||||
) as i64;
|
) as i64;
|
||||||
match reloc_kind {
|
match reloc_kind {
|
||||||
ObjRelocKind::Absolute => addend,
|
ObjRelocKind::Absolute => addend,
|
||||||
_ => todo!(),
|
_ => {
|
||||||
|
return Err(Error::msg(format!(
|
||||||
|
"Unsupported implicit relocation type {reloc_kind:?}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let addend = reloc.addend();
|
reloc.addend()
|
||||||
if addend < 0 {
|
|
||||||
return Err(Error::msg(format!("Negative addend in section reloc: {addend}")));
|
|
||||||
}
|
|
||||||
addend
|
|
||||||
};
|
};
|
||||||
// find_section_symbol(&obj_file, &symbol, addend as u64)
|
if addend < 0 {
|
||||||
to_obj_symbol(obj_file, &symbol, addend)
|
return Err(Error::msg(format!("Negative addend in section reloc: {addend}")));
|
||||||
|
}
|
||||||
|
Ok(addend)
|
||||||
}
|
}
|
||||||
_ => Err(Error::msg(format!("Unhandled relocation symbol type {:?}", symbol.kind()))),
|
_ => Err(Error::msg(format!("Unhandled relocation symbol type {:?}", symbol.kind()))),
|
||||||
}?;
|
}?;
|
||||||
let address = address & !3; // FIXME round down for instruction
|
let address = address & !3; // TODO hack: round down for instruction
|
||||||
let reloc_data = ObjReloc { kind: reloc_kind, address, target, target_section };
|
let reloc_data = ObjReloc { kind: reloc_kind, address, target_symbol, addend };
|
||||||
Ok(reloc_data)
|
Ok(reloc_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO needed?
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn find_section_symbol(
|
|
||||||
obj_file: &object::File<'_>,
|
|
||||||
target: &Symbol<'_, '_>,
|
|
||||||
addend: u64,
|
|
||||||
) -> Result<ObjSymbol> {
|
|
||||||
let section_index =
|
|
||||||
target.section_index().ok_or_else(|| Error::msg("Unknown section index"))?;
|
|
||||||
let section = obj_file.section_by_index(section_index)?;
|
|
||||||
let target_address = target.address() + addend;
|
|
||||||
|
|
||||||
let mut closest_symbol: Option<Symbol<'_, '_>> = None;
|
|
||||||
for symbol in obj_file.symbols() {
|
|
||||||
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if symbol.kind() == SymbolKind::Section || symbol.address() != target_address {
|
|
||||||
if symbol.address() < target_address
|
|
||||||
&& symbol.size() != 0
|
|
||||||
&& (closest_symbol.is_none()
|
|
||||||
|| matches!(&closest_symbol, Some(s) if s.address() <= symbol.address()))
|
|
||||||
{
|
|
||||||
closest_symbol = Some(symbol);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return to_obj_symbol(obj_file, &symbol, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(symbol) = closest_symbol {
|
|
||||||
let addend = target_address - symbol.address();
|
|
||||||
Ok(to_obj_symbol(obj_file, &symbol, addend as i64)?)
|
|
||||||
} else {
|
|
||||||
let addend = target_address - section.address();
|
|
||||||
Ok(ObjSymbol {
|
|
||||||
name: section.name()?.to_string(),
|
|
||||||
demangled_name: None,
|
|
||||||
address: section.address(),
|
|
||||||
section_address: 0,
|
|
||||||
size: section.size(),
|
|
||||||
size_known: true,
|
|
||||||
flags: Default::default(),
|
|
||||||
addend: addend as i64,
|
|
||||||
kind: ObjSymbolKind::Unknown,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,7 +51,8 @@ struct SectionOrder {
|
||||||
unit_order: Vec<(String, Vec<String>)>,
|
unit_order: Vec<(String, Vec<String>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_code_section(section: &str) -> bool { section == ".text" || section == ".init" }
|
#[inline]
|
||||||
|
fn is_code_section(section: &str) -> bool { matches!(section, ".text" | ".init") }
|
||||||
|
|
||||||
/// Iterate over the BTreeMap and generate an ordered list of symbols and TUs by address.
|
/// Iterate over the BTreeMap and generate an ordered list of symbols and TUs by address.
|
||||||
fn resolve_section_order(
|
fn resolve_section_order(
|
||||||
|
|
|
@ -2,3 +2,4 @@ pub(crate) mod asm;
|
||||||
pub(crate) mod elf;
|
pub(crate) mod elf;
|
||||||
pub(crate) mod map;
|
pub(crate) mod map;
|
||||||
pub(crate) mod obj;
|
pub(crate) mod obj;
|
||||||
|
pub(crate) mod split;
|
||||||
|
|
100
src/util/obj.rs
100
src/util/obj.rs
|
@ -1,5 +1,9 @@
|
||||||
use std::{collections::BTreeMap, path::PathBuf};
|
use std::{
|
||||||
|
collections::{btree_map, BTreeMap},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{Error, Result};
|
||||||
use flagset::{flags, FlagSet};
|
use flagset::{flags, FlagSet};
|
||||||
|
|
||||||
flags! {
|
flags! {
|
||||||
|
@ -8,14 +12,20 @@ flags! {
|
||||||
Local,
|
Local,
|
||||||
Weak,
|
Weak,
|
||||||
Common,
|
Common,
|
||||||
|
Hidden,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
|
||||||
pub struct ObjSymbolFlagSet(pub(crate) FlagSet<ObjSymbolFlags>);
|
pub struct ObjSymbolFlagSet(pub(crate) FlagSet<ObjSymbolFlags>);
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
|
impl Hash for ObjSymbolFlagSet {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) { self.0.bits().hash(state) }
|
||||||
|
}
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum ObjSectionKind {
|
pub enum ObjSectionKind {
|
||||||
Code,
|
Code,
|
||||||
Data,
|
Data,
|
||||||
|
ReadOnlyData,
|
||||||
Bss,
|
Bss,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -25,39 +35,49 @@ pub struct ObjSection {
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
|
pub align: u64,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub symbols: Vec<ObjSymbol>,
|
|
||||||
pub relocations: Vec<ObjReloc>,
|
pub relocations: Vec<ObjReloc>,
|
||||||
|
pub original_address: u64,
|
||||||
pub file_offset: u64,
|
pub file_offset: u64,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
pub enum ObjSymbolKind {
|
pub enum ObjSymbolKind {
|
||||||
|
#[default]
|
||||||
Unknown,
|
Unknown,
|
||||||
Function,
|
Function,
|
||||||
Object,
|
Object,
|
||||||
|
Section,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjSymbol {
|
pub struct ObjSymbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub demangled_name: Option<String>,
|
pub demangled_name: Option<String>,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub section_address: u64,
|
pub section: Option<usize>,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub size_known: bool,
|
pub size_known: bool,
|
||||||
pub flags: ObjSymbolFlagSet,
|
pub flags: ObjSymbolFlagSet,
|
||||||
pub addend: i64,
|
|
||||||
pub kind: ObjSymbolKind,
|
pub kind: ObjSymbolKind,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum ObjKind {
|
||||||
|
/// Fully linked file
|
||||||
|
Executable,
|
||||||
|
/// Relocatable file
|
||||||
|
Relocatable,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum ObjArchitecture {
|
pub enum ObjArchitecture {
|
||||||
PowerPc,
|
PowerPc,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInfo {
|
pub struct ObjInfo {
|
||||||
|
pub kind: ObjKind,
|
||||||
pub architecture: ObjArchitecture,
|
pub architecture: ObjArchitecture,
|
||||||
pub path: PathBuf,
|
pub name: String,
|
||||||
|
pub symbols: Vec<ObjSymbol>,
|
||||||
pub sections: Vec<ObjSection>,
|
pub sections: Vec<ObjSection>,
|
||||||
pub common: Vec<ObjSymbol>,
|
|
||||||
pub entry: u32,
|
pub entry: u32,
|
||||||
|
|
||||||
// Linker generated
|
// Linker generated
|
||||||
|
@ -71,7 +91,7 @@ pub struct ObjInfo {
|
||||||
pub splits: BTreeMap<u32, String>,
|
pub splits: BTreeMap<u32, String>,
|
||||||
pub link_order: Vec<String>,
|
pub link_order: Vec<String>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum ObjRelocKind {
|
pub enum ObjRelocKind {
|
||||||
Absolute,
|
Absolute,
|
||||||
PpcAddr16Hi,
|
PpcAddr16Hi,
|
||||||
|
@ -85,6 +105,58 @@ pub enum ObjRelocKind {
|
||||||
pub struct ObjReloc {
|
pub struct ObjReloc {
|
||||||
pub kind: ObjRelocKind,
|
pub kind: ObjRelocKind,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub target: ObjSymbol,
|
pub target_symbol: usize,
|
||||||
pub target_section: Option<String>,
|
pub addend: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjInfo {
|
||||||
|
pub fn symbols_for_section(
|
||||||
|
&self,
|
||||||
|
section_idx: usize,
|
||||||
|
) -> impl Iterator<Item = (usize, &ObjSymbol)> {
|
||||||
|
self.symbols
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(move |&(_, symbol)| symbol.section == Some(section_idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_symbol_map(&self, section_idx: usize) -> Result<BTreeMap<u32, Vec<usize>>> {
|
||||||
|
let mut symbols = BTreeMap::<u32, Vec<usize>>::new();
|
||||||
|
for (symbol_idx, symbol) in self.symbols_for_section(section_idx) {
|
||||||
|
let address = symbol.address as u32;
|
||||||
|
nested_push(&mut symbols, address, symbol_idx);
|
||||||
|
}
|
||||||
|
Ok(symbols)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjSection {
|
||||||
|
pub fn build_relocation_map(&self) -> Result<BTreeMap<u32, ObjReloc>> {
|
||||||
|
let mut relocations = BTreeMap::<u32, ObjReloc>::new();
|
||||||
|
for reloc in &self.relocations {
|
||||||
|
let address = reloc.address as u32;
|
||||||
|
match relocations.entry(address) {
|
||||||
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
e.insert(reloc.clone());
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(_) => {
|
||||||
|
return Err(Error::msg(format!("Duplicate relocation @ {address:#010X}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(relocations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn nested_push<T1, T2>(map: &mut BTreeMap<T1, Vec<T2>>, v1: T1, v2: T2)
|
||||||
|
where T1: Ord {
|
||||||
|
match map.entry(v1) {
|
||||||
|
btree_map::Entry::Occupied(mut e) => {
|
||||||
|
e.get_mut().push(v2);
|
||||||
|
}
|
||||||
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
e.insert(vec![v2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
use std::{cmp::min, collections::HashMap};
|
||||||
|
|
||||||
|
use anyhow::{Error, Result};
|
||||||
|
|
||||||
|
use crate::util::obj::{
|
||||||
|
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Split an executable object into relocatable objects.
|
||||||
|
pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
||||||
|
if obj.kind != ObjKind::Executable {
|
||||||
|
return Err(Error::msg(format!("Expected executable object, got {:?}", obj.kind)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut objects: Vec<ObjInfo> = vec![];
|
||||||
|
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
|
||||||
|
let mut name_to_obj: HashMap<String, usize> = HashMap::new();
|
||||||
|
for unit in &obj.link_order {
|
||||||
|
name_to_obj.insert(unit.clone(), objects.len());
|
||||||
|
object_symbols.push(vec![None; obj.symbols.len()]);
|
||||||
|
objects.push(ObjInfo {
|
||||||
|
kind: ObjKind::Relocatable,
|
||||||
|
architecture: ObjArchitecture::PowerPc,
|
||||||
|
name: unit.clone(),
|
||||||
|
symbols: vec![],
|
||||||
|
sections: vec![],
|
||||||
|
entry: 0,
|
||||||
|
stack_address: None,
|
||||||
|
stack_end: None,
|
||||||
|
db_stack_addr: None,
|
||||||
|
arena_lo: None,
|
||||||
|
arena_hi: None,
|
||||||
|
splits: Default::default(),
|
||||||
|
link_order: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||||
|
let mut current_address = section.address as u32;
|
||||||
|
let mut section_end = (section.address + section.size) as u32;
|
||||||
|
// .ctors and .dtors end with a linker-generated null pointer,
|
||||||
|
// adjust section size appropriately
|
||||||
|
if matches!(section.name.as_str(), ".ctors" | ".dtors")
|
||||||
|
&& section.data[section.data.len() - 4..] == [0u8; 4]
|
||||||
|
{
|
||||||
|
section_end -= 4;
|
||||||
|
}
|
||||||
|
let mut file_iter = obj.splits.range(current_address..).peekable();
|
||||||
|
|
||||||
|
// Build address to relocation / address to symbol maps
|
||||||
|
let relocations = section.build_relocation_map()?;
|
||||||
|
let symbols = obj.build_symbol_map(section_idx)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if current_address >= section_end {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (file_addr, unit) = match file_iter.next() {
|
||||||
|
Some((&addr, unit)) => (addr, unit),
|
||||||
|
None => return Err(Error::msg("No file found")),
|
||||||
|
};
|
||||||
|
if file_addr > current_address {
|
||||||
|
return Err(Error::msg(format!(
|
||||||
|
"Gap in files: {} @ {:#010X}, {} @ {:#010X}",
|
||||||
|
section.name, section.address, unit, file_addr
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let mut file_end = section_end;
|
||||||
|
if let Some(&(&next_addr, _)) = file_iter.peek() {
|
||||||
|
file_end = min(next_addr, section_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = name_to_obj
|
||||||
|
.get(unit)
|
||||||
|
.and_then(|&idx| objects.get_mut(idx))
|
||||||
|
.ok_or_else(|| Error::msg(format!("Unit '{unit}' not in link order")))?;
|
||||||
|
let symbol_idxs = name_to_obj
|
||||||
|
.get(unit)
|
||||||
|
.and_then(|&idx| object_symbols.get_mut(idx))
|
||||||
|
.ok_or_else(|| Error::msg(format!("Unit '{unit}' not in link order")))?;
|
||||||
|
let data = match section.kind {
|
||||||
|
ObjSectionKind::Bss => vec![],
|
||||||
|
_ => section.data[(current_address as u64 - section.address) as usize
|
||||||
|
..(file_end as u64 - section.address) as usize]
|
||||||
|
.to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate & verify section alignment
|
||||||
|
let mut align = default_section_align(section);
|
||||||
|
if current_address & (align as u32 - 1) != 0 {
|
||||||
|
log::warn!(
|
||||||
|
"Alignment for {} {} expected {}, but starts at {:#010X}",
|
||||||
|
unit,
|
||||||
|
section.name,
|
||||||
|
align,
|
||||||
|
current_address
|
||||||
|
);
|
||||||
|
align = 4;
|
||||||
|
}
|
||||||
|
if current_address & (align as u32 - 1) != 0 {
|
||||||
|
return Err(Error::msg(format!(
|
||||||
|
"Invalid alignment for split: {} {} {:#010X}",
|
||||||
|
unit, section.name, current_address
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect relocations; target_symbol will be updated later
|
||||||
|
let out_relocations = relocations
|
||||||
|
.range(current_address..file_end)
|
||||||
|
.map(|(_, o)| ObjReloc {
|
||||||
|
kind: o.kind,
|
||||||
|
address: o.address - current_address as u64,
|
||||||
|
target_symbol: o.target_symbol,
|
||||||
|
addend: o.addend,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let out_section_idx = file.sections.len();
|
||||||
|
file.sections.push(ObjSection {
|
||||||
|
name: section.name.clone(),
|
||||||
|
kind: section.kind,
|
||||||
|
address: 0,
|
||||||
|
size: file_end as u64 - current_address as u64,
|
||||||
|
data,
|
||||||
|
align,
|
||||||
|
index: out_section_idx,
|
||||||
|
relocations: out_relocations,
|
||||||
|
original_address: current_address as u64,
|
||||||
|
file_offset: section.file_offset + (current_address as u64 - section.address),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add section symbols
|
||||||
|
for &symbol_idx in symbols.range(current_address..file_end).flat_map(|(_, vec)| vec) {
|
||||||
|
if symbol_idxs[symbol_idx].is_some() {
|
||||||
|
continue; // should never happen?
|
||||||
|
}
|
||||||
|
let symbol = &obj.symbols[symbol_idx];
|
||||||
|
symbol_idxs[symbol_idx] = Some(file.symbols.len());
|
||||||
|
file.symbols.push(ObjSymbol {
|
||||||
|
name: symbol.name.clone(),
|
||||||
|
demangled_name: symbol.demangled_name.clone(),
|
||||||
|
address: symbol.address - current_address as u64,
|
||||||
|
section: Some(out_section_idx),
|
||||||
|
size: symbol.size,
|
||||||
|
size_known: symbol.size_known,
|
||||||
|
flags: symbol.flags,
|
||||||
|
kind: symbol.kind,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
current_address = file_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update relocations
|
||||||
|
for (obj_idx, out_obj) in objects.iter_mut().enumerate() {
|
||||||
|
let symbol_idxs = &mut object_symbols[obj_idx];
|
||||||
|
for section in &mut out_obj.sections {
|
||||||
|
for reloc in &mut section.relocations {
|
||||||
|
match symbol_idxs[reloc.target_symbol] {
|
||||||
|
Some(out_sym_idx) => {
|
||||||
|
reloc.target_symbol = out_sym_idx;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Extern
|
||||||
|
let out_sym_idx = out_obj.symbols.len();
|
||||||
|
let target_sym = &obj.symbols[reloc.target_symbol];
|
||||||
|
symbol_idxs[reloc.target_symbol] = Some(out_sym_idx);
|
||||||
|
out_obj.symbols.push(ObjSymbol {
|
||||||
|
name: target_sym.name.clone(),
|
||||||
|
demangled_name: target_sym.demangled_name.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
reloc.target_symbol = out_sym_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip linker generated symbols
|
||||||
|
for obj in &mut objects {
|
||||||
|
for symbol in &mut obj.symbols {
|
||||||
|
if is_skip_symbol(&symbol.name) {
|
||||||
|
if symbol.section.is_some() {
|
||||||
|
log::debug!("Externing {:?} in {}", symbol, obj.name);
|
||||||
|
*symbol = ObjSymbol {
|
||||||
|
name: symbol.name.clone(),
|
||||||
|
demangled_name: symbol.demangled_name.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if is_linker_symbol(&symbol.name) {
|
||||||
|
if let Some(section_idx) = symbol.section {
|
||||||
|
log::debug!("Skipping {:?} in {}", symbol, obj.name);
|
||||||
|
let section = &mut obj.sections[section_idx];
|
||||||
|
// TODO assuming end of file
|
||||||
|
section.size -= symbol.size;
|
||||||
|
section.data.truncate(section.data.len() - symbol.size as usize);
|
||||||
|
*symbol = ObjSymbol {
|
||||||
|
name: symbol.name.clone(),
|
||||||
|
demangled_name: symbol.demangled_name.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mwld doesn't preserve the original section alignment values
|
||||||
|
fn default_section_align(section: &ObjSection) -> u64 {
|
||||||
|
match section.kind {
|
||||||
|
ObjSectionKind::Code => 4,
|
||||||
|
_ => match section.name.as_str() {
|
||||||
|
".ctors" | ".dtors" | "extab" | "extabindex" => 4,
|
||||||
|
_ => 8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linker-generated symbols to extern
|
||||||
|
#[inline]
|
||||||
|
fn is_skip_symbol(name: &str) -> bool { matches!(name, "_ctors" | "_dtors") }
|
||||||
|
|
||||||
|
/// Linker generated symbols to strip entirely
|
||||||
|
#[inline]
|
||||||
|
fn is_linker_symbol(name: &str) -> bool {
|
||||||
|
matches!(name, "_eti_init_info" | "_rom_copy_info" | "_bss_init_info")
|
||||||
|
}
|
Loading…
Reference in New Issue