/* Support for the generic parts of PE/PEI, for BFD. Copyright 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. Written by Cygnus Solutions. 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. */ /* Most of this hacked by Steve Chamberlain, sac@cygnus.com PE/PEI rearrangement (and code added): Donn Terry Softway Systems, Inc. */ /* Hey look, some documentation [and in a place you expect to find it]! The main reference for the pei format is "Microsoft Portable Executable and Common Object File Format Specification 4.1". Get it if you need to do some serious hacking on this code. Another reference: "Peering Inside the PE: A Tour of the Win32 Portable Executable File Format", MSJ 1994, Volume 9. The *sole* difference between the pe format and the pei format is that the latter has an MSDOS 2.0 .exe header on the front that prints the message "This app must be run under Windows." (or some such). (FIXME: Whether that statement is *really* true or not is unknown. Are there more subtle differences between pe and pei formats? For now assume there aren't. If you find one, then for God sakes document it here!) The Microsoft docs use the word "image" instead of "executable" because the former can also refer to a DLL (shared library). Confusion can arise because the `i' in `pei' also refers to "image". The `pe' format can also create images (i.e. executables), it's just that to run on a win32 system you need to use the pei format. FIXME: Please add more docs here so the next poor fool that has to hack on this code has a chance of getting something accomplished without wasting too much time. */ #include "libpei.h" static boolean (*pe_saved_coff_bfd_print_private_bfd_data) PARAMS ((bfd *, PTR)) = #ifndef coff_bfd_print_private_bfd_data NULL; #else coff_bfd_print_private_bfd_data; #undef coff_bfd_print_private_bfd_data #endif static boolean pe_print_private_bfd_data PARAMS ((bfd *, PTR)); #define coff_bfd_print_private_bfd_data pe_print_private_bfd_data static boolean (*pe_saved_coff_bfd_copy_private_bfd_data) PARAMS ((bfd *, bfd *)) = #ifndef coff_bfd_copy_private_bfd_data NULL; #else coff_bfd_copy_private_bfd_data; #undef coff_bfd_copy_private_bfd_data #endif static boolean pe_bfd_copy_private_bfd_data PARAMS ((bfd *, bfd *)); #define coff_bfd_copy_private_bfd_data pe_bfd_copy_private_bfd_data #define coff_mkobject pe_mkobject #define coff_mkobject_hook pe_mkobject_hook static void coff_swap_reloc_in PARAMS ((bfd *, PTR, PTR)); static unsigned int coff_swap_reloc_out PARAMS ((bfd *, PTR, PTR)); static void coff_swap_filehdr_in PARAMS ((bfd *, PTR, PTR)); static void coff_swap_scnhdr_in PARAMS ((bfd *, PTR, PTR)); static boolean pe_mkobject PARAMS ((bfd *)); static PTR pe_mkobject_hook PARAMS ((bfd *, PTR, PTR)); /**********************************************************************/ static void coff_swap_reloc_in (abfd, src, dst) bfd *abfd; PTR src; PTR dst; { RELOC *reloc_src = (RELOC *) src; struct internal_reloc *reloc_dst = (struct internal_reloc *) dst; reloc_dst->r_vaddr = bfd_h_get_32(abfd, (bfd_byte *)reloc_src->r_vaddr); reloc_dst->r_symndx = bfd_h_get_signed_32(abfd, (bfd_byte *) reloc_src->r_symndx); reloc_dst->r_type = bfd_h_get_16(abfd, (bfd_byte *) reloc_src->r_type); #ifdef SWAP_IN_RELOC_OFFSET reloc_dst->r_offset = SWAP_IN_RELOC_OFFSET(abfd, (bfd_byte *) reloc_src->r_offset); #endif } static unsigned int coff_swap_reloc_out (abfd, src, dst) bfd *abfd; PTR src; PTR dst; { struct internal_reloc *reloc_src = (struct internal_reloc *)src; struct external_reloc *reloc_dst = (struct external_reloc *)dst; bfd_h_put_32(abfd, reloc_src->r_vaddr, (bfd_byte *) reloc_dst->r_vaddr); bfd_h_put_32(abfd, reloc_src->r_symndx, (bfd_byte *) reloc_dst->r_symndx); bfd_h_put_16(abfd, reloc_src->r_type, (bfd_byte *) reloc_dst->r_type); #ifdef SWAP_OUT_RELOC_OFFSET SWAP_OUT_RELOC_OFFSET(abfd, reloc_src->r_offset, (bfd_byte *) reloc_dst->r_offset); #endif #ifdef SWAP_OUT_RELOC_EXTRA SWAP_OUT_RELOC_EXTRA(abfd,reloc_src, reloc_dst); #endif return RELSZ; } static void coff_swap_filehdr_in (abfd, src, dst) bfd *abfd; PTR src; PTR dst; { FILHDR *filehdr_src = (FILHDR *) src; struct internal_filehdr *filehdr_dst = (struct internal_filehdr *) dst; filehdr_dst->f_magic = bfd_h_get_16(abfd, (bfd_byte *) filehdr_src->f_magic); filehdr_dst->f_nscns = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_nscns); filehdr_dst->f_timdat = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> f_timdat); filehdr_dst->f_nsyms = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> f_nsyms); filehdr_dst->f_flags = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_flags); filehdr_dst->f_symptr = bfd_h_get_32 (abfd, (bfd_byte *) filehdr_src->f_symptr); #ifdef COFF_IMAGE_WITH_PE /* There are really two magic numbers involved; the magic number that says this is a NT executable (PEI) and the magic number that determines the architecture. The former is DOSMAGIC, stored in the e_magic field. The latter is stored in the f_magic field. If the NT magic number isn't valid, the architecture magic number could be mimicked by some other field (specifically, the number of relocs in section 3). Since this routine can only be called correctly for a PEI file, check the e_magic number here, and, if it doesn't match, clobber the f_magic number so that we don't get a false match. */ if (bfd_h_get_16 (abfd, (bfd_byte *) filehdr_src->e_magic) != DOSMAGIC) filehdr_dst->f_magic = -1; #endif /* Other people's tools sometimes generate headers with an nsyms but a zero symptr. */ if (filehdr_dst->f_nsyms != 0 && filehdr_dst->f_symptr == 0) { filehdr_dst->f_nsyms = 0; filehdr_dst->f_flags |= F_LSYMS; } filehdr_dst->f_opthdr = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_opthdr); } #ifdef COFF_IMAGE_WITH_PE #define coff_swap_filehdr_out _bfd_pei_only_swap_filehdr_out #else #define coff_swap_filehdr_out _bfd_pe_only_swap_filehdr_out #endif static void coff_swap_scnhdr_in (abfd, ext, in) bfd *abfd; PTR ext; PTR in; { SCNHDR *scnhdr_ext = (SCNHDR *) ext; struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in; memcpy(scnhdr_int->s_name, scnhdr_ext->s_name, sizeof(scnhdr_int->s_name)); scnhdr_int->s_vaddr = GET_SCNHDR_VADDR (abfd, (bfd_byte *) scnhdr_ext->s_vaddr); scnhdr_int->s_paddr = GET_SCNHDR_PADDR (abfd, (bfd_byte *) scnhdr_ext->s_paddr); scnhdr_int->s_size = GET_SCNHDR_SIZE (abfd, (bfd_byte *) scnhdr_ext->s_size); scnhdr_int->s_scnptr = GET_SCNHDR_SCNPTR (abfd, (bfd_byte *) scnhdr_ext->s_scnptr); scnhdr_int->s_relptr = GET_SCNHDR_RELPTR (abfd, (bfd_byte *) scnhdr_ext->s_relptr); scnhdr_int->s_lnnoptr = GET_SCNHDR_LNNOPTR (abfd, (bfd_byte *) scnhdr_ext->s_lnnoptr); scnhdr_int->s_flags = bfd_h_get_32(abfd, (bfd_byte *) scnhdr_ext->s_flags); /* MS handles overflow of line numbers by carrying into the reloc field (it appears). Since it's supposed to be zero for PE *IMAGE* format, that's safe. This is still a bit iffy. */ #ifdef COFF_IMAGE_WITH_PE scnhdr_int->s_nlnno = (bfd_h_get_16 (abfd, (bfd_byte *) scnhdr_ext->s_nlnno) + (bfd_h_get_16 (abfd, (bfd_byte *) scnhdr_ext->s_nreloc) << 16)); scnhdr_int->s_nreloc = 0; #else scnhdr_int->s_nreloc = bfd_h_get_16 (abfd, (bfd_byte *) scnhdr_ext->s_nreloc); scnhdr_int->s_nlnno = bfd_h_get_16 (abfd, (bfd_byte *) scnhdr_ext->s_nlnno); #endif if (scnhdr_int->s_vaddr != 0) { scnhdr_int->s_vaddr += pe_data (abfd)->pe_opthdr.ImageBase; scnhdr_int->s_vaddr &= 0xffffffff; } /* If this section holds uninitialized data, use the virtual size (stored in s_paddr) instead of the physical size. */ if ((scnhdr_int->s_flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0) { scnhdr_int->s_size = scnhdr_int->s_paddr; /* This code used to set scnhdr_int->s_paddr to 0. However, coff_set_alignment_hook stores s_paddr in virt_size, which only works if it correctly holds the virtual size of the section. */ } } static boolean pe_mkobject (abfd) bfd * abfd; { pe_data_type *pe; abfd->tdata.pe_obj_data = (struct pe_tdata *) bfd_zalloc (abfd, sizeof (pe_data_type)); if (abfd->tdata.pe_obj_data == 0) return false; pe = pe_data (abfd); pe->coff.pe = 1; /* in_reloc_p is architecture dependent. */ pe->in_reloc_p = in_reloc_p; return true; } /* Create the COFF backend specific information. */ static PTR pe_mkobject_hook (abfd, filehdr, aouthdr) bfd * abfd; PTR filehdr; PTR aouthdr ATTRIBUTE_UNUSED; { struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; pe_data_type *pe; if (pe_mkobject (abfd) == false) return NULL; pe = pe_data (abfd); pe->coff.sym_filepos = internal_f->f_symptr; /* These members communicate important constants about the symbol table to GDB's symbol-reading code. These `constants' unfortunately vary among coff implementations... */ pe->coff.local_n_btmask = N_BTMASK; pe->coff.local_n_btshft = N_BTSHFT; pe->coff.local_n_tmask = N_TMASK; pe->coff.local_n_tshift = N_TSHIFT; pe->coff.local_symesz = SYMESZ; pe->coff.local_auxesz = AUXESZ; pe->coff.local_linesz = LINESZ; pe->coff.timestamp = internal_f->f_timdat; obj_raw_syment_count (abfd) = obj_conv_table_size (abfd) = internal_f->f_nsyms; pe->real_flags = internal_f->f_flags; if ((internal_f->f_flags & F_DLL) != 0) pe->dll = 1; if ((internal_f->f_flags & IMAGE_FILE_DEBUG_STRIPPED) == 0) abfd->flags |= HAS_DEBUG; #ifdef COFF_IMAGE_WITH_PE if (aouthdr) pe->pe_opthdr = ((struct internal_aouthdr *)aouthdr)->pe; #endif #ifdef ARM if (! _bfd_coff_arm_set_private_flags (abfd, internal_f->f_flags)) coff_data (abfd) ->flags = 0; #endif return (PTR) pe; } static boolean pe_print_private_bfd_data (abfd, vfile) bfd *abfd; PTR vfile; { FILE *file = (FILE *) vfile; if (!_bfd_pe_print_private_bfd_data_common (abfd, vfile)) return false; if (pe_saved_coff_bfd_print_private_bfd_data != NULL) { fputc ('\n', file); return pe_saved_coff_bfd_print_private_bfd_data (abfd, vfile); } return true; } /* Copy any private info we understand from the input bfd to the output bfd. */ static boolean pe_bfd_copy_private_bfd_data (ibfd, obfd) bfd *ibfd, *obfd; { if (!_bfd_pe_bfd_copy_private_bfd_data_common (ibfd, obfd)) return false; if (pe_saved_coff_bfd_copy_private_bfd_data) return pe_saved_coff_bfd_copy_private_bfd_data (ibfd, obfd); return true; } #define coff_bfd_copy_private_section_data \ _bfd_pe_bfd_copy_private_section_data #define coff_get_symbol_info _bfd_pe_get_symbol_info static const bfd_target * pe_bfd_object_p (abfd) bfd * abfd; { /* We need to handle a PE image correctly. In PE images created by the GNU linker, the offset to the COFF header is always the size. However, this is not the case in images generated by other PE linkers. The PE format stores a four byte offset to the PE signature just before the COFF header at location 0x3c of the file. We pick up that offset, verify that the PE signature is there, and then set ourselves up to read in the COFF header. */ bfd_byte buffer[4]; file_ptr offset; unsigned long signature; /* Detect if this a Microsoft Import Library Format element. */ if (bfd_seek (abfd, 0x00, SEEK_SET) != 0 || bfd_read (buffer, 1, 4, abfd) != 4) { if (bfd_get_error () != bfd_error_system_call) bfd_set_error (bfd_error_wrong_format); return NULL; } signature = bfd_h_get_32 (abfd, buffer); if (signature == 0xffff0000) { _bfd_error_handler (_("%s: Import Library Format archives are not currently supported"), bfd_get_filename (abfd)); bfd_set_error (bfd_error_wrong_format); return NULL; } if (bfd_seek (abfd, 0x3c, SEEK_SET) != 0 || bfd_read (buffer, 1, 4, abfd) != 4) { if (bfd_get_error () != bfd_error_system_call) bfd_set_error (bfd_error_wrong_format); return NULL; } offset = bfd_h_get_32 (abfd, buffer); if (bfd_seek (abfd, offset, SEEK_SET) != 0 || bfd_read (buffer, 1, 4, abfd) != 4) { if (bfd_get_error () != bfd_error_system_call) bfd_set_error (bfd_error_wrong_format); return NULL; } signature = bfd_h_get_32 (abfd, buffer); if (signature != 0x4550) { bfd_set_error (bfd_error_wrong_format); return NULL; } /* Here is the hack. coff_object_p wants to read filhsz bytes to pick up the COFF header. We adjust so that that will work. 20 is the size of the i386 COFF filehdr. */ if (bfd_seek (abfd, (bfd_tell (abfd) - bfd_coff_filhsz (abfd) + 20), SEEK_SET) != 0) { if (bfd_get_error () != bfd_error_system_call) bfd_set_error (bfd_error_wrong_format); return NULL; } return coff_object_p (abfd); } #define coff_object_p pe_bfd_object_p