Overhauled common BSS support & more

- With a map, attempts to detect and handle common BSS automatically
- With a map, attempts to detect and correct inflated common BSS bug (< GC 2.7 linker)
- Support for "stripped" symbols, sometimes required to match inflated common BSS sizes
- Warns on duplicated TUs in a map (other than common BSS)
- Automatically adds `comment:0` to `.s` TUs from a map (avoids linker crash)
This commit is contained in:
Luke Street 2023-11-29 18:14:17 -05:00
parent 5c22c8850e
commit 0cfc5df20b
10 changed files with 269 additions and 95 deletions

2
Cargo.lock generated
View File

@ -295,7 +295,7 @@ dependencies = [
[[package]] [[package]]
name = "decomp-toolkit" name = "decomp-toolkit"
version = "0.6.3" version = "0.6.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ar", "ar",

View File

@ -3,7 +3,7 @@ name = "decomp-toolkit"
description = "Yet another GameCube/Wii decompilation toolkit." description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.6.3" version = "0.6.4"
edition = "2021" edition = "2021"
publish = false publish = false
repository = "https://github.com/encounter/decomp-toolkit" repository = "https://github.com/encounter/decomp-toolkit"

View File

@ -739,7 +739,7 @@ fn load_analyze_dol(config: &ProjectConfig) -> Result<AnalyzeResult> {
} }
if let Some(map_path) = &config.base.map { if let Some(map_path) = &config.base.map {
apply_map_file(map_path, &mut obj)?; apply_map_file(map_path, &mut obj, config.common_start, config.mw_comment_version)?;
dep.push(map_path.clone()); dep.push(map_path.clone());
} }
@ -963,7 +963,7 @@ fn load_analyze_rel(config: &ProjectConfig, module_config: &ModuleConfig) -> Res
let mut dep = vec![module_config.object.clone()]; let mut dep = vec![module_config.object.clone()];
if let Some(map_path) = &module_config.map { if let Some(map_path) = &module_config.map {
apply_map_file(map_path, &mut module_obj)?; apply_map_file(map_path, &mut module_obj, None, None)?;
dep.push(map_path.clone()); dep.push(map_path.clone());
} }
@ -1451,11 +1451,10 @@ fn diff(args: DiffArgs) -> Result<()> {
log::info!("Loading {}", args.elf_file.display()); log::info!("Loading {}", args.elf_file.display());
let linked_obj = process_elf(&args.elf_file)?; let linked_obj = process_elf(&args.elf_file)?;
for orig_sym in obj let common_bss = obj.sections.common_bss_start();
.symbols for orig_sym in obj.symbols.iter().filter(|s| {
.iter() !matches!(s.kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section) && !s.flags.is_stripped()
.filter(|s| !matches!(s.kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section)) }) {
{
let Some(orig_section_index) = orig_sym.section else { continue }; let Some(orig_section_index) = orig_sym.section else { continue };
let orig_section = &obj.sections[orig_section_index]; let orig_section = &obj.sections[orig_section_index];
let (linked_section_index, linked_section) = let (linked_section_index, linked_section) =
@ -1474,7 +1473,12 @@ fn diff(args: DiffArgs) -> Result<()> {
let mut found = false; let mut found = false;
if let Some((_, linked_sym)) = linked_sym { if let Some((_, linked_sym)) = linked_sym {
if linked_sym.name.starts_with(&orig_sym.name) { if linked_sym.name.starts_with(&orig_sym.name) {
if linked_sym.size != orig_sym.size { if linked_sym.size != orig_sym.size &&
// TODO validate common symbol sizes
// (need to account for inflation bug)
matches!(common_bss, Some((idx, addr)) if
orig_section_index == idx && orig_sym.address as u32 >= addr)
{
log::error!( log::error!(
"Expected {} (type {:?}) to have size {:#X}, but found {:#X}", "Expected {} (type {:?}) to have size {:#X}, but found {:#X}",
orig_sym.name, orig_sym.name,

View File

@ -57,7 +57,7 @@ pub fn run(args: Args) -> Result<()> {
fn entries(args: EntriesArgs) -> Result<()> { fn entries(args: EntriesArgs) -> Result<()> {
let file = map_file(&args.map_file)?; let file = map_file(&args.map_file)?;
let entries = process_map(&mut file.as_reader())?; let entries = process_map(&mut file.as_reader(), None, None)?;
match entries.unit_entries.get_vec(&args.unit) { match entries.unit_entries.get_vec(&args.unit) {
Some(vec) => { Some(vec) => {
println!("Entries for {}:", args.unit); println!("Entries for {}:", args.unit);
@ -89,7 +89,7 @@ fn entries(args: EntriesArgs) -> Result<()> {
fn symbol(args: SymbolArgs) -> Result<()> { fn symbol(args: SymbolArgs) -> Result<()> {
let file = map_file(&args.map_file)?; let file = map_file(&args.map_file)?;
log::info!("Processing map..."); log::info!("Processing map...");
let entries = process_map(&mut file.as_reader())?; let entries = process_map(&mut file.as_reader(), None, None)?;
log::info!("Done!"); log::info!("Done!");
let mut opt_ref: Option<(String, SymbolEntry)> = None; let mut opt_ref: Option<(String, SymbolEntry)> = None;

View File

@ -125,6 +125,13 @@ impl ObjSections {
self.iter() self.iter()
.flat_map(|(idx, s)| s.splits.iter().map(move |(addr, split)| (idx, s, addr, split))) .flat_map(|(idx, s)| s.splits.iter().map(move |(addr, split)| (idx, s, addr, split)))
} }
pub fn common_bss_start(&self) -> Option<(usize, u32)> {
let Ok(Some((section_index, section))) = self.by_name(".bss") else {
return None;
};
section.splits.iter().find(|(_, split)| split.common).map(|(addr, _)| (section_index, addr))
}
} }
impl Index<usize> for ObjSections { impl Index<usize> for ObjSections {

View File

@ -26,9 +26,9 @@ pub enum ObjSymbolScope {
} }
flags! { flags! {
#[repr(u8)] #[repr(u32)]
#[derive(Deserialize_repr, Serialize_repr)] #[derive(Deserialize_repr, Serialize_repr)]
pub enum ObjSymbolFlags: u8 { pub enum ObjSymbolFlags: u32 {
Global, Global,
Local, Local,
Weak, Weak,
@ -39,6 +39,9 @@ flags! {
RelocationIgnore, RelocationIgnore,
/// Symbol won't be written to symbols file /// Symbol won't be written to symbols file
NoWrite, NoWrite,
/// Symbol was stripped from the original object,
/// but is still useful for common BSS matching.
Stripped,
} }
} }
@ -83,6 +86,9 @@ impl ObjSymbolFlagSet {
#[inline] #[inline]
pub fn is_no_write(&self) -> bool { self.0.contains(ObjSymbolFlags::NoWrite) } pub fn is_no_write(&self) -> bool { self.0.contains(ObjSymbolFlags::NoWrite) }
#[inline]
pub fn is_stripped(&self) -> bool { self.0.contains(ObjSymbolFlags::Stripped) }
#[inline] #[inline]
pub fn set_scope(&mut self, scope: ObjSymbolScope) { pub fn set_scope(&mut self, scope: ObjSymbolScope) {
match scope { match scope {
@ -119,7 +125,8 @@ impl ObjSymbolFlagSet {
self.0 self.0
& (ObjSymbolFlags::ForceActive & (ObjSymbolFlags::ForceActive
| ObjSymbolFlags::NoWrite | ObjSymbolFlags::NoWrite
| ObjSymbolFlags::RelocationIgnore) | ObjSymbolFlags::RelocationIgnore
| ObjSymbolFlags::Stripped)
} }
} }
@ -212,7 +219,10 @@ impl ObjSymbols {
} }
pub fn add(&mut self, in_symbol: ObjSymbol, replace: bool) -> Result<SymbolIndex> { pub fn add(&mut self, in_symbol: ObjSymbol, replace: bool) -> Result<SymbolIndex> {
let opt = if let Some(section_index) = in_symbol.section { let opt = if in_symbol.flags.is_stripped() {
// Stripped symbols don't overwrite existing symbols
None
} else if let Some(section_index) = in_symbol.section {
self.at_section_address(section_index, in_symbol.address as u32).find(|(_, symbol)| { self.at_section_address(section_index, in_symbol.address as u32).find(|(_, symbol)| {
symbol.kind == in_symbol.kind || symbol.kind == in_symbol.kind ||
// Replace auto symbols with real symbols // Replace auto symbols with real symbols
@ -228,7 +238,8 @@ impl ObjSymbols {
let replace = replace || (is_auto_symbol(existing) && !is_auto_symbol(&in_symbol)); let replace = replace || (is_auto_symbol(existing) && !is_auto_symbol(&in_symbol));
let size = let size =
if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size { if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size {
log::warn!( // TODO fix this and restore to warning
log::debug!(
"Conflicting size for {}: was {:#X}, now {:#X}", "Conflicting size for {}: was {:#X}, now {:#X}",
existing.name, existing.name,
existing.size, existing.size,
@ -336,6 +347,8 @@ impl ObjSymbols {
.into_iter() .into_iter()
.flatten() .flatten()
.map(move |&idx| (idx, &self.symbols[idx])) .map(move |&idx| (idx, &self.symbols[idx]))
// "Stripped" symbols don't actually exist at the address
.filter(|(_, sym)| !sym.flags.is_stripped())
} }
pub fn kind_at_section_address( pub fn kind_at_section_address(
@ -513,7 +526,7 @@ impl Index<SymbolIndex> for ObjSymbols {
impl ObjSymbol { impl ObjSymbol {
/// Whether this symbol can be referenced by the given relocation kind. /// Whether this symbol can be referenced by the given relocation kind.
pub fn referenced_by(&self, reloc_kind: ObjRelocKind) -> bool { pub fn referenced_by(&self, reloc_kind: ObjRelocKind) -> bool {
if self.flags.is_relocation_ignore() { if self.flags.is_relocation_ignore() || self.flags.is_stripped() {
return false; return false;
} }

View File

@ -281,9 +281,10 @@ impl CommentSym {
vis_flags |= 0xD; vis_flags |= 0xD;
} }
let mut active_flags = 0; let mut active_flags = 0;
if symbol.flags.is_force_active() if !symbol.flags.is_stripped()
|| (force_active && (symbol.flags.is_force_active()
&& matches!(symbol.kind, ObjSymbolKind::Function | ObjSymbolKind::Object)) || (force_active
&& matches!(symbol.kind, ObjSymbolKind::Function | ObjSymbolKind::Object)))
{ {
active_flags |= 0x8; active_flags |= 0x8;
} }

View File

@ -130,6 +130,9 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result<Option<ObjSymb
"force_active" => { "force_active" => {
symbol.flags.0 |= ObjSymbolFlags::ForceActive; symbol.flags.0 |= ObjSymbolFlags::ForceActive;
} }
"stripped" => {
symbol.flags.0 |= ObjSymbolFlags::Stripped;
}
"noreloc" => { "noreloc" => {
ensure!( ensure!(
symbol.size != 0, symbol.size != 0,
@ -270,6 +273,9 @@ where W: Write + ?Sized {
// if symbol.flags.is_force_active() { // if symbol.flags.is_force_active() {
// write!(w, " force_active")?; // write!(w, " force_active")?;
// } // }
if symbol.flags.is_stripped() {
write!(w, " stripped")?;
}
if let Some(section) = symbol.section { if let Some(section) = symbol.section {
if obj.blocked_ranges.contains_key(&SectionAddress::new(section, symbol.address as u32)) { if obj.blocked_ranges.contains_key(&SectionAddress::new(section, symbol.address as u32)) {
write!(w, " noreloc")?; write!(w, " noreloc")?;

View File

@ -1,22 +1,26 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_mut)] #![allow(unused_mut)]
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap, HashSet},
hash::Hash, hash::Hash,
io::BufRead, io::BufRead,
mem::replace, mem::{replace, take},
path::Path, path::Path,
}; };
use anyhow::{anyhow, bail, Error, Result}; use anyhow::{anyhow, bail, Error, Result};
use cwdemangle::{demangle, DemangleOptions}; use cwdemangle::{demangle, DemangleOptions};
use flagset::FlagSet; use flagset::FlagSet;
use itertools::Itertools;
use multimap::MultiMap; use multimap::MultiMap;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use crate::{ use crate::{
obj::{ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind}, obj::{
ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
ObjUnit,
},
util::{file::map_file, nested::NestedVec}, util::{file::map_file, nested::NestedVec},
}; };
@ -46,6 +50,7 @@ pub struct SymbolEntry {
pub address: u32, pub address: u32,
pub size: u32, pub size: u32,
pub align: Option<u32>, pub align: Option<u32>,
pub unused: bool,
} }
#[derive(Debug, Clone, Hash, Eq, PartialEq)] #[derive(Debug, Clone, Hash, Eq, PartialEq)]
@ -124,6 +129,9 @@ pub struct MapInfo {
pub link_map_symbols: HashMap<SymbolRef, SymbolEntry>, pub link_map_symbols: HashMap<SymbolRef, SymbolEntry>,
pub section_symbols: HashMap<String, BTreeMap<u32, Vec<SymbolEntry>>>, pub section_symbols: HashMap<String, BTreeMap<u32, Vec<SymbolEntry>>>,
pub section_units: HashMap<String, Vec<(u32, String)>>, pub section_units: HashMap<String, Vec<(u32, String)>>,
// For common BSS inflation correction
pub common_bss_start: Option<u32>,
pub mw_comment_version: Option<u8>,
} }
impl MapInfo { impl MapInfo {
@ -146,10 +154,10 @@ struct LinkMapState {
#[derive(Default)] #[derive(Default)]
struct SectionLayoutState { struct SectionLayoutState {
current_section: String, current_section: String,
current_unit: Option<String>,
units: Vec<(u32, String)>, units: Vec<(u32, String)>,
symbols: BTreeMap<u32, Vec<SymbolEntry>>, symbols: BTreeMap<u32, Vec<SymbolEntry>>,
has_link_map: bool, has_link_map: bool,
last_address: u32,
} }
enum ProcessMapState { enum ProcessMapState {
@ -379,6 +387,7 @@ impl StateMachine {
address: 0, address: 0,
size: 0, size: 0,
align: None, align: None,
unused: false,
}); });
if !is_duplicate { if !is_duplicate {
state.last_symbol = Some(symbol_ref.clone()); state.last_symbol = Some(symbol_ref.clone());
@ -405,59 +414,137 @@ impl StateMachine {
address: 0, address: 0,
size: 0, size: 0,
align: None, align: None,
unused: false,
}); });
Ok(()) Ok(())
} }
fn end_section_layout(mut state: SectionLayoutState, entries: &mut MapInfo) -> Result<()> { fn end_section_layout(mut state: SectionLayoutState, entries: &mut MapInfo) -> Result<()> {
// Resolve duplicate TUs // Check for duplicate TUs and common BSS
// let mut existing = HashSet::new(); let mut existing = HashSet::new();
// for idx in 0..state.units.len() { for (addr, unit) in state.units.iter().dedup_by(|(_, a), (_, b)| a == b) {
// let (addr, unit) = &state.units[idx]; if existing.contains(unit) {
// // FIXME if state.current_section == ".bss" {
// if if entries.common_bss_start.is_none() {
// /*state.current_section == ".bss" ||*/ log::warn!("Assuming common BSS start @ {:#010X} ({})", addr, unit);
// existing.contains(unit) { log::warn!("Please verify and set common_start in config.yml");
// if entries.common_bss_start = Some(*addr);
// /*state.current_section == ".bss" ||*/ }
// &state.units[idx - 1].1 != unit { } else {
// let new_name = format!("{unit}_{}_{:010X}", state.current_section, addr); log::error!(
// log::info!("Renaming {unit} to {new_name}"); "Duplicate TU in {}: {} @ {:#010X}",
// for idx2 in 0..idx { state.current_section,
// let (addr, n_unit) = &state.units[idx2]; unit,
// if unit == n_unit { addr
// let new_name = );
// format!("{n_unit}_{}_{:010X}", state.current_section, addr); log::error!("Please rename the TUs manually to avoid conflicts");
// log::info!("Renaming 2 {n_unit} to {new_name}"); }
// state.units[idx2].1 = new_name; } else {
// break; existing.insert(unit.clone());
// } }
// } }
// state.units[idx].1 = new_name;
// } // Perform common BSS inflation correction
// } else { // https://github.com/encounter/dtk-template/blob/main/docs/common_bss.md#inflation-bug
// existing.insert(unit.clone()); let check_common_bss_inflation = state.current_section == ".bss"
// } && entries.common_bss_start.is_some()
// } && matches!(entries.mw_comment_version, Some(n) if n < 11);
if check_common_bss_inflation {
log::info!("Checking for common BSS inflation...");
let common_bss_start = entries.common_bss_start.unwrap();
// Correct address for unused common BSS symbols that are first in a TU
let mut symbols_iter = state.symbols.iter_mut().peekable();
let mut last_unit = None;
let mut add_to_next = vec![];
while let Some((_, symbols)) = symbols_iter.next() {
let next_addr = if let Some((&next_addr, _)) = symbols_iter.peek() {
next_addr
} else {
u32::MAX
};
let mut to_add = take(&mut add_to_next);
symbols.retain(|e| {
if e.address >= common_bss_start && e.unused && e.unit != last_unit {
log::debug!(
"Updating address for {} @ {:#010X} to {:#010X}",
e.name,
e.address,
next_addr
);
let mut e = e.clone();
e.address = next_addr;
add_to_next.push(e);
return false;
}
if !e.unused {
last_unit = e.unit.clone();
}
true
});
to_add.extend(take(symbols));
*symbols = to_add;
}
// Correct size for common BSS symbols that are first in a TU (inflated)
let mut unit_iter = state
.units
.iter()
.skip_while(|&&(addr, _)| addr < common_bss_start)
.dedup_by(|&(_, a), &(_, b)| a == b)
.peekable();
while let Some((start_addr, unit)) = unit_iter.next() {
let unit_symbols = if let Some(&&(end_addr, _)) = unit_iter.peek() {
state.symbols.range(*start_addr..end_addr).collect_vec()
} else {
state.symbols.range(*start_addr..).collect_vec()
};
let mut symbol_iter = unit_symbols.iter().flat_map(|(_, v)| *v);
let Some(first_symbol) = symbol_iter.next() else { continue };
let first_addr = first_symbol.address;
let mut remaining_size = symbol_iter.map(|e| e.size).sum::<u32>();
if remaining_size == 0 {
continue;
}
if first_symbol.size > remaining_size {
let new_size = first_symbol.size - remaining_size;
log::info!(
"Correcting size for {} ({}) @ {:#010X} ({:#X} -> {:#X})",
first_symbol.name,
unit,
first_addr,
first_symbol.size,
new_size
);
state.symbols.get_mut(&first_addr).unwrap().iter_mut().next().unwrap().size =
new_size;
} else {
log::warn!(
"Inflated size not detected for {} ({}) @ {:#010X} ({} <= {})",
first_symbol.name,
unit,
first_addr,
first_symbol.size,
remaining_size
);
}
}
}
if !state.symbols.is_empty() { if !state.symbols.is_empty() {
// Remove "unused" symbols
for symbols in state.symbols.values_mut() {
symbols.retain(|e| {
!e.unused ||
// Except for unused common BSS symbols needed to match the inflated size
(check_common_bss_inflation && e.address >= entries.common_bss_start.unwrap())
});
}
entries.section_symbols.insert(state.current_section.clone(), state.symbols); entries.section_symbols.insert(state.current_section.clone(), state.symbols);
} }
if !state.units.is_empty() { if !state.units.is_empty() {
entries.section_units.insert(state.current_section.clone(), state.units); entries.section_units.insert(state.current_section.clone(), state.units);
} }
// Set last section size
// if let Some(last_unit) = state.section_units.last() {
// let last_unit = state.unit_override.as_ref().unwrap_or(last_unit);
// nested_try_insert(
// &mut entries.unit_section_ranges,
// last_unit.clone(),
// state.current_section.clone(),
// state.last_unit_start..state.last_section_end,
// )
// .with_context(|| {
// format!("TU '{}' already exists in section '{}'", last_unit, state.current_section)
// })?;
// }
Ok(()) Ok(())
} }
@ -466,10 +553,6 @@ impl StateMachine {
state: &mut SectionLayoutState, state: &mut SectionLayoutState,
result: &MapInfo, result: &MapInfo,
) -> Result<()> { ) -> Result<()> {
if captures["rom_addr"].trim() == "UNUSED" {
return Ok(());
}
let sym_name = captures["sym"].trim(); let sym_name = captures["sym"].trim();
if sym_name == "*fill*" { if sym_name == "*fill*" {
return Ok(()); return Ok(());
@ -479,14 +562,27 @@ impl StateMachine {
if tu == "*fill*" || tu == "Linker Generated Symbol File" { if tu == "*fill*" || tu == "Linker Generated Symbol File" {
return Ok(()); return Ok(());
} }
let is_new_tu = match state.units.last() {
None => true,
Some((_, name)) => name != &tu,
};
let address = u32::from_str_radix(captures["addr"].trim(), 16)?; let (address, unused) = if captures["rom_addr"].trim() == "UNUSED" {
// Addresses for unused symbols that _start_ a TU
// are corrected in end_section_layout
(state.last_address, true)
} else {
let address = u32::from_str_radix(captures["addr"].trim(), 16)?;
state.last_address = address;
(address, false)
};
let size = u32::from_str_radix(captures["size"].trim(), 16)?; let size = u32::from_str_radix(captures["size"].trim(), 16)?;
let align = captures.name("align").and_then(|m| m.as_str().trim().parse::<u32>().ok()); let align = captures.name("align").and_then(|m| m.as_str().trim().parse::<u32>().ok());
if state.current_unit.as_ref() != Some(&tu) || sym_name == state.current_section { if is_new_tu || sym_name == state.current_section {
state.current_unit = Some(tu.clone()); if !unused {
state.units.push((address, tu.clone())); state.units.push((address, tu.clone()));
}
if sym_name == state.current_section { if sym_name == state.current_section {
return Ok(()); return Ok(());
} }
@ -503,9 +599,10 @@ impl StateMachine {
address, address,
size, size,
align, align,
unused,
} }
} else { } else {
let mut visibility = if state.has_link_map { let mut visibility = if state.has_link_map && !unused {
log::warn!( log::warn!(
"Symbol not in link map: {} ({}). Type and visibility unknown.", "Symbol not in link map: {} ({}). Type and visibility unknown.",
sym_name, sym_name,
@ -535,6 +632,7 @@ impl StateMachine {
address, address,
size, size,
align, align,
unused,
} }
}; };
state.symbols.nested_push(address, entry); state.symbols.nested_push(address, entry);
@ -581,18 +679,24 @@ impl StateMachine {
address, address,
size: 0, size: 0,
align: None, align: None,
unused: false,
}); });
}; };
// log::info!("Linker generated symbol: {} @ {:#010X}", name, address);
Ok(()) Ok(())
} }
} }
pub fn process_map<R>(reader: &mut R) -> Result<MapInfo> pub fn process_map<R>(
where R: BufRead + ?Sized { reader: &mut R,
common_bss_start: Option<u32>,
mw_comment_version: Option<u8>,
) -> Result<MapInfo>
where
R: BufRead + ?Sized,
{
let mut sm = StateMachine { let mut sm = StateMachine {
state: ProcessMapState::None, state: ProcessMapState::None,
result: Default::default(), result: MapInfo { common_bss_start, mw_comment_version, ..Default::default() },
has_link_map: false, has_link_map: false,
}; };
for result in reader.lines() { for result in reader.lines() {
@ -607,10 +711,17 @@ where R: BufRead + ?Sized {
Ok(sm.result) Ok(sm.result)
} }
pub fn apply_map_file<P>(path: P, obj: &mut ObjInfo) -> Result<()> pub fn apply_map_file<P>(
where P: AsRef<Path> { path: P,
obj: &mut ObjInfo,
common_bss_start: Option<u32>,
mw_comment_version: Option<u8>,
) -> Result<()>
where
P: AsRef<Path>,
{
let file = map_file(&path)?; let file = map_file(&path)?;
let info = process_map(&mut file.as_reader())?; let info = process_map(&mut file.as_reader(), common_bss_start, mw_comment_version)?;
apply_map(&info, obj) apply_map(&info, obj)
} }
@ -644,6 +755,7 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
log::warn!("Section {} @ {:#010X} not found in map", section.name, section.address); log::warn!("Section {} @ {:#010X} not found in map", section.name, section.address);
} }
} }
// Add section symbols // Add section symbols
for (section_name, symbol_map) in &result.section_symbols { for (section_name, symbol_map) in &result.section_symbols {
let (section_index, _) = obj let (section_index, _) = obj
@ -654,11 +766,13 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
add_symbol(obj, symbol_entry, Some(section_index))?; add_symbol(obj, symbol_entry, Some(section_index))?;
} }
} }
// Add absolute symbols // Add absolute symbols
// TODO // TODO
// for symbol_entry in result.link_map_symbols.values().filter(|s| s.unit.is_none()) { // for symbol_entry in result.link_map_symbols.values().filter(|s| s.unit.is_none()) {
// add_symbol(obj, symbol_entry, None)?; // add_symbol(obj, symbol_entry, None)?;
// } // }
// Add splits // Add splits
for (section_name, unit_order) in &result.section_units { for (section_name, unit_order) in &result.section_units {
let (_, section) = obj let (_, section) = obj
@ -672,11 +786,24 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
.peek() .peek()
.map(|(addr, _)| *addr) .map(|(addr, _)| *addr)
.unwrap_or_else(|| (section.address + section.size) as u32); .unwrap_or_else(|| (section.address + section.size) as u32);
let common = section_name == ".bss"
&& matches!(result.common_bss_start, Some(start) if *addr >= start);
let unit = unit.replace(' ', "/");
// Disable mw_comment_version for assembly units
if unit.ends_with(".s") && !obj.link_order.iter().any(|u| u.name == unit) {
obj.link_order.push(ObjUnit {
name: unit.clone(),
autogenerated: false,
comment_version: Some(0),
});
}
section.splits.push(*addr, ObjSplit { section.splits.push(*addr, ObjSplit {
unit: unit.replace(' ', "/"), unit,
end: next, end: next,
align: None, align: None,
common: false, common,
autogenerated: false, autogenerated: false,
skip: false, skip: false,
rename: None, rename: None,
@ -698,6 +825,9 @@ fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usi
if symbol_entry.name.starts_with("..") { if symbol_entry.name.starts_with("..") {
flags |= ObjSymbolFlags::ForceActive; flags |= ObjSymbolFlags::ForceActive;
} }
if symbol_entry.unused {
flags |= ObjSymbolFlags::Stripped;
}
obj.add_symbol( obj.add_symbol(
ObjSymbol { ObjSymbol {
name: symbol_entry.name.clone(), name: symbol_entry.name.clone(),

View File

@ -439,11 +439,12 @@ fn create_gap_splits(obj: &mut ObjInfo) -> Result<()> {
/// Ensures that all .bss splits following a common split are also marked as common. /// Ensures that all .bss splits following a common split are also marked as common.
fn update_common_splits(obj: &mut ObjInfo, common_start: Option<u32>) -> Result<()> { fn update_common_splits(obj: &mut ObjInfo, common_start: Option<u32>) -> Result<()> {
let Some((bss_section_index, bss_section)) = obj.sections.by_name(".bss")? else { let Some((bss_section_index, common_bss_start)) = (match common_start {
return Ok(()); Some(addr) => Some((
}; obj.sections.by_name(".bss")?.ok_or_else(|| anyhow!("Failed to find .bss section"))?.0,
let Some(common_bss_start) = common_start.or_else(|| { addr,
bss_section.splits.iter().find(|(_, split)| split.common).map(|(addr, _)| addr) )),
None => obj.sections.common_bss_start(),
}) else { }) else {
return Ok(()); return Ok(());
}; };
@ -484,7 +485,7 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
if let Some((_, symbol)) = obj if let Some((_, symbol)) = obj
.symbols .symbols
.for_section_range(section_index, ..addr) .for_section_range(section_index, ..addr)
.filter(|&(_, s)| s.size_known && s.size > 0) .filter(|&(_, s)| s.size_known && s.size > 0 && !s.flags.is_stripped())
.next_back() .next_back()
{ {
ensure!( ensure!(
@ -503,7 +504,7 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
if let Some((_, symbol)) = obj if let Some((_, symbol)) = obj
.symbols .symbols
.for_section_range(section_index, ..split.end) .for_section_range(section_index, ..split.end)
.filter(|&(_, s)| s.size_known && s.size > 0) .filter(|&(_, s)| s.size_known && s.size > 0 && !s.flags.is_stripped())
.next_back() .next_back()
{ {
ensure!( ensure!(
@ -573,6 +574,7 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> {
} }
// Add padding symbols for gaps between symbols // Add padding symbols for gaps between symbols
let common_bss = obj.sections.common_bss_start();
for (section_index, section) in obj.sections.iter() { for (section_index, section) in obj.sections.iter() {
if section.name == ".ctors" || section.name == ".dtors" { if section.name == ".ctors" || section.name == ".dtors" {
continue; continue;
@ -585,6 +587,12 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> {
.filter(|(_, s)| s.size_known && s.size > 0) .filter(|(_, s)| s.size_known && s.size > 0)
.peekable(); .peekable();
while let (Some((_, symbol)), Some(&(_, next_symbol))) = (iter.next(), iter.peek()) { while let (Some((_, symbol)), Some(&(_, next_symbol))) = (iter.next(), iter.peek()) {
// Common BSS is allowed to have gaps and overlaps to accurately match the common BSS inflation bug
if matches!(common_bss, Some((idx, addr)) if
section_index == idx && symbol.address as u32 >= addr)
{
continue;
}
let aligned_end = let aligned_end =
align_up((symbol.address + symbol.size) as u32, next_symbol.align.unwrap_or(1)); align_up((symbol.address + symbol.size) as u32, next_symbol.align.unwrap_or(1));
match aligned_end.cmp(&(next_symbol.address as u32)) { match aligned_end.cmp(&(next_symbol.address as u32)) {
@ -655,7 +663,7 @@ fn trim_split_alignment(obj: &mut ObjInfo) -> Result<()> {
if let Some((_, symbol)) = obj if let Some((_, symbol)) = obj
.symbols .symbols
.for_section_range(section_index, addr..split.end) .for_section_range(section_index, addr..split.end)
.filter(|&(_, s)| s.size_known && s.size > 0) .filter(|&(_, s)| s.size_known && s.size > 0 && !s.flags.is_stripped())
.next_back() .next_back()
{ {
split_end = symbol.address as u32 + symbol.size as u32; split_end = symbol.address as u32 + symbol.size as u32;
@ -1038,7 +1046,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
size: symbol.size, size: symbol.size,
size_known: symbol.size_known, size_known: symbol.size_known,
flags: if split.common { flags: if split.common {
ObjSymbolFlagSet(ObjSymbolFlags::Common.into()) ObjSymbolFlagSet(symbol.flags.keep_flags() | ObjSymbolFlags::Common)
} else { } else {
symbol.flags symbol.flags
}, },
@ -1303,7 +1311,12 @@ pub fn end_for_section(obj: &ObjInfo, section_index: usize) -> Result<SectionAdd
let last_symbol = obj let last_symbol = obj
.symbols .symbols
.for_section_range(section_index, ..section_end) .for_section_range(section_index, ..section_end)
.filter(|(_, s)| s.kind == ObjSymbolKind::Object && s.size_known && s.size > 0) .filter(|(_, s)| {
s.kind == ObjSymbolKind::Object
&& s.size_known
&& s.size > 0
&& !s.flags.is_stripped()
})
.next_back(); .next_back();
match last_symbol { match last_symbol {
Some((_, symbol)) if is_linker_generated_object(&symbol.name) => { Some((_, symbol)) if is_linker_generated_object(&symbol.name) => {