Skip to content

Commit 86a4afc

Browse files
0xrinegadeclaude
andcommitted
feat(tui): Enhanced global search modal with advanced UX
Major UX improvements to the blockchain search experience: 🎨 Visual Enhancements: - Larger modal (90x30) with animated blinking cursor - Score bars showing match relevance (0-100%) - Entity-specific colored icons (👛🪙⚙️📜🕒) - Loading spinner with 10-frame animation - Gradient header with lightning bolts - Smart truncation for long addresses (keeps both ends visible) - Double-bordered modal with cyan accent 🧠 Smart Search Algorithm: - Fuzzy match scoring system (100=exact, 90=prefix, 70=contains) - Entity type auto-detection by string characteristics: * 32-44 chars alphanumeric → Wallet/Program * 80+ chars → Transaction signature * 3-10 uppercase chars → Token symbol - Results sorted by relevance score - Duplicate removal in search history 💾 Persistence: - Search history saved to ~/.osvm/search_history.json - Loads previous searches on startup - Max 5 recent searches with FIFO queue ⚡ Real Search Execution: - Wallet search → switches to Dashboard tab - Token search → switches to Graph tab - Transaction → switches to Logs tab - Updates target_wallet and triggers investigation - Logs formatted with emoji indicators 🎯 UX Polish: - Helpful hints when no results found - Keyboard shortcuts footer (↑↓ ⏎ ESC Ctrl+K) - Error state display with red styling - 'Recent Searches' vs 'Smart Suggestions' headers - Selection highlighting with bold + underline - Up to 10 visible results with overflow indicator Technical: - Added SearchResult struct for future preview data - Added match_score field to SearchSuggestion - Implemented calculate_match_score() method - Added entity_type_str() helper - Added load/save_search_history() methods - Added truncate_address() for display 🔧 Generated with Claude Code Co-Authored-By: Claude <[email protected]>
1 parent 79b4cf5 commit 86a4afc

File tree

3 files changed

+655
-8
lines changed

3 files changed

