Skip to content

Commit

Permalink
List of required symbols.
Browse files Browse the repository at this point in the history
symbols that should be forced into the linking process, even if they aren't referenced by the linked code.

Closes #56
  • Loading branch information
AngheloAlf committed Aug 13, 2024
1 parent 0b248df commit 46b191a
Show file tree
Hide file tree
Showing 19 changed files with 682 additions and 37 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allows to define symbols directly on the generated linker script.
- Symbols created this way can be defined with raw addresses, reference other
symbols or more complex expressions.
- These generated symbols do not have a corresponding section (defined as
`ABS` symbols on the elf).
- If a symbol assignment is emitted or not can be controlled with the same
conditional inclussion/exclussion mechanism used by the custom options.
- These definitions can be wrapped in `PROVIDE`, `HIDDEN` or `PROVIDE_HIDDEN`.
- Add new top-level attribute for the file format: `required_symbols`.
- Allows to specify a list of symbols that should be forced to be linked into
the build, even if they are not refenced by any other linked code.
- Useful for making sure a symbol from an static library is being linked.
- If a symbol assignment is emitted or not can be controlled with the same
conditional inclussion/exclussion mechanism used by the custom options.

### Changed

- Produce an error if the user specifies an empty conditional
inclusion/exclusion for any entry.

## [0.2.5] - 2024-07-17

Expand Down
45 changes: 45 additions & 0 deletions docs/file_format/required_symbols.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Required symbols

This entry is used to tell the linker that a symbol should be linked into the
build, even if it isn't refenced in any code.

This behavior is usually used when linking static libraries (`.a`) into the
build, since the default linking behavior for those archives is to only pull
symbols that are refenced on the rest of the build.

Every attribute listed is optional unless explicitly stated.

## Table of contents

