1use ab_contract_file::ContractFile;
12use ab_contract_file::instruction::{ContractInstruction, ContractRegister};
13use ab_contracts_common::{HOST_CALL_FN, HOST_CALL_FN_IMPORT};
14use ab_riscv_primitives::prelude::*;
15use anyhow::Context;
16use object::elf::{
17 DF_1_NOW, DF_BIND_NOW, DF_SYMBOLIC, DT_FLAGS, DT_FLAGS_1, DT_HASH, DT_JMPREL, DT_NULL,
18 DT_PLTGOT, DT_PLTREL, DT_PLTRELSZ, DT_RELA, DT_STRSZ, DT_STRTAB, DT_SYMENT, DT_SYMTAB, Dyn64,
19 EF_RISCV_RVC, ELFOSABI_GNU, EM_RISCV, ET_DYN, PF_R, PF_W, PF_X, PT_DYNAMIC, PT_GNU_STACK,
20 PT_LOAD, PT_PHDR, PT_RISCV_ATTRIBUTES, ProgramFlags, ProgramHeader64, ProgramType,
21 R_RISCV_JUMP_SLOT, Rela64, SHF_ALLOC, SHF_EXECINSTR, SHF_INFO_LINK, SHF_WRITE, SHN_UNDEF,
22 SHT_PROGBITS, SHT_RELA, SHT_RISCV_ATTRIBUTES, STB_GLOBAL, STT_FUNC, STT_NOTYPE, STV_DEFAULT,
23 Sym64, SymbolInfo, Tag_File,
24};
25use object::pod::bytes_of_slice;
26use object::write::StringId;
27use object::write::elf::{
28 AttributesWriter, FileHeader, ProgramHeader, Rel, SectionHeader, SectionIndex, Sym,
29 SymbolIndex, Writer,
30};
31use object::{Endianness, LittleEndian, U64};
32use std::collections::HashSet;
33
34const PAGE_SIZE: u64 = 0x1000;
36const WORD_ALIGNMENT: u64 = size_of::<u64>() as u64;
38const INSTRUCTION_ALIGNMENT: u64 = size_of::<u16>() as u64;
40const PROGRAM_HEADER_SIZE: u64 = size_of::<ProgramHeader64<LittleEndian>>() as u64;
41const SYMBOL_SIZE: u64 = size_of::<Sym64<LittleEndian>>() as u64;
42const RELA_SIZE: u64 = size_of::<Rela64<LittleEndian>>() as u64;
43const DYNAMIC_ENTRY_SIZE: u64 = size_of::<Dyn64<LittleEndian>>() as u64;
44const NUM_PROGRAM_HEADERS: usize = 7;
46const NUM_DYNAMIC_ENTRIES: usize = 8;
49const NUM_PLT_DYNAMIC_ENTRIES: usize = 4;
51const PLT_HEADER_SIZE: u64 = 32;
54const PLT_ENTRY_SIZE: u64 = 16;
55const PLT_SIZE: u64 = PLT_HEADER_SIZE + PLT_ENTRY_SIZE;
56const GOT_PLT_ENTRIES: usize = 3;
59const GOT_PLT_SIZE: u64 = (GOT_PLT_ENTRIES * size_of::<u64>()) as u64;
60const GOT_PLT_IMPORT_ENTRY_OFFSET: u64 = 2 * WORD_ALIGNMENT;
62const TAG_RISCV_STACK_ALIGN: u64 = 4;
64const TAG_RISCV_ARCH: u64 = 5;
66const RISCV_STACK_ALIGN: u64 = 16;
68
69#[derive(Debug, Copy, Clone)]
70struct ParsedHostCallFn {
71 address: u64,
73 size: u64,
74 plt_entry_address: u64,
77}
78
79#[derive(Debug)]
80struct ParsedContract<'a> {
81 metadata: &'a [u8],
82 rodata: &'a [u8],
83 metadata_first: bool,
85 read_only_memory_size: u64,
87 code: &'a [u8],
88 symbols: Vec<DynamicSymbol<'a>>,
91 host_call_fn: Option<ParsedHostCallFn>,
92}
93
94#[derive(Debug, Copy, Clone)]
95struct DynamicSymbol<'a> {
96 name: &'a str,
97 function: Option<(u64, u64)>,
100}
101
102fn parse_host_call_fn(
103 contract_file: &ContractFile<'_>,
104 code_offset: usize,
105 read_only_memory_size: u64,
106) -> anyhow::Result<Option<ParsedHostCallFn>> {
107 let host_call_fn_offset = contract_file.header().host_call_fn_offset;
108 if host_call_fn_offset == 0 {
109 return Ok(None);
110 }
111
112 let offset_in_code = usize::try_from(host_call_fn_offset)?
113 .checked_sub(code_offset)
114 .context("Host call function is before code section")?;
115 let instruction_bytes = contract_file
116 .get_code()
117 .get(offset_in_code..)
118 .context("Host call function is out of range of code section")?;
119
120 let mut instruction_word = [0; size_of::<u32>()];
123 instruction_word
124 .iter_mut()
125 .zip(instruction_bytes)
126 .for_each(|(target, source)| *target = *source);
127 let instruction =
128 <ContractInstruction as Instruction>::try_decode(u32::from_le_bytes(instruction_word))
129 .context("Failed to decode host call function instruction")?;
130
131 #[expect(
132 clippy::rest_pattern_accessible_field,
133 reason = "Do not need other fields"
134 )]
135 let jump_offset = match instruction {
136 ContractInstruction::Jal {
137 rd: ContractRegister::Zero,
138 imm,
139 ..
140 } => i64::from(imm),
141 ContractInstruction::CJ { imm, .. } => i64::from(imm),
142 _ => {
143 return Err(anyhow::anyhow!(
144 "Unexpected host call function instruction {instruction}"
145 ));
146 }
147 };
148
149 let address = read_only_memory_size + u64::try_from(offset_in_code)?;
150 let plt_entry_address = address
151 .checked_add_signed(jump_offset)
152 .context("Host call function jumps outside of contract memory")?;
153
154 Ok(Some(ParsedHostCallFn {
155 address,
156 size: u64::from(instruction.size()),
157 plt_entry_address,
158 }))
159}
160
161fn parse_contract(input_file: &[u8]) -> anyhow::Result<ParsedContract<'_>> {
162 let mut contract_file_methods = Vec::new();
163 let contract_file = ContractFile::parse(input_file, |contract_file_method| {
164 contract_file_methods.push(contract_file_method);
165 Ok(())
166 })
167 .context("Failed to parse contract file")?;
168 let header = contract_file.header();
169
170 let code_offset = input_file.len() - contract_file.get_code().len();
171 let code = input_file
172 .get(code_offset..)
173 .context("Code section is out of range of the contract file")?;
174 let read_only_size = usize::try_from(header.read_only_section_file_size)?;
175 let read_only_offset = code_offset
176 .checked_sub(read_only_size)
177 .context("Read-only section is out of range of the contract file")?;
178 let read_only = input_file
179 .get(read_only_offset..code_offset)
180 .context("Read-only section is out of range of the contract file")?;
181 let read_only_memory_size = u64::from(header.read_only_section_memory_size);
182
183 if header.metadata_size == 0 {
184 return Err(anyhow::anyhow!("Metadata not found"));
185 }
186 let metadata_offset = usize::try_from(header.metadata_offset)?;
187 let metadata = input_file
188 .get(metadata_offset..)
189 .and_then(|metadata| metadata.get(..usize::from(header.metadata_size)))
190 .context("Metadata is out of range of the contract file")?;
191
192 let (metadata_first, rodata) = if metadata_offset == read_only_offset {
195 let rodata = read_only
196 .get(metadata.len()..)
197 .context("Metadata is larger than read-only section")?;
198 (true, rodata)
199 } else if metadata_offset + metadata.len() == code_offset {
200 let rodata = read_only
201 .len()
202 .checked_sub(metadata.len())
203 .and_then(|rodata_size| read_only.get(..rodata_size))
204 .context("Metadata is larger than read-only section")?;
205 (false, rodata)
206 } else {
207 return Err(anyhow::anyhow!(
208 "Metadata must be either at the beginning or at the end of read-only section: \
209 metadata_offset={metadata_offset}, metadata_size={}, \
210 read_only_offset={read_only_offset}, code_offset={code_offset}",
211 metadata.len()
212 ));
213 };
214
215 let host_call_fn = parse_host_call_fn(&contract_file, code_offset, read_only_memory_size)?;
216
217 let mut symbol_names = HashSet::from([HOST_CALL_FN, HOST_CALL_FN_IMPORT]);
220 let methods = contract_file_methods
221 .into_iter()
222 .map(|contract_file_method| {
223 let method_name = contract_file_method.method_metadata_item.method_name;
224 let name = str::from_utf8(method_name)
225 .with_context(|| format!("Non-UTF-8 method name: {method_name:?}"))?;
226 if name.contains('\0') {
227 return Err(anyhow::anyhow!("Method name {name:?} contains zero byte"));
228 }
229 if !symbol_names.insert(name) {
230 return Err(anyhow::anyhow!("Duplicate or reserved method name {name}"));
231 }
232
233 Ok(DynamicSymbol {
234 name,
235 function: Some((
236 u64::from(contract_file_method.address),
237 u64::from(contract_file_method.size),
238 )),
239 })
240 })
241 .collect::<anyhow::Result<Vec<_>>>()?;
242
243 let symbols = host_call_fn
244 .map(|_host_call_fn| DynamicSymbol {
245 name: HOST_CALL_FN_IMPORT,
246 function: None,
247 })
248 .into_iter()
249 .chain(methods)
250 .chain(host_call_fn.map(|host_call_fn| DynamicSymbol {
251 name: HOST_CALL_FN,
252 function: Some((host_call_fn.address, host_call_fn.size)),
253 }))
254 .collect();
255
256 Ok(ParsedContract {
257 metadata,
258 rodata,
259 metadata_first,
260 read_only_memory_size,
261 code,
262 symbols,
263 host_call_fn,
264 })
265}
266
267#[derive(Debug, Default, Copy, Clone)]
269struct Placement {
270 offset: u64,
271 address: u64,
272 size: u64,
273}
274
275impl Placement {
276 fn identity(offset: u64, size: u64) -> Self {
278 Self {
279 offset,
280 address: offset,
281 size,
282 }
283 }
284
285 fn end_offset(&self) -> u64 {
286 self.offset + self.size
287 }
288
289 fn end_address(&self) -> u64 {
290 self.address + self.size
291 }
292
293 fn next_at(&self, offset: u64, size: u64) -> Self {
295 Self {
296 offset,
297 address: self.end_address() + (offset - self.end_offset()),
298 size,
299 }
300 }
301
302 fn reserve(&self, writer: &mut Writer<'_>) {
304 writer.reserve_until(self.offset);
305 writer.reserve(self.size, 1);
306 }
307
308 fn section_header(&self) -> SectionHeader {
309 SectionHeader {
310 sh_addr: self.address,
311 sh_offset: self.offset,
312 sh_size: self.size,
313 ..SectionHeader::default()
314 }
315 }
316
317 fn program_header(
318 &self,
319 p_type: ProgramType,
320 p_flags: ProgramFlags,
321 p_align: u64,
322 ) -> ProgramHeader {
323 ProgramHeader {
324 p_type,
325 p_flags,
326 p_offset: self.offset,
327 p_vaddr: self.address,
328 p_paddr: self.address,
329 p_filesz: self.size,
330 p_memsz: self.size,
331 p_align,
332 }
333 }
334}
335
336#[derive(Debug, Copy, Clone)]
338struct Section {
339 name: StringId,
340 index: SectionIndex,
341}
342
343impl Section {
344 fn reserve(writer: &mut Writer<'_>, name: &'static [u8]) -> Self {
345 Self {
346 name: writer.add_section_name(name),
347 index: writer.reserve_section_index(),
348 }
349 }
350
351 fn write_header(self, writer: &mut Writer<'_>, header: SectionHeader) {
352 writer.write_section_header(&SectionHeader {
353 sh_name: writer.section_name_offset(Some(self.name)),
354 ..header
355 });
356 }
357}
358
359#[derive(Debug, Copy, Clone)]
361struct HostCallParts {
362 import_symbol_index: SymbolIndex,
363 rela_plt_section: Section,
364 rela_plt: Placement,
365 plt_section: Section,
366 plt: Placement,
367 got_plt_section: Section,
368 got_plt: Placement,
369}
370
371fn pc_relative_hi_lo(offset: u64) -> (u32, u32) {
374 let hi = u32::try_from((offset.wrapping_add(0x800) >> 12) & 0xf_ffff)
375 .expect("Masked to 20 bits; qed");
376 let lo = u32::try_from(offset & 0xfff).expect("Masked to 12 bits; qed");
377 (hi << 12, lo << 20)
378}
379
380fn plt_code(plt_address: u64, got_plt_address: u64) -> [u8; PLT_SIZE as usize] {
382 let (header_hi, header_lo) = pc_relative_hi_lo(got_plt_address - plt_address);
383 let (entry_hi, entry_lo) = pc_relative_hi_lo(
384 (got_plt_address + GOT_PLT_IMPORT_ENTRY_OFFSET) - (plt_address + PLT_HEADER_SIZE),
385 );
386
387 let instructions: [u32; PLT_SIZE as usize / size_of::<u32>()] = [
388 0x0000_0397 | header_hi,
390 0x41c3_0333,
392 0x0003_be03 | header_lo,
394 0xfd43_0313,
396 0x0003_8293 | header_lo,
398 0x0013_5313,
400 0x0082_b283,
402 0x000e_0067,
404 0x0000_0e17 | entry_hi,
406 0x000e_3e03 | entry_lo,
408 0x000e_0367,
410 0x0000_0013,
412 ];
413
414 let mut plt = [0; _];
415 plt.as_chunks_mut::<{ size_of::<u32>() }>()
416 .0
417 .iter_mut()
418 .zip(instructions)
419 .for_each(|(bytes, instruction)| *bytes = instruction.to_le_bytes());
420 plt
421}
422
423fn riscv_attributes() -> Vec<u8> {
424 let mut attributes = AttributesWriter::new(Endianness::Little);
425 attributes.start_subsection(b"riscv");
426 attributes.start_subsubsection(Tag_File);
427 attributes.write_attribute_tag(TAG_RISCV_STACK_ALIGN);
428 attributes.write_attribute_integer(RISCV_STACK_ALIGN);
429 attributes.write_attribute_tag(TAG_RISCV_ARCH);
430 attributes.write_attribute_string(<ContractInstruction as Instruction>::ISA_STRING.as_bytes());
433 attributes.end_subsubsection();
434 attributes.end_subsection();
435 attributes.data()
436}
437
438pub fn recover(input_file: &[u8]) -> anyhow::Result<Vec<u8>> {
444 let ParsedContract {
445 metadata,
446 rodata,
447 metadata_first,
448 read_only_memory_size,
449 code,
450 symbols,
451 host_call_fn,
452 } = parse_contract(input_file)?;
453
454 let riscv_attributes = riscv_attributes();
455 let read_only_parts: [(&'static [u8], &[u8]); 2] = if metadata_first {
457 [(b"ab-contract-metadata", metadata), (b".rodata", rodata)]
458 } else {
459 [(b".rodata", rodata), (b"ab-contract-metadata", metadata)]
460 };
461
462 let mut output_file = Vec::new();
463 let mut writer = Writer::new(Endianness::Little, true, &mut output_file);
464
465 writer.reserve_dynsym_section_index();
467 writer.reserve_hash_section_index();
468 writer.reserve_dynstr_section_index();
469 let rela_plt_section = host_call_fn.map(|_| Section::reserve(&mut writer, b".rela.plt"));
470 let read_only_sections = read_only_parts
471 .into_iter()
472 .filter(|(_name, data)| !data.is_empty())
473 .map(|(name, data)| (Section::reserve(&mut writer, name), data))
474 .collect::<Vec<_>>();
475 let text_section = Section::reserve(&mut writer, b".text");
476 let plt_section = host_call_fn.map(|_| Section::reserve(&mut writer, b".plt"));
477 writer.reserve_dynamic_section_index();
478 let got_plt_section = host_call_fn.map(|_| Section::reserve(&mut writer, b".got.plt"));
479 let riscv_attributes_section = Section::reserve(&mut writer, b".riscv.attributes");
480 writer.reserve_shstrtab_section_index();
481
482 writer.reserve_null_dynamic_symbol_index();
483 let symbol_ids = symbols
484 .iter()
485 .map(|symbol| {
486 (
487 writer.reserve_dynamic_symbol_index(),
488 writer.add_dynamic_string(symbol.name.as_bytes()),
489 )
490 })
491 .collect::<Vec<_>>();
492 let import_symbol_index = symbols
493 .iter()
494 .zip(&symbol_ids)
495 .find_map(|(symbol, &(index, _name))| symbol.function.is_none().then_some(index));
496 let num_symbols = writer.dynamic_symbol_count();
497
498 writer.reserve_file_header();
501 writer.reserve_program_headers(u32::try_from(NUM_PROGRAM_HEADERS)?);
502 let dynsym_address = writer.reserve_dynsym();
503 let hash_address = writer.reserve_hash(num_symbols, num_symbols);
504 let dynstr = Placement::identity(writer.reserve_dynstr()?, u64::from(writer.dynstr_len()));
505 let rela_plt =
506 host_call_fn.map(|_| Placement::identity(writer.reserve_relocations(1, true), RELA_SIZE));
507 let read_only_start = writer.reserve(0, PAGE_SIZE);
509 let read_only_sections = read_only_sections
510 .into_iter()
511 .map(|(section, data)| {
512 let size = u64::try_from(data.len())?;
513 let placement = Placement::identity(writer.reserve(size, 1), size);
514 anyhow::Ok((section, placement, data))
515 })
516 .collect::<anyhow::Result<Vec<_>>>()?;
517 let read_only = Placement::identity(read_only_start, writer.reserved_len() - read_only_start);
518 let read_only_padding = read_only_memory_size
519 .checked_sub(read_only.size)
520 .context("Read-only section file size is larger than memory size")?;
521
522 let text = Placement {
525 offset: read_only.end_offset() + read_only_padding % PAGE_SIZE,
526 address: read_only.address + read_only_memory_size,
527 size: u64::try_from(code.len())?,
528 };
529 if !text.address.is_multiple_of(INSTRUCTION_ALIGNMENT) {
530 return Err(anyhow::anyhow!(
531 "Code section is not aligned to instruction boundary: read-only memory size \
532 {read_only_memory_size}"
533 ));
534 }
535 text.reserve(&mut writer);
536 let plt = host_call_fn
538 .map(|host_call_fn| {
539 let address = (read_only.address + host_call_fn.plt_entry_address)
540 .checked_sub(PLT_HEADER_SIZE)
541 .filter(|&address| address >= text.end_address())
542 .with_context(|| {
543 format!(
544 "Host call function must jump to PLT entry after code section, but jumps \
545 to {} relative to the beginning of contract memory",
546 host_call_fn.plt_entry_address
547 )
548 })?;
549
550 let plt = Placement {
551 offset: text.offset + (address - text.address),
552 address,
553 size: PLT_SIZE,
554 };
555 plt.reserve(&mut writer);
556 anyhow::Ok(plt)
557 })
558 .transpose()?;
559 let executable_end = plt.unwrap_or(text);
560
561 let num_dynamic_entries = if host_call_fn.is_some() {
564 NUM_DYNAMIC_ENTRIES + NUM_PLT_DYNAMIC_ENTRIES
565 } else {
566 NUM_DYNAMIC_ENTRIES
567 };
568 let dynamic = {
569 let offset = writer.reserve_dynamic(num_dynamic_entries);
570 Placement {
571 offset,
572 address: executable_end.end_address().next_multiple_of(PAGE_SIZE) + offset % PAGE_SIZE,
573 size: u64::try_from(num_dynamic_entries)? * DYNAMIC_ENTRY_SIZE,
574 }
575 };
576 let got_plt = host_call_fn
577 .map(|_| dynamic.next_at(writer.reserve(GOT_PLT_SIZE, WORD_ALIGNMENT), GOT_PLT_SIZE));
578 let writable_end = got_plt.unwrap_or(dynamic);
579
580 let riscv_attributes_placement = {
582 let size = u64::try_from(riscv_attributes.len())?;
583 Placement {
584 offset: writer.reserve(size, 1),
585 address: 0,
586 size,
587 }
588 };
589 writer.reserve_shstrtab()?;
590 writer.reserve_section_headers();
591
592 let host_call = try {
593 HostCallParts {
594 import_symbol_index: import_symbol_index?,
595 rela_plt_section: rela_plt_section?,
596 rela_plt: rela_plt?,
597 plt_section: plt_section?,
598 plt: plt?,
599 got_plt_section: got_plt_section?,
600 got_plt: got_plt?,
601 }
602 };
603
604 let mut dynamic_entries = vec![
605 (DT_FLAGS, (DF_SYMBOLIC | DF_BIND_NOW).0),
606 (DT_FLAGS_1, DF_1_NOW.0),
607 ];
608 if let Some(host_call) = host_call {
609 dynamic_entries.extend([
610 (DT_JMPREL, host_call.rela_plt.address),
611 (DT_PLTRELSZ, host_call.rela_plt.size),
612 (DT_PLTGOT, host_call.got_plt.address),
613 (DT_PLTREL, u64::try_from(DT_RELA.0)?),
614 ]);
615 }
616 dynamic_entries.extend([
617 (DT_SYMTAB, dynsym_address),
618 (DT_SYMENT, SYMBOL_SIZE),
619 (DT_STRTAB, dynstr.address),
620 (DT_STRSZ, dynstr.size),
621 (DT_HASH, hash_address),
622 (DT_NULL, 0),
623 ]);
624 if dynamic_entries.len() != num_dynamic_entries {
625 return Err(anyhow::anyhow!(
626 "Number of dynamic entries mismatch: {} != {num_dynamic_entries}",
627 dynamic_entries.len()
628 ));
629 }
630
631 writer.write_file_header(&FileHeader {
632 os_abi: ELFOSABI_GNU,
633 abi_version: 0,
634 e_type: ET_DYN,
635 e_machine: EM_RISCV,
636 e_entry: 0,
637 e_flags: EF_RISCV_RVC,
638 })?;
639
640 let program_headers = Placement::identity(
641 writer.write_align_program_headers(),
642 u64::try_from(NUM_PROGRAM_HEADERS)? * PROGRAM_HEADER_SIZE,
643 );
644 let program_headers: [_; NUM_PROGRAM_HEADERS] = [
645 program_headers.program_header(PT_PHDR, PF_R, WORD_ALIGNMENT),
646 Placement::identity(0, read_only.end_offset()).program_header(PT_LOAD, PF_R, PAGE_SIZE),
647 Placement {
648 size: executable_end.end_address() - text.address,
649 ..text
650 }
651 .program_header(PT_LOAD, PF_R | PF_X, PAGE_SIZE),
652 Placement {
653 size: writable_end.end_address() - dynamic.address,
654 ..dynamic
655 }
656 .program_header(PT_LOAD, PF_R | PF_W, PAGE_SIZE),
657 dynamic.program_header(PT_DYNAMIC, PF_R | PF_W, WORD_ALIGNMENT),
658 Placement::default().program_header(PT_GNU_STACK, PF_R | PF_W, 0),
659 riscv_attributes_placement.program_header(PT_RISCV_ATTRIBUTES, PF_R, 1),
660 ];
661 for program_header in &program_headers {
662 writer.write_program_header(program_header);
663 }
664
665 writer.write_null_dynamic_symbol();
666 for (symbol, (_index, name)) in symbols.iter().zip(symbol_ids) {
667 let (section, st_type, st_value, st_size) = match symbol.function {
668 Some((address, size)) => (
669 Some(text_section.index.0),
670 STT_FUNC,
671 read_only.address + address,
672 size,
673 ),
674 None => (None, STT_NOTYPE, 0, 0),
675 };
676 writer.write_dynamic_symbol(&Sym {
677 section,
678 st_name: writer.dynamic_string_offset(Some(name)),
679 st_info: SymbolInfo::new(STB_GLOBAL, st_type),
680 st_other: STV_DEFAULT.into(),
681 st_shndx: SHN_UNDEF,
683 st_value,
684 st_size,
685 });
686 }
687 writer.write_hash(num_symbols, num_symbols, |symbol_index| {
689 let symbol = symbols.get(usize::try_from(symbol_index.checked_sub(1)?).ok()?)?;
690 Some(object::elf::hash(symbol.name.as_bytes()))
691 });
692 writer.write_dynstr();
693
694 if let Some(host_call) = host_call {
695 writer.write_align_relocation();
696 writer.write_relocation(
697 true,
698 &Rel {
699 r_offset: host_call.got_plt.address + GOT_PLT_IMPORT_ENTRY_OFFSET,
700 r_sym: host_call.import_symbol_index.0,
701 r_type: R_RISCV_JUMP_SLOT,
702 r_addend: 0,
703 },
704 );
705 }
706 for (_section, placement, data) in &read_only_sections {
707 writer.pad_until(placement.offset);
708 writer.write(data);
709 }
710 writer.pad_until(text.offset);
711 writer.write(code);
712 if let Some(host_call) = host_call {
713 writer.pad_until(host_call.plt.offset);
714 writer.write(&plt_code(host_call.plt.address, host_call.got_plt.address));
715 }
716
717 writer.write_align_dynamic();
718 for (d_tag, d_val) in dynamic_entries {
719 writer.write_dynamic(d_tag, d_val)?;
720 }
721 if let Some(host_call) = host_call {
722 let got_plt_entries: [_; GOT_PLT_ENTRIES] =
725 [0, 0, host_call.plt.address].map(|entry| U64::new(LittleEndian, entry));
726 writer.pad_until(host_call.got_plt.offset);
727 writer.write(bytes_of_slice(&got_plt_entries));
728 }
729 writer.pad_until(riscv_attributes_placement.offset);
730 writer.write(&riscv_attributes);
731 writer.write_shstrtab();
732
733 writer.write_null_section_header();
734 writer.write_dynsym_section_header(dynsym_address, 1);
736 writer.write_hash_section_header(hash_address);
737 writer.write_dynstr_section_header(dynstr.address);
738 if let Some(host_call) = host_call {
739 let dynsym_index = writer.dynsym_index();
740 host_call.rela_plt_section.write_header(
741 &mut writer,
742 SectionHeader {
743 sh_type: SHT_RELA,
744 sh_flags: SHF_ALLOC | SHF_INFO_LINK,
745 sh_link: dynsym_index.0,
746 sh_info: host_call.got_plt_section.index.0,
747 sh_addralign: WORD_ALIGNMENT,
748 sh_entsize: RELA_SIZE,
749 ..host_call.rela_plt.section_header()
750 },
751 );
752 }
753 for (section, placement, _data) in &read_only_sections {
754 section.write_header(
755 &mut writer,
756 SectionHeader {
757 sh_type: SHT_PROGBITS,
758 sh_flags: SHF_ALLOC,
759 sh_addralign: 1,
760 ..placement.section_header()
761 },
762 );
763 }
764 text_section.write_header(
765 &mut writer,
766 SectionHeader {
767 sh_type: SHT_PROGBITS,
768 sh_flags: SHF_ALLOC | SHF_EXECINSTR,
769 sh_addralign: INSTRUCTION_ALIGNMENT,
770 ..text.section_header()
771 },
772 );
773 if let Some(host_call) = host_call {
774 host_call.plt_section.write_header(
775 &mut writer,
776 SectionHeader {
777 sh_type: SHT_PROGBITS,
778 sh_flags: SHF_ALLOC | SHF_EXECINSTR,
779 sh_addralign: INSTRUCTION_ALIGNMENT,
780 ..host_call.plt.section_header()
781 },
782 );
783 }
784 writer.write_dynamic_section_header(dynamic.address);
785 if let Some(host_call) = host_call {
786 host_call.got_plt_section.write_header(
787 &mut writer,
788 SectionHeader {
789 sh_type: SHT_PROGBITS,
790 sh_flags: SHF_ALLOC | SHF_WRITE,
791 sh_addralign: WORD_ALIGNMENT,
792 ..host_call.got_plt.section_header()
793 },
794 );
795 }
796 riscv_attributes_section.write_header(
797 &mut writer,
798 SectionHeader {
799 sh_type: SHT_RISCV_ATTRIBUTES,
800 sh_addralign: 1,
801 ..riscv_attributes_placement.section_header()
802 },
803 );
804 writer.write_shstrtab_section_header();
805
806 Ok(output_file)
807}