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]]
name = "decomp-toolkit"
version = "0.6.3"
version = "0.6.4"
dependencies = [
"anyhow",
"ar",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.
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) => {