+655
-8
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Detailed ELF parser debugging
2+
use std::mem;
3+
4+
fn main() {
5+
let elf_path = std::env::args()
6+
.nth(1)
7+
.unwrap_or_else(|| "/tmp/hello_final.so".to_string());
8+
9+
println!("📂 Loading: {}", elf_path);
10+
let elf_bytes = std::fs::read(&elf_path).expect("Failed to read ELF");
11+
println!(" Size: {} bytes\n", elf_bytes.len());
12+
13+
// Parse ELF header
14+
if elf_bytes.len() < 64 {
15+
eprintln!("❌ File too small for ELF header");
16+
return;
17+
}
18+
19+
// Read e_phoff and e_phnum
20+
let e_phoff = u64::from_le_bytes(elf_bytes[32..40].try_into().unwrap());
21+
let e_phnum = u16::from_le_bytes(elf_bytes[56..58].try_into().unwrap());
22+
23+
println!("📊 Program Headers: {} entries at offset 0x{:x}", e_phnum, e_phoff);
24+
25+
// Find PT_DYNAMIC
26+
let mut pt_dynamic_offset = 0usize;
27+
let mut pt_dynamic_size = 0usize;
28+
29+
for i in 0..e_phnum {
30+
let phdr_offset = e_phoff as usize + (i as usize * 56);
31+
let p_type = u32::from_le_bytes(elf_bytes[phdr_offset..phdr_offset+4].try_into().unwrap());
32+
33+
if p_type == 2 { // PT_DYNAMIC
34+
let p_offset = u64::from_le_bytes(elf_bytes[phdr_offset+8..phdr_offset+16].try_into().unwrap());
35+
let p_filesz = u64::from_le_bytes(elf_bytes[phdr_offset+32..phdr_offset+40].try_into().unwrap());
36+
pt_dynamic_offset = p_offset as usize;
37+
pt_dynamic_size = p_filesz as usize;
38+
println!("✅ Found PT_DYNAMIC: offset=0x{:x}, size=0x{:x}", p_offset, p_filesz);
39+
break;
40+
}
41+
}
42+
43+
if pt_dynamic_offset == 0 {
44+
println!("⚠️ No PT_DYNAMIC found");
45+
return;
46+
}
47+
48+
// Parse dynamic entries
49+
println!("\n📊 Dynamic Entries:");
50+
let mut dt_symtab = 0u64;
51+
let mut dt_strtab = 0u64;
52+
let mut dt_rel = 0u64;
53+
let mut dt_relsz = 0u64;
54+
let mut dt_relent = 0u64;
55+
56+
let mut offset = pt_dynamic_offset;
57+
while offset < pt_dynamic_offset + pt_dynamic_size {
58+
let d_tag = u64::from_le_bytes(elf_bytes[offset..offset+8].try_into().unwrap());
59+
let d_val = u64::from_le_bytes(elf_bytes[offset+8..offset+16].try_into().unwrap());
60+
61+
match d_tag {
62+
0 => { println!(" DT_NULL"); break; }
63+
5 => { dt_strtab = d_val; println!(" DT_STRTAB: 0x{:x}", d_val); }
64+
6 => { dt_symtab = d_val; println!(" DT_SYMTAB: 0x{:x}", d_val); }
65+
17 => { dt_rel = d_val; println!(" DT_REL: 0x{:x}", d_val); }
66+
18 => { dt_relsz = d_val; println!(" DT_RELSZ: {} bytes", d_val); }
67+
19 => { dt_relent = d_val; println!(" DT_RELENT: {} bytes", d_val); }
68+
30 => { println!(" DT_FLAGS: 0x{:x}", d_val); }
69+
_ => {}
70+
}
71+
72+
offset += 16;
73+
}
74+
75+
// Validation checks that RBPF does
76+
println!("\n🔍 RBPF Validation Checks:");
77+
78+
// Check 1: DT_RELENT must be 16
79+
if dt_relent != 0 && dt_relent != 16 {
80+
println!("❌ DT_RELENT is {} but must be 16", dt_relent);
81+
} else if dt_relent == 16 {
82+
println!("✅ DT_RELENT = 16");
83+
}
84+
85+
// Check 2: DT_RELSZ must be non-zero if DT_REL is set
86+
if dt_rel != 0 && dt_relsz == 0 {
87+
println!("❌ DT_REL is set but DT_RELSZ is 0");
88+
} else if dt_rel != 0 {
89+
println!("✅ DT_RELSZ = {} bytes", dt_relsz);
90+
}
91+
92+
// Check 3: Find section header for DT_SYMTAB
93+
let e_shoff = u64::from_le_bytes(elf_bytes[40..48].try_into().unwrap());
94+
let e_shnum = u16::from_le_bytes(elf_bytes[60..62].try_into().unwrap());
95+
96+
println!("\n📊 Checking section headers for DT_SYMTAB=0x{:x}", dt_symtab);
97+
98+
let mut found_symtab_section = false;
99+
for i in 0..e_shnum {
100+
let shdr_offset = e_shoff as usize + (i as usize * 64);
101+
let sh_addr = u64::from_le_bytes(elf_bytes[shdr_offset+16..shdr_offset+24].try_into().unwrap());
102+
let sh_type = u32::from_le_bytes(elf_bytes[shdr_offset+4..shdr_offset+8].try_into().unwrap());
103+
104+
if sh_addr == dt_symtab {
105+
println!("✅ Found section [{}] with sh_addr=0x{:x}, sh_type=0x{:x}", i, sh_addr, sh_type);
106+
found_symtab_section = true;
107+
108+
// Check if it's DYNSYM type (11)
109+
if sh_type != 11 {
110+
println!(" ⚠️ Type is 0x{:x}, expected SHT_DYNSYM (0xb)", sh_type);
111+
}
112+
}
113+
}
114+
115+
if !found_symtab_section {
116+
println!("❌ No section header found with sh_addr matching DT_SYMTAB!");
117+
println!(" This is likely why RBPF fails with 'invalid dynamic section table'");
118+
}
119+
120+
// Check PT_LOAD coverage
121+
println!("\n📊 Checking PT_LOAD coverage for dynamic addresses:");
122+
123+
for i in 0..e_phnum {
124+
let phdr_offset = e_phoff as usize + (i as usize * 56);
125+
let p_type = u32::from_le_bytes(elf_bytes[phdr_offset..phdr_offset+4].try_into().unwrap());
126+
127+
if p_type == 1 { // PT_LOAD
128+
let p_vaddr = u64::from_le_bytes(elf_bytes[phdr_offset+16..phdr_offset+24].try_into().unwrap());
129+
let p_memsz = u64::from_le_bytes(elf_bytes[phdr_offset+40..phdr_offset+48].try_into().unwrap());
130+
let p_end = p_vaddr + p_memsz;
131+
132+
println!(" PT_LOAD[{}]: 0x{:x}-0x{:x}", i, p_vaddr, p_end);
133+
134+
if dt_symtab >= p_vaddr && dt_symtab < p_end {
135+
println!(" ✅ Contains DT_SYMTAB (0x{:x})", dt_symtab);
136+
}
137+
if dt_strtab >= p_vaddr && dt_strtab < p_end {
138+
println!(" ✅ Contains DT_STRTAB (0x{:x})", dt_strtab);
139+
}
140+
if dt_rel >= p_vaddr && dt_rel < p_end {
141+
println!(" ✅ Contains DT_REL (0x{:x})", dt_rel);
142+
}
143+
}
144+
}
145+
}

crates/ovsm/src/compiler/elf.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,15 @@ impl ElfWriter {
398398
let dyn_sections_size = dynsym_size + dynstr_size + reldyn_size;
399399
self.write_phdr_aligned(&mut elf, PT_LOAD, PF_R, dynsym_offset, dynsym_vaddr, dyn_sections_size);
400400

401-
// PT_DYNAMIC: Just .dynamic section
402-
self.write_phdr_aligned(&mut elf, PT_DYNAMIC, PF_R | PF_W, dynamic_offset, dynamic_vaddr, dynamic_size);
401+
// PT_DYNAMIC: Just .dynamic section (needs 8-byte alignment, not page alignment)
402+
elf.extend_from_slice(&PT_DYNAMIC.to_le_bytes());
403+
elf.extend_from_slice(&(PF_R | PF_W).to_le_bytes());
404+
elf.extend_from_slice(&(dynamic_offset as u64).to_le_bytes());
405+
elf.extend_from_slice(&dynamic_vaddr.to_le_bytes());
406+
elf.extend_from_slice(&dynamic_vaddr.to_le_bytes());
407+
elf.extend_from_slice(&(dynamic_size as u64).to_le_bytes());
408+
elf.extend_from_slice(&(dynamic_size as u64).to_le_bytes());
409+
elf.extend_from_slice(&0x8u64.to_le_bytes()); // 8-byte alignment for dynamic entries
403410

404411
// Padding to 0x1000
405412
while elf.len() < text_offset {

0 commit comments

Comments
 (0)