1use alloc::vec::Vec;
2
3use crate::pe as coff;
4use crate::write::coff::writer;
5use crate::write::util::*;
6use crate::write::*;
7
8#[derive(Default, Clone, Copy)]
9struct SectionOffsets {
10 name: writer::Name,
11 offset: u32,
12 reloc_offset: u32,
13 selection: u8,
14 associative_section: u32,
15}
16
17#[derive(Default, Clone, Copy)]
18struct SymbolOffsets {
19 name: writer::Name,
20 index: u32,
21 aux_count: u8,
22}
23
24/// Internal format to use for the `.drectve` section containing linker
25/// directives for symbol exports.
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum CoffExportStyle {
28 /// MSVC format supported by link.exe and LLD.
29 Msvc,
30 /// Gnu format supported by GNU LD and LLD.
31 Gnu,
32}
33
34impl<'a> Object<'a> {
35 pub(crate) fn coff_section_info(
36 &self,
37 section: StandardSection,
38 ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
39 match section {
40 StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None),
41 StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None),
42 StandardSection::ReadOnlyData
43 | StandardSection::ReadOnlyDataWithRel
44 | StandardSection::ReadOnlyString => (
45 &[],
46 &b".rdata"[..],
47 SectionKind::ReadOnlyData,
48 SectionFlags::None,
49 ),
50 StandardSection::UninitializedData => (
51 &[],
52 &b".bss"[..],
53 SectionKind::UninitializedData,
54 SectionFlags::None,
55 ),
56 // TLS sections are data sections with a special name.
57 StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Data, SectionFlags::None),
58 StandardSection::UninitializedTls => {
59 // Unsupported section.
60 (&[], &[], SectionKind::UninitializedTls, SectionFlags::None)
61 }
62 StandardSection::TlsVariables => {
63 // Unsupported section.
64 (&[], &[], SectionKind::TlsVariables, SectionFlags::None)
65 }
66 StandardSection::Common => {
67 // Unsupported section.
68 (&[], &[], SectionKind::Common, SectionFlags::None)
69 }
70 StandardSection::GnuProperty => {
71 // Unsupported section.
72 (&[], &[], SectionKind::Note, SectionFlags::None)
73 }
74 }
75 }
76
77 pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> {
78 let mut name = section.to_vec();
79 name.push(b'$');
80 name.extend_from_slice(value);
81 name
82 }
83
84 pub(crate) fn coff_fixup_relocation(&mut self, relocation: &mut Relocation) -> i64 {
85 if relocation.kind == RelocationKind::GotRelative {
86 // Use a stub symbol for the relocation instead.
87 // This isn't really a GOT, but it's a similar purpose.
88 // TODO: need to handle DLL imports differently?
89 relocation.kind = RelocationKind::Relative;
90 relocation.symbol = self.coff_add_stub_symbol(relocation.symbol);
91 } else if relocation.kind == RelocationKind::PltRelative {
92 // Windows doesn't need a separate relocation type for
93 // references to functions in import libraries.
94 // For convenience, treat this the same as Relative.
95 relocation.kind = RelocationKind::Relative;
96 }
97
98 let constant = match self.architecture {
99 Architecture::I386 | Architecture::Arm | Architecture::Aarch64 => match relocation.kind
100 {
101 RelocationKind::Relative => {
102 // IMAGE_REL_I386_REL32, IMAGE_REL_ARM_REL32, IMAGE_REL_ARM64_REL32
103 relocation.addend + 4
104 }
105 _ => relocation.addend,
106 },
107 Architecture::X86_64 => match relocation.kind {
108 RelocationKind::Relative => {
109 // IMAGE_REL_AMD64_REL32 through to IMAGE_REL_AMD64_REL32_5
110 if relocation.addend <= -4 && relocation.addend >= -9 {
111 0
112 } else {
113 relocation.addend + 4
114 }
115 }
116 _ => relocation.addend,
117 },
118 _ => unimplemented!(),
119 };
120 relocation.addend -= constant;
121 constant
122 }
123
124 fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> SymbolId {
125 if let Some(stub_id) = self.stub_symbols.get(&symbol_id) {
126 return *stub_id;
127 }
128 let stub_size = self.architecture.address_size().unwrap().bytes();
129
130 let name = b".rdata$.refptr".to_vec();
131 let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
132 let section = self.section_mut(section_id);
133 section.set_data(vec![0; stub_size as usize], u64::from(stub_size));
134 section.relocations = vec![Relocation {
135 offset: 0,
136 size: stub_size * 8,
137 kind: RelocationKind::Absolute,
138 encoding: RelocationEncoding::Generic,
139 symbol: symbol_id,
140 addend: 0,
141 }];
142
143 let mut name = b".refptr.".to_vec();
144 name.extend_from_slice(&self.symbol(symbol_id).name);
145 let stub_id = self.add_raw_symbol(Symbol {
146 name,
147 value: 0,
148 size: u64::from(stub_size),
149 kind: SymbolKind::Data,
150 scope: SymbolScope::Compilation,
151 weak: false,
152 section: SymbolSection::Section(section_id),
153 flags: SymbolFlags::None,
154 });
155 self.stub_symbols.insert(symbol_id, stub_id);
156
157 stub_id
158 }
159
160 /// Appends linker directives to the `.drectve` section to tell the linker
161 /// to export all symbols with `SymbolScope::Dynamic`.
162 ///
163 /// This must be called after all symbols have been defined.
164 pub fn add_coff_exports(&mut self, style: CoffExportStyle) {
165 assert_eq!(self.format, BinaryFormat::Coff);
166
167 let mut directives = vec![];
168 for symbol in &self.symbols {
169 if symbol.scope == SymbolScope::Dynamic {
170 match style {
171 CoffExportStyle::Msvc => directives.extend(b" /EXPORT:\""),
172 CoffExportStyle::Gnu => directives.extend(b" -export:\""),
173 }
174 directives.extend(&symbol.name);
175 directives.extend(b"\"");
176 if symbol.kind != SymbolKind::Text {
177 match style {
178 CoffExportStyle::Msvc => directives.extend(b",DATA"),
179 CoffExportStyle::Gnu => directives.extend(b",data"),
180 }
181 }
182 }
183 }
184 let drectve = self.add_section(vec![], b".drectve".to_vec(), SectionKind::Linker);
185 self.append_section_data(drectve, &directives, 1);
186 }
187
188 pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
189 let mut writer = writer::Writer::new(buffer);
190
191 // Add section strings to strtab.
192 let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
193 for (index, section) in self.sections.iter().enumerate() {
194 section_offsets[index].name = writer.add_name(&section.name);
195 }
196
197 // Set COMDAT flags.
198 for comdat in &self.comdats {
199 let symbol = &self.symbols[comdat.symbol.0];
200 let comdat_section = match symbol.section {
201 SymbolSection::Section(id) => id.0,
202 _ => {
203 return Err(Error(format!(
204 "unsupported COMDAT symbol `{}` section {:?}",
205 symbol.name().unwrap_or(""),
206 symbol.section
207 )));
208 }
209 };
210 section_offsets[comdat_section].selection = match comdat.kind {
211 ComdatKind::NoDuplicates => coff::IMAGE_COMDAT_SELECT_NODUPLICATES,
212 ComdatKind::Any => coff::IMAGE_COMDAT_SELECT_ANY,
213 ComdatKind::SameSize => coff::IMAGE_COMDAT_SELECT_SAME_SIZE,
214 ComdatKind::ExactMatch => coff::IMAGE_COMDAT_SELECT_EXACT_MATCH,
215 ComdatKind::Largest => coff::IMAGE_COMDAT_SELECT_LARGEST,
216 ComdatKind::Newest => coff::IMAGE_COMDAT_SELECT_NEWEST,
217 ComdatKind::Unknown => {
218 return Err(Error(format!(
219 "unsupported COMDAT symbol `{}` kind {:?}",
220 symbol.name().unwrap_or(""),
221 comdat.kind
222 )));
223 }
224 };
225 for id in &comdat.sections {
226 let section = &self.sections[id.0];
227 if section.symbol.is_none() {
228 return Err(Error(format!(
229 "missing symbol for COMDAT section `{}`",
230 section.name().unwrap_or(""),
231 )));
232 }
233 if id.0 != comdat_section {
234 section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE;
235 section_offsets[id.0].associative_section = comdat_section as u32 + 1;
236 }
237 }
238 }
239
240 // Reserve symbol indices and add symbol strings to strtab.
241 let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
242 for (index, symbol) in self.symbols.iter().enumerate() {
243 symbol_offsets[index].index = writer.reserve_symbol_index();
244 let mut name = &*symbol.name;
245 match symbol.kind {
246 SymbolKind::File => {
247 // Name goes in auxiliary symbol records.
248 symbol_offsets[index].aux_count = writer.reserve_aux_file_name(&symbol.name);
249 name = b".file";
250 }
251 SymbolKind::Section if symbol.section.id().is_some() => {
252 symbol_offsets[index].aux_count = writer.reserve_aux_section();
253 }
254 _ => {}
255 };
256 symbol_offsets[index].name = writer.add_name(name);
257 }
258
259 // Reserve file ranges.
260 writer.reserve_file_header();
261 writer.reserve_section_headers(self.sections.len() as u16);
262 for (index, section) in self.sections.iter().enumerate() {
263 section_offsets[index].offset = writer.reserve_section(section.data.len());
264 section_offsets[index].reloc_offset =
265 writer.reserve_relocations(section.relocations.len());
266 }
267 writer.reserve_symtab_strtab();
268
269 // Start writing.
270 writer.write_file_header(writer::FileHeader {
271 machine: match (self.architecture, self.sub_architecture) {
272 (Architecture::Arm, None) => coff::IMAGE_FILE_MACHINE_ARMNT,
273 (Architecture::Aarch64, None) => coff::IMAGE_FILE_MACHINE_ARM64,
274 (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)) => {
275 coff::IMAGE_FILE_MACHINE_ARM64EC
276 }
277 (Architecture::I386, None) => coff::IMAGE_FILE_MACHINE_I386,
278 (Architecture::X86_64, None) => coff::IMAGE_FILE_MACHINE_AMD64,
279 _ => {
280 return Err(Error(format!(
281 "unimplemented architecture {:?} with sub-architecture {:?}",
282 self.architecture, self.sub_architecture
283 )));
284 }
285 },
286 time_date_stamp: 0,
287 characteristics: match self.flags {
288 FileFlags::Coff { characteristics } => characteristics,
289 _ => 0,
290 },
291 })?;
292
293 // Write section headers.
294 for (index, section) in self.sections.iter().enumerate() {
295 let mut characteristics = if let SectionFlags::Coff {
296 characteristics, ..
297 } = section.flags
298 {
299 characteristics
300 } else {
301 match section.kind {
302 SectionKind::Text => {
303 coff::IMAGE_SCN_CNT_CODE
304 | coff::IMAGE_SCN_MEM_EXECUTE
305 | coff::IMAGE_SCN_MEM_READ
306 }
307 SectionKind::Data => {
308 coff::IMAGE_SCN_CNT_INITIALIZED_DATA
309 | coff::IMAGE_SCN_MEM_READ
310 | coff::IMAGE_SCN_MEM_WRITE
311 }
312 SectionKind::UninitializedData => {
313 coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA
314 | coff::IMAGE_SCN_MEM_READ
315 | coff::IMAGE_SCN_MEM_WRITE
316 }
317 SectionKind::ReadOnlyData
318 | SectionKind::ReadOnlyDataWithRel
319 | SectionKind::ReadOnlyString => {
320 coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ
321 }
322 SectionKind::Debug | SectionKind::Other | SectionKind::OtherString => {
323 coff::IMAGE_SCN_CNT_INITIALIZED_DATA
324 | coff::IMAGE_SCN_MEM_READ
325 | coff::IMAGE_SCN_MEM_DISCARDABLE
326 }
327 SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE,
328 SectionKind::Common
329 | SectionKind::Tls
330 | SectionKind::UninitializedTls
331 | SectionKind::TlsVariables
332 | SectionKind::Note
333 | SectionKind::Unknown
334 | SectionKind::Metadata
335 | SectionKind::Elf(_) => {
336 return Err(Error(format!(
337 "unimplemented section `{}` kind {:?}",
338 section.name().unwrap_or(""),
339 section.kind
340 )));
341 }
342 }
343 };
344 if section_offsets[index].selection != 0 {
345 characteristics |= coff::IMAGE_SCN_LNK_COMDAT;
346 };
347 if section.relocations.len() > 0xffff {
348 characteristics |= coff::IMAGE_SCN_LNK_NRELOC_OVFL;
349 }
350 characteristics |= match section.align {
351 1 => coff::IMAGE_SCN_ALIGN_1BYTES,
352 2 => coff::IMAGE_SCN_ALIGN_2BYTES,
353 4 => coff::IMAGE_SCN_ALIGN_4BYTES,
354 8 => coff::IMAGE_SCN_ALIGN_8BYTES,
355 16 => coff::IMAGE_SCN_ALIGN_16BYTES,
356 32 => coff::IMAGE_SCN_ALIGN_32BYTES,
357 64 => coff::IMAGE_SCN_ALIGN_64BYTES,
358 128 => coff::IMAGE_SCN_ALIGN_128BYTES,
359 256 => coff::IMAGE_SCN_ALIGN_256BYTES,
360 512 => coff::IMAGE_SCN_ALIGN_512BYTES,
361 1024 => coff::IMAGE_SCN_ALIGN_1024BYTES,
362 2048 => coff::IMAGE_SCN_ALIGN_2048BYTES,
363 4096 => coff::IMAGE_SCN_ALIGN_4096BYTES,
364 8192 => coff::IMAGE_SCN_ALIGN_8192BYTES,
365 _ => {
366 return Err(Error(format!(
367 "unimplemented section `{}` align {}",
368 section.name().unwrap_or(""),
369 section.align
370 )));
371 }
372 };
373 writer.write_section_header(writer::SectionHeader {
374 name: section_offsets[index].name,
375 size_of_raw_data: section.size as u32,
376 pointer_to_raw_data: section_offsets[index].offset,
377 pointer_to_relocations: section_offsets[index].reloc_offset,
378 pointer_to_linenumbers: 0,
379 number_of_relocations: section.relocations.len() as u32,
380 number_of_linenumbers: 0,
381 characteristics,
382 });
383 }
384
385 // Write section data and relocations.
386 for section in &self.sections {
387 writer.write_section(&section.data);
388
389 if !section.relocations.is_empty() {
390 //debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
391 writer.write_relocations_count(section.relocations.len());
392 for reloc in &section.relocations {
393 //assert!(reloc.implicit_addend);
394 let typ = match self.architecture {
395 Architecture::I386 => match (reloc.kind, reloc.size, reloc.addend) {
396 (RelocationKind::Absolute, 16, 0) => coff::IMAGE_REL_I386_DIR16,
397 (RelocationKind::Relative, 16, 0) => coff::IMAGE_REL_I386_REL16,
398 (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_I386_DIR32,
399 (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_I386_DIR32NB,
400 (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_I386_SECTION,
401 (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_I386_SECREL,
402 (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_I386_SECREL7,
403 (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_I386_REL32,
404 (RelocationKind::Coff(x), _, _) => x,
405 _ => {
406 return Err(Error(format!("unimplemented relocation {:?}", reloc)));
407 }
408 },
409 Architecture::X86_64 => match (reloc.kind, reloc.size, reloc.addend) {
410 (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_AMD64_ADDR64,
411 (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32,
412 (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32NB,
413 (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_AMD64_REL32,
414 (RelocationKind::Relative, 32, -5) => coff::IMAGE_REL_AMD64_REL32_1,
415 (RelocationKind::Relative, 32, -6) => coff::IMAGE_REL_AMD64_REL32_2,
416 (RelocationKind::Relative, 32, -7) => coff::IMAGE_REL_AMD64_REL32_3,
417 (RelocationKind::Relative, 32, -8) => coff::IMAGE_REL_AMD64_REL32_4,
418 (RelocationKind::Relative, 32, -9) => coff::IMAGE_REL_AMD64_REL32_5,
419 (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_AMD64_SECTION,
420 (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL,
421 (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7,
422 (RelocationKind::Coff(x), _, _) => x,
423 _ => {
424 return Err(Error(format!("unimplemented relocation {:?}", reloc)));
425 }
426 },
427 Architecture::Arm => match (reloc.kind, reloc.size, reloc.addend) {
428 (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM_ADDR32,
429 (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM_ADDR32NB,
430 (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM_REL32,
431 (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM_SECTION,
432 (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM_SECREL,
433 (RelocationKind::Coff(x), _, _) => x,
434 _ => {
435 return Err(Error(format!("unimplemented relocation {:?}", reloc)));
436 }
437 },
438 Architecture::Aarch64 => match (reloc.kind, reloc.size, reloc.addend) {
439 (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32,
440 (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32NB,
441 (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM64_SECTION,
442 (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM64_SECREL,
443 (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_ARM64_ADDR64,
444 (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM64_REL32,
445 (RelocationKind::Coff(x), _, _) => x,
446 _ => {
447 return Err(Error(format!("unimplemented relocation {:?}", reloc)));
448 }
449 },
450 _ => {
451 return Err(Error(format!(
452 "unimplemented architecture {:?}",
453 self.architecture
454 )));
455 }
456 };
457 writer.write_relocation(writer::Relocation {
458 virtual_address: reloc.offset as u32,
459 symbol: symbol_offsets[reloc.symbol.0].index,
460 typ,
461 });
462 }
463 }
464 }
465
466 // Write symbols.
467 for (index, symbol) in self.symbols.iter().enumerate() {
468 let section_number = match symbol.section {
469 SymbolSection::None => {
470 debug_assert_eq!(symbol.kind, SymbolKind::File);
471 coff::IMAGE_SYM_DEBUG as u16
472 }
473 SymbolSection::Undefined => coff::IMAGE_SYM_UNDEFINED as u16,
474 SymbolSection::Absolute => coff::IMAGE_SYM_ABSOLUTE as u16,
475 SymbolSection::Common => coff::IMAGE_SYM_UNDEFINED as u16,
476 SymbolSection::Section(id) => id.0 as u16 + 1,
477 };
478 let typ = if symbol.kind == SymbolKind::Text {
479 coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT
480 } else {
481 coff::IMAGE_SYM_TYPE_NULL
482 };
483 let storage_class = match symbol.kind {
484 SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE,
485 SymbolKind::Section => {
486 if symbol.section.id().is_some() {
487 coff::IMAGE_SYM_CLASS_STATIC
488 } else {
489 coff::IMAGE_SYM_CLASS_SECTION
490 }
491 }
492 SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL,
493 SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => {
494 match symbol.section {
495 SymbolSection::None => {
496 return Err(Error(format!(
497 "missing section for symbol `{}`",
498 symbol.name().unwrap_or("")
499 )));
500 }
501 SymbolSection::Undefined | SymbolSection::Common => {
502 coff::IMAGE_SYM_CLASS_EXTERNAL
503 }
504 SymbolSection::Absolute | SymbolSection::Section(_) => {
505 match symbol.scope {
506 // TODO: does this need aux symbol records too?
507 _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL,
508 SymbolScope::Unknown => {
509 return Err(Error(format!(
510 "unimplemented symbol `{}` scope {:?}",
511 symbol.name().unwrap_or(""),
512 symbol.scope
513 )));
514 }
515 SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC,
516 SymbolScope::Linkage | SymbolScope::Dynamic => {
517 coff::IMAGE_SYM_CLASS_EXTERNAL
518 }
519 }
520 }
521 }
522 }
523 SymbolKind::Unknown | SymbolKind::Null => {
524 return Err(Error(format!(
525 "unimplemented symbol `{}` kind {:?}",
526 symbol.name().unwrap_or(""),
527 symbol.kind
528 )));
529 }
530 };
531 let number_of_aux_symbols = symbol_offsets[index].aux_count;
532 let value = if symbol.section == SymbolSection::Common {
533 symbol.size as u32
534 } else {
535 symbol.value as u32
536 };
537 writer.write_symbol(writer::Symbol {
538 name: symbol_offsets[index].name,
539 value,
540 section_number,
541 typ,
542 storage_class,
543 number_of_aux_symbols,
544 });
545
546 // Write auxiliary symbols.
547 match symbol.kind {
548 SymbolKind::File => {
549 writer.write_aux_file_name(&symbol.name, number_of_aux_symbols);
550 }
551 SymbolKind::Section if symbol.section.id().is_some() => {
552 debug_assert_eq!(number_of_aux_symbols, 1);
553 let section_index = symbol.section.id().unwrap().0;
554 let section = &self.sections[section_index];
555 writer.write_aux_section(writer::AuxSymbolSection {
556 length: section.size as u32,
557 number_of_relocations: section.relocations.len() as u32,
558 number_of_linenumbers: 0,
559 check_sum: checksum(section.data()),
560 number: section_offsets[section_index].associative_section,
561 selection: section_offsets[section_index].selection,
562 });
563 }
564 _ => {
565 debug_assert_eq!(number_of_aux_symbols, 0);
566 }
567 }
568 }
569
570 writer.write_strtab();
571
572 debug_assert_eq!(writer.reserved_len(), writer.len());
573
574 Ok(())
575 }
576}
577
578// JamCRC
579fn checksum(data: &[u8]) -> u32 {
580 let mut hasher: Hasher = crc32fast::Hasher::new_with_initial(init:0xffff_ffff);
581 hasher.update(buf:data);
582 !hasher.finalize()
583}
584