Add symbol mapping feature (#118)

This allows users to "map" (or "link") symbols with different names so that they can be compared without having to update either the target or base objects. Symbol mappings are persisted in objdiff.json, so generators will need to ensure that they're preserved when updating. (Example: d1334bb79e)

Resolves #117
This commit is contained in:
2024-10-09 21:44:18 -06:00
committed by GitHub
parent 603dbd6882
commit 741d93e211
26 changed files with 2259 additions and 928 deletions

View File

@@ -17,8 +17,8 @@ crate-type = ["cdylib", "rlib"]
[features]
all = ["config", "dwarf", "mips", "ppc", "x86", "arm", "bindings"]
any-arch = [] # Implicit, used to check if any arch is enabled
config = ["globset", "semver", "serde_json", "serde_yaml"]
any-arch = ["bimap"] # Implicit, used to check if any arch is enabled
config = ["bimap", "globset", "semver", "serde_json", "serde_yaml"]
dwarf = ["gimli"]
mips = ["any-arch", "rabbitizer"]
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
@@ -32,6 +32,7 @@ features = ["all"]
[dependencies]
anyhow = "1.0"
bimap = { version = "0.6", features = ["serde"], optional = true }
byteorder = "1.5"
filetime = "0.2"
flagset = "0.4"

View File

@@ -1,77 +1,100 @@
use std::{
fs,
fs::File,
io::{BufReader, Read},
io::{BufReader, BufWriter, Read},
path::{Path, PathBuf},
};
use anyhow::{anyhow, Context, Result};
use bimap::BiBTreeMap;
use filetime::FileTime;
use globset::{Glob, GlobSet, GlobSetBuilder};
#[inline]
fn bool_true() -> bool { true }
#[derive(Default, Clone, serde::Deserialize)]
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProjectConfig {
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_version: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_make: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_args: Option<Vec<String>>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_dir: Option<PathBuf>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_dir: Option<PathBuf>,
#[serde(default = "bool_true")]
pub build_base: bool,
#[serde(default)]
pub build_target: bool,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build_base: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build_target: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub watch_patterns: Option<Vec<Glob>>,
#[serde(default, alias = "units")]
pub objects: Vec<ProjectObject>,
#[serde(default)]
pub progress_categories: Vec<ProjectProgressCategory>,
#[serde(default, alias = "objects", skip_serializing_if = "Option::is_none")]
pub units: Option<Vec<ProjectObject>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub progress_categories: Option<Vec<ProjectProgressCategory>>,
}
#[derive(Default, Clone, serde::Deserialize)]
impl ProjectConfig {
#[inline]
pub fn units(&self) -> &[ProjectObject] { self.units.as_deref().unwrap_or_default() }
#[inline]
pub fn units_mut(&mut self) -> &mut Vec<ProjectObject> {
self.units.get_or_insert_with(Vec::new)
}
#[inline]
pub fn progress_categories(&self) -> &[ProjectProgressCategory] {
self.progress_categories.as_deref().unwrap_or_default()
}
#[inline]
pub fn progress_categories_mut(&mut self) -> &mut Vec<ProjectProgressCategory> {
self.progress_categories.get_or_insert_with(Vec::new)
}
}
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProjectObject {
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<PathBuf>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_path: Option<PathBuf>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_path: Option<PathBuf>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[deprecated(note = "Use metadata.reverse_fn_order")]
pub reverse_fn_order: Option<bool>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[deprecated(note = "Use metadata.complete")]
pub complete: Option<bool>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scratch: Option<ScratchConfig>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<ProjectObjectMetadata>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol_mappings: Option<SymbolMappings>,
}
#[derive(Default, Clone, serde::Deserialize)]
pub type SymbolMappings = BiBTreeMap<String, String>;
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProjectObjectMetadata {
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub complete: Option<bool>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reverse_fn_order: Option<bool>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_path: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub progress_categories: Option<Vec<String>>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_generated: Option<bool>,
}
#[derive(Default, Clone, serde::Deserialize)]
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProjectProgressCategory {
#[serde(default)]
pub id: String,
@@ -112,12 +135,12 @@ impl ProjectObject {
}
pub fn complete(&self) -> Option<bool> {
#[allow(deprecated)]
#[expect(deprecated)]
self.metadata.as_ref().and_then(|m| m.complete).or(self.complete)
}
pub fn reverse_fn_order(&self) -> Option<bool> {
#[allow(deprecated)]
#[expect(deprecated)]
self.metadata.as_ref().and_then(|m| m.reverse_fn_order).or(self.reverse_fn_order)
}
@@ -132,16 +155,16 @@ impl ProjectObject {
#[derive(Default, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct ScratchConfig {
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub platform: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compiler: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub c_flags: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ctx_path: Option<PathBuf>,
#[serde(default)]
pub build_ctx: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build_ctx: Option<bool>,
}
pub const CONFIG_FILENAMES: [&str; 3] = ["objdiff.json", "objdiff.yml", "objdiff.yaml"];
@@ -154,7 +177,7 @@ pub const DEFAULT_WATCH_PATTERNS: &[&str] = &[
#[derive(Clone, Eq, PartialEq)]
pub struct ProjectConfigInfo {
pub path: PathBuf,
pub timestamp: FileTime,
pub timestamp: Option<FileTime>,
}
pub fn try_project_config(dir: &Path) -> Option<(Result<ProjectConfig>, ProjectConfigInfo)> {
@@ -180,12 +203,41 @@ pub fn try_project_config(dir: &Path) -> Option<(Result<ProjectConfig>, ProjectC
result = Err(e);
}
}
return Some((result, ProjectConfigInfo { path: config_path, timestamp: ts }));
return Some((result, ProjectConfigInfo { path: config_path, timestamp: Some(ts) }));
}
}
None
}
pub fn save_project_config(
config: &ProjectConfig,
info: &ProjectConfigInfo,
) -> Result<ProjectConfigInfo> {
if let Some(last_ts) = info.timestamp {
// Check if the file has changed since we last read it
if let Ok(metadata) = fs::metadata(&info.path) {
let ts = FileTime::from_last_modification_time(&metadata);
if ts != last_ts {
return Err(anyhow!("Config file has changed since last read"));
}
}
}
let mut writer =
BufWriter::new(File::create(&info.path).context("Failed to create config file")?);
let ext = info.path.extension().and_then(|ext| ext.to_str()).unwrap_or("json");
match ext {
"json" => serde_json::to_writer_pretty(&mut writer, config).context("Failed to write JSON"),
"yml" | "yaml" => {
serde_yaml::to_writer(&mut writer, config).context("Failed to write YAML")
}
_ => Err(anyhow!("Unknown config file extension: {ext}")),
}?;
let file = writer.into_inner().context("Failed to flush file")?;
let metadata = file.metadata().context("Failed to get file metadata")?;
let ts = FileTime::from_last_modification_time(&metadata);
Ok(ProjectConfigInfo { path: info.path.clone(), timestamp: Some(ts) })
}
fn validate_min_version(config: &ProjectConfig) -> Result<()> {
let Some(min_version) = &config.min_version else { return Ok(()) };
let version = semver::Version::parse(env!("CARGO_PKG_VERSION"))

View File

@@ -41,7 +41,7 @@ pub fn no_diff_code(out: &ProcessCodeResult, symbol_ref: SymbolRef) -> Result<Ob
});
}
resolve_branches(&mut diff);
Ok(ObjSymbolDiff { symbol_ref, diff_symbol: None, instructions: diff, match_percent: None })
Ok(ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: diff, match_percent: None })
}
pub fn diff_code(
@@ -67,7 +67,7 @@ pub fn diff_code(
right.arg_diff = result.right_args_diff;
}
let total = left_out.insts.len();
let total = left_out.insts.len().max(right_out.insts.len());
let percent = if diff_state.diff_count >= total {
0.0
} else {
@@ -77,13 +77,13 @@ pub fn diff_code(
Ok((
ObjSymbolDiff {
symbol_ref: left_symbol_ref,
diff_symbol: Some(right_symbol_ref),
target_symbol: Some(right_symbol_ref),
instructions: left_diff,
match_percent: Some(percent),
},
ObjSymbolDiff {
symbol_ref: right_symbol_ref,
diff_symbol: Some(left_symbol_ref),
target_symbol: Some(left_symbol_ref),
instructions: right_diff,
match_percent: Some(percent),
},
@@ -211,7 +211,7 @@ fn arg_eq(
left_diff: &ObjInsDiff,
right_diff: &ObjInsDiff,
) -> bool {
return match left {
match left {
ObjInsArg::PlainText(l) => match right {
ObjInsArg::PlainText(r) => l == r,
_ => false,
@@ -236,7 +236,7 @@ fn arg_eq(
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
}
};
}
}
#[derive(Default)]

View File

@@ -20,13 +20,13 @@ pub fn diff_bss_symbol(
Ok((
ObjSymbolDiff {
symbol_ref: left_symbol_ref,
diff_symbol: Some(right_symbol_ref),
target_symbol: Some(right_symbol_ref),
instructions: vec![],
match_percent: Some(percent),
},
ObjSymbolDiff {
symbol_ref: right_symbol_ref,
diff_symbol: Some(left_symbol_ref),
target_symbol: Some(left_symbol_ref),
instructions: vec![],
match_percent: Some(percent),
},
@@ -34,7 +34,7 @@ pub fn diff_bss_symbol(
}
pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff {
ObjSymbolDiff { symbol_ref, diff_symbol: None, instructions: vec![], match_percent: None }
ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: vec![], match_percent: None }
}
/// Compare the data sections of two object files.
@@ -158,13 +158,13 @@ pub fn diff_data_symbol(
Ok((
ObjSymbolDiff {
symbol_ref: left_symbol_ref,
diff_symbol: Some(right_symbol_ref),
target_symbol: Some(right_symbol_ref),
instructions: vec![],
match_percent: Some(match_percent),
},
ObjSymbolDiff {
symbol_ref: right_symbol_ref,
diff_symbol: Some(left_symbol_ref),
target_symbol: Some(left_symbol_ref),
instructions: vec![],
match_percent: Some(match_percent),
},

View File

@@ -29,7 +29,7 @@ pub enum DiffText<'a> {
Eol,
}
#[derive(Default, Clone, PartialEq, Eq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum HighlightKind {
#[default]
None,

View File

@@ -3,6 +3,7 @@ use std::collections::HashSet;
use anyhow::Result;
use crate::{
config::SymbolMappings,
diff::{
code::{diff_code, no_diff_code, process_code_symbol},
data::{
@@ -161,6 +162,8 @@ pub struct DiffObjConfig {
#[serde(default = "default_true")]
pub space_between_args: bool,
pub combine_data_sections: bool,
#[serde(default)]
pub symbol_mappings: MappingConfig,
// x86
pub x86_formatter: X86Formatter,
// MIPS
@@ -182,6 +185,7 @@ impl Default for DiffObjConfig {
relax_reloc_diffs: false,
space_between_args: true,
combine_data_sections: false,
symbol_mappings: Default::default(),
x86_formatter: Default::default(),
mips_abi: Default::default(),
mips_instr_category: Default::default(),
@@ -223,8 +227,10 @@ impl ObjSectionDiff {
#[derive(Debug, Clone, Default)]
pub struct ObjSymbolDiff {
/// The symbol ref this object
pub symbol_ref: SymbolRef,
pub diff_symbol: Option<SymbolRef>,
/// The symbol ref in the _other_ object that this symbol was diffed against
pub target_symbol: Option<SymbolRef>,
pub instructions: Vec<ObjInsDiff>,
pub match_percent: Option<f32>,
}
@@ -294,8 +300,13 @@ pub struct ObjInsBranchTo {
#[derive(Default)]
pub struct ObjDiff {
/// A list of all section diffs in the object.
pub sections: Vec<ObjSectionDiff>,
/// Common BSS symbols don't live in a section, so they're stored separately.
pub common: Vec<ObjSymbolDiff>,
/// If `selecting_left` or `selecting_right` is set, this is the list of symbols
/// that are being mapped to the other object.
pub mapping_symbols: Vec<ObjSymbolDiff>,
}
impl ObjDiff {
@@ -303,13 +314,14 @@ impl ObjDiff {
let mut result = Self {
sections: Vec::with_capacity(obj.sections.len()),
common: Vec::with_capacity(obj.common.len()),
mapping_symbols: vec![],
};
for (section_idx, section) in obj.sections.iter().enumerate() {
let mut symbols = Vec::with_capacity(section.symbols.len());
for (symbol_idx, _) in section.symbols.iter().enumerate() {
symbols.push(ObjSymbolDiff {
symbol_ref: SymbolRef { section_idx, symbol_idx },
diff_symbol: None,
target_symbol: None,
instructions: vec![],
match_percent: None,
});
@@ -328,7 +340,7 @@ impl ObjDiff {
for (symbol_idx, _) in obj.common.iter().enumerate() {
result.common.push(ObjSymbolDiff {
symbol_ref: SymbolRef { section_idx: obj.sections.len(), symbol_idx },
diff_symbol: None,
target_symbol: None,
instructions: vec![],
match_percent: None,
});
@@ -378,7 +390,7 @@ pub fn diff_objs(
right: Option<&ObjInfo>,
prev: Option<&ObjInfo>,
) -> Result<DiffObjsResult> {
let symbol_matches = matching_symbols(left, right, prev)?;
let symbol_matches = matching_symbols(left, right, prev, &config.symbol_mappings)?;
let section_matches = matching_sections(left, right)?;
let mut left = left.map(|p| (p, ObjDiff::new_from_obj(p)));
let mut right = right.map(|p| (p, ObjDiff::new_from_obj(p)));
@@ -529,6 +541,17 @@ pub fn diff_objs(
}
}
if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
(right.as_mut(), left.as_mut())
{
if let Some(right_name) = &config.symbol_mappings.selecting_left {
generate_mapping_symbols(right_obj, right_name, left_obj, left_out, config)?;
}
if let Some(left_name) = &config.symbol_mappings.selecting_right {
generate_mapping_symbols(left_obj, left_name, right_obj, right_out, config)?;
}
}
Ok(DiffObjsResult {
left: left.map(|(_, o)| o),
right: right.map(|(_, o)| o),
@@ -536,6 +559,63 @@ pub fn diff_objs(
})
}
/// When we're selecting a symbol to use as a comparison, we'll create comparisons for all
/// symbols in the other object that match the selected symbol's section and kind. This allows
/// us to display match percentages for all symbols in the other object that could be selected.
fn generate_mapping_symbols(
base_obj: &ObjInfo,
base_name: &str,
target_obj: &ObjInfo,
target_out: &mut ObjDiff,
config: &DiffObjConfig,
) -> Result<()> {
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
return Ok(());
};
let (base_section, base_symbol) = base_obj.section_symbol(base_symbol_ref);
let Some(base_section) = base_section else {
return Ok(());
};
let base_code = match base_section.kind {
ObjSectionKind::Code => Some(process_code_symbol(base_obj, base_symbol_ref, config)?),
_ => None,
};
for (target_section_index, target_section) in
target_obj.sections.iter().enumerate().filter(|(_, s)| s.kind == base_section.kind)
{
for (target_symbol_index, _target_symbol) in
target_section.symbols.iter().enumerate().filter(|(_, s)| s.kind == base_symbol.kind)
{
let target_symbol_ref =
SymbolRef { section_idx: target_section_index, symbol_idx: target_symbol_index };
match base_section.kind {
ObjSectionKind::Code => {
let target_code = process_code_symbol(target_obj, target_symbol_ref, config)?;
let (left_diff, _right_diff) = diff_code(
&target_code,
base_code.as_ref().unwrap(),
target_symbol_ref,
base_symbol_ref,
config,
)?;
target_out.mapping_symbols.push(left_diff);
}
ObjSectionKind::Data => {
let (left_diff, _right_diff) =
diff_data_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?;
target_out.mapping_symbols.push(left_diff);
}
ObjSectionKind::Bss => {
let (left_diff, _right_diff) =
diff_bss_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?;
target_out.mapping_symbols.push(left_diff);
}
}
}
}
Ok(())
}
#[derive(Copy, Clone, Eq, PartialEq)]
struct SymbolMatch {
left: Option<SymbolRef>,
@@ -551,19 +631,115 @@ struct SectionMatch {
section_kind: ObjSectionKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, serde::Deserialize, serde::Serialize)]
pub struct MappingConfig {
/// Manual symbol mappings
pub mappings: SymbolMappings,
/// The right object symbol name that we're selecting a left symbol for
pub selecting_left: Option<String>,
/// The left object symbol name that we're selecting a right symbol for
pub selecting_right: Option<String>,
}
fn symbol_ref_by_name(obj: &ObjInfo, name: &str) -> Option<SymbolRef> {
for (section_idx, section) in obj.sections.iter().enumerate() {
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
if symbol.name == name {
return Some(SymbolRef { section_idx, symbol_idx });
}
}
}
None
}
fn apply_symbol_mappings(
left: &ObjInfo,
right: &ObjInfo,
mapping_config: &MappingConfig,
left_used: &mut HashSet<SymbolRef>,
right_used: &mut HashSet<SymbolRef>,
matches: &mut Vec<SymbolMatch>,
) -> Result<()> {
// If we're selecting a symbol to use as a comparison, mark it as used
// This ensures that we don't match it to another symbol at any point
if let Some(left_name) = &mapping_config.selecting_left {
if let Some(left_symbol) = symbol_ref_by_name(left, left_name) {
left_used.insert(left_symbol);
}
}
if let Some(right_name) = &mapping_config.selecting_right {
if let Some(right_symbol) = symbol_ref_by_name(right, right_name) {
right_used.insert(right_symbol);
}
}
// Apply manual symbol mappings
for (left_name, right_name) in &mapping_config.mappings {
let Some(left_symbol) = symbol_ref_by_name(left, left_name) else {
continue;
};
if left_used.contains(&left_symbol) {
continue;
}
let Some(right_symbol) = symbol_ref_by_name(right, right_name) else {
continue;
};
if right_used.contains(&right_symbol) {
continue;
}
let left_section = &left.sections[left_symbol.section_idx];
let right_section = &right.sections[right_symbol.section_idx];
if left_section.kind != right_section.kind {
log::warn!(
"Symbol section kind mismatch: {} ({:?}) vs {} ({:?})",
left_name,
left_section.kind,
right_name,
right_section.kind
);
continue;
}
matches.push(SymbolMatch {
left: Some(left_symbol),
right: Some(right_symbol),
prev: None, // TODO
section_kind: left_section.kind,
});
left_used.insert(left_symbol);
right_used.insert(right_symbol);
}
Ok(())
}
/// Find matching symbols between each object.
fn matching_symbols(
left: Option<&ObjInfo>,
right: Option<&ObjInfo>,
prev: Option<&ObjInfo>,
mappings: &MappingConfig,
) -> Result<Vec<SymbolMatch>> {
let mut matches = Vec::new();
let mut left_used = HashSet::new();
let mut right_used = HashSet::new();
if let Some(left) = left {
if let Some(right) = right {
apply_symbol_mappings(
left,
right,
mappings,
&mut left_used,
&mut right_used,
&mut matches,
)?;
}
for (section_idx, section) in left.sections.iter().enumerate() {
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
let symbol_ref = SymbolRef { section_idx, symbol_idx };
if left_used.contains(&symbol_ref) {
continue;
}
let symbol_match = SymbolMatch {
left: Some(SymbolRef { section_idx, symbol_idx }),
left: Some(symbol_ref),
right: find_symbol(right, symbol, section, Some(&right_used)),
prev: find_symbol(prev, symbol, section, None),
section_kind: section.kind,
@@ -575,8 +751,12 @@ fn matching_symbols(
}
}
for (symbol_idx, symbol) in left.common.iter().enumerate() {
let symbol_ref = SymbolRef { section_idx: left.sections.len(), symbol_idx };
if left_used.contains(&symbol_ref) {
continue;
}
let symbol_match = SymbolMatch {
left: Some(SymbolRef { section_idx: left.sections.len(), symbol_idx }),
left: Some(symbol_ref),
right: find_common_symbol(right, symbol),
prev: find_common_symbol(prev, symbol),
section_kind: ObjSectionKind::Bss,

View File

@@ -112,6 +112,15 @@ pub struct ObjIns {
pub orig: Option<String>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub enum ObjSymbolKind {
#[default]
Unknown,
Function,
Object,
Section,
}
#[derive(Debug, Clone)]
pub struct ObjSymbol {
pub name: String,
@@ -120,6 +129,7 @@ pub struct ObjSymbol {
pub section_address: u64,
pub size: u64,
pub size_known: bool,
pub kind: ObjSymbolKind,
pub flags: ObjSymbolFlagSet,
pub addend: i64,
/// Original virtual address (from .note.split section)

View File

@@ -23,6 +23,7 @@ use crate::{
obj::{
split_meta::{SplitMeta, SPLITMETA_SECTION},
ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
ObjSymbolKind,
},
util::{read_u16, read_u32},
};
@@ -94,6 +95,13 @@ fn to_obj_symbol(
})
.unwrap_or(&[]);
let kind = match symbol.kind() {
SymbolKind::Text => ObjSymbolKind::Function,
SymbolKind::Data => ObjSymbolKind::Object,
SymbolKind::Section => ObjSymbolKind::Section,
_ => ObjSymbolKind::Unknown,
};
Ok(ObjSymbol {
name: name.to_string(),
demangled_name,
@@ -101,6 +109,7 @@ fn to_obj_symbol(
section_address,
size: symbol.size(),
size_known: symbol.size() != 0,
kind,
flags,
addend,
virtual_address,
@@ -173,12 +182,19 @@ fn symbols_by_section(
result.sort_by(|a, b| a.address.cmp(&b.address).then(a.size.cmp(&b.size)));
let mut iter = result.iter_mut().peekable();
while let Some(symbol) = iter.next() {
if symbol.size == 0 {
if symbol.kind == ObjSymbolKind::Unknown && symbol.size == 0 {
if let Some(next_symbol) = iter.peek() {
symbol.size = next_symbol.address - symbol.address;
} else {
symbol.size = (section.address + section.size) - symbol.address;
}
// Set symbol kind if we ended up with a non-zero size
if symbol.size > 0 {
symbol.kind = match section.kind {
ObjSectionKind::Code => ObjSymbolKind::Function,
ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object,
};
}
}
}
if result.is_empty() {
@@ -196,6 +212,10 @@ fn symbols_by_section(
section_address: 0,
size: section.size,
size_known: true,
kind: match section.kind {
ObjSectionKind::Code => ObjSymbolKind::Function,
ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object,
},
flags: Default::default(),
addend: 0,
virtual_address: None,
@@ -281,6 +301,7 @@ fn find_section_symbol(
section_address: 0,
size: 0,
size_known: false,
kind: ObjSymbolKind::Section,
flags: Default::default(),
addend: address as i64 - section.address() as i64,
virtual_address: None,
@@ -568,6 +589,7 @@ fn update_combined_symbol(symbol: ObjSymbol, address_change: i64) -> Result<ObjS
section_address: (symbol.section_address as i64 + address_change).try_into()?,
size: symbol.size,
size_known: symbol.size_known,
kind: symbol.kind,
flags: symbol.flags,
addend: symbol.addend,
virtual_address: if let Some(virtual_address) = symbol.virtual_address {