/* BFD back-end for the Palm OS PRC resource database format. Copyright 2002 John Marshall. (For now.) Contributed by Falch.net as. 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 "bfd.h" #include "sysdep.h" #include "safe-ctype.h" #include "libbfd.h" #include "prc.h" static bfd_boolean swap_in_header PARAMS ((bfd *abfd, asection *sec, struct palmos_prc_header *header)); static bfd_boolean swap_in_resource_headers PARAMS ((bfd *abfd, unsigned int n, struct palmos_prc_resource_header *headers)); static void add_section PARAMS ((bfd *abfd, struct palmos_prc_header *header, const char *secname, flagword basic_flags, unsigned long offset, unsigned long lim_offset)); static file_ptr tie_down_section PARAMS ((bfd *abfd, asection *sec, file_ptr offset)); static void compute_section_file_positions PARAMS ((bfd *abfd, file_ptr offset)); /* The PRC file format is documented in the "Palm File Format Specification", available from . */ #define HEADER_SIZE 0x4e #define RSRCENTRY_SIZE 10 /* Read from SEC if non-NULL, otherwise directly from the start of ABFD. */ static bfd_boolean swap_in_header (abfd, sec, header) bfd *abfd; asection *sec; struct palmos_prc_header *header; { char raw[HEADER_SIZE]; if (sec) { if (! bfd_get_section_contents (abfd, sec, raw, 0, sizeof raw)) return FALSE; } else { if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0 || bfd_bread (raw, sizeof raw, abfd) != sizeof raw) return FALSE; } memcpy (header->name, &raw[0], 32); header->name[32] = '\0'; header->flags = bfd_h_get_16 (abfd, &raw[0x20]); header->version = bfd_h_get_16 (abfd, &raw[0x22]); header->create_time = bfd_h_get_32 (abfd, &raw[0x24]); header->mod_time = bfd_h_get_32 (abfd, &raw[0x28]); header->backup_time = bfd_h_get_32 (abfd, &raw[0x2c]); header->appinfo_offset = bfd_h_get_32 (abfd, &raw[0x34]); header->sortinfo_offset = bfd_h_get_32 (abfd, &raw[0x38]); memcpy (header->type, &raw[0x3c], 4); header->type[4] = '\0'; memcpy (header->creator, &raw[0x40], 4); header->creator[4] = '\0'; header->nextlist_offset = bfd_h_get_32 (abfd, &raw[0x48]); header->nresources = bfd_h_get_16 (abfd, &raw[0x4c]); return TRUE; } static bfd_boolean swap_in_resource_headers (abfd, n, headers) bfd *abfd; unsigned int n; struct palmos_prc_resource_header *headers; { unsigned int i; if (bfd_seek (abfd, (file_ptr) HEADER_SIZE, SEEK_SET) != 0) return FALSE; for (i = 0; i < n; i++) { char raw[RSRCENTRY_SIZE]; if (bfd_bread (raw, sizeof raw, abfd) != sizeof raw) return FALSE; memcpy (headers[i].type, &raw[0], 4); headers[i].type[4] = '\0'; headers[i].id = bfd_h_get_16 (abfd, &raw[4]); headers[i].offset = bfd_h_get_32 (abfd, &raw[6]); } return TRUE; } static bfd_boolean printable_string_p (const char *s) { while (*s) if (! ISPRINT (*s++)) return FALSE; return TRUE; } /* We use SEC_LINKER_CREATED to signify arcane processing; namely a section, such as header information or appinfo, which does not correspond to a real resource. */ static void add_section (abfd, header, secname, basic_flags, offset, lim_offset) bfd *abfd; struct palmos_prc_header *header; const char *secname; flagword basic_flags; unsigned long offset, lim_offset; { asection *sec = bfd_make_section (abfd, secname); if (sec == NULL) return; sec->vma = sec->lma = 0; sec->filepos = offset; bfd_set_section_size (abfd, sec, lim_offset - offset); /* FIXME?? sec->_raw_size = lim_offset - offset; */ sec->flags = (SEC_HAS_CONTENTS | basic_flags | (*(backend_data (abfd)->secflags)) (header, secname)); if (header->flags & 0x0002) sec->flags |= SEC_READONLY; } bfd_boolean _bfd_prc_mkobject (abfd) bfd *abfd ATTRIBUTE_UNUSED; { return TRUE; } const bfd_target * _bfd_prc_object_p (abfd) bfd *abfd; { struct palmos_prc_header h; struct palmos_prc_resource_header *resource = NULL; unsigned int i; struct stat statbuf; if (! swap_in_header (abfd, NULL, &h)) goto invalid_prc; resource = bfd_malloc ((h.nresources + 1) * sizeof (struct palmos_prc_resource_header)); if (resource == NULL) goto failed; if (! swap_in_resource_headers (abfd, h.nresources, resource)) goto invalid_prc; if (bfd_stat (abfd, &statbuf) < 0) goto failed; resource[h.nresources].offset = statbuf.st_size; /* The PRC format has no useful magic numbers, so checking that a file is a valid .prc is a black art. We check that: - name, type, creator, and resource types are all printable ASCII; - flags includes 0x1; - next_record_list is 0; - app_info, sort_info (when they are non-zero), section pointers are strictly ascending. */ if (! (printable_string_p (h.name) && (h.flags & 0x0001) && printable_string_p (h.type) && printable_string_p (h.creator) && h.nextlist_offset == 0 && (h.appinfo_offset == 0 || h.sortinfo_offset == 0 || h.appinfo_offset < h.sortinfo_offset) && (h.sortinfo_offset == 0 || h.sortinfo_offset < resource[0].offset))) goto invalid_prc; for (i = 0; i < h.nresources; i++) if (! (printable_string_p (resource[i].type) && resource[i].offset < resource[i + 1].offset)) goto invalid_prc; add_section (abfd, &h, ".header", SEC_LINKER_CREATED, 0, HEADER_SIZE); if (h.appinfo_offset) add_section (abfd, &h, ".appinfo", SEC_LINKER_CREATED | SEC_ALLOC | SEC_LOAD, h.appinfo_offset, h.sortinfo_offset? h.sortinfo_offset : resource[0].offset); if (h.sortinfo_offset) add_section (abfd, &h, ".sortinfo", SEC_LINKER_CREATED | SEC_ALLOC | SEC_LOAD, h.sortinfo_offset, resource[0].offset); for (i = 0; i < h.nresources; i++) { char secnamebuf[32]; char *secname; sprintf (secnamebuf, "%s.%u", resource[i].type, resource[i].id); /* FIXME: If this ever fails, it'll leak the previous names. */ secname = bfd_alloc (abfd, strlen (secnamebuf) + 1); if (secname == NULL) goto failed; strcpy (secname, secnamebuf); add_section (abfd, &h, secname, SEC_ALLOC | SEC_LOAD, resource[i].offset, resource[i + 1].offset); } if (strcmp (h.type, "appl") == 0) abfd->flags |= EXEC_P; else if (strcmp (h.type, "libr") == 0 || strcmp (h.type, "GLib") == 0) abfd->flags |= DYNAMIC; bfd_default_set_arch_mach (abfd, backend_data (abfd)->arch, backend_data (abfd)->mach); /* FIXME */ abfd->symcount = 0; return abfd->xvec; /* Otherwise ABFD was not recognized as being in PRC format. Jumps to are asking for a wrong_format error; jumps to imply that something major has just failed and has already set a suitable error code. */ invalid_prc: bfd_set_error (bfd_error_wrong_format); failed: if (resource) free (resource); return NULL; } static file_ptr tie_down_section (abfd, sec, offset) bfd *abfd ATTRIBUTE_UNUSED; asection *sec; file_ptr offset; { sec->filepos = offset; return offset + bfd_section_size (abfd, sec); } static void compute_section_file_positions (abfd, offset) bfd *abfd; file_ptr offset; { asection *sec; sec = bfd_get_section_by_name (abfd, ".appinfo"); if (sec) offset = tie_down_section (abfd, sec, offset); sec = bfd_get_section_by_name (abfd, ".sortinfo"); if (sec) offset = tie_down_section (abfd, sec, offset); for (sec = abfd->sections; sec != NULL; sec = sec->next) if (! (sec->flags & SEC_LINKER_CREATED)) offset = tie_down_section (abfd, sec, offset); } bfd_boolean _bfd_prc_write_object_contents (abfd) bfd *abfd; { /* FIXME This is not yet implemented. */ compute_section_file_positions (abfd, HEADER_SIZE); return TRUE; } asymbol * _bfd_prc_make_empty_symbol (abfd) bfd *abfd; { asymbol *sym = (asymbol *) bfd_zalloc (abfd, sizeof (asymbol)); if (sym) sym->the_bfd = abfd; return sym; } long _bfd_prc_get_symtab_upper_bound (abfd) bfd *abfd; { return (bfd_get_symcount (abfd) + 1) * sizeof (asymbol *); } long _bfd_prc_get_symtab (abfd, alocation) bfd *abfd ATTRIBUTE_UNUSED; asymbol **alocation ATTRIBUTE_UNUSED; { return 0; } void _bfd_prc_get_symbol_info (abfd, symbol, ret) bfd *abfd ATTRIBUTE_UNUSED; asymbol *symbol; symbol_info *ret; { bfd_symbol_info (symbol, ret); } struct flag_meaning { unsigned int mask; const char *name; }; /* xgettext: These names should not be translated, because they correspond to constants defined in Palm OS SDKs. */ static const struct flag_meaning meanings[] = { { 0x0001, "RESOURCE" }, { 0x0002, "READONLY" }, { 0x0004, "APPINFO-DIRTY" }, { 0x0008, "BACKUP" }, { 0x0010, "OK-TO-INSTALL-NEWER" }, { 0x0020, "RESET-AFTER-INSTALL" }, { 0x0040, "COPY-PREVENTION" }, { 0x0080, "STREAM" }, { 0x0100, "HIDDEN" }, { 0x0200, "LAUNCHABLE-DATA" }, { 0x0400, "RECYCLABLE" }, { 0x0800, "BUNDLE" }, { 0x8000, "OPEN" }, { 0, NULL } }; /* FIXME: internationalise me! */ bfd_boolean _bfd_prc_bfd_print_private_bfd_data (abfd, ptr) bfd *abfd; PTR ptr; { FILE *f = (FILE *) ptr; struct palmos_prc_header header; unsigned int flags; const struct flag_meaning *meaning; swap_in_header (abfd, bfd_get_section_by_name (abfd, ".header"), &header); flags = header.flags; fprintf (f, "\nDatabase Header:\n"); fprintf (f, " Name: %s\n", header.name); fprintf (f, " Flags: "); for (meaning = meanings; meaning->name; meaning++) if (flags & meaning->mask) { fprintf (f, " %s", meaning->name); flags &= ~meaning->mask; } if (flags != 0) fprintf (f, " 0x%x", flags); fprintf (f, "\n"); fprintf (f, " Type: %s\n", header.type); fprintf (f, " Creator: %s\n", header.creator); fprintf (f, " Version: %u\n", header.version); /* FIXME: Output the dates too, if we can be bothered converting from Palm OS's seconds-since-1904-01-01T00:00:00 format. */ return TRUE; }