use std::path::{Path, PathBuf};
use super::pattern::Pattern;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Span {
pub file: u32,
pub line: u32,
pub column: u32,
pub offset: u32,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Script {
pub commands: Vec<Command>,
pub files: Vec<PathBuf>,
}
impl Script {
#[must_use]
pub fn file_of(&self, span: Span) -> &Path {
usize::try_from(span.file)
.ok()
.and_then(|i| self.files.get(i))
.map_or(Path::new(""), PathBuf::as_path)
}
#[must_use]
pub fn error_at(&self, span: Span, message: impl Into<String>) -> super::ScriptError {
super::ScriptError::new(
self.file_of(span),
span.line,
span.column,
u64::from(span.offset),
message,
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Command {
pub span: Span,
pub kind: CommandKind,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CommandKind {
Entry(Vec<u8>),
Input(Vec<InputFile>),
Group(Vec<InputFile>),
Lib(Vec<InputFile>),
Output(Vec<u8>),
SearchDir(Vec<u8>),
Startup(Vec<u8>),
OutputFormat {
default: Vec<u8>,
big: Option<Vec<u8>>,
little: Option<Vec<u8>>,
},
OutputArch(Vec<u8>),
Target(Vec<u8>),
Map(Vec<u8>),
Extern(Vec<Vec<u8>>),
ForceCommonAllocation,
ForceGroupAllocation,
InhibitCommonAllocation,
NoCrossRefs(Vec<Vec<u8>>),
NoCrossRefsTo(Vec<Vec<u8>>),
RegionAlias {
alias: Vec<u8>,
region: Vec<u8>,
},
Insert {
position: InsertPosition,
section: Vec<u8>,
},
LdFeature(Vec<u8>),
Assignment(Assignment),
Assert(Assert),
Sections(Vec<SectionsCommand>),
OverwriteSections(Vec<SectionsCommand>),
Memory(Vec<MemoryRegion>),
Phdrs(Vec<Phdr>),
Version(Vec<VersionNode>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InsertPosition {
After,
Before,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InputFile {
pub name: InputName,
pub as_needed: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InputName {
Path(Vec<u8>),
Library(Vec<u8>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Assignment {
pub target: Vec<u8>,
pub op: AssignOp,
pub expr: Expr,
pub kind: AssignKind,
}
impl Assignment {
#[must_use]
pub fn is_dot(&self) -> bool {
self.target == b"."
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssignOp {
Assign,
Add,
Sub,
Mul,
Div,
Shl,
Shr,
And,
Or,
Xor,
}
impl AssignOp {
#[must_use]
pub fn binary(self) -> Option<BinaryOp> {
Some(match self {
Self::Assign => return None,
Self::Add => BinaryOp::Add,
Self::Sub => BinaryOp::Sub,
Self::Mul => BinaryOp::Mul,
Self::Div => BinaryOp::Div,
Self::Shl => BinaryOp::Shl,
Self::Shr => BinaryOp::Shr,
Self::And => BinaryOp::And,
Self::Or => BinaryOp::Or,
Self::Xor => BinaryOp::Xor,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssignKind {
Normal,
Hidden,
Provide,
ProvideHidden,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Assert {
pub expr: Expr,
pub message: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SectionsCommand {
pub span: Span,
pub kind: SectionsCommandKind,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SectionsCommandKind {
Assignment(Assignment),
Entry(Vec<u8>),
Assert(Assert),
OutputSection(Box<OutputSection>),
Overlay(Box<Overlay>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct OutputSection {
pub name: Vec<u8>,
pub address: Option<Expr>,
pub section_type: OutputSectionType,
pub load_address: Option<Expr>,
pub align: Option<Expr>,
pub align_with_input: bool,
pub subalign: Option<Expr>,
pub constraint: SectionConstraint,
pub commands: Vec<OutputSectionCommand>,
pub region: Option<Vec<u8>>,
pub load_region: Option<Vec<u8>>,
pub phdrs: Vec<Vec<u8>>,
pub fill: Option<Fill>,
}
impl OutputSection {
#[must_use]
pub fn is_discard(&self) -> bool {
self.name == b"/DISCARD/"
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub enum OutputSectionType {
#[default]
Normal,
NoLoad,
DSect,
Copy,
Info,
Overlay,
ReadOnly,
Type(Expr),
ReadOnlyType(Expr),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum SectionConstraint {
#[default]
None,
OnlyIfRo,
OnlyIfRw,
Special,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Fill {
pub expr: Expr,
pub hex_digits: Option<Vec<u8>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Overlay {
pub address: Option<Expr>,
pub no_cross_refs: bool,
pub load_address: Option<Expr>,
pub subalign: Option<Expr>,
pub sections: Vec<OverlaySection>,
pub region: Option<Vec<u8>>,
pub load_region: Option<Vec<u8>>,
pub phdrs: Vec<Vec<u8>>,
pub fill: Option<Fill>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct OverlaySection {
pub span: Span,
pub name: Vec<u8>,
pub commands: Vec<OutputSectionCommand>,
pub phdrs: Vec<Vec<u8>>,
pub fill: Option<Fill>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct OutputSectionCommand {
pub span: Span,
pub kind: OutputSectionCommandKind,
}
#[derive(Clone, Debug, PartialEq)]
pub enum OutputSectionCommandKind {
Assignment(Assignment),
Input(InputSectionDescription),
Data {
size: DataSize,
expr: Expr,
},
Fill(Fill),
Asciz(Vec<u8>),
LinkerVersion,
CreateObjectSymbols,
Constructors {
sorted: bool,
},
Assert(Assert),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DataSize {
Byte,
Short,
Long,
Quad,
SQuad,
}
impl DataSize {
#[must_use]
pub fn bytes(self) -> u64 {
match self {
Self::Byte => 1,
Self::Short => 2,
Self::Long => 4,
Self::Quad | Self::SQuad => 8,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct InputSectionDescription {
pub keep: bool,
pub flags: Vec<SectionFlag>,
pub file: FileSpec,
pub sections: Option<Vec<SectionSpec>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SectionFlag {
pub name: Vec<u8>,
pub negated: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FileSpec {
pub pattern: Pattern,
pub exclude: Vec<Pattern>,
pub sort: SortMode,
pub reverse: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SectionSpec {
pub pattern: Pattern,
pub exclude_files: Vec<Pattern>,
pub sort: SortMode,
pub reverse: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum SortMode {
#[default]
None,
Name,
Alignment,
NameAlignment,
AlignmentName,
InitPriority,
NoSort,
}
#[derive(Clone, Debug, PartialEq)]
pub struct MemoryRegion {
pub span: Span,
pub name: Vec<u8>,
pub attributes: MemoryAttributes,
pub origin: Expr,
pub length: Expr,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct MemoryAttributes {
pub flags: u8,
pub not_flags: u8,
}
impl MemoryAttributes {
pub const READ_ONLY: u8 = 1;
pub const WRITE: u8 = 2;
pub const EXEC: u8 = 4;
pub const ALLOC: u8 = 8;
pub const LOAD: u8 = 16;
}
#[derive(Clone, Debug, PartialEq)]
pub struct Phdr {
pub span: Span,
pub name: Vec<u8>,
pub phdr_type: Expr,
pub filehdr: bool,
pub phdrs: bool,
pub at: Option<Expr>,
pub flags: Option<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct VersionNode {
pub span: Span,
pub name: Option<Vec<u8>>,
pub globals: Vec<VersionPattern>,
pub locals: Vec<VersionPattern>,
pub dependencies: Vec<Vec<u8>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VersionPattern {
pub pattern: Vec<u8>,
pub language: Option<Vec<u8>>,
pub literal: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
Number(u64),
Symbol(Vec<u8>),
Dot,
Unary(UnaryOp, Box<Expr>),
Binary(BinaryOp, Box<Expr>, Box<Expr>),
Conditional(Box<Expr>, Box<Expr>, Box<Expr>),
Absolute(Box<Expr>),
Addr(Vec<u8>),
Align(Box<Expr>),
AlignExpr(Box<Expr>, Box<Expr>),
AlignOf(Vec<u8>),
Block(Box<Expr>),
DataSegmentAlign(Box<Expr>, Box<Expr>),
DataSegmentEnd(Box<Expr>),
DataSegmentRelroEnd(Box<Expr>, Box<Expr>),
Defined(Vec<u8>),
Length(Vec<u8>),
LoadAddr(Vec<u8>),
Log2Ceil(Box<Expr>),
Max(Box<Expr>, Box<Expr>),
Min(Box<Expr>, Box<Expr>),
Next(Box<Expr>),
Origin(Vec<u8>),
SegmentStart(Vec<u8>, Box<Expr>),
SizeOf(Vec<u8>),
SizeOfHeaders,
Constant(Vec<u8>),
Assert(Box<Expr>, Vec<u8>),
}
impl Expr {
pub fn for_each_symbol(&self, f: &mut dyn FnMut(&[u8])) {
match self {
Self::Symbol(name) | Self::Defined(name) => f(name),
Self::Number(_)
| Self::Dot
| Self::Addr(_)
| Self::AlignOf(_)
| Self::Length(_)
| Self::LoadAddr(_)
| Self::Origin(_)
| Self::SizeOf(_)
| Self::SizeOfHeaders
| Self::Constant(_) => {}
Self::Unary(_, a)
| Self::Absolute(a)
| Self::Align(a)
| Self::Block(a)
| Self::DataSegmentEnd(a)
| Self::Log2Ceil(a)
| Self::Next(a)
| Self::SegmentStart(_, a)
| Self::Assert(a, _) => a.for_each_symbol(f),
Self::Binary(_, a, b)
| Self::AlignExpr(a, b)
| Self::DataSegmentAlign(a, b)
| Self::DataSegmentRelroEnd(a, b)
| Self::Max(a, b)
| Self::Min(a, b) => {
a.for_each_symbol(f);
b.for_each_symbol(f);
}
Self::Conditional(c, a, b) => {
c.for_each_symbol(f);
a.for_each_symbol(f);
b.for_each_symbol(f);
}
}
}
pub fn for_each_value_symbol(&self, f: &mut dyn FnMut(&[u8])) {
match self {
Self::Symbol(name) => f(name),
Self::Unary(_, a)
| Self::Absolute(a)
| Self::Align(a)
| Self::Block(a)
| Self::DataSegmentEnd(a)
| Self::Log2Ceil(a)
| Self::Next(a)
| Self::SegmentStart(_, a)
| Self::Assert(a, _) => a.for_each_value_symbol(f),
Self::Binary(_, a, b)
| Self::AlignExpr(a, b)
| Self::DataSegmentAlign(a, b)
| Self::DataSegmentRelroEnd(a, b)
| Self::Max(a, b)
| Self::Min(a, b) => {
a.for_each_value_symbol(f);
b.for_each_value_symbol(f);
}
Self::Conditional(c, a, b) => {
c.for_each_value_symbol(f);
a.for_each_value_symbol(f);
b.for_each_value_symbol(f);
}
_ => {}
}
}
#[must_use]
pub fn type_source(&self) -> Option<&[u8]> {
fn walk<'e>(expr: &'e Expr, found: &mut Option<&'e [u8]>, count: &mut u32) {
match expr {
Expr::Symbol(name) => {
*count = count.saturating_add(1);
*found = Some(name);
}
Expr::Conditional(_, a, b) => {
walk(a, found, count);
walk(b, found, count);
}
Expr::Unary(_, a)
| Expr::Absolute(a)
| Expr::Align(a)
| Expr::Block(a)
| Expr::DataSegmentEnd(a)
| Expr::Log2Ceil(a)
| Expr::Next(a)
| Expr::SegmentStart(_, a)
| Expr::Assert(a, _) => walk(a, found, count),
Expr::Binary(_, a, b)
| Expr::AlignExpr(a, b)
| Expr::DataSegmentAlign(a, b)
| Expr::DataSegmentRelroEnd(a, b)
| Expr::Max(a, b)
| Expr::Min(a, b) => {
walk(a, found, count);
walk(b, found, count);
}
_ => {}
}
}
let mut found = None;
let mut count = 0u32;
walk(self, &mut found, &mut count);
found.filter(|_| count == 1)
}
#[must_use]
pub fn uses_dot(&self) -> bool {
match self {
Self::Dot | Self::Align(_) | Self::Block(_) | Self::Next(_) => true,
Self::DataSegmentAlign(..) => true,
Self::Number(_)
| Self::Symbol(_)
| Self::Defined(_)
| Self::Addr(_)
| Self::AlignOf(_)
| Self::Length(_)
| Self::LoadAddr(_)
| Self::Origin(_)
| Self::SizeOf(_)
| Self::SizeOfHeaders
| Self::Constant(_) => false,
Self::Unary(_, a)
| Self::Absolute(a)
| Self::DataSegmentEnd(a)
| Self::Log2Ceil(a)
| Self::SegmentStart(_, a)
| Self::Assert(a, _) => a.uses_dot(),
Self::Binary(_, a, b)
| Self::AlignExpr(a, b)
| Self::DataSegmentRelroEnd(a, b)
| Self::Max(a, b)
| Self::Min(a, b) => a.uses_dot() || b.uses_dot(),
Self::Conditional(c, a, b) => c.uses_dot() || a.uses_dot() || b.uses_dot(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryOp {
Neg,
Not,
BitNot,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinaryOp {
Mul,
Div,
Rem,
Add,
Sub,
Shl,
Shr,
Lt,
Gt,
Le,
Ge,
Eq,
Ne,
And,
Xor,
Or,
LogicalAnd,
LogicalOr,
}
impl InputSectionDescription {
#[must_use]
pub fn matches(&self, file: &[u8], archive: Option<&[u8]>, section: &[u8]) -> Option<usize> {
if !self.file.matches(file, archive) {
return None;
}
let Some(sections) = &self.sections else {
return Some(0);
};
sections.iter().position(|spec| {
spec.pattern.matches(section)
&& !spec
.exclude_files
.iter()
.any(|p| super::pattern::file_matches(p, file, archive, true))
})
}
}
impl FileSpec {
#[must_use]
pub fn matches(&self, file: &[u8], archive: Option<&[u8]>) -> bool {
super::pattern::file_matches(&self.pattern, file, archive, false)
&& !self
.exclude
.iter()
.any(|p| super::pattern::file_matches(p, file, archive, true))
}
}