- [Required symbols](#required-symbols)
- [Table of contents](#table-of-contents)
- [`name`](#name)
- [Example](#example)
- [Valid values](#valid-values)
- [`include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`](#include_if_any-include_if_all-exclude_if_any-and-exclude_if_all)

## `name`

This field is **required**.

The name of the corresponding symbol to be required.

### Example

```yaml
required_symbols:
- name: guMtxCatL
```
### Valid values
Non empty string.
TODO: Impose rules for valid names?
## `include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`

These fields allow to conditionally include or exclude a given segment depending
on the current [custom options](custom_options.md).

Their syntax is the same as their [`file`](file.md#include_if_any) counterparts.
16 changes: 13 additions & 3 deletions slinky/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use std::{fs, path::Path};
use serde::Deserialize;

use crate::{
absent_nullable::AbsentNullable, segment::SegmentSerial, settings::SettingsSerial,
symbol_assignment::SymbolAssignmentSerial, traits::Serial, vram_class::VramClassSerial,
Segment, Settings, SlinkyError, SymbolAssignment, VramClass,
absent_nullable::AbsentNullable, required_symbol::RequiredSymbolSerial, segment::SegmentSerial,
settings::SettingsSerial, symbol_assignment::SymbolAssignmentSerial, traits::Serial,
vram_class::VramClassSerial, RequiredSymbol, Segment, Settings, SlinkyError, SymbolAssignment,
VramClass,
};

#[derive(PartialEq, Debug)]
Expand All @@ -20,6 +21,7 @@ pub struct Document {
pub segments: Vec<Segment>,

pub symbol_assignments: Vec<SymbolAssignment>,
pub required_symbols: Vec<RequiredSymbol>,
}

impl Document {
Expand Down Expand Up @@ -59,6 +61,8 @@ pub(crate) struct DocumentSerial {

#[serde(default)]
pub symbol_assignments: AbsentNullable<Vec<SymbolAssignmentSerial>>,
#[serde(default)]
pub required_symbols: AbsentNullable<Vec<RequiredSymbolSerial>>,
}

impl DocumentSerial {
Expand Down Expand Up @@ -86,11 +90,17 @@ impl DocumentSerial {
.get_non_null("symbol_assignments", Vec::new)?
.unserialize(&settings)?;

let required_symbols = self
.required_symbols
.get_non_null("required_symbols", Vec::new)?
.unserialize(&settings)?;

Ok(Document {
settings,
vram_classes,
segments,
symbol_assignments,
required_symbols,
})
}
}
19 changes: 19 additions & 0 deletions slinky/src/escaped_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ impl From<String> for EscapedPath {
}
}

impl<P: AsRef<Path>> Extend<P> for EscapedPath {
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
self.0.extend(iter);
}
}

impl<'a> IntoIterator for &'a EscapedPath {
type Item = &'a std::ffi::OsStr;
type IntoIter = std::path::Iter<'a>;

fn into_iter(self) -> std::path::Iter<'a> {
self.0.iter()
}
}

impl Default for EscapedPath {
fn default() -> Self {
Self::new()
Expand All @@ -41,6 +56,10 @@ impl EscapedPath {
Self(PathBuf::new())
}

pub fn is_empty(&self) -> bool {
self.0.as_os_str().is_empty()
}

pub fn push(&mut self, path: EscapedPath) {
self.0.push(path.0)
}
Expand Down
2 changes: 2 additions & 0 deletions slinky/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod settings;

mod file_info;
mod file_kind;
mod required_symbol;
mod segment;
mod symbol_assignment;

Expand All @@ -36,6 +37,7 @@ pub use settings::Settings;

pub use file_info::FileInfo;
pub use file_kind::FileKind;
pub use required_symbol::RequiredSymbol;
pub use segment::Segment;
pub use symbol_assignment::SymbolAssignment;

Expand Down
54 changes: 50 additions & 4 deletions slinky/src/linker_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
use std::io::Write;

use crate::{
utils, version, Document, EscapedPath, FileInfo, FileKind, RuntimeSettings, ScriptExporter,
ScriptGenerator, ScriptImporter, Segment, SlinkyError, SymbolAssignment, VramClass,
utils, version, Document, EscapedPath, FileInfo, FileKind, RequiredSymbol, RuntimeSettings,
ScriptExporter, ScriptGenerator, ScriptImporter, Segment, SlinkyError, SymbolAssignment,
VramClass,
};

use crate::script_buffer::ScriptBuffer;
Expand Down Expand Up @@ -102,15 +103,30 @@ impl ScriptImporter for LinkerWriter<'_> {
}

self.begin_symbol_assignments()?;

for symbol_assignment in symbol_assignments {
self.add_symbol_assignment(symbol_assignment)?;
}

self.end_symbol_assignments()?;

Ok(())
}

fn add_all_required_symbols(
&mut self,
required_symbols: &[RequiredSymbol],
) -> Result<(), SlinkyError> {
if required_symbols.is_empty() {
return Ok(());
}

self.begin_required_symbols()?;
for required_symbol in required_symbols {
self.add_required_symbol(required_symbol)?;
}
self.end_required_symbols()?;

Ok(())
}
}

impl ScriptExporter for LinkerWriter<'_> {
Expand Down Expand Up @@ -626,6 +642,36 @@ impl LinkerWriter<'_> {

Ok(())
}

pub(crate) fn begin_required_symbols(&mut self) -> Result<(), SlinkyError> {
if !self.buffer.is_empty() {
self.buffer.write_empty_line();
}

Ok(())
}

pub(crate) fn end_required_symbols(&mut self) -> Result<(), SlinkyError> {
Ok(())
}

pub(crate) fn add_required_symbol(
&mut self,
required_symbol: &RequiredSymbol,
) -> Result<(), SlinkyError> {
if !self.rs.should_emit_entry(
&required_symbol.exclude_if_any,
&required_symbol.exclude_if_all,
&required_symbol.include_if_any,
&required_symbol.include_if_all,
) {
return Ok(());
}

self.buffer.write_required_symbol(&required_symbol.name);

Ok(())
}
}

// internal functions
Expand Down
65 changes: 55 additions & 10 deletions slinky/src/partial_linker_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT */

use crate::{
Document, EscapedPath, FileInfo, LinkerWriter, RuntimeSettings, ScriptExporter,
Document, EscapedPath, FileInfo, LinkerWriter, RequiredSymbol, RuntimeSettings, ScriptExporter,
ScriptGenerator, ScriptImporter, Segment, SlinkyError, SymbolAssignment,
};

Expand Down Expand Up @@ -30,6 +30,15 @@ impl<'a> PartialLinkerWriter<'a> {

impl ScriptImporter for PartialLinkerWriter<'_> {
fn add_all_segments(&mut self, segments: &[Segment]) -> Result<(), SlinkyError> {
let partial_build_segments_folder = match &self.d.settings.partial_build_segments_folder {
Some(p) => p,
None => {
return Err(SlinkyError::MissingRequiredField {
name: "partial_build_segments_folder".to_string(),
})
}
};

self.main_writer.begin_sections()?;

self.partial_writers.reserve(segments.len());
Expand All @@ -53,7 +62,7 @@ impl ScriptImporter for PartialLinkerWriter<'_> {
self.partial_writers
.push((partial_writer, segment.name.clone()));

let mut p = self.d.settings.partial_build_segments_folder.clone();
let mut p = partial_build_segments_folder.clone();

p.push(&format!("{}.o", segment.name));

Expand All @@ -73,14 +82,31 @@ impl ScriptImporter for PartialLinkerWriter<'_> {
self.main_writer
.add_all_symbol_assignments(symbol_assignments)
}

fn add_all_required_symbols(
&mut self,
required_symbols: &[RequiredSymbol],
) -> Result<(), SlinkyError> {
self.main_writer.add_all_required_symbols(required_symbols)
}
}

impl ScriptExporter for PartialLinkerWriter<'_> {
fn export_linker_script_to_file(&self, path: &EscapedPath) -> Result<(), SlinkyError> {
let partial_scripts_folder =
match self.d.settings.partial_scripts_folder_escaped(self.rs)? {
Some(p) => p,
None => {
return Err(SlinkyError::MissingRequiredField {
name: "partial_scripts_folder".to_string(),
})
}
};

self.main_writer.export_linker_script_to_file(path)?;

for (partial, name) in &self.partial_writers {
let mut p = self.d.settings.partial_scripts_folder_escaped(self.rs)?;
let mut p = partial_scripts_folder.clone();

p.push(EscapedPath::from(format!("{}.ld", name)));

Expand All @@ -103,20 +129,39 @@ impl ScriptExporter for PartialLinkerWriter<'_> {
}

fn save_other_files(&self) -> Result<(), SlinkyError> {
let base_path = self.d.settings.base_path_escaped(self.rs)?;
let partial_build_segments_folder = match self
.d
.settings
.partial_build_segments_folder_escaped(self.rs)?
{
Some(p) => p,
None => {
return Err(SlinkyError::MissingRequiredField {
name: "partial_build_segments_folder".to_string(),
})
}
};
let partial_scripts_folder =
match self.d.settings.partial_scripts_folder_escaped(self.rs)? {
Some(p) => p,
None => {
return Err(SlinkyError::MissingRequiredField {
name: "partial_scripts_folder".to_string(),
})
}
};

self.main_writer.save_other_files()?;

if self.d.settings.d_path.is_some() {
for (partial, name) in &self.partial_writers {
let mut target_path = self.d.settings.base_path_escaped(self.rs)?;
let mut target_path = base_path.clone();

target_path.push(
self.d
.settings
.partial_build_segments_folder_escaped(self.rs)?,
);
target_path.extend(&partial_build_segments_folder);
target_path.push(EscapedPath::from(format!("{}.o", name)));

let mut d_path = self.d.settings.partial_scripts_folder_escaped(self.rs)?;
let mut d_path = partial_scripts_folder.clone();

d_path.push(EscapedPath::from(format!("{}.d", name)));

Expand Down
Loading

0 comments on commit 46b191a

Please sign in to comment.