| /* BFD back-end for ALPHA Extended-Coff files. | |
| Copyright (C) 1993-2024 Free Software Foundation, Inc. | |
| Modified from coff-mips.c by Steve Chamberlain <sac@cygnus.com> and | |
| Ian Lance Taylor <ian@cygnus.com>. | |
| 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 3 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., 51 Franklin Street - Fifth Floor, Boston, | |
| MA 02110-1301, USA. */ | |
| #include "sysdep.h" | |
| #include "bfd.h" | |
| #include "bfdlink.h" | |
| #include "libbfd.h" | |
| #include "coff/internal.h" | |
| #include "coff/sym.h" | |
| #include "coff/symconst.h" | |
| #include "coff/ecoff.h" | |
| #include "coff/alpha.h" | |
| #include "aout/ar.h" | |
| #include "libcoff.h" | |
| #include "libecoff.h" | |
| /* Prototypes for static functions. */ | |
| /* ECOFF has COFF sections, but the debugging information is stored in | |
| a completely different format. ECOFF targets use some of the | |
| swapping routines from coffswap.h, and some of the generic COFF | |
| routines in coffgen.c, but, unlike the real COFF targets, do not | |
| use coffcode.h itself. | |
| Get the generic COFF swapping routines, except for the reloc, | |
| symbol, and lineno ones. Give them ecoff names. Define some | |
| accessor macros for the large sizes used for Alpha ECOFF. */ | |
| #define GET_FILEHDR_SYMPTR H_GET_64 | |
| #define PUT_FILEHDR_SYMPTR H_PUT_64 | |
| #define GET_AOUTHDR_TSIZE H_GET_64 | |
| #define PUT_AOUTHDR_TSIZE H_PUT_64 | |
| #define GET_AOUTHDR_DSIZE H_GET_64 | |
| #define PUT_AOUTHDR_DSIZE H_PUT_64 | |
| #define GET_AOUTHDR_BSIZE H_GET_64 | |
| #define PUT_AOUTHDR_BSIZE H_PUT_64 | |
| #define GET_AOUTHDR_ENTRY H_GET_64 | |
| #define PUT_AOUTHDR_ENTRY H_PUT_64 | |
| #define GET_AOUTHDR_TEXT_START H_GET_64 | |
| #define PUT_AOUTHDR_TEXT_START H_PUT_64 | |
| #define GET_AOUTHDR_DATA_START H_GET_64 | |
| #define PUT_AOUTHDR_DATA_START H_PUT_64 | |
| #define GET_SCNHDR_PADDR H_GET_64 | |
| #define PUT_SCNHDR_PADDR H_PUT_64 | |
| #define GET_SCNHDR_VADDR H_GET_64 | |
| #define PUT_SCNHDR_VADDR H_PUT_64 | |
| #define GET_SCNHDR_SIZE H_GET_64 | |
| #define PUT_SCNHDR_SIZE H_PUT_64 | |
| #define GET_SCNHDR_SCNPTR H_GET_64 | |
| #define PUT_SCNHDR_SCNPTR H_PUT_64 | |
| #define GET_SCNHDR_RELPTR H_GET_64 | |
| #define PUT_SCNHDR_RELPTR H_PUT_64 | |
| #define GET_SCNHDR_LNNOPTR H_GET_64 | |
| #define PUT_SCNHDR_LNNOPTR H_PUT_64 | |
| #define ALPHAECOFF | |
| #define NO_COFF_RELOCS | |
| #define NO_COFF_SYMBOLS | |
| #define NO_COFF_LINENOS | |
| #define coff_swap_filehdr_in alpha_ecoff_swap_filehdr_in | |
| #define coff_swap_filehdr_out alpha_ecoff_swap_filehdr_out | |
| #define coff_swap_aouthdr_in alpha_ecoff_swap_aouthdr_in | |
| #define coff_swap_aouthdr_out alpha_ecoff_swap_aouthdr_out | |
| #define coff_swap_scnhdr_in alpha_ecoff_swap_scnhdr_in | |
| #define coff_swap_scnhdr_out alpha_ecoff_swap_scnhdr_out | |
| #include "coffswap.h" | |
| /* Get the ECOFF swapping routines. */ | |
| #define ECOFF_64 | |
| #include "ecoffswap.h" | |
| /* How to process the various reloc types. */ | |
| static bfd_reloc_status_type | |
| reloc_nil (bfd *abfd ATTRIBUTE_UNUSED, | |
| arelent *reloc ATTRIBUTE_UNUSED, | |
| asymbol *sym ATTRIBUTE_UNUSED, | |
| void * data ATTRIBUTE_UNUSED, | |
| asection *sec ATTRIBUTE_UNUSED, | |
| bfd *output_bfd ATTRIBUTE_UNUSED, | |
| char **error_message ATTRIBUTE_UNUSED) | |
| { | |
| return bfd_reloc_ok; | |
| } | |
| /* In case we're on a 32-bit machine, construct a 64-bit "-1" value | |
| from smaller values. Start with zero, widen, *then* decrement. */ | |
| #define MINUS_ONE (((bfd_vma)0) - 1) | |
| static reloc_howto_type alpha_howto_table[] = | |
| { | |
| /* Reloc type 0 is ignored by itself. However, it appears after a | |
| GPDISP reloc to identify the location where the low order 16 bits | |
| of the gp register are loaded. */ | |
| HOWTO (ALPHA_R_IGNORE, /* type */ | |
| 0, /* rightshift */ | |
| 1, /* size */ | |
| 8, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| reloc_nil, /* special_function */ | |
| "IGNORE", /* name */ | |
| true, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| 0, /* dst_mask */ | |
| true), /* pcrel_offset */ | |
| /* A 32 bit reference to a symbol. */ | |
| HOWTO (ALPHA_R_REFLONG, /* type */ | |
| 0, /* rightshift */ | |
| 4, /* size */ | |
| 32, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_bitfield, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "REFLONG", /* name */ | |
| true, /* partial_inplace */ | |
| 0xffffffff, /* src_mask */ | |
| 0xffffffff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* A 64 bit reference to a symbol. */ | |
| HOWTO (ALPHA_R_REFQUAD, /* type */ | |
| 0, /* rightshift */ | |
| 8, /* size */ | |
| 64, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_bitfield, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "REFQUAD", /* name */ | |
| true, /* partial_inplace */ | |
| MINUS_ONE, /* src_mask */ | |
| MINUS_ONE, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* A 32 bit GP relative offset. This is just like REFLONG except | |
| that when the value is used the value of the gp register will be | |
| added in. */ | |
| HOWTO (ALPHA_R_GPREL32, /* type */ | |
| 0, /* rightshift */ | |
| 4, /* size */ | |
| 32, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_bitfield, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "GPREL32", /* name */ | |
| true, /* partial_inplace */ | |
| 0xffffffff, /* src_mask */ | |
| 0xffffffff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Used for an instruction that refers to memory off the GP | |
| register. The offset is 16 bits of the 32 bit instruction. This | |
| reloc always seems to be against the .lita section. */ | |
| HOWTO (ALPHA_R_LITERAL, /* type */ | |
| 0, /* rightshift */ | |
| 4, /* size */ | |
| 16, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_signed, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "LITERAL", /* name */ | |
| true, /* partial_inplace */ | |
| 0xffff, /* src_mask */ | |
| 0xffff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* This reloc only appears immediately following a LITERAL reloc. | |
| It identifies a use of the literal. It seems that the linker can | |
| use this to eliminate a portion of the .lita section. The symbol | |
| index is special: 1 means the literal address is in the base | |
| register of a memory format instruction; 2 means the literal | |
| address is in the byte offset register of a byte-manipulation | |
| instruction; 3 means the literal address is in the target | |
| register of a jsr instruction. This does not actually do any | |
| relocation. */ | |
| HOWTO (ALPHA_R_LITUSE, /* type */ | |
| 0, /* rightshift */ | |
| 4, /* size */ | |
| 32, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| reloc_nil, /* special_function */ | |
| "LITUSE", /* name */ | |
| false, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| 0, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Load the gp register. This is always used for a ldah instruction | |
| which loads the upper 16 bits of the gp register. The next reloc | |
| will be an IGNORE reloc which identifies the location of the lda | |
| instruction which loads the lower 16 bits. The symbol index of | |
| the GPDISP instruction appears to actually be the number of bytes | |
| between the ldah and lda instructions. This gives two different | |
| ways to determine where the lda instruction is; I don't know why | |
| both are used. The value to use for the relocation is the | |
| difference between the GP value and the current location; the | |
| load will always be done against a register holding the current | |
| address. */ | |
| HOWTO (ALPHA_R_GPDISP, /* type */ | |
| 16, /* rightshift */ | |
| 4, /* size */ | |
| 16, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| reloc_nil, /* special_function */ | |
| "GPDISP", /* name */ | |
| true, /* partial_inplace */ | |
| 0xffff, /* src_mask */ | |
| 0xffff, /* dst_mask */ | |
| true), /* pcrel_offset */ | |
| /* A 21 bit branch. The native assembler generates these for | |
| branches within the text segment, and also fills in the PC | |
| relative offset in the instruction. */ | |
| HOWTO (ALPHA_R_BRADDR, /* type */ | |
| 2, /* rightshift */ | |
| 4, /* size */ | |
| 21, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_signed, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "BRADDR", /* name */ | |
| true, /* partial_inplace */ | |
| 0x1fffff, /* src_mask */ | |
| 0x1fffff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* A hint for a jump to a register. */ | |
| HOWTO (ALPHA_R_HINT, /* type */ | |
| 2, /* rightshift */ | |
| 4, /* size */ | |
| 14, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "HINT", /* name */ | |
| true, /* partial_inplace */ | |
| 0x3fff, /* src_mask */ | |
| 0x3fff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* 16 bit PC relative offset. */ | |
| HOWTO (ALPHA_R_SREL16, /* type */ | |
| 0, /* rightshift */ | |
| 2, /* size */ | |
| 16, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_signed, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "SREL16", /* name */ | |
| true, /* partial_inplace */ | |
| 0xffff, /* src_mask */ | |
| 0xffff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* 32 bit PC relative offset. */ | |
| HOWTO (ALPHA_R_SREL32, /* type */ | |
| 0, /* rightshift */ | |
| 4, /* size */ | |
| 32, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_signed, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "SREL32", /* name */ | |
| true, /* partial_inplace */ | |
| 0xffffffff, /* src_mask */ | |
| 0xffffffff, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* A 64 bit PC relative offset. */ | |
| HOWTO (ALPHA_R_SREL64, /* type */ | |
| 0, /* rightshift */ | |
| 8, /* size */ | |
| 64, /* bitsize */ | |
| true, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_signed, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "SREL64", /* name */ | |
| true, /* partial_inplace */ | |
| MINUS_ONE, /* src_mask */ | |
| MINUS_ONE, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Push a value on the reloc evaluation stack. */ | |
| HOWTO (ALPHA_R_OP_PUSH, /* type */ | |
| 0, /* rightshift */ | |
| 0, /* size */ | |
| 0, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "OP_PUSH", /* name */ | |
| false, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| 0, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Store the value from the stack at the given address. Store it in | |
| a bitfield of size r_size starting at bit position r_offset. */ | |
| HOWTO (ALPHA_R_OP_STORE, /* type */ | |
| 0, /* rightshift */ | |
| 8, /* size */ | |
| 64, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "OP_STORE", /* name */ | |
| false, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| MINUS_ONE, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Subtract the reloc address from the value on the top of the | |
| relocation stack. */ | |
| HOWTO (ALPHA_R_OP_PSUB, /* type */ | |
| 0, /* rightshift */ | |
| 0, /* size */ | |
| 0, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "OP_PSUB", /* name */ | |
| false, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| 0, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Shift the value on the top of the relocation stack right by the | |
| given value. */ | |
| HOWTO (ALPHA_R_OP_PRSHIFT, /* type */ | |
| 0, /* rightshift */ | |
| 0, /* size */ | |
| 0, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "OP_PRSHIFT", /* name */ | |
| false, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| 0, /* dst_mask */ | |
| false), /* pcrel_offset */ | |
| /* Adjust the GP value for a new range in the object file. */ | |
| HOWTO (ALPHA_R_GPVALUE, /* type */ | |
| 0, /* rightshift */ | |
| 0, /* size */ | |
| 0, /* bitsize */ | |
| false, /* pc_relative */ | |
| 0, /* bitpos */ | |
| complain_overflow_dont, /* complain_on_overflow */ | |
| 0, /* special_function */ | |
| "GPVALUE", /* name */ | |
| false, /* partial_inplace */ | |
| 0, /* src_mask */ | |
| 0, /* dst_mask */ | |
| false) /* pcrel_offset */ | |
| }; | |
| /* Recognize an Alpha ECOFF file. */ | |
| static bfd_cleanup | |
| alpha_ecoff_object_p (bfd *abfd) | |
| { | |
| bfd_cleanup ret; | |
| ret = coff_object_p (abfd); | |
| if (ret != NULL) | |
| { | |
| asection *sec; | |
| /* Alpha ECOFF has a .pdata section. The lnnoptr field of the | |
| .pdata section is the number of entries it contains. Each | |
| entry takes up 8 bytes. The number of entries is required | |
| since the section is aligned to a 16 byte boundary. When we | |
| link .pdata sections together, we do not want to include the | |
| alignment bytes. We handle this on input by faking the size | |
| of the .pdata section to remove the unwanted alignment bytes. | |
| On output we will set the lnnoptr field and force the | |
| alignment. */ | |
| sec = bfd_get_section_by_name (abfd, _PDATA); | |
| if (sec != (asection *) NULL) | |
| { | |
| bfd_size_type size; | |
| size = (bfd_size_type) sec->line_filepos * 8; | |
| BFD_ASSERT (size == sec->size | |
| || size + 8 == sec->size); | |
| if (!bfd_set_section_size (sec, size)) | |
| return NULL; | |
| } | |
| } | |
| return ret; | |
| } | |
| /* See whether the magic number matches. */ | |
| static bool | |
| alpha_ecoff_bad_format_hook (bfd *abfd ATTRIBUTE_UNUSED, | |
| void * filehdr) | |
| { | |
| struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; | |
| if (! ALPHA_ECOFF_BADMAG (*internal_f)) | |
| return true; | |
| if (ALPHA_ECOFF_COMPRESSEDMAG (*internal_f)) | |
| _bfd_error_handler | |
| (_("%pB: cannot handle compressed Alpha binaries; " | |
| "use compiler flags, or objZ, to generate uncompressed binaries"), | |
| abfd); | |
| return false; | |
| } | |
| /* This is a hook called by coff_real_object_p to create any backend | |
| specific information. */ | |
| static void * | |
| alpha_ecoff_mkobject_hook (bfd *abfd, void * filehdr, void * aouthdr) | |
| { | |
| void * ecoff; | |
| ecoff = _bfd_ecoff_mkobject_hook (abfd, filehdr, aouthdr); | |
| if (ecoff != NULL) | |
| { | |
| struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; | |
| /* Set additional BFD flags according to the object type from the | |
| machine specific file header flags. */ | |
| switch (internal_f->f_flags & F_ALPHA_OBJECT_TYPE_MASK) | |
| { | |
| case F_ALPHA_SHARABLE: | |
| abfd->flags |= DYNAMIC; | |
| break; | |
| case F_ALPHA_CALL_SHARED: | |
| /* Always executable if using shared libraries as the run time | |
| loader might resolve undefined references. */ | |
| abfd->flags |= (DYNAMIC | EXEC_P); | |
| break; | |
| } | |
| } | |
| return ecoff; | |
| } | |
| /* Reloc handling. */ | |
| /* Swap a reloc in. */ | |
| static void | |
| alpha_ecoff_swap_reloc_in (bfd *abfd, | |
| void * ext_ptr, | |
| struct internal_reloc *intern) | |
| { | |
| const RELOC *ext = (RELOC *) ext_ptr; | |
| intern->r_vaddr = H_GET_64 (abfd, ext->r_vaddr); | |
| intern->r_symndx = H_GET_32 (abfd, ext->r_symndx); | |
| BFD_ASSERT (bfd_header_little_endian (abfd)); | |
| intern->r_type = ((ext->r_bits[0] & RELOC_BITS0_TYPE_LITTLE) | |
| >> RELOC_BITS0_TYPE_SH_LITTLE); | |
| intern->r_extern = (ext->r_bits[1] & RELOC_BITS1_EXTERN_LITTLE) != 0; | |
| intern->r_offset = ((ext->r_bits[1] & RELOC_BITS1_OFFSET_LITTLE) | |
| >> RELOC_BITS1_OFFSET_SH_LITTLE); | |
| /* Ignored the reserved bits. */ | |
| intern->r_size = ((ext->r_bits[3] & RELOC_BITS3_SIZE_LITTLE) | |
| >> RELOC_BITS3_SIZE_SH_LITTLE); | |
| if (intern->r_type == ALPHA_R_LITUSE | |
| || intern->r_type == ALPHA_R_GPDISP) | |
| { | |
| /* Handle the LITUSE and GPDISP relocs specially. Its symndx | |
| value is not actually a symbol index, but is instead a | |
| special code. We put the code in the r_size field, and | |
| clobber the symndx. */ | |
| if (intern->r_size != 0) | |
| abort (); | |
| intern->r_size = intern->r_symndx; | |
| intern->r_symndx = RELOC_SECTION_NONE; | |
| } | |
| else if (intern->r_type == ALPHA_R_IGNORE) | |
| { | |
| /* The IGNORE reloc generally follows a GPDISP reloc, and is | |
| against the .lita section. The section is irrelevant. */ | |
| if (! intern->r_extern && | |
| intern->r_symndx == RELOC_SECTION_ABS) | |
| abort (); | |
| if (! intern->r_extern && intern->r_symndx == RELOC_SECTION_LITA) | |
| intern->r_symndx = RELOC_SECTION_ABS; | |
| } | |
| } | |
| /* Swap a reloc out. */ | |
| static void | |
| alpha_ecoff_swap_reloc_out (bfd *abfd, | |
| const struct internal_reloc *intern, | |
| void * dst) | |
| { | |
| RELOC *ext = (RELOC *) dst; | |
| long symndx; | |
| unsigned char size; | |
| /* Undo the hackery done in swap_reloc_in. */ | |
| if (intern->r_type == ALPHA_R_LITUSE | |
| || intern->r_type == ALPHA_R_GPDISP) | |
| { | |
| symndx = intern->r_size; | |
| size = 0; | |
| } | |
| else if (intern->r_type == ALPHA_R_IGNORE | |
| && ! intern->r_extern | |
| && intern->r_symndx == RELOC_SECTION_ABS) | |
| { | |
| symndx = RELOC_SECTION_LITA; | |
| size = intern->r_size; | |
| } | |
| else | |
| { | |
| symndx = intern->r_symndx; | |
| size = intern->r_size; | |
| } | |
| /* XXX FIXME: The maximum symndx value used to be 14 but this | |
| fails with object files produced by DEC's C++ compiler. | |
| Where does the value 14 (or 15) come from anyway ? */ | |
| BFD_ASSERT (intern->r_extern | |
| || (intern->r_symndx >= 0 && intern->r_symndx <= 15)); | |
| H_PUT_64 (abfd, intern->r_vaddr, ext->r_vaddr); | |
| H_PUT_32 (abfd, symndx, ext->r_symndx); | |
| BFD_ASSERT (bfd_header_little_endian (abfd)); | |
| ext->r_bits[0] = ((intern->r_type << RELOC_BITS0_TYPE_SH_LITTLE) | |
| & RELOC_BITS0_TYPE_LITTLE); | |
| ext->r_bits[1] = ((intern->r_extern ? RELOC_BITS1_EXTERN_LITTLE : 0) | |
| | ((intern->r_offset << RELOC_BITS1_OFFSET_SH_LITTLE) | |
| & RELOC_BITS1_OFFSET_LITTLE)); | |
| ext->r_bits[2] = 0; | |
| ext->r_bits[3] = ((size << RELOC_BITS3_SIZE_SH_LITTLE) | |
| & RELOC_BITS3_SIZE_LITTLE); | |
| } | |
| /* Finish canonicalizing a reloc. Part of this is generic to all | |
| ECOFF targets, and that part is in ecoff.c. The rest is done in | |
| this backend routine. It must fill in the howto field. */ | |
| static void | |
| alpha_adjust_reloc_in (bfd *abfd, | |
| const struct internal_reloc *intern, | |
| arelent *rptr) | |
| { | |
| if (intern->r_type > ALPHA_R_GPVALUE) | |
| { | |
| /* xgettext:c-format */ | |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), | |
| abfd, intern->r_type); | |
| bfd_set_error (bfd_error_bad_value); | |
| rptr->addend = 0; | |
| rptr->howto = NULL; | |
| return; | |
| } | |
| switch (intern->r_type) | |
| { | |
| case ALPHA_R_BRADDR: | |
| case ALPHA_R_SREL16: | |
| case ALPHA_R_SREL32: | |
| case ALPHA_R_SREL64: | |
| /* This relocs appear to be fully resolved when they are against | |
| internal symbols. Against external symbols, BRADDR at least | |
| appears to be resolved against the next instruction. */ | |
| if (! intern->r_extern) | |
| rptr->addend = 0; | |
| else | |
| rptr->addend = - (intern->r_vaddr + 4); | |
| break; | |
| case ALPHA_R_GPREL32: | |
| case ALPHA_R_LITERAL: | |
| /* Copy the gp value for this object file into the addend, to | |
| ensure that we are not confused by the linker. */ | |
| if (! intern->r_extern) | |
| rptr->addend += ecoff_data (abfd)->gp; | |
| break; | |
| case ALPHA_R_LITUSE: | |
| case ALPHA_R_GPDISP: | |
| /* The LITUSE and GPDISP relocs do not use a symbol, or an | |
| addend, but they do use a special code. Put this code in the | |
| addend field. */ | |
| rptr->addend = intern->r_size; | |
| break; | |
| case ALPHA_R_OP_STORE: | |
| /* The STORE reloc needs the size and offset fields. We store | |
| them in the addend. */ | |
| BFD_ASSERT (intern->r_offset <= 256); | |
| rptr->addend = (intern->r_offset << 8) + intern->r_size; | |
| break; | |
| case ALPHA_R_OP_PUSH: | |
| case ALPHA_R_OP_PSUB: | |
| case ALPHA_R_OP_PRSHIFT: | |
| /* The PUSH, PSUB and PRSHIFT relocs do not actually use an | |
| address. I believe that the address supplied is really an | |
| addend. */ | |
| rptr->addend = intern->r_vaddr; | |
| break; | |
| case ALPHA_R_GPVALUE: | |
| /* Set the addend field to the new GP value. */ | |
| rptr->addend = intern->r_symndx + ecoff_data (abfd)->gp; | |
| break; | |
| case ALPHA_R_IGNORE: | |
| /* If the type is ALPHA_R_IGNORE, make sure this is a reference | |
| to the absolute section so that the reloc is ignored. For | |
| some reason the address of this reloc type is not adjusted by | |
| the section vma. We record the gp value for this object file | |
| here, for convenience when doing the GPDISP relocation. */ | |
| rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; | |
| rptr->address = intern->r_vaddr; | |
| rptr->addend = ecoff_data (abfd)->gp; | |
| break; | |
| default: | |
| break; | |
| } | |
| rptr->howto = &alpha_howto_table[intern->r_type]; | |
| } | |
| /* When writing out a reloc we need to pull some values back out of | |
| the addend field into the reloc. This is roughly the reverse of | |
| alpha_adjust_reloc_in, except that there are several changes we do | |
| not need to undo. */ | |
| static void | |
| alpha_adjust_reloc_out (bfd *abfd ATTRIBUTE_UNUSED, | |
| const arelent *rel, | |
| struct internal_reloc *intern) | |
| { | |
| switch (intern->r_type) | |
| { | |
| case ALPHA_R_LITUSE: | |
| case ALPHA_R_GPDISP: | |
| intern->r_size = rel->addend; | |
| break; | |
| case ALPHA_R_OP_STORE: | |
| intern->r_size = rel->addend & 0xff; | |
| intern->r_offset = (rel->addend >> 8) & 0xff; | |
| break; | |
| case ALPHA_R_OP_PUSH: | |
| case ALPHA_R_OP_PSUB: | |
| case ALPHA_R_OP_PRSHIFT: | |
| intern->r_vaddr = rel->addend; | |
| break; | |
| case ALPHA_R_IGNORE: | |
| intern->r_vaddr = rel->address; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| /* The size of the stack for the relocation evaluator. */ | |
| #define RELOC_STACKSIZE (10) | |
| /* Alpha ECOFF relocs have a built in expression evaluator as well as | |
| other interdependencies. Rather than use a bunch of special | |
| functions and global variables, we use a single routine to do all | |
| the relocation for a section. I haven't yet worked out how the | |
| assembler is going to handle this. */ | |
| static bfd_byte * | |
| alpha_ecoff_get_relocated_section_contents (bfd *abfd, | |
| struct bfd_link_info *link_info, | |
| struct bfd_link_order *link_order, | |
| bfd_byte *data, | |
| bool relocatable, | |
| asymbol **symbols) | |
| { | |
| bfd *input_bfd = link_order->u.indirect.section->owner; | |
| asection *input_section = link_order->u.indirect.section; | |
| long reloc_size; | |
| arelent **reloc_vector; | |
| long reloc_count; | |
| bfd *output_bfd = relocatable ? abfd : (bfd *) NULL; | |
| bfd_vma gp; | |
| bool gp_undefined; | |
| bfd_vma stack[RELOC_STACKSIZE]; | |
| int tos = 0; | |
| reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section); | |
| if (reloc_size < 0) | |
| return NULL; | |
| bfd_byte *orig_data = data; | |
| if (!bfd_get_full_section_contents (input_bfd, input_section, &data)) | |
| return NULL; | |
| if (data == NULL) | |
| return NULL; | |
| if (reloc_size == 0) | |
| return data; | |
| reloc_vector = (arelent **) bfd_malloc (reloc_size); | |
| if (reloc_vector == NULL) | |
| goto error_return; | |
| reloc_count = bfd_canonicalize_reloc (input_bfd, input_section, | |
| reloc_vector, symbols); | |
| if (reloc_count < 0) | |
| goto error_return; | |
| if (reloc_count == 0) | |
| goto successful_return; | |
| /* Get the GP value for the output BFD. */ | |
| gp_undefined = false; | |
| gp = _bfd_get_gp_value (abfd); | |
| if (gp == 0) | |
| { | |
| if (relocatable) | |
| { | |
| asection *sec; | |
| bfd_vma lo; | |
| /* Make up a value. */ | |
| lo = (bfd_vma) -1; | |
| for (sec = abfd->sections; sec != NULL; sec = sec->next) | |
| { | |
| if (sec->vma < lo | |
| && (strcmp (sec->name, ".sbss") == 0 | |
| || strcmp (sec->name, ".sdata") == 0 | |
| || strcmp (sec->name, ".lit4") == 0 | |
| || strcmp (sec->name, ".lit8") == 0 | |
| || strcmp (sec->name, ".lita") == 0)) | |
| lo = sec->vma; | |
| } | |
| gp = lo + 0x8000; | |
| _bfd_set_gp_value (abfd, gp); | |
| } | |
| else | |
| { | |
| struct bfd_link_hash_entry *h; | |
| h = bfd_link_hash_lookup (link_info->hash, "_gp", false, false, | |
| true); | |
| if (h == (struct bfd_link_hash_entry *) NULL | |
| || h->type != bfd_link_hash_defined) | |
| gp_undefined = true; | |
| else | |
| { | |
| gp = (h->u.def.value | |
| + h->u.def.section->output_section->vma | |
| + h->u.def.section->output_offset); | |
| _bfd_set_gp_value (abfd, gp); | |
| } | |
| } | |
| } | |
| for (arelent **relp = reloc_vector; *relp != NULL; relp++) | |
| { | |
| arelent *rel; | |
| bfd_reloc_status_type r; | |
| char *err; | |
| unsigned int r_type; | |
| rel = *relp; | |
| if (rel->howto == NULL) | |
| { | |
| r = bfd_reloc_notsupported; | |
| r_type = ALPHA_R_IGNORE; | |
| } | |
| else | |
| { | |
| r = bfd_reloc_ok; | |
| r_type = rel->howto->type; | |
| } | |
| switch (r_type) | |
| { | |
| case ALPHA_R_IGNORE: | |
| rel->address += input_section->output_offset; | |
| break; | |
| case ALPHA_R_REFLONG: | |
| case ALPHA_R_REFQUAD: | |
| case ALPHA_R_BRADDR: | |
| case ALPHA_R_HINT: | |
| case ALPHA_R_SREL16: | |
| case ALPHA_R_SREL32: | |
| case ALPHA_R_SREL64: | |
| if (relocatable | |
| && ((*rel->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0) | |
| { | |
| rel->address += input_section->output_offset; | |
| break; | |
| } | |
| r = bfd_perform_relocation (input_bfd, rel, data, input_section, | |
| output_bfd, &err); | |
| break; | |
| case ALPHA_R_GPREL32: | |
| /* This relocation is used in a switch table. It is a 32 | |
| bit offset from the current GP value. We must adjust it | |
| by the different between the original GP value and the | |
| current GP value. The original GP value is stored in the | |
| addend. We adjust the addend and let | |
| bfd_perform_relocation finish the job. */ | |
| rel->addend -= gp; | |
| r = bfd_perform_relocation (input_bfd, rel, data, input_section, | |
| output_bfd, &err); | |
| if (r == bfd_reloc_ok && gp_undefined) | |
| { | |
| r = bfd_reloc_dangerous; | |
| err = (char *) _("GP relative relocation used when GP not defined"); | |
| } | |
| break; | |
| case ALPHA_R_LITERAL: | |
| /* This is a reference to a literal value, generally | |
| (always?) in the .lita section. This is a 16 bit GP | |
| relative relocation. Sometimes the subsequent reloc is a | |
| LITUSE reloc, which indicates how this reloc is used. | |
| This sometimes permits rewriting the two instructions | |
| referred to by the LITERAL and the LITUSE into different | |
| instructions which do not refer to .lita. This can save | |
| a memory reference, and permits removing a value from | |
| .lita thus saving GP relative space. | |
| We do not these optimizations. To do them we would need | |
| to arrange to link the .lita section first, so that by | |
| the time we got here we would know the final values to | |
| use. This would not be particularly difficult, but it is | |
| not currently implemented. */ | |
| rel->addend -= gp; | |
| r = bfd_perform_relocation (input_bfd, rel, data, input_section, | |
| output_bfd, &err); | |
| if (r == bfd_reloc_ok && gp_undefined) | |
| { | |
| r = bfd_reloc_dangerous; | |
| err = (char *) _("GP relative relocation used" | |
| " when GP not defined"); | |
| } | |
| break; | |
| case ALPHA_R_LITUSE: | |
| /* See ALPHA_R_LITERAL above for the uses of this reloc. It | |
| does not cause anything to happen, itself. */ | |
| rel->address += input_section->output_offset; | |
| break; | |
| case ALPHA_R_GPDISP: | |
| /* This marks the ldah of an ldah/lda pair which loads the | |
| gp register with the difference of the gp value and the | |
| current location. The second of the pair is r_size bytes | |
| ahead; it used to be marked with an ALPHA_R_IGNORE reloc, | |
| but that no longer happens in OSF/1 3.2. */ | |
| if (bfd_reloc_offset_in_range (rel->howto, input_bfd, input_section, | |
| rel->address) | |
| && bfd_reloc_offset_in_range (rel->howto, input_bfd, input_section, | |
| rel->address + rel->addend)) | |
| { | |
| /* Get the two instructions. */ | |
| bfd_byte *p = data + rel->address; | |
| bfd_vma insn1 = bfd_get_32 (input_bfd, p); | |
| bfd_vma insn2 = bfd_get_32 (input_bfd, p + rel->addend); | |
| BFD_ASSERT (((insn1 >> 26) & 0x3f) == 0x09); /* ldah */ | |
| BFD_ASSERT (((insn2 >> 26) & 0x3f) == 0x08); /* lda */ | |
| /* Get the existing addend. We must account for the sign | |
| extension done by lda and ldah. */ | |
| bfd_vma addend = (((((insn1 & 0xffff) ^ 0x8000) - 0x8000) << 16) | |
| + ((((insn2 & 0xffff) ^ 0x8000) - 0x8000))); | |
| /* The existing addend includes the different between the | |
| gp of the input BFD and the address in the input BFD. | |
| Subtract this out. */ | |
| addend -= ecoff_data (input_bfd)->gp - input_section->vma; | |
| /* Now add in the final gp value, and subtract out the | |
| final address. */ | |
| addend += gp - (input_section->output_section->vma | |
| + input_section->output_offset); | |
| /* Change the instructions, accounting for the sign | |
| extension, and write them out. */ | |
| insn1 = (insn1 & ~0xffff) | (((addend + 0x8000) >> 16) & 0xffff); | |
| insn2 = (insn2 & ~0xffff) | (addend & 0xffff); | |
| bfd_put_32 (input_bfd, insn1, p); | |
| bfd_put_32 (input_bfd, insn2, p + rel->addend); | |
| } | |
| else | |
| r = bfd_reloc_outofrange; | |
| rel->address += input_section->output_offset; | |
| break; | |
| case ALPHA_R_OP_PUSH: | |
| /* Push a value on the reloc evaluation stack. */ | |
| { | |
| asymbol *symbol; | |
| bfd_vma relocation; | |
| if (relocatable) | |
| { | |
| rel->address += input_section->output_offset; | |
| break; | |
| } | |
| /* Figure out the relocation of this symbol. */ | |
| symbol = *rel->sym_ptr_ptr; | |
| if (bfd_is_und_section (symbol->section)) | |
| r = bfd_reloc_undefined; | |
| if (bfd_is_com_section (symbol->section)) | |
| relocation = 0; | |
| else | |
| relocation = symbol->value; | |
| relocation += symbol->section->output_section->vma; | |
| relocation += symbol->section->output_offset; | |
| relocation += rel->addend; | |
| if (tos >= RELOC_STACKSIZE) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| stack[tos++] = relocation; | |
| } | |
| break; | |
| case ALPHA_R_OP_STORE: | |
| /* Store a value from the reloc stack into a bitfield. */ | |
| { | |
| if (relocatable) | |
| { | |
| rel->address += input_section->output_offset; | |
| break; | |
| } | |
| if (tos == 0) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| /* The offset and size in bits for this reloc are encoded | |
| into the addend field by alpha_adjust_reloc_in. */ | |
| unsigned int offset = (rel->addend >> 8) & 0xff; | |
| unsigned int size = rel->addend & 0xff; | |
| unsigned int startbyte = offset >> 3; | |
| unsigned int endbyte = (offset + size + 7) >> 3; | |
| unsigned int bytes = endbyte + 1 - startbyte; | |
| if (bytes <= 8 | |
| && rel->address + startbyte + bytes >= rel->address | |
| && (rel->address + startbyte + bytes | |
| <= bfd_get_section_limit_octets (input_bfd, input_section))) | |
| { | |
| uint64_t val = 0; | |
| for (int off = bytes - 1; off >= 0; --off) | |
| val = (val << 8) | data[rel->address + startbyte + off]; | |
| offset -= startbyte << 3; | |
| size -= startbyte << 3; | |
| uint64_t mask = (((uint64_t) 1 << size) - 1) << offset; | |
| val = (val & ~mask) | ((stack[--tos] << offset) & mask); | |
| for (unsigned int off = 0; off < bytes; ++off) | |
| { | |
| data[rel->address + startbyte + off] = val & 0xff; | |
| val >>= 8; | |
| } | |
| } | |
| else | |
| r = bfd_reloc_outofrange; | |
| } | |
| break; | |
| case ALPHA_R_OP_PSUB: | |
| /* Subtract a value from the top of the stack. */ | |
| { | |
| asymbol *symbol; | |
| bfd_vma relocation; | |
| if (relocatable) | |
| { | |
| rel->address += input_section->output_offset; | |
| break; | |
| } | |
| /* Figure out the relocation of this symbol. */ | |
| symbol = *rel->sym_ptr_ptr; | |
| if (bfd_is_und_section (symbol->section)) | |
| r = bfd_reloc_undefined; | |
| if (bfd_is_com_section (symbol->section)) | |
| relocation = 0; | |
| else | |
| relocation = symbol->value; | |
| relocation += symbol->section->output_section->vma; | |
| relocation += symbol->section->output_offset; | |
| relocation += rel->addend; | |
| if (tos == 0) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| stack[tos - 1] -= relocation; | |
| } | |
| break; | |
| case ALPHA_R_OP_PRSHIFT: | |
| /* Shift the value on the top of the stack. */ | |
| { | |
| asymbol *symbol; | |
| bfd_vma relocation; | |
| if (relocatable) | |
| { | |
| rel->address += input_section->output_offset; | |
| break; | |
| } | |
| /* Figure out the relocation of this symbol. */ | |
| symbol = *rel->sym_ptr_ptr; | |
| if (bfd_is_und_section (symbol->section)) | |
| r = bfd_reloc_undefined; | |
| if (bfd_is_com_section (symbol->section)) | |
| relocation = 0; | |
| else | |
| relocation = symbol->value; | |
| relocation += symbol->section->output_section->vma; | |
| relocation += symbol->section->output_offset; | |
| relocation += rel->addend; | |
| if (tos == 0) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| stack[tos - 1] >>= relocation; | |
| } | |
| break; | |
| case ALPHA_R_GPVALUE: | |
| /* I really don't know if this does the right thing. */ | |
| gp = rel->addend; | |
| gp_undefined = false; | |
| break; | |
| default: | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| if (relocatable) | |
| { | |
| asection *os = input_section->output_section; | |
| /* A partial link, so keep the relocs. */ | |
| os->orelocation[os->reloc_count] = rel; | |
| os->reloc_count++; | |
| } | |
| if (r != bfd_reloc_ok) | |
| { | |
| switch (r) | |
| { | |
| case bfd_reloc_undefined: | |
| (*link_info->callbacks->undefined_symbol) | |
| (link_info, bfd_asymbol_name (*rel->sym_ptr_ptr), | |
| input_bfd, input_section, rel->address, true); | |
| break; | |
| case bfd_reloc_dangerous: | |
| (*link_info->callbacks->reloc_dangerous) | |
| (link_info, err, input_bfd, input_section, rel->address); | |
| break; | |
| case bfd_reloc_overflow: | |
| (*link_info->callbacks->reloc_overflow) | |
| (link_info, NULL, bfd_asymbol_name (*rel->sym_ptr_ptr), | |
| rel->howto->name, rel->addend, input_bfd, | |
| input_section, rel->address); | |
| break; | |
| case bfd_reloc_outofrange: | |
| (*link_info->callbacks->einfo) | |
| /* xgettext:c-format */ | |
| (_("%X%P: %pB(%pA): relocation \"%pR\" goes out of range\n"), | |
| input_bfd, input_section, rel); | |
| goto error_return; | |
| case bfd_reloc_notsupported: | |
| (*link_info->callbacks->einfo) | |
| /* xgettext:c-format */ | |
| (_("%X%P: %pB(%pA): relocation \"%pR\" is not supported\n"), | |
| input_bfd, input_section, rel); | |
| goto error_return; | |
| default: | |
| (*link_info->callbacks->einfo) | |
| /* xgettext:c-format */ | |
| (_("%X%P: %pB(%pA): relocation \"%pR\"" | |
| " returns an unrecognized value %x\n"), | |
| input_bfd, input_section, rel, r); | |
| break; | |
| } | |
| } | |
| } | |
| if (tos != 0) | |
| goto error_return; | |
| successful_return: | |
| free (reloc_vector); | |
| return data; | |
| error_return: | |
| free (reloc_vector); | |
| if (orig_data == NULL) | |
| free (data); | |
| return NULL; | |
| } | |
| /* Get the howto structure for a generic reloc type. */ | |
| static reloc_howto_type * | |
| alpha_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, | |
| bfd_reloc_code_real_type code) | |
| { | |
| int alpha_type; | |
| switch (code) | |
| { | |
| case BFD_RELOC_32: | |
| alpha_type = ALPHA_R_REFLONG; | |
| break; | |
| case BFD_RELOC_64: | |
| case BFD_RELOC_CTOR: | |
| alpha_type = ALPHA_R_REFQUAD; | |
| break; | |
| case BFD_RELOC_GPREL32: | |
| alpha_type = ALPHA_R_GPREL32; | |
| break; | |
| case BFD_RELOC_ALPHA_LITERAL: | |
| alpha_type = ALPHA_R_LITERAL; | |
| break; | |
| case BFD_RELOC_ALPHA_LITUSE: | |
| alpha_type = ALPHA_R_LITUSE; | |
| break; | |
| case BFD_RELOC_ALPHA_GPDISP_HI16: | |
| alpha_type = ALPHA_R_GPDISP; | |
| break; | |
| case BFD_RELOC_ALPHA_GPDISP_LO16: | |
| alpha_type = ALPHA_R_IGNORE; | |
| break; | |
| case BFD_RELOC_23_PCREL_S2: | |
| alpha_type = ALPHA_R_BRADDR; | |
| break; | |
| case BFD_RELOC_ALPHA_HINT: | |
| alpha_type = ALPHA_R_HINT; | |
| break; | |
| case BFD_RELOC_16_PCREL: | |
| alpha_type = ALPHA_R_SREL16; | |
| break; | |
| case BFD_RELOC_32_PCREL: | |
| alpha_type = ALPHA_R_SREL32; | |
| break; | |
| case BFD_RELOC_64_PCREL: | |
| alpha_type = ALPHA_R_SREL64; | |
| break; | |
| default: | |
| return (reloc_howto_type *) NULL; | |
| } | |
| return &alpha_howto_table[alpha_type]; | |
| } | |
| static reloc_howto_type * | |
| alpha_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, | |
| const char *r_name) | |
| { | |
| unsigned int i; | |
| for (i = 0; | |
| i < sizeof (alpha_howto_table) / sizeof (alpha_howto_table[0]); | |
| i++) | |
| if (alpha_howto_table[i].name != NULL | |
| && strcasecmp (alpha_howto_table[i].name, r_name) == 0) | |
| return &alpha_howto_table[i]; | |
| return NULL; | |
| } | |
| /* A helper routine for alpha_relocate_section which converts an | |
| external reloc when generating relocatable output. Returns the | |
| relocation amount. */ | |
| static bfd_vma | |
| alpha_convert_external_reloc (bfd *output_bfd ATTRIBUTE_UNUSED, | |
| struct bfd_link_info *info, | |
| bfd *input_bfd, | |
| struct external_reloc *ext_rel, | |
| struct ecoff_link_hash_entry *h) | |
| { | |
| unsigned long r_symndx; | |
| bfd_vma relocation; | |
| BFD_ASSERT (bfd_link_relocatable (info)); | |
| if (h->root.type == bfd_link_hash_defined | |
| || h->root.type == bfd_link_hash_defweak) | |
| { | |
| asection *hsec; | |
| const char *name; | |
| /* This symbol is defined in the output. Convert the reloc from | |
| being against the symbol to being against the section. */ | |
| /* Clear the r_extern bit. */ | |
| ext_rel->r_bits[1] &=~ RELOC_BITS1_EXTERN_LITTLE; | |
| /* Compute a new r_symndx value. */ | |
| hsec = h->root.u.def.section; | |
| name = bfd_section_name (hsec->output_section); | |
| r_symndx = (unsigned long) -1; | |
| switch (name[1]) | |
| { | |
| case 'A': | |
| if (strcmp (name, "*ABS*") == 0) | |
| r_symndx = RELOC_SECTION_ABS; | |
| break; | |
| case 'b': | |
| if (strcmp (name, ".bss") == 0) | |
| r_symndx = RELOC_SECTION_BSS; | |
| break; | |
| case 'd': | |
| if (strcmp (name, ".data") == 0) | |
| r_symndx = RELOC_SECTION_DATA; | |
| break; | |
| case 'f': | |
| if (strcmp (name, ".fini") == 0) | |
| r_symndx = RELOC_SECTION_FINI; | |
| break; | |
| case 'i': | |
| if (strcmp (name, ".init") == 0) | |
| r_symndx = RELOC_SECTION_INIT; | |
| break; | |
| case 'l': | |
| if (strcmp (name, ".lita") == 0) | |
| r_symndx = RELOC_SECTION_LITA; | |
| else if (strcmp (name, ".lit8") == 0) | |
| r_symndx = RELOC_SECTION_LIT8; | |
| else if (strcmp (name, ".lit4") == 0) | |
| r_symndx = RELOC_SECTION_LIT4; | |
| break; | |
| case 'p': | |
| if (strcmp (name, ".pdata") == 0) | |
| r_symndx = RELOC_SECTION_PDATA; | |
| break; | |
| case 'r': | |
| if (strcmp (name, ".rdata") == 0) | |
| r_symndx = RELOC_SECTION_RDATA; | |
| else if (strcmp (name, ".rconst") == 0) | |
| r_symndx = RELOC_SECTION_RCONST; | |
| break; | |
| case 's': | |
| if (strcmp (name, ".sdata") == 0) | |
| r_symndx = RELOC_SECTION_SDATA; | |
| else if (strcmp (name, ".sbss") == 0) | |
| r_symndx = RELOC_SECTION_SBSS; | |
| break; | |
| case 't': | |
| if (strcmp (name, ".text") == 0) | |
| r_symndx = RELOC_SECTION_TEXT; | |
| break; | |
| case 'x': | |
| if (strcmp (name, ".xdata") == 0) | |
| r_symndx = RELOC_SECTION_XDATA; | |
| break; | |
| } | |
| if (r_symndx == (unsigned long) -1) | |
| abort (); | |
| /* Add the section VMA and the symbol value. */ | |
| relocation = (h->root.u.def.value | |
| + hsec->output_section->vma | |
| + hsec->output_offset); | |
| } | |
| else | |
| { | |
| /* Change the symndx value to the right one for | |
| the output BFD. */ | |
| r_symndx = h->indx; | |
| if (r_symndx == (unsigned long) -1) | |
| { | |
| /* Caller must give an error. */ | |
| r_symndx = 0; | |
| } | |
| relocation = 0; | |
| } | |
| /* Write out the new r_symndx value. */ | |
| H_PUT_32 (input_bfd, r_symndx, ext_rel->r_symndx); | |
| return relocation; | |
| } | |
| /* Relocate a section while linking an Alpha ECOFF file. This is | |
| quite similar to get_relocated_section_contents. Perhaps they | |
| could be combined somehow. */ | |
| static bool | |
| alpha_relocate_section (bfd *output_bfd, | |
| struct bfd_link_info *info, | |
| bfd *input_bfd, | |
| asection *input_section, | |
| bfd_byte *contents, | |
| void * external_relocs) | |
| { | |
| asection **symndx_to_section, *lita_sec; | |
| struct ecoff_link_hash_entry **sym_hashes; | |
| bfd_vma gp; | |
| bool gp_undefined; | |
| bfd_vma stack[RELOC_STACKSIZE]; | |
| int tos = 0; | |
| struct external_reloc *ext_rel; | |
| struct external_reloc *ext_rel_end; | |
| bfd_size_type amt; | |
| bool ret = true; | |
| /* We keep a table mapping the symndx found in an internal reloc to | |
| the appropriate section. This is faster than looking up the | |
| section by name each time. */ | |
| symndx_to_section = ecoff_data (input_bfd)->symndx_to_section; | |
| if (symndx_to_section == (asection **) NULL) | |
| { | |
| amt = NUM_RELOC_SECTIONS * sizeof (asection *); | |
| symndx_to_section = (asection **) bfd_alloc (input_bfd, amt); | |
| if (!symndx_to_section) | |
| return false; | |
| symndx_to_section[RELOC_SECTION_NONE] = NULL; | |
| symndx_to_section[RELOC_SECTION_TEXT] = | |
| bfd_get_section_by_name (input_bfd, ".text"); | |
| symndx_to_section[RELOC_SECTION_RDATA] = | |
| bfd_get_section_by_name (input_bfd, ".rdata"); | |
| symndx_to_section[RELOC_SECTION_DATA] = | |
| bfd_get_section_by_name (input_bfd, ".data"); | |
| symndx_to_section[RELOC_SECTION_SDATA] = | |
| bfd_get_section_by_name (input_bfd, ".sdata"); | |
| symndx_to_section[RELOC_SECTION_SBSS] = | |
| bfd_get_section_by_name (input_bfd, ".sbss"); | |
| symndx_to_section[RELOC_SECTION_BSS] = | |
| bfd_get_section_by_name (input_bfd, ".bss"); | |
| symndx_to_section[RELOC_SECTION_INIT] = | |
| bfd_get_section_by_name (input_bfd, ".init"); | |
| symndx_to_section[RELOC_SECTION_LIT8] = | |
| bfd_get_section_by_name (input_bfd, ".lit8"); | |
| symndx_to_section[RELOC_SECTION_LIT4] = | |
| bfd_get_section_by_name (input_bfd, ".lit4"); | |
| symndx_to_section[RELOC_SECTION_XDATA] = | |
| bfd_get_section_by_name (input_bfd, ".xdata"); | |
| symndx_to_section[RELOC_SECTION_PDATA] = | |
| bfd_get_section_by_name (input_bfd, ".pdata"); | |
| symndx_to_section[RELOC_SECTION_FINI] = | |
| bfd_get_section_by_name (input_bfd, ".fini"); | |
| symndx_to_section[RELOC_SECTION_LITA] = | |
| bfd_get_section_by_name (input_bfd, ".lita"); | |
| symndx_to_section[RELOC_SECTION_ABS] = bfd_abs_section_ptr; | |
| symndx_to_section[RELOC_SECTION_RCONST] = | |
| bfd_get_section_by_name (input_bfd, ".rconst"); | |
| ecoff_data (input_bfd)->symndx_to_section = symndx_to_section; | |
| } | |
| sym_hashes = ecoff_data (input_bfd)->sym_hashes; | |
| /* On the Alpha, the .lita section must be addressable by the global | |
| pointer. To support large programs, we need to allow multiple | |
| global pointers. This works as long as each input .lita section | |
| is <64KB big. This implies that when producing relocatable | |
| output, the .lita section is limited to 64KB. . */ | |
| lita_sec = symndx_to_section[RELOC_SECTION_LITA]; | |
| gp = _bfd_get_gp_value (output_bfd); | |
| if (! bfd_link_relocatable (info) && lita_sec != NULL) | |
| { | |
| struct ecoff_section_tdata *lita_sec_data; | |
| /* Make sure we have a section data structure to which we can | |
| hang on to the gp value we pick for the section. */ | |
| lita_sec_data = ecoff_section_data (input_bfd, lita_sec); | |
| if (lita_sec_data == NULL) | |
| { | |
| amt = sizeof (struct ecoff_section_tdata); | |
| lita_sec_data = ((struct ecoff_section_tdata *) | |
| bfd_zalloc (input_bfd, amt)); | |
| lita_sec->used_by_bfd = lita_sec_data; | |
| } | |
| if (lita_sec_data->gp != 0) | |
| { | |
| /* If we already assigned a gp to this section, we better | |
| stick with that value. */ | |
| gp = lita_sec_data->gp; | |
| } | |
| else | |
| { | |
| bfd_vma lita_vma; | |
| bfd_size_type lita_size; | |
| lita_vma = lita_sec->output_offset + lita_sec->output_section->vma; | |
| lita_size = lita_sec->size; | |
| if (gp == 0 | |
| || lita_vma < gp - 0x8000 | |
| || lita_vma + lita_size >= gp + 0x8000) | |
| { | |
| /* Either gp hasn't been set at all or the current gp | |
| cannot address this .lita section. In both cases we | |
| reset the gp to point into the "middle" of the | |
| current input .lita section. */ | |
| if (gp && !ecoff_data (output_bfd)->issued_multiple_gp_warning) | |
| { | |
| (*info->callbacks->warning) (info, | |
| _("using multiple gp values"), | |
| (char *) NULL, output_bfd, | |
| (asection *) NULL, (bfd_vma) 0); | |
| ecoff_data (output_bfd)->issued_multiple_gp_warning = true; | |
| } | |
| if (lita_vma < gp - 0x8000) | |
| gp = lita_vma + lita_size - 0x8000; | |
| else | |
| gp = lita_vma + 0x8000; | |
| } | |
| lita_sec_data->gp = gp; | |
| } | |
| _bfd_set_gp_value (output_bfd, gp); | |
| } | |
| gp_undefined = (gp == 0); | |
| BFD_ASSERT (bfd_header_little_endian (output_bfd)); | |
| BFD_ASSERT (bfd_header_little_endian (input_bfd)); | |
| ext_rel = (struct external_reloc *) external_relocs; | |
| ext_rel_end = ext_rel + input_section->reloc_count; | |
| for (; ext_rel < ext_rel_end; ext_rel++) | |
| { | |
| bfd_vma r_vaddr; | |
| unsigned long r_symndx; | |
| int r_type; | |
| int r_extern; | |
| int r_offset; | |
| int r_size; | |
| bool relocatep; | |
| bool adjust_addrp; | |
| bool gp_usedp; | |
| bfd_vma addend; | |
| bfd_reloc_status_type r; | |
| r_vaddr = H_GET_64 (input_bfd, ext_rel->r_vaddr); | |
| r_symndx = H_GET_32 (input_bfd, ext_rel->r_symndx); | |
| r_type = ((ext_rel->r_bits[0] & RELOC_BITS0_TYPE_LITTLE) | |
| >> RELOC_BITS0_TYPE_SH_LITTLE); | |
| r_extern = (ext_rel->r_bits[1] & RELOC_BITS1_EXTERN_LITTLE) != 0; | |
| r_offset = ((ext_rel->r_bits[1] & RELOC_BITS1_OFFSET_LITTLE) | |
| >> RELOC_BITS1_OFFSET_SH_LITTLE); | |
| /* Ignored the reserved bits. */ | |
| r_size = ((ext_rel->r_bits[3] & RELOC_BITS3_SIZE_LITTLE) | |
| >> RELOC_BITS3_SIZE_SH_LITTLE); | |
| relocatep = false; | |
| adjust_addrp = true; | |
| gp_usedp = false; | |
| addend = 0; | |
| r = bfd_reloc_ok; | |
| switch (r_type) | |
| { | |
| default: | |
| r = bfd_reloc_notsupported; | |
| break; | |
| case ALPHA_R_IGNORE: | |
| /* This reloc appears after a GPDISP reloc. On earlier | |
| versions of OSF/1, It marked the position of the second | |
| instruction to be altered by the GPDISP reloc, but it is | |
| not otherwise used for anything. For some reason, the | |
| address of the relocation does not appear to include the | |
| section VMA, unlike the other relocation types. */ | |
| if (bfd_link_relocatable (info)) | |
| H_PUT_64 (input_bfd, input_section->output_offset + r_vaddr, | |
| ext_rel->r_vaddr); | |
| adjust_addrp = false; | |
| break; | |
| case ALPHA_R_REFLONG: | |
| case ALPHA_R_REFQUAD: | |
| case ALPHA_R_HINT: | |
| relocatep = true; | |
| break; | |
| case ALPHA_R_BRADDR: | |
| case ALPHA_R_SREL16: | |
| case ALPHA_R_SREL32: | |
| case ALPHA_R_SREL64: | |
| if (r_extern) | |
| addend += - (r_vaddr + 4); | |
| relocatep = true; | |
| break; | |
| case ALPHA_R_GPREL32: | |
| /* This relocation is used in a switch table. It is a 32 | |
| bit offset from the current GP value. We must adjust it | |
| by the different between the original GP value and the | |
| current GP value. */ | |
| relocatep = true; | |
| addend = ecoff_data (input_bfd)->gp - gp; | |
| gp_usedp = true; | |
| break; | |
| case ALPHA_R_LITERAL: | |
| /* This is a reference to a literal value, generally | |
| (always?) in the .lita section. This is a 16 bit GP | |
| relative relocation. Sometimes the subsequent reloc is a | |
| LITUSE reloc, which indicates how this reloc is used. | |
| This sometimes permits rewriting the two instructions | |
| referred to by the LITERAL and the LITUSE into different | |
| instructions which do not refer to .lita. This can save | |
| a memory reference, and permits removing a value from | |
| .lita thus saving GP relative space. | |
| We do not these optimizations. To do them we would need | |
| to arrange to link the .lita section first, so that by | |
| the time we got here we would know the final values to | |
| use. This would not be particularly difficult, but it is | |
| not currently implemented. */ | |
| relocatep = true; | |
| addend = ecoff_data (input_bfd)->gp - gp; | |
| gp_usedp = true; | |
| break; | |
| case ALPHA_R_LITUSE: | |
| /* See ALPHA_R_LITERAL above for the uses of this reloc. It | |
| does not cause anything to happen, itself. */ | |
| break; | |
| case ALPHA_R_GPDISP: | |
| /* This marks the ldah of an ldah/lda pair which loads the | |
| gp register with the difference of the gp value and the | |
| current location. The second of the pair is r_symndx | |
| bytes ahead. It used to be marked with an ALPHA_R_IGNORE | |
| reloc, but OSF/1 3.2 no longer does that. */ | |
| if (r_vaddr >= input_section->vma | |
| && r_vaddr - input_section->vma < input_section->size | |
| && input_section->size - (r_vaddr - input_section->vma) > r_symndx | |
| && (input_section->size - (r_vaddr - input_section->vma) | |
| - r_symndx >= 4)) | |
| { | |
| /* Get the two instructions. */ | |
| bfd_byte *p = contents + r_vaddr - input_section->vma; | |
| bfd_vma insn1 = bfd_get_32 (input_bfd, p); | |
| bfd_vma insn2 = bfd_get_32 (input_bfd, p + r_symndx); | |
| BFD_ASSERT (((insn1 >> 26) & 0x3f) == 0x09); /* ldah */ | |
| BFD_ASSERT (((insn2 >> 26) & 0x3f) == 0x08); /* lda */ | |
| /* Get the existing addend. We must account for the sign | |
| extension done by lda and ldah. */ | |
| addend = (((((insn1 & 0xffff) ^ 0x8000) - 0x8000) << 16) | |
| + (((insn2 & 0xffff) ^ 0x8000) - 0x8000)); | |
| /* The existing addend includes the difference between the | |
| gp of the input BFD and the address in the input BFD. | |
| We want to change this to the difference between the | |
| final GP and the final address. */ | |
| addend -= ecoff_data (input_bfd)->gp - input_section->vma; | |
| addend += gp - (input_section->output_section->vma | |
| + input_section->output_offset); | |
| /* Change the instructions, accounting for the sign | |
| extension, and write them out. */ | |
| insn1 = (insn1 & ~0xffff) | (((addend + 0x8000) >> 16) & 0xffff); | |
| insn2 = (insn2 & ~0xffff) | (addend & 0xffff); | |
| bfd_put_32 (input_bfd, insn1, p); | |
| bfd_put_32 (input_bfd, insn2, p + r_symndx); | |
| gp_usedp = true; | |
| } | |
| else | |
| r = bfd_reloc_outofrange; | |
| break; | |
| case ALPHA_R_OP_PUSH: | |
| case ALPHA_R_OP_PSUB: | |
| case ALPHA_R_OP_PRSHIFT: | |
| /* Manipulate values on the reloc evaluation stack. The | |
| r_vaddr field is not an address in input_section, it is | |
| the current value (including any addend) of the object | |
| being used. */ | |
| if (! r_extern) | |
| { | |
| asection *s; | |
| s = symndx_to_section[r_symndx]; | |
| if (s == NULL) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| addend = s->output_section->vma + s->output_offset - s->vma; | |
| } | |
| else | |
| { | |
| struct ecoff_link_hash_entry *h; | |
| h = sym_hashes[r_symndx]; | |
| if (h == NULL) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| if (! bfd_link_relocatable (info)) | |
| { | |
| if (h->root.type == bfd_link_hash_defined | |
| || h->root.type == bfd_link_hash_defweak) | |
| addend = (h->root.u.def.value | |
| + h->root.u.def.section->output_section->vma | |
| + h->root.u.def.section->output_offset); | |
| else | |
| { | |
| /* Note that we pass the address as 0, since we | |
| do not have a meaningful number for the | |
| location within the section that is being | |
| relocated. */ | |
| (*info->callbacks->undefined_symbol) | |
| (info, h->root.root.string, input_bfd, | |
| input_section, (bfd_vma) 0, true); | |
| addend = 0; | |
| } | |
| } | |
| else | |
| { | |
| if (h->root.type != bfd_link_hash_defined | |
| && h->root.type != bfd_link_hash_defweak | |
| && h->indx == -1) | |
| { | |
| /* This symbol is not being written out. Pass | |
| the address as 0, as with undefined_symbol, | |
| above. */ | |
| (*info->callbacks->unattached_reloc) | |
| (info, h->root.root.string, | |
| input_bfd, input_section, (bfd_vma) 0); | |
| } | |
| addend = alpha_convert_external_reloc (output_bfd, info, | |
| input_bfd, | |
| ext_rel, h); | |
| } | |
| } | |
| addend += r_vaddr; | |
| if (bfd_link_relocatable (info)) | |
| { | |
| /* Adjust r_vaddr by the addend. */ | |
| H_PUT_64 (input_bfd, addend, ext_rel->r_vaddr); | |
| } | |
| else | |
| { | |
| switch (r_type) | |
| { | |
| case ALPHA_R_OP_PUSH: | |
| if (tos >= RELOC_STACKSIZE) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| stack[tos++] = addend; | |
| break; | |
| case ALPHA_R_OP_PSUB: | |
| if (tos == 0) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| stack[tos - 1] -= addend; | |
| break; | |
| case ALPHA_R_OP_PRSHIFT: | |
| if (tos == 0) | |
| { | |
| r = bfd_reloc_notsupported; | |
| break; | |
| } | |
| stack[tos - 1] >>= addend; | |
| break; | |
| } | |
| } | |
| adjust_addrp = false; | |
| break; | |
| case ALPHA_R_OP_STORE: | |
| /* Store a value from the reloc stack into a bitfield. If | |
| we are generating relocatable output, all we do is | |
| adjust the address of the reloc. */ | |
| if (! bfd_link_relocatable (info)) | |
| { | |
| unsigned int startbyte = r_offset >> 3; | |
| unsigned int endbyte = (r_offset + r_size + 7) >> 3; | |
| unsigned int bytes = endbyte + 1 - startbyte; | |
| if (bytes <= 8 | |
| && r_vaddr >= input_section->vma | |
| && r_vaddr - input_section->vma < input_section->size | |
| && (input_section->size - (r_vaddr - input_section->vma) | |
| >= startbyte + bytes)) | |
| { | |
| bfd_byte *p = contents + (r_vaddr - input_section->vma); | |
| uint64_t val = 0; | |
| for (int off = bytes - 1; off >= 0; --off) | |
| val = (val << 8) | p[startbyte + off]; | |
| r_offset -= startbyte << 3; | |
| r_size -= startbyte << 3; | |
| uint64_t mask = (((uint64_t) 1 << r_size) - 1) << r_offset; | |
| val = (val & ~mask) | ((stack[--tos] << r_offset) & mask); | |
| for (unsigned int off = 0; off < bytes; ++off) | |
| { | |
| p[startbyte + off] = val & 0xff; | |
| val >>= 8; | |
| } | |
| } | |
| else | |
| r = bfd_reloc_outofrange; | |
| } | |
| break; | |
| case ALPHA_R_GPVALUE: | |
| /* I really don't know if this does the right thing. */ | |
| gp = ecoff_data (input_bfd)->gp + r_symndx; | |
| gp_undefined = false; | |
| break; | |
| } | |
| if (relocatep && r == bfd_reloc_ok) | |
| { | |
| reloc_howto_type *howto; | |
| struct ecoff_link_hash_entry *h = NULL; | |
| asection *s = NULL; | |
| bfd_vma relocation; | |
| /* Perform a relocation. */ | |
| howto = &alpha_howto_table[r_type]; | |
| if (r_extern) | |
| { | |
| h = sym_hashes[r_symndx]; | |
| /* If h is NULL, that means that there is a reloc | |
| against an external symbol which we thought was just | |
| a debugging symbol. This should not happen. */ | |
| if (h == NULL) | |
| r = bfd_reloc_notsupported; | |
| } | |
| else | |
| { | |
| if (r_symndx >= NUM_RELOC_SECTIONS) | |
| s = NULL; | |
| else | |
| s = symndx_to_section[r_symndx]; | |
| if (s == NULL) | |
| r = bfd_reloc_notsupported; | |
| } | |
| if (r != bfd_reloc_ok) | |
| ; | |
| else if (bfd_link_relocatable (info)) | |
| { | |
| /* We are generating relocatable output, and must | |
| convert the existing reloc. */ | |
| if (r_extern) | |
| { | |
| if (h->root.type != bfd_link_hash_defined | |
| && h->root.type != bfd_link_hash_defweak | |
| && h->indx == -1) | |
| { | |
| /* This symbol is not being written out. */ | |
| (*info->callbacks->unattached_reloc) | |
| (info, h->root.root.string, input_bfd, | |
| input_section, r_vaddr - input_section->vma); | |
| } | |
| relocation = alpha_convert_external_reloc (output_bfd, | |
| info, | |
| input_bfd, | |
| ext_rel, | |
| h); | |
| } | |
| else | |
| { | |
| /* This is a relocation against a section. Adjust | |
| the value by the amount the section moved. */ | |
| relocation = (s->output_section->vma | |
| + s->output_offset | |
| - s->vma); | |
| } | |
| /* If this is PC relative, the existing object file | |
| appears to already have the reloc worked out. We | |
| must subtract out the old value and add in the new | |
| one. */ | |
| if (howto->pc_relative) | |
| relocation -= (input_section->output_section->vma | |
| + input_section->output_offset | |
| - input_section->vma); | |
| /* Put in any addend. */ | |
| relocation += addend; | |
| /* Adjust the contents. */ | |
| r = _bfd_relocate_contents (howto, input_bfd, relocation, | |
| (contents | |
| + r_vaddr | |
| - input_section->vma)); | |
| } | |
| else | |
| { | |
| /* We are producing a final executable. */ | |
| if (r_extern) | |
| { | |
| /* This is a reloc against a symbol. */ | |
| if (h->root.type == bfd_link_hash_defined | |
| || h->root.type == bfd_link_hash_defweak) | |
| { | |
| asection *hsec; | |
| hsec = h->root.u.def.section; | |
| relocation = (h->root.u.def.value | |
| + hsec->output_section->vma | |
| + hsec->output_offset); | |
| } | |
| else | |
| r = bfd_reloc_undefined; | |
| } | |
| else | |
| { | |
| /* This is a reloc against a section. */ | |
| relocation = (s->output_section->vma | |
| + s->output_offset | |
| - s->vma); | |
| /* Adjust a PC relative relocation by removing the | |
| reference to the original source section. */ | |
| if (howto->pc_relative) | |
| relocation += input_section->vma; | |
| } | |
| if (r == bfd_reloc_ok) | |
| r = _bfd_final_link_relocate (howto, | |
| input_bfd, | |
| input_section, | |
| contents, | |
| r_vaddr - input_section->vma, | |
| relocation, | |
| addend); | |
| } | |
| } | |
| if (bfd_link_relocatable (info) && adjust_addrp) | |
| { | |
| /* Change the address of the relocation. */ | |
| H_PUT_64 (input_bfd, | |
| (input_section->output_section->vma | |
| + input_section->output_offset | |
| - input_section->vma | |
| + r_vaddr), | |
| ext_rel->r_vaddr); | |
| } | |
| if (gp_usedp && gp_undefined) | |
| { | |
| r = bfd_reloc_dangerous; | |
| /* Only give the error once per link. */ | |
| gp = 4; | |
| _bfd_set_gp_value (output_bfd, gp); | |
| gp_undefined = false; | |
| } | |
| if (r != bfd_reloc_ok) | |
| { | |
| switch (r) | |
| { | |
| case bfd_reloc_overflow: | |
| { | |
| const char *name; | |
| if (r_extern) | |
| name = sym_hashes[r_symndx]->root.root.string; | |
| else | |
| name = bfd_section_name (symndx_to_section[r_symndx]); | |
| (*info->callbacks->reloc_overflow) | |
| (info, NULL, name, alpha_howto_table[r_type].name, | |
| (bfd_vma) 0, input_bfd, input_section, | |
| r_vaddr - input_section->vma); | |
| } | |
| break; | |
| case bfd_reloc_outofrange: | |
| (*info->callbacks->einfo) | |
| /* xgettext:c-format */ | |
| (_("%X%P: %pB(%pA): relocation out of range\n"), | |
| input_bfd, input_section); | |
| break; | |
| case bfd_reloc_undefined: | |
| (*info->callbacks->undefined_symbol) | |
| (info, sym_hashes[r_symndx]->root.root.string, | |
| input_bfd, input_section, | |
| r_vaddr - input_section->vma, true); | |
| break; | |
| case bfd_reloc_notsupported: | |
| (*info->callbacks->einfo) | |
| /* xgettext:c-format */ | |
| (_("%X%P: %pB(%pA): relocation is not supported\n"), | |
| input_bfd, input_section); | |
| break; | |
| case bfd_reloc_dangerous: | |
| (*info->callbacks->reloc_dangerous) | |
| (info, _("GP relative relocation used when GP not defined"), | |
| input_bfd, input_section, r_vaddr - input_section->vma); | |
| break; | |
| default: | |
| abort (); | |
| } | |
| ret = false; | |
| } | |
| } | |
| if (tos != 0) | |
| ret = false; | |
| return ret; | |
| } | |
| /* Do final adjustments to the filehdr and the aouthdr. This routine | |
| sets the dynamic bits in the file header. */ | |
| static bool | |
| alpha_adjust_headers (bfd *abfd, | |
| struct internal_filehdr *fhdr, | |
| struct internal_aouthdr *ahdr ATTRIBUTE_UNUSED) | |
| { | |
| if ((abfd->flags & (DYNAMIC | EXEC_P)) == (DYNAMIC | EXEC_P)) | |
| fhdr->f_flags |= F_ALPHA_CALL_SHARED; | |
| else if ((abfd->flags & DYNAMIC) != 0) | |
| fhdr->f_flags |= F_ALPHA_SHARABLE; | |
| return true; | |
| } | |
| /* Archive handling. In OSF/1 (or Digital Unix) v3.2, Digital | |
| introduced archive packing, in which the elements in an archive are | |
| optionally compressed using a simple dictionary scheme. We know | |
| how to read such archives, but we don't write them. */ | |
| #define alpha_ecoff_slurp_armap _bfd_ecoff_slurp_armap | |
| #define alpha_ecoff_slurp_extended_name_table \ | |
| _bfd_ecoff_slurp_extended_name_table | |
| #define alpha_ecoff_construct_extended_name_table \ | |
| _bfd_ecoff_construct_extended_name_table | |
| #define alpha_ecoff_truncate_arname _bfd_ecoff_truncate_arname | |
| #define alpha_ecoff_write_armap _bfd_ecoff_write_armap | |
| #define alpha_ecoff_write_ar_hdr _bfd_generic_write_ar_hdr | |
| #define alpha_ecoff_generic_stat_arch_elt _bfd_ecoff_generic_stat_arch_elt | |
| #define alpha_ecoff_update_armap_timestamp _bfd_ecoff_update_armap_timestamp | |
| /* A compressed file uses this instead of ARFMAG. */ | |
| #define ARFZMAG "Z\012" | |
| /* Read an archive header. This is like the standard routine, but it | |
| also accepts ARFZMAG. */ | |
| static void * | |
| alpha_ecoff_read_ar_hdr (bfd *abfd) | |
| { | |
| struct areltdata *ret; | |
| struct ar_hdr *h; | |
| ret = (struct areltdata *) _bfd_generic_read_ar_hdr_mag (abfd, ARFZMAG); | |
| if (ret == NULL) | |
| return NULL; | |
| h = (struct ar_hdr *) ret->arch_header; | |
| if (strncmp (h->ar_fmag, ARFZMAG, 2) == 0) | |
| { | |
| bfd_byte ab[8]; | |
| /* This is a compressed file. We must set the size correctly. | |
| The size is the eight bytes after the dummy file header. */ | |
| if (bfd_seek (abfd, FILHSZ, SEEK_CUR) != 0 | |
| || bfd_read (ab, 8, abfd) != 8 | |
| || bfd_seek (abfd, -(FILHSZ + 8), SEEK_CUR) != 0) | |
| { | |
| free (ret); | |
| return NULL; | |
| } | |
| ret->parsed_size = H_GET_64 (abfd, ab); | |
| } | |
| return ret; | |
| } | |
| /* Get an archive element at a specified file position. This is where | |
| we uncompress the archive element if necessary. */ | |
| static bfd * | |
| alpha_ecoff_get_elt_at_filepos (bfd *archive, file_ptr filepos, | |
| struct bfd_link_info *info) | |
| { | |
| bfd *nbfd = NULL; | |
| struct areltdata *tdata; | |
| struct ar_hdr *hdr; | |
| bfd_byte ab[8]; | |
| bfd_size_type size; | |
| bfd_byte *buf, *p; | |
| struct bfd_in_memory *bim; | |
| ufile_ptr filesize; | |
| buf = NULL; | |
| nbfd = _bfd_get_elt_at_filepos (archive, filepos, info); | |
| if (nbfd == NULL) | |
| goto error_return; | |
| if ((nbfd->flags & BFD_IN_MEMORY) != 0) | |
| { | |
| /* We have already expanded this BFD. */ | |
| return nbfd; | |
| } | |
| tdata = (struct areltdata *) nbfd->arelt_data; | |
| hdr = (struct ar_hdr *) tdata->arch_header; | |
| if (strncmp (hdr->ar_fmag, ARFZMAG, 2) != 0) | |
| return nbfd; | |
| /* We must uncompress this element. We do this by copying it into a | |
| memory buffer, and making bfd_read and bfd_seek use that buffer. | |
| This can use a lot of memory, but it's simpler than getting a | |
| temporary file, making that work with the file descriptor caching | |
| code, and making sure that it is deleted at all appropriate | |
| times. It can be changed if it ever becomes important. */ | |
| /* The compressed file starts with a dummy ECOFF file header. */ | |
| if (bfd_seek (nbfd, FILHSZ, SEEK_SET) != 0) | |
| goto error_return; | |
| /* The next eight bytes are the real file size. */ | |
| if (bfd_read (ab, 8, nbfd) != 8) | |
| goto error_return; | |
| size = H_GET_64 (nbfd, ab); | |
| /* The decompression algorithm will at most expand by eight times. */ | |
| filesize = bfd_get_file_size (archive); | |
| if (filesize != 0 && size / 8 > filesize) | |
| { | |
| bfd_set_error (bfd_error_malformed_archive); | |
| goto error_return; | |
| } | |
| if (size != 0) | |
| { | |
| bfd_size_type left; | |
| bfd_byte dict[4096]; | |
| unsigned int h; | |
| bfd_byte b; | |
| buf = (bfd_byte *) bfd_malloc (size); | |
| if (buf == NULL) | |
| goto error_return; | |
| p = buf; | |
| left = size; | |
| /* I don't know what the next eight bytes are for. */ | |
| if (bfd_read (ab, 8, nbfd) != 8) | |
| goto error_return; | |
| /* This is the uncompression algorithm. It's a simple | |
| dictionary based scheme in which each character is predicted | |
| by a hash of the previous three characters. A control byte | |
| indicates whether the character is predicted or whether it | |
| appears in the input stream; each control byte manages the | |
| next eight bytes in the output stream. */ | |
| memset (dict, 0, sizeof dict); | |
| h = 0; | |
| while (bfd_read (&b, 1, nbfd) == 1) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < 8; i++, b >>= 1) | |
| { | |
| bfd_byte n; | |
| if ((b & 1) == 0) | |
| n = dict[h]; | |
| else | |
| { | |
| if (bfd_read (&n, 1, nbfd) != 1) | |
| goto error_return; | |
| dict[h] = n; | |
| } | |
| *p++ = n; | |
| --left; | |
| if (left == 0) | |
| break; | |
| h <<= 4; | |
| h ^= n; | |
| h &= sizeof dict - 1; | |
| } | |
| if (left == 0) | |
| break; | |
| } | |
| } | |
| /* Now the uncompressed file contents are in buf. */ | |
| bim = ((struct bfd_in_memory *) | |
| bfd_malloc ((bfd_size_type) sizeof (struct bfd_in_memory))); | |
| if (bim == NULL) | |
| goto error_return; | |
| bim->size = size; | |
| bim->buffer = buf; | |
| nbfd->mtime_set = true; | |
| nbfd->mtime = strtol (hdr->ar_date, (char **) NULL, 10); | |
| nbfd->flags |= BFD_IN_MEMORY; | |
| nbfd->iostream = bim; | |
| nbfd->iovec = &_bfd_memory_iovec; | |
| nbfd->origin = 0; | |
| nbfd->size = 0; | |
| BFD_ASSERT (! nbfd->cacheable); | |
| return nbfd; | |
| error_return: | |
| free (buf); | |
| if (nbfd != NULL) | |
| bfd_close (nbfd); | |
| return NULL; | |
| } | |
| /* Open the next archived file. */ | |
| static bfd * | |
| alpha_ecoff_openr_next_archived_file (bfd *archive, bfd *last_file) | |
| { | |
| ufile_ptr filestart; | |
| if (last_file == NULL) | |
| filestart = bfd_ardata (archive)->first_file_filepos; | |
| else | |
| { | |
| struct areltdata *t; | |
| struct ar_hdr *h; | |
| bfd_size_type size; | |
| /* We can't use arelt_size here, because that uses parsed_size, | |
| which is the uncompressed size. We need the compressed size. */ | |
| t = (struct areltdata *) last_file->arelt_data; | |
| h = (struct ar_hdr *) t->arch_header; | |
| size = strtol (h->ar_size, (char **) NULL, 10); | |
| /* Pad to an even boundary... | |
| Note that last_file->origin can be odd in the case of | |
| BSD-4.4-style element with a long odd size. */ | |
| filestart = last_file->proxy_origin + size; | |
| filestart += filestart % 2; | |
| if (filestart < last_file->proxy_origin) | |
| { | |
| /* Prevent looping. See PR19256. */ | |
| bfd_set_error (bfd_error_malformed_archive); | |
| return NULL; | |
| } | |
| } | |
| return alpha_ecoff_get_elt_at_filepos (archive, filestart, NULL); | |
| } | |
| /* Open the archive file given an index into the armap. */ | |
| static bfd * | |
| alpha_ecoff_get_elt_at_index (bfd *abfd, symindex sym_index) | |
| { | |
| carsym *entry; | |
| entry = bfd_ardata (abfd)->symdefs + sym_index; | |
| return alpha_ecoff_get_elt_at_filepos (abfd, entry->file_offset, | |
| NULL); | |
| } | |
| static void | |
| alpha_ecoff_swap_coff_aux_in (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *ext1 ATTRIBUTE_UNUSED, | |
| int type ATTRIBUTE_UNUSED, | |
| int in_class ATTRIBUTE_UNUSED, | |
| int indx ATTRIBUTE_UNUSED, | |
| int numaux ATTRIBUTE_UNUSED, | |
| void *in1 ATTRIBUTE_UNUSED) | |
| { | |
| } | |
| static void | |
| alpha_ecoff_swap_coff_sym_in (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *ext1 ATTRIBUTE_UNUSED, | |
| void *in1 ATTRIBUTE_UNUSED) | |
| { | |
| } | |
| static void | |
| alpha_ecoff_swap_coff_lineno_in (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *ext1 ATTRIBUTE_UNUSED, | |
| void *in1 ATTRIBUTE_UNUSED) | |
| { | |
| } | |
| static unsigned int | |
| alpha_ecoff_swap_coff_aux_out (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *inp ATTRIBUTE_UNUSED, | |
| int type ATTRIBUTE_UNUSED, | |
| int in_class ATTRIBUTE_UNUSED, | |
| int indx ATTRIBUTE_UNUSED, | |
| int numaux ATTRIBUTE_UNUSED, | |
| void *extp ATTRIBUTE_UNUSED) | |
| { | |
| return 0; | |
| } | |
| static unsigned int | |
| alpha_ecoff_swap_coff_sym_out (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *inp ATTRIBUTE_UNUSED, | |
| void *extp ATTRIBUTE_UNUSED) | |
| { | |
| return 0; | |
| } | |
| static unsigned int | |
| alpha_ecoff_swap_coff_lineno_out (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *inp ATTRIBUTE_UNUSED, | |
| void *extp ATTRIBUTE_UNUSED) | |
| { | |
| return 0; | |
| } | |
| static unsigned int | |
| alpha_ecoff_swap_coff_reloc_out (bfd *abfd ATTRIBUTE_UNUSED, | |
| void *inp ATTRIBUTE_UNUSED, | |
| void *extp ATTRIBUTE_UNUSED) | |
| { | |
| return 0; | |
| } | |
| /* This is the ECOFF backend structure. The backend field of the | |
| target vector points to this. */ | |
| static const struct ecoff_backend_data alpha_ecoff_backend_data = | |
| { | |
| /* COFF backend structure. */ | |
| { | |
| alpha_ecoff_swap_coff_aux_in, alpha_ecoff_swap_coff_sym_in, | |
| alpha_ecoff_swap_coff_lineno_in, alpha_ecoff_swap_coff_aux_out, | |
| alpha_ecoff_swap_coff_sym_out, alpha_ecoff_swap_coff_lineno_out, | |
| alpha_ecoff_swap_coff_reloc_out, | |
| alpha_ecoff_swap_filehdr_out, alpha_ecoff_swap_aouthdr_out, | |
| alpha_ecoff_swap_scnhdr_out, | |
| FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, FILNMLEN, true, | |
| ECOFF_NO_LONG_SECTION_NAMES, 4, false, 2, 32768, | |
| alpha_ecoff_swap_filehdr_in, alpha_ecoff_swap_aouthdr_in, | |
| alpha_ecoff_swap_scnhdr_in, NULL, | |
| alpha_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook, | |
| alpha_ecoff_mkobject_hook, _bfd_ecoff_styp_to_sec_flags, | |
| _bfd_ecoff_set_alignment_hook, _bfd_ecoff_slurp_symbol_table, | |
| NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
| NULL, NULL, NULL, NULL | |
| }, | |
| /* Supported architecture. */ | |
| bfd_arch_alpha, | |
| /* Initial portion of armap string. */ | |
| "________64", | |
| /* The page boundary used to align sections in a demand-paged | |
| executable file. E.g., 0x1000. */ | |
| 0x2000, | |
| /* TRUE if the .rdata section is part of the text segment, as on the | |
| Alpha. FALSE if .rdata is part of the data segment, as on the | |
| MIPS. */ | |
| true, | |
| /* Bitsize of constructor entries. */ | |
| 64, | |
| /* Reloc to use for constructor entries. */ | |
| &alpha_howto_table[ALPHA_R_REFQUAD], | |
| { | |
| /* Symbol table magic number. */ | |
| magicSym2, | |
| /* Alignment of debugging information. E.g., 4. */ | |
| 8, | |
| /* Sizes of external symbolic information. */ | |
| sizeof (struct hdr_ext), | |
| sizeof (struct dnr_ext), | |
| sizeof (struct pdr_ext), | |
| sizeof (struct sym_ext), | |
| sizeof (struct opt_ext), | |
| sizeof (struct fdr_ext), | |
| sizeof (struct rfd_ext), | |
| sizeof (struct ext_ext), | |
| /* Functions to swap in external symbolic data. */ | |
| ecoff_swap_hdr_in, | |
| ecoff_swap_dnr_in, | |
| ecoff_swap_pdr_in, | |
| ecoff_swap_sym_in, | |
| ecoff_swap_opt_in, | |
| ecoff_swap_fdr_in, | |
| ecoff_swap_rfd_in, | |
| ecoff_swap_ext_in, | |
| _bfd_ecoff_swap_tir_in, | |
| _bfd_ecoff_swap_rndx_in, | |
| /* Functions to swap out external symbolic data. */ | |
| ecoff_swap_hdr_out, | |
| ecoff_swap_dnr_out, | |
| ecoff_swap_pdr_out, | |
| ecoff_swap_sym_out, | |
| ecoff_swap_opt_out, | |
| ecoff_swap_fdr_out, | |
| ecoff_swap_rfd_out, | |
| ecoff_swap_ext_out, | |
| _bfd_ecoff_swap_tir_out, | |
| _bfd_ecoff_swap_rndx_out, | |
| /* Function to read in symbolic data. */ | |
| _bfd_ecoff_slurp_symbolic_info | |
| }, | |
| /* External reloc size. */ | |
| RELSZ, | |
| /* Reloc swapping functions. */ | |
| alpha_ecoff_swap_reloc_in, | |
| alpha_ecoff_swap_reloc_out, | |
| /* Backend reloc tweaking. */ | |
| alpha_adjust_reloc_in, | |
| alpha_adjust_reloc_out, | |
| /* Relocate section contents while linking. */ | |
| alpha_relocate_section, | |
| /* Do final adjustments to filehdr and aouthdr. */ | |
| alpha_adjust_headers, | |
| /* Read an element from an archive at a given file position. */ | |
| alpha_ecoff_get_elt_at_filepos | |
| }; | |
| /* Looking up a reloc type is Alpha specific. */ | |
| #define _bfd_ecoff_bfd_reloc_type_lookup alpha_bfd_reloc_type_lookup | |
| #define _bfd_ecoff_bfd_reloc_name_lookup \ | |
| alpha_bfd_reloc_name_lookup | |
| /* So is getting relocated section contents. */ | |
| #define _bfd_ecoff_bfd_get_relocated_section_contents \ | |
| alpha_ecoff_get_relocated_section_contents | |
| /* Handling file windows is generic. */ | |
| #define _bfd_ecoff_get_section_contents_in_window \ | |
| _bfd_generic_get_section_contents_in_window | |
| /* Input section flag lookup is generic. */ | |
| #define _bfd_ecoff_bfd_lookup_section_flags bfd_generic_lookup_section_flags | |
| /* Relaxing sections is generic. */ | |
| #define _bfd_ecoff_bfd_relax_section bfd_generic_relax_section | |
| #define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections | |
| #define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections | |
| #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section | |
| #define _bfd_ecoff_bfd_group_name bfd_generic_group_name | |
| #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group | |
| #define _bfd_ecoff_section_already_linked \ | |
| _bfd_coff_section_already_linked | |
| #define _bfd_ecoff_bfd_define_common_symbol bfd_generic_define_common_symbol | |
| #define _bfd_ecoff_bfd_link_hide_symbol _bfd_generic_link_hide_symbol | |
| #define _bfd_ecoff_bfd_define_start_stop bfd_generic_define_start_stop | |
| #define _bfd_ecoff_bfd_link_check_relocs _bfd_generic_link_check_relocs | |
| /* Installing internal relocations in a section is also generic. */ | |
| #define _bfd_ecoff_set_reloc _bfd_generic_set_reloc | |
| const bfd_target alpha_ecoff_le_vec = | |
| { | |
| "ecoff-littlealpha", /* name */ | |
| bfd_target_ecoff_flavour, | |
| BFD_ENDIAN_LITTLE, /* data byte order is little */ | |
| BFD_ENDIAN_LITTLE, /* header byte order is little */ | |
| (HAS_RELOC | EXEC_P /* object flags */ | |
| | HAS_LINENO | HAS_DEBUG | |
| | HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), | |
| (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | |
| | SEC_DATA | SEC_SMALL_DATA), | |
| 0, /* leading underscore */ | |
| ' ', /* ar_pad_char */ | |
| 15, /* ar_max_namelen */ | |
| 0, /* match priority. */ | |
| TARGET_KEEP_UNUSED_SECTION_SYMBOLS, /* keep unused section symbols. */ | |
| bfd_getl64, bfd_getl_signed_64, bfd_putl64, | |
| bfd_getl32, bfd_getl_signed_32, bfd_putl32, | |
| bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */ | |
| bfd_getl64, bfd_getl_signed_64, bfd_putl64, | |
| bfd_getl32, bfd_getl_signed_32, bfd_putl32, | |
| bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */ | |
| { /* bfd_check_format */ | |
| _bfd_dummy_target, | |
| alpha_ecoff_object_p, | |
| bfd_generic_archive_p, | |
| _bfd_dummy_target | |
| }, | |
| { /* bfd_set_format */ | |
| _bfd_bool_bfd_false_error, | |
| _bfd_ecoff_mkobject, | |
| _bfd_generic_mkarchive, | |
| _bfd_bool_bfd_false_error | |
| }, | |
| { /* bfd_write_contents */ | |
| _bfd_bool_bfd_false_error, | |
| _bfd_ecoff_write_object_contents, | |
| _bfd_write_archive_contents, | |
| _bfd_bool_bfd_false_error | |
| }, | |
| BFD_JUMP_TABLE_GENERIC (_bfd_ecoff), | |
| BFD_JUMP_TABLE_COPY (_bfd_ecoff), | |
| BFD_JUMP_TABLE_CORE (_bfd_nocore), | |
| BFD_JUMP_TABLE_ARCHIVE (alpha_ecoff), | |
| BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff), | |
| BFD_JUMP_TABLE_RELOCS (_bfd_ecoff), | |
| BFD_JUMP_TABLE_WRITE (_bfd_ecoff), | |
| BFD_JUMP_TABLE_LINK (_bfd_ecoff), | |
| BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), | |
| NULL, | |
| &alpha_ecoff_backend_data | |
| }; |