mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
3f0a5f17d7
Andrew Sadek <andrew.sadek.se@gmail.com> A new implemented feature in GCC Microblaze that allows Position Independent Code to run using Data Text Relative addressing instead of using Global Offset Table. Its aim was to make 'PIC' more efficient and flexible as elf size excess performance overhead were noticed when using GOT due to the indirect addressing. include/ChangeLog: * bfdlink.h (Add flag): Add new flag @ 'bfd_link_info' struct. * elf/microblaze.h (Add 3 new relocations): R_MICROBLAZE_TEXTPCREL_64, R_MICROBLAZE_TEXTREL_64 and R_MICROBLAZE_TEXTREL_32_LO for relax function. bfd/ChangeLog: * bfd/reloc.c (2 new BFD relocations): BFD_RELOC_MICROBLAZE_64_TEXTPCREL & BFD_RELOC_MICROBLAZE_64_TEXTPCREL * bfd/bfd-in2.h: Regenerate * bfd/libbfd.h: Regenerate * bfd/elf32-microblaze.c (Handle new relocs): define 'HOWTO' of 3 new relocs and handle them in both relocate and relax functions. (microblaze_elf_reloc_type_lookup): add mapping between for new bfd relocs. (microblaze_elf_relocate_section): Handle new relocs in case of elf relocation. (microblaze_elf_relax_section): Handle new relocs for elf relaxation. gas/ChangeLog: * gas/config/tc-microblaze.c (Handle new relocs directives in assembler): Handle new relocs from compiler output. (imm_types): add new imm types for data text relative addressing TEXT_OFFSET, TEXT_PC_OFFSET (md_convert_frag): conversion for BFD_RELOC_MICROBLAZE_64_TEXTPCREL, BFD_RELOC_MICROBLAZE_64_TEXTPCREL (md_apply_fix): apply fix for BFD_RELOC_MICROBLAZE_64_TEXTPCREL, BFD_RELOC_MICROBLAZE_64_TEXTPCREL (md_estimate_size_before_relax): estimate size for BFD_RELOC_MICROBLAZE_64_TEXTPCREL, BFD_RELOC_MICROBLAZE_64_TEXTPCREL (tc_gen_reloc): generate relocations for BFD_RELOC_MICROBLAZE_64_TEXTPCREL, BFD_RELOC_MICROBLAZE_64_TEXTPCREL ld/ChangeLog: * ld/lexsup.c (Add 2 ld options): (ld_options): add disable-multiple-abs-defs @ 'ld_options' array (parse_args): parse new option and pass flag to 'link_info' struct. * ld/ldlex.h (Add enum): add new enum @ 'option_values' enum. * ld/ld.texinfo (Add new option): Add description for 'disable-multiple-abs-defs' * ld/main.c: Initialize flags with false @ 'main'. Handle disable-multiple-abs-defs @ 'mutiple_definition'.
2565 lines
71 KiB
C
2565 lines
71 KiB
C
/* tc-microblaze.c -- Assemble code for Xilinx MicroBlaze
|
||
|
||
Copyright (C) 2009-2018 Free Software Foundation, Inc.
|
||
|
||
This file is part of GAS, the GNU Assembler.
|
||
|
||
GAS 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, or (at your option)
|
||
any later version.
|
||
|
||
GAS 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 GAS; see the file COPYING. If not, write to the Free
|
||
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
|
||
02110-1301, USA. */
|
||
|
||
#include "as.h"
|
||
#include <stdio.h>
|
||
#include "bfd.h"
|
||
#include "subsegs.h"
|
||
#define DEFINE_TABLE
|
||
#include "../opcodes/microblaze-opc.h"
|
||
#include "../opcodes/microblaze-opcm.h"
|
||
#include "safe-ctype.h"
|
||
#include <string.h>
|
||
#include <dwarf2dbg.h>
|
||
#include "aout/stab_gnu.h"
|
||
|
||
#ifndef streq
|
||
#define streq(a,b) (strcmp (a, b) == 0)
|
||
#endif
|
||
|
||
#define OPTION_EB (OPTION_MD_BASE + 0)
|
||
#define OPTION_EL (OPTION_MD_BASE + 1)
|
||
|
||
void microblaze_generate_symbol (char *sym);
|
||
static bfd_boolean check_spl_reg (unsigned *);
|
||
|
||
/* Several places in this file insert raw instructions into the
|
||
object. They should generate the instruction
|
||
and then use these four macros to crack the instruction value into
|
||
the appropriate byte values. */
|
||
#define INST_BYTE0(x) (target_big_endian ? (((x) >> 24) & 0xFF) : ((x) & 0xFF))
|
||
#define INST_BYTE1(x) (target_big_endian ? (((x) >> 16) & 0xFF) : (((x) >> 8) & 0xFF))
|
||
#define INST_BYTE2(x) (target_big_endian ? (((x) >> 8) & 0xFF) : (((x) >> 16) & 0xFF))
|
||
#define INST_BYTE3(x) (target_big_endian ? ((x) & 0xFF) : (((x) >> 24) & 0xFF))
|
||
|
||
/* This array holds the chars that always start a comment. If the
|
||
pre-processor is disabled, these aren't very useful. */
|
||
const char comment_chars[] = "#";
|
||
|
||
const char line_separator_chars[] = ";";
|
||
|
||
/* This array holds the chars that only start a comment at the beginning of
|
||
a line. */
|
||
const char line_comment_chars[] = "#";
|
||
|
||
const int md_reloc_size = 8; /* Size of relocation record. */
|
||
|
||
/* Chars that can be used to separate mant
|
||
from exp in floating point numbers. */
|
||
const char EXP_CHARS[] = "eE";
|
||
|
||
/* Chars that mean this number is a floating point constant
|
||
As in 0f12.456
|
||
or 0d1.2345e12. */
|
||
const char FLT_CHARS[] = "rRsSfFdDxXpP";
|
||
|
||
/* INST_PC_OFFSET and INST_NO_OFFSET are 0 and 1. */
|
||
#define UNDEFINED_PC_OFFSET 2
|
||
#define DEFINED_ABS_SEGMENT 3
|
||
#define DEFINED_PC_OFFSET 4
|
||
#define DEFINED_RO_SEGMENT 5
|
||
#define DEFINED_RW_SEGMENT 6
|
||
#define LARGE_DEFINED_PC_OFFSET 7
|
||
#define GOT_OFFSET 8
|
||
#define PLT_OFFSET 9
|
||
#define GOTOFF_OFFSET 10
|
||
#define TLSGD_OFFSET 11
|
||
#define TLSLD_OFFSET 12
|
||
#define TLSDTPMOD_OFFSET 13
|
||
#define TLSDTPREL_OFFSET 14
|
||
#define TLSGOTTPREL_OFFSET 15
|
||
#define TLSTPREL_OFFSET 16
|
||
#define TEXT_OFFSET 17
|
||
#define TEXT_PC_OFFSET 18
|
||
|
||
/* Initialize the relax table. */
|
||
const relax_typeS md_relax_table[] =
|
||
{
|
||
{ 1, 1, 0, 0 }, /* 0: Unused. */
|
||
{ 1, 1, 0, 0 }, /* 1: Unused. */
|
||
{ 1, 1, 0, 0 }, /* 2: Unused. */
|
||
{ 1, 1, 0, 0 }, /* 3: Unused. */
|
||
{ 32767, -32768, INST_WORD_SIZE, LARGE_DEFINED_PC_OFFSET }, /* 4: DEFINED_PC_OFFSET. */
|
||
{ 1, 1, 0, 0 }, /* 5: Unused. */
|
||
{ 1, 1, 0, 0 }, /* 6: Unused. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 7: LARGE_DEFINED_PC_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 8: GOT_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 9: PLT_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 10: GOTOFF_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 11: TLSGD_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 12: TLSLD_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*1, 0 }, /* 13: TLSDTPMOD_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 14: TLSDTPREL_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 15: TLSGOTTPREL_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 16: TLSTPREL_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 17: TEXT_OFFSET. */
|
||
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 } /* 18: TEXT_PC_OFFSET. */
|
||
};
|
||
|
||
static struct hash_control * opcode_hash_control; /* Opcode mnemonics. */
|
||
|
||
static segT sbss_segment = 0; /* Small bss section. */
|
||
static segT sbss2_segment = 0; /* Section not used. */
|
||
static segT sdata_segment = 0; /* Small data section. */
|
||
static segT sdata2_segment = 0; /* Small read-only section. */
|
||
static segT rodata_segment = 0; /* read-only section. */
|
||
|
||
/* Generate a symbol for stabs information. */
|
||
|
||
void
|
||
microblaze_generate_symbol (char *sym)
|
||
{
|
||
#define MICROBLAZE_FAKE_LABEL_NAME "XL0\001"
|
||
static int microblaze_label_count;
|
||
sprintf (sym, "%sL%d", MICROBLAZE_FAKE_LABEL_NAME, microblaze_label_count);
|
||
++microblaze_label_count;
|
||
}
|
||
|
||
/* Handle the section changing pseudo-ops. */
|
||
|
||
static void
|
||
microblaze_s_text (int ignore ATTRIBUTE_UNUSED)
|
||
{
|
||
#ifdef OBJ_ELF
|
||
obj_elf_text (ignore);
|
||
#else
|
||
s_text (ignore);
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
microblaze_s_data (int ignore ATTRIBUTE_UNUSED)
|
||
{
|
||
#ifdef OBJ_ELF
|
||
obj_elf_change_section (".data", SHT_PROGBITS, 0, SHF_ALLOC+SHF_WRITE,
|
||
0, 0, 0, 0);
|
||
#else
|
||
s_data (ignore);
|
||
#endif
|
||
}
|
||
|
||
/* Things in the .sdata segment are always considered to be in the small data section. */
|
||
|
||
static void
|
||
microblaze_s_sdata (int ignore ATTRIBUTE_UNUSED)
|
||
{
|
||
#ifdef OBJ_ELF
|
||
obj_elf_change_section (".sdata", SHT_PROGBITS, 0, SHF_ALLOC+SHF_WRITE,
|
||
0, 0, 0, 0);
|
||
#else
|
||
s_data (ignore);
|
||
#endif
|
||
}
|
||
|
||
/* Pseudo op to make file scope bss items. */
|
||
|
||
static void
|
||
microblaze_s_lcomm (int xxx ATTRIBUTE_UNUSED)
|
||
{
|
||
char *name;
|
||
char c;
|
||
char *p;
|
||
offsetT size;
|
||
symbolS *symbolP;
|
||
offsetT align;
|
||
char *pfrag;
|
||
int align2;
|
||
segT current_seg = now_seg;
|
||
subsegT current_subseg = now_subseg;
|
||
|
||
c = get_symbol_name (&name);
|
||
|
||
/* Just after name is now '\0'. */
|
||
p = input_line_pointer;
|
||
(void) restore_line_pointer (c);
|
||
SKIP_WHITESPACE ();
|
||
if (*input_line_pointer != ',')
|
||
{
|
||
as_bad (_("Expected comma after symbol-name: rest of line ignored."));
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
|
||
input_line_pointer++; /* skip ',' */
|
||
if ((size = get_absolute_expression ()) < 0)
|
||
{
|
||
as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
|
||
/* The third argument to .lcomm is the alignment. */
|
||
if (*input_line_pointer != ',')
|
||
align = 8;
|
||
else
|
||
{
|
||
++input_line_pointer;
|
||
align = get_absolute_expression ();
|
||
if (align <= 0)
|
||
{
|
||
as_warn (_("ignoring bad alignment"));
|
||
align = 8;
|
||
}
|
||
}
|
||
|
||
*p = 0;
|
||
symbolP = symbol_find_or_make (name);
|
||
*p = c;
|
||
|
||
if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
|
||
{
|
||
as_bad (_("Ignoring attempt to re-define symbol `%s'."),
|
||
S_GET_NAME (symbolP));
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
|
||
if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
|
||
{
|
||
as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
|
||
S_GET_NAME (symbolP),
|
||
(long) S_GET_VALUE (symbolP),
|
||
(long) size);
|
||
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
|
||
/* Allocate_bss. */
|
||
if (align)
|
||
{
|
||
/* Convert to a power of 2 alignment. */
|
||
for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2);
|
||
if (align != 1)
|
||
{
|
||
as_bad (_("Common alignment not a power of 2"));
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
align2 = 0;
|
||
|
||
record_alignment (current_seg, align2);
|
||
subseg_set (current_seg, current_subseg);
|
||
if (align2)
|
||
frag_align (align2, 0, 0);
|
||
if (S_GET_SEGMENT (symbolP) == current_seg)
|
||
symbol_get_frag (symbolP)->fr_symbol = 0;
|
||
symbol_set_frag (symbolP, frag_now);
|
||
pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
|
||
(char *) 0);
|
||
*pfrag = 0;
|
||
S_SET_SIZE (symbolP, size);
|
||
S_SET_SEGMENT (symbolP, current_seg);
|
||
subseg_set (current_seg, current_subseg);
|
||
demand_empty_rest_of_line ();
|
||
}
|
||
|
||
static void
|
||
microblaze_s_rdata (int localvar)
|
||
{
|
||
#ifdef OBJ_ELF
|
||
if (localvar == 0)
|
||
{
|
||
/* rodata. */
|
||
obj_elf_change_section (".rodata", SHT_PROGBITS, 0, SHF_ALLOC,
|
||
0, 0, 0, 0);
|
||
if (rodata_segment == 0)
|
||
rodata_segment = subseg_new (".rodata", 0);
|
||
}
|
||
else
|
||
{
|
||
/* 1 .sdata2. */
|
||
obj_elf_change_section (".sdata2", SHT_PROGBITS, 0, SHF_ALLOC,
|
||
0, 0, 0, 0);
|
||
}
|
||
#else
|
||
s_data (ignore);
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
microblaze_s_bss (int localvar)
|
||
{
|
||
#ifdef OBJ_ELF
|
||
if (localvar == 0) /* bss. */
|
||
obj_elf_change_section (".bss", SHT_NOBITS, 0, SHF_ALLOC+SHF_WRITE,
|
||
0, 0, 0, 0);
|
||
else if (localvar == 1)
|
||
{
|
||
/* sbss. */
|
||
obj_elf_change_section (".sbss", SHT_NOBITS, 0, SHF_ALLOC+SHF_WRITE,
|
||
0, 0, 0, 0);
|
||
if (sbss_segment == 0)
|
||
sbss_segment = subseg_new (".sbss", 0);
|
||
}
|
||
#else
|
||
s_data (ignore);
|
||
#endif
|
||
}
|
||
|
||
/* endp_p is always 1 as this func is called only for .end <funcname>
|
||
This func consumes the <funcname> and calls regular processing
|
||
s_func(1) with arg 1 (1 for end). */
|
||
|
||
static void
|
||
microblaze_s_func (int end_p ATTRIBUTE_UNUSED)
|
||
{
|
||
char *name;
|
||
restore_line_pointer (get_symbol_name (&name));
|
||
s_func (1);
|
||
}
|
||
|
||
/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */
|
||
|
||
static void
|
||
microblaze_s_weakext (int ignore ATTRIBUTE_UNUSED)
|
||
{
|
||
char *name;
|
||
int c;
|
||
symbolS *symbolP;
|
||
expressionS exp;
|
||
|
||
c = get_symbol_name (&name);
|
||
symbolP = symbol_find_or_make (name);
|
||
S_SET_WEAK (symbolP);
|
||
(void) restore_line_pointer (c);
|
||
|
||
SKIP_WHITESPACE ();
|
||
|
||
if (!is_end_of_line[(unsigned char) *input_line_pointer])
|
||
{
|
||
if (S_IS_DEFINED (symbolP))
|
||
{
|
||
as_bad ("Ignoring attempt to redefine symbol `%s'.",
|
||
S_GET_NAME (symbolP));
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
|
||
if (*input_line_pointer == ',')
|
||
{
|
||
++input_line_pointer;
|
||
SKIP_WHITESPACE ();
|
||
}
|
||
|
||
expression (&exp);
|
||
if (exp.X_op != O_symbol)
|
||
{
|
||
as_bad ("bad .weakext directive");
|
||
ignore_rest_of_line ();
|
||
return;
|
||
}
|
||
symbol_set_value_expression (symbolP, &exp);
|
||
}
|
||
|
||
demand_empty_rest_of_line ();
|
||
}
|
||
|
||
/* This table describes all the machine specific pseudo-ops the assembler
|
||
has to support. The fields are:
|
||
Pseudo-op name without dot
|
||
Function to call to execute this pseudo-op
|
||
Integer arg to pass to the function. */
|
||
/* If the pseudo-op is not found in this table, it searches in the obj-elf.c,
|
||
and then in the read.c table. */
|
||
const pseudo_typeS md_pseudo_table[] =
|
||
{
|
||
{"lcomm", microblaze_s_lcomm, 1},
|
||
{"data", microblaze_s_data, 0},
|
||
{"data8", cons, 1}, /* Same as byte. */
|
||
{"data16", cons, 2}, /* Same as hword. */
|
||
{"data32", cons, 4}, /* Same as word. */
|
||
{"ent", s_func, 0}, /* Treat ent as function entry point. */
|
||
{"end", microblaze_s_func, 1}, /* Treat end as function end point. */
|
||
{"gpword", s_rva, 4}, /* gpword label => store resolved label address in data section. */
|
||
{"weakext", microblaze_s_weakext, 0},
|
||
{"rodata", microblaze_s_rdata, 0},
|
||
{"sdata2", microblaze_s_rdata, 1},
|
||
{"sdata", microblaze_s_sdata, 0},
|
||
{"bss", microblaze_s_bss, 0},
|
||
{"sbss", microblaze_s_bss, 1},
|
||
{"text", microblaze_s_text, 0},
|
||
{"word", cons, 4},
|
||
{"frame", s_ignore, 0},
|
||
{"mask", s_ignore, 0}, /* Emitted by gcc. */
|
||
{NULL, NULL, 0}
|
||
};
|
||
|
||
/* This function is called once, at assembler startup time. This should
|
||
set up all the tables, etc that the MD part of the assembler needs. */
|
||
|
||
void
|
||
md_begin (void)
|
||
{
|
||
struct op_code_struct * opcode;
|
||
|
||
opcode_hash_control = hash_new ();
|
||
|
||
/* Insert unique names into hash table. */
|
||
for (opcode = opcodes; opcode->name; opcode ++)
|
||
hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
|
||
}
|
||
|
||
/* Try to parse a reg name. */
|
||
|
||
static char *
|
||
parse_reg (char * s, unsigned * reg)
|
||
{
|
||
unsigned tmpreg = 0;
|
||
|
||
/* Strip leading whitespace. */
|
||
while (ISSPACE (* s))
|
||
++ s;
|
||
|
||
if (strncasecmp (s, "rpc", 3) == 0)
|
||
{
|
||
*reg = REG_PC;
|
||
return s + 3;
|
||
}
|
||
else if (strncasecmp (s, "rmsr", 4) == 0)
|
||
{
|
||
*reg = REG_MSR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "rear", 4) == 0)
|
||
{
|
||
*reg = REG_EAR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "resr", 4) == 0)
|
||
{
|
||
*reg = REG_ESR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "rfsr", 4) == 0)
|
||
{
|
||
*reg = REG_FSR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "rbtr", 4) == 0)
|
||
{
|
||
*reg = REG_BTR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "redr", 4) == 0)
|
||
{
|
||
*reg = REG_EDR;
|
||
return s + 4;
|
||
}
|
||
/* MMU registers start. */
|
||
else if (strncasecmp (s, "rpid", 4) == 0)
|
||
{
|
||
*reg = REG_PID;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "rzpr", 4) == 0)
|
||
{
|
||
*reg = REG_ZPR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "rtlbx", 5) == 0)
|
||
{
|
||
*reg = REG_TLBX;
|
||
return s + 5;
|
||
}
|
||
else if (strncasecmp (s, "rtlblo", 6) == 0)
|
||
{
|
||
*reg = REG_TLBLO;
|
||
return s + 6;
|
||
}
|
||
else if (strncasecmp (s, "rtlbhi", 6) == 0)
|
||
{
|
||
*reg = REG_TLBHI;
|
||
return s + 6;
|
||
}
|
||
else if (strncasecmp (s, "rtlbsx", 6) == 0)
|
||
{
|
||
*reg = REG_TLBSX;
|
||
return s + 6;
|
||
}
|
||
/* MMU registers end. */
|
||
else if (strncasecmp (s, "rpvr", 4) == 0)
|
||
{
|
||
if (ISDIGIT (s[4]) && ISDIGIT (s[5]))
|
||
{
|
||
tmpreg = (s[4]-'0')*10 + s[5] - '0';
|
||
s += 6;
|
||
}
|
||
|
||
else if (ISDIGIT (s[4]))
|
||
{
|
||
tmpreg = s[4] - '0';
|
||
s += 5;
|
||
}
|
||
else
|
||
as_bad (_("register expected, but saw '%.6s'"), s);
|
||
if ((int) tmpreg >= MIN_PVR_REGNUM && tmpreg <= MAX_PVR_REGNUM)
|
||
*reg = REG_PVR + tmpreg;
|
||
else
|
||
{
|
||
as_bad (_("Invalid register number at '%.6s'"), s);
|
||
*reg = REG_PVR;
|
||
}
|
||
return s;
|
||
}
|
||
else if (strncasecmp (s, "rsp", 3) == 0)
|
||
{
|
||
*reg = REG_SP;
|
||
return s + 3;
|
||
}
|
||
else if (strncasecmp (s, "rfsl", 4) == 0)
|
||
{
|
||
if (ISDIGIT (s[4]) && ISDIGIT (s[5]))
|
||
{
|
||
tmpreg = (s[4] - '0') * 10 + s[5] - '0';
|
||
s += 6;
|
||
}
|
||
else if (ISDIGIT (s[4]))
|
||
{
|
||
tmpreg = s[4] - '0';
|
||
s += 5;
|
||
}
|
||
else
|
||
as_bad (_("register expected, but saw '%.6s'"), s);
|
||
|
||
if ((int) tmpreg >= MIN_REGNUM && tmpreg <= MAX_REGNUM)
|
||
*reg = tmpreg;
|
||
else
|
||
{
|
||
as_bad (_("Invalid register number at '%.6s'"), s);
|
||
*reg = 0;
|
||
}
|
||
return s;
|
||
}
|
||
/* Stack protection registers. */
|
||
else if (strncasecmp (s, "rshr", 4) == 0)
|
||
{
|
||
*reg = REG_SHR;
|
||
return s + 4;
|
||
}
|
||
else if (strncasecmp (s, "rslr", 4) == 0)
|
||
{
|
||
*reg = REG_SLR;
|
||
return s + 4;
|
||
}
|
||
else
|
||
{
|
||
if (TOLOWER (s[0]) == 'r')
|
||
{
|
||
if (ISDIGIT (s[1]) && ISDIGIT (s[2]))
|
||
{
|
||
tmpreg = (s[1] - '0') * 10 + s[2] - '0';
|
||
s += 3;
|
||
}
|
||
else if (ISDIGIT (s[1]))
|
||
{
|
||
tmpreg = s[1] - '0';
|
||
s += 2;
|
||
}
|
||
else
|
||
as_bad (_("register expected, but saw '%.6s'"), s);
|
||
|
||
if ((int)tmpreg >= MIN_REGNUM && tmpreg <= MAX_REGNUM)
|
||
*reg = tmpreg;
|
||
else
|
||
{
|
||
as_bad (_("Invalid register number at '%.6s'"), s);
|
||
*reg = 0;
|
||
}
|
||
return s;
|
||
}
|
||
}
|
||
as_bad (_("register expected, but saw '%.6s'"), s);
|
||
*reg = 0;
|
||
return s;
|
||
}
|
||
|
||
static char *
|
||
parse_exp (char *s, expressionS *e)
|
||
{
|
||
char *save;
|
||
char *new_pointer;
|
||
|
||
/* Skip whitespace. */
|
||
while (ISSPACE (* s))
|
||
++ s;
|
||
|
||
save = input_line_pointer;
|
||
input_line_pointer = s;
|
||
|
||
expression (e);
|
||
|
||
if (e->X_op == O_absent)
|
||
as_fatal (_("missing operand"));
|
||
|
||
new_pointer = input_line_pointer;
|
||
input_line_pointer = save;
|
||
|
||
return new_pointer;
|
||
}
|
||
|
||
/* Symbol modifiers (@GOT, @PLT, @GOTOFF). */
|
||
#define IMM_NONE 0
|
||
#define IMM_GOT 1
|
||
#define IMM_PLT 2
|
||
#define IMM_GOTOFF 3
|
||
#define IMM_TLSGD 4
|
||
#define IMM_TLSLD 5
|
||
#define IMM_TLSDTPMOD 6
|
||
#define IMM_TLSDTPREL 7
|
||
#define IMM_TLSTPREL 8
|
||
#define IMM_TXTREL 9
|
||
#define IMM_TXTPCREL 10
|
||
#define IMM_MAX 11
|
||
|
||
struct imm_type {
|
||
const char *isuffix; /* Suffix String */
|
||
int itype; /* Suffix Type */
|
||
int otype; /* Offset Type */
|
||
};
|
||
|
||
/* These are NOT in ascending order of type, GOTOFF is ahead to make
|
||
sure @GOTOFF does not get matched with @GOT */
|
||
static struct imm_type imm_types[] = {
|
||
{ "NONE", IMM_NONE , 0 },
|
||
{ "GOTOFF", IMM_GOTOFF , GOTOFF_OFFSET },
|
||
{ "GOT", IMM_GOT , GOT_OFFSET },
|
||
{ "PLT", IMM_PLT , PLT_OFFSET },
|
||
{ "TLSGD", IMM_TLSGD , TLSGD_OFFSET },
|
||
{ "TLSLDM", IMM_TLSLD, TLSLD_OFFSET },
|
||
{ "TLSDTPMOD", IMM_TLSDTPMOD, TLSDTPMOD_OFFSET },
|
||
{ "TLSDTPREL", IMM_TLSDTPREL, TLSDTPREL_OFFSET },
|
||
{ "TLSTPREL", IMM_TLSTPREL, TLSTPREL_OFFSET },
|
||
{ "TXTREL", IMM_TXTREL, TEXT_OFFSET },
|
||
{ "TXTPCREL", IMM_TXTPCREL, TEXT_PC_OFFSET }
|
||
};
|
||
|
||
static int
|
||
match_imm (const char *s, int *ilen)
|
||
{
|
||
int i;
|
||
int slen;
|
||
|
||
/* Check for matching suffix */
|
||
for (i = 1; i < IMM_MAX; i++)
|
||
{
|
||
slen = strlen (imm_types[i].isuffix);
|
||
|
||
if (strncmp (imm_types[i].isuffix, s, slen) == 0)
|
||
{
|
||
*ilen = slen;
|
||
return imm_types[i].itype;
|
||
}
|
||
} /* for */
|
||
*ilen = 0;
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
get_imm_otype (int itype)
|
||
{
|
||
int i, otype;
|
||
|
||
otype = 0;
|
||
/* Check for matching itype */
|
||
for (i = 1; i < IMM_MAX; i++)
|
||
{
|
||
if (imm_types[i].itype == itype)
|
||
{
|
||
otype = imm_types[i].otype;
|
||
break;
|
||
}
|
||
}
|
||
return otype;
|
||
}
|
||
|
||
static symbolS * GOT_symbol;
|
||
|
||
#define GOT_SYMBOL_NAME "_GLOBAL_OFFSET_TABLE_"
|
||
|
||
static char *
|
||
parse_imm (char * s, expressionS * e, offsetT min, offsetT max)
|
||
{
|
||
char *new_pointer;
|
||
char *atp;
|
||
int itype, ilen;
|
||
|
||
ilen = 0;
|
||
|
||
/* Find the start of "@GOT" or "@PLT" suffix (if any) */
|
||
for (atp = s; *atp != '@'; atp++)
|
||
if (is_end_of_line[(unsigned char) *atp])
|
||
break;
|
||
|
||
if (*atp == '@')
|
||
{
|
||
itype = match_imm (atp + 1, &ilen);
|
||
if (itype != 0)
|
||
{
|
||
*atp = 0;
|
||
e->X_md = itype;
|
||
}
|
||
else
|
||
{
|
||
atp = NULL;
|
||
e->X_md = 0;
|
||
ilen = 0;
|
||
}
|
||
*atp = 0;
|
||
}
|
||
else
|
||
{
|
||
atp = NULL;
|
||
e->X_md = 0;
|
||
}
|
||
|
||
if (atp && !GOT_symbol)
|
||
{
|
||
GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
|
||
}
|
||
|
||
new_pointer = parse_exp (s, e);
|
||
|
||
if (!GOT_symbol && ! strncmp (s, GOT_SYMBOL_NAME, 20))
|
||
{
|
||
GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
|
||
}
|
||
|
||
if (e->X_op == O_absent)
|
||
; /* An error message has already been emitted. */
|
||
else if ((e->X_op != O_constant && e->X_op != O_symbol) )
|
||
as_fatal (_("operand must be a constant or a label"));
|
||
else if (e->X_op == O_constant)
|
||
{
|
||
/* Special case: sign extend negative 32-bit values to offsetT size. */
|
||
if ((e->X_add_number >> 31) == 1)
|
||
e->X_add_number |= -((addressT) (1U << 31));
|
||
|
||
if (e->X_add_number < min || e->X_add_number > max)
|
||
{
|
||
as_fatal (_("operand must be absolute in range %lx..%lx, not %lx"),
|
||
(long) min, (long) max, (long) e->X_add_number);
|
||
}
|
||
}
|
||
|
||
if (atp)
|
||
{
|
||
*atp = '@'; /* restore back (needed?) */
|
||
if (new_pointer >= atp)
|
||
new_pointer += ilen + 1; /* sizeof (imm_suffix) + 1 for '@' */
|
||
}
|
||
return new_pointer;
|
||
}
|
||
|
||
static char *
|
||
check_got (int * got_type, int * got_len)
|
||
{
|
||
char *new_pointer;
|
||
char *atp;
|
||
char *past_got;
|
||
int first, second;
|
||
char *tmpbuf;
|
||
|
||
/* Find the start of "@GOT" or "@PLT" suffix (if any). */
|
||
for (atp = input_line_pointer; *atp != '@'; atp++)
|
||
if (is_end_of_line[(unsigned char) *atp])
|
||
return NULL;
|
||
|
||
if (strncmp (atp + 1, "GOTOFF", 5) == 0)
|
||
{
|
||
*got_len = 6;
|
||
*got_type = IMM_GOTOFF;
|
||
}
|
||
else if (strncmp (atp + 1, "GOT", 3) == 0)
|
||
{
|
||
*got_len = 3;
|
||
*got_type = IMM_GOT;
|
||
}
|
||
else if (strncmp (atp + 1, "PLT", 3) == 0)
|
||
{
|
||
*got_len = 3;
|
||
*got_type = IMM_PLT;
|
||
}
|
||
else
|
||
return NULL;
|
||
|
||
if (!GOT_symbol)
|
||
GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
|
||
|
||
first = atp - input_line_pointer;
|
||
|
||
past_got = atp + *got_len + 1;
|
||
for (new_pointer = past_got; !is_end_of_line[(unsigned char) *new_pointer++];)
|
||
;
|
||
second = new_pointer - past_got;
|
||
/* One extra byte for ' ' and one for NUL. */
|
||
tmpbuf = XNEWVEC (char, first + second + 2);
|
||
memcpy (tmpbuf, input_line_pointer, first);
|
||
tmpbuf[first] = ' '; /* @GOTOFF is replaced with a single space. */
|
||
memcpy (tmpbuf + first + 1, past_got, second);
|
||
tmpbuf[first + second + 1] = '\0';
|
||
|
||
return tmpbuf;
|
||
}
|
||
|
||
extern bfd_reloc_code_real_type
|
||
parse_cons_expression_microblaze (expressionS *exp, int size)
|
||
{
|
||
if (size == 4)
|
||
{
|
||
/* Handle @GOTOFF et.al. */
|
||
char *save, *gotfree_copy;
|
||
int got_len, got_type;
|
||
|
||
save = input_line_pointer;
|
||
gotfree_copy = check_got (& got_type, & got_len);
|
||
if (gotfree_copy)
|
||
input_line_pointer = gotfree_copy;
|
||
|
||
expression (exp);
|
||
|
||
if (gotfree_copy)
|
||
{
|
||
exp->X_md = got_type;
|
||
input_line_pointer = save + (input_line_pointer - gotfree_copy)
|
||
+ got_len;
|
||
free (gotfree_copy);
|
||
}
|
||
}
|
||
else
|
||
expression (exp);
|
||
return BFD_RELOC_NONE;
|
||
}
|
||
|
||
/* This is the guts of the machine-dependent assembler. STR points to a
|
||
machine dependent instruction. This function is supposed to emit
|
||
the frags/bytes it assembles to. */
|
||
|
||
static const char * str_microblaze_ro_anchor = "RO";
|
||
static const char * str_microblaze_rw_anchor = "RW";
|
||
|
||
static bfd_boolean
|
||
check_spl_reg (unsigned * reg)
|
||
{
|
||
if ((*reg == REG_MSR) || (*reg == REG_PC)
|
||
|| (*reg == REG_EAR) || (*reg == REG_ESR)
|
||
|| (*reg == REG_FSR) || (*reg == REG_BTR) || (*reg == REG_EDR)
|
||
|| (*reg == REG_PID) || (*reg == REG_ZPR)
|
||
|| (*reg == REG_TLBX) || (*reg == REG_TLBLO)
|
||
|| (*reg == REG_TLBHI) || (*reg == REG_TLBSX)
|
||
|| (*reg == REG_SHR) || (*reg == REG_SLR)
|
||
|| (*reg >= REG_PVR+MIN_PVR_REGNUM && *reg <= REG_PVR+MAX_PVR_REGNUM))
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/* Here we decide which fixups can be adjusted to make them relative to
|
||
the beginning of the section instead of the symbol. Basically we need
|
||
to make sure that the dynamic relocations are done correctly, so in
|
||
some cases we force the original symbol to be used. */
|
||
|
||
int
|
||
tc_microblaze_fix_adjustable (struct fix *fixP)
|
||
{
|
||
if (GOT_symbol && fixP->fx_subsy == GOT_symbol)
|
||
return 0;
|
||
|
||
if (fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOTOFF
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_GOTOFF
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOT
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGD
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSLD
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPMOD
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPREL
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSDTPREL
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL
|
||
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSTPREL)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
void
|
||
md_assemble (char * str)
|
||
{
|
||
char * op_start;
|
||
char * op_end;
|
||
struct op_code_struct * opcode, *opcode1;
|
||
char * output = NULL;
|
||
int nlen = 0;
|
||
int i;
|
||
unsigned long inst, inst1;
|
||
unsigned reg1;
|
||
unsigned reg2;
|
||
unsigned reg3;
|
||
unsigned isize;
|
||
unsigned int immed, temp;
|
||
expressionS exp;
|
||
char name[20];
|
||
|
||
/* Drop leading whitespace. */
|
||
while (ISSPACE (* str))
|
||
str ++;
|
||
|
||
/* Find the op code end. */
|
||
for (op_start = op_end = str;
|
||
*op_end && !is_end_of_line[(unsigned char) *op_end] && *op_end != ' ';
|
||
op_end++)
|
||
{
|
||
name[nlen] = op_start[nlen];
|
||
nlen++;
|
||
if (nlen == sizeof (name) - 1)
|
||
break;
|
||
}
|
||
|
||
name [nlen] = 0;
|
||
|
||
if (nlen == 0)
|
||
{
|
||
as_bad (_("can't find opcode "));
|
||
return;
|
||
}
|
||
|
||
opcode = (struct op_code_struct *) hash_find (opcode_hash_control, name);
|
||
if (opcode == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), name);
|
||
return;
|
||
}
|
||
|
||
inst = opcode->bit_sequence;
|
||
isize = 4;
|
||
|
||
switch (opcode->inst_type)
|
||
{
|
||
case INST_TYPE_RD_R1_R2:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®3); /* Get r2. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg3 = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (& reg1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (& reg2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (& reg3))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
if (streq (name, "sub"))
|
||
{
|
||
/* sub rd, r1, r2 becomes rsub rd, r2, r1. */
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg3 << RA_LOW) & RA_MASK;
|
||
inst |= (reg2 << RB_LOW) & RB_MASK;
|
||
}
|
||
else
|
||
{
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
inst |= (reg3 << RB_LOW) & RB_MASK;
|
||
}
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RD_R1_IMM:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
|
||
else
|
||
as_fatal (_("Error in statement syntax"));
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (& reg1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (& reg2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
if (exp.X_op != O_constant || exp.X_md == IMM_TXTPCREL)
|
||
{
|
||
const char *opc;
|
||
relax_substateT subtype;
|
||
|
||
if (streq (name, "lmi"))
|
||
as_fatal (_("lmi pseudo instruction should not use a label in imm field"));
|
||
else if (streq (name, "smi"))
|
||
as_fatal (_("smi pseudo instruction should not use a label in imm field"));
|
||
|
||
if (reg2 == REG_ROSDP)
|
||
opc = str_microblaze_ro_anchor;
|
||
else if (reg2 == REG_RWSDP)
|
||
opc = str_microblaze_rw_anchor;
|
||
else
|
||
opc = NULL;
|
||
if (exp.X_md != 0)
|
||
subtype = get_imm_otype(exp.X_md);
|
||
else
|
||
subtype = opcode->inst_offset_type;
|
||
|
||
output = frag_var (rs_machine_dependent,
|
||
isize * 2, /* maxm of 2 words. */
|
||
isize, /* minm of 1 word. */
|
||
subtype, /* PC-relative or not. */
|
||
exp.X_add_symbol,
|
||
exp.X_add_number,
|
||
(char *) opc);
|
||
immed = 0;
|
||
}
|
||
else
|
||
{
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
|
||
if (streq (name, "lmi") || streq (name, "smi"))
|
||
{
|
||
/* Load/store 32-d consecutive registers. Used on exit/entry
|
||
to subroutines to save and restore registers to stack.
|
||
Generate 32-d insts. */
|
||
int count;
|
||
|
||
count = 32 - reg1;
|
||
if (streq (name, "lmi"))
|
||
opcode = (struct op_code_struct *) hash_find (opcode_hash_control, "lwi");
|
||
else
|
||
opcode = (struct op_code_struct *) hash_find (opcode_hash_control, "swi");
|
||
if (opcode == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "lwi");
|
||
return;
|
||
}
|
||
inst = opcode->bit_sequence;
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
|
||
for (i = 0; i < count - 1; i++)
|
||
{
|
||
output[0] = INST_BYTE0 (inst);
|
||
output[1] = INST_BYTE1 (inst);
|
||
output[2] = INST_BYTE2 (inst);
|
||
output[3] = INST_BYTE3 (inst);
|
||
output = frag_more (isize);
|
||
immed = immed + 4;
|
||
reg1++;
|
||
inst = opcode->bit_sequence;
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
temp = immed & 0xFFFF8000;
|
||
if ((temp != 0) && (temp != 0xFFFF8000))
|
||
{
|
||
/* Needs an immediate inst. */
|
||
opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
|
||
if (opcode1 == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "imm");
|
||
return;
|
||
}
|
||
|
||
inst1 = opcode1->bit_sequence;
|
||
inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
|
||
output[0] = INST_BYTE0 (inst1);
|
||
output[1] = INST_BYTE1 (inst1);
|
||
output[2] = INST_BYTE2 (inst1);
|
||
output[3] = INST_BYTE3 (inst1);
|
||
output = frag_more (isize);
|
||
}
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
}
|
||
break;
|
||
|
||
case INST_TYPE_RD_R1_IMM5:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
|
||
else
|
||
as_fatal (_("Error in statement syntax"));
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (®2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
if (exp.X_op != O_constant)
|
||
as_warn (_("Symbol used as immediate for shift instruction"));
|
||
else
|
||
{
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
|
||
if (immed != (immed % 32))
|
||
{
|
||
as_warn (_("Shift value > 32. using <value %% 32>"));
|
||
immed = immed % 32;
|
||
}
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM5_MASK;
|
||
break;
|
||
|
||
case INST_TYPE_R1_R2:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r2. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (& reg1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (& reg2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RA_LOW) & RA_MASK;
|
||
inst |= (reg2 << RB_LOW) & RB_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RD_R1:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 =0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (®2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RD_RFSL:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, &immed); /* Get rfslN. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
immed = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (immed << IMM_LOW) & RFSL_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RD_IMM15:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM15, MAX_IMM15);
|
||
else
|
||
as_fatal (_("Error in statement syntax"));
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
if (exp.X_op != O_constant)
|
||
as_fatal (_("Symbol used as immediate value for msrset/msrclr instructions"));
|
||
else
|
||
{
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM15_MASK;
|
||
break;
|
||
|
||
case INST_TYPE_R1_RFSL:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, &immed); /* Get rfslN. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
immed = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & RFSL_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RFSL:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, &immed); /* Get rfslN. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
immed = 0;
|
||
}
|
||
inst |= (immed << IMM_LOW) & RFSL_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_R1:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RA_LOW) & RA_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
/* For tuqula insn...:) */
|
||
case INST_TYPE_RD:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RD_SPECIAL:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
|
||
if (reg2 == REG_MSR)
|
||
immed = opcode->immval_mask | REG_MSR_MASK;
|
||
else if (reg2 == REG_PC)
|
||
immed = opcode->immval_mask | REG_PC_MASK;
|
||
else if (reg2 == REG_EAR)
|
||
immed = opcode->immval_mask | REG_EAR_MASK;
|
||
else if (reg2 == REG_ESR)
|
||
immed = opcode->immval_mask | REG_ESR_MASK;
|
||
else if (reg2 == REG_FSR)
|
||
immed = opcode->immval_mask | REG_FSR_MASK;
|
||
else if (reg2 == REG_BTR)
|
||
immed = opcode->immval_mask | REG_BTR_MASK;
|
||
else if (reg2 == REG_EDR)
|
||
immed = opcode->immval_mask | REG_EDR_MASK;
|
||
else if (reg2 == REG_PID)
|
||
immed = opcode->immval_mask | REG_PID_MASK;
|
||
else if (reg2 == REG_ZPR)
|
||
immed = opcode->immval_mask | REG_ZPR_MASK;
|
||
else if (reg2 == REG_TLBX)
|
||
immed = opcode->immval_mask | REG_TLBX_MASK;
|
||
else if (reg2 == REG_TLBLO)
|
||
immed = opcode->immval_mask | REG_TLBLO_MASK;
|
||
else if (reg2 == REG_TLBHI)
|
||
immed = opcode->immval_mask | REG_TLBHI_MASK;
|
||
else if (reg2 == REG_SHR)
|
||
immed = opcode->immval_mask | REG_SHR_MASK;
|
||
else if (reg2 == REG_SLR)
|
||
immed = opcode->immval_mask | REG_SLR_MASK;
|
||
else if (reg2 >= (REG_PVR+MIN_PVR_REGNUM) && reg2 <= (REG_PVR+MAX_PVR_REGNUM))
|
||
immed = opcode->immval_mask | REG_PVR_MASK | reg2;
|
||
else
|
||
as_fatal (_("invalid value for special purpose register"));
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_SPECIAL_R1:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
|
||
if (reg1 == REG_MSR)
|
||
immed = opcode->immval_mask | REG_MSR_MASK;
|
||
else if (reg1 == REG_PC)
|
||
immed = opcode->immval_mask | REG_PC_MASK;
|
||
else if (reg1 == REG_EAR)
|
||
immed = opcode->immval_mask | REG_EAR_MASK;
|
||
else if (reg1 == REG_ESR)
|
||
immed = opcode->immval_mask | REG_ESR_MASK;
|
||
else if (reg1 == REG_FSR)
|
||
immed = opcode->immval_mask | REG_FSR_MASK;
|
||
else if (reg1 == REG_BTR)
|
||
immed = opcode->immval_mask | REG_BTR_MASK;
|
||
else if (reg1 == REG_EDR)
|
||
immed = opcode->immval_mask | REG_EDR_MASK;
|
||
else if (reg1 == REG_PID)
|
||
immed = opcode->immval_mask | REG_PID_MASK;
|
||
else if (reg1 == REG_ZPR)
|
||
immed = opcode->immval_mask | REG_ZPR_MASK;
|
||
else if (reg1 == REG_TLBX)
|
||
immed = opcode->immval_mask | REG_TLBX_MASK;
|
||
else if (reg1 == REG_TLBLO)
|
||
immed = opcode->immval_mask | REG_TLBLO_MASK;
|
||
else if (reg1 == REG_TLBHI)
|
||
immed = opcode->immval_mask | REG_TLBHI_MASK;
|
||
else if (reg1 == REG_TLBSX)
|
||
immed = opcode->immval_mask | REG_TLBSX_MASK;
|
||
else if (reg1 == REG_SHR)
|
||
immed = opcode->immval_mask | REG_SHR_MASK;
|
||
else if (reg1 == REG_SLR)
|
||
immed = opcode->immval_mask | REG_SLR_MASK;
|
||
else
|
||
as_fatal (_("invalid value for special purpose register"));
|
||
inst |= (reg2 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_R1_R2_SPECIAL:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r2. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 =0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (®2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
/* insn wic ra, rb => wic ra, ra, rb. */
|
||
inst |= (reg1 << RA_LOW) & RA_MASK;
|
||
inst |= (reg2 << RB_LOW) & RB_MASK;
|
||
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_RD_R2:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r2. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
if (check_spl_reg (®2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (reg2 << RB_LOW) & RB_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_R1_IMM:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get r1. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
|
||
else
|
||
as_fatal (_("Error in statement syntax"));
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
if (exp.X_op != O_constant)
|
||
{
|
||
char *opc = NULL;
|
||
relax_substateT subtype;
|
||
|
||
if (exp.X_md != 0)
|
||
subtype = get_imm_otype(exp.X_md);
|
||
else
|
||
subtype = opcode->inst_offset_type;
|
||
|
||
output = frag_var (rs_machine_dependent,
|
||
isize * 2, /* maxm of 2 words. */
|
||
isize, /* minm of 1 word. */
|
||
subtype, /* PC-relative or not. */
|
||
exp.X_add_symbol,
|
||
exp.X_add_number,
|
||
opc);
|
||
immed = 0;
|
||
}
|
||
else
|
||
{
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
|
||
temp = immed & 0xFFFF8000;
|
||
if ((temp != 0) && (temp != 0xFFFF8000))
|
||
{
|
||
/* Needs an immediate inst. */
|
||
opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
|
||
if (opcode1 == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "imm");
|
||
return;
|
||
}
|
||
|
||
inst1 = opcode1->bit_sequence;
|
||
inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
|
||
output[0] = INST_BYTE0 (inst1);
|
||
output[1] = INST_BYTE1 (inst1);
|
||
output[2] = INST_BYTE2 (inst1);
|
||
output[3] = INST_BYTE3 (inst1);
|
||
output = frag_more (isize);
|
||
}
|
||
|
||
inst |= (reg1 << RA_LOW) & RA_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
break;
|
||
|
||
case INST_TYPE_RD_IMM:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg1 = 0;
|
||
}
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
|
||
else
|
||
as_fatal (_("Error in statement syntax"));
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®1))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
if (exp.X_op != O_constant)
|
||
{
|
||
char *opc = NULL;
|
||
relax_substateT subtype;
|
||
|
||
if (exp.X_md != 0)
|
||
subtype = get_imm_otype(exp.X_md);
|
||
else
|
||
subtype = opcode->inst_offset_type;
|
||
|
||
output = frag_var (rs_machine_dependent,
|
||
isize * 2, /* maxm of 2 words. */
|
||
isize, /* minm of 1 word. */
|
||
subtype, /* PC-relative or not. */
|
||
exp.X_add_symbol,
|
||
exp.X_add_number,
|
||
opc);
|
||
immed = 0;
|
||
}
|
||
else
|
||
{
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
|
||
temp = immed & 0xFFFF8000;
|
||
if ((temp != 0) && (temp != 0xFFFF8000))
|
||
{
|
||
/* Needs an immediate inst. */
|
||
opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
|
||
if (opcode1 == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "imm");
|
||
return;
|
||
}
|
||
|
||
inst1 = opcode1->bit_sequence;
|
||
inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
|
||
output[0] = INST_BYTE0 (inst1);
|
||
output[1] = INST_BYTE1 (inst1);
|
||
output[2] = INST_BYTE2 (inst1);
|
||
output[3] = INST_BYTE3 (inst1);
|
||
output = frag_more (isize);
|
||
}
|
||
|
||
inst |= (reg1 << RD_LOW) & RD_MASK;
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
break;
|
||
|
||
case INST_TYPE_R2:
|
||
if (strcmp (op_end, ""))
|
||
op_end = parse_reg (op_end + 1, ®2); /* Get r2. */
|
||
else
|
||
{
|
||
as_fatal (_("Error in statement syntax"));
|
||
reg2 = 0;
|
||
}
|
||
|
||
/* Check for spl registers. */
|
||
if (check_spl_reg (®2))
|
||
as_fatal (_("Cannot use special register with this instruction"));
|
||
|
||
inst |= (reg2 << RB_LOW) & RB_MASK;
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_IMM:
|
||
if (streq (name, "imm"))
|
||
as_fatal (_("An IMM instruction should not be present in the .s file"));
|
||
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
|
||
|
||
if (exp.X_op != O_constant)
|
||
{
|
||
char *opc = NULL;
|
||
relax_substateT subtype;
|
||
|
||
if (exp.X_md != 0)
|
||
subtype = get_imm_otype(exp.X_md);
|
||
else
|
||
subtype = opcode->inst_offset_type;
|
||
|
||
output = frag_var (rs_machine_dependent,
|
||
isize * 2, /* maxm of 2 words. */
|
||
isize, /* minm of 1 word. */
|
||
subtype, /* PC-relative or not. */
|
||
exp.X_add_symbol,
|
||
exp.X_add_number,
|
||
opc);
|
||
immed = 0;
|
||
}
|
||
else
|
||
{
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
|
||
|
||
temp = immed & 0xFFFF8000;
|
||
if ((temp != 0) && (temp != 0xFFFF8000))
|
||
{
|
||
/* Needs an immediate inst. */
|
||
opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
|
||
if (opcode1 == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "imm");
|
||
return;
|
||
}
|
||
|
||
inst1 = opcode1->bit_sequence;
|
||
inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
|
||
output[0] = INST_BYTE0 (inst1);
|
||
output[1] = INST_BYTE1 (inst1);
|
||
output[2] = INST_BYTE2 (inst1);
|
||
output[3] = INST_BYTE3 (inst1);
|
||
output = frag_more (isize);
|
||
}
|
||
inst |= (immed << IMM_LOW) & IMM_MASK;
|
||
break;
|
||
|
||
case INST_TYPE_NONE:
|
||
output = frag_more (isize);
|
||
break;
|
||
|
||
case INST_TYPE_IMM5:
|
||
if (strcmp(op_end, ""))
|
||
op_end = parse_imm (op_end + 1, & exp, MIN_IMM5, MAX_IMM5);
|
||
else
|
||
as_fatal(_("Error in statement syntax"));
|
||
if (exp.X_op != O_constant) {
|
||
as_warn(_("Symbol used as immediate for mbar instruction"));
|
||
} else {
|
||
output = frag_more (isize);
|
||
immed = exp.X_add_number;
|
||
}
|
||
if (immed != (immed % 32)) {
|
||
as_warn(_("Immediate value for mbar > 32. using <value %% 32>"));
|
||
immed = immed % 32;
|
||
}
|
||
inst |= (immed << IMM_MBAR);
|
||
break;
|
||
|
||
default:
|
||
as_fatal (_("unimplemented opcode \"%s\""), name);
|
||
}
|
||
|
||
/* Drop whitespace after all the operands have been parsed. */
|
||
while (ISSPACE (* op_end))
|
||
op_end ++;
|
||
|
||
/* Give warning message if the insn has more operands than required. */
|
||
if (strcmp (op_end, opcode->name) && strcmp (op_end, ""))
|
||
as_warn (_("ignoring operands: %s "), op_end);
|
||
|
||
output[0] = INST_BYTE0 (inst);
|
||
output[1] = INST_BYTE1 (inst);
|
||
output[2] = INST_BYTE2 (inst);
|
||
output[3] = INST_BYTE3 (inst);
|
||
|
||
#ifdef OBJ_ELF
|
||
dwarf2_emit_insn (4);
|
||
#endif
|
||
}
|
||
|
||
symbolS *
|
||
md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
/* Various routines to kill one day. */
|
||
/* Equal to MAX_PRECISION in atof-ieee.c */
|
||
#define MAX_LITTLENUMS 6
|
||
|
||
/* Turn a string in input_line_pointer into a floating point constant of type
|
||
type, and store the appropriate bytes in *litP. The number of LITTLENUMS
|
||
emitted is stored in *sizeP. An error message is returned, or NULL on OK.*/
|
||
const char *
|
||
md_atof (int type, char * litP, int * sizeP)
|
||
{
|
||
int prec;
|
||
LITTLENUM_TYPE words[MAX_LITTLENUMS];
|
||
int i;
|
||
char * t;
|
||
|
||
switch (type)
|
||
{
|
||
case 'f':
|
||
case 'F':
|
||
case 's':
|
||
case 'S':
|
||
prec = 2;
|
||
break;
|
||
|
||
case 'd':
|
||
case 'D':
|
||
case 'r':
|
||
case 'R':
|
||
prec = 4;
|
||
break;
|
||
|
||
case 'x':
|
||
case 'X':
|
||
prec = 6;
|
||
break;
|
||
|
||
case 'p':
|
||
case 'P':
|
||
prec = 6;
|
||
break;
|
||
|
||
default:
|
||
*sizeP = 0;
|
||
return _("Bad call to MD_NTOF()");
|
||
}
|
||
|
||
t = atof_ieee (input_line_pointer, type, words);
|
||
|
||
if (t)
|
||
input_line_pointer = t;
|
||
|
||
*sizeP = prec * sizeof (LITTLENUM_TYPE);
|
||
|
||
if (! target_big_endian)
|
||
{
|
||
for (i = prec - 1; i >= 0; i--)
|
||
{
|
||
md_number_to_chars (litP, (valueT) words[i],
|
||
sizeof (LITTLENUM_TYPE));
|
||
litP += sizeof (LITTLENUM_TYPE);
|
||
}
|
||
}
|
||
else
|
||
for (i = 0; i < prec; i++)
|
||
{
|
||
md_number_to_chars (litP, (valueT) words[i],
|
||
sizeof (LITTLENUM_TYPE));
|
||
litP += sizeof (LITTLENUM_TYPE);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
const char * md_shortopts = "";
|
||
|
||
struct option md_longopts[] =
|
||
{
|
||
{"EB", no_argument, NULL, OPTION_EB},
|
||
{"EL", no_argument, NULL, OPTION_EL},
|
||
{ NULL, no_argument, NULL, 0}
|
||
};
|
||
|
||
size_t md_longopts_size = sizeof (md_longopts);
|
||
|
||
int md_short_jump_size;
|
||
|
||
void
|
||
md_create_short_jump (char * ptr ATTRIBUTE_UNUSED,
|
||
addressT from_Nddr ATTRIBUTE_UNUSED,
|
||
addressT to_Nddr ATTRIBUTE_UNUSED,
|
||
fragS * frag ATTRIBUTE_UNUSED,
|
||
symbolS * to_symbol ATTRIBUTE_UNUSED)
|
||
{
|
||
as_fatal (_("failed sanity check: short_jump"));
|
||
}
|
||
|
||
void
|
||
md_create_long_jump (char * ptr ATTRIBUTE_UNUSED,
|
||
addressT from_Nddr ATTRIBUTE_UNUSED,
|
||
addressT to_Nddr ATTRIBUTE_UNUSED,
|
||
fragS * frag ATTRIBUTE_UNUSED,
|
||
symbolS * to_symbol ATTRIBUTE_UNUSED)
|
||
{
|
||
as_fatal (_("failed sanity check: long_jump"));
|
||
}
|
||
|
||
/* Called after relaxing, change the frags so they know how big they are. */
|
||
|
||
void
|
||
md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
|
||
segT sec ATTRIBUTE_UNUSED,
|
||
fragS * fragP)
|
||
{
|
||
fixS *fixP;
|
||
|
||
switch (fragP->fr_subtype)
|
||
{
|
||
case UNDEFINED_PC_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, TRUE, BFD_RELOC_64_PCREL);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case DEFINED_ABS_SEGMENT:
|
||
if (fragP->fr_symbol == GOT_symbol)
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, TRUE, BFD_RELOC_MICROBLAZE_64_GOTPC);
|
||
else
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_64);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case DEFINED_RO_SEGMENT:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_32_ROSDA);
|
||
fragP->fr_fix += INST_WORD_SIZE;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case DEFINED_RW_SEGMENT:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_32_RWSDA);
|
||
fragP->fr_fix += INST_WORD_SIZE;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case DEFINED_PC_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE, fragP->fr_symbol,
|
||
fragP->fr_offset, TRUE, BFD_RELOC_MICROBLAZE_32_LO_PCREL);
|
||
fragP->fr_fix += INST_WORD_SIZE;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case LARGE_DEFINED_PC_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, TRUE, BFD_RELOC_64_PCREL);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case GOT_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_GOT);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case TEXT_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TEXTREL);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case TEXT_PC_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TEXTPCREL);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case PLT_OFFSET:
|
||
fixP = fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, TRUE, BFD_RELOC_MICROBLAZE_64_PLT);
|
||
/* fixP->fx_plt = 1; */
|
||
(void) fixP;
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case GOTOFF_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_GOTOFF);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case TLSGD_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSGD);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case TLSLD_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSLD);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
case TLSDTPREL_OFFSET:
|
||
fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
|
||
fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSDTPREL);
|
||
fragP->fr_fix += INST_WORD_SIZE * 2;
|
||
fragP->fr_var = 0;
|
||
break;
|
||
|
||
default:
|
||
abort ();
|
||
}
|
||
}
|
||
|
||
/* Applies the desired value to the specified location.
|
||
Also sets up addends for 'rela' type relocations. */
|
||
void
|
||
md_apply_fix (fixS * fixP,
|
||
valueT * valp,
|
||
segT segment)
|
||
{
|
||
char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
|
||
const char * file = fixP->fx_file ? fixP->fx_file : _("unknown");
|
||
const char * symname;
|
||
/* Note: use offsetT because it is signed, valueT is unsigned. */
|
||
offsetT val = (offsetT) * valp;
|
||
int i;
|
||
struct op_code_struct * opcode1;
|
||
unsigned long inst1;
|
||
|
||
symname = fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : _("<unknown>");
|
||
|
||
/* fixP->fx_offset is supposed to be set up correctly for all
|
||
symbol relocations. */
|
||
if (fixP->fx_addsy == NULL)
|
||
{
|
||
if (!fixP->fx_pcrel)
|
||
fixP->fx_offset = val; /* Absolute relocation. */
|
||
else
|
||
fprintf (stderr, "NULL symbol PC-relative relocation? offset = %08x, val = %08x\n",
|
||
(unsigned int) fixP->fx_offset, (unsigned int) val);
|
||
}
|
||
|
||
/* If we aren't adjusting this fixup to be against the section
|
||
symbol, we need to adjust the value. */
|
||
if (fixP->fx_addsy != NULL)
|
||
{
|
||
if (S_IS_WEAK (fixP->fx_addsy)
|
||
|| (symbol_used_in_reloc_p (fixP->fx_addsy)
|
||
&& (((bfd_get_section_flags (stdoutput,
|
||
S_GET_SEGMENT (fixP->fx_addsy))
|
||
& SEC_LINK_ONCE) != 0)
|
||
|| !strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
|
||
".gnu.linkonce",
|
||
sizeof (".gnu.linkonce") - 1))))
|
||
{
|
||
val -= S_GET_VALUE (fixP->fx_addsy);
|
||
if (val != 0 && ! fixP->fx_pcrel)
|
||
{
|
||
/* In this case, the bfd_install_relocation routine will
|
||
incorrectly add the symbol value back in. We just want
|
||
the addend to appear in the object file.
|
||
FIXME: If this makes VALUE zero, we're toast. */
|
||
val -= S_GET_VALUE (fixP->fx_addsy);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* If the fix is relative to a symbol which is not defined, or not
|
||
in the same segment as the fix, we cannot resolve it here. */
|
||
/* fixP->fx_addsy is NULL if valp contains the entire relocation. */
|
||
if (fixP->fx_addsy != NULL
|
||
&& (!S_IS_DEFINED (fixP->fx_addsy)
|
||
|| (S_GET_SEGMENT (fixP->fx_addsy) != segment)))
|
||
{
|
||
fixP->fx_done = 0;
|
||
#ifdef OBJ_ELF
|
||
/* For ELF we can just return and let the reloc that will be generated
|
||
take care of everything. For COFF we still have to insert 'val'
|
||
into the insn since the addend field will be ignored. */
|
||
/* return; */
|
||
#endif
|
||
}
|
||
/* All fixups in the text section must be handled in the linker. */
|
||
else if (segment->flags & SEC_CODE)
|
||
fixP->fx_done = 0;
|
||
else if (!fixP->fx_pcrel && fixP->fx_addsy != NULL)
|
||
fixP->fx_done = 0;
|
||
else
|
||
fixP->fx_done = 1;
|
||
|
||
switch (fixP->fx_r_type)
|
||
{
|
||
case BFD_RELOC_MICROBLAZE_32_LO:
|
||
case BFD_RELOC_MICROBLAZE_32_LO_PCREL:
|
||
if (target_big_endian)
|
||
{
|
||
buf[2] |= ((val >> 8) & 0xff);
|
||
buf[3] |= (val & 0xff);
|
||
}
|
||
else
|
||
{
|
||
buf[1] |= ((val >> 8) & 0xff);
|
||
buf[0] |= (val & 0xff);
|
||
}
|
||
break;
|
||
case BFD_RELOC_MICROBLAZE_32_ROSDA:
|
||
case BFD_RELOC_MICROBLAZE_32_RWSDA:
|
||
/* Don't do anything if the symbol is not defined. */
|
||
if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
|
||
{
|
||
if (((val & 0xFFFF8000) != 0) && ((val & 0xFFFF8000) != 0xFFFF8000))
|
||
as_bad_where (file, fixP->fx_line,
|
||
_("pcrel for branch to %s too far (0x%x)"),
|
||
symname, (int) val);
|
||
if (target_big_endian)
|
||
{
|
||
buf[2] |= ((val >> 8) & 0xff);
|
||
buf[3] |= (val & 0xff);
|
||
}
|
||
else
|
||
{
|
||
buf[1] |= ((val >> 8) & 0xff);
|
||
buf[0] |= (val & 0xff);
|
||
}
|
||
}
|
||
break;
|
||
case BFD_RELOC_32:
|
||
case BFD_RELOC_RVA:
|
||
case BFD_RELOC_32_PCREL:
|
||
case BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM:
|
||
/* Don't do anything if the symbol is not defined. */
|
||
if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
|
||
{
|
||
if (target_big_endian)
|
||
{
|
||
buf[0] |= ((val >> 24) & 0xff);
|
||
buf[1] |= ((val >> 16) & 0xff);
|
||
buf[2] |= ((val >> 8) & 0xff);
|
||
buf[3] |= (val & 0xff);
|
||
}
|
||
else
|
||
{
|
||
buf[3] |= ((val >> 24) & 0xff);
|
||
buf[2] |= ((val >> 16) & 0xff);
|
||
buf[1] |= ((val >> 8) & 0xff);
|
||
buf[0] |= (val & 0xff);
|
||
}
|
||
}
|
||
break;
|
||
case BFD_RELOC_64_PCREL:
|
||
case BFD_RELOC_64:
|
||
case BFD_RELOC_MICROBLAZE_64_TEXTREL:
|
||
/* Add an imm instruction. First save the current instruction. */
|
||
for (i = 0; i < INST_WORD_SIZE; i++)
|
||
buf[i + INST_WORD_SIZE] = buf[i];
|
||
|
||
/* Generate the imm instruction. */
|
||
opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
|
||
if (opcode1 == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "imm");
|
||
return;
|
||
}
|
||
|
||
inst1 = opcode1->bit_sequence;
|
||
if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
|
||
inst1 |= ((val & 0xFFFF0000) >> 16) & IMM_MASK;
|
||
|
||
buf[0] = INST_BYTE0 (inst1);
|
||
buf[1] = INST_BYTE1 (inst1);
|
||
buf[2] = INST_BYTE2 (inst1);
|
||
buf[3] = INST_BYTE3 (inst1);
|
||
|
||
/* Add the value only if the symbol is defined. */
|
||
if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
|
||
{
|
||
if (target_big_endian)
|
||
{
|
||
buf[6] |= ((val >> 8) & 0xff);
|
||
buf[7] |= (val & 0xff);
|
||
}
|
||
else
|
||
{
|
||
buf[5] |= ((val >> 8) & 0xff);
|
||
buf[4] |= (val & 0xff);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSGD:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSLD:
|
||
S_SET_THREAD_LOCAL (fixP->fx_addsy);
|
||
/* Fall through. */
|
||
|
||
case BFD_RELOC_MICROBLAZE_64_GOTPC:
|
||
case BFD_RELOC_MICROBLAZE_64_GOT:
|
||
case BFD_RELOC_MICROBLAZE_64_PLT:
|
||
case BFD_RELOC_MICROBLAZE_64_GOTOFF:
|
||
case BFD_RELOC_MICROBLAZE_64_TEXTPCREL:
|
||
/* Add an imm instruction. First save the current instruction. */
|
||
for (i = 0; i < INST_WORD_SIZE; i++)
|
||
buf[i + INST_WORD_SIZE] = buf[i];
|
||
|
||
/* Generate the imm instruction. */
|
||
opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
|
||
if (opcode1 == NULL)
|
||
{
|
||
as_bad (_("unknown opcode \"%s\""), "imm");
|
||
return;
|
||
}
|
||
|
||
inst1 = opcode1->bit_sequence;
|
||
|
||
/* We can fixup call to a defined non-global address
|
||
within the same section only. */
|
||
buf[0] = INST_BYTE0 (inst1);
|
||
buf[1] = INST_BYTE1 (inst1);
|
||
buf[2] = INST_BYTE2 (inst1);
|
||
buf[3] = INST_BYTE3 (inst1);
|
||
return;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (fixP->fx_addsy == NULL)
|
||
{
|
||
/* This fixup has been resolved. Create a reloc in case the linker
|
||
moves code around due to relaxing. */
|
||
if (fixP->fx_r_type == BFD_RELOC_64_PCREL)
|
||
fixP->fx_r_type = BFD_RELOC_MICROBLAZE_64_NONE;
|
||
else
|
||
fixP->fx_r_type = BFD_RELOC_NONE;
|
||
fixP->fx_addsy = section_symbol (absolute_section);
|
||
}
|
||
return;
|
||
}
|
||
|
||
void
|
||
md_operand (expressionS * expressionP)
|
||
{
|
||
/* Ignore leading hash symbol, if present. */
|
||
if (*input_line_pointer == '#')
|
||
{
|
||
input_line_pointer ++;
|
||
expression (expressionP);
|
||
}
|
||
}
|
||
|
||
/* Called just before address relaxation, return the length
|
||
by which a fragment must grow to reach it's destination. */
|
||
|
||
int
|
||
md_estimate_size_before_relax (fragS * fragP,
|
||
segT segment_type)
|
||
{
|
||
sbss_segment = bfd_get_section_by_name (stdoutput, ".sbss");
|
||
sbss2_segment = bfd_get_section_by_name (stdoutput, ".sbss2");
|
||
sdata_segment = bfd_get_section_by_name (stdoutput, ".sdata");
|
||
sdata2_segment = bfd_get_section_by_name (stdoutput, ".sdata2");
|
||
|
||
switch (fragP->fr_subtype)
|
||
{
|
||
case INST_PC_OFFSET:
|
||
/* Used to be a PC-relative branch. */
|
||
if (!fragP->fr_symbol)
|
||
{
|
||
/* We know the abs value: Should never happen. */
|
||
as_bad (_("Absolute PC-relative value in relaxation code. Assembler error....."));
|
||
abort ();
|
||
}
|
||
else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type &&
|
||
!S_IS_WEAK (fragP->fr_symbol))
|
||
{
|
||
fragP->fr_subtype = DEFINED_PC_OFFSET;
|
||
/* Don't know now whether we need an imm instruction. */
|
||
fragP->fr_var = INST_WORD_SIZE;
|
||
}
|
||
else if (S_IS_DEFINED (fragP->fr_symbol)
|
||
&& (((S_GET_SEGMENT (fragP->fr_symbol))->flags & SEC_CODE) == 0))
|
||
{
|
||
/* Cannot have a PC-relative branch to a diff segment. */
|
||
as_bad (_("PC relative branch to label %s which is not in the instruction space"),
|
||
S_GET_NAME (fragP->fr_symbol));
|
||
fragP->fr_subtype = UNDEFINED_PC_OFFSET;
|
||
fragP->fr_var = INST_WORD_SIZE*2;
|
||
}
|
||
else
|
||
{
|
||
fragP->fr_subtype = UNDEFINED_PC_OFFSET;
|
||
fragP->fr_var = INST_WORD_SIZE*2;
|
||
}
|
||
break;
|
||
|
||
case INST_NO_OFFSET:
|
||
case TEXT_OFFSET:
|
||
/* Used to be a reference to somewhere which was unknown. */
|
||
if (fragP->fr_symbol)
|
||
{
|
||
if (fragP->fr_opcode == NULL)
|
||
{
|
||
/* Used as an absolute value. */
|
||
if (fragP->fr_subtype == INST_NO_OFFSET)
|
||
fragP->fr_subtype = DEFINED_ABS_SEGMENT;
|
||
/* Variable part does not change. */
|
||
fragP->fr_var = INST_WORD_SIZE*2;
|
||
}
|
||
else if (streq (fragP->fr_opcode, str_microblaze_ro_anchor))
|
||
{
|
||
/* It is accessed using the small data read only anchor. */
|
||
if ((S_GET_SEGMENT (fragP->fr_symbol) == bfd_com_section_ptr)
|
||
|| (S_GET_SEGMENT (fragP->fr_symbol) == sdata2_segment)
|
||
|| (S_GET_SEGMENT (fragP->fr_symbol) == sbss2_segment)
|
||
|| (! S_IS_DEFINED (fragP->fr_symbol)))
|
||
{
|
||
fragP->fr_subtype = DEFINED_RO_SEGMENT;
|
||
fragP->fr_var = INST_WORD_SIZE;
|
||
}
|
||
else
|
||
{
|
||
/* Variable not in small data read only segment accessed
|
||
using small data read only anchor. */
|
||
const char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
|
||
|
||
as_bad_where (file, fragP->fr_line,
|
||
_("Variable is accessed using small data read "
|
||
"only anchor, but it is not in the small data "
|
||
"read only section"));
|
||
fragP->fr_subtype = DEFINED_RO_SEGMENT;
|
||
fragP->fr_var = INST_WORD_SIZE;
|
||
}
|
||
}
|
||
else if (streq (fragP->fr_opcode, str_microblaze_rw_anchor))
|
||
{
|
||
if ((S_GET_SEGMENT (fragP->fr_symbol) == bfd_com_section_ptr)
|
||
|| (S_GET_SEGMENT (fragP->fr_symbol) == sdata_segment)
|
||
|| (S_GET_SEGMENT (fragP->fr_symbol) == sbss_segment)
|
||
|| (!S_IS_DEFINED (fragP->fr_symbol)))
|
||
{
|
||
/* It is accessed using the small data read write anchor. */
|
||
fragP->fr_subtype = DEFINED_RW_SEGMENT;
|
||
fragP->fr_var = INST_WORD_SIZE;
|
||
}
|
||
else
|
||
{
|
||
const char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
|
||
|
||
as_bad_where (file, fragP->fr_line,
|
||
_("Variable is accessed using small data read "
|
||
"write anchor, but it is not in the small data "
|
||
"read write section"));
|
||
fragP->fr_subtype = DEFINED_RW_SEGMENT;
|
||
fragP->fr_var = INST_WORD_SIZE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
as_bad (_("Incorrect fr_opcode value in frag. Internal error....."));
|
||
abort ();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* We know the abs value: Should never happen. */
|
||
as_bad (_("Absolute value in relaxation code. Assembler error....."));
|
||
abort ();
|
||
}
|
||
break;
|
||
|
||
case UNDEFINED_PC_OFFSET:
|
||
case LARGE_DEFINED_PC_OFFSET:
|
||
case DEFINED_ABS_SEGMENT:
|
||
case GOT_OFFSET:
|
||
case PLT_OFFSET:
|
||
case GOTOFF_OFFSET:
|
||
case TEXT_PC_OFFSET:
|
||
case TLSGD_OFFSET:
|
||
case TLSLD_OFFSET:
|
||
case TLSTPREL_OFFSET:
|
||
case TLSDTPREL_OFFSET:
|
||
fragP->fr_var = INST_WORD_SIZE*2;
|
||
break;
|
||
case DEFINED_RO_SEGMENT:
|
||
case DEFINED_RW_SEGMENT:
|
||
case DEFINED_PC_OFFSET:
|
||
case TLSDTPMOD_OFFSET:
|
||
fragP->fr_var = INST_WORD_SIZE;
|
||
break;
|
||
default:
|
||
abort ();
|
||
}
|
||
|
||
return fragP->fr_var;
|
||
}
|
||
|
||
/* Put number into target byte order. */
|
||
|
||
void
|
||
md_number_to_chars (char * ptr, valueT use, int nbytes)
|
||
{
|
||
if (target_big_endian)
|
||
number_to_chars_bigendian (ptr, use, nbytes);
|
||
else
|
||
number_to_chars_littleendian (ptr, use, nbytes);
|
||
}
|
||
|
||
/* Round up a section size to the appropriate boundary. */
|
||
|
||
valueT
|
||
md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
|
||
{
|
||
return size; /* Byte alignment is fine. */
|
||
}
|
||
|
||
|
||
/* The location from which a PC relative jump should be calculated,
|
||
given a PC relative reloc. */
|
||
|
||
long
|
||
md_pcrel_from_section (fixS * fixp, segT sec ATTRIBUTE_UNUSED)
|
||
{
|
||
#ifdef OBJ_ELF
|
||
/* If the symbol is undefined or defined in another section
|
||
we leave the add number alone for the linker to fix it later.
|
||
Only account for the PC pre-bump (No PC-pre-bump on the Microblaze). */
|
||
|
||
if (fixp->fx_addsy != (symbolS *) NULL
|
||
&& (!S_IS_DEFINED (fixp->fx_addsy)
|
||
|| (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
|
||
return 0;
|
||
else
|
||
{
|
||
/* The case where we are going to resolve things... */
|
||
if (fixp->fx_r_type == BFD_RELOC_64_PCREL)
|
||
return fixp->fx_where + fixp->fx_frag->fr_address + INST_WORD_SIZE;
|
||
else
|
||
return fixp->fx_where + fixp->fx_frag->fr_address;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
|
||
#define MAP(SZ,PCREL,TYPE) case F (SZ, PCREL): code = (TYPE); break
|
||
|
||
arelent *
|
||
tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
|
||
{
|
||
arelent * rel;
|
||
bfd_reloc_code_real_type code;
|
||
|
||
switch (fixp->fx_r_type)
|
||
{
|
||
case BFD_RELOC_NONE:
|
||
case BFD_RELOC_MICROBLAZE_64_NONE:
|
||
case BFD_RELOC_32:
|
||
case BFD_RELOC_MICROBLAZE_32_LO:
|
||
case BFD_RELOC_MICROBLAZE_32_LO_PCREL:
|
||
case BFD_RELOC_RVA:
|
||
case BFD_RELOC_64:
|
||
case BFD_RELOC_64_PCREL:
|
||
case BFD_RELOC_MICROBLAZE_32_ROSDA:
|
||
case BFD_RELOC_MICROBLAZE_32_RWSDA:
|
||
case BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM:
|
||
case BFD_RELOC_MICROBLAZE_64_GOTPC:
|
||
case BFD_RELOC_MICROBLAZE_64_GOT:
|
||
case BFD_RELOC_MICROBLAZE_64_PLT:
|
||
case BFD_RELOC_MICROBLAZE_64_GOTOFF:
|
||
case BFD_RELOC_MICROBLAZE_32_GOTOFF:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSGD:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSLD:
|
||
case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
|
||
case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
|
||
case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
|
||
case BFD_RELOC_MICROBLAZE_64_TEXTPCREL:
|
||
case BFD_RELOC_MICROBLAZE_64_TEXTREL:
|
||
code = fixp->fx_r_type;
|
||
break;
|
||
|
||
default:
|
||
switch (F (fixp->fx_size, fixp->fx_pcrel))
|
||
{
|
||
MAP (1, 0, BFD_RELOC_8);
|
||
MAP (2, 0, BFD_RELOC_16);
|
||
MAP (4, 0, BFD_RELOC_32);
|
||
MAP (1, 1, BFD_RELOC_8_PCREL);
|
||
MAP (2, 1, BFD_RELOC_16_PCREL);
|
||
MAP (4, 1, BFD_RELOC_32_PCREL);
|
||
default:
|
||
code = fixp->fx_r_type;
|
||
as_bad (_("Can not do %d byte %srelocation"),
|
||
fixp->fx_size,
|
||
fixp->fx_pcrel ? _("pc-relative ") : "");
|
||
}
|
||
break;
|
||
}
|
||
|
||
rel = XNEW (arelent);
|
||
rel->sym_ptr_ptr = XNEW (asymbol *);
|
||
|
||
if (code == BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM)
|
||
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
|
||
else
|
||
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
|
||
|
||
rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
|
||
/* Always pass the addend along! */
|
||
rel->addend = fixp->fx_offset;
|
||
rel->howto = bfd_reloc_type_lookup (stdoutput, code);
|
||
|
||
if (rel->howto == NULL)
|
||
{
|
||
as_bad_where (fixp->fx_file, fixp->fx_line,
|
||
_("Cannot represent relocation type %s"),
|
||
bfd_get_reloc_code_name (code));
|
||
|
||
/* Set howto to a garbage value so that we can keep going. */
|
||
rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
|
||
gas_assert (rel->howto != NULL);
|
||
}
|
||
return rel;
|
||
}
|
||
|
||
int
|
||
md_parse_option (int c, const char * arg ATTRIBUTE_UNUSED)
|
||
{
|
||
switch (c)
|
||
{
|
||
case OPTION_EB:
|
||
target_big_endian = 1;
|
||
break;
|
||
case OPTION_EL:
|
||
target_big_endian = 0;
|
||
break;
|
||
default:
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
void
|
||
md_show_usage (FILE * stream ATTRIBUTE_UNUSED)
|
||
{
|
||
/* fprintf(stream, _("\
|
||
MicroBlaze options:\n\
|
||
-noSmall Data in the comm and data sections do not go into the small data section\n")); */
|
||
}
|
||
|
||
|
||
/* Create a fixup for a cons expression. If parse_cons_expression_microblaze
|
||
found a machine specific op in an expression,
|
||
then we create relocs accordingly. */
|
||
|
||
void
|
||
cons_fix_new_microblaze (fragS * frag,
|
||
int where,
|
||
int size,
|
||
expressionS *exp,
|
||
bfd_reloc_code_real_type r)
|
||
{
|
||
if ((exp->X_op == O_subtract) && (exp->X_add_symbol) &&
|
||
(exp->X_op_symbol) && (now_seg != absolute_section) && (size == 4)
|
||
&& (!S_IS_LOCAL (exp->X_op_symbol)))
|
||
r = BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM;
|
||
else if (exp->X_md == IMM_GOTOFF && exp->X_op == O_symbol_rva)
|
||
{
|
||
exp->X_op = O_symbol;
|
||
r = BFD_RELOC_MICROBLAZE_32_GOTOFF;
|
||
}
|
||
else
|
||
{
|
||
switch (size)
|
||
{
|
||
case 1:
|
||
r = BFD_RELOC_8;
|
||
break;
|
||
case 2:
|
||
r = BFD_RELOC_16;
|
||
break;
|
||
case 4:
|
||
r = BFD_RELOC_32;
|
||
break;
|
||
case 8:
|
||
r = BFD_RELOC_64;
|
||
break;
|
||
default:
|
||
as_bad (_("unsupported BFD relocation size %u"), size);
|
||
r = BFD_RELOC_32;
|
||
break;
|
||
}
|
||
}
|
||
fix_new_exp (frag, where, size, exp, 0, r);
|
||
}
|