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:
parent
5c22c8850e
commit
0cfc5df20b
|
@ -295,7 +295,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "decomp-toolkit"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ar",
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "decomp-toolkit"
|
|||
description = "Yet another GameCube/Wii decompilation toolkit."
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
repository = "https://github.com/encounter/decomp-toolkit"
|
||||
|
|
|
@ -739,7 +739,7 @@ fn load_analyze_dol(config: &ProjectConfig) -> Result<AnalyzeResult> {
|
|||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -963,7 +963,7 @@ fn load_analyze_rel(config: &ProjectConfig, module_config: &ModuleConfig) -> Res
|
|||
|
||||
let mut dep = vec![module_config.object.clone()];
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -1451,11 +1451,10 @@ fn diff(args: DiffArgs) -> Result<()> {
|
|||
log::info!("Loading {}", args.elf_file.display());
|
||||
let linked_obj = process_elf(&args.elf_file)?;
|
||||
|
||||
for orig_sym in obj
|
||||
.symbols
|
||||
.iter()
|
||||
.filter(|s| !matches!(s.kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section))
|
||||
{
|
||||
let common_bss = obj.sections.common_bss_start();
|
||||
for orig_sym in obj.symbols.iter().filter(|s| {
|
||||
!matches!(s.kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section) && !s.flags.is_stripped()
|
||||
}) {
|
||||
let Some(orig_section_index) = orig_sym.section else { continue };
|
||||
let orig_section = &obj.sections[orig_section_index];
|
||||
let (linked_section_index, linked_section) =
|
||||
|
@ -1474,7 +1473,12 @@ fn diff(args: DiffArgs) -> Result<()> {
|
|||
let mut found = false;
|
||||
if let Some((_, linked_sym)) = linked_sym {
|
||||
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!(
|
||||
"Expected {} (type {:?}) to have size {:#X}, but found {:#X}",
|
||||
orig_sym.name,
|
||||
|
|
|
@ -57,7 +57,7 @@ pub fn run(args: Args) -> Result<()> {
|
|||
|
||||
fn entries(args: EntriesArgs) -> Result<()> {
|
||||
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) {
|
||||
Some(vec) => {
|
||||
println!("Entries for {}:", args.unit);
|
||||
|
@ -89,7 +89,7 @@ fn entries(args: EntriesArgs) -> Result<()> {
|
|||
fn symbol(args: SymbolArgs) -> Result<()> {
|
||||
let file = map_file(&args.map_file)?;
|
||||
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!");
|
||||
let mut opt_ref: Option<(String, SymbolEntry)> = None;
|
||||
|
||||
|
|
|
@ -125,6 +125,13 @@ impl ObjSections {
|
|||
self.iter()
|
||||
.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 {
|
||||
|
|
|
@ -26,9 +26,9 @@ pub enum ObjSymbolScope {
|
|||
}
|
||||
|
||||
flags! {
|
||||
#[repr(u8)]
|
||||
#[repr(u32)]
|
||||
#[derive(Deserialize_repr, Serialize_repr)]
|
||||
pub enum ObjSymbolFlags: u8 {
|
||||
pub enum ObjSymbolFlags: u32 {
|
||||
Global,
|
||||
Local,
|
||||
Weak,
|
||||
|
@ -39,6 +39,9 @@ flags! {
|
|||
RelocationIgnore,
|
||||
/// Symbol won't be written to symbols file
|
||||
NoWrite,
|
||||
/// Symbol was stripped from the original object,
|
||||
/// but is still useful for common BSS matching.
|
||||
Stripped,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +86,9 @@ impl ObjSymbolFlagSet {
|
|||
#[inline]
|
||||
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]
|
||||
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
|
||||
match scope {
|
||||
|
@ -119,7 +125,8 @@ impl ObjSymbolFlagSet {
|
|||
self.0
|
||||
& (ObjSymbolFlags::ForceActive
|
||||
| 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> {
|
||||
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)| {
|
||||
symbol.kind == in_symbol.kind ||
|
||||
// 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 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}",
|
||||
existing.name,
|
||||
existing.size,
|
||||
|
@ -336,6 +347,8 @@ impl ObjSymbols {
|
|||
.into_iter()
|
||||
.flatten()
|
||||
.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(
|
||||
|
@ -513,7 +526,7 @@ impl Index<SymbolIndex> for ObjSymbols {
|
|||
impl ObjSymbol {
|
||||
/// Whether this symbol can be referenced by the given relocation kind.
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -281,9 +281,10 @@ impl CommentSym {
|
|||
vis_flags |= 0xD;
|
||||
}
|
||||
let mut active_flags = 0;
|
||||
if symbol.flags.is_force_active()
|
||||
|| (force_active
|
||||
&& matches!(symbol.kind, ObjSymbolKind::Function | ObjSymbolKind::Object))
|
||||
if !symbol.flags.is_stripped()
|
||||
&& (symbol.flags.is_force_active()
|
||||
|| (force_active
|
||||
&& matches!(symbol.kind, ObjSymbolKind::Function | ObjSymbolKind::Object)))
|
||||
{
|
||||
active_flags |= 0x8;
|
||||
}
|
||||
|
|
|
@ -130,6 +130,9 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result<Option<ObjSymb
|
|||
"force_active" => {
|
||||
symbol.flags.0 |= ObjSymbolFlags::ForceActive;
|
||||
}
|
||||
"stripped" => {
|
||||
symbol.flags.0 |= ObjSymbolFlags::Stripped;
|
||||
}
|
||||
"noreloc" => {
|
||||
ensure!(
|
||||
symbol.size != 0,
|
||||
|
@ -270,6 +273,9 @@ where W: Write + ?Sized {
|
|||
// if symbol.flags.is_force_active() {
|
||||
// write!(w, " force_active")?;
|
||||
// }
|
||||
if symbol.flags.is_stripped() {
|
||||
write!(w, " stripped")?;
|
||||
}
|
||||
if let Some(section) = symbol.section {
|
||||
if obj.blocked_ranges.contains_key(&SectionAddress::new(section, symbol.address as u32)) {
|
||||
write!(w, " noreloc")?;
|
||||
|
|
258
src/util/map.rs
258
src/util/map.rs
|
@ -1,22 +1,26 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_mut)]
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
hash::Hash,
|
||||
io::BufRead,
|
||||
mem::replace,
|
||||
mem::{replace, take},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Error, Result};
|
||||
use cwdemangle::{demangle, DemangleOptions};
|
||||
use flagset::FlagSet;
|
||||
use itertools::Itertools;
|
||||
use multimap::MultiMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::{Captures, Regex};
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
|
@ -46,6 +50,7 @@ pub struct SymbolEntry {
|
|||
pub address: u32,
|
||||
pub size: u32,
|
||||
pub align: Option<u32>,
|
||||
pub unused: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
|
@ -124,6 +129,9 @@ pub struct MapInfo {
|
|||
pub link_map_symbols: HashMap<SymbolRef, SymbolEntry>,
|
||||
pub section_symbols: HashMap<String, BTreeMap<u32, Vec<SymbolEntry>>>,
|
||||
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 {
|
||||
|
@ -146,10 +154,10 @@ struct LinkMapState {
|
|||
#[derive(Default)]
|
||||
struct SectionLayoutState {
|
||||
current_section: String,
|
||||
current_unit: Option<String>,
|
||||
units: Vec<(u32, String)>,
|
||||
symbols: BTreeMap<u32, Vec<SymbolEntry>>,
|
||||
has_link_map: bool,
|
||||
last_address: u32,
|
||||
}
|
||||
|
||||
enum ProcessMapState {
|
||||
|
@ -379,6 +387,7 @@ impl StateMachine {
|
|||
address: 0,
|
||||
size: 0,
|
||||
align: None,
|
||||
unused: false,
|
||||
});
|
||||
if !is_duplicate {
|
||||
state.last_symbol = Some(symbol_ref.clone());
|
||||
|
@ -405,59 +414,137 @@ impl StateMachine {
|
|||
address: 0,
|
||||
size: 0,
|
||||
align: None,
|
||||
unused: false,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end_section_layout(mut state: SectionLayoutState, entries: &mut MapInfo) -> Result<()> {
|
||||
// Resolve duplicate TUs
|
||||
// let mut existing = HashSet::new();
|
||||
// for idx in 0..state.units.len() {
|
||||
// let (addr, unit) = &state.units[idx];
|
||||
// // FIXME
|
||||
// if
|
||||
// /*state.current_section == ".bss" ||*/
|
||||
// existing.contains(unit) {
|
||||
// if
|
||||
// /*state.current_section == ".bss" ||*/
|
||||
// &state.units[idx - 1].1 != unit {
|
||||
// let new_name = format!("{unit}_{}_{:010X}", state.current_section, addr);
|
||||
// log::info!("Renaming {unit} to {new_name}");
|
||||
// for idx2 in 0..idx {
|
||||
// let (addr, n_unit) = &state.units[idx2];
|
||||
// if unit == n_unit {
|
||||
// let new_name =
|
||||
// format!("{n_unit}_{}_{:010X}", state.current_section, addr);
|
||||
// log::info!("Renaming 2 {n_unit} to {new_name}");
|
||||
// state.units[idx2].1 = new_name;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// state.units[idx].1 = new_name;
|
||||
// }
|
||||
// } else {
|
||||
// existing.insert(unit.clone());
|
||||
// }
|
||||
// }
|
||||
// Check for duplicate TUs and common BSS
|
||||
let mut existing = HashSet::new();
|
||||
for (addr, unit) in state.units.iter().dedup_by(|(_, a), (_, b)| a == b) {
|
||||
if existing.contains(unit) {
|
||||
if state.current_section == ".bss" {
|
||||
if entries.common_bss_start.is_none() {
|
||||
log::warn!("Assuming common BSS start @ {:#010X} ({})", addr, unit);
|
||||
log::warn!("Please verify and set common_start in config.yml");
|
||||
entries.common_bss_start = Some(*addr);
|
||||
}
|
||||
} else {
|
||||
log::error!(
|
||||
"Duplicate TU in {}: {} @ {:#010X}",
|
||||
state.current_section,
|
||||
unit,
|
||||
addr
|
||||
);
|
||||
log::error!("Please rename the TUs manually to avoid conflicts");
|
||||
}
|
||||
} else {
|
||||
existing.insert(unit.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Perform common BSS inflation correction
|
||||
// https://github.com/encounter/dtk-template/blob/main/docs/common_bss.md#inflation-bug
|
||||
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() {
|
||||
// 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);
|
||||
}
|
||||
if !state.units.is_empty() {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -466,10 +553,6 @@ impl StateMachine {
|
|||
state: &mut SectionLayoutState,
|
||||
result: &MapInfo,
|
||||
) -> Result<()> {
|
||||
if captures["rom_addr"].trim() == "UNUSED" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let sym_name = captures["sym"].trim();
|
||||
if sym_name == "*fill*" {
|
||||
return Ok(());
|
||||
|
@ -479,14 +562,27 @@ impl StateMachine {
|
|||
if tu == "*fill*" || tu == "Linker Generated Symbol File" {
|
||||
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 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 {
|
||||
state.current_unit = Some(tu.clone());
|
||||
state.units.push((address, tu.clone()));
|
||||
if is_new_tu || sym_name == state.current_section {
|
||||
if !unused {
|
||||
state.units.push((address, tu.clone()));
|
||||
}
|
||||
if sym_name == state.current_section {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -503,9 +599,10 @@ impl StateMachine {
|
|||
address,
|
||||
size,
|
||||
align,
|
||||
unused,
|
||||
}
|
||||
} else {
|
||||
let mut visibility = if state.has_link_map {
|
||||
let mut visibility = if state.has_link_map && !unused {
|
||||
log::warn!(
|
||||
"Symbol not in link map: {} ({}). Type and visibility unknown.",
|
||||
sym_name,
|
||||
|
@ -535,6 +632,7 @@ impl StateMachine {
|
|||
address,
|
||||
size,
|
||||
align,
|
||||
unused,
|
||||
}
|
||||
};
|
||||
state.symbols.nested_push(address, entry);
|
||||
|
@ -581,18 +679,24 @@ impl StateMachine {
|
|||
address,
|
||||
size: 0,
|
||||
align: None,
|
||||
unused: false,
|
||||
});
|
||||
};
|
||||
// log::info!("Linker generated symbol: {} @ {:#010X}", name, address);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_map<R>(reader: &mut R) -> Result<MapInfo>
|
||||
where R: BufRead + ?Sized {
|
||||
pub fn process_map<R>(
|
||||
reader: &mut R,
|
||||
common_bss_start: Option<u32>,
|
||||
mw_comment_version: Option<u8>,
|
||||
) -> Result<MapInfo>
|
||||
where
|
||||
R: BufRead + ?Sized,
|
||||
{
|
||||
let mut sm = StateMachine {
|
||||
state: ProcessMapState::None,
|
||||
result: Default::default(),
|
||||
result: MapInfo { common_bss_start, mw_comment_version, ..Default::default() },
|
||||
has_link_map: false,
|
||||
};
|
||||
for result in reader.lines() {
|
||||
|
@ -607,10 +711,17 @@ where R: BufRead + ?Sized {
|
|||
Ok(sm.result)
|
||||
}
|
||||
|
||||
pub fn apply_map_file<P>(path: P, obj: &mut ObjInfo) -> Result<()>
|
||||
where P: AsRef<Path> {
|
||||
pub fn apply_map_file<P>(
|
||||
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 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)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// Add section symbols
|
||||
for (section_name, symbol_map) in &result.section_symbols {
|
||||
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 absolute symbols
|
||||
// TODO
|
||||
// for symbol_entry in result.link_map_symbols.values().filter(|s| s.unit.is_none()) {
|
||||
// add_symbol(obj, symbol_entry, None)?;
|
||||
// }
|
||||
|
||||
// Add splits
|
||||
for (section_name, unit_order) in &result.section_units {
|
||||
let (_, section) = obj
|
||||
|
@ -672,11 +786,24 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
|
|||
.peek()
|
||||
.map(|(addr, _)| *addr)
|
||||
.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 {
|
||||
unit: unit.replace(' ', "/"),
|
||||
unit,
|
||||
end: next,
|
||||
align: None,
|
||||
common: false,
|
||||
common,
|
||||
autogenerated: false,
|
||||
skip: false,
|
||||
rename: None,
|
||||
|
@ -698,6 +825,9 @@ fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usi
|
|||
if symbol_entry.name.starts_with("..") {
|
||||
flags |= ObjSymbolFlags::ForceActive;
|
||||
}
|
||||
if symbol_entry.unused {
|
||||
flags |= ObjSymbolFlags::Stripped;
|
||||
}
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: symbol_entry.name.clone(),
|
||||
|
|
|
@ -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.
|
||||
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 {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(common_bss_start) = common_start.or_else(|| {
|
||||
bss_section.splits.iter().find(|(_, split)| split.common).map(|(addr, _)| addr)
|
||||
let Some((bss_section_index, common_bss_start)) = (match common_start {
|
||||
Some(addr) => Some((
|
||||
obj.sections.by_name(".bss")?.ok_or_else(|| anyhow!("Failed to find .bss section"))?.0,
|
||||
addr,
|
||||
)),
|
||||
None => obj.sections.common_bss_start(),
|
||||
}) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
@ -484,7 +485,7 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
|
|||
if let Some((_, symbol)) = obj
|
||||
.symbols
|
||||
.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()
|
||||
{
|
||||
ensure!(
|
||||
|
@ -503,7 +504,7 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
|
|||
if let Some((_, symbol)) = obj
|
||||
.symbols
|
||||
.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()
|
||||
{
|
||||
ensure!(
|
||||
|
@ -573,6 +574,7 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> {
|
|||
}
|
||||
|
||||
// Add padding symbols for gaps between symbols
|
||||
let common_bss = obj.sections.common_bss_start();
|
||||
for (section_index, section) in obj.sections.iter() {
|
||||
if section.name == ".ctors" || section.name == ".dtors" {
|
||||
continue;
|
||||
|
@ -585,6 +587,12 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> {
|
|||
.filter(|(_, s)| s.size_known && s.size > 0)
|
||||
.peekable();
|
||||
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 =
|
||||
align_up((symbol.address + symbol.size) as u32, next_symbol.align.unwrap_or(1));
|
||||
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
|
||||
.symbols
|
||||
.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()
|
||||
{
|
||||
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_known: symbol.size_known,
|
||||
flags: if split.common {
|
||||
ObjSymbolFlagSet(ObjSymbolFlags::Common.into())
|
||||
ObjSymbolFlagSet(symbol.flags.keep_flags() | ObjSymbolFlags::Common)
|
||||
} else {
|
||||
symbol.flags
|
||||
},
|
||||
|
@ -1303,7 +1311,12 @@ pub fn end_for_section(obj: &ObjInfo, section_index: usize) -> Result<SectionAdd
|
|||
let last_symbol = obj
|
||||
.symbols
|
||||
.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();
|
||||
match last_symbol {
|
||||
Some((_, symbol)) if is_linker_generated_object(&symbol.name) => {
|
||||
|
|
Loading…
Reference in New Issue