| 104 | } |
| 105 | |
| 106 | std::size_t ElfCoreStream::read(u64 offset, void* out_v, std::size_t len) { |
| 107 | u8* out = static_cast<u8*>(out_v); |
| 108 | std::memset(out, 0, len); |
| 109 | if (offset >= total_size_) return len; // past EOF → zero-filled (POSIX) |
| 110 | if (offset + len > total_size_) len = static_cast<std::size_t>(total_size_ - offset); |
| 111 | |
| 112 | std::size_t written = 0; |
| 113 | u64 cur = offset; |
| 114 | u64 remaining = len; |
| 115 | |
| 116 | // 1) Serve any bytes that fall inside the precomputed header blob. |
| 117 | if (cur < header_blob_.size()) { |
| 118 | std::size_t take = static_cast<std::size_t>( |
| 119 | std::min<u64>(header_blob_.size() - cur, remaining)); |
| 120 | std::memcpy(out, header_blob_.data() + cur, take); |
| 121 | out += take; cur += take; remaining -= take; written += take; |
| 122 | } |
| 123 | if (remaining == 0) return written; |
| 124 | |
| 125 | // 2) Walk segments; for each that overlaps, pull only the touched bytes |
| 126 | // through the per-process page cache. Repeated reads of the same page |
| 127 | // (typical for editors / hex viewers) become pure memcpy. |
| 128 | for (const auto& s : segs_) { |
| 129 | u64 s_end_file = s.file_off + s.file_len; |
| 130 | if (s_end_file <= cur) continue; |
| 131 | if (s.file_off >= cur + remaining) break; |
| 132 | |
| 133 | u64 hit_start = std::max(cur, s.file_off); |
| 134 | u64 hit_end = std::min(cur + remaining, s_end_file); |
| 135 | std::size_t hit_len = static_cast<std::size_t>(hit_end - hit_start); |
| 136 | u64 vm_addr = s.vm_start + (hit_start - s.file_off); |
| 137 | |
| 138 | page_cache_.read(*user_pt_, vm_addr, out + (hit_start - cur), hit_len); |
| 139 | written += hit_len; |
| 140 | } |
| 141 | return len; // total advertised, including zero-fill for any sparse gap |
| 142 | } |
| 143 | |
| 144 | } // namespace lmpfs::linux |