feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+254
@@ -0,0 +1,254 @@
|
||||
use object::{build, elf};
|
||||
|
||||
// Test that offset 0 is supported for SHT_NOBITS sections.
|
||||
#[test]
|
||||
fn test_nobits_offset() {
|
||||
let mut builder = build::elf::Builder::new(object::Endianness::Little, true);
|
||||
builder.header.e_type = elf::ET_EXEC;
|
||||
builder.header.e_phoff = 0x40;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".shstrtab"[..].into();
|
||||
section.sh_type = elf::SHT_STRTAB;
|
||||
section.data = build::elf::SectionData::SectionString;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".bss"[..].into();
|
||||
section.sh_type = elf::SHT_NOBITS;
|
||||
section.sh_flags = (elf::SHF_ALLOC | elf::SHF_WRITE) as u64;
|
||||
section.sh_addr = 0x1000;
|
||||
section.sh_offset = 0;
|
||||
section.sh_size = 0x1000;
|
||||
section.sh_addralign = 16;
|
||||
section.data = build::elf::SectionData::UninitializedData(0x1000);
|
||||
let section_id = section.id();
|
||||
|
||||
let segment = builder.segments.add();
|
||||
segment.p_type = elf::PT_LOAD;
|
||||
segment.p_flags = elf::PF_R | elf::PF_W;
|
||||
segment.p_offset = 0x1000;
|
||||
segment.p_vaddr = 0x1000;
|
||||
segment.p_paddr = 0x1000;
|
||||
segment.p_filesz = 0;
|
||||
segment.p_memsz = 0x1000;
|
||||
segment.p_align = 16;
|
||||
segment.sections.push(section_id);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
builder.write(&mut buf).unwrap();
|
||||
}
|
||||
|
||||
// Test that we can read and write a file with no dynamic string table.
|
||||
#[test]
|
||||
fn test_no_dynstr() {
|
||||
let mut builder = build::elf::Builder::new(object::Endianness::Little, true);
|
||||
builder.header.e_type = elf::ET_EXEC;
|
||||
builder.header.e_machine = elf::EM_X86_64;
|
||||
builder.header.e_phoff = 0x40;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".shstrtab"[..].into();
|
||||
section.sh_type = elf::SHT_STRTAB;
|
||||
section.data = build::elf::SectionData::SectionString;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".dynsym"[..].into();
|
||||
section.sh_type = elf::SHT_DYNSYM;
|
||||
section.sh_flags = elf::SHF_ALLOC as u64;
|
||||
section.sh_addralign = 8;
|
||||
section.data = build::elf::SectionData::DynamicSymbol;
|
||||
let dynsym_id = section.id();
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".rela.dyn"[..].into();
|
||||
section.sh_type = elf::SHT_RELA;
|
||||
section.sh_flags = elf::SHF_ALLOC as u64;
|
||||
section.sh_addralign = 8;
|
||||
section.data =
|
||||
build::elf::SectionData::DynamicRelocation(vec![build::elf::DynamicRelocation {
|
||||
r_offset: 0x1000,
|
||||
symbol: None,
|
||||
r_type: elf::R_X86_64_64,
|
||||
r_addend: 0x300,
|
||||
}]);
|
||||
let rela_id = section.id();
|
||||
|
||||
builder.set_section_sizes();
|
||||
|
||||
let segment = builder.segments.add();
|
||||
segment.p_type = elf::PT_LOAD;
|
||||
segment.p_flags = elf::PF_R;
|
||||
segment.p_filesz = 0x1000;
|
||||
segment.p_memsz = 0x1000;
|
||||
segment.p_align = 8;
|
||||
segment.append_section(builder.sections.get_mut(dynsym_id));
|
||||
segment.append_section(builder.sections.get_mut(rela_id));
|
||||
|
||||
let mut buf = Vec::new();
|
||||
builder.write(&mut buf).unwrap();
|
||||
|
||||
let builder = build::elf::Builder::read(&*buf).unwrap();
|
||||
assert_eq!(builder.sections.count(), 3);
|
||||
assert_eq!(builder.segments.count(), 1);
|
||||
for section in &builder.sections {
|
||||
match §ion.data {
|
||||
build::elf::SectionData::DynamicSymbol => {
|
||||
assert_eq!(section.sh_offset, 0x1000);
|
||||
}
|
||||
build::elf::SectionData::DynamicRelocation(rela) => {
|
||||
assert_eq!(section.sh_offset, 0x1018);
|
||||
assert_eq!(rela.len(), 1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attribute() {
|
||||
let mut builder = build::elf::Builder::new(object::Endianness::Little, true);
|
||||
builder.header.e_type = elf::ET_EXEC;
|
||||
builder.header.e_machine = elf::EM_X86_64;
|
||||
builder.header.e_phoff = 0x40;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".shstrtab"[..].into();
|
||||
section.sh_type = elf::SHT_STRTAB;
|
||||
section.data = build::elf::SectionData::SectionString;
|
||||
|
||||
let attributes = build::elf::AttributesSection {
|
||||
subsections: vec![build::elf::AttributesSubsection {
|
||||
vendor: b"GNU"[..].into(),
|
||||
subsubsections: vec![
|
||||
(build::elf::AttributesSubsubsection {
|
||||
tag: build::elf::AttributeTag::File,
|
||||
data: b"123"[..].into(),
|
||||
}),
|
||||
],
|
||||
}],
|
||||
};
|
||||
let section = builder.sections.add();
|
||||
section.name = b".gnu.attributes"[..].into();
|
||||
section.sh_type = elf::SHT_GNU_ATTRIBUTES;
|
||||
section.sh_addralign = 8;
|
||||
section.data = build::elf::SectionData::Attributes(attributes);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
builder.write(&mut buf).unwrap();
|
||||
|
||||
let builder = build::elf::Builder::read(&*buf).unwrap();
|
||||
assert_eq!(builder.sections.count(), 2);
|
||||
for section in &builder.sections {
|
||||
if let build::elf::SectionData::Attributes(attributes) = §ion.data {
|
||||
assert_eq!(attributes.subsections.len(), 1);
|
||||
assert_eq!(attributes.subsections[0].vendor.as_slice(), b"GNU");
|
||||
assert_eq!(attributes.subsections[0].subsubsections.len(), 1);
|
||||
assert_eq!(
|
||||
attributes.subsections[0].subsubsections[0].tag,
|
||||
build::elf::AttributeTag::File
|
||||
);
|
||||
assert_eq!(
|
||||
attributes.subsections[0].subsubsections[0].data.as_slice(),
|
||||
b"123"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dynsym() {
|
||||
let mut builder = build::elf::Builder::new(object::Endianness::Little, true);
|
||||
builder.header.e_type = elf::ET_EXEC;
|
||||
builder.header.e_machine = elf::EM_X86_64;
|
||||
builder.header.e_phoff = 0x40;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".shstrtab"[..].into();
|
||||
section.sh_type = elf::SHT_STRTAB;
|
||||
section.data = build::elf::SectionData::SectionString;
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".text"[..].into();
|
||||
section.sh_type = elf::SHT_PROGBITS;
|
||||
section.sh_flags = (elf::SHF_ALLOC | elf::SHF_EXECINSTR) as u64;
|
||||
section.sh_addralign = 16;
|
||||
section.data = build::elf::SectionData::Data(vec![0xcc; 100].into());
|
||||
let text_id = section.id();
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".dynsym"[..].into();
|
||||
section.sh_type = elf::SHT_DYNSYM;
|
||||
section.sh_flags = elf::SHF_ALLOC as u64;
|
||||
section.sh_addralign = 8;
|
||||
section.data = build::elf::SectionData::DynamicSymbol;
|
||||
let dynsym_id = section.id();
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".dynstr"[..].into();
|
||||
section.sh_type = elf::SHT_STRTAB;
|
||||
section.sh_flags = elf::SHF_ALLOC as u64;
|
||||
section.sh_addralign = 1;
|
||||
section.data = build::elf::SectionData::DynamicString;
|
||||
let dynstr_id = section.id();
|
||||
|
||||
let section = builder.sections.add();
|
||||
section.name = b".gnu.hash"[..].into();
|
||||
section.sh_type = elf::SHT_GNU_HASH;
|
||||
section.sh_flags = elf::SHF_ALLOC as u64;
|
||||
section.sh_addralign = 8;
|
||||
section.data = build::elf::SectionData::GnuHash;
|
||||
let gnu_hash_id = section.id();
|
||||
builder.gnu_hash_bloom_shift = 1;
|
||||
builder.gnu_hash_bloom_count = 1;
|
||||
builder.gnu_hash_bucket_count = 1;
|
||||
|
||||
let symbol = builder.dynamic_symbols.add();
|
||||
symbol.name = b"global"[..].into();
|
||||
symbol.set_st_info(elf::STB_GLOBAL, elf::STT_FUNC);
|
||||
symbol.section = Some(text_id);
|
||||
|
||||
let symbol = builder.dynamic_symbols.add();
|
||||
symbol.name = b"undefined"[..].into();
|
||||
symbol.set_st_info(elf::STB_GLOBAL, elf::STT_NOTYPE);
|
||||
|
||||
let symbol = builder.dynamic_symbols.add();
|
||||
symbol.name = b"local"[..].into();
|
||||
symbol.set_st_info(elf::STB_LOCAL, elf::STT_FUNC);
|
||||
symbol.section = Some(text_id);
|
||||
|
||||
builder.set_section_sizes();
|
||||
|
||||
let segment = builder.segments.add();
|
||||
segment.p_type = elf::PT_LOAD;
|
||||
segment.p_flags = elf::PF_R;
|
||||
segment.p_filesz = 0x1000;
|
||||
segment.p_memsz = 0x1000;
|
||||
segment.p_align = 8;
|
||||
segment.append_section(builder.sections.get_mut(text_id));
|
||||
segment.append_section(builder.sections.get_mut(dynsym_id));
|
||||
segment.append_section(builder.sections.get_mut(dynstr_id));
|
||||
segment.append_section(builder.sections.get_mut(gnu_hash_id));
|
||||
|
||||
let mut buf = Vec::new();
|
||||
builder.write(&mut buf).unwrap();
|
||||
|
||||
let builder = build::elf::Builder::read(&*buf).unwrap();
|
||||
assert_eq!(builder.sections.count(), 5);
|
||||
assert_eq!(builder.dynamic_symbols.count(), 3);
|
||||
// Check that the dynamic symbol table sorting handles
|
||||
// local and undefined symbols correctly.
|
||||
assert_eq!(
|
||||
builder
|
||||
.dynamic_symbols
|
||||
.iter()
|
||||
.map(|s| s.name.as_slice())
|
||||
.collect::<Vec<_>>(),
|
||||
vec![&b"local"[..], &b"undefined"[..], &b"global"[..]]
|
||||
);
|
||||
for section in &builder.sections {
|
||||
if let build::elf::SectionData::DynamicSymbol = §ion.data {
|
||||
// Check that sh_info includes the number of local symbols.
|
||||
assert_eq!(section.sh_info, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
#![cfg(feature = "build")]
|
||||
|
||||
mod elf;
|
||||
Reference in new issue
Block a user