From d5c2e14320b094f9713cb2f92288eea8682122e3 Mon Sep 17 00:00:00 2001 From: angie Date: Sun, 11 Aug 2024 15:17:52 -0400 Subject: [PATCH] Add `provide` and `hidden` for symbol_assignments --- CHANGELOG.md | 10 +++++ docs/file_format/symbol_assignments.md | 49 +++++++++++++++++++++++ slinky/src/linker_writer.rs | 24 ++++++----- slinky/src/script_buffer.rs | 21 +++++++--- slinky/src/symbol_assignment.rs | 17 ++++++++ tests/partial_linking/undefined_syms.ld | 4 +- tests/partial_linking/undefined_syms.yaml | 10 +++++ tests/test_cases/undefined_syms.ld | 4 +- tests/test_cases/undefined_syms.yaml | 10 +++++ 9 files changed, 132 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 579ebd2..4fdf300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add new top-level attribute for the file format: `symbol_assignments`. + - 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. + - 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`. + ## [0.2.5] - 2024-07-17 ### Fixed diff --git a/docs/file_format/symbol_assignments.md b/docs/file_format/symbol_assignments.md index 8f9e866..617a0d9 100644 --- a/docs/file_format/symbol_assignments.md +++ b/docs/file_format/symbol_assignments.md @@ -21,6 +21,12 @@ Every attribute listed is optional unless explicitly stated. - [`value`](#value) - [Example](#example-1) - [Valid values](#valid-values-1) + - [`provide`](#provide) + - [Valid values](#valid-values-2) + - [Default value](#default-value) + - [`hidden`](#hidden) + - [Valid values](#valid-values-3) + - [Default value](#default-value-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` @@ -66,6 +72,49 @@ symbol_assignments: Non empty string. +## `provide` + +If `provide` is enabled for an entry then this symbol assignment will only be +applied if the given symbol is referenced but is not defined in any object +included in the link. + +See GNU LD documentation for +[`PROVIDE`](https://sourceware.org/binutils/docs/ld/PROVIDE.html). + +This option can be combined with [`hidden`](#hidden). For more info see the GNU +LD documentation for +[`PROVIDE_HIDDEN`](https://sourceware.org/binutils/docs/ld/PROVIDE_005fHIDDEN.html). + +### Valid values + +Bool. + +### Default value + +`False` + +## `hidden` + +Allows defining the symbol that will be hidden and won't be exported. + +On a more technical sense, the binding of the generated symbol on the elf will +be marked as `LOCAL` instead of `GLOBAL.` + +See GNU LD documentation for +[`HIDDEN`](https://sourceware.org/binutils/docs/ld/HIDDEN.html). + +This option can be combined with [`provide`](#provide). For more info see the +GNU LD documentation for +[`PROVIDE_HIDDEN`](https://sourceware.org/binutils/docs/ld/PROVIDE_005fHIDDEN.html). + +### Valid values + +Bool. + +### Default value + +`False` + ## `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 diff --git a/slinky/src/linker_writer.rs b/slinky/src/linker_writer.rs index bd9d577..a8ab2ff 100644 --- a/slinky/src/linker_writer.rs +++ b/slinky/src/linker_writer.rs @@ -101,8 +101,8 @@ impl<'a> LinkerWriter<'a> { self.begin_symbol_assignments()?; - for undefined_sym in symbol_assignments { - self.add_undefined_sym(undefined_sym)?; + for symbol_assignment in symbol_assignments { + self.add_symbol_assignment(symbol_assignment)?; } self.end_symbol_assignments()?; @@ -606,21 +606,25 @@ impl LinkerWriter<'_> { Ok(()) } - pub(crate) fn add_undefined_sym( + pub(crate) fn add_symbol_assignment( &mut self, - undefined_sym: &SymbolAssignment, + symbol_assignment: &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, + &symbol_assignment.exclude_if_any, + &symbol_assignment.exclude_if_all, + &symbol_assignment.include_if_any, + &symbol_assignment.include_if_all, ) { return Ok(()); } - self.buffer - .write_symbol_assignment(&undefined_sym.name, &undefined_sym.value); + self.buffer.write_symbol_assignment( + &symbol_assignment.name, + &symbol_assignment.value, + symbol_assignment.provide, + symbol_assignment.hidden, + ); Ok(()) } diff --git a/slinky/src/script_buffer.rs b/slinky/src/script_buffer.rs index 557729d..0201a7d 100644 --- a/slinky/src/script_buffer.rs +++ b/slinky/src/script_buffer.rs @@ -49,15 +49,26 @@ impl ScriptBuffer { pub fn write_linker_symbol(&mut self, symbol: &str, value: &str) { // TODO: check `symbol` is a valid C identifier - self.write_symbol_assignment(symbol, value); + self.write_symbol_assignment(symbol, value, false, false); 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 write_symbol_assignment( + &mut self, + symbol: &str, + value: &str, + provide: bool, + hidden: bool, + ) { + let line = match (provide, hidden) { + (true, true) => format!("PROVIDE_HIDDEN({} = {});", symbol, value), + (true, false) => format!("PROVIDE({} = {});", symbol, value), + (false, true) => format!("HIDDEN({} = {});", symbol, value), + (false, false) => format!("{} = {};", symbol, value), + }; + + self.writeln(&line); } pub fn align_symbol(&mut self, symbol: &str, align_value: u32) { diff --git a/slinky/src/symbol_assignment.rs b/slinky/src/symbol_assignment.rs index 0608fbf..2ec7d9a 100644 --- a/slinky/src/symbol_assignment.rs +++ b/slinky/src/symbol_assignment.rs @@ -13,6 +13,13 @@ pub struct SymbolAssignment { /// Value or expression to assign to this symbol pub value: String, + /// Signals if this assignment should be wrapped in a `PROVIDE` statement. + /// Can be used with `hidden`. + pub provide: bool, + /// Signals if this assignment should be wrapped in a `HIDDEN` statement. + /// Can be used with `provide`. + pub hidden: bool, + pub include_if_any: Vec<(String, String)>, pub include_if_all: Vec<(String, String)>, pub exclude_if_any: Vec<(String, String)>, @@ -25,6 +32,11 @@ pub(crate) struct SymbolAssignmentSerial { pub name: String, pub value: String, + #[serde(default)] + pub provide: AbsentNullable, + #[serde(default)] + pub hidden: AbsentNullable, + #[serde(default)] pub include_if_any: AbsentNullable>, #[serde(default)] @@ -51,6 +63,9 @@ impl SymbolAssignmentSerial { } let value = self.value; + let provide = self.provide.get_non_null("provide", || false)?; + let hidden = self.hidden.get_non_null("hidden", || false)?; + let include_if_any = self .include_if_any .get_non_null("include_if_any", Vec::new)?; @@ -67,6 +82,8 @@ impl SymbolAssignmentSerial { Ok(SymbolAssignment { name, value, + provide, + hidden, include_if_any, include_if_all, exclude_if_any, diff --git a/tests/partial_linking/undefined_syms.ld b/tests/partial_linking/undefined_syms.ld index 19679ab..bd190c5 100644 --- a/tests/partial_linking/undefined_syms.ld +++ b/tests/partial_linking/undefined_syms.ld @@ -297,4 +297,6 @@ osVersion = 0x80000314; osMemSize = 0x80000318; osAppNMIBuffer = 0x8000031C; dummy_test = 0x80801234; -_gp = boot_SCOMMON_START + 0x7FF0; +HIDDEN(_gp = boot_SCOMMON_START + 0x7FF0); +PROVIDE(provided_sym = 0x1234); +PROVIDE_HIDDEN(provided_and_hidden_sym = 0x1234); diff --git a/tests/partial_linking/undefined_syms.yaml b/tests/partial_linking/undefined_syms.yaml index 806bcda..06ef9f2 100644 --- a/tests/partial_linking/undefined_syms.yaml +++ b/tests/partial_linking/undefined_syms.yaml @@ -112,3 +112,13 @@ symbol_assignments: - name: _gp value: boot_SCOMMON_START + 0x7FF0 + hidden: True + + - name: provided_sym + value: 0x1234 + provide: True + + - name: provided_and_hidden_sym + value: 0x1234 + provide: True + hidden: True diff --git a/tests/test_cases/undefined_syms.ld b/tests/test_cases/undefined_syms.ld index f44e4ea..a14ea37 100644 --- a/tests/test_cases/undefined_syms.ld +++ b/tests/test_cases/undefined_syms.ld @@ -313,4 +313,6 @@ osVersion = 0x80000314; osMemSize = 0x80000318; osAppNMIBuffer = 0x8000031C; dummy_test = 0x80801234; -_gp = boot_SCOMMON_START + 0x7FF0; +HIDDEN(_gp = boot_SCOMMON_START + 0x7FF0); +PROVIDE(provided_sym = 0x1234); +PROVIDE_HIDDEN(provided_and_hidden_sym = 0x1234); diff --git a/tests/test_cases/undefined_syms.yaml b/tests/test_cases/undefined_syms.yaml index c0121fa..2cbb163 100644 --- a/tests/test_cases/undefined_syms.yaml +++ b/tests/test_cases/undefined_syms.yaml @@ -109,3 +109,13 @@ symbol_assignments: - name: _gp value: boot_SCOMMON_START + 0x7FF0 + hidden: True + + - name: provided_sym + value: 0x1234 + provide: True + + - name: provided_and_hidden_sym + value: 0x1234 + provide: True + hidden: True