#![deny(clippy::arithmetic_side_effects)]
use hashbrown::HashMap;
use crate::error::{Error, Result};
use crate::macho::read::consts::{
S_4BYTE_LITERALS, S_8BYTE_LITERALS, S_16BYTE_LITERALS, S_ATTR_DEBUG, S_ATTR_EXT_RELOC,
S_ATTR_LOC_RELOC, S_ATTR_NO_DEAD_STRIP, S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS,
S_CSTRING_LITERALS, S_NON_LAZY_SYMBOL_POINTERS, S_REGULAR, S_SYMBOL_STUBS,
S_THREAD_LOCAL_REGULAR, S_THREAD_LOCAL_VARIABLE_POINTERS, S_THREAD_LOCAL_VARIABLES,
S_THREAD_LOCAL_ZEROFILL, S_ZEROFILL, SECTION_TYPE,
};
use super::buf::align_up;
use super::state::{Link, NONE};
pub const SG_READ_ONLY: u32 = 0x10;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SectionKind {
Input,
Stubs,
Got,
AuthGot,
ThreadPtrs,
Common,
UnwindInfo,
EhFrame,
Sectcreate(usize),
}
#[derive(Clone, Debug)]
pub struct OutSection {
pub segname: Vec<u8>,
pub sectname: Vec<u8>,
pub flags: u32,
pub align: u32,
pub addr: u64,
pub size: u64,
pub offset: u64,
pub kind: SectionKind,
pub input_order: usize,
pub inputs: Vec<(u32, u32)>,
pub reserved1: u32,
pub reserved2: u32,
pub segment: usize,
}
impl OutSection {
#[must_use]
pub fn is_zerofill(&self) -> bool {
matches!(
self.flags & SECTION_TYPE,
S_ZEROFILL | S_THREAD_LOCAL_ZEROFILL | 0x0c
)
}
#[must_use]
pub fn has_code(&self) -> bool {
self.flags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS) != 0
}
#[must_use]
pub fn end(&self) -> u64 {
self.addr.saturating_add(self.size)
}
}
#[derive(Clone, Debug, Default)]
pub struct OutSegment {
pub name: Vec<u8>,
pub vmaddr: u64,
pub vmsize: u64,
pub fileoff: u64,
pub filesize: u64,
pub maxprot: u32,
pub initprot: u32,
pub flags: u32,
pub sections: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
pub struct Layout {
pub sections: Vec<OutSection>,
pub segments: Vec<OutSegment>,
pub atom_section: Vec<u32>,
pub atom_offset: Vec<u64>,
pub common_offset: Vec<u64>,
pub header_size: u64,
pub members: Vec<Vec<usize>>,
pub islands: Vec<Island>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Island {
pub section: usize,
pub offset: u64,
pub size: u64,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct SyntheticSizes {
pub stubs: u64,
pub got: u64,
pub auth_got: u64,
pub thread_ptrs: u64,
pub unwind_info: u64,
pub eh_frame: u64,
}
#[must_use]
pub fn output_names<'s>(
segname: &'s [u8],
sectname: &'s [u8],
data_const: bool,
) -> (&'s [u8], &'s [u8]) {
match (segname, sectname) {
(b"__TEXT", b"__StaticInit" | b"__textcoal_nt") => (b"__TEXT", b"__text"),
(b"__TEXT", b"__const_coal") => (b"__TEXT", b"__const"),
(b"__DATA", b"__datacoal_nt") => (b"__DATA", b"__data"),
(
b"__DATA",
b"__got" | b"__const" | b"__cfstring" | b"__mod_init_func" | b"__mod_term_func"
| b"__nl_symbol_ptr" | b"__objc_classlist" | b"__objc_nlclslist" | b"__objc_catlist"
| b"__objc_nlcatlist" | b"__objc_protolist" | b"__objc_imageinfo",
) if data_const => (b"__DATA_CONST", sectname),
_ => (segname, sectname),
}
}
pub(super) type Member = ((usize, bool), usize, usize);
pub(super) fn is_literal_section(flags: u32) -> bool {
matches!(
flags & SECTION_TYPE,
S_CSTRING_LITERALS | S_4BYTE_LITERALS | S_8BYTE_LITERALS | S_16BYTE_LITERALS
)
}
#[derive(Debug, Default)]
pub(super) struct Literals {
first: Vec<usize>,
align: Vec<u32>,
}
impl Literals {
pub(super) fn canonical(&self, position: usize) -> usize {
self.first.get(position).copied().unwrap_or(position)
}
pub(super) fn align(&self, position: usize) -> Option<u32> {
self.align.get(position).copied()
}
}
pub(super) fn dedup_literals(link: &Link<'_>, list: &[Member]) -> Result<Literals> {
let mut seen: HashMap<&[u8], usize> = HashMap::with_capacity(list.len());
let mut first = Vec::with_capacity(list.len());
let mut align = vec![0u32; list.len()];
let mut merged = false;
for (position, &(_, file, atom)) in list.iter().enumerate() {
let Some(object) = link.object(file) else {
first.push(position);
continue;
};
let Some(info) = object.atoms.atoms().get(atom) else {
first.push(position);
continue;
};
let data = object
.file
.section_data(usize::try_from(info.section).unwrap_or(usize::MAX))?;
let Some(bytes) = usize::try_from(info.offset)
.ok()
.zip(usize::try_from(info.range().end).ok())
.and_then(|(start, end)| data.get(start..end))
else {
first.push(position);
continue;
};
let canonical = *seen.entry(bytes).or_insert(position);
merged |= canonical != position;
first.push(canonical);
if let Some(slot) = align.get_mut(canonical) {
*slot = (*slot).max(info.align);
}
}
if !merged {
return Ok(Literals::default());
}
Ok(Literals { first, align })
}
#[must_use]
pub fn atom_location(link: &Link<'_>, atom: usize) -> (usize, usize) {
let atom32 = u32::try_from(atom).unwrap_or(u32::MAX);
let file = link
.atom_base
.partition_point(|&base| base <= atom32)
.saturating_sub(1);
let mut owner = file;
while owner > 0
&& link.atom_base.get(owner) == link.atom_base.get(owner.saturating_sub(1))
&& link.object(owner).is_none()
{
owner = owner.saturating_sub(1);
}
let base = link.atom_base.get(owner).copied().unwrap_or(0);
(
owner,
usize::try_from(atom32.saturating_sub(base)).unwrap_or(0),
)
}
pub fn read_order_file(path: &std::path::Path, arch: &str) -> Result<HashMap<Vec<u8>, usize>> {
let text = std::fs::read(path).map_err(|error| Error::io(path, error))?;
let mut order = HashMap::new();
for line in text.split(|&b| b == b'\n') {
let line = match line.iter().position(|&b| b == b'#') {
Some(hash) => line.get(..hash).unwrap_or(&[]),
None => line,
};
let mut symbol = line.trim_ascii();
for known in [b"arm64:".as_slice(), b"x86_64:", b"arm64e:", b"x86_64h:"] {
if let Some(rest) = symbol.strip_prefix(known) {
if !known.starts_with(arch.as_bytes())
|| known.len() != arch.len().saturating_add(1)
{
symbol = b"";
} else {
symbol = rest;
}
break;
}
}
if let Some(position) = symbol.windows(3).position(|w| w == b".o:") {
symbol = symbol.get(position.saturating_add(3)..).unwrap_or(&[]);
}
if symbol.is_empty() {
continue;
}
let next = order.len();
order.entry(symbol.to_vec()).or_insert(next);
}
Ok(order)
}
#[must_use]
pub fn is_consumed(segname: &[u8], sectname: &[u8], flags: u32) -> bool {
flags & S_ATTR_DEBUG != 0
|| segname == b"__LLVM"
|| (segname == b"__LD" && sectname == b"__compact_unwind")
|| (segname == b"__TEXT" && sectname == b"__eh_frame")
}
fn section_order(section: &OutSection) -> (i64, usize) {
let big = i64::MAX;
let order = match section.segname.as_slice() {
b"__TEXT" => match section.sectname.as_slice() {
b"__text" => -5,
b"__stubs" | b"__auth_stubs" => -4,
b"__stub_helper" => -3,
b"__unwind_info" => big.saturating_sub(1),
b"__eh_frame" => big,
_ => 0,
},
b"__DATA" | b"__DATA_CONST" => match section.flags & SECTION_TYPE {
S_THREAD_LOCAL_VARIABLES => big.saturating_sub(3),
S_THREAD_LOCAL_REGULAR => big.saturating_sub(2),
S_THREAD_LOCAL_ZEROFILL => big.saturating_sub(1),
S_ZEROFILL => big,
_ => match section.sectname.as_slice() {
b"__got" => -3,
b"__auth_got" => -2,
b"__la_symbol_ptr" => -2,
b"__const" => -1,
_ => 0,
},
},
_ => 0,
};
(order, section.input_order)
}
fn segment_order(name: &[u8]) -> i64 {
match name {
b"__PAGEZERO" => -4,
b"__TEXT" => -3,
b"__DATA_CONST" => -2,
b"__DATA" => -1,
b"__LLVM" => i64::MAX.saturating_sub(1),
b"__LINKEDIT" => i64::MAX,
_ => 0,
}
}
fn protection(name: &[u8]) -> u32 {
match name {
b"__PAGEZERO" => 0,
b"__TEXT" => 5,
b"__LINKEDIT" => 1,
_ => 3,
}
}
#[allow(clippy::too_many_arguments)]
fn synthetic_section(
builder: &mut Builder,
input_sections: usize,
segname: &[u8],
sectname: &[u8],
flags: u32,
kind: SectionKind,
size: u64,
align: u32,
) -> usize {
let index = builder.get(segname, sectname, flags, kind);
if let Some(section) = builder.sections.get_mut(index) {
section.kind = kind;
section.size = size;
section.align = section.align.max(align);
section.input_order = section.input_order.saturating_add(input_sections);
}
index
}
struct Builder {
sections: Vec<OutSection>,
by_name: HashMap<(Vec<u8>, Vec<u8>), usize>,
}
impl Builder {
fn get(&mut self, segname: &[u8], sectname: &[u8], flags: u32, kind: SectionKind) -> usize {
let key = (segname.to_vec(), sectname.to_vec());
if let Some(&index) = self.by_name.get(&key) {
return index;
}
let index = self.sections.len();
self.sections.push(OutSection {
segname: key.0.clone(),
sectname: key.1.clone(),
flags: flags & !(S_ATTR_EXT_RELOC | S_ATTR_LOC_RELOC),
align: 0,
addr: 0,
size: 0,
offset: 0,
kind,
input_order: index,
inputs: Vec::new(),
reserved1: 0,
reserved2: 0,
segment: 0,
});
self.by_name.insert(key, index);
index
}
}
pub fn plan(
link: &Link<'_>,
synthetic: &SyntheticSizes,
commons: &[(u64, u32)],
sectcreate: &[(Vec<u8>, Vec<u8>, u64)],
order: &HashMap<Vec<u8>, usize>,
) -> Result<Layout> {
let config = link.config;
let mut builder = Builder {
sections: Vec::new(),
by_name: HashMap::new(),
};
let mut atom_section = vec![NONE; link.atom_count];
let mut atom_offset = vec![0u64; link.atom_count];
let data_const = config.data_const;
let mut members: Vec<Vec<Member>> = Vec::new();
for (file_index, _) in link.files.iter().enumerate() {
let Some(object) = link.object(file_index) else {
continue;
};
for (section_index, section) in object.file.sections().iter().enumerate() {
if is_consumed(section.segname, section.sectname, section.flags) {
continue;
}
let Some(range) = object.atoms.section_range(section_index) else {
continue;
};
let atoms: Vec<usize> = range
.filter(|&atom| link.is_live(file_index, atom))
.collect();
if atoms.is_empty() {
continue;
}
let (segname, sectname) = output_names(section.segname, section.sectname, data_const);
let (moved, atoms): (Vec<usize>, Vec<usize>) = atoms.into_iter().partition(|&atom| {
link.objc
.rewrite(link.atom_id(file_index, atom))
.is_some_and(|r| r.method_list)
});
let mut groups = Vec::with_capacity(2);
if !atoms.is_empty() {
let out = builder.get(segname, sectname, section.flags, SectionKind::Input);
if sectname == b"__objc_imageinfo"
&& builder
.sections
.get(out)
.is_some_and(|s| !s.inputs.is_empty())
{
continue;
}
groups.push((out, atoms));
}
if !moved.is_empty() {
let out = builder.get(
b"__TEXT",
b"__objc_methlist",
S_REGULAR | S_ATTR_NO_DEAD_STRIP,
SectionKind::Input,
);
groups.push((out, moved));
}
for (out, atoms) in groups {
let Some(out_section) = builder.sections.get_mut(out) else {
continue;
};
out_section.inputs.push((
u32::try_from(file_index).unwrap_or(NONE),
u32::try_from(section_index).unwrap_or(NONE),
));
if members.len() <= out {
members.resize_with(out.saturating_add(1), Vec::new);
}
let Some(list) = members.get_mut(out) else {
continue;
};
for atom in atoms {
let mut listed = usize::MAX;
let mut cold = false;
for &symbol in object.atoms.atom_symbols(atom) {
let Ok(entry) = object.file.symbols().get(symbol) else {
continue;
};
cold |= entry.is_cold_func();
if let Some(&position) = order.get(entry.name) {
listed = listed.min(position);
}
}
let key = if listed == usize::MAX {
(usize::MAX, cold)
} else {
(listed, false)
};
list.push((key, file_index, atom));
}
}
}
}
let mut ordered: Vec<Vec<usize>> = vec![Vec::new(); builder.sections.len()];
for (out, list) in members.iter_mut().enumerate() {
list.sort_by_key(|&(key, _, _)| key);
let Some(out_section) = builder.sections.get_mut(out) else {
continue;
};
let literals = if is_literal_section(out_section.flags) {
dedup_literals(link, list)?
} else {
Literals::default()
};
if let Some(slot) = ordered.get_mut(out) {
*slot = list
.iter()
.enumerate()
.filter(|&(position, _)| literals.canonical(position) == position)
.map(|(_, &(_, file, atom))| link.atom_id(file, atom))
.collect();
}
for (position, &(_, file_index, atom)) in list.iter().enumerate() {
let Some(info) = link
.object(file_index)
.and_then(|object| object.atoms.atoms().get(atom))
else {
continue;
};
let id = link.atom_id(file_index, atom);
let canonical = literals.canonical(position);
if canonical != position {
let first = list
.get(canonical)
.map(|&(_, file, atom)| link.atom_id(file, atom));
if let Some(first) = first {
let offset = atom_offset.get(first).copied().unwrap_or(0);
if let Some(slot) = atom_section.get_mut(id) {
*slot = u32::try_from(out).unwrap_or(NONE);
}
if let Some(slot) = atom_offset.get_mut(id) {
*slot = offset;
}
}
continue;
}
let rewrite = link.objc.rewrite(id);
let align = literals
.align(position)
.or(rewrite.map(|r| r.align))
.unwrap_or(info.align);
let size = rewrite.map_or(info.size, |r| {
u64::try_from(r.bytes.len()).unwrap_or(u64::MAX)
});
out_section.align = out_section.align.max(align);
let alignment = 1u64.checked_shl(align).unwrap_or(1);
let offset = align_up(out_section.size, alignment);
out_section.size = offset
.checked_add(size)
.ok_or_else(|| Error::Limit("output section larger than 2^64".into()))?;
if let Some(slot) = atom_section.get_mut(id) {
*slot = u32::try_from(out).unwrap_or(NONE);
}
if let Some(slot) = atom_offset.get_mut(id) {
*slot = offset;
}
}
}
let input_sections = builder.sections.len();
let got_segment: &[u8] = if data_const {
b"__DATA_CONST"
} else {
b"__DATA"
};
if synthetic.stubs > 0 {
let index = synthetic_section(
&mut builder,
input_sections,
b"__TEXT",
if config.is_arm64e() {
b"__auth_stubs"
} else {
b"__stubs"
},
S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
SectionKind::Stubs,
synthetic.stubs.saturating_mul(config.stub_size()),
if config.is_arm64() { 2 } else { 0 },
);
if let Some(section) = builder.sections.get_mut(index) {
section.reserved2 = u32::try_from(config.stub_size()).unwrap_or(0);
}
}
if synthetic.got > 0 {
synthetic_section(
&mut builder,
input_sections,
got_segment,
b"__got",
S_NON_LAZY_SYMBOL_POINTERS,
SectionKind::Got,
synthetic.got.saturating_mul(8),
3,
);
}
if synthetic.auth_got > 0 {
synthetic_section(
&mut builder,
input_sections,
got_segment,
b"__auth_got",
S_NON_LAZY_SYMBOL_POINTERS,
SectionKind::AuthGot,
synthetic.auth_got.saturating_mul(8),
3,
);
}
if synthetic.thread_ptrs > 0 {
synthetic_section(
&mut builder,
input_sections,
b"__DATA",
b"__thread_ptrs",
S_THREAD_LOCAL_VARIABLE_POINTERS,
SectionKind::ThreadPtrs,
synthetic.thread_ptrs.saturating_mul(8),
3,
);
}
if !commons.is_empty() {
let index = synthetic_section(
&mut builder,
input_sections,
b"__DATA",
b"__common",
S_ZEROFILL,
SectionKind::Common,
0,
0,
);
if let Some(section) = builder.sections.get_mut(index) {
let mut size = section.size;
for &(symbol_size, align) in commons {
let offset = align_up(size, 1u64.checked_shl(align).unwrap_or(1));
size = offset.saturating_add(symbol_size);
section.align = section.align.max(align);
}
section.size = size;
}
}
for (index, (segname, sectname, size)) in sectcreate.iter().enumerate() {
synthetic_section(
&mut builder,
input_sections,
segname,
sectname,
S_REGULAR,
SectionKind::Sectcreate(index),
*size,
0,
);
}
if synthetic.unwind_info > 0 {
synthetic_section(
&mut builder,
input_sections,
b"__TEXT",
b"__unwind_info",
S_REGULAR,
SectionKind::UnwindInfo,
synthetic.unwind_info,
2,
);
}
if synthetic.eh_frame > 0 {
synthetic_section(
&mut builder,
input_sections,
b"__TEXT",
b"__eh_frame",
0x6000_000b,
SectionKind::EhFrame,
synthetic.eh_frame,
3,
);
}
let mut common_offset = Vec::with_capacity(commons.len());
{
let mut size = 0u64;
for &(symbol_size, align) in commons {
let offset = align_up(size, 1u64.checked_shl(align).unwrap_or(1));
common_offset.push(offset);
size = offset.saturating_add(symbol_size);
}
}
let mut segment_names: Vec<Vec<u8>> = Vec::new();
if config.pagezero > 0 {
segment_names.push(b"__PAGEZERO".to_vec());
}
segment_names.push(b"__TEXT".to_vec());
for section in &builder.sections {
if !segment_names.contains(§ion.segname) {
segment_names.push(section.segname.clone());
}
}
segment_names.push(b"__LINKEDIT".to_vec());
let mut indexed: Vec<(usize, Vec<u8>)> = segment_names.into_iter().enumerate().collect();
indexed.sort_by_key(|(index, name)| (segment_order(name), *index));
let segments: Vec<OutSegment> = indexed
.into_iter()
.map(|(_, name)| OutSegment {
maxprot: protection(&name),
initprot: protection(&name),
flags: if name == b"__DATA_CONST" {
SG_READ_ONLY
} else {
0
},
name,
..OutSegment::default()
})
.collect();
let mut order: Vec<usize> = (0..builder.sections.len()).collect();
order.sort_by_key(|&index| {
let section = &builder.sections[index];
let segment = segments
.iter()
.position(|s| s.name == section.segname)
.unwrap_or(usize::MAX);
(segment, section_order(section))
});
let mut remap = vec![0usize; builder.sections.len()];
for (new, &old) in order.iter().enumerate() {
if let Some(slot) = remap.get_mut(old) {
*slot = new;
}
}
let mut sections: Vec<OutSection> = order
.iter()
.filter_map(|&old| builder.sections.get(old).cloned())
.collect();
let mut segments = segments;
for (index, section) in sections.iter_mut().enumerate() {
let segment = segments
.iter()
.position(|s| s.name == section.segname)
.unwrap_or(0);
section.segment = segment;
if let Some(segment) = segments.get_mut(segment) {
segment.sections.push(index);
}
}
for slot in &mut atom_section {
if *slot != NONE
&& let Some(&new) = remap.get(usize::try_from(*slot).unwrap_or(usize::MAX))
{
*slot = u32::try_from(new).unwrap_or(NONE);
}
}
let members = order
.iter()
.map(|&old| ordered.get(old).cloned().unwrap_or_default())
.collect();
Ok(Layout {
sections,
segments,
atom_section,
atom_offset,
common_offset,
header_size: 0,
members,
islands: Vec::new(),
})
}
impl Layout {
pub fn place_islands(
&mut self,
link: &Link<'_>,
section: usize,
islands: &[(usize, u64)],
) -> Result<()> {
let Some(members) = self.members.get(section).cloned() else {
return Ok(());
};
self.islands.retain(|i| i.section != section);
let mut size = 0u64;
let mut next_island = islands.iter().peekable();
for (position, &atom) in members.iter().enumerate() {
let (file, local) = atom_location(link, atom);
let Some(info) = link.object(file).and_then(|o| o.atoms.atoms().get(local)) else {
continue;
};
let alignment = 1u64.checked_shl(info.align).unwrap_or(1);
let offset = align_up(size, alignment);
if let Some(slot) = self.atom_offset.get_mut(atom) {
*slot = offset;
}
size = offset
.checked_add(info.size)
.ok_or_else(|| Error::Limit("output section larger than 2^64".into()))?;
while let Some(&&(after, island_size)) = next_island.peek() {
if after != position {
break;
}
next_island.next();
let offset = align_up(size, 4);
self.islands.push(Island {
section,
offset,
size: island_size,
});
size = offset
.checked_add(island_size)
.ok_or_else(|| Error::Limit("output section larger than 2^64".into()))?;
}
}
if let Some(out) = self.sections.get_mut(section) {
out.size = size;
out.align = out.align.max(2);
}
Ok(())
}
pub fn assign_addresses(&mut self, link: &Link<'_>, header_size: u64) -> Result<()> {
let config = link.config;
let page = config.page_size;
self.header_size = header_size;
let overflow = || Error::Limit("the image does not fit the address space".into());
let mut addr = 0u64;
let mut file = 0u64;
for segment_index in 0..self.segments.len() {
let Some(segment) = self.segments.get_mut(segment_index) else {
continue;
};
if segment.name == b"__PAGEZERO" {
segment.vmaddr = 0;
segment.vmsize = config.pagezero;
addr = config.image_base.max(config.pagezero);
continue;
}
if segment.name == b"__LINKEDIT" {
segment.vmaddr = addr;
segment.fileoff = file;
continue;
}
segment.vmaddr = addr;
segment.fileoff = file;
let (mut seg_addr, mut seg_file) = (addr, file);
if segment.name == b"__TEXT" {
seg_addr = seg_addr.checked_add(header_size).ok_or_else(overflow)?;
seg_file = seg_file.checked_add(header_size).ok_or_else(overflow)?;
}
let section_list = segment.sections.clone();
for §ion_index in §ion_list {
let Some(section) = self.sections.get_mut(section_index) else {
continue;
};
let alignment = 1u64.checked_shl(section.align).unwrap_or(1);
seg_addr = align_up(seg_addr, alignment);
section.addr = seg_addr;
seg_addr = seg_addr.checked_add(section.size).ok_or_else(overflow)?;
if section.is_zerofill() {
section.offset = 0;
} else {
seg_file = align_up(seg_file, alignment);
section.offset = seg_file;
seg_file = seg_file.checked_add(section.size).ok_or_else(overflow)?;
}
}
let Some(segment) = self.segments.get_mut(segment_index) else {
continue;
};
segment.vmsize = align_up(seg_addr.saturating_sub(segment.vmaddr), page);
segment.filesize = align_up(seg_file.saturating_sub(segment.fileoff), page);
if seg_file == segment.fileoff {
segment.filesize = 0;
}
addr = segment
.vmaddr
.checked_add(segment.vmsize)
.ok_or_else(overflow)?;
file = segment
.fileoff
.checked_add(segment.filesize)
.ok_or_else(overflow)?;
}
Ok(())
}
#[must_use]
pub fn atom_address(&self, atom: usize) -> Option<u64> {
let section = *self.atom_section.get(atom)?;
if section == NONE {
return None;
}
let section = self.sections.get(usize::try_from(section).ok()?)?;
Some(section.addr.saturating_add(*self.atom_offset.get(atom)?))
}
#[must_use]
pub fn find(&self, kind: SectionKind) -> Option<&OutSection> {
self.sections.iter().find(|s| s.kind == kind)
}
#[must_use]
pub fn by_name(&self, segname: &[u8], sectname: &[u8]) -> Option<&OutSection> {
self.sections
.iter()
.find(|s| s.segname == segname && s.sectname == sectname)
}
#[must_use]
pub fn segment(&self, name: &[u8]) -> Option<&OutSegment> {
self.segments.iter().find(|s| s.name == name)
}
#[must_use]
pub fn tlv_template_start(&self) -> Option<u64> {
self.sections
.iter()
.filter(|s| {
matches!(
s.flags & SECTION_TYPE,
S_THREAD_LOCAL_REGULAR | S_THREAD_LOCAL_ZEROFILL
)
})
.map(|s| s.addr)
.min()
}
#[must_use]
pub fn segment_of(&self, addr: u64) -> Option<(usize, u64)> {
self.segments.iter().enumerate().find_map(|(index, s)| {
(addr >= s.vmaddr
&& addr < s.vmaddr.saturating_add(s.vmsize)
&& s.name != b"__PAGEZERO")
.then(|| (index, addr.saturating_sub(s.vmaddr)))
})
}
#[must_use]
pub fn file_offset(&self, addr: u64) -> Option<u64> {
let segment = self.segments.iter().find(|s| {
s.filesize > 0 && addr >= s.vmaddr && addr < s.vmaddr.saturating_add(s.filesize)
})?;
Some(
segment
.fileoff
.saturating_add(addr.saturating_sub(segment.vmaddr)),
)
}
}