1179407Sobrien/* BFD back-end for MIPS PE COFF files. 2179407Sobrien Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 3218822Sdim 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. 4179407Sobrien Modified from coff-i386.c by DJ Delorie, dj@cygnus.com 5179407Sobrien 6218822Sdim This file is part of BFD, the Binary File Descriptor library. 7179407Sobrien 8218822Sdim This program is free software; you can redistribute it and/or modify 9218822Sdim it under the terms of the GNU General Public License as published by 10218822Sdim the Free Software Foundation; either version 2 of the License, or 11218822Sdim (at your option) any later version. 12179407Sobrien 13218822Sdim This program is distributed in the hope that it will be useful, 14218822Sdim but WITHOUT ANY WARRANTY; without even the implied warranty of 15218822Sdim MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16218822Sdim GNU General Public License for more details. 17179407Sobrien 18218822Sdim You should have received a copy of the GNU General Public License 19218822Sdim along with this program; if not, write to the Free Software 20218822Sdim Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 21179407Sobrien 22179407Sobrien#define COFF_WITH_PE 23179407Sobrien#define COFF_LONG_SECTION_NAMES 24179407Sobrien#define PCRELOFFSET TRUE 25179407Sobrien 26218822Sdim#include "sysdep.h" 27179407Sobrien#include "bfd.h" 28179407Sobrien#include "libbfd.h" 29179407Sobrien#include "coff/mipspe.h" 30179407Sobrien#include "coff/internal.h" 31179407Sobrien#include "coff/pe.h" 32179407Sobrien#include "libcoff.h" 33179407Sobrien 34218822Sdim#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER 2 35179407Sobrien/* The page size is a guess based on ELF. */ 36179407Sobrien 37179407Sobrien#define COFF_PAGE_SIZE 0x1000 38179407Sobrien 39179407Sobrien/* For some reason when using mips COFF the value stored in the .text 40179407Sobrien section for a reference to a common symbol is the value itself plus 41179407Sobrien any desired offset. Ian Taylor, Cygnus Support. */ 42179407Sobrien 43179407Sobrien/* If we are producing relocatable output, we need to do some 44179407Sobrien adjustments to the object file that are not done by the 45179407Sobrien bfd_perform_relocation function. This function is called by every 46179407Sobrien reloc type to make any required adjustments. */ 47179407Sobrien 48179407Sobrienstatic bfd_reloc_status_type 49218822Sdimcoff_mips_reloc (bfd *abfd, 50218822Sdim arelent *reloc_entry, 51218822Sdim asymbol *symbol, 52218822Sdim void * data, 53218822Sdim asection *input_section ATTRIBUTE_UNUSED, 54218822Sdim bfd *output_bfd, 55218822Sdim char **error_message ATTRIBUTE_UNUSED) 56179407Sobrien{ 57179407Sobrien symvalue diff; 58179407Sobrien 59218822Sdim if (output_bfd == NULL) 60179407Sobrien return bfd_reloc_continue; 61179407Sobrien 62179407Sobrien if (bfd_is_com_section (symbol->section)) 63179407Sobrien { 64179407Sobrien#ifndef COFF_WITH_PE 65179407Sobrien /* We are relocating a common symbol. The current value in the 66179407Sobrien object file is ORIG + OFFSET, where ORIG is the value of the 67179407Sobrien common symbol as seen by the object file when it was compiled 68179407Sobrien (this may be zero if the symbol was undefined) and OFFSET is 69179407Sobrien the offset into the common symbol (normally zero, but may be 70179407Sobrien non-zero when referring to a field in a common structure). 71179407Sobrien ORIG is the negative of reloc_entry->addend, which is set by 72179407Sobrien the CALC_ADDEND macro below. We want to replace the value in 73179407Sobrien the object file with NEW + OFFSET, where NEW is the value of 74179407Sobrien the common symbol which we are going to put in the final 75179407Sobrien object file. NEW is symbol->value. */ 76179407Sobrien diff = symbol->value + reloc_entry->addend; 77179407Sobrien#else 78179407Sobrien /* In PE mode, we do not offset the common symbol. */ 79179407Sobrien diff = reloc_entry->addend; 80179407Sobrien#endif 81179407Sobrien } 82179407Sobrien else 83218822Sdim /* For some reason bfd_perform_relocation always effectively 84218822Sdim ignores the addend for a COFF target when producing 85218822Sdim relocatable output. This seems to be always wrong for 386 86218822Sdim COFF, so we handle the addend here instead. */ 87218822Sdim diff = reloc_entry->addend; 88179407Sobrien 89179407Sobrien#define DOIT(x) \ 90179407Sobrien x = ((x & ~howto->dst_mask) | (((x & howto->src_mask) + (diff >> howto->rightshift)) & howto->dst_mask)) 91179407Sobrien 92179407Sobrien if (diff != 0) 93179407Sobrien { 94179407Sobrien reloc_howto_type *howto = reloc_entry->howto; 95179407Sobrien unsigned char *addr = (unsigned char *) data + reloc_entry->address; 96179407Sobrien 97179407Sobrien switch (howto->size) 98179407Sobrien { 99179407Sobrien case 0: 100179407Sobrien { 101179407Sobrien char x = bfd_get_8 (abfd, addr); 102218822Sdim 103179407Sobrien DOIT (x); 104179407Sobrien bfd_put_8 (abfd, x, addr); 105179407Sobrien } 106179407Sobrien break; 107179407Sobrien 108179407Sobrien case 1: 109179407Sobrien { 110179407Sobrien short x = bfd_get_16 (abfd, addr); 111218822Sdim 112179407Sobrien DOIT (x); 113179407Sobrien bfd_put_16 (abfd, (bfd_vma) x, addr); 114179407Sobrien } 115179407Sobrien break; 116179407Sobrien 117179407Sobrien case 2: 118179407Sobrien { 119179407Sobrien long x = bfd_get_32 (abfd, addr); 120218822Sdim 121179407Sobrien DOIT (x); 122179407Sobrien bfd_put_32 (abfd, (bfd_vma) x, addr); 123179407Sobrien } 124179407Sobrien break; 125179407Sobrien 126179407Sobrien default: 127179407Sobrien abort (); 128179407Sobrien } 129179407Sobrien } 130179407Sobrien 131179407Sobrien /* Now let bfd_perform_relocation finish everything up. */ 132179407Sobrien return bfd_reloc_continue; 133179407Sobrien} 134179407Sobrien 135179407Sobrien#ifdef COFF_WITH_PE 136179407Sobrien/* Return TRUE if this relocation should 137179407Sobrien appear in the output .reloc section. */ 138179407Sobrien 139179407Sobrienstatic bfd_boolean 140218822Sdimin_reloc_p (bfd * abfd ATTRIBUTE_UNUSED, reloc_howto_type *howto) 141179407Sobrien{ 142179407Sobrien return ! howto->pc_relative && howto->type != MIPS_R_RVA; 143179407Sobrien} 144179407Sobrien#endif 145179407Sobrien 146179407Sobrien#ifndef PCRELOFFSET 147179407Sobrien#define PCRELOFFSET FALSE 148179407Sobrien#endif 149179407Sobrien 150179407Sobrienstatic reloc_howto_type howto_table[] = 151179407Sobrien{ 152179407Sobrien /* Reloc type 0 is ignored. The reloc reading code ensures that 153179407Sobrien this is a reference to the .abs section, which will cause 154179407Sobrien bfd_perform_relocation to do nothing. */ 155218822Sdim HOWTO (MIPS_R_ABSOLUTE, /* Type. */ 156218822Sdim 0, /* Rightshift. */ 157218822Sdim 0, /* Size (0 = byte, 1 = short, 2 = long). */ 158218822Sdim 8, /* Bitsize. */ 159218822Sdim FALSE, /* PC_relative. */ 160218822Sdim 0, /* Bitpos. */ 161218822Sdim complain_overflow_dont, /* Complain_on_overflow. */ 162218822Sdim 0, /* Special_function. */ 163218822Sdim "IGNORE", /* Name. */ 164218822Sdim FALSE, /* Partial_inplace. */ 165218822Sdim 0, /* Src_mask. */ 166218822Sdim 0, /* Dst_mask. */ 167218822Sdim FALSE), /* Pcrel_offset. */ 168179407Sobrien 169179407Sobrien /* A 16 bit reference to a symbol, normally from a data section. */ 170218822Sdim HOWTO (MIPS_R_REFHALF, /* Type. */ 171218822Sdim 0, /* Rightshift. */ 172218822Sdim 1, /* Size (0 = byte, 1 = short, 2 = long). */ 173218822Sdim 16, /* Bitsize. */ 174218822Sdim FALSE, /* PC_relative. */ 175218822Sdim 0, /* Bitpos. */ 176218822Sdim complain_overflow_bitfield, /* Complain_on_overflow. */ 177218822Sdim coff_mips_reloc, /* Special_function. */ 178218822Sdim "REFHALF", /* Name. */ 179218822Sdim TRUE, /* Partial_inplace. */ 180218822Sdim 0xffff, /* Src_mask. */ 181218822Sdim 0xffff, /* Dst_mask. */ 182218822Sdim FALSE), /* Pcrel_offset. */ 183179407Sobrien 184179407Sobrien /* A 32 bit reference to a symbol, normally from a data section. */ 185218822Sdim HOWTO (MIPS_R_REFWORD, /* Type. */ 186218822Sdim 0, /* Rightshift. */ 187218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 188218822Sdim 32, /* Bitsize. */ 189218822Sdim FALSE, /* PC_relative. */ 190218822Sdim 0, /* Bitpos. */ 191218822Sdim complain_overflow_bitfield, /* Complain_on_overflow. */ 192218822Sdim coff_mips_reloc, /* Special_function. */ 193218822Sdim "REFWORD", /* Name. */ 194218822Sdim TRUE, /* Partial_inplace. */ 195218822Sdim 0xffffffff, /* Src_mask. */ 196218822Sdim 0xffffffff, /* Dst_mask. */ 197218822Sdim FALSE), /* Pcrel_offset. */ 198179407Sobrien 199179407Sobrien /* A 26 bit absolute jump address. */ 200218822Sdim HOWTO (MIPS_R_JMPADDR, /* Type. */ 201218822Sdim 2, /* Rightshift. */ 202218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 203218822Sdim 26, /* Bitsize. */ 204218822Sdim FALSE, /* PC_relative. */ 205218822Sdim 0, /* Bitpos. */ 206218822Sdim complain_overflow_dont, /* Complain_on_overflow. */ 207179407Sobrien /* This needs complex overflow 208179407Sobrien detection, because the upper four 209179407Sobrien bits must match the PC. */ 210218822Sdim coff_mips_reloc, /* Special_function. */ 211218822Sdim "JMPADDR", /* Name. */ 212218822Sdim TRUE, /* Partial_inplace. */ 213218822Sdim 0x3ffffff, /* Src_mask. */ 214218822Sdim 0x3ffffff, /* Dst_mask. */ 215218822Sdim FALSE), /* Pcrel_offset. */ 216179407Sobrien 217179407Sobrien /* The high 16 bits of a symbol value. Handled by the function 218179407Sobrien mips_refhi_reloc. */ 219218822Sdim HOWTO (MIPS_R_REFHI, /* Type. */ 220218822Sdim 16, /* Rightshift. */ 221218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 222218822Sdim 16, /* Bitsize. */ 223218822Sdim FALSE, /* PC_relative. */ 224218822Sdim 0, /* Bitpos. */ 225218822Sdim complain_overflow_bitfield, /* Complain_on_overflow. */ 226218822Sdim coff_mips_reloc, /* Special_function. */ 227218822Sdim "REFHI", /* Name. */ 228218822Sdim TRUE, /* Partial_inplace. */ 229218822Sdim 0xffff, /* Src_mask. */ 230218822Sdim 0xffff, /* Dst_mask. */ 231218822Sdim FALSE), /* Pcrel_offset. */ 232179407Sobrien 233179407Sobrien /* The low 16 bits of a symbol value. */ 234218822Sdim HOWTO (MIPS_R_REFLO, /* Type. */ 235218822Sdim 0, /* Rightshift. */ 236218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 237218822Sdim 16, /* Bitsize. */ 238218822Sdim FALSE, /* PC_relative. */ 239218822Sdim 0, /* Bitpos. */ 240218822Sdim complain_overflow_dont, /* Complain_on_overflow. */ 241218822Sdim coff_mips_reloc, /* Special_function. */ 242218822Sdim "REFLO", /* Name. */ 243218822Sdim TRUE, /* Partial_inplace. */ 244218822Sdim 0xffff, /* Src_mask. */ 245218822Sdim 0xffff, /* Dst_mask. */ 246218822Sdim FALSE), /* Pcrel_offset. */ 247179407Sobrien 248179407Sobrien /* A reference to an offset from the gp register. Handled by the 249179407Sobrien function mips_gprel_reloc. */ 250218822Sdim HOWTO (MIPS_R_GPREL, /* Type. */ 251218822Sdim 0, /* Rightshift. */ 252218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 253218822Sdim 16, /* Bitsize. */ 254218822Sdim FALSE, /* PC_relative. */ 255218822Sdim 0, /* Bitpos. */ 256218822Sdim complain_overflow_signed, /* Complain_on_overflow. */ 257218822Sdim coff_mips_reloc, /* Special_function. */ 258218822Sdim "GPREL", /* Name. */ 259218822Sdim TRUE, /* Partial_inplace. */ 260218822Sdim 0xffff, /* Src_mask. */ 261218822Sdim 0xffff, /* Dst_mask. */ 262218822Sdim FALSE), /* Pcrel_offset. */ 263179407Sobrien 264179407Sobrien /* A reference to a literal using an offset from the gp register. 265179407Sobrien Handled by the function mips_gprel_reloc. */ 266218822Sdim HOWTO (MIPS_R_LITERAL, /* Type. */ 267218822Sdim 0, /* Rightshift. */ 268218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 269218822Sdim 16, /* Bitsize. */ 270218822Sdim FALSE, /* PC_relative. */ 271218822Sdim 0, /* Bitpos. */ 272218822Sdim complain_overflow_signed, /* Complain_on_overflow. */ 273218822Sdim coff_mips_reloc, /* Special_function. */ 274218822Sdim "LITERAL", /* Name. */ 275218822Sdim TRUE, /* Partial_inplace. */ 276218822Sdim 0xffff, /* Src_mask. */ 277218822Sdim 0xffff, /* Dst_mask. */ 278218822Sdim FALSE), /* Pcrel_offset. */ 279179407Sobrien 280179407Sobrien EMPTY_HOWTO (8), 281179407Sobrien EMPTY_HOWTO (9), 282179407Sobrien EMPTY_HOWTO (10), 283179407Sobrien EMPTY_HOWTO (11), 284179407Sobrien EMPTY_HOWTO (12), 285179407Sobrien EMPTY_HOWTO (13), 286179407Sobrien EMPTY_HOWTO (14), 287179407Sobrien EMPTY_HOWTO (15), 288179407Sobrien EMPTY_HOWTO (16), 289179407Sobrien EMPTY_HOWTO (17), 290179407Sobrien EMPTY_HOWTO (18), 291179407Sobrien EMPTY_HOWTO (19), 292179407Sobrien EMPTY_HOWTO (20), 293179407Sobrien EMPTY_HOWTO (21), 294179407Sobrien EMPTY_HOWTO (22), 295179407Sobrien EMPTY_HOWTO (23), 296179407Sobrien EMPTY_HOWTO (24), 297179407Sobrien EMPTY_HOWTO (25), 298179407Sobrien EMPTY_HOWTO (26), 299179407Sobrien EMPTY_HOWTO (27), 300179407Sobrien EMPTY_HOWTO (28), 301179407Sobrien EMPTY_HOWTO (29), 302179407Sobrien EMPTY_HOWTO (30), 303179407Sobrien EMPTY_HOWTO (31), 304179407Sobrien EMPTY_HOWTO (32), 305179407Sobrien EMPTY_HOWTO (33), 306218822Sdim HOWTO (MIPS_R_RVA, /* Type. */ 307218822Sdim 0, /* Rightshift. */ 308218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 309218822Sdim 32, /* Bitsize. */ 310218822Sdim FALSE, /* PC_relative. */ 311218822Sdim 0, /* Bitpos. */ 312218822Sdim complain_overflow_bitfield, /* Complain_on_overflow. */ 313218822Sdim coff_mips_reloc, /* Special_function. */ 314218822Sdim "rva32", /* Name. */ 315218822Sdim TRUE, /* Partial_inplace. */ 316218822Sdim 0xffffffff, /* Src_mask. */ 317218822Sdim 0xffffffff, /* Dst_mask. */ 318218822Sdim FALSE), /* Pcrel_offset. */ 319179407Sobrien EMPTY_HOWTO (35), 320179407Sobrien EMPTY_HOWTO (36), 321218822Sdim HOWTO (MIPS_R_PAIR, /* Type. */ 322218822Sdim 0, /* Rightshift. */ 323218822Sdim 2, /* Size (0 = byte, 1 = short, 2 = long). */ 324218822Sdim 32, /* Bitsize. */ 325218822Sdim FALSE, /* PC_relative. */ 326218822Sdim 0, /* Bitpos. */ 327218822Sdim complain_overflow_bitfield, /* Complain_on_overflow. */ 328218822Sdim coff_mips_reloc, /* Special_function. */ 329218822Sdim "PAIR", /* Name. */ 330218822Sdim TRUE, /* Partial_inplace. */ 331218822Sdim 0xffffffff, /* Src_mask. */ 332218822Sdim 0xffffffff, /* Dst_mask. */ 333218822Sdim FALSE), /* Pcrel_offset. */ 334179407Sobrien}; 335179407Sobrien 336218822Sdim/* Turn a howto into a reloc nunmber. */ 337179407Sobrien 338218822Sdim#define SELECT_RELOC(x, howto) { x.r_type = howto->type; } 339218822Sdim#define BADMAG(x) MIPSBADMAG (x) 340179407Sobrien 341218822Sdim/* Customize coffcode.h. */ 342218822Sdim#define MIPS 1 343218822Sdim 344179407Sobrien#define RTYPE2HOWTO(cache_ptr, dst) \ 345179407Sobrien (cache_ptr)->howto = howto_table + (dst)->r_type; 346179407Sobrien 347179407Sobrien/* Compute the addend of a reloc. If the reloc is to a common symbol, 348179407Sobrien the object file contains the value of the common symbol. By the 349179407Sobrien time this is called, the linker may be using a different symbol 350179407Sobrien from a different object file with a different value. Therefore, we 351179407Sobrien hack wildly to locate the original symbol from this file so that we 352179407Sobrien can make the correct adjustment. This macro sets coffsym to the 353179407Sobrien symbol from the original file, and uses it to set the addend value 354179407Sobrien correctly. If this is not a common symbol, the usual addend 355179407Sobrien calculation is done, except that an additional tweak is needed for 356179407Sobrien PC relative relocs. 357179407Sobrien FIXME: This macro refers to symbols and asect; these are from the 358179407Sobrien calling function, not the macro arguments. */ 359179407Sobrien 360179407Sobrien#define CALC_ADDEND(abfd, ptr, reloc, cache_ptr) \ 361179407Sobrien { \ 362218822Sdim coff_symbol_type *coffsym = NULL; \ 363179407Sobrien if (ptr && bfd_asymbol_bfd (ptr) != abfd) \ 364179407Sobrien coffsym = (obj_symbols (abfd) \ 365179407Sobrien + (cache_ptr->sym_ptr_ptr - symbols)); \ 366179407Sobrien else if (ptr) \ 367179407Sobrien coffsym = coff_symbol_from (abfd, ptr); \ 368218822Sdim if (coffsym != NULL \ 369179407Sobrien && coffsym->native->u.syment.n_scnum == 0) \ 370179407Sobrien cache_ptr->addend = - coffsym->native->u.syment.n_value; \ 371179407Sobrien else if (ptr && bfd_asymbol_bfd (ptr) == abfd \ 372218822Sdim && ptr->section != NULL) \ 373179407Sobrien cache_ptr->addend = - (ptr->section->vma + ptr->value); \ 374179407Sobrien else \ 375179407Sobrien cache_ptr->addend = 0; \ 376179407Sobrien if (ptr && howto_table[reloc.r_type].pc_relative) \ 377179407Sobrien cache_ptr->addend += asect->vma; \ 378179407Sobrien } 379179407Sobrien 380179407Sobrien/* Convert an rtype to howto for the COFF backend linker. */ 381179407Sobrien 382179407Sobrienstatic reloc_howto_type * 383218822Sdimcoff_mips_rtype_to_howto (bfd *abfd ATTRIBUTE_UNUSED, 384218822Sdim asection *sec, 385218822Sdim struct internal_reloc *rel, 386218822Sdim struct coff_link_hash_entry *h, 387218822Sdim struct internal_syment *sym, 388218822Sdim bfd_vma *addendp) 389179407Sobrien{ 390179407Sobrien 391179407Sobrien reloc_howto_type *howto; 392179407Sobrien 393179407Sobrien howto = howto_table + rel->r_type; 394179407Sobrien 395179407Sobrien#ifdef COFF_WITH_PE 396179407Sobrien *addendp = 0; 397179407Sobrien#endif 398179407Sobrien 399179407Sobrien if (howto->pc_relative) 400179407Sobrien *addendp += sec->vma; 401179407Sobrien 402179407Sobrien if (sym != NULL && sym->n_scnum == 0 && sym->n_value != 0) 403179407Sobrien { 404179407Sobrien /* This is a common symbol. The section contents include the 405179407Sobrien size (sym->n_value) as an addend. The relocate_section 406179407Sobrien function will be adding in the final value of the symbol. We 407179407Sobrien need to subtract out the current size in order to get the 408179407Sobrien correct result. */ 409179407Sobrien 410179407Sobrien BFD_ASSERT (h != NULL); 411179407Sobrien 412179407Sobrien#ifndef COFF_WITH_PE 413179407Sobrien /* I think we *do* want to bypass this. If we don't, I have 414179407Sobrien seen some data parameters get the wrong relocation address. 415179407Sobrien If I link two versions with and without this section bypassed 416179407Sobrien and then do a binary comparison, the addresses which are 417179407Sobrien different can be looked up in the map. The case in which 418179407Sobrien this section has been bypassed has addresses which correspond 419179407Sobrien to values I can find in the map. */ 420179407Sobrien *addendp -= sym->n_value; 421179407Sobrien#endif 422179407Sobrien } 423179407Sobrien 424179407Sobrien#ifndef COFF_WITH_PE 425179407Sobrien /* If the output symbol is common (in which case this must be a 426179407Sobrien relocatable link), we need to add in the final size of the 427179407Sobrien common symbol. */ 428179407Sobrien if (h != NULL && h->root.type == bfd_link_hash_common) 429179407Sobrien *addendp += h->root.u.c.size; 430179407Sobrien#endif 431179407Sobrien 432179407Sobrien#ifdef COFF_WITH_PE 433179407Sobrien if (howto->pc_relative) 434179407Sobrien { 435179407Sobrien *addendp -= 4; 436179407Sobrien 437179407Sobrien /* If the symbol is defined, then the generic code is going to 438179407Sobrien add back the symbol value in order to cancel out an 439179407Sobrien adjustment it made to the addend. However, we set the addend 440179407Sobrien to 0 at the start of this function. We need to adjust here, 441179407Sobrien to avoid the adjustment the generic code will make. FIXME: 442179407Sobrien This is getting a bit hackish. */ 443179407Sobrien if (sym != NULL && sym->n_scnum != 0) 444179407Sobrien *addendp -= sym->n_value; 445179407Sobrien } 446179407Sobrien 447179407Sobrien if (rel->r_type == MIPS_R_RVA) 448218822Sdim *addendp -= pe_data (sec->output_section->owner)->pe_opthdr.ImageBase; 449179407Sobrien#endif 450179407Sobrien 451179407Sobrien return howto; 452179407Sobrien} 453179407Sobrien 454218822Sdim#define coff_rtype_to_howto coff_mips_rtype_to_howto 455218822Sdim#define coff_bfd_reloc_type_lookup coff_mips_reloc_type_lookup 456218822Sdim#define coff_bfd_reloc_name_lookup coff_mips_reloc_name_lookup 457179407Sobrien 458179407Sobrien/* Get the howto structure for a generic reloc type. */ 459179407Sobrien 460179407Sobrienstatic reloc_howto_type * 461218822Sdimcoff_mips_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, 462218822Sdim bfd_reloc_code_real_type code) 463179407Sobrien{ 464179407Sobrien int mips_type; 465179407Sobrien 466179407Sobrien switch (code) 467179407Sobrien { 468179407Sobrien case BFD_RELOC_16: 469179407Sobrien mips_type = MIPS_R_REFHALF; 470179407Sobrien break; 471179407Sobrien case BFD_RELOC_32: 472179407Sobrien case BFD_RELOC_CTOR: 473179407Sobrien mips_type = MIPS_R_REFWORD; 474179407Sobrien break; 475179407Sobrien case BFD_RELOC_MIPS_JMP: 476179407Sobrien mips_type = MIPS_R_JMPADDR; 477179407Sobrien break; 478179407Sobrien case BFD_RELOC_HI16_S: 479179407Sobrien mips_type = MIPS_R_REFHI; 480179407Sobrien break; 481179407Sobrien case BFD_RELOC_LO16: 482179407Sobrien mips_type = MIPS_R_REFLO; 483179407Sobrien break; 484179407Sobrien case BFD_RELOC_GPREL16: 485179407Sobrien mips_type = MIPS_R_GPREL; 486179407Sobrien break; 487179407Sobrien case BFD_RELOC_MIPS_LITERAL: 488179407Sobrien mips_type = MIPS_R_LITERAL; 489179407Sobrien break; 490179407Sobrien case BFD_RELOC_RVA: 491179407Sobrien mips_type = MIPS_R_RVA; 492179407Sobrien break; 493179407Sobrien default: 494218822Sdim return NULL; 495179407Sobrien } 496179407Sobrien 497218822Sdim return & howto_table [mips_type]; 498179407Sobrien} 499179407Sobrien 500218822Sdimstatic reloc_howto_type * 501218822Sdimcoff_mips_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, 502218822Sdim const char *r_name) 503218822Sdim{ 504218822Sdim unsigned int i; 505218822Sdim 506218822Sdim for (i = 0; 507218822Sdim i < sizeof (howto_table) / sizeof (howto_table[0]); 508218822Sdim i++) 509218822Sdim if (howto_table[i].name != NULL 510218822Sdim && strcasecmp (howto_table[i].name, r_name) == 0) 511218822Sdim return &howto_table[i]; 512218822Sdim 513218822Sdim return NULL; 514218822Sdim} 515218822Sdim 516179407Sobrienstatic void 517218822Sdimmips_swap_reloc_in (bfd * abfd, void * src, void * dst) 518179407Sobrien{ 519179407Sobrien static struct internal_reloc pair_prev; 520179407Sobrien RELOC *reloc_src = (RELOC *) src; 521179407Sobrien struct internal_reloc *reloc_dst = (struct internal_reloc *) dst; 522179407Sobrien 523179407Sobrien reloc_dst->r_vaddr = H_GET_32 (abfd, reloc_src->r_vaddr); 524179407Sobrien reloc_dst->r_symndx = H_GET_S32 (abfd, reloc_src->r_symndx); 525179407Sobrien reloc_dst->r_type = H_GET_16 (abfd, reloc_src->r_type); 526179407Sobrien reloc_dst->r_size = 0; 527179407Sobrien reloc_dst->r_extern = 0; 528179407Sobrien reloc_dst->r_offset = 0; 529179407Sobrien 530179407Sobrien switch (reloc_dst->r_type) 531179407Sobrien { 532179407Sobrien case MIPS_R_REFHI: 533179407Sobrien pair_prev = *reloc_dst; 534179407Sobrien break; 535179407Sobrien case MIPS_R_PAIR: 536179407Sobrien reloc_dst->r_offset = reloc_dst->r_symndx; 537179407Sobrien if (reloc_dst->r_offset & 0x8000) 538179407Sobrien reloc_dst->r_offset -= 0x10000; 539179407Sobrien reloc_dst->r_symndx = pair_prev.r_symndx; 540179407Sobrien break; 541179407Sobrien } 542179407Sobrien} 543179407Sobrien 544179407Sobrienstatic unsigned int 545218822Sdimmips_swap_reloc_out (bfd * abfd, void * src, void * dst) 546179407Sobrien{ 547179407Sobrien static int prev_offset = 1; 548179407Sobrien static bfd_vma prev_addr = 0; 549179407Sobrien struct internal_reloc *reloc_src = (struct internal_reloc *)src; 550179407Sobrien struct external_reloc *reloc_dst = (struct external_reloc *)dst; 551179407Sobrien 552179407Sobrien switch (reloc_src->r_type) 553179407Sobrien { 554179407Sobrien case MIPS_R_REFHI: 555179407Sobrien prev_addr = reloc_src->r_vaddr; 556179407Sobrien prev_offset = reloc_src->r_offset; 557179407Sobrien break; 558179407Sobrien case MIPS_R_REFLO: 559179407Sobrien if (reloc_src->r_vaddr == prev_addr) 560179407Sobrien { 561179407Sobrien /* FIXME: only slightly hackish. If we see a REFLO pointing to 562179407Sobrien the same address as a REFHI, we assume this is the matching 563179407Sobrien PAIR reloc and output it accordingly. The symndx is really 564179407Sobrien the low 16 bits of the addend */ 565179407Sobrien H_PUT_32 (abfd, reloc_src->r_vaddr, reloc_dst->r_vaddr); 566179407Sobrien H_PUT_32 (abfd, reloc_src->r_symndx, reloc_dst->r_symndx); 567179407Sobrien H_PUT_16 (abfd, MIPS_R_PAIR, reloc_dst->r_type); 568179407Sobrien return RELSZ; 569179407Sobrien } 570179407Sobrien break; 571179407Sobrien } 572179407Sobrien 573179407Sobrien H_PUT_32 (abfd, reloc_src->r_vaddr, reloc_dst->r_vaddr); 574179407Sobrien H_PUT_32 (abfd, reloc_src->r_symndx, reloc_dst->r_symndx); 575179407Sobrien 576179407Sobrien H_PUT_16 (abfd, reloc_src->r_type, reloc_dst->r_type); 577179407Sobrien return RELSZ; 578179407Sobrien} 579179407Sobrien 580218822Sdim#define coff_swap_reloc_in mips_swap_reloc_in 581218822Sdim#define coff_swap_reloc_out mips_swap_reloc_out 582179407Sobrien#define NO_COFF_RELOCS 583179407Sobrien 584179407Sobrienstatic bfd_boolean 585218822Sdimcoff_pe_mips_relocate_section (bfd *output_bfd, 586218822Sdim struct bfd_link_info *info, 587218822Sdim bfd *input_bfd, 588218822Sdim asection *input_section, 589218822Sdim bfd_byte *contents, 590218822Sdim struct internal_reloc *relocs, 591218822Sdim struct internal_syment *syms, 592218822Sdim asection **sections) 593179407Sobrien{ 594179407Sobrien bfd_vma gp; 595179407Sobrien bfd_boolean gp_undefined; 596179407Sobrien size_t adjust; 597179407Sobrien struct internal_reloc *rel; 598179407Sobrien struct internal_reloc *rel_end; 599179407Sobrien unsigned int i; 600179407Sobrien bfd_boolean got_lo; 601179407Sobrien 602179407Sobrien if (info->relocatable) 603218822Sdim { 604218822Sdim (*_bfd_error_handler) 605218822Sdim (_("%B: `ld -r' not supported with PE MIPS objects\n"), input_bfd); 606218822Sdim bfd_set_error (bfd_error_bad_value); 607218822Sdim return FALSE; 608218822Sdim } 609179407Sobrien 610179407Sobrien BFD_ASSERT (input_bfd->xvec->byteorder 611179407Sobrien == output_bfd->xvec->byteorder); 612179407Sobrien 613179407Sobrien gp = _bfd_get_gp_value (output_bfd); 614218822Sdim gp_undefined = (gp == 0) ? TRUE : FALSE; 615179407Sobrien got_lo = FALSE; 616179407Sobrien adjust = 0; 617179407Sobrien rel = relocs; 618179407Sobrien rel_end = rel + input_section->reloc_count; 619218822Sdim 620179407Sobrien for (i = 0; rel < rel_end; rel++, i++) 621179407Sobrien { 622179407Sobrien long symndx; 623179407Sobrien struct coff_link_hash_entry *h; 624179407Sobrien struct internal_syment *sym; 625179407Sobrien bfd_vma addend = 0; 626179407Sobrien bfd_vma val, tmp, targ, src, low; 627179407Sobrien reloc_howto_type *howto; 628179407Sobrien unsigned char *mem = contents + rel->r_vaddr; 629179407Sobrien 630179407Sobrien symndx = rel->r_symndx; 631179407Sobrien 632179407Sobrien if (symndx == -1) 633179407Sobrien { 634179407Sobrien h = NULL; 635179407Sobrien sym = NULL; 636179407Sobrien } 637179407Sobrien else 638179407Sobrien { 639179407Sobrien h = obj_coff_sym_hashes (input_bfd)[symndx]; 640179407Sobrien sym = syms + symndx; 641179407Sobrien } 642179407Sobrien 643179407Sobrien /* COFF treats common symbols in one of two ways. Either the 644179407Sobrien size of the symbol is included in the section contents, or it 645179407Sobrien is not. We assume that the size is not included, and force 646179407Sobrien the rtype_to_howto function to adjust the addend as needed. */ 647179407Sobrien 648179407Sobrien if (sym != NULL && sym->n_scnum != 0) 649179407Sobrien addend = - sym->n_value; 650179407Sobrien else 651179407Sobrien addend = 0; 652179407Sobrien 653179407Sobrien howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h, 654179407Sobrien sym, &addend); 655179407Sobrien if (howto == NULL) 656179407Sobrien return FALSE; 657179407Sobrien 658179407Sobrien /* If we are doing a relocatable link, then we can just ignore 659179407Sobrien a PC relative reloc that is pcrel_offset. It will already 660179407Sobrien have the correct value. If this is not a relocatable link, 661179407Sobrien then we should ignore the symbol value. */ 662179407Sobrien if (howto->pc_relative && howto->pcrel_offset) 663179407Sobrien { 664179407Sobrien if (info->relocatable) 665179407Sobrien continue; 666179407Sobrien if (sym != NULL && sym->n_scnum != 0) 667179407Sobrien addend += sym->n_value; 668179407Sobrien } 669179407Sobrien 670179407Sobrien val = 0; 671179407Sobrien 672179407Sobrien if (h == NULL) 673179407Sobrien { 674179407Sobrien asection *sec; 675179407Sobrien 676179407Sobrien if (symndx == -1) 677179407Sobrien { 678179407Sobrien sec = bfd_abs_section_ptr; 679179407Sobrien val = 0; 680179407Sobrien } 681179407Sobrien else 682179407Sobrien { 683179407Sobrien sec = sections[symndx]; 684179407Sobrien val = (sec->output_section->vma 685179407Sobrien + sec->output_offset 686179407Sobrien + sym->n_value); 687179407Sobrien if (! obj_pe (input_bfd)) 688179407Sobrien val -= sec->vma; 689179407Sobrien } 690179407Sobrien } 691179407Sobrien else 692179407Sobrien { 693179407Sobrien if (h->root.type == bfd_link_hash_defined 694179407Sobrien || h->root.type == bfd_link_hash_defweak) 695179407Sobrien { 696179407Sobrien asection *sec; 697179407Sobrien 698179407Sobrien sec = h->root.u.def.section; 699179407Sobrien val = (h->root.u.def.value 700179407Sobrien + sec->output_section->vma 701179407Sobrien + sec->output_offset); 702179407Sobrien } 703179407Sobrien 704179407Sobrien else if (! info->relocatable) 705179407Sobrien { 706179407Sobrien if (! ((*info->callbacks->undefined_symbol) 707179407Sobrien (info, h->root.root.string, input_bfd, input_section, 708179407Sobrien rel->r_vaddr - input_section->vma, TRUE))) 709179407Sobrien return FALSE; 710179407Sobrien } 711179407Sobrien } 712179407Sobrien 713179407Sobrien src = rel->r_vaddr + input_section->output_section->vma 714179407Sobrien + input_section->output_offset; 715179407Sobrien 716179407Sobrien /* OK, at this point the following variables are set up: 717179407Sobrien src = VMA of the memory we're fixing up 718179407Sobrien mem = pointer to memory we're fixing up 719218822Sdim val = VMA of what we need to refer to. */ 720179407Sobrien 721218822Sdim#define UI(x) (*_bfd_error_handler) (_("%B: unimplemented %s\n"), \ 722218822Sdim input_bfd, x); \ 723179407Sobrien bfd_set_error (bfd_error_bad_value); 724179407Sobrien 725179407Sobrien switch (rel->r_type) 726179407Sobrien { 727179407Sobrien case MIPS_R_ABSOLUTE: 728218822Sdim /* Ignore these. */ 729179407Sobrien break; 730179407Sobrien 731179407Sobrien case MIPS_R_REFHALF: 732218822Sdim UI ("refhalf"); 733179407Sobrien break; 734179407Sobrien 735179407Sobrien case MIPS_R_REFWORD: 736218822Sdim tmp = bfd_get_32 (input_bfd, mem); 737179407Sobrien /* printf ("refword: src=%08x targ=%08x+%08x\n", src, tmp, val); */ 738179407Sobrien tmp += val; 739218822Sdim bfd_put_32 (input_bfd, tmp, mem); 740179407Sobrien break; 741179407Sobrien 742179407Sobrien case MIPS_R_JMPADDR: 743218822Sdim tmp = bfd_get_32 (input_bfd, mem); 744218822Sdim targ = val + (tmp & 0x03ffffff) * 4; 745179407Sobrien if ((src & 0xf0000000) != (targ & 0xf0000000)) 746179407Sobrien { 747218822Sdim (*_bfd_error_handler) (_("%B: jump too far away\n"), input_bfd); 748179407Sobrien bfd_set_error (bfd_error_bad_value); 749179407Sobrien return FALSE; 750179407Sobrien } 751179407Sobrien tmp &= 0xfc000000; 752218822Sdim tmp |= (targ / 4) & 0x3ffffff; 753218822Sdim bfd_put_32 (input_bfd, tmp, mem); 754179407Sobrien break; 755179407Sobrien 756179407Sobrien case MIPS_R_REFHI: 757218822Sdim tmp = bfd_get_32 (input_bfd, mem); 758179407Sobrien switch (rel[1].r_type) 759179407Sobrien { 760179407Sobrien case MIPS_R_PAIR: 761179407Sobrien /* MS PE object */ 762179407Sobrien targ = val + rel[1].r_offset + ((tmp & 0xffff) << 16); 763179407Sobrien break; 764179407Sobrien case MIPS_R_REFLO: 765179407Sobrien /* GNU COFF object */ 766218822Sdim low = bfd_get_32 (input_bfd, contents + rel[1].r_vaddr); 767179407Sobrien low &= 0xffff; 768179407Sobrien if (low & 0x8000) 769179407Sobrien low -= 0x10000; 770179407Sobrien targ = val + low + ((tmp & 0xffff) << 16); 771179407Sobrien break; 772179407Sobrien default: 773218822Sdim (*_bfd_error_handler) (_("%B: bad pair/reflo after refhi\n"), 774218822Sdim input_bfd); 775179407Sobrien bfd_set_error (bfd_error_bad_value); 776179407Sobrien return FALSE; 777179407Sobrien } 778179407Sobrien tmp &= 0xffff0000; 779179407Sobrien tmp |= (targ >> 16) & 0xffff; 780218822Sdim bfd_put_32 (input_bfd, tmp, mem); 781179407Sobrien break; 782179407Sobrien 783179407Sobrien case MIPS_R_REFLO: 784218822Sdim tmp = bfd_get_32 (input_bfd, mem); 785179407Sobrien targ = val + (tmp & 0xffff); 786179407Sobrien /* printf ("refword: src=%08x targ=%08x\n", src, targ); */ 787179407Sobrien tmp &= 0xffff0000; 788179407Sobrien tmp |= targ & 0xffff; 789218822Sdim bfd_put_32 (input_bfd, tmp, mem); 790179407Sobrien break; 791179407Sobrien 792179407Sobrien case MIPS_R_GPREL: 793179407Sobrien case MIPS_R_LITERAL: 794218822Sdim UI ("gprel"); 795179407Sobrien break; 796179407Sobrien 797179407Sobrien case MIPS_R_SECTION: 798218822Sdim UI ("section"); 799179407Sobrien break; 800179407Sobrien 801179407Sobrien case MIPS_R_SECREL: 802218822Sdim UI ("secrel"); 803179407Sobrien break; 804179407Sobrien 805179407Sobrien case MIPS_R_SECRELLO: 806218822Sdim UI ("secrello"); 807179407Sobrien break; 808179407Sobrien 809179407Sobrien case MIPS_R_SECRELHI: 810218822Sdim UI ("secrelhi"); 811179407Sobrien break; 812179407Sobrien 813179407Sobrien case MIPS_R_RVA: 814179407Sobrien tmp = bfd_get_32 (input_bfd, mem); 815179407Sobrien /* printf ("rva: src=%08x targ=%08x+%08x\n", src, tmp, val); */ 816179407Sobrien tmp += val 817179407Sobrien - pe_data (input_section->output_section->owner)->pe_opthdr.ImageBase; 818179407Sobrien bfd_put_32 (input_bfd, tmp, mem); 819179407Sobrien break; 820179407Sobrien 821179407Sobrien case MIPS_R_PAIR: 822179407Sobrien /* ignore these */ 823179407Sobrien break; 824179407Sobrien } 825179407Sobrien } 826179407Sobrien 827179407Sobrien return TRUE; 828179407Sobrien} 829179407Sobrien 830179407Sobrien#define coff_relocate_section coff_pe_mips_relocate_section 831179407Sobrien 832179407Sobrien#ifdef TARGET_UNDERSCORE 833179407Sobrien 834179407Sobrien/* If mips gcc uses underscores for symbol names, then it does not use 835179407Sobrien a leading dot for local labels, so if TARGET_UNDERSCORE is defined 836179407Sobrien we treat all symbols starting with L as local. */ 837179407Sobrien 838179407Sobrienstatic bfd_boolean 839218822Sdimcoff_mips_is_local_label_name (bfd *abfd, const char *name) 840179407Sobrien{ 841179407Sobrien if (name[0] == 'L') 842179407Sobrien return TRUE; 843179407Sobrien 844179407Sobrien return _bfd_coff_is_local_label_name (abfd, name); 845179407Sobrien} 846179407Sobrien 847179407Sobrien#define coff_bfd_is_local_label_name coff_mips_is_local_label_name 848179407Sobrien 849179407Sobrien#endif /* TARGET_UNDERSCORE */ 850179407Sobrien 851179407Sobrien#define COFF_NO_HACK_SCNHDR_SIZE 852179407Sobrien 853179407Sobrien#include "coffcode.h" 854179407Sobrien 855179407Sobrienconst bfd_target 856179407Sobrien#ifdef TARGET_SYM 857179407Sobrien TARGET_SYM = 858179407Sobrien#else 859179407Sobrien mipslpe_vec = 860179407Sobrien#endif 861179407Sobrien{ 862179407Sobrien#ifdef TARGET_NAME 863179407Sobrien TARGET_NAME, 864179407Sobrien#else 865218822Sdim "pe-mips", /* Name. */ 866179407Sobrien#endif 867179407Sobrien bfd_target_coff_flavour, 868218822Sdim BFD_ENDIAN_LITTLE, /* Data byte order is little. */ 869218822Sdim BFD_ENDIAN_LITTLE, /* Header byte order is little. */ 870179407Sobrien 871218822Sdim (HAS_RELOC | EXEC_P | /* Object flags. */ 872179407Sobrien HAS_LINENO | HAS_DEBUG | 873179407Sobrien HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), 874179407Sobrien 875179407Sobrien#ifndef COFF_WITH_PE 876218822Sdim (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC /* Section flags. */ 877179407Sobrien | SEC_CODE | SEC_DATA), 878179407Sobrien#else 879218822Sdim (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC /* Section flags. */ 880179407Sobrien | SEC_CODE | SEC_DATA 881179407Sobrien | SEC_LINK_ONCE | SEC_LINK_DUPLICATES), 882179407Sobrien#endif 883179407Sobrien 884179407Sobrien#ifdef TARGET_UNDERSCORE 885218822Sdim TARGET_UNDERSCORE, /* Leading underscore. */ 886179407Sobrien#else 887179407Sobrien 0, /* leading underscore */ 888179407Sobrien#endif 889218822Sdim '/', /* AR_pad_char. */ 890218822Sdim 15, /* AR_max_namelen. */ 891179407Sobrien 892179407Sobrien bfd_getl64, bfd_getl_signed_64, bfd_putl64, 893179407Sobrien bfd_getl32, bfd_getl_signed_32, bfd_putl32, 894218822Sdim bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* Data. */ 895179407Sobrien bfd_getl64, bfd_getl_signed_64, bfd_putl64, 896179407Sobrien bfd_getl32, bfd_getl_signed_32, bfd_putl32, 897218822Sdim bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* Headers. */ 898179407Sobrien 899218822Sdim /* Note that we allow an object file to be treated as a core file as well. */ 900218822Sdim {_bfd_dummy_target, coff_object_p, /* bfd_check_format. */ 901218822Sdim bfd_generic_archive_p, coff_object_p}, 902218822Sdim {bfd_false, coff_mkobject, _bfd_generic_mkarchive, /* bfd_set_format. */ 903218822Sdim bfd_false}, 904218822Sdim {bfd_false, coff_write_object_contents, /* bfd_write_contents. */ 905218822Sdim _bfd_write_archive_contents, bfd_false}, 906179407Sobrien 907218822Sdim BFD_JUMP_TABLE_GENERIC (coff), 908218822Sdim BFD_JUMP_TABLE_COPY (coff), 909218822Sdim BFD_JUMP_TABLE_CORE (_bfd_nocore), 910218822Sdim BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff), 911218822Sdim BFD_JUMP_TABLE_SYMBOLS (coff), 912218822Sdim BFD_JUMP_TABLE_RELOCS (coff), 913218822Sdim BFD_JUMP_TABLE_WRITE (coff), 914218822Sdim BFD_JUMP_TABLE_LINK (coff), 915218822Sdim BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), 916179407Sobrien 917179407Sobrien NULL, 918179407Sobrien 919179407Sobrien COFF_SWAP_TABLE 920179407Sobrien}; 921