Skip to content

Commit

Permalink
Add list of symbol assignments (a.k.a. undefined_syms) to the generat…
Browse files Browse the repository at this point in the history
…ed linker script

Closes #59
  • Loading branch information
AngheloAlf committed Aug 11, 2024
1 parent 535fc89 commit 7b63389
Show file tree
Hide file tree
Showing 20 changed files with 1,694 additions and 23 deletions.
4 changes: 2 additions & 2 deletions docs/file_format/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
The input file format is composed by two required top-level attributes, the
[`settings`](settings.md) attribute and the [`segments`](segments.md)
attribute. Other optional top-level attributes may be specified, like
[vram_classes](vram_classes.md). Check their specific documents for in-deep
explanations.
[vram_classes](vram_classes.md) or [symbol_assignments](symbol_assignments.md).
Check their specific documents for in-deep explanations.

## Example

Expand Down
74 changes: 74 additions & 0 deletions docs/file_format/symbol_assignments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Symbols assignments

A "symbol assignment" entry describes extra symbol assignments to define on the
generated linker script.

These are usually used for defining "undefined symbols", but this system also
allows for more complex expressions.

Symbols defined in this way do not have a corresponding elf section assigned to
them, in other words they look like `*ABS*` symbols on the linked elf.

Every attribute listed is optional unless explicitly stated.

## Table of contents

