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]]
|
[[package]]
|
||||||
name = "decomp-toolkit"
|
name = "decomp-toolkit"
|
||||||
version = "0.6.3"
|
version = "0.6.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ar",
|
"ar",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")?;
|
||||||
|
|
258
src/util/map.rs
258
src/util/map.rs
|
@ -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(),
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
Loading…
Reference in New Issue