# This file is part of PeachPy package and is licensed under the Simplified BSD license. # See license.rst for the full text of the license. active_writers = [] class TextWriter(object): def __init__(self, output_path): super(TextWriter, self).__init__() self.output_path = output_path self.prologue = [] self.content = [] self.epilogue = [] def __enter__(self): global active_writers active_writers.append(self) self.output_file = open(self.output_path, "w") return self def __exit__(self, exc_type, exc_value, traceback): global active_writers active_writers.remove(self) if exc_type is None: self.output_file.write(self.serialize()) self.output_file.close() self.output_file = None else: import os os.unlink(self.output_file.name) self.output_file = None raise def serialize(self): import os prologue = self.prologue if not isinstance(prologue, str): prologue = os.linesep.join(map(str, prologue)) if prologue: prologue += os.linesep * 3 content = self.content if not isinstance(content, str): content = os.linesep.join(map(str, content)) epilogue = self.epilogue if not isinstance(epilogue, str): epilogue = os.linesep.join(map(str, epilogue)) if epilogue: epilogue = os.linesep * 3 + epilogue return prologue + content + epilogue class AssemblyWriter(TextWriter): def __init__(self, output_path, assembly_format, input_path=None): super(AssemblyWriter, self).__init__(output_path) if assembly_format not in {"go", "nasm", "masm", "gas"}: raise ValueError("Unknown assembly format: %s" % assembly_format) self.assembly_format = assembly_format self.comment_prefix = { "go": "//", "nasm": ";", "masm": ";", "gas": "#" }[assembly_format] if assembly_format == "go": from os import linesep self.prologue = "// +build !noasm" + linesep else: self.prologue = "" import peachpy if input_path is not None: self.prologue += "{escape} Generated by PeachPy {version} from {filename}".format( escape=self.comment_prefix, version=peachpy.__version__, filename=input_path) else: self.prologue += "{escape} Generated by PeachPy {version}".format( escape=self.comment_prefix, version=peachpy.__version__) self.prologue_lines = len(self.prologue.splitlines()) + 3 def add_function(self, function): import peachpy.x86_64.function assert isinstance(function, peachpy.x86_64.function.ABIFunction), \ "Function must be finalized with an ABI before its assembly can be used" function_lines = function.format(self.assembly_format, line_separator=None, line_number=self.prologue_lines + len(self.content)) self.content += function_lines class ImageWriter(object): def __init__(self, output_path): super(ImageWriter, self).__init__() self.output_path = output_path def __enter__(self): global active_writers active_writers.append(self) self.output_file = open(self.output_path, "wb", buffering=0) return self def __exit__(self, exc_type, exc_value, traceback): global active_writers active_writers.remove(self) if exc_type is None: self.output_file.write(self.encode()) self.output_file.close() self.output_file = None else: import os os.unlink(self.output_file.name) self.output_file = None raise def encode(self): return bytearray() class ELFWriter(ImageWriter): def __init__(self, output_path, abi, input_path=None): super(ELFWriter, self).__init__(output_path) from peachpy.formats.elf.image import Image from peachpy.formats.elf.section import TextSection, ProgramBitsSection self.abi = abi self.image = Image(abi, input_path) self.text_section = TextSection() self.image.add_section(self.text_section) self.gnu_stack_section = ProgramBitsSection(".note.GNU-stack", allocate=False) self.image.add_section(self.gnu_stack_section) self.text_rela_section = None self.rodata_section = None def encode(self): return self.image.as_bytearray def add_function(self, function): import peachpy.x86_64.function from peachpy.util import roundup assert isinstance(function, peachpy.x86_64.function.ABIFunction), \ "Function must be finalized with an ABI before its assembly can be used" encoded_function = function.encode() code_offset = len(self.text_section.content) code_padding = bytearray([encoded_function.code_section.alignment_byte] * (roundup(code_offset, encoded_function.code_section.alignment) - code_offset)) self.text_section.content += code_padding code_offset += len(code_padding) self.text_section.content += encoded_function.code_section.content self.text_section.alignment = max(self.text_section.alignment, encoded_function.code_section.alignment) const_offset = 0 if encoded_function.const_section.content: if self.rodata_section is None: from peachpy.formats.elf.section import ReadOnlyDataSection self.rodata_section = ReadOnlyDataSection() self.image.add_section(self.rodata_section) const_offset = self.rodata_section.get_content_size(self.abi) const_padding = bytearray([encoded_function.const_section.alignment_byte] * (roundup(const_offset, encoded_function.const_section.alignment) - const_offset)) self.rodata_section.content += const_padding const_offset += len(const_padding) self.rodata_section.content += encoded_function.const_section.content self.rodata_section.alignment = max(self.rodata_section.alignment, encoded_function.const_section.alignment) # Map from symbol name to symbol index from peachpy.formats.elf.symbol import Symbol, SymbolBinding, SymbolType symbol_map = dict() for symbol in encoded_function.const_section.symbols: const_symbol = Symbol() const_symbol.name = function.mangled_name + "." + symbol.name const_symbol.value = const_offset + symbol.offset const_symbol.size = symbol.size const_symbol.section = self.rodata_section const_symbol.binding = SymbolBinding.local const_symbol.type = SymbolType.data_object self.image.symtab.add(const_symbol) symbol_map[symbol] = const_symbol if encoded_function.code_section.relocations: if self.text_rela_section is None: from peachpy.formats.elf.section import RelocationsWithAddendSection self.text_rela_section = RelocationsWithAddendSection(self.text_section, self.image.symtab) self.image.add_section(self.text_rela_section) from peachpy.formats.elf.symbol import RelocationWithAddend, RelocationType for relocation in encoded_function.code_section.relocations: elf_relocation = RelocationWithAddend(RelocationType.x86_64_pc32, code_offset + relocation.offset, symbol_map[relocation.symbol], relocation.offset - relocation.program_counter) self.text_rela_section.add(elf_relocation) function_symbol = Symbol() function_symbol.name = function.mangled_name function_symbol.value = code_offset function_symbol.content_size = len(encoded_function.code_section) function_symbol.section = self.text_section function_symbol.binding = SymbolBinding.global_ function_symbol.type = SymbolType.function self.image.symtab.add(function_symbol) class MachOWriter(ImageWriter): def __init__(self, output_path, abi): super(MachOWriter, self).__init__(output_path) from peachpy.formats.macho.image import Image self.abi = abi self.image = Image(abi) def encode(self): return self.image.encode() def add_function(self, function): import peachpy.x86_64.function assert isinstance(function, peachpy.x86_64.function.ABIFunction), \ "Function must be finalized with an ABI before its assembly can be used" from peachpy.formats.macho.symbol import Symbol, SymbolType, SymbolDescription, SymbolVisibility, \ Relocation, RelocationType from peachpy.util import roundup encoded_function = function.encode() code_offset = len(self.image.text_section.content) code_padding = bytearray([encoded_function.code_section.alignment_byte] * (roundup(code_offset, encoded_function.code_section.alignment) - code_offset)) self.image.text_section.content += code_padding code_offset += len(code_padding) self.image.text_section.content += encoded_function.code_section.content self.image.text_section.alignment = \ max(self.image.text_section.alignment, encoded_function.code_section.alignment) const_offset = self.image.const_section.content_size const_padding = bytearray([encoded_function.const_section.alignment_byte] * (roundup(const_offset, encoded_function.const_section.alignment) - const_offset)) self.image.const_section.content += const_padding const_offset += len(const_padding) self.image.const_section.content += encoded_function.const_section.content self.image.const_section.alignment = \ max(self.image.const_section.alignment, encoded_function.const_section.alignment) # Map from PeachPy symbol to Mach-O symbol symbol_map = dict() for symbol in encoded_function.const_section.symbols: macho_symbol = Symbol("_" + function.mangled_name + "." + symbol.name, SymbolType.section_relative, self.image.const_section, const_offset + symbol.offset) macho_symbol.description = SymbolDescription.defined self.image.symbol_table.add_symbol(macho_symbol) symbol_map[symbol] = macho_symbol for relocation in encoded_function.code_section.relocations: macho_relocation = Relocation(RelocationType.x86_64_signed, code_offset + relocation.offset, 4, symbol_map[relocation.symbol], is_pc_relative=True) relocation_addend = relocation.offset + 4 - relocation.program_counter if relocation_addend != 0: self.image.text_section.content[code_offset + relocation.offset] = relocation_addend & 0xFF self.image.text_section.content[code_offset + relocation.offset + 1] = (relocation_addend >> 8) & 0xFF self.image.text_section.content[code_offset + relocation.offset + 2] = (relocation_addend >> 16) & 0xFF self.image.text_section.content[code_offset + relocation.offset + 3] = (relocation_addend >> 24) & 0xFF self.image.text_section.relocations.append(macho_relocation) function_symbol = Symbol("_" + function.mangled_name, SymbolType.section_relative, self.image.text_section, value=code_offset) function_symbol.description = SymbolDescription.defined function_symbol.visibility = SymbolVisibility.external self.image.symbol_table.add_symbol(function_symbol) class MSCOFFWriter(ImageWriter): def __init__(self, output_path, abi, input_path=None): super(MSCOFFWriter, self).__init__(output_path) from peachpy.formats.mscoff import Image, TextSection, ReadOnlyDataSection self.output_path = output_path self.abi = abi self.image = Image(abi, input_path) self.text_section = TextSection() self.image.add_section(self.text_section) self.rdata_section = ReadOnlyDataSection() self.image.add_section(self.rdata_section) def encode(self): return self.image.encode() def add_function(self, function): import peachpy.x86_64.function assert isinstance(function, peachpy.x86_64.function.ABIFunction), \ "Function must be finalized with an ABI before its assembly can be used" from peachpy.util import roundup from peachpy.formats.mscoff import Symbol, SymbolType, StorageClass, Relocation, RelocationType encoded_function = function.encode() code_offset = len(self.text_section.content) code_padding = bytearray([encoded_function.code_section.alignment_byte] * (roundup(code_offset, encoded_function.code_section.alignment) - code_offset)) self.text_section.content += code_padding code_offset += len(code_padding) self.text_section.content += encoded_function.code_section.content self.text_section.alignment = \ max(self.text_section.alignment, encoded_function.code_section.alignment) rdata_offset = self.rdata_section.content_size rdata_padding = bytearray([encoded_function.const_section.alignment_byte] * (roundup(rdata_offset, encoded_function.const_section.alignment) - rdata_offset)) self.rdata_section.content += rdata_padding rdata_offset += len(rdata_padding) self.rdata_section.content += encoded_function.const_section.content self.rdata_section.alignment = \ max(self.rdata_section.alignment, encoded_function.const_section.alignment) # Map from PeachPy symbol to Mach-O symbol symbol_map = dict() for symbol in encoded_function.const_section.symbols: mscoff_symbol = Symbol() mscoff_symbol.name = symbol.name mscoff_symbol.value = rdata_offset + symbol.offset mscoff_symbol.section = self.rdata_section mscoff_symbol.symbol_type = SymbolType.non_function mscoff_symbol.storage_class = StorageClass.static self.image.add_symbol(mscoff_symbol) symbol_map[symbol] = mscoff_symbol for relocation in encoded_function.code_section.relocations: relocation_type_map = { 4: RelocationType.x86_64_relocation_offset32, 5: RelocationType.x86_64_relocation_plus_1_offset32, 6: RelocationType.x86_64_relocation_plus_2_offset32, 7: RelocationType.x86_64_relocation_plus_3_offset32, 8: RelocationType.x86_64_relocation_plus_4_offset32, 9: RelocationType.x86_64_relocation_plus_5_offset32 } relocation_type = relocation_type_map[relocation.program_counter - relocation.offset] mscoff_relocation = Relocation(relocation_type, code_offset + relocation.offset, symbol_map[relocation.symbol]) self.text_section.relocations.append(mscoff_relocation) function_symbol = Symbol() function_symbol.name = function.mangled_name function_symbol.value = code_offset function_symbol.section = self.text_section function_symbol.symbol_type = SymbolType.function function_symbol.storage_class = StorageClass.external self.image.add_symbol(function_symbol) class MetadataWriter(TextWriter): def __init__(self, output_path): super(MetadataWriter, self).__init__(output_path) self.metadata = [] def add_function(self, function): import peachpy.x86_64.function assert isinstance(function, peachpy.x86_64.function.ABIFunction), \ "Function must be finalized with an ABI before its assembly can be used" self.metadata.append(function.metadata) class JSONMetadataWriter(MetadataWriter): def __init__(self, output_path): super(JSONMetadataWriter, self).__init__(output_path) def serialize(self): import json return json.dumps(self.metadata) class CHeaderWriter(TextWriter): def __init__(self, output_path, input_path=None): super(CHeaderWriter, self).__init__(output_path) import peachpy if input_path is not None: self.prologue = ["/* Generated by PeachPy %s from %s */" % (peachpy.__version__, input_path)] else: self.prologue = ["/* Generated by PeachPy %s */" % peachpy.__version__] self.prologue += [ "", "#pragma once", "", "#ifdef __cplusplus", "extern \"C\" {", "#endif" ] self.epilogue = [ "#ifdef __cplusplus", "} /* extern \"C\" */", "#endif", "" ] def add_function(self, function): import peachpy.x86_64.function assert isinstance(function, peachpy.x86_64.function.ABIFunction), \ "Function must be finalized with an ABI before its assembly can be used" return_type = "void" if function.result_type is None else str(function.result_type) arguments = [str(arg.c_type) + " " + arg.name for arg in function.arguments] self.content.append(return_type + " " + function.mangled_name + "(" + ", ".join(arguments) + ");")