From 46b191a30002642483412fa503731b641b428723 Mon Sep 17 00:00:00 2001 From: angie Date: Tue, 13 Aug 2024 08:45:50 -0400 Subject: [PATCH] List of required symbols. symbols that should be forced into the linking process, even if they aren't referenced by the linked code. Closes #56 --- CHANGELOG.md | 13 +++ docs/file_format/required_symbols.md | 45 ++++++++++ slinky/src/document.rs | 16 +++- slinky/src/escaped_path.rs | 19 +++++ slinky/src/lib.rs | 2 + slinky/src/linker_writer.rs | 54 +++++++++++- slinky/src/partial_linker_writer.rs | 65 ++++++++++++--- slinky/src/required_symbol.rs | 66 +++++++++++++++ slinky/src/script_buffer.rs | 12 +++ slinky/src/settings.rs | 37 +++++---- slinky/src/traits.rs | 12 ++- slinky/tests/integration_test.rs | 9 +- tests/partial_linking/required_syms.h | 41 ++++++++++ tests/partial_linking/required_syms.ld | 91 +++++++++++++++++++++ tests/partial_linking/required_syms.yaml | 21 +++++ tests/partial_linking/required_syms/boot.ld | 66 +++++++++++++++ tests/test_cases/required_syms.h | 41 ++++++++++ tests/test_cases/required_syms.ld | 91 +++++++++++++++++++++ tests/test_cases/required_syms.yaml | 18 ++++ 19 files changed, 682 insertions(+), 37 deletions(-) create mode 100644 docs/file_format/required_symbols.md create mode 100644 slinky/src/required_symbol.rs create mode 100644 tests/partial_linking/required_syms.h create mode 100644 tests/partial_linking/required_syms.ld create mode 100644 tests/partial_linking/required_syms.yaml create mode 100644 tests/partial_linking/required_syms/boot.ld create mode 100644 tests/test_cases/required_syms.h create mode 100644 tests/test_cases/required_syms.ld create mode 100644 tests/test_cases/required_syms.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fdf300..1de2031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/docs/file_format/required_symbols.md b/docs/file_format/required_symbols.md new file mode 100644 index 0000000..2b847f3 --- /dev/null +++ b/docs/file_format/required_symbols.md @@ -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. diff --git a/slinky/src/document.rs b/slinky/src/document.rs index e0e19bb..a965c88 100644 --- a/slinky/src/document.rs +++ b/slinky/src/document.rs @@ -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)] @@ -20,6 +21,7 @@ pub struct Document { pub segments: Vec, pub symbol_assignments: Vec, + pub required_symbols: Vec, } impl Document { @@ -59,6 +61,8 @@ pub(crate) struct DocumentSerial { #[serde(default)] pub symbol_assignments: AbsentNullable>, + #[serde(default)] + pub required_symbols: AbsentNullable>, } impl DocumentSerial { @@ -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, }) } } diff --git a/slinky/src/escaped_path.rs b/slinky/src/escaped_path.rs index 3aa72b3..ff309a3 100644 --- a/slinky/src/escaped_path.rs +++ b/slinky/src/escaped_path.rs @@ -30,6 +30,21 @@ impl From for EscapedPath { } } +impl> Extend

