From c9fb5e17382c4831982776fb5b83fc66b130b2f5 Mon Sep 17 00:00:00 2001 From: Angie Date: Tue, 12 Mar 2024 16:38:08 -0300 Subject: [PATCH] Output symbol header Fixes #13 --- docs/file_format/settings.md | 89 +++++++++++ slinky-cli/src/main.rs | 4 + slinky/src/linker_writer.rs | 60 ++++++- slinky/src/settings.rs | 41 ++++- tests/input_files/hardcoded_gp.yaml | 4 + tests/input_files/makerom_zelda_like.yaml | 2 + tests/linker_scripts/hardcoded_gp.h | 35 +++++ tests/linker_scripts/makerom_zelda_like.h | 182 ++++++++++++++++++++++ 8 files changed, 413 insertions(+), 4 deletions(-) create mode 100644 tests/linker_scripts/hardcoded_gp.h create mode 100644 tests/linker_scripts/makerom_zelda_like.h diff --git a/docs/file_format/settings.md b/docs/file_format/settings.md index bf20380..ccfbfa7 100644 --- a/docs/file_format/settings.md +++ b/docs/file_format/settings.md @@ -125,6 +125,10 @@ settings: target_path: build/game.elf ``` +### Valid values + +Non-empty path. + ## `target_path` The path to the file that will result of linking, usually an `.elf` file. @@ -139,6 +143,91 @@ settings: target_path: build/game.elf ``` +### Valid values + +Non-empty path. + +## `symbols_header_path` + +Path to output a C header containing the generated linker symbols. + +The symbols are declared as `extern` and their type can be customized with +[`symbols_header_type`](#symbols_header_type) and +[`symbols_header_as_array`](#symbols_header_as_array). + +This file is generated only if `symbols_header_path` is specified. + +### Example + +```yaml +settings: + symbols_header_path: include/linker_symbols.h +``` + +### Valid values + +Non-empty path. + +## `symbols_header_type` + +The type used for every linker symbol on the generated header. + +This option is ignored if [`symbols_header_path`](#symbols_header_path) was not +set. + +### Example + +```yaml +settings: + symbols_header_path: include/linker_symbols.h + symbols_header_type: u32 +``` + +Generates entries like: + +```c +extern u32 main_BSS_START[]; +``` + +### Valid values + +String + +### Default value + +`char` + +## `symbols_header_as_array` + +Allows customizing if the entries from the generated symbols header should be +declared as arrays or not. + +This option is ignored if [`symbols_header_path`](#symbols_header_path) was not +set. + +### Example + +```yaml +settings: + symbols_header_path: include/linker_symbols.h + symbols_header_type: Addr + symbols_header_as_array: False +``` + +Generates entries like: + +```c +extern Addr main_BSS_START; +``` + +### Valid values + +Boolean + +### Default value + +`True` + ## `sections_allowlist` A list of sections to that should be preserved during linking. diff --git a/slinky-cli/src/main.rs b/slinky-cli/src/main.rs index 5467cdc..5ad1cb9 100644 --- a/slinky-cli/src/main.rs +++ b/slinky-cli/src/main.rs @@ -50,4 +50,8 @@ fn main() { .expect("Error writing dependencies file"); } } + + if let Some(symbols_header_path) = &document.settings.symbols_header_path { + writer.save_symbol_header(symbols_header_path).expect("Error writing symbol header file"); + } } diff --git a/slinky/src/linker_writer.rs b/slinky/src/linker_writer.rs index e089c72..dd3391b 100644 --- a/slinky/src/linker_writer.rs +++ b/slinky/src/linker_writer.rs @@ -99,7 +99,6 @@ impl<'a> LinkerWriter<'a> { assert!(self.indent_level == 0); } - // TODO: figure out a better way to handle Options pub fn add_segment(&mut self, segment: &Segment) { let style = &self.settings.linker_symbols_style; let dotted_seg_name = format!(".{}", segment.name); @@ -174,6 +173,63 @@ impl<'a> LinkerWriter<'a> { ret } + + pub fn save_symbol_header(&self, path: &Path) -> Result<(), SlinkyError> { + let mut f = utils::create_file_and_parents(path)?; + + if let Err(e) = write!(f, "#ifndef HEADER_SYMBOLS_H\n#define HEADER_SYMBOLS_H\n\n") { + return Err(SlinkyError::FailedFileWrite { + path: path.to_path_buf(), + description: e.to_string(), + contents: "".into(), + }); + } + + let arr_suffix = if self.settings.symbols_header_as_array { "[]" } else {""}; + + let mut linker_symbols_sorted: Vec<_> = (&self.linker_symbols).into_iter().collect(); + linker_symbols_sorted.sort(); + + for sym in linker_symbols_sorted { + if let Err(e) = writeln!(f, "extern {} {}{};", self.settings.symbols_header_type, sym, arr_suffix) { + return Err(SlinkyError::FailedFileWrite { + path: path.to_path_buf(), + description: e.to_string(), + contents: sym.into(), + }); + } + } + + if let Err(e) = write!(f, "\n#endif\n") { + return Err(SlinkyError::FailedFileWrite { + path: path.to_path_buf(), + description: e.to_string(), + contents: "".into(), + }); + } + + Ok(()) + } + + #[must_use] + pub fn export_symbol_header_as_string(&self) -> String { + let mut ret = String::new(); + + ret += "#ifndef HEADER_SYMBOLS_H\n#define HEADER_SYMBOLS_H\n\n"; + + let arr_suffix = if self.settings.symbols_header_as_array { "[]" } else {""}; + + let mut linker_symbols_sorted: Vec<_> = (&self.linker_symbols).into_iter().collect(); + linker_symbols_sorted.sort(); + + for sym in linker_symbols_sorted { + ret += &format!("extern {} {}{};", self.settings.symbols_header_type, sym, arr_suffix); + } + + ret += "\n#endif\n"; + + ret + } } // internal functions @@ -209,7 +265,7 @@ impl LinkerWriter<'_> { self.writeln(&format!("{} = {};", symbol, value)); - self.linker_symbols.insert(value.to_string()); + self.linker_symbols.insert(symbol.to_string()); } fn align_symbol(&mut self, symbol: &str, align_value: u32) { diff --git a/slinky/src/settings.rs b/slinky/src/settings.rs index a1d5d9d..f8e5ba0 100644 --- a/slinky/src/settings.rs +++ b/slinky/src/settings.rs @@ -18,6 +18,10 @@ pub struct Settings { pub d_path: Option, pub target_path: Option, + pub symbols_header_path: Option, + pub symbols_header_type: String, + pub symbols_header_as_array: bool, + pub sections_allowlist: Vec, pub sections_allowlist_extra: Vec, pub sections_denylist: Vec, @@ -54,6 +58,18 @@ fn settings_default_target_path() -> Option { None } +fn settings_default_symbols_header_path() -> Option { + None +} + +fn settings_default_symbols_header_type() -> String { + "char".to_string() +} + +fn settings_default_symbols_header_as_array() -> bool { + true +} + fn settings_default_hardcoded_gp_value() -> Option { None } @@ -125,10 +141,14 @@ impl Default for Settings { base_path: settings_default_base_path(), linker_symbols_style: settings_default_linker_symbols_style(), + hardcoded_gp_value: settings_default_hardcoded_gp_value(), + d_path: settings_default_d_path(), target_path: settings_default_target_path(), - hardcoded_gp_value: settings_default_hardcoded_gp_value(), + symbols_header_path: settings_default_symbols_header_path(), + symbols_header_type: settings_default_symbols_header_type(), + symbols_header_as_array: settings_default_symbols_header_as_array(), sections_allowlist: settings_default_sections_allowlist(), sections_allowlist_extra: settings_default_sections_allowlist_extra(), @@ -157,13 +177,20 @@ pub(crate) struct SettingsSerial { #[serde(default)] pub linker_symbols_style: AbsentNullable, + #[serde(default)] + pub hardcoded_gp_value: AbsentNullable, + #[serde(default)] pub d_path: AbsentNullable, #[serde(default)] pub target_path: AbsentNullable, #[serde(default)] - pub hardcoded_gp_value: AbsentNullable, + pub symbols_header_path: AbsentNullable, + #[serde(default)] + pub symbols_header_type: AbsentNullable, + #[serde(default)] + pub symbols_header_as_array: AbsentNullable, #[serde(default)] pub sections_allowlist: AbsentNullable>, @@ -215,6 +242,10 @@ impl SettingsSerial { .target_path .get_optional_nullable("target_path", settings_default_target_path)?; + let symbols_header_path = self.symbols_header_path.get_optional_nullable("symbols_header_path", settings_default_symbols_header_path)?; + let symbols_header_type = self.symbols_header_type.get_non_null("symbols_header_type", settings_default_symbols_header_type)?; + let symbols_header_as_array = self.symbols_header_as_array.get_non_null("symbols_header_as_array", settings_default_symbols_header_as_array)?; + let sections_allowlist = self .sections_allowlist .get_non_null("sections_allowlist", settings_default_sections_allowlist)?; @@ -268,8 +299,14 @@ impl SettingsSerial { base_path, linker_symbols_style, hardcoded_gp_value, + d_path, target_path, + + symbols_header_path, + symbols_header_type, + symbols_header_as_array, + sections_allowlist, sections_allowlist_extra, sections_denylist, diff --git a/tests/input_files/hardcoded_gp.yaml b/tests/input_files/hardcoded_gp.yaml index a0adc7e..0e0e1a9 100644 --- a/tests/input_files/hardcoded_gp.yaml +++ b/tests/input_files/hardcoded_gp.yaml @@ -9,6 +9,10 @@ settings: subalign: null + symbols_header_path: tests/linker_scripts/hardcoded_gp.h + symbols_header_as_array: False + symbols_header_type: Addr + segments: - name: main fixed_vram: 0x80010200 diff --git a/tests/input_files/makerom_zelda_like.yaml b/tests/input_files/makerom_zelda_like.yaml index e905ec9..7629357 100644 --- a/tests/input_files/makerom_zelda_like.yaml +++ b/tests/input_files/makerom_zelda_like.yaml @@ -3,6 +3,8 @@ settings: subalign: null linker_symbols_style: makerom + symbols_header_path: tests/linker_scripts/makerom_zelda_like.h + segments: - name: makerom fixed_vram: 0x80024C00 diff --git a/tests/linker_scripts/hardcoded_gp.h b/tests/linker_scripts/hardcoded_gp.h new file mode 100644 index 0000000..fb73c1c --- /dev/null +++ b/tests/linker_scripts/hardcoded_gp.h @@ -0,0 +1,35 @@ +#ifndef HEADER_SYMBOLS_H +#define HEADER_SYMBOLS_H + +extern Addr main_BSS_END; +extern Addr main_BSS_SIZE; +extern Addr main_BSS_START; +extern Addr main_DATA_END; +extern Addr main_DATA_SIZE; +extern Addr main_DATA_START; +extern Addr main_RODATA_END; +extern Addr main_RODATA_SIZE; +extern Addr main_RODATA_START; +extern Addr main_ROM_END; +extern Addr main_ROM_SIZE; +extern Addr main_ROM_START; +extern Addr main_SBSS_END; +extern Addr main_SBSS_SIZE; +extern Addr main_SBSS_START; +extern Addr main_SDATA_END; +extern Addr main_SDATA_SIZE; +extern Addr main_SDATA_START; +extern Addr main_TEXT_END; +extern Addr main_TEXT_SIZE; +extern Addr main_TEXT_START; +extern Addr main_VRAM; +extern Addr main_VRAM_END; +extern Addr main_VRAM_SIZE; +extern Addr main_alloc_VRAM; +extern Addr main_alloc_VRAM_END; +extern Addr main_alloc_VRAM_SIZE; +extern Addr main_noload_VRAM; +extern Addr main_noload_VRAM_END; +extern Addr main_noload_VRAM_SIZE; + +#endif diff --git a/tests/linker_scripts/makerom_zelda_like.h b/tests/linker_scripts/makerom_zelda_like.h new file mode 100644 index 0000000..555b557 --- /dev/null +++ b/tests/linker_scripts/makerom_zelda_like.h @@ -0,0 +1,182 @@ +#ifndef HEADER_SYMBOLS_H +#define HEADER_SYMBOLS_H + +extern char _bootSegmentBssEnd[]; +extern char _bootSegmentBssSize[]; +extern char _bootSegmentBssStart[]; +extern char _bootSegmentCOMMONEnd[]; +extern char _bootSegmentCOMMONSize[]; +extern char _bootSegmentCOMMONStart[]; +extern char _bootSegmentDataEnd[]; +extern char _bootSegmentDataSize[]; +extern char _bootSegmentDataStart[]; +extern char _bootSegmentEnd[]; +extern char _bootSegmentRoDataEnd[]; +extern char _bootSegmentRoDataSize[]; +extern char _bootSegmentRoDataStart[]; +extern char _bootSegmentRomEnd[]; +extern char _bootSegmentRomSize[]; +extern char _bootSegmentRomStart[]; +extern char _bootSegmentSbssEnd[]; +extern char _bootSegmentSbssSize[]; +extern char _bootSegmentSbssStart[]; +extern char _bootSegmentScommonEnd[]; +extern char _bootSegmentScommonSize[]; +extern char _bootSegmentScommonStart[]; +extern char _bootSegmentSdataEnd[]; +extern char _bootSegmentSdataSize[]; +extern char _bootSegmentSdataStart[]; +extern char _bootSegmentSize[]; +extern char _bootSegmentStart[]; +extern char _bootSegmentTextEnd[]; +extern char _bootSegmentTextSize[]; +extern char _bootSegmentTextStart[]; +extern char _boot_allocSegmentEnd[]; +extern char _boot_allocSegmentSize[]; +extern char _boot_allocSegmentStart[]; +extern char _boot_noloadSegmentEnd[]; +extern char _boot_noloadSegmentSize[]; +extern char _boot_noloadSegmentStart[]; +extern char _codeSegmentBssEnd[]; +extern char _codeSegmentBssSize[]; +extern char _codeSegmentBssStart[]; +extern char _codeSegmentCOMMONEnd[]; +extern char _codeSegmentCOMMONSize[]; +extern char _codeSegmentCOMMONStart[]; +extern char _codeSegmentDataEnd[]; +extern char _codeSegmentDataSize[]; +extern char _codeSegmentDataStart[]; +extern char _codeSegmentEnd[]; +extern char _codeSegmentRoDataEnd[]; +extern char _codeSegmentRoDataSize[]; +extern char _codeSegmentRoDataStart[]; +extern char _codeSegmentRomEnd[]; +extern char _codeSegmentRomSize[]; +extern char _codeSegmentRomStart[]; +extern char _codeSegmentSbssEnd[]; +extern char _codeSegmentSbssSize[]; +extern char _codeSegmentSbssStart[]; +extern char _codeSegmentScommonEnd[]; +extern char _codeSegmentScommonSize[]; +extern char _codeSegmentScommonStart[]; +extern char _codeSegmentSdataEnd[]; +extern char _codeSegmentSdataSize[]; +extern char _codeSegmentSdataStart[]; +extern char _codeSegmentSize[]; +extern char _codeSegmentStart[]; +extern char _codeSegmentTextEnd[]; +extern char _codeSegmentTextSize[]; +extern char _codeSegmentTextStart[]; +extern char _code_allocSegmentEnd[]; +extern char _code_allocSegmentSize[]; +extern char _code_allocSegmentStart[]; +extern char _code_noloadSegmentEnd[]; +extern char _code_noloadSegmentSize[]; +extern char _code_noloadSegmentStart[]; +extern char _gameplay_dangeon_keepSegmentBssEnd[]; +extern char _gameplay_dangeon_keepSegmentBssSize[]; +extern char _gameplay_dangeon_keepSegmentBssStart[]; +extern char _gameplay_dangeon_keepSegmentCOMMONEnd[]; +extern char _gameplay_dangeon_keepSegmentCOMMONSize[]; +extern char _gameplay_dangeon_keepSegmentCOMMONStart[]; +extern char _gameplay_dangeon_keepSegmentDataEnd[]; +extern char _gameplay_dangeon_keepSegmentDataSize[]; +extern char _gameplay_dangeon_keepSegmentDataStart[]; +extern char _gameplay_dangeon_keepSegmentEnd[]; +extern char _gameplay_dangeon_keepSegmentRoDataEnd[]; +extern char _gameplay_dangeon_keepSegmentRoDataSize[]; +extern char _gameplay_dangeon_keepSegmentRoDataStart[]; +extern char _gameplay_dangeon_keepSegmentRomEnd[]; +extern char _gameplay_dangeon_keepSegmentRomSize[]; +extern char _gameplay_dangeon_keepSegmentRomStart[]; +extern char _gameplay_dangeon_keepSegmentSbssEnd[]; +extern char _gameplay_dangeon_keepSegmentSbssSize[]; +extern char _gameplay_dangeon_keepSegmentSbssStart[]; +extern char _gameplay_dangeon_keepSegmentScommonEnd[]; +extern char _gameplay_dangeon_keepSegmentScommonSize[]; +extern char _gameplay_dangeon_keepSegmentScommonStart[]; +extern char _gameplay_dangeon_keepSegmentSdataEnd[]; +extern char _gameplay_dangeon_keepSegmentSdataSize[]; +extern char _gameplay_dangeon_keepSegmentSdataStart[]; +extern char _gameplay_dangeon_keepSegmentSize[]; +extern char _gameplay_dangeon_keepSegmentStart[]; +extern char _gameplay_dangeon_keepSegmentTextEnd[]; +extern char _gameplay_dangeon_keepSegmentTextSize[]; +extern char _gameplay_dangeon_keepSegmentTextStart[]; +extern char _gameplay_dangeon_keep_allocSegmentEnd[]; +extern char _gameplay_dangeon_keep_allocSegmentSize[]; +extern char _gameplay_dangeon_keep_allocSegmentStart[]; +extern char _gameplay_dangeon_keep_noloadSegmentEnd[]; +extern char _gameplay_dangeon_keep_noloadSegmentSize[]; +extern char _gameplay_dangeon_keep_noloadSegmentStart[]; +extern char _gameplay_keepSegmentBssEnd[]; +extern char _gameplay_keepSegmentBssSize[]; +extern char _gameplay_keepSegmentBssStart[]; +extern char _gameplay_keepSegmentCOMMONEnd[]; +extern char _gameplay_keepSegmentCOMMONSize[]; +extern char _gameplay_keepSegmentCOMMONStart[]; +extern char _gameplay_keepSegmentDataEnd[]; +extern char _gameplay_keepSegmentDataSize[]; +extern char _gameplay_keepSegmentDataStart[]; +extern char _gameplay_keepSegmentEnd[]; +extern char _gameplay_keepSegmentRoDataEnd[]; +extern char _gameplay_keepSegmentRoDataSize[]; +extern char _gameplay_keepSegmentRoDataStart[]; +extern char _gameplay_keepSegmentRomEnd[]; +extern char _gameplay_keepSegmentRomSize[]; +extern char _gameplay_keepSegmentRomStart[]; +extern char _gameplay_keepSegmentSbssEnd[]; +extern char _gameplay_keepSegmentSbssSize[]; +extern char _gameplay_keepSegmentSbssStart[]; +extern char _gameplay_keepSegmentScommonEnd[]; +extern char _gameplay_keepSegmentScommonSize[]; +extern char _gameplay_keepSegmentScommonStart[]; +extern char _gameplay_keepSegmentSdataEnd[]; +extern char _gameplay_keepSegmentSdataSize[]; +extern char _gameplay_keepSegmentSdataStart[]; +extern char _gameplay_keepSegmentSize[]; +extern char _gameplay_keepSegmentStart[]; +extern char _gameplay_keepSegmentTextEnd[]; +extern char _gameplay_keepSegmentTextSize[]; +extern char _gameplay_keepSegmentTextStart[]; +extern char _gameplay_keep_allocSegmentEnd[]; +extern char _gameplay_keep_allocSegmentSize[]; +extern char _gameplay_keep_allocSegmentStart[]; +extern char _gameplay_keep_noloadSegmentEnd[]; +extern char _gameplay_keep_noloadSegmentSize[]; +extern char _gameplay_keep_noloadSegmentStart[]; +extern char _makeromSegmentBssEnd[]; +extern char _makeromSegmentBssSize[]; +extern char _makeromSegmentBssStart[]; +extern char _makeromSegmentCOMMONEnd[]; +extern char _makeromSegmentCOMMONSize[]; +extern char _makeromSegmentCOMMONStart[]; +extern char _makeromSegmentDataEnd[]; +extern char _makeromSegmentDataSize[]; +extern char _makeromSegmentDataStart[]; +extern char _makeromSegmentEnd[]; +extern char _makeromSegmentRoDataEnd[]; +extern char _makeromSegmentRoDataSize[]; +extern char _makeromSegmentRoDataStart[]; +extern char _makeromSegmentRomEnd[]; +extern char _makeromSegmentRomSize[]; +extern char _makeromSegmentRomStart[]; +extern char _makeromSegmentSbssEnd[]; +extern char _makeromSegmentSbssSize[]; +extern char _makeromSegmentSbssStart[]; +extern char _makeromSegmentScommonEnd[]; +extern char _makeromSegmentScommonSize[]; +extern char _makeromSegmentScommonStart[]; +extern char _makeromSegmentSize[]; +extern char _makeromSegmentStart[]; +extern char _makeromSegmentTextEnd[]; +extern char _makeromSegmentTextSize[]; +extern char _makeromSegmentTextStart[]; +extern char _makerom_allocSegmentEnd[]; +extern char _makerom_allocSegmentSize[]; +extern char _makerom_allocSegmentStart[]; +extern char _makerom_noloadSegmentEnd[]; +extern char _makerom_noloadSegmentSize[]; +extern char _makerom_noloadSegmentStart[]; + +#endif