use crate::args::LinkOptions;
use crate::diag::{Diagnostic, DiagnosticSink};
use crate::elf::inputs::ElfInput;
use crate::elf::layout::Trailer;
use crate::elf::object::ObjectInput;
use crate::elf::sections::Sections;
use crate::elf::values::Addresses;
use crate::elf::write::Prerendered;
use crate::error::Result;
use crate::ids::FileId;
use crate::symbols::Resolution;
use super::GdbIndex;
use crate::debug::debug_names::DebugNames;
use crate::debug::section::{OutputCompression, compress_section};
#[derive(Debug, Default)]
pub struct DebugIndexes<'a> {
gdb_index: Option<GdbIndex<'a>>,
debug_names: Option<DebugNames>,
compressed_names: Option<Vec<u8>>,
}
impl<'a> DebugIndexes<'a> {
pub fn build<F: crate::elf::read::ElfFormat>(
files: &[ElfInput<'a, F>],
resolution: &Resolution<'_>,
sections: &Sections,
options: &LinkOptions,
) -> Result<Self> {
let mut this = Self::default();
if !options.gdb_index && !options.debug_names {
return Ok(this);
}
let objects = live_objects(files, resolution);
let live = |file: usize, section: u32| sections.is_present_in(file, section);
if options.gdb_index {
this.gdb_index = Some(GdbIndex::build(&objects, &live)?);
}
if options.debug_names {
this.debug_names = Some(DebugNames::build(&objects, &live)?);
}
Ok(this)
}
pub fn apply<F: crate::elf::read::ElfFormat>(
&mut self,
files: &[ElfInput<'a, F>],
resolution: &Resolution<'_>,
sections: &mut Sections,
diagnostics: &dyn DiagnosticSink,
) {
if let Some(names) = &self.debug_names {
for (file, object) in live_objects(files, resolution) {
for (index, section) in object.sections.iter().enumerate() {
if section.name == b".debug_names"
&& !section.is_alloc()
&& let Some(id) =
sections.id(file, u32::try_from(index).unwrap_or(u32::MAX))
&& let Some(slot) = sections.live.get_mut(id.index())
{
*slot = false;
}
}
}
if names.is_empty() {
self.debug_names = None;
}
}
let Some(index) = &self.gdb_index else {
return;
};
for (file, section, problem) in &index.problems {
let (name, object) = files
.get(*file)
.map(|f| (f.display(), f.object.as_ref()))
.unwrap_or_default();
let section_name = object
.and_then(|o| o.section(*section))
.map_or_else(String::new, |s| {
String::from_utf8_lossy(s.name).into_owned()
});
diagnostics.emit(Diagnostic::warning(format!(
"{name}:({section_name}): malformed DWARF ({} at offset {:#x}); --gdb-index skips the rest",
problem.what, problem.offset
)));
}
for (file, object) in live_objects(files, resolution) {
for (index, section) in object.sections.iter().enumerate() {
if super::is_consumed(section.name)
&& let Some(id) = sections.id(file, u32::try_from(index).unwrap_or(u32::MAX))
&& let Some(slot) = sections.live.get_mut(id.index())
{
*slot = false;
}
}
}
if index.is_empty() {
self.gdb_index = None;
}
}
#[must_use]
pub fn debug_names_size(&self) -> u64 {
if let Some(bytes) = &self.compressed_names {
return bytes.len() as u64;
}
self.debug_names.as_ref().map_or(0, DebugNames::size)
}
pub fn compress<F: crate::elf::read::ElfFormat>(
&mut self,
addresses: &Addresses<'_, '_, F>,
compression: OutputCompression,
) -> Result<Option<bool>> {
let Some(names) = &self.debug_names else {
return Ok(None);
};
let offset = |file: usize, section: u32, value: u64| {
addresses.section_offset_address(file, section, value)
};
let bytes = names.render(&offset)?;
let compressed = compress_section::<F>(&bytes, compression, 4);
if compressed.len() >= bytes.len() {
return Ok(None);
}
self.compressed_names = Some(compressed);
Ok(Some(compression.is_gabi()))
}
#[must_use]
pub fn gdb_index_size(&self) -> u64 {
self.gdb_index.as_ref().map_or(0, GdbIndex::size)
}
pub fn render<F: crate::elf::read::ElfFormat>(
&self,
addresses: &Addresses<'_, '_, F>,
prerendered: &mut Vec<Prerendered>,
) -> Result<()> {
if let Some(names) = &self.debug_names {
let position = generated_position(addresses, b".debug_names")?;
let offset = |file: usize, section: u32, value: u64| {
addresses.section_offset_address(file, section, value)
};
let (bytes, compressed) = match &self.compressed_names {
Some(bytes) => (bytes.clone(), true),
None => (names.render(&offset)?, false),
};
prerendered.push(Prerendered {
position,
bytes,
compressed,
});
}
let Some(index) = &self.gdb_index else {
return Ok(());
};
let position = generated_position(addresses, b".gdb_index")?;
let offset = |file: usize, section: u32| addresses.section_offset_address(file, section, 0);
let bytes = index.render(&offset, &offset)?;
prerendered.push(Prerendered {
position,
bytes,
compressed: false,
});
Ok(())
}
}
fn live_objects<'x, 'a, F: crate::elf::read::ElfFormat>(
files: &'x [ElfInput<'a, F>],
resolution: &Resolution<'_>,
) -> Vec<(usize, &'x ObjectInput<'a, F>)> {
files
.iter()
.enumerate()
.filter(|(index, _)| resolution.is_live(FileId::new(*index)))
.filter_map(|(index, file)| Some((index, file.object.as_ref()?)))
.collect()
}
fn generated_position<F: crate::elf::read::ElfFormat>(
addresses: &Addresses<'_, '_, F>,
name: &[u8],
) -> Result<u32> {
addresses
.layout
.sections
.iter()
.position(|s| {
s.trailer == Trailer::Generated
&& (s.name == name || (s.name_prefix == b".z" && name.get(1..) == Some(s.name)))
})
.and_then(|p| u32::try_from(p).ok())
.ok_or_else(|| {
crate::Error::Internal(format!(
"layout has no {} section",
String::from_utf8_lossy(name)
))
})
}