#define COFF_WITH_PE
#define COFF_LONG_SECTION_NAMES
#define PCRELOFFSET TRUE
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "coff/mipspe.h"
#include "coff/internal.h"
#include "coff/pe.h"
#include "libcoff.h"
#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER 2
#define COFF_PAGE_SIZE 0x1000
static bfd_reloc_status_type
coff_mips_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void * data,
asection *input_section ATTRIBUTE_UNUSED,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
symvalue diff;
if (output_bfd == NULL)
return bfd_reloc_continue;
if (bfd_is_com_section (symbol->section))
{
#ifndef COFF_WITH_PE
diff = symbol->value + reloc_entry->addend;
#else
diff = reloc_entry->addend;
#endif
}
else
diff = reloc_entry->addend;
#define DOIT(x) \
x = ((x & ~howto->dst_mask) | (((x & howto->src_mask) + (diff >> howto->rightshift)) & howto->dst_mask))
if (diff != 0)
{
reloc_howto_type *howto = reloc_entry->howto;
unsigned char *addr = (unsigned char *) data + reloc_entry->address;
switch (howto->size)
{
case 0:
{
char x = bfd_get_8 (abfd, addr);
DOIT (x);
bfd_put_8 (abfd, x, addr);
}
break;
case 1:
{
short x = bfd_get_16 (abfd, addr);
DOIT (x);
bfd_put_16 (abfd, (bfd_vma) x, addr);
}
break;
case 2:
{
long x = bfd_get_32 (abfd, addr);
DOIT (x);
bfd_put_32 (abfd, (bfd_vma) x, addr);
}
break;
default:
abort ();
}
}
return bfd_reloc_continue;
}
#ifdef COFF_WITH_PE
static bfd_boolean
in_reloc_p (bfd * abfd ATTRIBUTE_UNUSED, reloc_howto_type *howto)
{
return ! howto->pc_relative && howto->type != MIPS_R_RVA;
}
#endif
#ifndef PCRELOFFSET
#define PCRELOFFSET FALSE
#endif
static reloc_howto_type howto_table[] =
{
HOWTO (MIPS_R_ABSOLUTE,
0,
0,
8,
FALSE,
0,
complain_overflow_dont,
0,
"IGNORE",
FALSE,
0,
0,
FALSE),
HOWTO (MIPS_R_REFHALF,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
coff_mips_reloc,
"REFHALF",
TRUE,
0xffff,
0xffff,
FALSE),
HOWTO (MIPS_R_REFWORD,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_mips_reloc,
"REFWORD",
TRUE,
0xffffffff,
0xffffffff,
FALSE),
HOWTO (MIPS_R_JMPADDR,
2,
2,
26,
FALSE,
0,
complain_overflow_dont,
coff_mips_reloc,
"JMPADDR",
TRUE,
0x3ffffff,
0x3ffffff,
FALSE),
HOWTO (MIPS_R_REFHI,
16,
2,
16,
FALSE,
0,
complain_overflow_bitfield,
coff_mips_reloc,
"REFHI",
TRUE,
0xffff,
0xffff,
FALSE),
HOWTO (MIPS_R_REFLO,
0,
2,
16,
FALSE,
0,
complain_overflow_dont,
coff_mips_reloc,
"REFLO",
TRUE,
0xffff,
0xffff,
FALSE),
HOWTO (MIPS_R_GPREL,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
coff_mips_reloc,
"GPREL",
TRUE,
0xffff,
0xffff,
FALSE),
HOWTO (MIPS_R_LITERAL,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
coff_mips_reloc,
"LITERAL",
TRUE,
0xffff,
0xffff,
FALSE),
EMPTY_HOWTO (8),
EMPTY_HOWTO (9),
EMPTY_HOWTO (10),
EMPTY_HOWTO (11),
EMPTY_HOWTO (12),
EMPTY_HOWTO (13),
EMPTY_HOWTO (14),
EMPTY_HOWTO (15),
EMPTY_HOWTO (16),
EMPTY_HOWTO (17),
EMPTY_HOWTO (18),
EMPTY_HOWTO (19),
EMPTY_HOWTO (20),
EMPTY_HOWTO (21),
EMPTY_HOWTO (22),
EMPTY_HOWTO (23),
EMPTY_HOWTO (24),
EMPTY_HOWTO (25),
EMPTY_HOWTO (26),
EMPTY_HOWTO (27),
EMPTY_HOWTO (28),
EMPTY_HOWTO (29),
EMPTY_HOWTO (30),
EMPTY_HOWTO (31),
EMPTY_HOWTO (32),
EMPTY_HOWTO (33),
HOWTO (MIPS_R_RVA,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_mips_reloc,
"rva32",
TRUE,
0xffffffff,
0xffffffff,
FALSE),
EMPTY_HOWTO (35),
EMPTY_HOWTO (36),
HOWTO (MIPS_R_PAIR,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_mips_reloc,
"PAIR",
TRUE,
0xffffffff,
0xffffffff,
FALSE),
};
#define SELECT_RELOC(x, howto) { x.r_type = howto->type; }
#define BADMAG(x) MIPSBADMAG (x)
#define MIPS 1
#define RTYPE2HOWTO(cache_ptr, dst) \
(cache_ptr)->howto = howto_table + (dst)->r_type;
#define CALC_ADDEND(abfd, ptr, reloc, cache_ptr) \
{ \
coff_symbol_type *coffsym = NULL; \
if (ptr && bfd_asymbol_bfd (ptr) != abfd) \
coffsym = (obj_symbols (abfd) \
+ (cache_ptr->sym_ptr_ptr - symbols)); \
else if (ptr) \
coffsym = coff_symbol_from (abfd, ptr); \
if (coffsym != NULL \
&& coffsym->native->u.syment.n_scnum == 0) \
cache_ptr->addend = - coffsym->native->u.syment.n_value; \
else if (ptr && bfd_asymbol_bfd (ptr) == abfd \
&& ptr->section != NULL) \
cache_ptr->addend = - (ptr->section->vma + ptr->value); \
else \
cache_ptr->addend = 0; \
if (ptr && howto_table[reloc.r_type].pc_relative) \
cache_ptr->addend += asect->vma; \
}
static reloc_howto_type *
coff_mips_rtype_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec,
struct internal_reloc *rel,
struct coff_link_hash_entry *h,
struct internal_syment *sym,
bfd_vma *addendp)
{
reloc_howto_type *howto;
howto = howto_table + rel->r_type;
#ifdef COFF_WITH_PE
*addendp = 0;
#endif
if (howto->pc_relative)
*addendp += sec->vma;
if (sym != NULL && sym->n_scnum == 0 && sym->n_value != 0)
{
BFD_ASSERT (h != NULL);
#ifndef COFF_WITH_PE
*addendp -= sym->n_value;
#endif
}
#ifndef COFF_WITH_PE
if (h != NULL && h->root.type == bfd_link_hash_common)
*addendp += h->root.u.c.size;
#endif
#ifdef COFF_WITH_PE
if (howto->pc_relative)
{
*addendp -= 4;
if (sym != NULL && sym->n_scnum != 0)
*addendp -= sym->n_value;
}
if (rel->r_type == MIPS_R_RVA)
*addendp -= pe_data (sec->output_section->owner)->pe_opthdr.ImageBase;
#endif
return howto;
}
#define coff_rtype_to_howto coff_mips_rtype_to_howto
#define coff_bfd_reloc_type_lookup coff_mips_reloc_type_lookup
static reloc_howto_type *
coff_mips_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
bfd_reloc_code_real_type code)
{
int mips_type;
switch (code)
{
case BFD_RELOC_16:
mips_type = MIPS_R_REFHALF;
break;
case BFD_RELOC_32:
case BFD_RELOC_CTOR:
mips_type = MIPS_R_REFWORD;
break;
case BFD_RELOC_MIPS_JMP:
mips_type = MIPS_R_JMPADDR;
break;
case BFD_RELOC_HI16_S:
mips_type = MIPS_R_REFHI;
break;
case BFD_RELOC_LO16:
mips_type = MIPS_R_REFLO;
break;
case BFD_RELOC_GPREL16:
mips_type = MIPS_R_GPREL;
break;
case BFD_RELOC_MIPS_LITERAL:
mips_type = MIPS_R_LITERAL;
break;
case BFD_RELOC_RVA:
mips_type = MIPS_R_RVA;
break;
default:
return NULL;
}
return & howto_table [mips_type];
}
static void
mips_swap_reloc_in (bfd * abfd, void * src, void * dst)
{
static struct internal_reloc pair_prev;
RELOC *reloc_src = (RELOC *) src;
struct internal_reloc *reloc_dst = (struct internal_reloc *) dst;
reloc_dst->r_vaddr = H_GET_32 (abfd, reloc_src->r_vaddr);
reloc_dst->r_symndx = H_GET_S32 (abfd, reloc_src->r_symndx);
reloc_dst->r_type = H_GET_16 (abfd, reloc_src->r_type);
reloc_dst->r_size = 0;
reloc_dst->r_extern = 0;
reloc_dst->r_offset = 0;
switch (reloc_dst->r_type)
{
case MIPS_R_REFHI:
pair_prev = *reloc_dst;
break;
case MIPS_R_PAIR:
reloc_dst->r_offset = reloc_dst->r_symndx;
if (reloc_dst->r_offset & 0x8000)
reloc_dst->r_offset -= 0x10000;
reloc_dst->r_symndx = pair_prev.r_symndx;
break;
}
}
static unsigned int
mips_swap_reloc_out (bfd * abfd, void * src, void * dst)
{
static int prev_offset = 1;
static bfd_vma prev_addr = 0;
struct internal_reloc *reloc_src = (struct internal_reloc *)src;
struct external_reloc *reloc_dst = (struct external_reloc *)dst;
switch (reloc_src->r_type)
{
case MIPS_R_REFHI:
prev_addr = reloc_src->r_vaddr;
prev_offset = reloc_src->r_offset;
break;
case MIPS_R_REFLO:
if (reloc_src->r_vaddr == prev_addr)
{
H_PUT_32 (abfd, reloc_src->r_vaddr, reloc_dst->r_vaddr);
H_PUT_32 (abfd, reloc_src->r_symndx, reloc_dst->r_symndx);
H_PUT_16 (abfd, MIPS_R_PAIR, reloc_dst->r_type);
return RELSZ;
}
break;
}
H_PUT_32 (abfd, reloc_src->r_vaddr, reloc_dst->r_vaddr);
H_PUT_32 (abfd, reloc_src->r_symndx, reloc_dst->r_symndx);
H_PUT_16 (abfd, reloc_src->r_type, reloc_dst->r_type);
return RELSZ;
}
#define coff_swap_reloc_in mips_swap_reloc_in
#define coff_swap_reloc_out mips_swap_reloc_out
#define NO_COFF_RELOCS
static bfd_boolean
coff_pe_mips_relocate_section (bfd *output_bfd,
struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
bfd_byte *contents,
struct internal_reloc *relocs,
struct internal_syment *syms,
asection **sections)
{
bfd_vma gp;
bfd_boolean gp_undefined;
size_t adjust;
struct internal_reloc *rel;
struct internal_reloc *rel_end;
unsigned int i;
bfd_boolean got_lo;
if (info->relocatable)
{
(*_bfd_error_handler)
(_("%B: `ld -r' not supported with PE MIPS objects\n"), input_bfd);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
BFD_ASSERT (input_bfd->xvec->byteorder
== output_bfd->xvec->byteorder);
gp = _bfd_get_gp_value (output_bfd);
gp_undefined = (gp == 0) ? TRUE : FALSE;
got_lo = FALSE;
adjust = 0;
rel = relocs;
rel_end = rel + input_section->reloc_count;
for (i = 0; rel < rel_end; rel++, i++)
{
long symndx;
struct coff_link_hash_entry *h;
struct internal_syment *sym;
bfd_vma addend = 0;
bfd_vma val, tmp, targ, src, low;
reloc_howto_type *howto;
unsigned char *mem = contents + rel->r_vaddr;
symndx = rel->r_symndx;
if (symndx == -1)
{
h = NULL;
sym = NULL;
}
else
{
h = obj_coff_sym_hashes (input_bfd)[symndx];
sym = syms + symndx;
}
if (sym != NULL && sym->n_scnum != 0)
addend = - sym->n_value;
else
addend = 0;
howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
sym, &addend);
if (howto == NULL)
return FALSE;
if (howto->pc_relative && howto->pcrel_offset)
{
if (info->relocatable)
continue;
if (sym != NULL && sym->n_scnum != 0)
addend += sym->n_value;
}
val = 0;
if (h == NULL)
{
asection *sec;
if (symndx == -1)
{
sec = bfd_abs_section_ptr;
val = 0;
}
else
{
sec = sections[symndx];
val = (sec->output_section->vma
+ sec->output_offset
+ sym->n_value);
if (! obj_pe (input_bfd))
val -= sec->vma;
}
}
else
{
if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
asection *sec;
sec = h->root.u.def.section;
val = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
}
else if (! info->relocatable)
{
if (! ((*info->callbacks->undefined_symbol)
(info, h->root.root.string, input_bfd, input_section,
rel->r_vaddr - input_section->vma, TRUE)))
return FALSE;
}
}
src = rel->r_vaddr + input_section->output_section->vma
+ input_section->output_offset;
#define UI(x) (*_bfd_error_handler) (_("%B: unimplemented %s\n"), \
input_bfd, x); \
bfd_set_error (bfd_error_bad_value);
switch (rel->r_type)
{
case MIPS_R_ABSOLUTE:
break;
case MIPS_R_REFHALF:
UI ("refhalf");
break;
case MIPS_R_REFWORD:
tmp = bfd_get_32 (input_bfd, mem);
tmp += val;
bfd_put_32 (input_bfd, tmp, mem);
break;
case MIPS_R_JMPADDR:
tmp = bfd_get_32 (input_bfd, mem);
targ = val + (tmp & 0x03ffffff) * 4;
if ((src & 0xf0000000) != (targ & 0xf0000000))
{
(*_bfd_error_handler) (_("%B: jump too far away\n"), input_bfd);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
tmp &= 0xfc000000;
tmp |= (targ / 4) & 0x3ffffff;
bfd_put_32 (input_bfd, tmp, mem);
break;
case MIPS_R_REFHI:
tmp = bfd_get_32 (input_bfd, mem);
switch (rel[1].r_type)
{
case MIPS_R_PAIR:
targ = val + rel[1].r_offset + ((tmp & 0xffff) << 16);
break;
case MIPS_R_REFLO:
low = bfd_get_32 (input_bfd, contents + rel[1].r_vaddr);
low &= 0xffff;
if (low & 0x8000)
low -= 0x10000;
targ = val + low + ((tmp & 0xffff) << 16);
break;
default:
(*_bfd_error_handler) (_("%B: bad pair/reflo after refhi\n"),
input_bfd);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
tmp &= 0xffff0000;
tmp |= (targ >> 16) & 0xffff;
bfd_put_32 (input_bfd, tmp, mem);
break;
case MIPS_R_REFLO:
tmp = bfd_get_32 (input_bfd, mem);
targ = val + (tmp & 0xffff);
tmp &= 0xffff0000;
tmp |= targ & 0xffff;
bfd_put_32 (input_bfd, tmp, mem);
break;
case MIPS_R_GPREL:
case MIPS_R_LITERAL:
UI ("gprel");
break;
case MIPS_R_SECTION:
UI ("section");
break;
case MIPS_R_SECREL:
UI ("secrel");
break;
case MIPS_R_SECRELLO:
UI ("secrello");
break;
case MIPS_R_SECRELHI:
UI ("secrelhi");
break;
case MIPS_R_RVA:
tmp = bfd_get_32 (input_bfd, mem);
tmp += val
- pe_data (input_section->output_section->owner)->pe_opthdr.ImageBase;
bfd_put_32 (input_bfd, tmp, mem);
break;
case MIPS_R_PAIR:
break;
}
}
return TRUE;
}
#define coff_relocate_section coff_pe_mips_relocate_section
#ifdef TARGET_UNDERSCORE
static bfd_boolean
coff_mips_is_local_label_name (bfd *abfd, const char *name)
{
if (name[0] == 'L')
return TRUE;
return _bfd_coff_is_local_label_name (abfd, name);
}
#define coff_bfd_is_local_label_name coff_mips_is_local_label_name
#endif
#define COFF_NO_HACK_SCNHDR_SIZE
#include "coffcode.h"
const bfd_target
#ifdef TARGET_SYM
TARGET_SYM =
#else
mipslpe_vec =
#endif
{
#ifdef TARGET_NAME
TARGET_NAME,
#else
"pe-mips",
#endif
bfd_target_coff_flavour,
BFD_ENDIAN_LITTLE,
BFD_ENDIAN_LITTLE,
(HAS_RELOC | EXEC_P |
HAS_LINENO | HAS_DEBUG |
HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
#ifndef COFF_WITH_PE
(SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_CODE | SEC_DATA),
#else
(SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_CODE | SEC_DATA
| SEC_LINK_ONCE | SEC_LINK_DUPLICATES),
#endif
#ifdef TARGET_UNDERSCORE
TARGET_UNDERSCORE,
#else
0,
#endif
'/',
15,
bfd_getl64, bfd_getl_signed_64, bfd_putl64,
bfd_getl32, bfd_getl_signed_32, bfd_putl32,
bfd_getl16, bfd_getl_signed_16, bfd_putl16,
bfd_getl64, bfd_getl_signed_64, bfd_putl64,
bfd_getl32, bfd_getl_signed_32, bfd_putl32,
bfd_getl16, bfd_getl_signed_16, bfd_putl16,
{_bfd_dummy_target, coff_object_p,
bfd_generic_archive_p, coff_object_p},
{bfd_false, coff_mkobject, _bfd_generic_mkarchive,
bfd_false},
{bfd_false, coff_write_object_contents,
_bfd_write_archive_contents, bfd_false},
BFD_JUMP_TABLE_GENERIC (coff),
BFD_JUMP_TABLE_COPY (coff),
BFD_JUMP_TABLE_CORE (_bfd_nocore),
BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff),
BFD_JUMP_TABLE_SYMBOLS (coff),
BFD_JUMP_TABLE_RELOCS (coff),
BFD_JUMP_TABLE_WRITE (coff),
BFD_JUMP_TABLE_LINK (coff),
BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
NULL,
COFF_SWAP_TABLE
};