- [Symbols assignments](#symbols-assignments)
- [Table of contents](#table-of-contents)
- [`name`](#name)
- [Example](#example)
- [Valid values](#valid-values)
- [`value`](#value)
- [Example](#example-1)
- [Valid values](#valid-values-1)
- [`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 declared.

### Example

```yaml
symbol_assignments:
- name: osMemSize
value: 0x80000318
```
### Valid values
Non empty string.
TODO: Impose rules for valid names?
## `value`

This field is **required**.

The value or expression to assign to this symbol.

Usually raw addresses are used as values for a given symbol, but more complex
expressions are allowed too.
See the GNU LD documentation for [Expressions in Linker Scripts](https://sourceware.org/binutils/docs/ld/Expressions.html)
for documentation on what is allowed on those expressions.

### Example

```yaml
symbol_assignments:
- name: _gp
value: boot_SCOMMON_START + 0x7FF0
```

### Valid values

Non empty string.

## `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.
6 changes: 6 additions & 0 deletions slinky-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ fn main() {
let mut writer = slinky::PartialLinkerWriter::new(&document, &rs);

writer.add_all_segments(&document.segments).expect("");
writer
.add_all_undefined_syms(&document.symbol_assignments)
.expect("???");

let output_path = cli
.output
Expand All @@ -94,6 +97,9 @@ fn main() {
let mut writer = slinky::LinkerWriter::new(&document, &rs);

writer.add_all_segments(&document.segments).expect("ah?");
writer
.add_all_undefined_syms(&document.symbol_assignments)
.expect("???");

if let Some(output_path) = cli.output {
writer
Expand Down
22 changes: 21 additions & 1 deletion slinky/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use serde::Deserialize;

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

#[derive(PartialEq, Debug)]
Expand All @@ -17,6 +18,8 @@ pub struct Document {
pub vram_classes: Vec<VramClass>,

pub segments: Vec<Segment>,

pub symbol_assignments: Vec<SymbolAssignment>,
}

impl Document {
Expand Down Expand Up @@ -53,6 +56,9 @@ pub(crate) struct DocumentSerial {
pub vram_classes: AbsentNullable<Vec<VramClassSerial>>,

pub segments: Vec<SegmentSerial>,

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

impl DocumentSerial {
Expand Down Expand Up @@ -83,10 +89,24 @@ impl DocumentSerial {
segments.push(seg.unserialize(&settings)?);
}

let mut undefined_symbols = Vec::new();
match self
.symbol_assignments
.get_non_null_no_default("undefined_symbols")?
{
None => (),
Some(v) => {
for c in v {
undefined_symbols.push(c.unserialize(&settings)?);
}
}
}

Ok(Document {
settings,
vram_classes,
segments,
symbol_assignments: undefined_symbols,
})
}
}
2 changes: 2 additions & 0 deletions slinky/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod settings;
mod file_info;
mod file_kind;
mod segment;
mod symbol_assignment;

mod vram_class;

Expand All @@ -33,6 +34,7 @@ pub use settings::Settings;
pub use file_info::FileInfo;
pub use file_kind::FileKind;
pub use segment::Segment;
pub use symbol_assignment::SymbolAssignment;

pub use vram_class::VramClass;

Expand Down
78 changes: 65 additions & 13 deletions slinky/src/linker_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::PathBuf;
use std::{io::Write, path::Path};

use crate::{file_kind::FileKind, SlinkyError};
use crate::{utils, version, Document, RuntimeSettings, VramClass};
use crate::{utils, version, Document, RuntimeSettings, SymbolAssignment, VramClass};
use crate::{FileInfo, Segment};

use crate::script_buffer::ScriptBuffer;
Expand Down Expand Up @@ -90,6 +90,25 @@ impl<'a> LinkerWriter<'a> {

Ok(())
}

pub fn add_all_undefined_syms(
&mut self,
undefined_syms: &[SymbolAssignment],
) -> Result<(), SlinkyError> {
if undefined_syms.is_empty() {
return Ok(());
}

self.begin_undefined_syms()?;

for undefined_sym in undefined_syms {
self.add_undefined_sym(undefined_sym)?;
}

self.end_undefined_syms()?;

Ok(())
}
}

impl LinkerWriter<'_> {
Expand Down Expand Up @@ -360,7 +379,7 @@ impl LinkerWriter<'_> {
continue;
}

self.buffer.write_symbol(
self.buffer.write_linker_symbol(
&style.vram_class_size(vram_class_name),
&format!(
"{} - {}",
Expand Down Expand Up @@ -469,11 +488,13 @@ impl LinkerWriter<'_> {

if let Some(fixed_vram) = vram_class.fixed_vram {
self.buffer
.write_symbol(&vram_class_sym, &format!("0x{:08X}", fixed_vram));
.write_linker_symbol(&vram_class_sym, &format!("0x{:08X}", fixed_vram));
} else if let Some(fixed_symbol) = &vram_class.fixed_symbol {
self.buffer.write_symbol(&vram_class_sym, fixed_symbol);
self.buffer
.write_linker_symbol(&vram_class_sym, fixed_symbol);
} else {
self.buffer.write_symbol(&vram_class_sym, "0x00000000");
self.buffer
.write_linker_symbol(&vram_class_sym, "0x00000000");
for other_class_name in &vram_class.follows_classes {
self.buffer.write_symbol_max_self(
&vram_class_sym,
Expand All @@ -482,7 +503,7 @@ impl LinkerWriter<'_> {
}
}
self.buffer
.write_symbol(&style.vram_class_end(vram_class_name), "0x00000000");
.write_linker_symbol(&style.vram_class_end(vram_class_name), "0x00000000");

self.buffer.write_empty_line();

Expand All @@ -496,9 +517,9 @@ impl LinkerWriter<'_> {
}

self.buffer
.write_symbol(&main_seg_rom_sym_start, "__romPos");
.write_linker_symbol(&main_seg_rom_sym_start, "__romPos");
self.buffer
.write_symbol(&main_seg_sym_start, &format!("ADDR(.{})", segment.name));
.write_linker_symbol(&main_seg_sym_start, &format!("ADDR(.{})", segment.name));

// Emit alloc segment
self.write_segment(segment, &segment.alloc_sections, false)?;
Expand Down Expand Up @@ -572,15 +593,46 @@ impl LinkerWriter<'_> {

Ok(())
}

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

Ok(())
}

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

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

self.buffer
.write_symbol_assignment(&undefined_sym.name, &undefined_sym.value);

Ok(())
}
}

// internal functions
impl LinkerWriter<'_> {
fn write_sym_end_size(&mut self, start: &str, end: &str, size: &str, value: &str) {
self.buffer.write_symbol(end, value);
self.buffer.write_linker_symbol(end, value);

self.buffer
.write_symbol(size, &format!("ABSOLUTE({} - {})", end, start));
.write_linker_symbol(size, &format!("ABSOLUTE({} - {})", end, start));
}

fn write_sections_kind_start(&mut self, segment: &Segment, noload: bool) {
Expand All @@ -592,7 +644,7 @@ impl LinkerWriter<'_> {

let seg_sym_start = style.segment_vram_start(&seg_sym);

self.buffer.write_symbol(&seg_sym_start, ".");
self.buffer.write_linker_symbol(&seg_sym_start, ".");

self.buffer.write_empty_line();
}
Expand Down Expand Up @@ -628,7 +680,7 @@ impl LinkerWriter<'_> {

let section_start_sym = style.segment_section_start(&segment.name, section);

self.buffer.write_symbol(&section_start_sym, ".");
self.buffer.write_linker_symbol(&section_start_sym, ".");
}
}

Expand Down Expand Up @@ -754,7 +806,7 @@ impl LinkerWriter<'_> {
FileKind::LinkerOffset => {
if file.section == section {
self.buffer
.write_symbol(&style.linker_offset(&file.linker_offset_name), ".");
.write_linker_symbol(&style.linker_offset(&file.linker_offset_name), ".");
}
}
FileKind::Group => {
Expand Down
11 changes: 10 additions & 1 deletion slinky/src/partial_linker_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

use std::path::{Path, PathBuf};

use crate::{Document, FileInfo, LinkerWriter, RuntimeSettings, Segment, SlinkyError};
use crate::{
Document, FileInfo, LinkerWriter, RuntimeSettings, Segment, SlinkyError, SymbolAssignment,
};

pub struct PartialLinkerWriter<'a> {
main_writer: LinkerWriter<'a>,
Expand Down Expand Up @@ -69,6 +71,13 @@ impl<'a> PartialLinkerWriter<'a> {
Ok(())
}

pub fn add_all_undefined_syms(
&mut self,
undefined_syms: &[SymbolAssignment],
) -> Result<(), SlinkyError> {
self.main_writer.add_all_undefined_syms(undefined_syms)
}

pub fn export_linker_script_to_files(&self, path: &Path) -> Result<(), SlinkyError> {
self.main_writer.export_linker_script_to_file(path)?;

Expand Down
15 changes: 13 additions & 2 deletions slinky/src/script_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,20 @@ impl ScriptBuffer {
self.writeln("}");
}

pub fn write_symbol(&mut self, symbol: &str, value: &str) {
pub fn write_linker_symbol(&mut self, symbol: &str, value: &str) {
// TODO: check `symbol` is a valid C identifier

self.writeln(&format!("{} = {};", symbol, value));
self.write_symbol_assignment(symbol, value);

self.linker_symbols.insert(symbol.to_string());
}

pub fn write_symbol_assignment(&mut self, symbol: &str, value: &str) {
// TODO: check `symbol` is a valid C identifier

self.writeln(&format!("{} = {};", symbol, value));
}

pub fn align_symbol(&mut self, symbol: &str, align_value: u32) {
self.writeln(&format!(
"{} = ALIGN({}, 0x{:X});",
Expand Down Expand Up @@ -82,4 +88,9 @@ impl ScriptBuffer {
pub fn get_linker_symbols(&self) -> &indexmap::IndexSet<String> {
&self.linker_symbols
}

#[must_use]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
}
Loading

0 comments on commit 7b63389

Please sign in to comment.