diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cae2b1..e290b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Add option to control emission of `noload` segment. + ### Changed - Include `.symtab` and `.strtab` in default value of `sections_allowlist_extra` @@ -15,6 +17,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 explicitly on the linker script. - Add `.got` to the default list of discarded sections. - Simplify the formatting used for extra sections. +- `alloc_sections` and `noload_sections` sections now accept `null` values. + - This allows to avoid emitting those sections completely for a given segment + or globally. + - The main difference between using `null` and an empty list is `null` will + not produce linker symbol nor the section itself in the generated linker + script, while the empty list would generate the starting and ending linker + symbols and the section with no files. ## [0.3.0] - 2024-08-17 diff --git a/docs/file_format/segments.md b/docs/file_format/segments.md index 89a6cf1..c95244c 100644 --- a/docs/file_format/segments.md +++ b/docs/file_format/segments.md @@ -295,6 +295,10 @@ segment. The sections from this list will be emitted for each file in the specified order. +A `null` value can be used to signal that no allocatable sections should be +emitted for this segment. Setting to `null` will avoid emitting linker symbols +for allocatable sections too. + This allows to override the global settings in case a specific segment has an order different than the global one. See [settings.md#alloc_sections](settings.md#alloc_sections) for more info. @@ -308,7 +312,7 @@ settings: ### Valid values -List of strings. +List of strings or `null`. ### Default value @@ -322,6 +326,9 @@ segment. The sections from this list will be emitted for each file in the specified order. +A `null` value can be used to signal that no noload sections should be emitted. +Setting to `null` will avoid emitting linker symbols for noload sections too. + This allows to override the global settings in case a specific segment has an order different than the global one. See [settings.md#noload_sections](settings.md#noload_sections) for more info. @@ -335,7 +342,7 @@ settings: ### Valid values -List of strings. +List of strings or `null`. ### Default value diff --git a/docs/file_format/settings.md b/docs/file_format/settings.md index 3ab9969..8dc625c 100644 --- a/docs/file_format/settings.md +++ b/docs/file_format/settings.md @@ -536,6 +536,10 @@ List of allocatable sections (the ones that take ROM space). The sections from this list will be emitted for each file in the specified order. +A `null` value can be used to signal that no allocatable sections should be +emitted. Setting to `null` will avoid emitting linker symbols for allocatable +sections too. + This option can be overriden per segment, see [segments.md#alloc_sections](segments.md#alloc_sections) for more info. @@ -548,7 +552,7 @@ settings: ### Valid values -List of strings. +List of strings or `null`. ### Default value @@ -561,6 +565,9 @@ List of noload sections (the ones that don't take ROM space). The sections from this list will be emitted for each file in the specified order. +A `null` value can be used to signal that no noload sections should be emitted. +Setting to `null` will avoid emitting linker symbols for noload sections too. + This option can be overriden per segment, see [segments.md#noload_sections](segments.md#noload_sections) for more info. @@ -573,7 +580,7 @@ settings: ### Valid values -List of strings. +List of strings or `null`. ### Default value diff --git a/slinky/src/linker_writer.rs b/slinky/src/linker_writer.rs index 93313ba..2480d0d 100644 --- a/slinky/src/linker_writer.rs +++ b/slinky/src/linker_writer.rs @@ -554,12 +554,16 @@ impl LinkerWriter<'_> { .write_linker_symbol(&main_seg_sym_start, &format!("ADDR(.{})", segment.name)); // Emit alloc segment - self.write_segment(segment, &segment.alloc_sections, false)?; + if let Some(alloc_sections) = &segment.alloc_sections { + self.write_segment(segment, alloc_sections, false)?; + } self.buffer.write_empty_line(); // Emit noload segment - self.write_segment(segment, &segment.noload_sections, true)?; + if let Some(noload_sections) = &segment.noload_sections { + self.write_segment(segment, noload_sections, true)?; + } self.buffer.write_empty_line(); @@ -612,12 +616,16 @@ impl LinkerWriter<'_> { } // Emit alloc segment - self.write_single_segment(segment, &segment.alloc_sections, false)?; + if let Some(alloc_sections) = &segment.alloc_sections { + self.write_single_segment(segment, alloc_sections, false)?; + } self.buffer.write_empty_line(); // Emit noload segment - self.write_single_segment(segment, &segment.noload_sections, true)?; + if let Some(noload_sections) = &segment.noload_sections { + self.write_single_segment(segment, noload_sections, true)?; + } self.buffer.write_empty_line(); diff --git a/slinky/src/segment.rs b/slinky/src/segment.rs index ca75d0d..5a09d5f 100644 --- a/slinky/src/segment.rs +++ b/slinky/src/segment.rs @@ -10,7 +10,7 @@ use crate::{ file_info::{FileInfo, FileInfoSerial}, gp_info::{GpInfo, GpInfoSerial}, traits::Serial, - EscapedPath, KeepSections, RuntimeSettings, Settings, SlinkyError, + utils, EscapedPath, KeepSections, RuntimeSettings, Settings, SlinkyError, }; #[derive(PartialEq, Debug, Clone)] @@ -48,8 +48,8 @@ pub struct Segment { pub exclude_if_all: Vec<(String, String)>, // The default value of the following members come from Settings - pub alloc_sections: Vec, - pub noload_sections: Vec, + pub alloc_sections: Option>, + pub noload_sections: Option>, pub subalign: Option, pub segment_start_align: Option, @@ -293,13 +293,15 @@ impl Serial for SegmentSerial { let alloc_sections = self .alloc_sections - .get_non_null("alloc_sections", || settings.alloc_sections.clone())?; + .get_optional_nullable("alloc_sections", || settings.alloc_sections.clone())?; let noload_sections = self .noload_sections - .get_non_null("noload_sections", || settings.noload_sections.clone())?; + .get_optional_nullable("noload_sections", || settings.noload_sections.clone())?; if let Some(gp) = &gp_info { - if !alloc_sections.contains(&gp.section) && !noload_sections.contains(&gp.section) { + if utils::is_none_or(alloc_sections.as_ref(), |s| !s.contains(&gp.section)) + && utils::is_none_or(noload_sections.as_ref(), |s| !s.contains(&gp.section)) + { return Err(SlinkyError::MissingSectionForSegment { field_name: Cow::from("gp_info"), section: Cow::from(gp_info.unwrap().section), diff --git a/slinky/src/settings.rs b/slinky/src/settings.rs index 3d685b0..49670b5 100644 --- a/slinky/src/settings.rs +++ b/slinky/src/settings.rs @@ -34,8 +34,8 @@ pub struct Settings { pub partial_build_segments_folder: Option, // Options passed down to each segment - pub alloc_sections: Vec, - pub noload_sections: Vec, + pub alloc_sections: Option>, + pub noload_sections: Option>, pub subalign: Option, pub segment_start_align: Option, @@ -120,22 +120,22 @@ const fn settings_default_partial_build_segments_folder() -> Option { None } -fn settings_default_alloc_sections() -> Vec { - vec![ +fn settings_default_alloc_sections() -> Option> { + Some(vec![ ".text".into(), ".data".into(), ".rodata".into(), ".sdata".into(), - ] + ]) } -fn settings_default_noload_sections() -> Vec { - vec![ +fn settings_default_noload_sections() -> Option> { + Some(vec![ ".sbss".into(), ".scommon".into(), ".bss".into(), "COMMON".into(), - ] + ]) } const fn settings_default_subalign() -> Option { @@ -417,10 +417,10 @@ impl SettingsSerial { let alloc_sections = self .alloc_sections - .get_non_null("alloc_sections", settings_default_alloc_sections)?; + .get_optional_nullable("alloc_sections", settings_default_alloc_sections)?; let noload_sections = self .noload_sections - .get_non_null("noload_sections", settings_default_noload_sections)?; + .get_optional_nullable("noload_sections", settings_default_noload_sections)?; let subalign = self .subalign @@ -488,6 +488,7 @@ impl SettingsSerial { alloc_sections, noload_sections, + subalign, segment_start_align, segment_end_align, diff --git a/slinky/src/utils.rs b/slinky/src/utils.rs index f982ca8..b2658ce 100644 --- a/slinky/src/utils.rs +++ b/slinky/src/utils.rs @@ -37,3 +37,13 @@ pub(crate) fn create_file_and_parents(path: &Path) -> Result }), } } + +pub(crate) fn is_none_or(val: Option<&T>, f: F) -> bool +where + F: FnOnce(&T) -> bool, +{ + match val { + None => true, + Some(x) => f(x), + } +} diff --git a/tests/test_cases/null_sections.h b/tests/test_cases/null_sections.h new file mode 100644 index 0000000..1008189 --- /dev/null +++ b/tests/test_cases/null_sections.h @@ -0,0 +1,83 @@ +#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[]; +extern char buffers_ROM_START[]; +extern char buffers_VRAM[]; +extern char buffers_noload_VRAM[]; +extern char buffers_SBSS_START[]; +extern char buffers_SBSS_END[]; +extern char buffers_SBSS_SIZE[]; +extern char buffers_SCOMMON_START[]; +extern char buffers_SCOMMON_END[]; +extern char buffers_SCOMMON_SIZE[]; +extern char buffers_BSS_START[]; +extern char buffers_BSS_END[]; +extern char buffers_BSS_SIZE[]; +extern char buffersCOMMON_START[]; +extern char buffersCOMMON_END[]; +extern char buffersCOMMON_SIZE[]; +extern char buffers_noload_VRAM_END[]; +extern char buffers_noload_VRAM_SIZE[]; +extern char buffers_VRAM_END[]; +extern char buffers_VRAM_SIZE[]; +extern char buffers_ROM_END[]; +extern char buffers_ROM_SIZE[]; +extern char assets_ROM_START[]; +extern char assets_VRAM[]; +extern char assets_alloc_VRAM[]; +extern char assets_TEXT_START[]; +extern char assets_TEXT_END[]; +extern char assets_TEXT_SIZE[]; +extern char assets_DATA_START[]; +extern char assets_DATA_END[]; +extern char assets_DATA_SIZE[]; +extern char assets_RODATA_START[]; +extern char assets_RODATA_END[]; +extern char assets_RODATA_SIZE[]; +extern char assets_SDATA_START[]; +extern char assets_SDATA_END[]; +extern char assets_SDATA_SIZE[]; +extern char assets_alloc_VRAM_END[]; +extern char assets_alloc_VRAM_SIZE[]; +extern char assets_VRAM_END[]; +extern char assets_VRAM_SIZE[]; +extern char assets_ROM_END[]; +extern char assets_ROM_SIZE[]; + +#endif diff --git a/tests/test_cases/null_sections.ld b/tests/test_cases/null_sections.ld new file mode 100644 index 0000000..87e47df --- /dev/null +++ b/tests/test_cases/null_sections.ld @@ -0,0 +1,194 @@ +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_main.o(.text*); + build/src/dmadata.o(.text*); + build/asm/util.o(.text*); + boot_TEXT_END = .; + boot_TEXT_SIZE = ABSOLUTE(boot_TEXT_END - boot_TEXT_START); + + boot_DATA_START = .; + build/src/boot_main.o(.data*); + build/src/dmadata.o(.data*); + build/asm/util.o(.data*); + boot_DATA_END = .; + boot_DATA_SIZE = ABSOLUTE(boot_DATA_END - boot_DATA_START); + + boot_RODATA_START = .; + build/src/boot_main.o(.rodata*); + build/src/dmadata.o(.rodata*); + build/asm/util.o(.rodata*); + boot_RODATA_END = .; + boot_RODATA_SIZE = ABSOLUTE(boot_RODATA_END - boot_RODATA_START); + + boot_SDATA_START = .; + build/src/boot_main.o(.sdata*); + build/src/dmadata.o(.sdata*); + build/asm/util.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_main.o(.sbss*); + build/src/dmadata.o(.sbss*); + build/asm/util.o(.sbss*); + boot_SBSS_END = .; + boot_SBSS_SIZE = ABSOLUTE(boot_SBSS_END - boot_SBSS_START); + + boot_SCOMMON_START = .; + build/src/boot_main.o(.scommon*); + build/src/dmadata.o(.scommon*); + build/asm/util.o(.scommon*); + boot_SCOMMON_END = .; + boot_SCOMMON_SIZE = ABSOLUTE(boot_SCOMMON_END - boot_SCOMMON_START); + + boot_BSS_START = .; + build/src/boot_main.o(.bss*); + build/src/dmadata.o(.bss*); + build/asm/util.o(.bss*); + boot_BSS_END = .; + boot_BSS_SIZE = ABSOLUTE(boot_BSS_END - boot_BSS_START); + + bootCOMMON_START = .; + build/src/boot_main.o(COMMON*); + build/src/dmadata.o(COMMON*); + build/asm/util.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); + + buffers_ROM_START = __romPos; + buffers_VRAM = ADDR(.buffers); + + buffers_noload_VRAM = .; + + .buffers.noload (NOLOAD) : + { + FILL(0x00000000); + buffers_SBSS_START = .; + build/src/arenabuffer.o(.sbss*); + build/src/scratchpad.o(.sbss*); + build/src/framebuffer.o(.sbss*); + buffers_SBSS_END = .; + buffers_SBSS_SIZE = ABSOLUTE(buffers_SBSS_END - buffers_SBSS_START); + + buffers_SCOMMON_START = .; + build/src/arenabuffer.o(.scommon*); + build/src/scratchpad.o(.scommon*); + build/src/framebuffer.o(.scommon*); + buffers_SCOMMON_END = .; + buffers_SCOMMON_SIZE = ABSOLUTE(buffers_SCOMMON_END - buffers_SCOMMON_START); + + buffers_BSS_START = .; + build/src/arenabuffer.o(.bss*); + build/src/scratchpad.o(.bss*); + build/src/framebuffer.o(.bss*); + buffers_BSS_END = .; + buffers_BSS_SIZE = ABSOLUTE(buffers_BSS_END - buffers_BSS_START); + + buffersCOMMON_START = .; + build/src/arenabuffer.o(COMMON*); + build/src/scratchpad.o(COMMON*); + build/src/framebuffer.o(COMMON*); + buffersCOMMON_END = .; + buffersCOMMON_SIZE = ABSOLUTE(buffersCOMMON_END - buffersCOMMON_START); + } + + buffers_noload_VRAM_END = .; + buffers_noload_VRAM_SIZE = ABSOLUTE(buffers_noload_VRAM_END - buffers_noload_VRAM); + + __romPos += SIZEOF(.buffers); + buffers_VRAM_END = .; + buffers_VRAM_SIZE = ABSOLUTE(buffers_VRAM_END - buffers_VRAM); + buffers_ROM_END = __romPos; + buffers_ROM_SIZE = ABSOLUTE(buffers_ROM_END - buffers_ROM_START); + + assets_ROM_START = __romPos; + assets_VRAM = ADDR(.assets); + assets_alloc_VRAM = .; + + .assets : AT(assets_ROM_START) + { + FILL(0x00000000); + assets_TEXT_START = .; + build/src/font.o(.text*); + build/src/music.o(.text*); + build/src/videos.o(.text*); + assets_TEXT_END = .; + assets_TEXT_SIZE = ABSOLUTE(assets_TEXT_END - assets_TEXT_START); + + assets_DATA_START = .; + build/src/font.o(.data*); + build/src/music.o(.data*); + build/src/videos.o(.data*); + assets_DATA_END = .; + assets_DATA_SIZE = ABSOLUTE(assets_DATA_END - assets_DATA_START); + + assets_RODATA_START = .; + build/src/font.o(.rodata*); + build/src/music.o(.rodata*); + build/src/videos.o(.rodata*); + assets_RODATA_END = .; + assets_RODATA_SIZE = ABSOLUTE(assets_RODATA_END - assets_RODATA_START); + + assets_SDATA_START = .; + build/src/font.o(.sdata*); + build/src/music.o(.sdata*); + build/src/videos.o(.sdata*); + assets_SDATA_END = .; + assets_SDATA_SIZE = ABSOLUTE(assets_SDATA_END - assets_SDATA_START); + } + + assets_alloc_VRAM_END = .; + assets_alloc_VRAM_SIZE = ABSOLUTE(assets_alloc_VRAM_END - assets_alloc_VRAM); + + + __romPos += SIZEOF(.assets); + assets_VRAM_END = .; + assets_VRAM_SIZE = ABSOLUTE(assets_VRAM_END - assets_VRAM); + assets_ROM_END = __romPos; + assets_ROM_SIZE = ABSOLUTE(assets_ROM_END - assets_ROM_START); + + .symtab 0 : { *(.symtab); } + .strtab 0 : { *(.strtab); } + .shstrtab 0 : { *(.shstrtab); } + + /DISCARD/ : + { + *(.reginfo); + *(.MIPS.abiflags); + *(.MIPS.options); + *(.note.gnu.build-id); + *(.interp); + *(.eh_frame); + *(.got); + *(*); + } +} diff --git a/tests/test_cases/null_sections.yaml b/tests/test_cases/null_sections.yaml new file mode 100644 index 0000000..cce7d55 --- /dev/null +++ b/tests/test_cases/null_sections.yaml @@ -0,0 +1,25 @@ +settings: + base_path: build + symbols_header_path: tests/test_cases/null_sections.h + +segments: + + - name: boot + files: + - { path: src/boot_main.o } + - { path: src/dmadata.o } + - { path: asm/util.o } + + - name: buffers + alloc_sections: null + files: + - { path: src/arenabuffer.o } + - { path: src/scratchpad.o } + - { path: src/framebuffer.o } + + - name: assets + noload_sections: null + files: + - { path: src/font.o } + - { path: src/music.o } + - { path: src/videos.o }