/* PEF support for BFD. Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. 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. */ #include "safe-ctype.h" #include "pef.h" #include "pef-traceback.h" #include "bfd.h" #include "sysdep.h" #include "libbfd.h" #include "libiberty.h" #ifndef BFD_IO_FUNCS #define BFD_IO_FUNCS 0 #endif #define bfd_pef_close_and_cleanup _bfd_generic_close_and_cleanup #define bfd_pef_bfd_free_cached_info _bfd_generic_bfd_free_cached_info #define bfd_pef_new_section_hook _bfd_generic_new_section_hook #define bfd_pef_bfd_is_local_label_name bfd_generic_is_local_label_name #define bfd_pef_get_lineno _bfd_nosymbols_get_lineno #define bfd_pef_find_nearest_line _bfd_nosymbols_find_nearest_line #define bfd_pef_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol #define bfd_pef_read_minisymbols _bfd_generic_read_minisymbols #define bfd_pef_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol #define bfd_pef_get_reloc_upper_bound _bfd_norelocs_get_reloc_upper_bound #define bfd_pef_canonicalize_reloc _bfd_norelocs_canonicalize_reloc #define bfd_pef_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup #define bfd_pef_set_arch_mach _bfd_generic_set_arch_mach #define bfd_pef_get_section_contents _bfd_generic_get_section_contents #define bfd_pef_set_section_contents _bfd_generic_set_section_contents #define bfd_pef_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents #define bfd_pef_bfd_relax_section bfd_generic_relax_section #define bfd_pef_bfd_gc_sections bfd_generic_gc_sections #define bfd_pef_bfd_merge_sections bfd_generic_merge_sections #define bfd_pef_bfd_discard_group bfd_generic_discard_group #define bfd_pef_bfd_link_hash_table_create _bfd_generic_link_hash_table_create #define bfd_pef_bfd_link_hash_table_free _bfd_generic_link_hash_table_free #define bfd_pef_bfd_link_add_symbols _bfd_generic_link_add_symbols #define bfd_pef_bfd_link_just_syms _bfd_generic_link_just_syms #define bfd_pef_bfd_final_link _bfd_generic_final_link #define bfd_pef_bfd_link_split_section _bfd_generic_link_split_section #define bfd_pef_get_section_contents_in_window _bfd_generic_get_section_contents_in_window static void bfd_pef_print_symbol PARAMS ((bfd *, PTR, asymbol *, bfd_print_symbol_type)); static void bfd_pef_convert_architecture PARAMS ((unsigned long, enum bfd_architecture *, unsigned long *)); static bfd_boolean bfd_pef_mkobject PARAMS ((bfd *)); static int bfd_pef_parse_traceback_table PARAMS ((bfd *, asection *, unsigned char *, size_t, size_t, asymbol *, FILE *)); static const char *bfd_pef_section_name PARAMS ((bfd_pef_section *)); static unsigned long bfd_pef_section_flags PARAMS ((bfd_pef_section *)); static asection *bfd_pef_make_bfd_section PARAMS ((bfd *, bfd_pef_section *)); static int bfd_pef_read_header PARAMS ((bfd *, bfd_pef_header *)); static const bfd_target *bfd_pef_object_p PARAMS ((bfd *)); static int bfd_pef_parse_traceback_tables PARAMS ((bfd *, asection *, unsigned char *, size_t, long *, asymbol **)); static int bfd_pef_parse_function_stub PARAMS ((bfd *, unsigned char *, size_t, unsigned long *)); static int bfd_pef_parse_function_stubs PARAMS ((bfd *, asection *, unsigned char *, size_t, unsigned char *, size_t, unsigned long *, asymbol **)); static long bfd_pef_parse_symbols PARAMS ((bfd *, asymbol **)); static long bfd_pef_count_symbols PARAMS ((bfd *)); static long bfd_pef_get_symtab_upper_bound PARAMS ((bfd *)); static long bfd_pef_get_symtab PARAMS ((bfd *, asymbol **)); static asymbol *bfd_pef_make_empty_symbol PARAMS ((bfd *)); static void bfd_pef_get_symbol_info PARAMS ((bfd *, asymbol *, symbol_info *)); static int bfd_pef_sizeof_headers PARAMS ((bfd *, bfd_boolean)); static int bfd_pef_xlib_read_header PARAMS ((bfd *, bfd_pef_xlib_header *)); static int bfd_pef_xlib_scan PARAMS ((bfd *, bfd_pef_xlib_header *)); static const bfd_target *bfd_pef_xlib_object_p PARAMS ((bfd *)); static void bfd_pef_print_symbol (abfd, afile, symbol, how) bfd *abfd; PTR afile; asymbol *symbol; bfd_print_symbol_type how; { FILE *file = (FILE *) afile; switch (how) { case bfd_print_symbol_name: fprintf (file, "%s", symbol->name); break; default: bfd_print_symbol_vandf (abfd, (PTR) file, symbol); fprintf (file, " %-5s %s", symbol->section->name, symbol->name); if (strncmp (symbol->name, "__traceback_", strlen ("__traceback_")) == 0) { char *buf = alloca (symbol->udata.i); size_t offset = symbol->value + 4; size_t len = symbol->udata.i; int ret; bfd_get_section_contents (abfd, symbol->section, buf, offset, len); ret = bfd_pef_parse_traceback_table (abfd, symbol->section, buf, len, 0, NULL, file); if (ret < 0) fprintf (file, " [ERROR]"); } } } static void bfd_pef_convert_architecture (architecture, type, subtype) unsigned long architecture; enum bfd_architecture *type; unsigned long *subtype; { const unsigned long ARCH_POWERPC = 0x70777063; /* 'pwpc' */ const unsigned long ARCH_M68K = 0x6d36386b; /* 'm68k' */ *subtype = bfd_arch_unknown; *type = bfd_arch_unknown; if (architecture == ARCH_POWERPC) *type = bfd_arch_powerpc; else if (architecture == ARCH_M68K) *type = bfd_arch_m68k; } static bfd_boolean bfd_pef_mkobject (abfd) bfd *abfd ATTRIBUTE_UNUSED; { return TRUE; } static int bfd_pef_parse_traceback_table (abfd, section, buf, len, pos, sym, file) bfd *abfd; asection *section; unsigned char *buf; size_t len; size_t pos; asymbol *sym; FILE *file; { struct traceback_table table; size_t offset; const char *s; asymbol tmpsymbol; if (sym == NULL) sym = &tmpsymbol; sym->name = NULL; sym->value = 0; sym->the_bfd = abfd; sym->section = section; sym->flags = 0; sym->udata.i = 0; /* memcpy is fine since all fields are unsigned char. */ if ((pos + 8) > len) return -1; memcpy (&table, buf + pos, 8); /* Calling code relies on returned symbols having a name and correct offset. */ if ((table.lang != TB_C) && (table.lang != TB_CPLUSPLUS)) return -1; if (! (table.flags2 & TB_NAME_PRESENT)) return -1; if (! table.flags1 & TB_HAS_TBOFF) return -1; offset = 8; if ((table.flags5 & TB_FLOATPARAMS) || (table.fixedparams)) offset += 4; if (table.flags1 & TB_HAS_TBOFF) { struct traceback_table_tboff off; if ((pos + offset + 4) > len) return -1; off.tb_offset = bfd_getb32 (buf + pos + offset); offset += 4; /* Need to subtract 4 because the offset includes the 0x0L preceding the table. */ if (file != NULL) fprintf (file, " [offset = 0x%lx]", off.tb_offset); if ((file == NULL) && ((off.tb_offset + 4) > (pos + offset))) return -1; sym->value = pos - off.tb_offset - 4; } if (table.flags2 & TB_INT_HNDL) offset += 4; if (table.flags1 & TB_HAS_CTL) { struct traceback_table_anchors anchors; if ((pos + offset + 4) > len) return -1; anchors.ctl_info = bfd_getb32 (buf + pos + offset); offset += 4; if (anchors.ctl_info > 1024) return -1; offset += anchors.ctl_info * 4; } if (table.flags2 & TB_NAME_PRESENT) { struct traceback_table_routine name; char *namebuf; if ((pos + offset + 2) > len) return -1; name.name_len = bfd_getb16 (buf + pos + offset); offset += 2; if (name.name_len > 4096) return -1; if ((pos + offset + name.name_len) > len) return -1; namebuf = (char *) bfd_alloc (abfd, name.name_len + 1); if (namebuf == NULL) return -1; memcpy (namebuf, buf + pos + offset, name.name_len); namebuf[name.name_len] = '\0'; /* Strip leading period inserted by compiler. */ if (namebuf[0] == '.') memmove (namebuf, namebuf + 1, name.name_len + 1); sym->name = namebuf; for (s = sym->name; (*s != '\0'); s++) if (! ISPRINT (*s)) return -1; offset += name.name_len; } if (table.flags2 & TB_USES_ALLOCA) offset += 4; if (table.flags4 & TB_HAS_VEC_INFO) offset += 4; if (file != NULL) fprintf (file, " [length = 0x%lx]", (long) offset); return offset; } static const char *bfd_pef_section_name (section) bfd_pef_section *section; { switch (section->section_kind) { case BFD_PEF_SECTION_CODE: return "code"; case BFD_PEF_SECTION_UNPACKED_DATA: return "unpacked-data"; case BFD_PEF_SECTION_PACKED_DATA: return "packed-data"; case BFD_PEF_SECTION_CONSTANT: return "constant"; case BFD_PEF_SECTION_LOADER: return "loader"; case BFD_PEF_SECTION_DEBUG: return "debug"; case BFD_PEF_SECTION_EXEC_DATA: return "exec-data"; case BFD_PEF_SECTION_EXCEPTION: return "exception"; case BFD_PEF_SECTION_TRACEBACK: return "traceback"; default: return "unknown"; } } static unsigned long bfd_pef_section_flags (section) bfd_pef_section *section; { switch (section->section_kind) { case BFD_PEF_SECTION_CODE: return SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC | SEC_CODE; case BFD_PEF_SECTION_UNPACKED_DATA: case BFD_PEF_SECTION_PACKED_DATA: case BFD_PEF_SECTION_CONSTANT: case BFD_PEF_SECTION_LOADER: case BFD_PEF_SECTION_DEBUG: case BFD_PEF_SECTION_EXEC_DATA: case BFD_PEF_SECTION_EXCEPTION: case BFD_PEF_SECTION_TRACEBACK: default: return SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC; } } static asection * bfd_pef_make_bfd_section (abfd, section) bfd *abfd; bfd_pef_section *section; { asection *bfdsec; const char *name = bfd_pef_section_name (section); bfdsec = bfd_make_section_anyway (abfd, name); if (bfdsec == NULL) return NULL; bfdsec->vma = section->default_address + section->container_offset; bfdsec->lma = section->default_address + section->container_offset; bfdsec->_raw_size = section->container_length; bfdsec->filepos = section->container_offset; bfdsec->alignment_power = section->alignment; bfdsec->flags = bfd_pef_section_flags (section); return bfdsec; } int bfd_pef_parse_loader_header (abfd, buf, len, header) bfd *abfd ATTRIBUTE_UNUSED; unsigned char *buf; size_t len; bfd_pef_loader_header *header; { BFD_ASSERT (len == 56); header->main_section = bfd_getb32 (buf); header->main_offset = bfd_getb32 (buf + 4); header->init_section = bfd_getb32 (buf + 8); header->init_offset = bfd_getb32 (buf + 12); header->term_section = bfd_getb32 (buf + 16); header->term_offset = bfd_getb32 (buf + 20); header->imported_library_count = bfd_getb32 (buf + 24); header->total_imported_symbol_count = bfd_getb32 (buf + 28); header->reloc_section_count = bfd_getb32 (buf + 32); header->reloc_instr_offset = bfd_getb32 (buf + 36); header->loader_strings_offset = bfd_getb32 (buf + 40); header->export_hash_offset = bfd_getb32 (buf + 44); header->export_hash_table_power = bfd_getb32 (buf + 48); header->exported_symbol_count = bfd_getb32 (buf + 52); return 0; } int bfd_pef_parse_imported_library (abfd, buf, len, header) bfd *abfd ATTRIBUTE_UNUSED; unsigned char *buf; size_t len; bfd_pef_imported_library *header; { BFD_ASSERT (len == 24); header->name_offset = bfd_getb32 (buf); header->old_implementation_version = bfd_getb32 (buf + 4); header->current_version = bfd_getb32 (buf + 8); header->imported_symbol_count = bfd_getb32 (buf + 12); header->first_imported_symbol = bfd_getb32 (buf + 16); header->options = buf[20]; header->reserved_a = buf[21]; header->reserved_b = bfd_getb16 (buf + 22); return 0; } int bfd_pef_parse_imported_symbol (abfd, buf, len, symbol) bfd *abfd ATTRIBUTE_UNUSED; unsigned char *buf; size_t len; bfd_pef_imported_symbol *symbol; { unsigned long value; BFD_ASSERT (len == 4); value = bfd_getb32 (buf); symbol->class = value >> 24; symbol->name = value & 0x00ffffff; return 0; } int bfd_pef_scan_section (abfd, section) bfd *abfd; bfd_pef_section *section; { unsigned char buf[28]; bfd_seek (abfd, section->header_offset, SEEK_SET); if (bfd_bread ((PTR) buf, 28, abfd) != 28) return -1; section->name_offset = bfd_h_get_32 (abfd, buf); section->default_address = bfd_h_get_32 (abfd, buf + 4); section->total_length = bfd_h_get_32 (abfd, buf + 8); section->unpacked_length = bfd_h_get_32 (abfd, buf + 12); section->container_length = bfd_h_get_32 (abfd, buf + 16); section->container_offset = bfd_h_get_32 (abfd, buf + 20); section->section_kind = buf[24]; section->share_kind = buf[25]; section->alignment = buf[26]; section->reserved = buf[27]; section->bfd_section = bfd_pef_make_bfd_section (abfd, section); if (section->bfd_section == NULL) return -1; return 0; } void bfd_pef_print_loader_header (abfd, header, file) bfd *abfd ATTRIBUTE_UNUSED; bfd_pef_loader_header *header; FILE *file; { fprintf (file, "main_section: %ld\n", header->main_section); fprintf (file, "main_offset: %lu\n", header->main_offset); fprintf (file, "init_section: %ld\n", header->init_section); fprintf (file, "init_offset: %lu\n", header->init_offset); fprintf (file, "term_section: %ld\n", header->term_section); fprintf (file, "term_offset: %lu\n", header->term_offset); fprintf (file, "imported_library_count: %lu\n", header->imported_library_count); fprintf (file, "total_imported_symbol_count: %lu\n", header->total_imported_symbol_count); fprintf (file, "reloc_section_count: %lu\n", header->reloc_section_count); fprintf (file, "reloc_instr_offset: %lu\n", header->reloc_instr_offset); fprintf (file, "loader_strings_offset: %lu\n", header->loader_strings_offset); fprintf (file, "export_hash_offset: %lu\n", header->export_hash_offset); fprintf (file, "export_hash_table_power: %lu\n", header->export_hash_table_power); fprintf (file, "exported_symbol_count: %lu\n", header->exported_symbol_count); } int bfd_pef_print_loader_section (abfd, file) bfd *abfd; FILE *file; { bfd_pef_loader_header header; asection *loadersec = NULL; unsigned char *loaderbuf = NULL; size_t loaderlen = 0; int ret; loadersec = bfd_get_section_by_name (abfd, "loader"); if (loadersec == NULL) return -1; loaderlen = bfd_section_size (abfd, loadersec); loaderbuf = (unsigned char *) bfd_malloc (loaderlen); if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0) { free (loaderbuf); return -1; } if (bfd_bread ((PTR) loaderbuf, loaderlen, abfd) != loaderlen) { free (loaderbuf); return -1; } if (loaderlen < 56) { free (loaderbuf); return -1; } ret = bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header); if (ret < 0) { free (loaderbuf); return -1; } bfd_pef_print_loader_header (abfd, &header, file); return 0; } int bfd_pef_scan_start_address (abfd) bfd *abfd; { bfd_pef_loader_header header; asection *section; asection *loadersec = NULL; unsigned char *loaderbuf = NULL; size_t loaderlen = 0; int ret; loadersec = bfd_get_section_by_name (abfd, "loader"); if (loadersec == NULL) goto end; loaderlen = bfd_section_size (abfd, loadersec); loaderbuf = (unsigned char *) bfd_malloc (loaderlen); if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0) goto error; if (bfd_bread ((PTR) loaderbuf, loaderlen, abfd) != loaderlen) goto error; if (loaderlen < 56) goto error; ret = bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header); if (ret < 0) goto error; if (header.main_section < 0) goto end; for (section = abfd->sections; section != NULL; section = section->next) if ((section->index + 1) == header.main_section) break; if (section == NULL) goto error; abfd->start_address = section->vma + header.main_offset; end: if (loaderbuf != NULL) free (loaderbuf); return 0; error: if (loaderbuf != NULL) free (loaderbuf); return -1; } int bfd_pef_scan (abfd, header, mdata) bfd *abfd; bfd_pef_header *header; bfd_pef_data_struct *mdata; { unsigned int i; enum bfd_architecture cputype; unsigned long cpusubtype; mdata->header = *header; bfd_pef_convert_architecture (header->architecture, &cputype, &cpusubtype); if (cputype == bfd_arch_unknown) { fprintf (stderr, "bfd_pef_scan: unknown architecture 0x%lx\n", header->architecture); return -1; } bfd_set_arch_mach (abfd, cputype, cpusubtype); mdata->header = *header; abfd->flags = (abfd->xvec->object_flags | (abfd->flags & (BFD_IN_MEMORY | BFD_IO_FUNCS))); if (header->section_count != 0) { mdata->sections = ((bfd_pef_section *) bfd_alloc (abfd, header->section_count * sizeof (bfd_pef_section))); if (mdata->sections == NULL) return -1; for (i = 0; i < header->section_count; i++) { bfd_pef_section *cur = &mdata->sections[i]; cur->header_offset = 40 + (i * 28); if (bfd_pef_scan_section (abfd, cur) < 0) return -1; } } if (bfd_pef_scan_start_address (abfd) < 0) { #if 0 fprintf (stderr, "bfd_pef_scan: unable to scan start address: %s\n", bfd_errmsg (bfd_get_error ())); return -1; #endif } abfd->tdata.pef_data = mdata; return 0; } static int bfd_pef_read_header (abfd, header) bfd *abfd; bfd_pef_header *header; { unsigned char buf[40]; bfd_seek (abfd, 0, SEEK_SET); if (bfd_bread ((PTR) buf, 40, abfd) != 40) return -1; header->tag1 = bfd_getb32 (buf); header->tag2 = bfd_getb32 (buf + 4); header->architecture = bfd_getb32 (buf + 8); header->format_version = bfd_getb32 (buf + 12); header->timestamp = bfd_getb32 (buf + 16); header->old_definition_version = bfd_getb32 (buf + 20); header->old_implementation_version = bfd_getb32 (buf + 24); header->current_version = bfd_getb32 (buf + 28); header->section_count = bfd_getb32 (buf + 32) + 1; header->instantiated_section_count = bfd_getb32 (buf + 34); header->reserved = bfd_getb32 (buf + 36); return 0; } static const bfd_target * bfd_pef_object_p (abfd) bfd *abfd; { struct bfd_preserve preserve; bfd_pef_header header; preserve.marker = NULL; if (bfd_pef_read_header (abfd, &header) != 0) goto wrong; if (header.tag1 != BFD_PEF_TAG1 || header.tag2 != BFD_PEF_TAG2) goto wrong; preserve.marker = bfd_zalloc (abfd, sizeof (bfd_pef_data_struct)); if (preserve.marker == NULL || !bfd_preserve_save (abfd, &preserve)) goto fail; if (bfd_pef_scan (abfd, &header, (bfd_pef_data_struct *) preserve.marker) != 0) goto wrong; bfd_preserve_finish (abfd, &preserve); return abfd->xvec; wrong: bfd_set_error (bfd_error_wrong_format); fail: if (preserve.marker != NULL) bfd_preserve_restore (abfd, &preserve); return NULL; } static int bfd_pef_parse_traceback_tables (abfd, sec, buf, len, nsym, csym) bfd *abfd; asection *sec; unsigned char *buf; size_t len; long *nsym; asymbol **csym; { char *name; asymbol function; asymbol traceback; const char *const tbprefix = "__traceback_"; size_t tbnamelen; size_t pos = 0; unsigned long count = 0; int ret; for (;;) { /* We're reading symbols two at a time. */ if (csym && ((csym[count] == NULL) || (csym[count + 1] == NULL))) break; pos += 3; pos -= (pos % 4); while ((pos + 4) <= len) { if (bfd_getb32 (buf + pos) == 0) break; pos += 4; } if ((pos + 4) > len) break; ret = bfd_pef_parse_traceback_table (abfd, sec, buf, len, pos + 4, &function, 0); if (ret < 0) { /* Skip over 0x0L to advance to next possible traceback table. */ pos += 4; continue; } BFD_ASSERT (function.name != NULL); /* Don't bother to compute the name if we are just counting symbols. */ if (csym) { tbnamelen = strlen (tbprefix) + strlen (function.name); name = bfd_alloc (abfd, tbnamelen + 1); if (name == NULL) { bfd_release (abfd, (PTR) function.name); function.name = NULL; break; } snprintf (name, tbnamelen + 1, "%s%s", tbprefix, function.name); traceback.name = name; traceback.value = pos; traceback.the_bfd = abfd; traceback.section = sec; traceback.flags = 0; traceback.udata.i = ret; *(csym[count]) = function; *(csym[count + 1]) = traceback; } pos += ret; count += 2; } *nsym = count; return 0; } static int bfd_pef_parse_function_stub (abfd, buf, len, offset) bfd *abfd ATTRIBUTE_UNUSED; unsigned char *buf; size_t len; unsigned long *offset; { BFD_ASSERT (len == 24); if ((bfd_getb32 (buf) & 0xffff0000) != 0x81820000) return -1; if (bfd_getb32 (buf + 4) != 0x90410014) return -1; if (bfd_getb32 (buf + 8) != 0x800c0000) return -1; if (bfd_getb32 (buf + 12) != 0x804c0004) return -1; if (bfd_getb32 (buf + 16) != 0x7c0903a6) return -1; if (bfd_getb32 (buf + 20) != 0x4e800420) return -1; if (offset != NULL) *offset = (bfd_getb32 (buf) & 0x0000ffff) / 4; return 0; } static int bfd_pef_parse_function_stubs (abfd, codesec, codebuf, codelen, loaderbuf, loaderlen, nsym, csym) bfd *abfd; asection *codesec; unsigned char *codebuf; size_t codelen; unsigned char *loaderbuf; size_t loaderlen; unsigned long *nsym; asymbol **csym; { const char *const sprefix = "__stub_"; size_t codepos = 0; unsigned long count = 0; bfd_pef_loader_header header; bfd_pef_imported_library *libraries = NULL; bfd_pef_imported_symbol *imports = NULL; unsigned long i; int ret; if (loaderlen < 56) goto error; ret = bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header); if (ret < 0) goto error; libraries = (bfd_pef_imported_library *) bfd_malloc (header.imported_library_count * sizeof (bfd_pef_imported_library)); imports = (bfd_pef_imported_symbol *) bfd_malloc (header.total_imported_symbol_count * sizeof (bfd_pef_imported_symbol)); if (loaderlen < (56 + (header.imported_library_count * 24))) goto error; for (i = 0; i < header.imported_library_count; i++) { ret = bfd_pef_parse_imported_library (abfd, loaderbuf + 56 + (i * 24), 24, &libraries[i]); if (ret < 0) goto error; } if (loaderlen < (56 + (header.imported_library_count * 24) + (header.total_imported_symbol_count * 4))) goto error; for (i = 0; i < header.total_imported_symbol_count; i++) { ret = (bfd_pef_parse_imported_symbol (abfd, loaderbuf + 56 + (header.imported_library_count * 24) + (i * 4), 4, &imports[i])); if (ret < 0) goto error; } codepos = 0; for (;;) { asymbol sym; const char *symname; char *name; unsigned long index; int ret; if (csym && (csym[count] == NULL)) break; codepos += 3; codepos -= (codepos % 4); while ((codepos + 4) <= codelen) { if ((bfd_getb32 (codebuf + codepos) & 0xffff0000) == 0x81820000) break; codepos += 4; } if ((codepos + 4) > codelen) break; ret = bfd_pef_parse_function_stub (abfd, codebuf + codepos, 24, &index); if (ret < 0) { codepos += 24; continue; } if (index >= header.total_imported_symbol_count) { codepos += 24; continue; } { size_t max, namelen; const char *s; if (loaderlen < (header.loader_strings_offset + imports[index].name)) goto error; max = loaderlen - (header.loader_strings_offset + imports[index].name); symname = loaderbuf + header.loader_strings_offset + imports[index].name; namelen = 0; for (s = symname; s < (symname + max); s++) { if (*s == '\0') break; if (! ISPRINT (*s)) goto error; namelen++; } if (*s != '\0') goto error; name = bfd_alloc (abfd, strlen (sprefix) + namelen + 1); if (name == NULL) break; snprintf (name, strlen (sprefix) + namelen + 1, "%s%s", sprefix, symname); sym.name = name; } sym.value = codepos; sym.the_bfd = abfd; sym.section = codesec; sym.flags = 0; sym.udata.i = 0; codepos += 24; if (csym != NULL) *(csym[count]) = sym; count++; } goto end; end: if (libraries != NULL) free (libraries); if (imports != NULL) free (imports); *nsym = count; return 0; error: if (libraries != NULL) free (libraries); if (imports != NULL) free (imports); *nsym = count; return -1; } static long bfd_pef_parse_symbols (abfd, csym) bfd *abfd; asymbol **csym; { unsigned long count = 0; asection *codesec = NULL; unsigned char *codebuf = NULL; size_t codelen = 0; asection *loadersec = NULL; unsigned char *loaderbuf = NULL; size_t loaderlen = 0; codesec = bfd_get_section_by_name (abfd, "code"); if (codesec != NULL) { codelen = bfd_section_size (abfd, codesec); codebuf = (unsigned char *) bfd_malloc (codelen); if (bfd_seek (abfd, codesec->filepos, SEEK_SET) < 0) goto end; if (bfd_bread ((PTR) codebuf, codelen, abfd) != codelen) goto end; } loadersec = bfd_get_section_by_name (abfd, "loader"); if (loadersec != NULL) { loaderlen = bfd_section_size (abfd, loadersec); loaderbuf = (unsigned char *) bfd_malloc (loaderlen); if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0) goto end; if (bfd_bread ((PTR) loaderbuf, loaderlen, abfd) != loaderlen) goto end; } count = 0; if (codesec != NULL) { unsigned long ncount = 0; bfd_pef_parse_traceback_tables (abfd, codesec, codebuf, codelen, &ncount, csym); count += ncount; } if ((codesec != NULL) && (loadersec != NULL)) { unsigned long ncount = 0; bfd_pef_parse_function_stubs (abfd, codesec, codebuf, codelen, loaderbuf, loaderlen, &ncount, (csym != NULL) ? (csym + count) : NULL); count += ncount; } if (csym != NULL) csym[count] = NULL; end: if (codebuf != NULL) free (codebuf); if (loaderbuf != NULL) free (loaderbuf); return count; } static long bfd_pef_count_symbols (abfd) bfd *abfd; { return bfd_pef_parse_symbols (abfd, NULL); } static long bfd_pef_get_symtab_upper_bound (abfd) bfd *abfd; { long nsyms = bfd_pef_count_symbols (abfd); if (nsyms < 0) return nsyms; return ((nsyms + 1) * sizeof (asymbol *)); } static long bfd_pef_get_symtab (abfd, alocation) bfd *abfd; asymbol **alocation; { long i; asymbol *syms; long ret; long nsyms = bfd_pef_count_symbols (abfd); if (nsyms < 0) return nsyms; syms = bfd_alloc (abfd, nsyms * sizeof (asymbol)); if (syms == NULL) return -1; for (i = 0; i < nsyms; i++) alocation[i] = &syms[i]; alocation[nsyms] = NULL; ret = bfd_pef_parse_symbols (abfd, alocation); if (ret != nsyms) return 0; return ret; } static asymbol * bfd_pef_make_empty_symbol (abfd) bfd *abfd; { return (asymbol *) bfd_alloc (abfd, sizeof (asymbol)); } static void bfd_pef_get_symbol_info (abfd, symbol, ret) bfd *abfd ATTRIBUTE_UNUSED; asymbol *symbol; symbol_info *ret; { bfd_symbol_info (symbol, ret); } static int bfd_pef_sizeof_headers (abfd, exec) bfd *abfd ATTRIBUTE_UNUSED; bfd_boolean exec ATTRIBUTE_UNUSED; { return 0; } const bfd_target pef_vec = { "pef", /* name */ bfd_target_pef_flavour, /* flavour */ BFD_ENDIAN_BIG, /* byteorder */ BFD_ENDIAN_BIG, /* header_byteorder */ (HAS_RELOC | EXEC_P | /* object flags */ HAS_LINENO | HAS_DEBUG | HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */ 0, /* symbol_leading_char */ ' ', /* ar_pad_char */ 16, /* ar_max_namelen */ bfd_getb64, bfd_getb_signed_64, bfd_putb64, bfd_getb32, bfd_getb_signed_32, bfd_putb32, bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */ bfd_getb64, bfd_getb_signed_64, bfd_putb64, bfd_getb32, bfd_getb_signed_32, bfd_putb32, bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */ { /* bfd_check_format */ _bfd_dummy_target, bfd_pef_object_p, /* bfd_check_format */ _bfd_dummy_target, _bfd_dummy_target, }, { /* bfd_set_format */ bfd_false, bfd_pef_mkobject, bfd_false, bfd_false, }, { /* bfd_write_contents */ bfd_false, bfd_true, bfd_false, bfd_false, }, BFD_JUMP_TABLE_GENERIC (bfd_pef), BFD_JUMP_TABLE_COPY (_bfd_generic), BFD_JUMP_TABLE_CORE (_bfd_nocore), BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), BFD_JUMP_TABLE_SYMBOLS (bfd_pef), BFD_JUMP_TABLE_RELOCS (bfd_pef), BFD_JUMP_TABLE_WRITE (bfd_pef), BFD_JUMP_TABLE_LINK (bfd_pef), BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), NULL, NULL }; #define bfd_pef_xlib_close_and_cleanup _bfd_generic_close_and_cleanup #define bfd_pef_xlib_bfd_free_cached_info _bfd_generic_bfd_free_cached_info #define bfd_pef_xlib_new_section_hook _bfd_generic_new_section_hook #define bfd_pef_xlib_get_section_contents _bfd_generic_get_section_contents #define bfd_pef_xlib_set_section_contents _bfd_generic_set_section_contents #define bfd_pef_xlib_get_section_contents_in_window _bfd_generic_get_section_contents_in_window #define bfd_pef_xlib_set_section_contents_in_window _bfd_generic_set_section_contents_in_window static int bfd_pef_xlib_read_header (abfd, header) bfd *abfd; bfd_pef_xlib_header *header; { unsigned char buf[76]; bfd_seek (abfd, 0, SEEK_SET); if (bfd_bread ((PTR) buf, 76, abfd) != 76) return -1; header->tag1 = bfd_getb32 (buf); header->tag2 = bfd_getb32 (buf + 4); header->current_format = bfd_getb32 (buf + 8); header->container_strings_offset = bfd_getb32 (buf + 12); header->export_hash_offset = bfd_getb32 (buf + 16); header->export_key_offset = bfd_getb32 (buf + 20); header->export_symbol_offset = bfd_getb32 (buf + 24); header->export_names_offset = bfd_getb32 (buf + 28); header->export_hash_table_power = bfd_getb32 (buf + 32); header->exported_symbol_count = bfd_getb32 (buf + 36); header->frag_name_offset = bfd_getb32 (buf + 40); header->frag_name_length = bfd_getb32 (buf + 44); header->dylib_path_offset = bfd_getb32 (buf + 48); header->dylib_path_length = bfd_getb32 (buf + 52); header->cpu_family = bfd_getb32 (buf + 56); header->cpu_model = bfd_getb32 (buf + 60); header->date_time_stamp = bfd_getb32 (buf + 64); header->current_version = bfd_getb32 (buf + 68); header->old_definition_version = bfd_getb32 (buf + 72); header->old_implementation_version = bfd_getb32 (buf + 76); return 0; } int bfd_pef_xlib_scan (abfd, header) bfd *abfd; bfd_pef_xlib_header *header; { bfd_pef_xlib_data_struct *mdata = NULL; mdata = ((bfd_pef_xlib_data_struct *) bfd_alloc (abfd, sizeof (bfd_pef_xlib_data_struct))); if (mdata == NULL) return -1; mdata->header = *header; abfd->flags = (abfd->xvec->object_flags | (abfd->flags & (BFD_IN_MEMORY | BFD_IO_FUNCS))); abfd->tdata.pef_xlib_data = mdata; return 0; } static const bfd_target * bfd_pef_xlib_object_p (abfd) bfd *abfd; { struct bfd_preserve preserve; bfd_pef_xlib_header header; if (bfd_pef_xlib_read_header (abfd, &header) != 0) { bfd_set_error (bfd_error_wrong_format); return NULL; } if ((header.tag1 != BFD_PEF_XLIB_TAG1) || ((header.tag2 != BFD_PEF_VLIB_TAG2) && (header.tag2 != BFD_PEF_BLIB_TAG2))) { bfd_set_error (bfd_error_wrong_format); return NULL; } if (! bfd_preserve_save (abfd, &preserve)) { bfd_set_error (bfd_error_wrong_format); return NULL; } if (bfd_pef_xlib_scan (abfd, &header) != 0) { bfd_preserve_restore (abfd, &preserve); bfd_set_error (bfd_error_wrong_format); return NULL; } bfd_preserve_finish (abfd, &preserve); return abfd->xvec; } const bfd_target pef_xlib_vec = { "pef-xlib", /* name */ bfd_target_pef_xlib_flavour, /* flavour */ BFD_ENDIAN_BIG, /* byteorder */ BFD_ENDIAN_BIG, /* header_byteorder */ (HAS_RELOC | EXEC_P | /* object flags */ HAS_LINENO | HAS_DEBUG | HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */ 0, /* symbol_leading_char */ ' ', /* ar_pad_char */ 16, /* ar_max_namelen */ bfd_getb64, bfd_getb_signed_64, bfd_putb64, bfd_getb32, bfd_getb_signed_32, bfd_putb32, bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */ bfd_getb64, bfd_getb_signed_64, bfd_putb64, bfd_getb32, bfd_getb_signed_32, bfd_putb32, bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */ { /* bfd_check_format */ _bfd_dummy_target, bfd_pef_xlib_object_p, /* bfd_check_format */ _bfd_dummy_target, _bfd_dummy_target, }, { /* bfd_set_format */ bfd_false, bfd_pef_mkobject, bfd_false, bfd_false, }, { /* bfd_write_contents */ bfd_false, bfd_true, bfd_false, bfd_false, }, BFD_JUMP_TABLE_GENERIC (bfd_pef_xlib), BFD_JUMP_TABLE_COPY (_bfd_generic), BFD_JUMP_TABLE_CORE (_bfd_nocore), BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols), BFD_JUMP_TABLE_RELOCS (_bfd_norelocs), BFD_JUMP_TABLE_WRITE (_bfd_nowrite), BFD_JUMP_TABLE_LINK (_bfd_nolink), BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), NULL, NULL };