/*********************************************************************** * toolchain/nxflat/ldnxflat.c * Convert ELF (or any BFD format) to NXFLAT binary format * * ldnxflat takes a fully resolvable elf binary which was linked with -r * and resolves all references, then generates relocation table entries for * any relocation entries in data sections. This is designed to work with * the options -fpic -msingle-pic-base -mno-got (or -membedded-pic) * * Copyright (C) 2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * * Modified from ldelf2xflat (see http://xflat.org): * * Copyright (c) 2002, 2006, Cadenux, LLC. All rights reserved. * Copyright (c) 2002, 2006, Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Extended from the FLAT ldnxflat.c (original copyright below ) * * Copyright (C) 2000 NETsilicon, Inc. * Copyright (C) 2000 WireSpeed Communications Corp * * author : Joe deBlaquiere ( joe@wirespeed.com ) * * converted from elf2flt.c ( original copyright below ) * * elf2flt copyright : * * (c) 1999, Greg Ungerer * (c) 1999, Phil Blundell, Nexus Electronics Ltd * * Hacked this about badly to fully support relocating binaries. * * Originally obj-res.c * * (c) 1998, Kenneth Albanowski * (c) 1998, D. Jeff Dionne * (c) 1998, The Silver Hammer Group Ltd. * (c) 1996, 1997 Dionne & Associates * jeff@ryeham.ee.ryerson.ca * * Relocation added March 1997, Kresten Krab Thorup * krab@california.daimi.aau.dk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ***********************************************************************/ /*********************************************************************** * Included Files ***********************************************************************/ #include #include #include #include #include #include #include #include #include #include "bfd.h" #include "arch/arch.h" #include "nxflat.h" /*********************************************************************** * Compilation Switches ***********************************************************************/ /* #define RELOCS_IN_NETWORK_ORDER 1 */ #define LIBS_CAN_INCLUDE_LIBS 1 /*********************************************************************** * Definitions ***********************************************************************/ #ifndef PARAMS # define PARAMS(x) x #endif #ifdef __CYGWIN32__ # define O_PLATFORM O_BINARY #else # define O_PLATFORM 0 #endif #define MAX_SECTIONS 16 #define DEFAULT_STACK_SIZE 4096 #define IS_GLOBAL(x) ((((x)->flags)&(BSF_GLOBAL))!=0) #define NXFLAT_HDR_SIZE sizeof(struct nxflat_hdr_s) /* The names of these fields have changed in later versions of binutils * (after 2.13 and before 2.15) and the meaning of _rawsize is has also * changed somewhat. In the same timeframe, the name of the section * structure changed. */ #if 0 # define COOKED_SIZE _cooked_size # define RAW_SIZE _raw_size # define bfd_section sec #else # define COOKED_SIZE size # define RAW_SIZE rawsize #endif /*********************************************************************** * Private Types ***********************************************************************/ struct elf_internal_sym { bfd_vma st_value; /* Value of the symbol */ bfd_vma st_size; /* Associated symbol size */ u_int32_t st_name; /* Symbol name, index in string tbl */ u_int8_t st_info; /* Type and binding attributes */ u_int8_t st_other; /* No defined meaning, 0 */ u_int16_t st_shndx; /* Associated section index */ }; typedef struct { /* The BFD symbol. */ asymbol symbol; /* ELF symbol information. */ struct elf_internal_sym internal_elf_sym; /* Backend specific information. */ union { unsigned int hppa_arg_reloc; void *mips_extr; void *any; } tc_data; /* Version information. This is from an Elf_Internal_Versym structure in a * SHT_GNU_versym section. It is zero if there is no version information. */ u_int16_t version; } elf_symbol_type; typedef struct _segment_info { const char *name; bfd_vma low_mark; bfd_vma high_mark; size_t size; void *contents; asection *subsect[MAX_SECTIONS]; int nsubsects; } segment_info; typedef void (*func_type) (asymbol * sym, void *arg1, void *arg2, void *arg3); /*********************************************************************** * Private Variable Data ***********************************************************************/ static int verbose = 0; static int dsyms = 0; static int stack_size = 0; static int32_t counter = 0; static const char *program_name = NULL; static const char *bfd_filename = NULL; static const char *entry_name = NULL; static char *out_filename = NULL; static segment_info text_info; static segment_info data_info; static segment_info bss_info; static asymbol **symbol_table = NULL; static int32_t number_of_symbols = 0; static asymbol *entry_symbol = NULL; static asymbol *dynname_begin_symbol = NULL; static asymbol *dynname_end_symbol = NULL; static asymbol *dynimport_begin_symbol = NULL; static asymbol *dynimport_end_symbol = NULL; /*********************************************************************** * Private Constant Data ***********************************************************************/ static const char default_exe_entry_name[] = "_start"; static const char dynpath_begin_name[] = "__dynpath_begin"; static const char dynpath_end_name[] = "__dynpath_end"; static const char dynname_begin_name[] = "__dynname_begin"; static const char dynname_end_name[] = "__dynname_end"; static const char dynexport_begin_name[] = "__dynexport_begin"; static const char dynexport_end_name[] = "__dynexport_end"; static const char dynimport_begin_name[] = "__dynimport_begin"; static const char dynimport_end_name[] = "__dynimport_end"; static const char dynloader_name[] = "__dynloader"; /*********************************************************************** * Private Functions ***********************************************************************/ /*********************************************************************** * nxflat_swap32 ***********************************************************************/ #ifdef ARCH_BIG_ENDIAN static inline u_int32_t nxflat_swap32(u_int32_t little) { u_int32_t big = ((little >> 24) & 0xff) | (((little >> 16) & 0xff) << 8) | (((little >> 8) & 0xff) << 16) | ((little & 0xff) << 24); return big; } #endif /*********************************************************************** * get_xflat32 ***********************************************************************/ static inline u_int32_t get_xflat32(u_int32_t * addr32) { return ntohl(*addr32); } /*********************************************************************** * put_xflat32 ***********************************************************************/ static void inline put_xflat32(u_int32_t * addr32, u_int32_t val32) { *addr32 = htonl(val32); } /*********************************************************************** * put_xflat16 ***********************************************************************/ static void inline put_xflat16(u_int16_t * addr16, u_int16_t val16) { #if 1 *addr16 = htons(val16); #else u_int32_t *addr32 = (u_int32_t *) (((u_int32_t) addr16) & ~3); u_int32_t ndx = ((((u_int32_t) addr16) >> 1) & 1); union { u_int16_t hw[2]; u_int32_t w; } uword; /* Fetch the 32 bit value */ uword.w = get_xflat32(addr32); /* Add in the 16 bit value */ uword.hw[ndx] = val16; /* Then save the 32-bit value */ put_xflat32(addr32, uword.w); #endif } /*********************************************************************** * get_symbols ***********************************************************************/ static asymbol **get_symbols(bfd * abfd, int32_t * num) { int32_t storage_needed; if (dsyms) { storage_needed = bfd_get_dynamic_symtab_upper_bound(abfd); } else { storage_needed = bfd_get_symtab_upper_bound(abfd); } if (storage_needed < 0) { abort(); } if (storage_needed == 0) { return NULL; } symbol_table = (asymbol **) malloc(storage_needed); if (dsyms) { number_of_symbols = bfd_canonicalize_dynamic_symtab(abfd, symbol_table); } else { number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table); } if (number_of_symbols < 0) { abort(); } *num = number_of_symbols; if (verbose) { printf("Read %d symbols\n", number_of_symbols); } return symbol_table; } /*********************************************************************** * traverse_global_symbols ***********************************************************************/ static void traverse_global_symbols(void *arg1, void *arg2, void *arg3, func_type fn) { int i; for (i = 0; i < number_of_symbols; i++) { /* Check if it is a global function symbol defined in this */ if (IS_GLOBAL(symbol_table[i])) { /* Yes, process the symbol */ fn(symbol_table[i], arg1, arg2, arg3); } } } /*********************************************************************** * check_special_symbol ***********************************************************************/ static void check_special_symbol(asymbol * sym, void *arg1, void *arg2, void *arg3) { if ((entry_name) && (strcmp(entry_name, sym->name) == 0)) { entry_symbol = sym; } else if (strcmp(dynname_begin_name, sym->name) == 0) { dynname_begin_symbol = sym; } else if (strcmp(dynname_end_name, sym->name) == 0) { dynname_end_symbol = sym; } else if (strcmp(dynimport_begin_name, sym->name) == 0) { dynimport_begin_symbol = sym; } else if (strcmp(dynimport_end_name, sym->name) == 0) { dynimport_end_symbol = sym; } counter++; } /*********************************************************************** * find_special_symbols ***********************************************************************/ static void inline find_special_symbols(void) { counter = 0; traverse_global_symbols(NULL, NULL, NULL, check_special_symbol); if (entry_symbol == NULL) { fprintf(stderr, "ERROR: Executable entry point \"%s\" not found\n", entry_name); exit(1); } if (dynname_begin_symbol == NULL) { fprintf(stderr, "WARNING: Special symbol \"%s\" not found\n", dynname_begin_name); } else { if (dynname_end_symbol == NULL) { fprintf(stderr, "ERROR: Symbol \"%s\" found, but missing \"%s\"\n", dynname_begin_name, dynname_end_name); exit(1); } #ifndef LIBS_CAN_INCLUDE_LIBS if (binary_type == NXFLAT_BINARY_TYPE_DYN) { fprintf(stderr, "WARNING: Shared library binary contains " "library names!\n"); fprintf(stderr, " Library names cannot be used by a " "shared library object\n"); fprintf(stderr, " You may want to rebuild rebuild the " "shared library intermediate file without\n"); fprintf(strderr, " specifying Library names (-l option " "in ldelflib)\n"); dynname_begin_symbol = NULL; dynname_end_symbol = NULL; } #endif /* LIBS_CAN_INCLUDE_LIBS */ } if (dynimport_begin_symbol == NULL) { fprintf(stderr, "WARNING: Special symbol \"%s\" not found\n", dynimport_begin_name); dynimport_end_symbol = NULL; } else if (dynimport_end_symbol == NULL) { fprintf(stderr, "ERROR: Symbol \"%s\" found, but missing \"%s\"\n", dynimport_begin_name, dynimport_end_name); exit(1); } } /*********************************************************************** * put_special_symbol ***********************************************************************/ static void put_special_symbol(asymbol * begin_sym, asymbol * end_sym, u_int32_t * addr, u_int16_t * count, u_int32_t elem_size, u_int32_t offset) { u_int32_t file_offset = 0; u_int32_t elems = 0; u_int32_t begin_sym_value; u_int32_t begin_sect_vma; /* We'll assume its okay if this symbol was not found. */ if (begin_sym != NULL) { /* Get the value of the beginning symbol and the section that it is * defined in. */ begin_sym_value = begin_sym->value; if (begin_sym->section == NULL) { fprintf(stderr, "No section for symbol \"%s\"\n", begin_sym->name); exit(1); } else { /* Get the file offset to the beginning symbol */ begin_sect_vma = begin_sym->section->vma; file_offset = NXFLAT_HDR_SIZE + /* Size of the xFLT header */ begin_sect_vma + /* Virtual address of section */ begin_sym_value + /* Value of the symbol */ offset; /* Additional file offset */ /* If there is a begin symbol, then there MUST be a corresponding * ending symbol. We must have this to get the size of the data * structure. This size will be used to determined the number of * elements in the array. */ if (end_sym == NULL) { /* No matching end symbol */ fprintf(stderr, "ERROR: Begin sym \"%s\" found, no corresponding end\n", begin_sym->name); exit(1); } else if (end_sym->section == NULL) { /* No section associated with the end symbol */ fprintf(stderr, "No section for symbol \"%s\"\n", end_sym->name); exit(1); } else if (end_sym->section != begin_sym->section) { /* Section associated with the end symbol is not the same as the * section associated with the begin symbol. */ fprintf(stderr, "ERROR: Begin sym \"%s\" is defined in section \"%s\"\n", begin_sym->name, begin_sym->section->name); fprintf(stderr, " but sym \"%s\" is defined in section \"%s\"\n", end_sym->name, end_sym->section->name); exit(1); } else if (end_sym->value < begin_sym_value) { /* End symbol is before the begin symbol? */ fprintf(stderr, "ERROR: Begin sym \"%s\" lies at offset %d " "in section \"%s\"\n", begin_sym->name, begin_sym_value, begin_sym->section->name); fprintf(stderr, " but sym \"%s\" is before that at offset: %d\n", end_sym->name, (u_int32_t) end_sym->value); exit(1); } else { /* Get the size of the structure in bytes */ u_int32_t array_size = end_sym->value - begin_sym_value; /* Get the number of elements in the structure. */ elems = array_size / elem_size; /* Verify that there are an even number of elements in the array. */ if (elems * elem_size != array_size) { fprintf(stderr, "ERROR: Array size (%d) is not a multiple " "of the element size (%d)\n", array_size, elem_size); exit(1); } } } if (verbose) { printf("Symbol %s: value: %08x section offset: %08x " "file offset: %08x count: %d\n", begin_sym->name, begin_sym_value, begin_sect_vma, file_offset, elems); } } put_xflat32(addr, file_offset); put_xflat16(count, elems); } /*********************************************************************** * put_entry_point ***********************************************************************/ static void put_entry_point(struct nxflat_hdr_s *hdr) { u_int32_t entry_point = 0; if (entry_symbol) { struct bfd_section *sect; /* Does this symbol lie in the text section? */ sect = entry_symbol->section; if (sect == NULL) { fprintf(stderr, "No section for entry symbol \"%s\"\n", entry_symbol->name); exit(1); } /* Get the file offset to the entry point symbol */ entry_point = NXFLAT_HDR_SIZE + sect->vma + entry_symbol->value; printf("Entry symbol \"%s\": %08x in section \"%s\"\n", entry_symbol->name, entry_point, sect->name); /* If verbose is selected, we'll show the details of the calculation. */ if (verbose) { printf("(HDR: %08lx + Section VMA: %08x + Symbol Value: %08x)\n", NXFLAT_HDR_SIZE, (u_int32_t) sect->vma, (u_int32_t) entry_symbol->value); } } /* Does the entry point lie within the text region? */ if ((entry_point < NXFLAT_HDR_SIZE) || (entry_point >= NXFLAT_HDR_SIZE + text_info.size)) { /* No... One special case: A shared library may not need an * initialization entry point. */ if (entry_point == 0) { /* Complain a little in this case... The used might have intended to * specify one. */ fprintf(stderr, "WARNING: Library has no initialization entry point\n"); } else { /* Otherwise, complain a lot. We either have a program with no * entry_point or a bogus entry_point. */ fprintf(stderr, "ERROR: Invalid entry point: %08x\n", entry_point); fprintf(stderr, " Valid TEXT range: %08lx - %08lx\n", NXFLAT_HDR_SIZE, NXFLAT_HDR_SIZE + text_info.size); exit(1); } } /* Put the entry point into the xFLT header. */ put_xflat32(&hdr->h_entry, entry_point); } /*********************************************************************** * get_reloc_type ***********************************************************************/ static int get_reloc_type(asection * sym_section, segment_info ** sym_segment) { int i; /* Locate the address referred to by section type. In the context in which * this runs, we can no longer use the flags field (it is zero for some * reason). But we can search for matches with the buffered section * pointers. */ /* Check if the symbol is defined in a BSS section */ for (i = 0; i < bss_info.nsubsects; i++) { if (bss_info.subsect[i] == sym_section) { /* Yes... */ if (verbose) { printf("Sym section %s is BSS\n", sym_section->name); } if (sym_segment) *sym_segment = &bss_info; return NXFLAT_RELOC_TYPE_BSS; } } /* Check if the symbol is defined in a TEXT section */ for (i = 0; i < text_info.nsubsects; i++) { if (text_info.subsect[i] == sym_section) { /* Yes... */ if (verbose) { printf("Sym section %s is CODE\n", sym_section->name); } if (sym_segment) *sym_segment = &text_info; return NXFLAT_RELOC_TYPE_TEXT; } } /* Check if the symbol is defined in a DATA section */ for (i = 0; i < data_info.nsubsects; i++) { if (data_info.subsect[i] == sym_section) { /* Yes... */ if (verbose) { printf("Sym section %s is DATA\n", sym_section->name); } if (sym_segment) *sym_segment = &data_info; return NXFLAT_RELOC_TYPE_DATA; } } fprintf(stderr, "ERROR: Could not find region for sym_section \"%s\" (%p)\n", sym_section->name, sym_section); return NXFLAT_RELOC_TYPE_NONE; } /*********************************************************************** * resolve_segment_relocs ***********************************************************************/ static void resolve_segment_relocs(bfd * input_bfd, segment_info * inf, asymbol ** syms, u_int32_t * n_relocs, struct nxflat_reloc_s **relocs) { int i; int j; int relsize, relcount; arelent **relpp; struct nxflat_reloc_s *nxflat_relocs; u_int32_t nxflat_reloc_count; nxflat_relocs = *relocs; nxflat_reloc_count = *n_relocs; for (i = 0; i < inf->nsubsects; i++) { relcount = inf->subsect[i]->reloc_count; if (verbose) { printf("Section %s has %08x relocs\n", inf->subsect[i]->name, relcount); } if (0 >= relcount) continue; relsize = bfd_get_reloc_upper_bound(input_bfd, inf->subsect[i]); if (verbose) { printf("Section %s reloc size: %08x\n", inf->subsect[i]->name, relsize); } if (0 >= relsize) continue; relpp = (arelent **) malloc((size_t) relsize); relcount = bfd_canonicalize_reloc(input_bfd, inf->subsect[i], relpp, syms); if (relcount < 0) { fprintf(stderr, "ERROR: bfd_canonicalize_reloc failed!\n"); exit(1); } if (verbose) { printf("Section %s can'd %08x relocs\n", inf->subsect[i]->name, relcount); } for (j = 0; j < relcount; j++) { reloc_howto_type *how_to = relpp[j]->howto; asymbol *rel_sym = *relpp[j]->sym_ptr_ptr; asection *rel_section = rel_sym->section; if (verbose > 1) { printf("rel %d -> sym @ %p [%28s] s_addr @ %08x, " "rel %08x how %s\n", j, relpp[j]->sym_ptr_ptr, rel_sym->name, (u_int32_t) relpp[j]->address, (u_int32_t) relpp[j]->addend, how_to->name); } switch (how_to->type) { case R_ARM_PLT32: case R_ARM_PC24: { int32_t *opcode; int32_t temp; int32_t saved; if (verbose) { printf("performing PC24 link at addr %08x to " "sym [%20s] @ %08x\n", (u_int32_t) relpp[j]->address, rel_sym->name, (u_int32_t) rel_sym->value); } /* Can't fix what we ain't got */ if (!(SEC_IN_MEMORY & rel_sym->section->flags)) { fprintf(stderr, "ERROR: section %s not loaded into mem!\n", rel_sym->section->name); exit(1); } /* PC24 -> can only fix text to text refs */ if (!(SEC_CODE & rel_sym->section->flags)) { fprintf(stderr, "ERROR: section %s not code!\n", rel_sym->section->name); exit(1); } if (!(SEC_CODE & inf->subsect[i]->flags)) { fprintf(stderr, "ERROR: section %s not code!\n", rel_sym->section->name); exit(1); } opcode = (int32_t *) (inf->contents + relpp[j]->address); if (verbose > 1) { printf("original opcode @ %p is %08x ", #ifdef ARCH_BIG_ENDIAN opcode, (int32_t) nxflat_swap32(*opcode)); #else opcode, *opcode); #endif printf("rsh %d ", how_to->rightshift); printf(" sz %d ", how_to->size); printf("bit %d ", how_to->bitsize); printf("rel %d ", how_to->pc_relative); printf("smask %08x ", (u_int32_t) how_to->src_mask); printf("dmask %08x ", (u_int32_t) how_to->dst_mask); printf("off %d ", how_to->pcrel_offset); printf("\n"); } if (how_to->pcrel_offset) { #ifdef ARCH_BIG_ENDIAN saved = temp = (int32_t) nxflat_swap32(*opcode); #else saved = temp = *opcode; #endif /* mask */ temp &= how_to->src_mask; /* sign extend */ temp <<= (32 - how_to->bitsize); temp >>= (32 - how_to->bitsize); /* offset */ temp += ((rel_sym->value + rel_sym->section->vma) - relpp[j]->address) >> how_to->rightshift; /* demote */ /* temp >>= how_to->rightshift; */ /* mask upper bits from rollover */ temp &= how_to->dst_mask; /* replace data that was masked */ temp |= saved & (~how_to->dst_mask); } else { fprintf(stderr, "ERROR: Do not know how pcrel_offset\n"); exit(1); } if (verbose > 1) { printf("result opcode: %08x\n", temp); } #ifdef ARCH_BIG_ENDIAN *opcode = (int32_t) nxflat_swap32(temp); #else *opcode = temp; #endif } break; case R_ARM_ABS32: { int32_t *opcode; int32_t temp; int32_t saved; if (verbose) { printf("performing ABS32 link at addr %08x to " "sym [%20s] @ %08x\n", (u_int32_t) relpp[j]->address, rel_sym->name, (u_int32_t) rel_sym->value); } /* ABS32 links from .text are easy - since the fetches will */ /* always be base relative. the ABS32 refs from data will be */ /* handled the same, with an appropriate reloc record for */ /* the load_flat_binary() kernel routine to handle */ opcode = (int32_t *) (inf->contents + relpp[j]->address); if (verbose) { printf("original opcode @ %p is %08x ", #ifdef ARCH_BIG_ENDIAN opcode, (int32_t) nxflat_swap32(*opcode)); #else opcode, *opcode); #endif printf("rsh %d ", how_to->rightshift); printf(" sz %d ", how_to->size); printf("bit %d ", how_to->bitsize); printf("rel %d ", how_to->pc_relative); printf("smask %08x ", (u_int32_t) how_to->src_mask); printf("dmask %08x ", (u_int32_t) how_to->dst_mask); printf("off %d ", how_to->pcrel_offset); printf("\n"); } #ifdef ARCH_BIG_ENDIAN saved = temp = (int32_t) nxflat_swap32(*opcode); #else saved = temp = *opcode; #endif /* mask */ temp &= how_to->src_mask; /* sign extend */ temp <<= (32 - how_to->bitsize); temp >>= (32 - how_to->bitsize); /* offset */ temp += (rel_sym->value + rel_sym->section->vma) >> how_to->rightshift; /* demote */ /* temp >>= how_to->rightshift; */ /* mask upper bits from rollover */ temp &= how_to->dst_mask; /* replace data that was masked */ temp |= saved & (~how_to->dst_mask); if (verbose) { printf("result opcode: %08x\n", temp); } #ifdef ARCH_BIG_ENDIAN *opcode = (int32_t) nxflat_swap32(temp); #else *opcode = temp; #endif if ((inf->subsect[i]->flags & SEC_DATA) && (inf->subsect[i]->flags & SEC_ALLOC)) { int reltype; if (verbose) printf("RELOCATION in DATA!\n"); /* Locate the address referred to by section type. */ reltype = get_reloc_type(rel_section, NULL); /* Create space for one more relocation. */ nxflat_relocs = realloc(nxflat_relocs, (nxflat_reloc_count + 1) * sizeof(struct nxflat_reloc_s)); /* And tuck in the new relocation. */ nxflat_relocs[nxflat_reloc_count].r_info = NXFLAT_RELOC(reltype, relpp[j]->address); nxflat_reloc_count++; } } break; case R_ARM_GOTOFF: /* Relocation is relative to the start of the global offset * table. */ fprintf(stderr, "Attempted GOTOFF reloc at addr %08x to " "sym [%20s] @ %08x\n", (u_int32_t) relpp[j]->address, rel_sym->name, (u_int32_t) rel_sym->value); goto got_not_supported; case R_ARM_GOT32: /* Relocation is to the entry for this symbol in the global * offset table. */ fprintf(stderr, "Attempted GOT32 reloc at addr %08x to " "sym [%20s] @ %08x\n", (u_int32_t) relpp[j]->address, rel_sym->name, (u_int32_t) rel_sym->value); goto got_not_supported; case R_ARM_GOTPC: /* Use global offset table as symbol value. */ fprintf(stderr, "Attempted GOTPC reloc at addr %08x to " "sym [%20s] @ %08x\n", (u_int32_t) relpp[j]->address, rel_sym->name, (u_int32_t) rel_sym->value); got_not_supported: fprintf(stderr, "You must compile using the -mno-got or -membedded-pic " "compiler option\n"); break; default: fprintf(stderr, "Do not know how to handle reloc %d type %s @ %p!\n", how_to->type, how_to->name, how_to); break; } } /* Mark the section as having no relocs */ inf->subsect[i]->flags &= !(SEC_RELOC); inf->subsect[i]->reloc_count = 0; } /* Write reloc record data back out */ *relocs = nxflat_relocs; *n_relocs = nxflat_reloc_count; } /*********************************************************************** * dump_symbol ***********************************************************************/ static void dump_symbol(asymbol * psym) { if (bfd_is_com_section(psym->section)) { /* Common Global - unplaced */ printf("Sym[%24s] @ sz %04x ", psym->name, (u_int32_t) psym->value); printf("align %04x ", (u_int32_t) ((elf_symbol_type *) psym)->internal_elf_sym.st_value); } else { printf("Sym[%24s] @ %04x align ", psym->name, (u_int32_t) psym->value); printf("sz %04x ", (u_int32_t) ((elf_symbol_type *) psym)->internal_elf_sym.st_size); } printf("%c", psym->flags & BSF_OBJECT ? 'O' : ' '); printf("%c", psym->flags & BSF_DYNAMIC ? 'D' : ' '); printf("%c", psym->flags & BSF_FILE ? 'F' : ' '); printf("%c", psym->flags & BSF_INDIRECT ? 'I' : ' '); printf("%c", psym->flags & BSF_WARNING ? 'W' : ' '); printf("%c", psym->flags & BSF_CONSTRUCTOR ? 'C' : ' '); printf("%c", psym->flags & BSF_NOT_AT_END ? 'N' : ' '); printf("%c", psym->flags & BSF_OLD_COMMON ? 'c' : ' '); printf("%c", psym->flags & BSF_SECTION_SYM ? 'S' : ' '); printf("%c", psym->flags & BSF_WEAK ? 'w' : ' '); printf("%c", psym->flags & BSF_KEEP_G ? 'G' : ' '); printf("%c", psym->flags & BSF_KEEP ? 'K' : ' '); printf("%c", psym->flags & BSF_FUNCTION ? 'f' : ' '); printf("%c", psym->flags & BSF_DEBUGGING ? 'd' : ' '); printf("%c", psym->flags & BSF_GLOBAL ? 'g' : ' '); printf("%c", psym->flags & BSF_LOCAL ? 'l' : ' '); printf("\n"); } /*********************************************************************** * check_symbol_overlap ***********************************************************************/ static void check_symbol_overlap(asymbol ** symbols, int number_of_symbols) { int i; int j; for (i = 0; i < number_of_symbols; i++) { elf_symbol_type *sym_i; bfd_vma base_i; bfd_vma top_i; bfd_vma size_i; sym_i = (elf_symbol_type *) symbols[i]; base_i = sym_i->symbol.section->vma + sym_i->internal_elf_sym.st_value; size_i = sym_i->internal_elf_sym.st_size; if (0 == size_i) { if (sym_i->symbol.section->flags & SEC_CODE) { /* must be an internal branch - ignore */ if (verbose) printf("Sym [%20s] is zero len, skipping!\n", sym_i->symbol.name); continue; } else { /* pointer - fake size up */ size_i = 4; } } top_i = base_i + size_i; if (verbose) { printf("Sym->[%20s] base %08x, top %08x\n", sym_i->symbol.name, (u_int32_t) base_i, (u_int32_t) top_i); } for (j = (i + 1); j < number_of_symbols; j++) { elf_symbol_type *sym_j; bfd_vma base_j; bfd_vma top_j; bfd_vma size_j = 0; sym_j = (elf_symbol_type *) symbols[j]; base_j = sym_j->symbol.section->vma + sym_j->internal_elf_sym.st_value; if (0 == size_j) { if (sym_j->symbol.section->flags & SEC_CODE) { /* must be an internal branch - ignore */ continue; } else { /* pointer - fake size up */ size_j = 4; } } top_j = base_j + sym_j->internal_elf_sym.st_size; if (0 == sym_j->internal_elf_sym.st_size) { continue; } if ((base_j < top_i) && (top_j > base_i)) { /* symbols overlap - bad bad bad bad */ if (verbose) { fprintf(stdout, "symbols [%20s][%6s] and [%20s][%6s] OVERLAP!\n", sym_i->symbol.name, sym_i->symbol.section->name, sym_j->symbol.name, sym_j->symbol.section->name); fprintf(stdout, "Sym->[%20s] base %08x, top %08x\n", sym_i->symbol.name, (u_int32_t) base_i, (u_int32_t) top_i); fprintf(stdout, "Sym->[%20s] base %08x, top %08x\n", sym_j->symbol.name, (u_int32_t) base_j, (u_int32_t) top_j); } } } } } /*********************************************************************** * map_common_symbols ***********************************************************************/ static void map_common_symbols(bfd * input_bfd, asymbol ** symbols, int number_of_symbols) { asection *bss_s; int i; int j; bfd_vma baseaddr; bfd_vma align; bfd_vma size; bfd_vma symbase; bfd_vma offset; bss_s = bss_info.subsect[0]; baseaddr = 0; printf("checking overlap before mapping\n"); check_symbol_overlap(symbols, number_of_symbols); printf("mapping COMMONS\n"); if (NULL == bss_s) { fprintf(stderr, "WARNING: NULL section passed to map_common_symbols\n"); return; } if (verbose) { printf("Assigning COMMON symbols to section %s\n", bss_s->name); } for (i = 0; i < number_of_symbols; i++) { if (bfd_is_com_section(symbols[i]->section)) { if (verbose > 1) { printf("COMMON sym[%04d] -> ", i); dump_symbol(symbols[i]); } /* get parameters of unmapped symbol */ #if 0 align = ((elf_symbol_type *) symbols[i])->internal_elf_sym.st_value; #else /* Ignore alignment - just make sure we're word aligned we're not * worrying about page boundaries since we're flat mem and we're not * really concerned with cache alignment - maybe someday */ align = 0x04; #endif size = ((elf_symbol_type *) symbols[i])->internal_elf_sym.st_size; if (0 == size) { if (verbose) { printf("zero size symbol assumed to be a ptr size = 4\n"); } size = 0x04; } if (size % 0x04) { printf("non-mod4 symbol rounded up 4\n"); size = ((size >> 2) + 1) << 2; } /* INSERT SYMBOL AT END OF BSS - MUCH MO BETTA */ /* calulate transaction effects - insert blank b4 sym to get align */ baseaddr = bss_s->COOKED_SIZE; symbase = ((baseaddr + align - 1) / align) * align; offset = (symbase + size) - baseaddr; if (verbose > 1) { printf(" ba: %08x sb: %08x al: %04x sz: %04x of: %04x\n", (u_int32_t) baseaddr, (u_int32_t) symbase, (u_int32_t) align, (u_int32_t) size, (u_int32_t) offset); } /* Add space to bss segment and section */ bss_info.high_mark += offset; bss_info.size += offset; bss_s->COOKED_SIZE += offset; bss_s->RAW_SIZE += offset; /* find all end markers and offset */ for (j = 0; j < number_of_symbols; j++) { if (bss_s == symbols[j]->section) { if (verbose > 1) { printf("checking endsym? %08x sym[%04d] -> ", (u_int32_t) baseaddr, j); dump_symbol(symbols[j]); } if (symbols[j]->value >= baseaddr) { symbols[j]->value += offset; ((elf_symbol_type *) symbols[j])->internal_elf_sym. st_value += offset; if (verbose > 1) { printf("sym MOVED!!!\n"); printf("to sym[%04d] -> ", j); dump_symbol(symbols[j]); } } } } /* stuff sym at base */ symbols[i]->section = bss_s; symbols[i]->value = symbase; symbols[i]->flags = BSF_OBJECT | BSF_GLOBAL; ((elf_symbol_type *) symbols[i])->internal_elf_sym.st_value = symbase; if (verbose > 1) { printf("NEW sym[%04d] -> ", i); dump_symbol(symbols[i]); } } } check_symbol_overlap(symbols, number_of_symbols); } /*********************************************************************** * output_relocs ***********************************************************************/ static struct nxflat_reloc_s *output_relocs(bfd * input_bfd, asymbol ** symbols, int number_of_symbols, u_int32_t * n_relocs) { struct nxflat_reloc_s *nxflat_relocs; asection *sym_section; arelent **relpp, **p, *q; const char *sym_name, *section_name; u_int32_t nxflat_reloc_count; int relsize, relcount; int reltype; int rc; bfd_vma v, addend; segment_info *relseg, *sym_segment; u_int32_t seg_offset; int i; *n_relocs = 0; nxflat_relocs = NULL; nxflat_reloc_count = 0; rc = 0; #if 1 /* new code */ if (verbose) { printf(" Before mapp high mark %08x cooked %08x raw %08x \n", (u_int32_t) bss_info.high_mark, (u_int32_t) bss_info.subsect[0]->COOKED_SIZE, (u_int32_t) bss_info.subsect[0]->RAW_SIZE); } /* Unmapped 'common' symbols need to be stuffed into bss */ map_common_symbols(input_bfd, symbols, number_of_symbols); if (verbose) { printf(" After map high mark %08x cooked %08x raw %08x \n", (u_int32_t) bss_info.high_mark, (u_int32_t) bss_info.subsect[0]->COOKED_SIZE, (u_int32_t) bss_info.subsect[0]->RAW_SIZE); } if (verbose) { for (i = 0; i < number_of_symbols; i++) { printf("sym[%04d] -> ", i); dump_symbol(symbols[i]); } } /* Stuff the addrs into the code (and data) */ resolve_segment_relocs(input_bfd, &text_info, symbols, &nxflat_reloc_count, &nxflat_relocs); resolve_segment_relocs(input_bfd, &data_info, symbols, &nxflat_reloc_count, &nxflat_relocs); resolve_segment_relocs(input_bfd, &bss_info, symbols, &nxflat_reloc_count, &nxflat_relocs); *n_relocs = nxflat_reloc_count; printf(" returning %d relocs\n", nxflat_reloc_count); /* need to emit relocs for data only */ return nxflat_relocs; #endif relsize = bfd_get_dynamic_reloc_upper_bound(input_bfd); if (relsize <= 0) { if (verbose) printf("no relocation entries\n"); return 0; } relpp = (arelent **) malloc(relsize); relcount = bfd_canonicalize_dynamic_reloc(input_bfd, relpp, symbols); if (relcount <= 0) { if (verbose) printf("no relocation entries\n"); *n_relocs = 0; return NULL; } for (p = relpp; (relcount && (*p != NULL)); p++, relcount--) { q = *p; if (q->sym_ptr_ptr && *q->sym_ptr_ptr) { sym_name = (*(q->sym_ptr_ptr))->name; sym_section = (*(q->sym_ptr_ptr))->section; section_name = (*(q->sym_ptr_ptr))->section->name; if (verbose) { printf("Reloc address %08x type %s\n", (u_int32_t) q->address, q->howto->name); } switch (q->howto->type) { case R_ARM_RELATIVE: /* This reloc describes an offset from the "program base". We * need to figure out which segment it actually refers to, fiddle * the addend and output an appropriate FLAT reloc to be resolved * at run time. */ /* XXX In an ideal world, the linker would output different * relocs for the different sections, so we wouldn't need to grub * around in the memory map quite so much. */ v = q->address; reltype = -1; relseg = sym_segment = NULL; if (v >= text_info.low_mark && v <= text_info.high_mark) { /* The existing FLAT binary format only allows relocs to be * in the .data segment. Relocating in .text would be bad * practice anyway so this is no great loss. */ printf("WARNING: Illegal RELATIVE reloc in text " "segment ignored.\n"); continue; } else if (v >= data_info.low_mark && v <= data_info.high_mark) relseg = &data_info; if (!relseg) { /* This reloc isn't in the text or data segment. Probably it * refers to debug information and we can just ignore it. */ if (verbose) { fprintf(stderr, "WARNING: ignoring reloc at %08x\n", (u_int32_t) v); } continue; } /* Locate the reloc in the appropriate segment and extract the * existing addend. */ seg_offset = v - relseg->low_mark; addend = *((bfd_vma *) (relseg->contents + seg_offset)); if (verbose) { printf("RELATIVE reloc at (%s+%08x) addend %08x\n", relseg->name, seg_offset, (u_int32_t) addend); } /* Locate the address referred to by section type. */ reltype = get_reloc_type(sym_section, &sym_segment); if (!sym_segment) { /* The addend isn't within any of the segments we know about. * This might mean it was in a section we threw away. In any * case, there is nothing we can do about it now. */ printf("ERROR: reloc at (%s+%08x) has out of " " range addend %08x or unrecognized " " section \"%s\"\n", relseg->name, seg_offset, (u_int32_t) addend, sym_section->name); rc = -1; continue; } /* Verify that the addend offset lies within the address range we * have identified for the segment. */ if (addend < sym_segment->low_mark || addend > sym_segment->high_mark) { fprintf(stderr, "ERROR: Symbol addend %x does lie within " "segment address range (%x-%x)\n", (u_int32_t) addend, (u_int32_t) sym_segment->low_mark, (u_int32_t) sym_segment->high_mark); continue; } /* Factor out the starting VMA of the segment, adjust the addend * in core and output a new reloc. */ addend -= sym_segment->low_mark; if (verbose) { printf("(%s+%08x)\n", sym_segment->name, (u_int32_t) addend); } *((bfd_vma *) (relseg->contents + seg_offset)) = addend; nxflat_relocs = realloc(nxflat_relocs, (nxflat_reloc_count + 1) * sizeof(struct nxflat_reloc_s)); nxflat_relocs[nxflat_reloc_count].r_info = NXFLAT_RELOC(reltype, seg_offset); nxflat_reloc_count++; break; case R_ARM_PC24: case R_ARM_GLOB_DAT: /* Because we linked with -Bsymbolic, the linker will already * have resolved all function calls apart from those to undefined * symbols. So long as this is a weak reference, we do nothing * and the reloc will be incomplete - it is up to the application * to avoid executing such branches. Global data relocs are also * OK to leave unresolved since they will yield zero by default. * If we encounter an unresolved strong reference we punt; the * linker should have trapped this case already so this is a * "can't happen" situation. */ if (!((*q->sym_ptr_ptr)->flags & BSF_WEAK)) { printf("ERROR: undefined symbolic reference to %s\n", (*q->sym_ptr_ptr)->name); rc = -1; } break; default: printf("ERROR: unknown reloc type %s\n", q->howto->name); rc = -1; break; } } else { printf("ERROR: undefined relocation entry\n"); rc = -1; continue; } } if (rc < 0) return (NULL); *n_relocs = nxflat_reloc_count; return nxflat_relocs; } /*********************************************************************** * is_unwanted_section ***********************************************************************/ /* Return 1 if this is a section that we want to throw away but can only * identify by name. Normally, any section with appropriate-looking flags * will get copied into the output file. */ static int is_unwanted_section(asection * s) { if (!strcmp(s->name, ".hash")) return 1; if (!strcmp(s->name, ".dynstr") || !strcmp(s->name, ".dynsym")) return 1; if (s->flags & SEC_DEBUGGING) return 1; return 0; } /*********************************************************************** * register_section ***********************************************************************/ /* Mark this section for inclusion in some segment. */ static void register_section(asection * s, segment_info * inf) { if (verbose) printf("registering section %s to %s segment\n", s->name, inf->name); inf->subsect[inf->nsubsects++] = s; } /*********************************************************************** * dump_sections ***********************************************************************/ /* Print out the sections that make up this segment for debugging. */ static void dump_sections(segment_info * inf) { int i; printf(" [ "); for (i = 0; i < inf->nsubsects; i++) printf("%s ", inf->subsect[i]->name); printf("]\n"); } /*********************************************************************** * load_sections ***********************************************************************/ /* Pull the sections that make up this segment in off disk. */ static void load_sections(bfd * bfd, segment_info * inf) { int i; void *ptr; if (!inf->size) return; /* Nothing to do */ inf->contents = malloc(inf->size); if (!inf->contents) { fprintf(stderr, "ERROR: Out of memory.\n"); exit(1); } ptr = inf->contents; for (i = 0; i < inf->nsubsects; i++) { if (!bfd_get_section_contents(bfd, inf->subsect[i], ptr, 0, inf->subsect[i]->COOKED_SIZE)) { fprintf(stderr, "ERROR: Failed to read section contents.\n"); exit(1); } ptr += inf->subsect[i]->COOKED_SIZE; inf->subsect[i]->flags |= SEC_IN_MEMORY; } } /*********************************************************************** * stack_nxflat_segment ***********************************************************************/ static void stack_nxflat_segment(segment_info * inf) { bfd_vma min_addr = 0x7fffffff; bfd_vma max_addr = 0x00000000; int i; for (i = 0; i < inf->nsubsects; i++) { bfd_vma vma = inf->subsect[i]->vma; if (vma < min_addr) min_addr = vma; vma += inf->subsect[i]->COOKED_SIZE; if (vma > max_addr) max_addr = vma; } inf->low_mark = min_addr; inf->high_mark = max_addr; inf->size = max_addr - min_addr; } /*********************************************************************** * show_usage ***********************************************************************/ static void show_usage(void) { fprintf(stderr, "Usage: %s [options] \n\n", program_name); fprintf(stderr, "Where options are one or more of the following. Note\n"); fprintf(stderr, "that a space is always required between the option and\n"); fprintf(stderr, "any following arguments.\n\n"); fprintf(stderr, " -d Use dynamic symbol table [symtab]\n"); fprintf(stderr, " -e \n"); fprintf(stderr, " Entry point to module [%s for executable]\n", default_exe_entry_name); fprintf(stderr, " NULL for shared library\n"); fprintf(stderr, " -o \n"); fprintf(stderr, " Output to [.xflt]\n"); fprintf(stderr, " -s \n"); fprintf(stderr, " Set stack size to . Ignored if -l also\n"); fprintf(stderr, " selected. [%d]\n", DEFAULT_STACK_SIZE); fprintf(stderr, " -v Verbose output [no verbose output]\n"); fprintf(stderr, " If -v is applied twice, additional debug output\n"); fprintf(stderr, " be enabled.\n"); fprintf(stderr, "\n"); exit(2); } /*********************************************************************** * parse_args ***********************************************************************/ static void parse_args(int argc, char **argv) { int opt; /* Save our name (for show_usage) */ program_name = argv[0]; /* Set some default values */ stack_size = 0; entry_name = NULL; if (argc < 2) { fprintf(stderr, "ERROR: Missing required arguments\n\n"); show_usage(); } /* Get miscellaneous options from the command line. */ while ((opt = getopt(argc, argv, "de:lo:s:v")) != -1) { switch (opt) { case 'd': dsyms++; break; case 'e': entry_name = optarg; break; case 'o': out_filename = optarg; break; case 's': stack_size = atoi(optarg); break; case 'v': verbose++; break; case 'l': default: fprintf(stderr, "ERROR: %s Unknown option\n\n", argv[0]); show_usage(); break; } } /* The very last thing is also the name of the BFD input file */ bfd_filename = argv[argc - 1]; /* Verify that an appropriate stack size is selected. */ if (stack_size == 0) { /* Executables must have a stack_size selected. */ printf("Using default stack size: %d\n", DEFAULT_STACK_SIZE); stack_size = DEFAULT_STACK_SIZE; } if (entry_name == NULL) { printf("Using entry_point: %s\n", default_exe_entry_name); entry_name = default_exe_entry_name; } } /*********************************************************************** * Public Functions ***********************************************************************/ /*********************************************************************** * main ***********************************************************************/ int main(int argc, char **argv, char **envp) { int fd; bfd *bf; asection *s; asymbol **symbol_table; int32_t number_of_symbols; u_int32_t reloc_len; u_int32_t *reloc; struct nxflat_hdr_s hdr; /* Parse the incoming command line */ parse_args(argc, argv); /* Open the BFD input file */ if (!(bf = bfd_openr(argv[argc - 1], 0))) { fprintf(stderr, "ERROR: Failed to open %s\n", argv[argc - 1]); exit(1); } /* Verify the format of the BFD file */ if (bfd_check_format(bf, bfd_object) == 0) { printf("File is not an object file\n"); exit(2); } /* Read the symbol table from the file */ symbol_table = get_symbols(bf, &number_of_symbols); /* Find all of the special symbols that we will need in the symbol table that * we just read. */ find_special_symbols(); /* Walk the list of sections, figuring out where each one goes and how much * storage it requires. */ text_info.low_mark = data_info.low_mark = bss_info.low_mark = -1; text_info.high_mark = data_info.high_mark = bss_info.high_mark = 0; text_info.contents = data_info.contents = bss_info.contents = NULL; text_info.size = data_info.size = bss_info.size = 0; text_info.nsubsects = data_info.nsubsects = bss_info.nsubsects = 0; text_info.name = "text"; data_info.name = "data"; bss_info.name = "bss"; for (s = bf->sections; s != NULL; s = s->next) { if (verbose) printf("reading section %s\n", s->name); /* ignore blatantly useless sections */ if (is_unwanted_section(s)) continue; if (s->flags == SEC_ALLOC) { if (verbose) { printf("Section %s is ALLOC only\n", s->name); } register_section(s, &bss_info); continue; } if ((s->flags & SEC_CODE) && (s->flags & SEC_ALLOC)) { if (verbose) { printf("Section %s is CODE\n", s->name); } register_section(s, &text_info); continue; } if ((s->flags & SEC_DATA) && (s->flags & SEC_ALLOC)) { if (verbose) { printf("section %s is DATA\n", s->name); } register_section(s, &data_info); continue; } if (verbose) printf("WARNING: ignoring section %s\n", s->name); } /* Fixup high and low water VMA address */ stack_nxflat_segment(&text_info); stack_nxflat_segment(&data_info); stack_nxflat_segment(&bss_info); /* Check for a data offset due to the presence of a GOT */ printf("INPUT SECTIONS:\n"); printf("SECT LOW MARK HIGH MARK (SIZE BYTES)\n"); if (text_info.nsubsects == 0) { printf("TEXT Not found Not found ( Not found )\n"); } else { printf("TEXT %08x %08x (%08lx)\n", (u_int32_t) text_info.low_mark, (u_int32_t) text_info.high_mark, text_info.size); if (text_info.low_mark != 0) { fprintf(stderr, "ERROR: text section must be origined at zero"); exit(1); } } if (data_info.nsubsects == 0) { printf("DATA Not found Not found ( Not found )\n"); } else { printf("DATA %08x %08x (%08lx)\n", (u_int32_t) data_info.low_mark, (u_int32_t) data_info.high_mark, data_info.size); if (data_info.low_mark != 0) { fprintf(stderr, "ERROR: data section must be origined at zero"); exit(1); } } if (bss_info.nsubsects == 0) { printf("BSS Not found Not found ( Not found )\n"); } else { printf("BSS %08x %08x (%08lx)\n", (u_int32_t) bss_info.low_mark, (u_int32_t) bss_info.high_mark, bss_info.size); /* If data is present, then BSS was be origined immediately after the * data. */ if (data_info.nsubsects > 0) { /* There is data... Account for possible ALIGN 0x10 at end of data */ u_int32_t bss_start1 = data_info.high_mark; u_int32_t bss_start2 = ((bss_start1 + 0x0f) & ~0x0f); if ((bss_info.low_mark < bss_start1) && (bss_info.low_mark > bss_start2)) { fprintf(stderr, "ERROR: bss must be origined immediately after " "the data section\n"); exit(1); } } /* If there is no data, then the BSS must be origined at zero */ else if (bss_info.low_mark != 0) { fprintf(stderr, "ERROR: bss section (with no data section) must be"); fprintf(stderr, " origined at zero\n"); exit(1); } } if (verbose) { printf("TEXT:"); dump_sections(&text_info); printf("DATA:"); dump_sections(&data_info); printf("BSS: "); dump_sections(&bss_info); } /* Slurp the section contents in. No need to load BSS since we know * it * isn't initialised. */ load_sections(bf, &text_info); load_sections(bf, &data_info); reloc = (u_int32_t *) output_relocs(bf, symbol_table, number_of_symbols, &reloc_len); /* Fill in the xFLT file header */ memcpy(hdr.h_magic, "xFLT", 4); put_xflat32(&hdr.h_datastart, NXFLAT_HDR_SIZE + text_info.size); put_xflat32(&hdr.h_dataend, NXFLAT_HDR_SIZE + text_info.size + data_info.size); put_xflat32(&hdr.h_bssend, NXFLAT_HDR_SIZE + text_info.size + data_info.size + bss_info.size); put_xflat32(&hdr.h_stacksize, stack_size); /* FIXME */ put_xflat32(&hdr.h_relocstart, NXFLAT_HDR_SIZE + text_info.size + data_info.size); put_xflat32(&hdr.h_reloccount, reloc_len); put_entry_point(&hdr); put_special_symbol(dynimport_begin_symbol, dynimport_end_symbol, &hdr.h_importsymbols, &hdr.h_importcount, sizeof(struct nxflat_import_s), text_info.size); #ifdef RELOCS_IN_NETWORK_ORDER { int i; for (i = 0; i < reloc_len; i++) { reloc[i] = htonl(reloc[i]); } } #endif if (verbose && reloc) { printf("reloc size: %04x\n", reloc_len); } if (!out_filename) { out_filename = malloc(strlen(bfd_filename) + 5); /* 5 to add suffix */ strcpy(out_filename, bfd_filename); strcat(out_filename, ".xflt"); } fd = open(out_filename, O_WRONLY | O_PLATFORM | O_CREAT | O_TRUNC, 0744); if (fd < 0) { fprintf(stderr, "ERROR: Failed open output file %s\n", out_filename); exit(4); } write(fd, &hdr, sizeof(hdr)); write(fd, text_info.contents, text_info.size); write(fd, data_info.contents, data_info.size); if (reloc) { write(fd, reloc, reloc_len * 4); } close(fd); exit(0); }