| #!/usr/bin/env python3 | |
| # | |
| # Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file | |
| # for details. All rights reserved. Use of this source code is governed by a | |
| # BSD-style license that can be found in the LICENSE file. | |
| import argparse | |
| from ctypes import create_string_buffer | |
| from struct import * | |
| # FILE HEADER FLAGS | |
| FILE_HEADER_RELFLG = 0x1 # No relocation information | |
| FILE_HEADER_EXEC = 0x2 # Executable | |
| FILE_HEADER_LNNO = 0x4 # No line number information | |
| FILE_HEADER_LSYMS = 0x8 # Local symbols removed / not present | |
| FILE_HEADER_AR32WR = 0x100 # File is 32-bit little endian | |
| # SECTION HEADER FLAGS | |
| SECTION_HEADER_TEXT = 0x20 # Contains executable code | |
| SECTION_HEADER_DATA = 0x40 # Contains only initialized data | |
| SECTION_HEADER_BSS = 0x80 # Contains uninitialized data | |
| SECTION_HEADER_ALIGN64 = 0x00700000 # Align data on a 64-byte boundary | |
| SECTION_HEADER_EXECUTE = 0x20000000 # The section can be executed as code | |
| SECTION_HEADER_READ = 0x40000000 # The section can be read | |
| SECTION_HEADER_WRITE = 0x80000000 # The section can be written to | |
| # FILE HEADER FORMAT | |
| # typedef struct { | |
| # unsigned short f_magic; /* magic number */ | |
| # unsigned short f_nscns; /* number of sections */ | |
| # unsigned long f_timdat; /* time & date stamp */ | |
| # unsigned long f_symptr; /* file pointer to symtab */ | |
| # unsigned long f_nsyms; /* number of symtab entries */ | |
| # unsigned short f_opthdr; /* sizeof(optional hdr) */ | |
| # unsigned short f_flags; /* flags */ | |
| # } FILHDR; | |
| FILE_HEADER_FORMAT = 'HHIIIHH' | |
| FILE_HEADER_SIZE = calcsize(FILE_HEADER_FORMAT) | |
| FILE_HEADER_MAGICS = { | |
| 'x86': 0x014c, | |
| 'x64': 0x8664, | |
| 'arm': 0x1c0, | |
| 'arm64': 0xaa64, | |
| 'riscv32': 0x5032, | |
| 'riscv64': 0x5064, | |
| } | |
| FILE_HEADER_NUM_SECTIONS = 1 | |
| FILE_HEADER_TIMESTAMP = 0 | |
| FILE_HEADER_SIZE_OF_OPTIONAL = 0 | |
| FILE_HEADER_FLAGS = FILE_HEADER_LNNO | |
| # SECTION HEADER FORMAT | |
| # typedef struct { | |
| # char s_name[8]; /* section name */ | |
| # unsigned long s_paddr; /* physical address, aliased s_nlib */ | |
| # unsigned long s_vaddr; /* virtual address */ | |
| # unsigned long s_size; /* section size */ | |
| # unsigned long s_scnptr; /* file ptr to raw data for section */ | |
| # unsigned long s_relptr; /* file ptr to relocation */ | |
| # unsigned long s_lnnoptr; /* file ptr to line numbers */ | |
| # unsigned short s_nreloc; /* number of relocation entries */ | |
| # unsigned short s_nlnno; /* number of line number entries */ | |
| # unsigned long s_flags; /* flags */ | |
| # } SCNHDR; | |
| SECTION_HEADER_FORMAT = '8sIIIIIIHHI' | |
| SECTION_HEADER_SIZE = calcsize(SECTION_HEADER_FORMAT) | |
| SECTION_NAME_RDATA = b'.rdata' | |
| SECTION_NAME_TEXT = b'.text' | |
| SECTION_PADDR = 0x0 | |
| SECTION_VADDR = 0x0 | |
| SECTION_RAW_DATA_PTR = ( | |
| FILE_HEADER_SIZE + FILE_HEADER_NUM_SECTIONS * SECTION_HEADER_SIZE) | |
| SECTION_RELOCATION_PTR = 0x0 | |
| SECTION_LINE_NUMS_PTR = 0x0 | |
| SECTION_NUM_RELOCATION = 0 | |
| SECTION_NUM_LINE_NUMS = 0 | |
| # SYMBOL TABLE FORMAT | |
| # typedef struct { | |
| # union { | |
| # char e_name[8]; | |
| # struct { | |
| # unsigned long e_zeroes; | |
| # unsigned long e_offset; | |
| # } e; | |
| # } e; | |
| # unsigned long e_value; | |
| # short e_scnum; | |
| # unsigned short e_type; | |
| # unsigned char e_sclass; | |
| # unsigned char e_numaux; | |
| # } SYMENT; | |
| SYMBOL_TABLE_ENTRY_SHORT_LEN = 8 | |
| SYMBOL_TABLE_ENTRY_FORMAT_SHORT = '8sIhHBB' | |
| SYMBOL_TABLE_ENTRY_FORMAT_LONG = 'IIIhHBB' | |
| SYMBOL_TABLE_ENTRY_SIZE = calcsize(SYMBOL_TABLE_ENTRY_FORMAT_SHORT) | |
| SYMBOL_TABLE_ENTRY_ZEROS = 0x0 | |
| SYMBOL_TABLE_ENTRY_SECTION = 1 | |
| SYMBOL_TABLE_ENTRY_ABSOLUTE = -1 | |
| SYMBOL_TABLE_ENTRY_TYPE = 0 | |
| SYMBOL_TABLE_ENTRY_CLASS = 2 # External (public) symbol. | |
| SYMBOL_TABLE_ENTRY_CLASS_STATIC = 3 | |
| SYMBOL_TABLE_ENTRY_NUM_AUX = 0 # Number of auxiliary entries. | |
| STRING_TABLE_OFFSET = 0x4 # Starting offset for the string table. | |
| SIZE_FORMAT = 'I' | |
| SIZE_LENGTH = calcsize(SIZE_FORMAT) | |
| SIZE_SYMBOL_FORMAT_X64 = 'Q' | |
| SIZE_SYMBOL_LENGTH_X64 = calcsize(SIZE_SYMBOL_FORMAT_X64) | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description='Generate a COFF file for binary data.') | |
| parser.add_argument('--input', dest='input', help='Path of the input file.') | |
| parser.add_argument( | |
| '--output', dest='output', help='Name of the output file.') | |
| parser.add_argument( | |
| '--symbol_name', | |
| dest='symbol_name', | |
| help='Name of the symbol for the binary data') | |
| parser.add_argument( | |
| '--size_symbol_name', | |
| dest='size_name', | |
| help='Name of the symbol for the size of the binary data') | |
| parser.add_argument('--arch', dest='arch') | |
| parser.add_argument( | |
| '--executable', dest='executable', action='store_true', default=False) | |
| args = parser.parse_args() | |
| use_64_bit = args.arch in ['x64', 'arm64', 'riscv64'] | |
| with open(args.input, 'rb') as f: | |
| section_data = f.read() | |
| # We need to calculate the following to determine the size of our buffer: | |
| # 1) Size of the data | |
| # 2) Total length of the symbol strings which are over 8 characters | |
| section_size = len(section_data) | |
| includes_size_name = (args.size_name != None) | |
| # Symbols on x86 are prefixed with '_' | |
| symbol_prefix = b'_' if args.arch == 'x86' else b'' | |
| num_symbols = 1 | |
| symbol_name = symbol_prefix + args.symbol_name.encode() | |
| size_symbol_name = None | |
| if (includes_size_name): | |
| size_symbol = args.size_name if args.size_name else args.symbol_name + "Size" | |
| size_symbol_name = symbol_prefix + size_symbol.encode() | |
| num_symbols += 1 | |
| if (args.arch == 'x86'): | |
| feat_symbol_name = b'@feat.00' | |
| num_symbols += 1 | |
| size_symbol_format = SIZE_SYMBOL_FORMAT_X64 if use_64_bit else SIZE_FORMAT | |
| size_symbol_size = SIZE_SYMBOL_LENGTH_X64 if use_64_bit else SIZE_LENGTH | |
| # The symbol table is directly after the data section | |
| symbol_table_ptr = (FILE_HEADER_SIZE + SECTION_HEADER_SIZE + section_size + | |
| size_symbol_size) | |
| string_table_len = 0 | |
| # Symbols longer than 8 characters have their string representations stored | |
| # in the string table. | |
| long_symbol_name = False | |
| long_size_symbol_name = False | |
| long_feat_symbol_name = False | |
| if (len(symbol_name) > SYMBOL_TABLE_ENTRY_SHORT_LEN): | |
| string_table_len += len(symbol_name) + 1 | |
| long_symbol_name = True | |
| if (includes_size_name and | |
| (len(size_symbol_name) > SYMBOL_TABLE_ENTRY_SHORT_LEN)): | |
| string_table_len += len(size_symbol_name) + 1 | |
| long_size_symbol_name = True | |
| if (args.arch == 'x86' and | |
| (len(feat_symbol_name) > SYMBOL_TABLE_ENTRY_SHORT_LEN)): | |
| string_table_len += len(feat_symbol_name) + 1 | |
| long_feat_symbol_name = True | |
| # Create the buffer and start building. | |
| offset = 0 | |
| buff = create_string_buffer( | |
| FILE_HEADER_SIZE + SECTION_HEADER_SIZE + section_size + | |
| num_symbols * SYMBOL_TABLE_ENTRY_SIZE + SIZE_LENGTH + size_symbol_size + | |
| string_table_len) | |
| FILE_HEADER_MAGIC = FILE_HEADER_MAGICS[args.arch] | |
| # Populate the file header. Basically constant except for the pointer to the | |
| # beginning of the symbol table. | |
| pack_into(FILE_HEADER_FORMAT, buff, offset, FILE_HEADER_MAGIC, | |
| FILE_HEADER_NUM_SECTIONS, FILE_HEADER_TIMESTAMP, symbol_table_ptr, | |
| num_symbols, FILE_HEADER_SIZE_OF_OPTIONAL, FILE_HEADER_FLAGS) | |
| offset += FILE_HEADER_SIZE | |
| section_name = SECTION_NAME_RDATA | |
| section_flags = SECTION_HEADER_DATA | SECTION_HEADER_ALIGN64 | SECTION_HEADER_READ | |
| if args.executable: | |
| section_name = SECTION_NAME_TEXT | |
| section_flags = SECTION_HEADER_TEXT | SECTION_HEADER_ALIGN64 | SECTION_HEADER_EXECUTE | SECTION_HEADER_READ | |
| # Populate the section header for a single section. | |
| pack_into(SECTION_HEADER_FORMAT, buff, offset, section_name, SECTION_PADDR, | |
| SECTION_VADDR, section_size + size_symbol_size, | |
| SECTION_RAW_DATA_PTR, SECTION_RELOCATION_PTR, | |
| SECTION_LINE_NUMS_PTR, SECTION_NUM_RELOCATION, | |
| SECTION_NUM_LINE_NUMS, section_flags) | |
| offset += SECTION_HEADER_SIZE | |
| # Copy the binary data. | |
| buff[offset:offset + section_size] = section_data | |
| offset += section_size | |
| # Append the size of the section. | |
| pack_into(size_symbol_format, buff, offset, section_size) | |
| offset += size_symbol_size | |
| # Build the symbol table. If a symbol name is 8 characters or less, it's | |
| # placed directly in the symbol table. If not, it's entered in the string | |
| # table immediately after the symbol table. | |
| string_table_offset = STRING_TABLE_OFFSET | |
| if long_symbol_name: | |
| pack_into(SYMBOL_TABLE_ENTRY_FORMAT_LONG, buff, offset, | |
| SYMBOL_TABLE_ENTRY_ZEROS, string_table_offset, 0x0, | |
| SYMBOL_TABLE_ENTRY_SECTION, SYMBOL_TABLE_ENTRY_TYPE, | |
| SYMBOL_TABLE_ENTRY_CLASS, SYMBOL_TABLE_ENTRY_NUM_AUX) | |
| string_table_offset += len(symbol_name) + 1 | |
| else: | |
| pack_into(SYMBOL_TABLE_ENTRY_FORMAT_SHORT, buff, offset, symbol_name, | |
| 0x0, SYMBOL_TABLE_ENTRY_SECTION, SYMBOL_TABLE_ENTRY_TYPE, | |
| SYMBOL_TABLE_ENTRY_CLASS, SYMBOL_TABLE_ENTRY_NUM_AUX) | |
| offset += SYMBOL_TABLE_ENTRY_SIZE | |
| if includes_size_name: | |
| # The size symbol table entry actually contains the value for the size. | |
| if long_size_symbol_name: | |
| pack_into(SYMBOL_TABLE_ENTRY_FORMAT_LONG, buff, offset, | |
| SYMBOL_TABLE_ENTRY_ZEROS, string_table_offset, | |
| section_size, SYMBOL_TABLE_ENTRY_SECTION, | |
| SYMBOL_TABLE_ENTRY_TYPE, SYMBOL_TABLE_ENTRY_CLASS, | |
| SYMBOL_TABLE_ENTRY_NUM_AUX) | |
| else: | |
| pack_into(SYMBOL_TABLE_ENTRY_FORMAT_SHORT, buff, offset, | |
| symbol_name, section_size, SYMBOL_TABLE_ENTRY_SECTION, | |
| SYMBOL_TABLE_ENTRY_TYPE, SYMBOL_TABLE_ENTRY_CLASS, | |
| SYMBOL_TABLE_ENTRY_NUM_AUX) | |
| offset += SYMBOL_TABLE_ENTRY_SIZE | |
| if args.arch == 'x86': | |
| FEATURE_SAFESEH = 0x1 | |
| if long_feat_symbol_name: | |
| pack_into(SYMBOL_TABLE_ENTRY_FORMAT_LONG, buff, offset, | |
| SYMBOL_TABLE_ENTRY_ZEROS, string_table_offset, | |
| FEATURE_SAFESEH, SYMBOL_TABLE_ENTRY_ABSOLUTE, | |
| SYMBOL_TABLE_ENTRY_TYPE, SYMBOL_TABLE_ENTRY_CLASS_STATIC, | |
| SYMBOL_TABLE_ENTRY_NUM_AUX) | |
| string_table_offset += len(feat_symbol_name) + 1 | |
| else: | |
| pack_into(SYMBOL_TABLE_ENTRY_FORMAT_SHORT, buff, offset, | |
| feat_symbol_name, FEATURE_SAFESEH, | |
| SYMBOL_TABLE_ENTRY_ABSOLUTE, SYMBOL_TABLE_ENTRY_TYPE, | |
| SYMBOL_TABLE_ENTRY_CLASS_STATIC, | |
| SYMBOL_TABLE_ENTRY_NUM_AUX) | |
| offset += SYMBOL_TABLE_ENTRY_SIZE | |
| pack_into(SIZE_FORMAT, buff, offset, string_table_len + SIZE_LENGTH) | |
| offset += SIZE_LENGTH | |
| # Populate the string table for any symbols longer than 8 characters. | |
| if long_symbol_name: | |
| symbol_len = len(symbol_name) | |
| buff[offset:offset + symbol_len] = symbol_name | |
| offset += symbol_len | |
| buff[offset] = b'\0' | |
| offset += 1 | |
| if includes_size_name and long_size_symbol_name: | |
| symbol_len = len(size_symbol_name) | |
| buff[offset:offset + symbol_len] = size_symbol_name | |
| offset += symbol_len | |
| buff[offset] = b'\0' | |
| offset += 1 | |
| if args.arch == 'x86' and long_feat_symbol_name: | |
| symbol_len = len(feat_symbol_name) | |
| buff[offset:offset + symbol_len] = feat_symbol_name | |
| offset += symbol_len | |
| buff[offset] = b'\0' | |
| offset += 1 | |
| with open(args.output, 'wb') as f: | |
| f.write(buff.raw) | |
| if __name__ == '__main__': | |
| main() |