mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-15 16:16:20 +00:00
MW 2.7 LD compatibility fixes & more
- More robust .comment section handling - Auto-create .comment section for objects with common symbols (MW 2.7+ hack) - Support loading REL modules in `dol split` (currently only for references) - Add `dol diff` for quick diffing between linked ELF and expected symbols - Add `dol apply` for applying linked ELF symbols to symbol config file
This commit is contained in:
@@ -15,6 +15,15 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::util::{comment::MWComment, nested::NestedVec, rel::RelReloc};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub enum ObjSymbolScope {
|
||||
#[default]
|
||||
Unknown,
|
||||
Global,
|
||||
Weak,
|
||||
Local,
|
||||
}
|
||||
|
||||
flags! {
|
||||
#[repr(u8)]
|
||||
#[derive(Deserialize_repr, Serialize_repr)]
|
||||
@@ -25,6 +34,8 @@ flags! {
|
||||
Common,
|
||||
Hidden,
|
||||
ForceActive,
|
||||
// Same as ForceActive, but used internally
|
||||
ExternallyReferenced,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +43,19 @@ flags! {
|
||||
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
||||
|
||||
impl ObjSymbolFlagSet {
|
||||
#[inline]
|
||||
pub fn scope(&self) -> ObjSymbolScope {
|
||||
if self.is_local() {
|
||||
ObjSymbolScope::Local
|
||||
} else if self.is_weak() {
|
||||
ObjSymbolScope::Weak
|
||||
} else if self.0.contains(ObjSymbolFlags::Global) {
|
||||
ObjSymbolScope::Global
|
||||
} else {
|
||||
ObjSymbolScope::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_local(&self) -> bool { self.0.contains(ObjSymbolFlags::Local) }
|
||||
|
||||
@@ -51,9 +75,38 @@ impl ObjSymbolFlagSet {
|
||||
pub fn is_force_active(&self) -> bool { self.0.contains(ObjSymbolFlags::ForceActive) }
|
||||
|
||||
#[inline]
|
||||
pub fn set_global(&mut self) {
|
||||
self.0 =
|
||||
(self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Weak)) | ObjSymbolFlags::Global;
|
||||
pub fn is_externally_referenced(&self) -> bool {
|
||||
self.0.contains(ObjSymbolFlags::ExternallyReferenced)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
|
||||
match scope {
|
||||
ObjSymbolScope::Unknown => {
|
||||
self.0 &= !(ObjSymbolFlags::Local | ObjSymbolFlags::Global | ObjSymbolFlags::Weak)
|
||||
}
|
||||
ObjSymbolScope::Global => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Weak))
|
||||
| ObjSymbolFlags::Global
|
||||
}
|
||||
ObjSymbolScope::Weak => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Global))
|
||||
| ObjSymbolFlags::Weak
|
||||
}
|
||||
ObjSymbolScope::Local => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Global | ObjSymbolFlags::Weak))
|
||||
| ObjSymbolFlags::Local
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_externally_referenced(&mut self, value: bool) {
|
||||
if value {
|
||||
self.0 |= ObjSymbolFlags::ExternallyReferenced;
|
||||
} else {
|
||||
self.0 &= !ObjSymbolFlags::ExternallyReferenced;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,18 +192,29 @@ pub enum ObjArchitecture {
|
||||
PowerPc,
|
||||
}
|
||||
|
||||
/// Translation unit information.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ObjUnit {
|
||||
pub name: String,
|
||||
/// Generated, replaceable by user.
|
||||
pub autogenerated: bool,
|
||||
/// MW `.comment` section version.
|
||||
pub comment_version: Option<u8>,
|
||||
}
|
||||
|
||||
/// Marks a split point within a section.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ObjSplit {
|
||||
pub unit: String,
|
||||
pub end: u32,
|
||||
pub align: Option<u32>,
|
||||
/// Common BSS
|
||||
/// Whether this is a part of common BSS.
|
||||
pub common: bool,
|
||||
/// Generated, replaceable by user
|
||||
/// Generated, replaceable by user.
|
||||
pub autogenerated: bool,
|
||||
}
|
||||
|
||||
type SymbolIndex = usize;
|
||||
pub type SymbolIndex = usize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSymbols {
|
||||
@@ -167,7 +231,7 @@ pub struct ObjInfo {
|
||||
pub symbols: ObjSymbols,
|
||||
pub sections: Vec<ObjSection>,
|
||||
pub entry: u64,
|
||||
pub mw_comment: MWComment,
|
||||
pub mw_comment: Option<MWComment>,
|
||||
|
||||
// Linker generated
|
||||
pub sda2_base: Option<u32>,
|
||||
@@ -181,7 +245,7 @@ pub struct ObjInfo {
|
||||
// Extracted
|
||||
pub splits: BTreeMap<u32, Vec<ObjSplit>>,
|
||||
pub named_sections: BTreeMap<u32, String>,
|
||||
pub link_order: Vec<String>,
|
||||
pub link_order: Vec<ObjUnit>,
|
||||
pub blocked_ranges: BTreeMap<u32, u32>, // start -> end
|
||||
|
||||
// From extab
|
||||
@@ -232,6 +296,8 @@ impl ObjSymbols {
|
||||
(symbol.kind == ObjSymbolKind::Unknown && symbol.name.starts_with("lbl_")))
|
||||
// Hack to avoid replacing different ABS symbols
|
||||
&& (symbol.section.is_some() || symbol.name == in_symbol.name)
|
||||
// Avoid replacing symbols with ABS symbols, and vice versa
|
||||
&& (symbol.section == in_symbol.section)
|
||||
});
|
||||
let target_symbol_idx = if let Some((symbol_idx, existing)) = opt {
|
||||
let size =
|
||||
@@ -495,6 +561,10 @@ impl ObjSymbols {
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn set_externally_referenced(&mut self, idx: SymbolIndex, value: bool) {
|
||||
self.symbols[idx].flags.set_externally_referenced(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjInfo {
|
||||
@@ -809,6 +879,13 @@ impl ObjSection {
|
||||
pub fn contains_range(&self, range: Range<u32>) -> bool {
|
||||
(range.start as u64) >= self.address && (range.end as u64) <= self.address + self.size
|
||||
}
|
||||
|
||||
pub fn rename(&mut self, name: String) -> Result<()> {
|
||||
self.kind = section_kind_for_section(&name)?;
|
||||
self.name = name;
|
||||
self.section_known = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> {
|
||||
|
||||
@@ -12,10 +12,7 @@ use sha1::{Digest, Sha1};
|
||||
use crate::{
|
||||
analysis::tracker::{Relocation, Tracker},
|
||||
array_ref,
|
||||
obj::{
|
||||
section_kind_for_section, ObjInfo, ObjReloc, ObjRelocKind, ObjSymbol, ObjSymbolFlagSet,
|
||||
ObjSymbolKind,
|
||||
},
|
||||
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolKind},
|
||||
util::elf::process_elf,
|
||||
};
|
||||
|
||||
@@ -112,9 +109,7 @@ pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> R
|
||||
let target_section = &mut obj.sections[target_section_index];
|
||||
if !target_section.section_known {
|
||||
if let Some(section_name) = &sig_symbol.section {
|
||||
target_section.name = section_name.clone();
|
||||
target_section.kind = section_kind_for_section(section_name)?;
|
||||
target_section.section_known = true;
|
||||
target_section.rename(section_name.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,7 +228,10 @@ pub fn compare_signature(existing: &mut FunctionSignature, new: &FunctionSignatu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_signature(path: &Path, symbol_name: &str) -> Result<Option<FunctionSignature>> {
|
||||
pub fn generate_signature<P: AsRef<Path>>(
|
||||
path: P,
|
||||
symbol_name: &str,
|
||||
) -> Result<Option<FunctionSignature>> {
|
||||
let mut out_symbols: Vec<OutSymbol> = Vec::new();
|
||||
let mut out_relocs: Vec<OutReloc> = Vec::new();
|
||||
let mut symbol_map: BTreeMap<usize, usize> = BTreeMap::new();
|
||||
|
||||
@@ -7,26 +7,31 @@ use anyhow::{anyhow, bail, ensure, Result};
|
||||
use itertools::Itertools;
|
||||
use petgraph::{graph::NodeIndex, Graph};
|
||||
|
||||
use crate::obj::{
|
||||
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSplit, ObjSymbol,
|
||||
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
||||
use crate::{
|
||||
obj::{
|
||||
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSplit,
|
||||
ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, ObjUnit,
|
||||
},
|
||||
util::comment::MWComment,
|
||||
};
|
||||
|
||||
/// Create splits for function pointers in the given section.
|
||||
fn split_ctors_dtors(obj: &mut ObjInfo, section_start: u32, section_end: u32) -> Result<()> {
|
||||
let mut new_splits = BTreeMap::new();
|
||||
let mut current_address = section_start;
|
||||
let mut referenced_symbols = vec![];
|
||||
|
||||
while current_address < section_end {
|
||||
let (section, chunk) = obj.section_data(current_address, current_address + 4)?;
|
||||
let function_addr = u32::from_be_bytes(chunk[0..4].try_into().unwrap());
|
||||
log::debug!("Found {} entry: {:#010X}", section.name, function_addr);
|
||||
|
||||
let Some((_, function_symbol)) =
|
||||
let Some((function_symbol_idx, function_symbol)) =
|
||||
obj.symbols.kind_at_address(function_addr, ObjSymbolKind::Function)?
|
||||
else {
|
||||
bail!("Failed to find function symbol @ {:#010X}", function_addr);
|
||||
};
|
||||
referenced_symbols.push(function_symbol_idx);
|
||||
|
||||
let ctors_split = obj.split_for(current_address);
|
||||
let function_split = obj.split_for(function_addr);
|
||||
@@ -90,6 +95,11 @@ fn split_ctors_dtors(obj: &mut ObjInfo, section_start: u32, section_end: u32) ->
|
||||
obj.add_split(addr, split)?;
|
||||
}
|
||||
|
||||
// Hack to avoid deadstripping
|
||||
for symbol_idx in referenced_symbols {
|
||||
obj.symbols.set_externally_referenced(symbol_idx, true);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -418,7 +428,7 @@ pub fn update_splits(obj: &mut ObjInfo) -> Result<()> {
|
||||
/// We can use a topological sort to determine a valid global TU order.
|
||||
/// There can be ambiguities, but any solution that satisfies the link order
|
||||
/// constraints is considered valid.
|
||||
fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<String>> {
|
||||
fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct SplitEdge {
|
||||
@@ -483,7 +493,21 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<String>> {
|
||||
// println!("{:?}", dot);
|
||||
|
||||
match petgraph::algo::toposort(&graph, None) {
|
||||
Ok(vec) => Ok(vec.iter().map(|&idx| graph[idx].clone()).collect_vec()),
|
||||
Ok(vec) => Ok(vec
|
||||
.iter()
|
||||
.map(|&idx| {
|
||||
let name = &graph[idx];
|
||||
if let Some(existing) = obj.link_order.iter().find(|u| &u.name == name) {
|
||||
existing.clone()
|
||||
} else {
|
||||
ObjUnit {
|
||||
name: name.clone(),
|
||||
autogenerated: obj.is_unit_autogenerated(name),
|
||||
comment_version: None,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect_vec()),
|
||||
Err(e) => Err(anyhow!(
|
||||
"Cyclic dependency (involving {}) encountered while resolving link order",
|
||||
graph[e.node_id()]
|
||||
@@ -499,17 +523,21 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
||||
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
|
||||
let mut name_to_obj: HashMap<String, usize> = HashMap::new();
|
||||
for unit in &obj.link_order {
|
||||
name_to_obj.insert(unit.clone(), objects.len());
|
||||
name_to_obj.insert(unit.name.clone(), objects.len());
|
||||
object_symbols.push(vec![None; obj.symbols.count()]);
|
||||
let mut obj = ObjInfo::new(
|
||||
let mut split_obj = ObjInfo::new(
|
||||
ObjKind::Relocatable,
|
||||
ObjArchitecture::PowerPc,
|
||||
unit.clone(),
|
||||
unit.name.clone(),
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
obj.mw_comment = obj.mw_comment.clone();
|
||||
objects.push(obj);
|
||||
if let Some(comment_version) = unit.comment_version {
|
||||
split_obj.mw_comment = Some(MWComment::new(comment_version)?);
|
||||
} else {
|
||||
split_obj.mw_comment = obj.mw_comment.clone();
|
||||
}
|
||||
objects.push(split_obj);
|
||||
}
|
||||
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
@@ -635,6 +663,12 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
||||
})?;
|
||||
}
|
||||
|
||||
// For mwldeppc 2.7 and above, a .comment section is required to link without error
|
||||
// when common symbols are present. Automatically add one if needed.
|
||||
if split.common && file.mw_comment.is_none() {
|
||||
file.mw_comment = Some(MWComment::new(8)?);
|
||||
}
|
||||
|
||||
if !split.common {
|
||||
let data = match section.kind {
|
||||
ObjSectionKind::Bss => vec![],
|
||||
@@ -743,7 +777,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
||||
symbol.name = new_name.clone();
|
||||
if symbol.flags.is_local() {
|
||||
log::debug!("Globalizing {} in {}", symbol.name, obj.name);
|
||||
symbol.flags.set_global();
|
||||
symbol.flags.set_scope(ObjSymbolScope::Global);
|
||||
}
|
||||
obj.symbols.replace(symbol_idx, symbol)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user