for EscapedPath { + fn extend>(&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() @@ -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) } diff --git a/slinky/src/lib.rs b/slinky/src/lib.rs index 722ee67..e650319 100644 --- a/slinky/src/lib.rs +++ b/slinky/src/lib.rs @@ -12,6 +12,7 @@ mod settings; mod file_info; mod file_kind; +mod required_symbol; mod segment; mod symbol_assignment; @@ -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; diff --git a/slinky/src/linker_writer.rs b/slinky/src/linker_writer.rs index 9f5cd8f..010a102 100644 --- a/slinky/src/linker_writer.rs +++ b/slinky/src/linker_writer.rs @@ -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; @@ -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<'_> { @@ -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 diff --git a/slinky/src/partial_linker_writer.rs b/slinky/src/partial_linker_writer.rs index 81f17d8..6638111 100644 --- a/slinky/src/partial_linker_writer.rs +++ b/slinky/src/partial_linker_writer.rs @@ -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, }; @@ -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()); @@ -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)); @@ -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))); @@ -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))); diff --git a/slinky/src/required_symbol.rs b/slinky/src/required_symbol.rs new file mode 100644 index 0000000..d296be8 --- /dev/null +++ b/slinky/src/required_symbol.rs @@ -0,0 +1,66 @@ +/* SPDX-FileCopyrightText: © 2024 decompals */ +/* SPDX-License-Identifier: MIT */ + +use serde::Deserialize; + +use crate::{absent_nullable::AbsentNullable, traits::Serial, Settings, SlinkyError}; + +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct RequiredSymbol { + /// Name of the symbol + pub name: String, + + pub include_if_any: Vec<(String, String)>, + pub include_if_all: Vec<(String, String)>, + pub exclude_if_any: Vec<(String, String)>, + pub exclude_if_all: Vec<(String, String)>, +} + +#[derive(Deserialize, PartialEq, Debug)] +#[serde(deny_unknown_fields)] +pub(crate) struct RequiredSymbolSerial { + pub name: String, + + #[serde(default)] + pub include_if_any: AbsentNullable>, + #[serde(default)] + pub include_if_all: AbsentNullable>, + #[serde(default)] + pub exclude_if_any: AbsentNullable>, + #[serde(default)] + pub exclude_if_all: AbsentNullable>, +} + +impl Serial for RequiredSymbolSerial { + type Output = RequiredSymbol; + + fn unserialize(self, _settings: &Settings) -> Result { + if self.name.is_empty() { + return Err(SlinkyError::EmptyValue { + name: "name".to_string(), + }); + } + let name = self.name; + + let include_if_any = self + .include_if_any + .get_non_null_not_empty("include_if_any", Vec::new)?; + let include_if_all = self + .include_if_all + .get_non_null_not_empty("include_if_all", Vec::new)?; + let exclude_if_any = self + .exclude_if_any + .get_non_null_not_empty("exclude_if_any", Vec::new)?; + let exclude_if_all = self + .exclude_if_all + .get_non_null_not_empty("exclude_if_all", Vec::new)?; + + Ok(Self::Output { + name, + include_if_any, + include_if_all, + exclude_if_any, + exclude_if_all, + }) + } +} diff --git a/slinky/src/script_buffer.rs b/slinky/src/script_buffer.rs index 0201a7d..3cf37cc 100644 --- a/slinky/src/script_buffer.rs +++ b/slinky/src/script_buffer.rs @@ -81,6 +81,18 @@ impl ScriptBuffer { pub fn write_symbol_max_self(&mut self, symbol: &str, other_sym: &str) { self.writeln(&format!("{} = MAX({}, {});", symbol, symbol, other_sym)); } + + pub fn write_assert(&mut self, cond: &str, error_msg: &str) { + self.writeln(&format!("ASSERT({}, \"Error: {}\");", cond, error_msg)); + } + + pub fn write_required_symbol(&mut self, name: &str) { + self.writeln(&format!("EXTERN({});", name)); + self.write_assert( + &format!("DEFINED({})", name), + &format!("Required symbol '{}' was not linked", name), + ); + } } impl ScriptBuffer { diff --git a/slinky/src/settings.rs b/slinky/src/settings.rs index e254b40..ce66a3c 100644 --- a/slinky/src/settings.rs +++ b/slinky/src/settings.rs @@ -30,8 +30,8 @@ pub struct Settings { pub single_segment_mode: bool, - pub partial_scripts_folder: PathBuf, - pub partial_build_segments_folder: PathBuf, + pub partial_scripts_folder: Option, + pub partial_build_segments_folder: Option, // Options passed down to each segment pub alloc_sections: Vec, @@ -109,12 +109,12 @@ const fn settings_default_single_segment_mode() -> bool { false } -fn settings_default_partial_scripts_folder() -> PathBuf { - PathBuf::new() +const fn settings_default_partial_scripts_folder() -> Option { + None } -fn settings_default_partial_build_segments_folder() -> PathBuf { - PathBuf::new() +const fn settings_default_partial_build_segments_folder() -> Option { + None } fn settings_default_alloc_sections() -> Vec { @@ -249,15 +249,21 @@ impl Settings { pub fn partial_scripts_folder_escaped( &self, rs: &RuntimeSettings, - ) -> Result { - rs.escape_path(&self.partial_scripts_folder) + ) -> Result, SlinkyError> { + match &self.partial_scripts_folder { + Some(p) => Ok(Some(rs.escape_path(p)?)), + None => Ok(None), + } } pub fn partial_build_segments_folder_escaped( &self, rs: &RuntimeSettings, - ) -> Result { - rs.escape_path(&self.partial_build_segments_folder) + ) -> Result, SlinkyError> { + match &self.partial_build_segments_folder { + Some(p) => Ok(Some(rs.escape_path(p)?)), + None => Ok(None), + } } } @@ -380,14 +386,15 @@ impl SettingsSerial { .single_segment_mode .get_non_null("single_segment_mode", settings_default_single_segment_mode)?; - let partial_scripts_folder = self.partial_scripts_folder.get_non_null( + let partial_scripts_folder = self.partial_scripts_folder.get_optional_nullable( "partial_scripts_folder", settings_default_partial_scripts_folder, )?; - let partial_build_segments_folder = self.partial_build_segments_folder.get_non_null( - "partial_build_segments_folder", - settings_default_partial_build_segments_folder, - )?; + let partial_build_segments_folder = + self.partial_build_segments_folder.get_optional_nullable( + "partial_build_segments_folder", + settings_default_partial_build_segments_folder, + )?; if d_path.is_some() && target_path.is_none() { return Err(SlinkyError::MissingRequiredFieldCombo { diff --git a/slinky/src/traits.rs b/slinky/src/traits.rs index f4183e2..655994e 100644 --- a/slinky/src/traits.rs +++ b/slinky/src/traits.rs @@ -1,11 +1,13 @@ /* SPDX-FileCopyrightText: © 2024 decompals */ /* SPDX-License-Identifier: MIT */ -use crate::{Document, EscapedPath, Segment, Settings, SlinkyError, SymbolAssignment}; +use crate::{ + Document, EscapedPath, RequiredSymbol, Segment, Settings, SlinkyError, SymbolAssignment, +}; mod private { use crate::{ - file_info::FileInfoSerial, segment::SegmentSerial, + file_info::FileInfoSerial, required_symbol::RequiredSymbolSerial, segment::SegmentSerial, symbol_assignment::SymbolAssignmentSerial, vram_class::VramClassSerial, LinkerWriter, PartialLinkerWriter, }; @@ -19,6 +21,7 @@ mod private { impl Sealed for FileInfoSerial {} impl Sealed for VramClassSerial {} impl Sealed for SymbolAssignmentSerial {} + impl Sealed for RequiredSymbolSerial {} impl Sealed for Vec {} } @@ -29,10 +32,15 @@ pub trait ScriptImporter: private::Sealed { &mut self, symbol_assignments: &[SymbolAssignment], ) -> Result<(), SlinkyError>; + fn add_all_required_symbols( + &mut self, + required_symbols: &[RequiredSymbol], + ) -> Result<(), SlinkyError>; fn add_whole_document(&mut self, document: &Document) -> Result<(), SlinkyError> { self.add_all_segments(&document.segments)?; self.add_all_symbol_assignments(&document.symbol_assignments)?; + self.add_all_required_symbols(&document.required_symbols)?; Ok(()) } diff --git a/slinky/tests/integration_test.rs b/slinky/tests/integration_test.rs index 9ef16f7..f719b46 100644 --- a/slinky/tests/integration_test.rs +++ b/slinky/tests/integration_test.rs @@ -178,7 +178,8 @@ fn test_partial_linking_script_generation( document .settings .partial_scripts_folder_escaped(&rs) - .expect("Not able to escape path"), + .expect("Not able to escape path") + .unwrap(), ); p.push(&format!("{}.ld", name)); @@ -223,7 +224,8 @@ fn test_partial_linking_d_generation(#[files("../tests/partial_linking/*.d")] d_ document .settings .partial_scripts_folder_escaped(&rs) - .expect("Unable to escape path"), + .expect("Unable to escape path") + .unwrap(), ); p.push(&format!("{}.d", name)); @@ -239,7 +241,8 @@ fn test_partial_linking_d_generation(#[files("../tests/partial_linking/*.d")] d_ document .settings .partial_build_segments_folder_escaped(&rs) - .expect("Failed to escape path"), + .expect("Failed to escape path") + .unwrap(), ); partial_target.push(EscapedPath::from(format!("{}.o", name))); diff --git a/tests/partial_linking/required_syms.h b/tests/partial_linking/required_syms.h new file mode 100644 index 0000000..feb9600 --- /dev/null +++ b/tests/partial_linking/required_syms.h @@ -0,0 +1,41 @@ +#ifndef HEADER_SYMBOLS_H +#define HEADER_SYMBOLS_H + +extern char boot_ROM_START[]; +extern char boot_VRAM[]; +extern char boot_alloc_VRAM[]; +extern char boot_TEXT_START[]; +extern char boot_TEXT_END[]; +extern char boot_TEXT_SIZE[]; +extern char boot_DATA_START[]; +extern char boot_DATA_END[]; +extern char boot_DATA_SIZE[]; +extern char boot_RODATA_START[]; +extern char boot_RODATA_END[]; +extern char boot_RODATA_SIZE[]; +extern char boot_SDATA_START[]; +extern char boot_SDATA_END[]; +extern char boot_SDATA_SIZE[]; +extern char boot_alloc_VRAM_END[]; +extern char boot_alloc_VRAM_SIZE[]; +extern char boot_noload_VRAM[]; +extern char boot_SBSS_START[]; +extern char boot_SBSS_END[]; +extern char boot_SBSS_SIZE[]; +extern char boot_SCOMMON_START[]; +extern char boot_SCOMMON_END[]; +extern char boot_SCOMMON_SIZE[]; +extern char boot_BSS_START[]; +extern char boot_BSS_END[]; +extern char boot_BSS_SIZE[]; +extern char bootCOMMON_START[]; +extern char bootCOMMON_END[]; +extern char bootCOMMON_SIZE[]; +extern char boot_noload_VRAM_END[]; +extern char boot_noload_VRAM_SIZE[]; +extern char boot_VRAM_END[]; +extern char boot_VRAM_SIZE[]; +extern char boot_ROM_END[]; +extern char boot_ROM_SIZE[]; + +#endif diff --git a/tests/partial_linking/required_syms.ld b/tests/partial_linking/required_syms.ld new file mode 100644 index 0000000..f6fa916 --- /dev/null +++ b/tests/partial_linking/required_syms.ld @@ -0,0 +1,91 @@ +SECTIONS +{ + __romPos = 0x0; + + boot_ROM_START = __romPos; + boot_VRAM = ADDR(.boot); + boot_alloc_VRAM = .; + + .boot : AT(boot_ROM_START) + { + FILL(0x00000000); + boot_TEXT_START = .; + build/segments/boot.o(.text*); + boot_TEXT_END = .; + boot_TEXT_SIZE = ABSOLUTE(boot_TEXT_END - boot_TEXT_START); + + boot_DATA_START = .; + build/segments/boot.o(.data*); + boot_DATA_END = .; + boot_DATA_SIZE = ABSOLUTE(boot_DATA_END - boot_DATA_START); + + boot_RODATA_START = .; + build/segments/boot.o(.rodata*); + boot_RODATA_END = .; + boot_RODATA_SIZE = ABSOLUTE(boot_RODATA_END - boot_RODATA_START); + + boot_SDATA_START = .; + build/segments/boot.o(.sdata*); + boot_SDATA_END = .; + boot_SDATA_SIZE = ABSOLUTE(boot_SDATA_END - boot_SDATA_START); + } + + boot_alloc_VRAM_END = .; + boot_alloc_VRAM_SIZE = ABSOLUTE(boot_alloc_VRAM_END - boot_alloc_VRAM); + + boot_noload_VRAM = .; + + .boot.noload (NOLOAD) : + { + FILL(0x00000000); + boot_SBSS_START = .; + build/segments/boot.o(.sbss*); + boot_SBSS_END = .; + boot_SBSS_SIZE = ABSOLUTE(boot_SBSS_END - boot_SBSS_START); + + boot_SCOMMON_START = .; + build/segments/boot.o(.scommon*); + boot_SCOMMON_END = .; + boot_SCOMMON_SIZE = ABSOLUTE(boot_SCOMMON_END - boot_SCOMMON_START); + + boot_BSS_START = .; + build/segments/boot.o(.bss*); + boot_BSS_END = .; + boot_BSS_SIZE = ABSOLUTE(boot_BSS_END - boot_BSS_START); + + bootCOMMON_START = .; + build/segments/boot.o(COMMON*); + bootCOMMON_END = .; + bootCOMMON_SIZE = ABSOLUTE(bootCOMMON_END - bootCOMMON_START); + } + + boot_noload_VRAM_END = .; + boot_noload_VRAM_SIZE = ABSOLUTE(boot_noload_VRAM_END - boot_noload_VRAM); + + __romPos += SIZEOF(.boot); + boot_VRAM_END = .; + boot_VRAM_SIZE = ABSOLUTE(boot_VRAM_END - boot_VRAM); + boot_ROM_END = __romPos; + boot_ROM_SIZE = ABSOLUTE(boot_ROM_END - boot_ROM_START); + + .shstrtab 0 : + { + *(.shstrtab); + } + + /DISCARD/ : + { + *(.reginfo); + *(.MIPS.abiflags); + *(.MIPS.options); + *(.note.gnu.build-id); + *(.interp); + *(.eh_frame); + *(*); + } +} + +EXTERN(guMtxCatL); +ASSERT(DEFINED(guMtxCatL), "Error: Required symbol 'guMtxCatL' was not linked"); +EXTERN(__osSetCause); +ASSERT(DEFINED(__osSetCause), "Error: Required symbol '__osSetCause' was not linked"); diff --git a/tests/partial_linking/required_syms.yaml b/tests/partial_linking/required_syms.yaml new file mode 100644 index 0000000..7f4bf42 --- /dev/null +++ b/tests/partial_linking/required_syms.yaml @@ -0,0 +1,21 @@ +settings: + base_path: build + + symbols_header_path: tests/partial_linking/required_syms.h + + partial_scripts_folder: tests/partial_linking/required_syms + partial_build_segments_folder: segments + +segments: + - name: boot + files: + - { path: src/boot/boot_main.o } + +required_symbols: + - name: guMtxCatL + + - name: guS2DEmuSetScissor + include_if_any: [[libultra, debug]] + + - name: __osSetCause + include_if_any: [[version, us]] diff --git a/tests/partial_linking/required_syms/boot.ld b/tests/partial_linking/required_syms/boot.ld new file mode 100644 index 0000000..f6dc7ac --- /dev/null +++ b/tests/partial_linking/required_syms/boot.ld @@ -0,0 +1,66 @@ +SECTIONS +{ + .text : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.text*); + } + + .data : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.data*); + } + + .rodata : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.rodata*); + } + + .sdata : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.sdata*); + } + + .sbss (NOLOAD) : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.sbss*); + } + + .scommon (NOLOAD) : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.scommon*); + } + + .bss (NOLOAD) : + { + FILL(0x00000000); + build/src/boot/boot_main.o(.bss*); + } + + COMMON (NOLOAD) : + { + FILL(0x00000000); + build/src/boot/boot_main.o(COMMON*); + } + + .shstrtab 0 : + { + *(.shstrtab); + } + + /DISCARD/ : + { + *(.reginfo); + *(.MIPS.abiflags); + *(.MIPS.options); + *(.note.gnu.build-id); + *(.interp); + *(.eh_frame); + *(*); + } +} diff --git a/tests/test_cases/required_syms.h b/tests/test_cases/required_syms.h new file mode 100644 index 0000000..feb9600 --- /dev/null +++ b/tests/test_cases/required_syms.h @@ -0,0 +1,41 @@ +#ifndef HEADER_SYMBOLS_H +#define HEADER_SYMBOLS_H + +extern char boot_ROM_START[]; +extern char boot_VRAM[]; +extern char boot_alloc_VRAM[]; +extern char boot_TEXT_START[]; +extern char boot_TEXT_END[]; +extern char boot_TEXT_SIZE[]; +extern char boot_DATA_START[]; +extern char boot_DATA_END[]; +extern char boot_DATA_SIZE[]; +extern char boot_RODATA_START[]; +extern char boot_RODATA_END[]; +extern char boot_RODATA_SIZE[]; +extern char boot_SDATA_START[]; +extern char boot_SDATA_END[]; +extern char boot_SDATA_SIZE[]; +extern char boot_alloc_VRAM_END[]; +extern char boot_alloc_VRAM_SIZE[]; +extern char boot_noload_VRAM[]; +extern char boot_SBSS_START[]; +extern char boot_SBSS_END[]; +extern char boot_SBSS_SIZE[]; +extern char boot_SCOMMON_START[]; +extern char boot_SCOMMON_END[]; +extern char boot_SCOMMON_SIZE[]; +extern char boot_BSS_START[]; +extern char boot_BSS_END[]; +extern char boot_BSS_SIZE[]; +extern char bootCOMMON_START[]; +extern char bootCOMMON_END[]; +extern char bootCOMMON_SIZE[]; +extern char boot_noload_VRAM_END[]; +extern char boot_noload_VRAM_SIZE[]; +extern char boot_VRAM_END[]; +extern char boot_VRAM_SIZE[]; +extern char boot_ROM_END[]; +extern char boot_ROM_SIZE[]; + +#endif diff --git a/tests/test_cases/required_syms.ld b/tests/test_cases/required_syms.ld new file mode 100644 index 0000000..c301590 --- /dev/null +++ b/tests/test_cases/required_syms.ld @@ -0,0 +1,91 @@ +SECTIONS +{ + __romPos = 0x0; + + boot_ROM_START = __romPos; + boot_VRAM = ADDR(.boot); + boot_alloc_VRAM = .; + + .boot : AT(boot_ROM_START) + { + FILL(0x00000000); + boot_TEXT_START = .; + build/src/boot/boot_main.o(.text*); + boot_TEXT_END = .; + boot_TEXT_SIZE = ABSOLUTE(boot_TEXT_END - boot_TEXT_START); + + boot_DATA_START = .; + build/src/boot/boot_main.o(.data*); + boot_DATA_END = .; + boot_DATA_SIZE = ABSOLUTE(boot_DATA_END - boot_DATA_START); + + boot_RODATA_START = .; + build/src/boot/boot_main.o(.rodata*); + boot_RODATA_END = .; + boot_RODATA_SIZE = ABSOLUTE(boot_RODATA_END - boot_RODATA_START); + + boot_SDATA_START = .; + build/src/boot/boot_main.o(.sdata*); + boot_SDATA_END = .; + boot_SDATA_SIZE = ABSOLUTE(boot_SDATA_END - boot_SDATA_START); + } + + boot_alloc_VRAM_END = .; + boot_alloc_VRAM_SIZE = ABSOLUTE(boot_alloc_VRAM_END - boot_alloc_VRAM); + + boot_noload_VRAM = .; + + .boot.noload (NOLOAD) : + { + FILL(0x00000000); + boot_SBSS_START = .; + build/src/boot/boot_main.o(.sbss*); + boot_SBSS_END = .; + boot_SBSS_SIZE = ABSOLUTE(boot_SBSS_END - boot_SBSS_START); + + boot_SCOMMON_START = .; + build/src/boot/boot_main.o(.scommon*); + boot_SCOMMON_END = .; + boot_SCOMMON_SIZE = ABSOLUTE(boot_SCOMMON_END - boot_SCOMMON_START); + + boot_BSS_START = .; + build/src/boot/boot_main.o(.bss*); + boot_BSS_END = .; + boot_BSS_SIZE = ABSOLUTE(boot_BSS_END - boot_BSS_START); + + bootCOMMON_START = .; + build/src/boot/boot_main.o(COMMON*); + bootCOMMON_END = .; + bootCOMMON_SIZE = ABSOLUTE(bootCOMMON_END - bootCOMMON_START); + } + + boot_noload_VRAM_END = .; + boot_noload_VRAM_SIZE = ABSOLUTE(boot_noload_VRAM_END - boot_noload_VRAM); + + __romPos += SIZEOF(.boot); + boot_VRAM_END = .; + boot_VRAM_SIZE = ABSOLUTE(boot_VRAM_END - boot_VRAM); + boot_ROM_END = __romPos; + boot_ROM_SIZE = ABSOLUTE(boot_ROM_END - boot_ROM_START); + + .shstrtab 0 : + { + *(.shstrtab); + } + + /DISCARD/ : + { + *(.reginfo); + *(.MIPS.abiflags); + *(.MIPS.options); + *(.note.gnu.build-id); + *(.interp); + *(.eh_frame); + *(*); + } +} + +EXTERN(guMtxCatL); +ASSERT(DEFINED(guMtxCatL), "Error: Required symbol 'guMtxCatL' was not linked"); +EXTERN(__osSetCause); +ASSERT(DEFINED(__osSetCause), "Error: Required symbol '__osSetCause' was not linked"); diff --git a/tests/test_cases/required_syms.yaml b/tests/test_cases/required_syms.yaml new file mode 100644 index 0000000..d059995 --- /dev/null +++ b/tests/test_cases/required_syms.yaml @@ -0,0 +1,18 @@ +settings: + base_path: build + + symbols_header_path: tests/test_cases/required_syms.h + +segments: + - name: boot + files: + - { path: src/boot/boot_main.o } + +required_symbols: + - name: guMtxCatL + + - name: guS2DEmuSetScissor + include_if_any: [[libultra, debug]] + + - name: __osSetCause + include_if_any: [[version, us]]