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:
2023-08-08 23:34:00 -04:00
parent 5bdffa94c4
commit d9e1ae2777
16 changed files with 822 additions and 281 deletions

View File

@@ -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> {

View File

@@ -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();

View File

@@ -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)?;
}