1use goblin;
2use goblin::Object;
3
4use arch::ISA;
5
6use yaxpeax_arch::{Arch, Address, AddressBase, AddressDisplay};
7use memory::repr::FlatMemoryRepr;
8use memory::repr::ReadCursor;
9use memory::{LayoutError, MemoryRange, MemoryRepr, Named, PatchyMemoryRepr};
10use std::ops::Range;
11
12#[derive(Debug)]
13pub struct PESymbol {
14 pub name: String,
15 pub va: u64
16}
17#[derive(Debug)]
18pub struct PEReloc {}
19#[derive(Debug)]
20pub struct PEImport {
21 pub name: String,
22 pub dll: String,
23 ordinal: u16,
24 pub offset: usize,
25 pub rva: usize,
26 size: usize
27}
28impl <'a, 'b> From<&'b goblin::pe::import::Import<'a>> for PEImport {
29 fn from(imp: &'b goblin::pe::import::Import<'a>) -> PEImport {
30 match imp {
31 goblin::pe::import::Import {
32 name,
33 dll,
34 ordinal,
35 offset,
36 rva,
37 size
38 } => {
39 PEImport {
40 name: name.to_string(),
41 dll: dll.to_string(),
42 ordinal: *ordinal,
43 offset: *offset,
44 rva: *rva,
45 size: *size
46 }
47 }
48 }
49 }
50}
51#[derive(Debug)]
52pub enum Reexport {
53 DLLName(String, String),
54 DLLOrdinal(String, usize)
55}
56impl <'a, 'b> From<&'b goblin::pe::export::Reexport<'a>> for Reexport {
57 fn from(reexp: &'b goblin::pe::export::Reexport<'a>) -> Reexport {
58 match reexp {
59 goblin::pe::export::Reexport::DLLName {
60 export,
61 lib
62 } => { Reexport::DLLName(lib.to_string(), export.to_string()) },
63 goblin::pe::export::Reexport::DLLOrdinal {
64 export,
67 ordinal
68 } => { Reexport::DLLOrdinal(export.to_string(), *ordinal) }
69 }
70 }
71}
72#[derive(Debug)]
73pub struct PEExport {
74 pub name: Option<String>,
75 pub offset: usize,
76 pub rva: usize,
77 size: usize,
78 reexport: Option<Reexport>
79}
80impl <'a, 'b> From<&'b goblin::pe::export::Export<'a>> for PEExport {
81 fn from(exp: &'b goblin::pe::export::Export<'a>) -> PEExport {
82 match exp {
83 goblin::pe::export::Export {
84 name,
85 offset,
86 rva,
87 size,
88 reexport
89 } => {
90 PEExport {
91 name: name.map(|x| x.to_string()),
92 offset: *offset,
93 rva: *rva,
94 size: *size,
95 reexport: reexport.as_ref().map(|x| x.into())
96 }
97 }
98 }
99 }
100}
101#[derive(Debug)]
102pub struct ELFSymbol {
103 pub name: String,
104 pub section_index: usize,
105 pub addr: u64
106}
107#[derive(Debug)]
108pub struct ELFReloc {}
109#[derive(Debug)]
110pub struct ELFImport {
111 pub name: String,
112 pub section_index: usize,
113 pub value: u64
114}
115#[derive(Clone, Debug)]
116pub struct ELFExport {
117 pub name: String,
118 pub section_index: usize,
119 pub addr: u64,
120}
121
122#[derive(Debug)]
123pub struct Segment {
124 pub start: usize,
125 pub data: Vec<u8>,
126 pub name: String
127}
128
129impl Segment {
130 pub fn contains<A: Address>(&self, addr: A) -> bool {
131 let linear = addr.to_linear();
132 if linear < self.start {
133 false
134 } else if linear - self.start >= self.data.len() {
135 false
136 } else {
137 true
138 }
139 }
140 pub fn start(&self) -> usize {
141 self.start
142 }
143 pub fn end(&self) -> usize {
144 self.start + self.data.len()
145 }
146}
147
148impl Named for Segment {
149 fn name(&self) -> &str {
150 &self.name
151 }
152}
153
154impl <A: Arch> MemoryRepr<A> for Segment {
155 fn read(&self, addr: A::Address) -> Option<u8> {
156 if self.contains(addr) {
157 Some(self.data[addr.to_linear() - self.start])
158 } else {
159 None
160 }
161 }
162 fn as_flat(&self) -> Option<FlatMemoryRepr> {
163 None
164 }
165 fn module_info(&self) -> Option<&ModuleInfo> { None }
166 fn module_for(&self, addr: A::Address) -> Option<&dyn MemoryRepr<A>> {
167 if self.contains(addr) {
168 Some(self)
169 } else {
170 None
171 }
172 }
173 fn size(&self) -> Option<u64> {
174 Some(self.data.len() as u64)
175 }
176 fn start(&self) -> Option<u64> {
177 Some(self.start as u64)
178 }
179}
180
181#[derive(Debug)]
182pub enum ISAHint {
183 Hint(crate::arch::ISA),
184 Unknown(String)
185}
186
187#[derive(Debug)]
188pub struct ELFSection {
189 pub name: String,
190 pub start: u64,
191 pub size: u64,
192}
193
194#[derive(Debug)]
195pub enum ModuleInfo {
196 PE(ISAHint, goblin::pe::header::Header, Vec<goblin::pe::section_table::SectionTable>, u64, Vec<PEReloc>, Vec<PEImport>, Vec<PEExport>, Vec<PESymbol>),
197 ELF(ISAHint, goblin::elf::header::Header, Vec<goblin::elf::program_header::ProgramHeader>, Vec<ELFSection>, u64, Vec<ELFReloc>, Vec<ELFImport>, Vec<ELFExport>, Vec<ELFSymbol>)
198 }
202
203impl ModuleInfo {
204 pub fn isa_hint(&self) -> &ISAHint {
205 match self {
206 ModuleInfo::PE(hint, _, _, _, _, _, _, _) |
207 ModuleInfo::ELF(hint, _, _, _, _, _, _, _, _) => {
208 hint
209 }
210 }
211 }
212}
213
214mod elf {
215 pub(crate) mod program_header {
216 pub(crate) fn type_to_str(machine: u16, tpe: u32) -> String {
217 match tpe {
218 goblin::elf::program_header::PT_NULL => "NULL".to_string(),
219 goblin::elf::program_header::PT_LOAD => "LOAD".to_string(),
220 goblin::elf::program_header::PT_DYNAMIC => "DYNAMIC".to_string(),
221 goblin::elf::program_header::PT_INTERP => "INTERP".to_string(),
222 goblin::elf::program_header::PT_NOTE => "NOTE".to_string(),
223 goblin::elf::program_header::PT_SHLIB => "SHLIB".to_string(),
224 goblin::elf::program_header::PT_PHDR => "PHDR".to_string(),
225 goblin::elf::program_header::PT_TLS => "TLS".to_string(),
226 other => {
227 if other >= goblin::elf::program_header::PT_LOOS && other <= goblin::elf::program_header::PT_HIOS {
228 match other {
230 goblin::elf::program_header::PT_GNU_EH_FRAME => {
231 "GNU_EH_FRAME".to_string()
232 }
233 0x6464e550 => {
236 "SUNW_UNWIND".to_string()
237 }
238 goblin::elf::program_header::PT_GNU_STACK => {
239 "GNU_STACK".to_string()
240 }
241 goblin::elf::program_header::PT_GNU_RELRO => {
242 "GNU_RELRO".to_string()
243 }
244 0x65a3dbe6 => {
247 "OPENBSD_RANDOMIZE".to_string()
248 }
249 0x65a3dbe7 => {
251 "OPENBSD_WXNEEDED".to_string()
252 }
253 0x65a41be6 => {
255 "OPENBSD_BOOTDATA".to_string()
256 }
257 _ => {
258 format!("unknown OS section type: {:#x}", other)
259 }
260 }
261 } else if other >= goblin::elf::program_header::PT_LOPROC && other <= goblin::elf::program_header::PT_HIPROC {
262 match (machine, other) {
263 (goblin::elf::header::EM_ARM, 0x70000000) => {
264 "ARM_ARCHEXT".to_string()
265 }
266 (goblin::elf::header::EM_ARM, 0x70000001) => {
267 "ARM_UNWIND".to_string()
269 }
270 (goblin::elf::header::EM_MIPS, 0x70000000) => {
271 "MIPS_REGINFO".to_string()
273 }
274 (goblin::elf::header::EM_MIPS, 0x70000001) => {
275 "MIPS_RTPROC".to_string()
277 }
278 (goblin::elf::header::EM_MIPS, 0x70000002) => {
279 "MIPS_OPTIONS".to_string()
281 }
282 (goblin::elf::header::EM_MIPS, 0x70000003) => {
283 "MIPS_ABIFLAGS".to_string()
285 }
286 (machine, other) => {
287 format!("unknown machine-dependent section type, machine: {:#x} p_type: {:#x}", machine, other)
288 }
289 }
290 } else {
291 format!("unknown elf section type: {:#x}", other)
292 }
293 }
294 }
295 }
296 }
297}
298
299fn map_elf_machine(machine: u16) -> ISAHint {
300 match machine {
301 3 => {
302 ISAHint::Hint(ISA::x86)
304 },
305 20 => {
306 ISAHint::Hint(ISA::PowerPC)
307 },
308 40 => {
309 ISAHint::Hint(ISA::ARM)
311 }
312 41 => {
313 ISAHint::Hint(ISA::Alpha)
315 },
316 44 => {
340 ISAHint::Hint(ISA::Tricore)
342 },
343 50 => {
344 ISAHint::Hint(ISA::IA64)
346 },
347 62 => {
348 ISAHint::Hint(ISA::x86_64)
350 },
351 183 => { ISAHint::Hint(ISA::AArch64)
375 },
376 magic @ _ => {
377 ISAHint::Unknown(format!("Unknown machine magic: {:#x}", magic))
378 }
379 }
380}
381fn map_pe_machine(machine: u16) -> ISAHint {
382 match machine {
383 0x0000 => {
384 ISAHint::Unknown("PE machine field is 0".to_string())
386 },
387 0x014d => {
388 ISAHint::Unknown("Okay, I know this is an Intel I860, but I don't know what to do with that.".to_string())
390 },
391 0x014c => {
392 ISAHint::Hint(ISA::x86)
394 },
395 0x0162 => {
396 ISAHint::Hint(ISA::MIPS)
399 },
400 0x0166 => {
401 ISAHint::Hint(ISA::MIPS)
404 },
405 0x0168 => {
406 ISAHint::Hint(ISA::MIPS)
409 },
410 0x0169 => {
411 ISAHint::Hint(ISA::MIPS)
414 },
415 0x0184 => {
416 ISAHint::Hint(ISA::Alpha)
418 },
419 0x01a2 => {
420 ISAHint::Hint(ISA::SH3)
422 },
423 0x01a3 => {
424 ISAHint::Hint(ISA::SH3DSP)
426 },
427 0x01a4 => {
428 ISAHint::Hint(ISA::SH3E)
430 },
431 0x01a6 => {
432 ISAHint::Hint(ISA::SH4)
434 },
435 0x01a8 => {
436 ISAHint::Unknown("SuperH-5 never had shipping hardware...".to_string())
438 },
439 0x01c0 => {
440 ISAHint::Hint(ISA::ARM)
442 },
443 0x01c2 => {
444 ISAHint::Hint(ISA::ARM)
446 },
447 0x01c4 => {
448 ISAHint::Hint(ISA::ARM)
450 },
451 0x01d3 => {
452 ISAHint::Unknown("AM33?".to_string())
454 },
455 0x01f0 => {
456 ISAHint::Hint(ISA::PowerPC)
458 },
459 0x01f1 => {
460 ISAHint::Hint(ISA::PowerPC)
462 },
463 0x0200 => {
464 ISAHint::Hint(ISA::IA64)
466 },
467 0x0266 => {
468 ISAHint::Hint(ISA::MIPS)
470 },
471 0x0284 => {
472 ISAHint::Hint(ISA::Alpha64)
474 },
475 0x0366 => {
476 ISAHint::Hint(ISA::MIPS)
478 },
479 0x0466 => {
480 ISAHint::Hint(ISA::MIPS)
482 },
483 0x0520 => {
484 ISAHint::Hint(ISA::Tricore)
486 },
487 0x0cef => {
488 ISAHint::Unknown("Common Executable Format? Sounds fake".to_string())
491 },
492 0x0ebc => {
493 ISAHint::Unknown("EBC is unknown".to_string())
495 },
496 0x8664 => {
497 ISAHint::Hint(ISA::x86_64)
499 },
500 0x9041 => {
501 ISAHint::Unknown("M32R is unknown".to_string())
503 },
504 0xc0ee => {
505 ISAHint::Unknown("CEE is unknown".to_string())
507 },
508 0x01c5 => {
509 ISAHint::Hint(ISA::AArch64)
511 },
512 0xaa64 => {
513 ISAHint::Hint(ISA::AArch64)
515 },
516 magic @ _ => {
517 ISAHint::Unknown(format!("Unknown machine magic: {:#x}", magic))
518 }
519 }
520}
521
522impl ModuleInfo {
523 pub fn from_goblin(obj: &goblin::Object, _data: &[u8]) -> Option<ModuleInfo> {
524 match obj {
525 Object::PE(pe) => {
526 let isa = map_pe_machine(pe.header.coff_header.machine);
532
533 Some(ModuleInfo::PE(
535 isa,
536 pe.header,
537 vec![],
538 pe.header.optional_header.map(|x| x.windows_fields.image_base as usize).unwrap_or(0x400000) as u64,
539 vec![],
540 pe.imports.iter().map(|x| x.into()).collect(),
541 pe.exports.iter().map(|x| x.into()).collect(),
542 vec![]
543 ))
544 }
545 Object::Elf(elf) => {
546 let imports: Vec<ELFImport> = Vec::new();
547 let mut exports: Vec<ELFExport> = Vec::new();
548 let mut syms: Vec<ELFSymbol> = Vec::new();
549 let mut sections: Vec<ELFSection> = Vec::new();
550 for section in elf.section_headers.iter() {
551 if section.sh_name == 0 {
552 continue;
553 }
554 sections.push(ELFSection {
555 name: elf.shdr_strtab.get(section.sh_name).unwrap().unwrap().to_string(),
556 start: section.sh_addr,
557 size: section.sh_size,
558 });
559 }
560 for sym in elf.syms.iter() {
561 syms.push(ELFSymbol {
562 name: elf.strtab.get(sym.st_name).unwrap().unwrap().to_string(),
563 section_index: sym.st_shndx,
564 addr: sym.st_value
565 })
566 }
567
568 for dynsym in elf.dynsyms.iter() {
569 if dynsym.st_bind() == 1 && dynsym.st_type() == 2 && dynsym.st_value != 0 {
575 syms.push(ELFSymbol {
577 name: elf.dynstrtab.get(dynsym.st_name).unwrap().unwrap().to_string(),
578 section_index: dynsym.st_shndx,
579 addr: dynsym.st_value
580 });
581 exports.push(ELFExport {
582 name: elf.dynstrtab.get(dynsym.st_name).unwrap().unwrap().to_string(),
583 section_index: dynsym.st_shndx,
584 addr: dynsym.st_value
585 });
586 } else {
587 }
589 }
590
591 let isa = map_elf_machine(elf.header.e_machine);
592
593 Some(ModuleInfo::ELF(
595 isa,
596 elf.header,
597 vec![],
598 sections,
599 elf.entry,
600 vec![],
601 imports,
602 exports,
603 syms
605 ))
606 }
607 _ => {
608 None
609 }
610 }
611 }
612}
613
614#[derive(Debug)]
615pub struct ModuleData {
616 pub segments: Vec<Segment>,
617 pub module_info: ModuleInfo,
618 pub name: String
619}
621
622impl ModuleData {
623 pub fn load_from(data: &[u8], name: String) -> Option<ModuleData> {
624 match Object::parse(data) {
625 Ok(obj @ Object::Elf(_)) => {
626 let mut module = ModuleData {
627 segments: vec![],
628 module_info: ModuleInfo::from_goblin(&obj, data).unwrap(),
629 name
630 };
631
632 let elf = match obj {
633 Object::Elf(elf) => elf,
634 _ => panic!()
635 };
636for (i, section) in elf.program_headers.iter().enumerate() {
649 let mut section_data = vec![0; section.p_memsz as usize];
653 tracing::trace!("virtual size: {:#x}, size of raw data: {:#x}", section.p_memsz, section.p_filesz);
654 tracing::trace!("{:?}", section);
655 let physical_copy_end = (section.p_offset as usize) + std::cmp::min(section.p_filesz as usize, section.p_memsz as usize);
656 let copy_size = if physical_copy_end > data.len() {
657 if (section.p_offset as usize) < data.len() {
658 data.len() - section.p_offset as usize
659 } else {
660 0
661 }
662 } else {
663 std::cmp::min(section.p_filesz as usize, section.p_memsz as usize)
664 };
665
666 tracing::trace!("mapping section {} by copying {:#x} bytes starting from {:#x}", i, copy_size, section.p_offset);
667 tracing::trace!("virtual size is {:#x}", section_data.len());
668 for i in 0..copy_size {
669 section_data[i] = data[(section.p_offset as usize) + i];
670 }
671
672 let new_section = Segment {
673 start: section.p_vaddr as usize,
674 data: section_data,
675 name: elf::program_header::type_to_str(elf.header.e_machine, section.p_type),
676 };
677 tracing::trace!("mapped section {} to [{}, {})",
678 i,
679 new_section.start.show(),
680 (new_section.start as u64 + new_section.data.len() as u64).show()
681 );
682 module.segments.push(new_section);
683 }
684 Some(module)
685 },
686 Ok(obj @ Object::PE(_)) => {
687 let mut module = ModuleData {
688 segments: vec![],
689 module_info: ModuleInfo::from_goblin(&obj, data).unwrap(),
690 name
691 };
692
693 let pe = match obj {
694 Object::PE(pe) => pe,
695 _ => { unreachable!(); }
696 };
697for section in pe.sections.iter() {
700 let mut section_data = vec![0; section.virtual_size as usize];
704let physical_copy_end = (section.pointer_to_raw_data as usize) + std::cmp::min(section.size_of_raw_data as usize, section.virtual_size as usize);
707 let copy_size = if physical_copy_end > data.len() {
708 if (section.pointer_to_raw_data as usize) < data.len() {
709 data.len() - section.pointer_to_raw_data as usize
710 } else {
711 0
712 }
713 } else {
714 std::cmp::min(section.size_of_raw_data as usize, section.virtual_size as usize)
715 };
716
717 println!("mapping section \"{}\" by copying {:#x} bytes starting from {:#x}", std::str::from_utf8(§ion.name[..]).unwrap(), copy_size, section.pointer_to_raw_data);
718 println!("virtual size is {:#x}", section_data.len());
719 for i in 0..copy_size {
720 section_data[i] = data[(section.pointer_to_raw_data as usize) + i];
721 }
722
723 let new_section = Segment {
724 start: section.virtual_address as usize + pe.header.optional_header.map(|x| x.windows_fields.image_base as usize).unwrap_or(0x400000),
725 data: section_data,
726 name: std::str::from_utf8(§ion.name[..]).unwrap().to_string()
727 };
728 tracing::trace!("mapped {} to [{}, {})",
729 std::str::from_utf8(§ion.name[..]).unwrap(),
730 new_section.start.show(),
731 (new_section.start as u64 + new_section.data.len() as u64).show()
732 );
733 module.segments.push(new_section);
734 }
735 match &module.module_info {
736 ModuleInfo::PE(_, _, _, _, _, ref imports, ref _exports, _) => {
737 for _i in imports.iter() {
738}
740 }
741 _ => { }
742 }
743 Some(module)
744 },
745 Ok(Object::Mach(mach)) => {
746 panic!("Mach objects are not yet supported: {:?}", mach);
747 },
748 Ok(Object::Archive(archive)) => {
749 panic!("AR archives are not yet supported: {:?}", archive);
750 },
751 Ok(Object::Unknown(magic)) => {
752 tracing::warn!("goblin found unknown magic: {:#x}", magic);
753 None
754 },
755 Err(e) => {
756 tracing::error!("goblin error: {:?}", e);
757 None
758 }
759 }
760 }
761 fn segment_for<A: Address>(&self, addr: A) -> Option<&Segment> {
762 for segment in self.segments.iter() {
763 if segment.contains(addr) {
764 return Some(segment);
765 }
766 }
767 None
768 }
769 #[allow(dead_code)]
770 fn segment_after(&self, _segment: &Segment) -> Option<&Segment> {
771 unreachable!()
772 }
773}
774
775impl <A: Arch> MemoryRepr<A> for ModuleData where Segment: MemoryRepr<A> {
776 fn read(&self, addr: A::Address) -> Option<u8> {
777 self.segment_for(addr).and_then(|segment| segment.read(addr))
778 }
779 fn as_flat(&self) -> Option<FlatMemoryRepr> {
780 None
781 }
782 fn module_info(&self) -> Option<&ModuleInfo> { Some(&self.module_info) }
783 fn module_for(&self, addr: A::Address) -> Option<&dyn MemoryRepr<A>> {
784 if self.segment_for(addr).is_some() {
785 Some(self)
786 } else {
787 None
788 }
789 }
790 fn size(&self) -> Option<u64> {
791 match ((self as &dyn MemoryRepr<A>).end(), (self as &dyn MemoryRepr<A>).start()) {
792 (Some(end), Some(start)) => {
793 Some(end - start)
794 }
795 _ => None
796 }
797 }
798 fn end(&self) -> Option<u64> {
799 self.segments.iter().map(|s| s.end()).max().map(|x| x as u64)
800 }
801 fn start(&self) -> Option<u64> {
802 self.segments.iter().map(|s| s.start()).min().map(|x| x as u64)
803 }
804}
805
806impl Named for ModuleData {
807 fn name(&self) -> &str {
808 &self.name
809 }
810}
811
812impl <A: Arch> MemoryRange<A> for Segment {
813 fn range<'a>(&'a self, range: Range<A::Address>) -> Option<ReadCursor<'a, A, Self>> {
814 if self.contains(range.start) && self.contains(range.end) {
815 Some(ReadCursor::from(self, range.start, Some(range.end)))
817 } else {
818 None
820 }
821 }
822 fn range_from<'a>(&'a self, start: A::Address) -> Option<ReadCursor<'a, A, Self>> {
823 Some(ReadCursor::from(self, start, None))
824 }
825}
826
827impl <A: Arch> MemoryRange<A> for ModuleData {
828 fn range<'a>(&'a self, range: Range<A::Address>) -> Option<ReadCursor<'a, A, Self>> {
829 self.segment_for(range.start).and_then(|section| {
830 if section.contains(range.end) {
831 Some(ReadCursor::from(self, range.start, Some(range.end)))
833 } else {
834 None
836 }
837 })
838 }
839 fn range_from<'a>(&'a self, start: A::Address) -> Option<ReadCursor<'a, A, Self>> {
840 self.segment_for(start).map(|_segment| ReadCursor::from(self, start, None))
841 }
842}
843
844#[derive(Debug)]
845pub struct ProcessMemoryRepr {
846 pub modules: Vec<ModuleData>
847}
848
849impl <A: Arch> MemoryRepr<A> for ProcessMemoryRepr where ModuleData: MemoryRepr<A> {
850 fn read(&self, addr: A::Address) -> Option<u8> {
851 for module in self.modules.iter() {
853 match module.read(addr) {
854 Some(data) => { return Some(data); },
855 None => ()
856 }
857 }
858 return None
859 }
860 fn as_flat(&self) -> Option<FlatMemoryRepr> {
861 None
862 }
863 fn module_info(&self) -> Option<&ModuleInfo> { None }
864 fn module_for(&self, addr: A::Address) -> Option<&dyn MemoryRepr<A>> {
865 for module in self.modules.iter() {
866 if module.segment_for(addr).is_some() {
867 return Some(module)
868 }
869 }
870 None
871 }
872 fn size(&self) -> Option<u64> {
873 Some(self.modules.iter().map(|m| <ModuleData as MemoryRepr<A>>::size(m).unwrap()).sum())
874 }
875}
876
877impl Named for ProcessMemoryRepr {
878 fn name(&self) -> &str {
879 "process"
880 }
881}
882
883impl <A: Arch> PatchyMemoryRepr<A> for ProcessMemoryRepr {
884 fn add(&mut self, _data: Vec<u8>, _addr: A::Address) -> Result<(), LayoutError> {
885 Err(LayoutError::Unsupported)
886 }
887}
888
889impl <A: Arch> MemoryRange<A> for ProcessMemoryRepr {
890 fn range<'a>(&'a self, range: Range<A::Address>) -> Option<ReadCursor<'a, A, Self>> {
892Some(ReadCursor::from(self, range.start, Some(range.end)))
894 }
905 fn range_from<'a>(&'a self, start: A::Address) -> Option<ReadCursor<'a, A, Self>> {
907Some(ReadCursor::from(self, start, None))
909}
913}
914