Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/elf/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum RangeType {
ShdrField(&'static str),
Segment(u16),
Section(u16),
StrtabString(u16, u32),
SegmentSubrange,
}

Expand Down Expand Up @@ -96,6 +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(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)
Expand Down Expand Up @@ -284,6 +288,52 @@ 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, u16, u32)> = Vec::new();

Comment on lines +295 to +297
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_strtab_ranges builds a Vec of all string ranges (str_ranges) and then iterates again to add them to self.ranges. This can double memory usage for large string tables. You can usually avoid the temporary vector by borrowing disjoint fields (let (ranges, shdrs, contents) = (&mut self.ranges, &self.shdrs, self.contents);) and calling ranges.add_range(...) directly while iterating.

Copilot uses AI. Check for mistakes.
for (section_idx, shdr) in self.shdrs.iter().enumerate() {
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;
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,
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,
section_idx as u16,
str_idx,
));
}
}

for (start, len, section_idx, str_idx) in str_ranges {
self.ranges
.add_range(start, len, RangeType::StrtabString(section_idx, str_idx));
}
}

fn parse_notes(&mut self, endianness: u8) {
Expand Down
43 changes: 43 additions & 0 deletions src/js/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment on lines +56 to +71
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds a document-level mouseover handler that runs on every mouseover anywhere in the page, clearing/re-adding classes and doing a querySelectorAll each time. On large dumps this can become a noticeable hotspot. Consider either (1) using event delegation but tracking the last refClass and returning early when it hasn't changed, and/or (2) attaching mouseenter/mouseleave listeners only to .strtab_str elements and pre-indexing .strtab_entry nodes by refClass.

Copilot uses AI. Check for mistakes.
}
32 changes: 27 additions & 5 deletions src/report_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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, "<tr>");
w!(o, 7, "<td></td>");
Expand All @@ -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,
"<span class='strtab_entry strtab_ref{}_{}'>{}</span>",
section_idx,
str_idx,
escape_html_str(string)
);
str_idx += 1;
Comment on lines +318 to +326
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_strtab_ranges() (parser side) adds a final string range when the section doesn't end with \0, but this renderer only emits entries when it hits a NUL terminator. That can leave highlighted .strtab_str ranges with no matching .strtab_entry (sync highlight no-ops). Consider emitting a final entry after the loop when curr_start < section.len() to match the range-generation behavior.

Copilot uses AI. Check for mistakes.
}
}
Comment on lines 316 to 328
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sync-highlighting is keyed off str_idx, but this code only emits .strtab_entry elements for strings where std::str::from_utf8(...) succeeds. add_strtab_ranges() will still create .strtab_str spans for non-UTF8 byte sequences, so hovering those bytes won't highlight anything on the right. Consider rendering non-UTF8 strings using a lossless/escaped representation (e.g., from_utf8_lossy or hex-escaped bytes) so every highlighted range has a corresponding entry.

Copilot uses AI. Check for mistakes.

Expand All @@ -315,11 +336,11 @@ fn generate_strtab_data(o: &mut String, section: &[u8]) {
w!(o, 6, "</tr>");
}

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);
}
}

Expand Down Expand Up @@ -359,7 +380,7 @@ fn generate_section_info_tables(o: &mut String, elf: &ParsedElf) {

if has_section_detail(shdr.shtype) {
w!(o, 6, "<tr><td><br></td></tr>");
generate_section_info_table(o, elf, shdr);
generate_section_info_table(o, elf, idx, shdr);
}

w!(o, 5, "</table>");
Expand Down Expand Up @@ -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, "</script>");
}
Expand Down
9 changes: 9 additions & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ li {
.section {
background-color: #f9f;
}
.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(
Expand Down
Loading