From b23dad0b92d035001841c8f82728381551e68adc Mon Sep 17 00:00:00 2001 From: harkerhand Date: Sun, 22 Mar 2026 16:23:39 +0800 Subject: [PATCH 1/2] parser: add StrtabString variant and implement string table range handling style: highlight strtab_str on hover --- src/elf/parser.rs | 35 +++++++++++++++++++++++++++++++++++ src/style.css | 3 +++ 2 files changed, 38 insertions(+) diff --git a/src/elf/parser.rs b/src/elf/parser.rs index c090281..3d3820d 100644 --- a/src/elf/parser.rs +++ b/src/elf/parser.rs @@ -21,6 +21,7 @@ pub enum RangeType { ShdrField(&'static str), Segment(u16), Section(u16), + StrtabString, SegmentSubrange, } @@ -96,6 +97,7 @@ impl RangeType { RangeType::SectionHeader(idx) => format!("class='bin_shdr{} shdr'", idx), RangeType::Segment(idx) => format!("class='bin_segment{} segment'", idx), RangeType::Section(idx) => format!("class='bin_section{} section hover'", idx), + RangeType::StrtabString => "class='strtab_str'".to_string(), RangeType::SegmentSubrange => "class='segment_subrange hover'".to_string(), RangeType::HeaderField(field) | RangeType::PhdrField(field) @@ -284,6 +286,39 @@ impl ParsedElf<'_> { self.shnstrtab.populate(section, shdr.size); } + + self.add_strtab_ranges(); + } + + fn add_strtab_ranges(&mut self) { + let mut str_ranges: Vec<(usize, usize)> = Vec::new(); + + for shdr in &self.shdrs { + if shdr.shtype != SHT_STRTAB || shdr.size == 0 { + continue; + } + + let section_start = shdr.file_offset; + let section = &self.contents[section_start..section_start + shdr.size]; + let mut curr_start = 0; + + for (idx, byte) in section.iter().enumerate() { + if *byte == 0 { + if idx > curr_start { + str_ranges.push((section_start + curr_start, idx - curr_start)); + } + curr_start = idx + 1; + } + } + + if curr_start < section.len() { + str_ranges.push((section_start + curr_start, section.len() - curr_start)); + } + } + + for (start, len) in str_ranges { + self.ranges.add_range(start, len, RangeType::StrtabString); + } } fn parse_notes(&mut self, endianness: u8) { diff --git a/src/style.css b/src/style.css index 0538335..a57d0db 100644 --- a/src/style.css +++ b/src/style.css @@ -223,6 +223,9 @@ li { .section { background-color: #f9f; } +.strtab_str:hover { + background-color: #9ee; +} /* there are only sections inside segments due to code */ .segment > .section, .segm_sect_legend { background: repeating-linear-gradient( From 5d1d361637fa053c7b60de1e4d32a1c31c715b3e Mon Sep 17 00:00:00 2001 From: harkerhand Date: Sun, 22 Mar 2026 16:33:45 +0800 Subject: [PATCH 2/2] parser: update StrtabString variant to include section and string indices; enhance string table range handling js: add highlighting functionality for strtab sections report_gen: modify string table generation to include section and string indices; escape HTML characters style: add styles for strtab entries and active highlighting --- src/elf/parser.rs | 31 +++++++++++++++++++++++-------- src/js/highlight.js | 43 +++++++++++++++++++++++++++++++++++++++++++ src/report_gen.rs | 32 +++++++++++++++++++++++++++----- src/style.css | 6 ++++++ 4 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/elf/parser.rs b/src/elf/parser.rs index 3d3820d..616252e 100644 --- a/src/elf/parser.rs +++ b/src/elf/parser.rs @@ -21,7 +21,7 @@ pub enum RangeType { ShdrField(&'static str), Segment(u16), Section(u16), - StrtabString, + StrtabString(u16, u32), SegmentSubrange, } @@ -97,7 +97,9 @@ impl RangeType { RangeType::SectionHeader(idx) => format!("class='bin_shdr{} shdr'", idx), RangeType::Segment(idx) => format!("class='bin_segment{} segment'", idx), RangeType::Section(idx) => format!("class='bin_section{} section hover'", idx), - RangeType::StrtabString => "class='strtab_str'".to_string(), + RangeType::StrtabString(section_idx, str_idx) => { + format!("class='strtab_str strtab_ref{}_{}'", section_idx, str_idx) + } RangeType::SegmentSubrange => "class='segment_subrange hover'".to_string(), RangeType::HeaderField(field) | RangeType::PhdrField(field) @@ -291,9 +293,9 @@ impl ParsedElf<'_> { } fn add_strtab_ranges(&mut self) { - let mut str_ranges: Vec<(usize, usize)> = Vec::new(); + let mut str_ranges: Vec<(usize, usize, u16, u32)> = Vec::new(); - for shdr in &self.shdrs { + for (section_idx, shdr) in self.shdrs.iter().enumerate() { if shdr.shtype != SHT_STRTAB || shdr.size == 0 { continue; } @@ -301,23 +303,36 @@ impl ParsedElf<'_> { let section_start = shdr.file_offset; let section = &self.contents[section_start..section_start + shdr.size]; let mut curr_start = 0; + let mut str_idx: u32 = 0; for (idx, byte) in section.iter().enumerate() { if *byte == 0 { if idx > curr_start { - str_ranges.push((section_start + curr_start, idx - curr_start)); + str_ranges.push(( + section_start + curr_start, + idx - curr_start, + section_idx as u16, + str_idx, + )); + str_idx += 1; } curr_start = idx + 1; } } if curr_start < section.len() { - str_ranges.push((section_start + curr_start, section.len() - curr_start)); + str_ranges.push(( + section_start + curr_start, + section.len() - curr_start, + section_idx as u16, + str_idx, + )); } } - for (start, len) in str_ranges { - self.ranges.add_range(start, len, RangeType::StrtabString); + for (start, len, section_idx, str_idx) in str_ranges { + self.ranges + .add_range(start, len, RangeType::StrtabString(section_idx, str_idx)); } } diff --git a/src/js/highlight.js b/src/js/highlight.js index 669fc07..0bb8d32 100644 --- a/src/js/highlight.js +++ b/src/js/highlight.js @@ -27,3 +27,46 @@ function highlightClasses(primaryClass, secondaryClass) { addPairHighlighting(secondaryElem, primaryElem); } +function addStrtabSectionSyncHighlighting() { + let activeEntries = []; + + function clearActiveEntries() { + for (let entry of activeEntries) { + entry.classList.remove("strtab_entry_active"); + } + activeEntries = []; + } + + function getRefClass(target) { + let strElem = target.closest(".strtab_str"); + + if (strElem === null) { + return null; + } + + for (let cls of strElem.classList) { + if (cls.startsWith("strtab_ref")) { + return cls; + } + } + + return null; + } + + document.addEventListener("mouseover", function(event) { + let refClass = getRefClass(event.target); + + clearActiveEntries(); + + if (refClass === null) { + return; + } + + let entries = document.querySelectorAll(".strtab_entry." + refClass); + + for (let entry of entries) { + entry.classList.add("strtab_entry_active"); + activeEntries.push(entry); + } + }, false); +} diff --git a/src/report_gen.rs b/src/report_gen.rs index b7ef0ab..5ed1956 100644 --- a/src/report_gen.rs +++ b/src/report_gen.rs @@ -235,6 +235,18 @@ fn format_string_slice(slice: &[u8]) -> String { .fold(String::new(), |s, b| s + &format_string_byte(*b)) } +fn escape_html_str(string: &str) -> String { + string.chars().fold(String::new(), |mut escaped, ch| { + if let Some(entity) = utils::html_escape(ch) { + escaped.push_str(entity); + } else { + escaped.push(ch); + } + + escaped + }) +} + fn generate_note_data(o: &mut String, note: &Note) { let name = if note.name.is_empty() { String::new() @@ -286,8 +298,9 @@ fn generate_segment_info_table(o: &mut String, elf: &ParsedElf, phdr: &ParsedPhd } } -fn generate_strtab_data(o: &mut String, section: &[u8]) { +fn generate_strtab_data(o: &mut String, section_idx: usize, section: &[u8]) { let mut curr_start = 0; + let mut str_idx: u32 = 0; w!(o, 6, ""); w!(o, 7, ""); @@ -302,7 +315,15 @@ fn generate_strtab_data(o: &mut String, section: &[u8]) { if let Ok(string) = maybe { if section[curr_start] != 0 { - w!(o, 9, "{}", string); + w!( + o, + 9, + "{}", + section_idx, + str_idx, + escape_html_str(string) + ); + str_idx += 1; } } @@ -315,11 +336,11 @@ fn generate_strtab_data(o: &mut String, section: &[u8]) { w!(o, 6, ""); } -fn generate_section_info_table(o: &mut String, elf: &ParsedElf, shdr: &ParsedShdr) { +fn generate_section_info_table(o: &mut String, elf: &ParsedElf, idx: usize, shdr: &ParsedShdr) { let section = &elf.contents[shdr.file_offset..shdr.file_offset + shdr.size]; if shdr.shtype == SHT_STRTAB { - generate_strtab_data(o, section); + generate_strtab_data(o, idx, section); } } @@ -359,7 +380,7 @@ fn generate_section_info_tables(o: &mut String, elf: &ParsedElf) { if has_section_detail(shdr.shtype) { w!(o, 6, "
"); - generate_section_info_table(o, elf, shdr); + generate_section_info_table(o, elf, idx, shdr); } w!(o, 5, ""); @@ -415,6 +436,7 @@ fn add_highlight_script(o: &mut String) { for class in &classes { w!(o, 3, "highlightClasses('fileinfo_{}', '{}')", class, class); } + w!(o, 3, "addStrtabSectionSyncHighlighting();"); w!(o, 2, ""); } diff --git a/src/style.css b/src/style.css index a57d0db..35b9621 100644 --- a/src/style.css +++ b/src/style.css @@ -226,6 +226,12 @@ li { .strtab_str:hover { background-color: #9ee; } +.strtab_entry { + display: block; +} +.strtab_entry_active { + background-color: #9ee; +} /* there are only sections inside segments due to code */ .segment > .section, .segm_sect_legend { background: repeating-linear-gradient(