#include <stdio.h>
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "dw2gencfi.h"
#include "opcode/ppc.h"
#ifdef OBJ_ELF
#include "elf/ppc.h"
#include "dwarf2dbg.h"
#endif
#ifdef TE_PE
#include "coff/pe.h"
#endif
extern int target_big_endian;
static int set_target_endian = 0;
#ifndef TARGET_REG_NAMES_P
#ifdef TE_PE
#define TARGET_REG_NAMES_P TRUE
#else
#define TARGET_REG_NAMES_P FALSE
#endif
#endif
#define PPC_LO(v) ((v) & 0xffff)
#define PPC_HI(v) (((v) >> 16) & 0xffff)
#define PPC_HA(v) PPC_HI ((v) + 0x8000)
#define PPC_HIGHER(v) (((v) >> 16 >> 16) & 0xffff)
#define PPC_HIGHERA(v) PPC_HIGHER ((v) + 0x8000)
#define PPC_HIGHEST(v) (((v) >> 24 >> 24) & 0xffff)
#define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000)
#define SEX16(val) ((((val) & 0xffff) ^ 0x8000) - 0x8000)
static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
static bfd_boolean register_name PARAMS ((expressionS *));
static void ppc_set_cpu PARAMS ((void));
static unsigned long ppc_insert_operand
PARAMS ((unsigned long insn, const struct powerpc_operand *operand,
offsetT val, char *file, unsigned int line));
static void ppc_macro PARAMS ((char *str, const struct powerpc_macro *macro));
static void ppc_byte PARAMS ((int));
#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
static int ppc_is_toc_sym PARAMS ((symbolS *sym));
static void ppc_tc PARAMS ((int));
static void ppc_machine PARAMS ((int));
#endif
#ifdef OBJ_XCOFF
static void ppc_comm PARAMS ((int));
static void ppc_bb PARAMS ((int));
static void ppc_bc PARAMS ((int));
static void ppc_bf PARAMS ((int));
static void ppc_biei PARAMS ((int));
static void ppc_bs PARAMS ((int));
static void ppc_eb PARAMS ((int));
static void ppc_ec PARAMS ((int));
static void ppc_ef PARAMS ((int));
static void ppc_es PARAMS ((int));
static void ppc_csect PARAMS ((int));
static void ppc_change_csect PARAMS ((symbolS *, offsetT));
static void ppc_function PARAMS ((int));
static void ppc_extern PARAMS ((int));
static void ppc_lglobl PARAMS ((int));
static void ppc_section PARAMS ((int));
static void ppc_named_section PARAMS ((int));
static void ppc_stabx PARAMS ((int));
static void ppc_rename PARAMS ((int));
static void ppc_toc PARAMS ((int));
static void ppc_xcoff_cons PARAMS ((int));
static void ppc_vbyte PARAMS ((int));
#endif
#ifdef OBJ_ELF
static bfd_reloc_code_real_type ppc_elf_suffix PARAMS ((char **, expressionS *));
static void ppc_elf_cons PARAMS ((int));
static void ppc_elf_rdata PARAMS ((int));
static void ppc_elf_lcomm PARAMS ((int));
static void ppc_elf_validate_fix PARAMS ((fixS *, segT));
static void ppc_apuinfo_section_add PARAMS ((unsigned int apu, unsigned int version));
#endif
#ifdef TE_PE
static void ppc_set_current_section PARAMS ((segT));
static void ppc_previous PARAMS ((int));
static void ppc_pdata PARAMS ((int));
static void ppc_ydata PARAMS ((int));
static void ppc_reldata PARAMS ((int));
static void ppc_rdata PARAMS ((int));
static void ppc_ualong PARAMS ((int));
static void ppc_znop PARAMS ((int));
static void ppc_pe_comm PARAMS ((int));
static void ppc_pe_section PARAMS ((int));
static void ppc_pe_function PARAMS ((int));
static void ppc_pe_tocd PARAMS ((int));
#endif
#ifdef OBJ_ELF
static const char ppc_solaris_comment_chars[] = "#!";
static const char ppc_eabi_comment_chars[] = "#";
#ifdef TARGET_SOLARIS_COMMENT
const char *ppc_comment_chars = ppc_solaris_comment_chars;
#else
const char *ppc_comment_chars = ppc_eabi_comment_chars;
#endif
#else
const char comment_chars[] = "#";
#endif
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";
const char ppc_symbol_chars[] = "+-";
int ppc_cie_data_alignment;
const pseudo_typeS md_pseudo_table[] =
{
{ "byte", ppc_byte, 0 },
#ifdef OBJ_XCOFF
{ "comm", ppc_comm, 0 },
{ "lcomm", ppc_comm, 1 },
{ "bb", ppc_bb, 0 },
{ "bc", ppc_bc, 0 },
{ "bf", ppc_bf, 0 },
{ "bi", ppc_biei, 0 },
{ "bs", ppc_bs, 0 },
{ "csect", ppc_csect, 0 },
{ "data", ppc_section, 'd' },
{ "eb", ppc_eb, 0 },
{ "ec", ppc_ec, 0 },
{ "ef", ppc_ef, 0 },
{ "ei", ppc_biei, 1 },
{ "es", ppc_es, 0 },
{ "extern", ppc_extern, 0 },
{ "function", ppc_function, 0 },
{ "lglobl", ppc_lglobl, 0 },
{ "rename", ppc_rename, 0 },
{ "section", ppc_named_section, 0 },
{ "stabx", ppc_stabx, 0 },
{ "text", ppc_section, 't' },
{ "toc", ppc_toc, 0 },
{ "long", ppc_xcoff_cons, 2 },
{ "llong", ppc_xcoff_cons, 3 },
{ "word", ppc_xcoff_cons, 1 },
{ "short", ppc_xcoff_cons, 1 },
{ "vbyte", ppc_vbyte, 0 },
#endif
#ifdef OBJ_ELF
{ "llong", ppc_elf_cons, 8 },
{ "quad", ppc_elf_cons, 8 },
{ "long", ppc_elf_cons, 4 },
{ "word", ppc_elf_cons, 2 },
{ "short", ppc_elf_cons, 2 },
{ "rdata", ppc_elf_rdata, 0 },
{ "rodata", ppc_elf_rdata, 0 },
{ "lcomm", ppc_elf_lcomm, 0 },
#endif
#ifdef TE_PE
{ "previous", ppc_previous, 0 },
{ "pdata", ppc_pdata, 0 },
{ "ydata", ppc_ydata, 0 },
{ "reldata", ppc_reldata, 0 },
{ "rdata", ppc_rdata, 0 },
{ "ualong", ppc_ualong, 0 },
{ "znop", ppc_znop, 0 },
{ "comm", ppc_pe_comm, 0 },
{ "lcomm", ppc_pe_comm, 1 },
{ "section", ppc_pe_section, 0 },
{ "function", ppc_pe_function,0 },
{ "tocd", ppc_pe_tocd, 0 },
#endif
#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
{ "tc", ppc_tc, 0 },
{ "machine", ppc_machine, 0 },
#endif
{ NULL, NULL, 0 }
};
struct pd_reg
{
char *name;
int value;
};
static const struct pd_reg pre_defined_registers[] =
{
{ "cr.0", 0 },
{ "cr.1", 1 },
{ "cr.2", 2 },
{ "cr.3", 3 },
{ "cr.4", 4 },
{ "cr.5", 5 },
{ "cr.6", 6 },
{ "cr.7", 7 },
{ "cr0", 0 },
{ "cr1", 1 },
{ "cr2", 2 },
{ "cr3", 3 },
{ "cr4", 4 },
{ "cr5", 5 },
{ "cr6", 6 },
{ "cr7", 7 },
{ "ctr", 9 },
{ "dar", 19 },
{ "dec", 22 },
{ "dsisr", 18 },
{ "f.0", 0 },
{ "f.1", 1 },
{ "f.10", 10 },
{ "f.11", 11 },
{ "f.12", 12 },
{ "f.13", 13 },
{ "f.14", 14 },
{ "f.15", 15 },
{ "f.16", 16 },
{ "f.17", 17 },
{ "f.18", 18 },
{ "f.19", 19 },
{ "f.2", 2 },
{ "f.20", 20 },
{ "f.21", 21 },
{ "f.22", 22 },
{ "f.23", 23 },
{ "f.24", 24 },
{ "f.25", 25 },
{ "f.26", 26 },
{ "f.27", 27 },
{ "f.28", 28 },
{ "f.29", 29 },
{ "f.3", 3 },
{ "f.30", 30 },
{ "f.31", 31 },
{ "f.4", 4 },
{ "f.5", 5 },
{ "f.6", 6 },
{ "f.7", 7 },
{ "f.8", 8 },
{ "f.9", 9 },
{ "f0", 0 },
{ "f1", 1 },
{ "f10", 10 },
{ "f11", 11 },
{ "f12", 12 },
{ "f13", 13 },
{ "f14", 14 },
{ "f15", 15 },
{ "f16", 16 },
{ "f17", 17 },
{ "f18", 18 },
{ "f19", 19 },
{ "f2", 2 },
{ "f20", 20 },
{ "f21", 21 },
{ "f22", 22 },
{ "f23", 23 },
{ "f24", 24 },
{ "f25", 25 },
{ "f26", 26 },
{ "f27", 27 },
{ "f28", 28 },
{ "f29", 29 },
{ "f3", 3 },
{ "f30", 30 },
{ "f31", 31 },
{ "f4", 4 },
{ "f5", 5 },
{ "f6", 6 },
{ "f7", 7 },
{ "f8", 8 },
{ "f9", 9 },
{ "fpscr", 0 },
{ "lr", 8 },
{ "pmr", 0 },
{ "r.0", 0 },
{ "r.1", 1 },
{ "r.10", 10 },
{ "r.11", 11 },
{ "r.12", 12 },
{ "r.13", 13 },
{ "r.14", 14 },
{ "r.15", 15 },
{ "r.16", 16 },
{ "r.17", 17 },
{ "r.18", 18 },
{ "r.19", 19 },
{ "r.2", 2 },
{ "r.20", 20 },
{ "r.21", 21 },
{ "r.22", 22 },
{ "r.23", 23 },
{ "r.24", 24 },
{ "r.25", 25 },
{ "r.26", 26 },
{ "r.27", 27 },
{ "r.28", 28 },
{ "r.29", 29 },
{ "r.3", 3 },
{ "r.30", 30 },
{ "r.31", 31 },
{ "r.4", 4 },
{ "r.5", 5 },
{ "r.6", 6 },
{ "r.7", 7 },
{ "r.8", 8 },
{ "r.9", 9 },
{ "r.sp", 1 },
{ "r.toc", 2 },
{ "r0", 0 },
{ "r1", 1 },
{ "r10", 10 },
{ "r11", 11 },
{ "r12", 12 },
{ "r13", 13 },
{ "r14", 14 },
{ "r15", 15 },
{ "r16", 16 },
{ "r17", 17 },
{ "r18", 18 },
{ "r19", 19 },
{ "r2", 2 },
{ "r20", 20 },
{ "r21", 21 },
{ "r22", 22 },
{ "r23", 23 },
{ "r24", 24 },
{ "r25", 25 },
{ "r26", 26 },
{ "r27", 27 },
{ "r28", 28 },
{ "r29", 29 },
{ "r3", 3 },
{ "r30", 30 },
{ "r31", 31 },
{ "r4", 4 },
{ "r5", 5 },
{ "r6", 6 },
{ "r7", 7 },
{ "r8", 8 },
{ "r9", 9 },
{ "rtoc", 2 },
{ "sdr1", 25 },
{ "sp", 1 },
{ "srr0", 26 },
{ "srr1", 27 },
{ "v.0", 0 },
{ "v.1", 1 },
{ "v.10", 10 },
{ "v.11", 11 },
{ "v.12", 12 },
{ "v.13", 13 },
{ "v.14", 14 },
{ "v.15", 15 },
{ "v.16", 16 },
{ "v.17", 17 },
{ "v.18", 18 },
{ "v.19", 19 },
{ "v.2", 2 },
{ "v.20", 20 },
{ "v.21", 21 },
{ "v.22", 22 },
{ "v.23", 23 },
{ "v.24", 24 },
{ "v.25", 25 },
{ "v.26", 26 },
{ "v.27", 27 },
{ "v.28", 28 },
{ "v.29", 29 },
{ "v.3", 3 },
{ "v.30", 30 },
{ "v.31", 31 },
{ "v.4", 4 },
{ "v.5", 5 },
{ "v.6", 6 },
{ "v.7", 7 },
{ "v.8", 8 },
{ "v.9", 9 },
{ "v0", 0 },
{ "v1", 1 },
{ "v10", 10 },
{ "v11", 11 },
{ "v12", 12 },
{ "v13", 13 },
{ "v14", 14 },
{ "v15", 15 },
{ "v16", 16 },
{ "v17", 17 },
{ "v18", 18 },
{ "v19", 19 },
{ "v2", 2 },
{ "v20", 20 },
{ "v21", 21 },
{ "v22", 22 },
{ "v23", 23 },
{ "v24", 24 },
{ "v25", 25 },
{ "v26", 26 },
{ "v27", 27 },
{ "v28", 28 },
{ "v29", 29 },
{ "v3", 3 },
{ "v30", 30 },
{ "v31", 31 },
{ "v4", 4 },
{ "v5", 5 },
{ "v6", 6 },
{ "v7", 7 },
{ "v8", 8 },
{ "v9", 9 },
{ "xer", 1 },
};
#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
static int reg_name_search
PARAMS ((const struct pd_reg *, int, const char * name));
static int
reg_name_search (regs, regcount, name)
const struct pd_reg *regs;
int regcount;
const char *name;
{
int middle, low, high;
int cmp;
low = 0;
high = regcount - 1;
do
{
middle = (low + high) / 2;
cmp = strcasecmp (name, regs[middle].name);
if (cmp < 0)
high = middle - 1;
else if (cmp > 0)
low = middle + 1;
else
return regs[middle].value;
}
while (low <= high);
return -1;
}
static bfd_boolean
register_name (expressionP)
expressionS *expressionP;
{
int reg_number;
char *name;
char *start;
char c;
start = name = input_line_pointer;
if (name[0] == '%' && ISALPHA (name[1]))
name = ++input_line_pointer;
else if (!reg_names_p || !ISALPHA (name[0]))
return FALSE;
c = get_symbol_end ();
reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
*input_line_pointer = c;
if (reg_number >= 0)
{
expressionP->X_op = O_register;
expressionP->X_add_number = reg_number;
expressionP->X_add_symbol = NULL;
expressionP->X_op_symbol = NULL;
return TRUE;
}
input_line_pointer = start;
return FALSE;
}
static bfd_boolean cr_operand;
static const struct pd_reg cr_names[] =
{
{ "cr0", 0 },
{ "cr1", 1 },
{ "cr2", 2 },
{ "cr3", 3 },
{ "cr4", 4 },
{ "cr5", 5 },
{ "cr6", 6 },
{ "cr7", 7 },
{ "eq", 2 },
{ "gt", 1 },
{ "lt", 0 },
{ "so", 3 },
{ "un", 3 }
};
int
ppc_parse_name (name, expr)
const char *name;
expressionS *expr;
{
int val;
if (! cr_operand)
return 0;
val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
name);
if (val < 0)
return 0;
expr->X_op = O_constant;
expr->X_add_number = val;
return 1;
}
static unsigned long ppc_cpu = 0;
static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
static struct hash_control *ppc_hash;
static struct hash_control *ppc_macro_hash;
#ifdef OBJ_ELF
static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE;
static flagword ppc_flags = 0;
#ifdef TARGET_SOLARIS_COMMENT
#define SOLARIS_P TRUE
#else
#define SOLARIS_P FALSE
#endif
static bfd_boolean msolaris = SOLARIS_P;
#endif
#ifdef OBJ_XCOFF
static subsegT ppc_text_subsegment = 2;
static symbolS *ppc_text_csects;
static subsegT ppc_data_subsegment = 2;
static symbolS *ppc_data_csects;
static symbolS *ppc_current_csect;
static symbolS *ppc_toc_csect;
static fragS *ppc_toc_frag;
static fragS *ppc_after_toc_frag;
static symbolS *ppc_current_block;
static asection *ppc_coff_debug_section;
#endif
#ifdef TE_PE
static segT ydata_section;
static segT pdata_section;
static segT reldata_section;
static segT rdata_section;
static segT tocdata_section;
static segT ppc_previous_section;
static segT ppc_current_section;
#endif
#ifdef OBJ_ELF
symbolS *GOT_symbol;
#define PPC_APUINFO_ISEL 0x40
#define PPC_APUINFO_PMR 0x41
#define PPC_APUINFO_RFMCI 0x42
#define PPC_APUINFO_CACHELCK 0x43
#define PPC_APUINFO_SPE 0x100
#define PPC_APUINFO_EFS 0x101
#define PPC_APUINFO_BRLOCK 0x102
unsigned long *ppc_apuinfo_list;
unsigned int ppc_apuinfo_num;
unsigned int ppc_apuinfo_num_alloc;
#endif
#ifdef OBJ_ELF
const char *const md_shortopts = "b:l:usm:K:VQ:";
#else
const char *const md_shortopts = "um:";
#endif
const struct option md_longopts[] = {
{NULL, no_argument, NULL, 0}
};
const size_t md_longopts_size = sizeof (md_longopts);
static int
parse_cpu (const char *arg)
{
if (strcmp (arg, "pwrx") == 0 || strcmp (arg, "pwr2") == 0)
ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_32;
else if (strcmp (arg, "pwr") == 0)
ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_32;
else if (strcmp (arg, "601") == 0)
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC
| PPC_OPCODE_601 | PPC_OPCODE_32);
else if (strcmp (arg, "ppc") == 0
|| strcmp (arg, "ppc32") == 0
|| strcmp (arg, "603") == 0
|| strcmp (arg, "604") == 0)
ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32;
else if (strcmp (arg, "403") == 0
|| strcmp (arg, "405") == 0)
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC
| PPC_OPCODE_403 | PPC_OPCODE_32);
else if (strcmp (arg, "440") == 0)
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32
| PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI);
else if (strcmp (arg, "7400") == 0
|| strcmp (arg, "7410") == 0
|| strcmp (arg, "7450") == 0
|| strcmp (arg, "7455") == 0)
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC
| PPC_OPCODE_ALTIVEC | PPC_OPCODE_32);
else if (strcmp (arg, "altivec") == 0)
{
if (ppc_cpu == 0)
ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC;
else
ppc_cpu |= PPC_OPCODE_ALTIVEC;
}
else if (strcmp (arg, "e500") == 0 || strcmp (arg, "e500x2") == 0)
{
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_SPE
| PPC_OPCODE_ISEL | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK
| PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK
| PPC_OPCODE_RFMCI);
}
else if (strcmp (arg, "spe") == 0)
{
if (ppc_cpu == 0)
ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_SPE | PPC_OPCODE_EFS;
else
ppc_cpu |= PPC_OPCODE_SPE;
}
else if (strcmp (arg, "ppc64") == 0 || strcmp (arg, "620") == 0)
{
ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64;
}
else if (strcmp (arg, "ppc64bridge") == 0)
{
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC
| PPC_OPCODE_64_BRIDGE | PPC_OPCODE_64);
}
else if (strcmp (arg, "booke") == 0 || strcmp (arg, "booke32") == 0)
{
ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32;
}
else if (strcmp (arg, "booke64") == 0)
{
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE
| PPC_OPCODE_BOOKE64 | PPC_OPCODE_64);
}
else if (strcmp (arg, "power4") == 0)
{
ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC
| PPC_OPCODE_64 | PPC_OPCODE_POWER4);
}
else if (strcmp (arg, "com") == 0)
ppc_cpu = PPC_OPCODE_COMMON | PPC_OPCODE_32;
else if (strcmp (arg, "any") == 0)
ppc_cpu |= PPC_OPCODE_ANY;
else
return 0;
return 1;
}
int
md_parse_option (c, arg)
int c;
char *arg;
{
switch (c)
{
case 'u':
break;
#ifdef OBJ_ELF
case 'l':
if (strcmp (arg, "e") == 0)
{
target_big_endian = 0;
set_target_endian = 1;
}
else
return 0;
break;
case 'b':
if (strcmp (arg, "e") == 0)
{
target_big_endian = 1;
set_target_endian = 1;
}
else
return 0;
break;
case 'K':
if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0)
{
shlib = SHLIB_PIC;
ppc_flags |= EF_PPC_RELOCATABLE_LIB;
}
else
return 0;
break;
#endif
case 'a':
if (strcmp (arg, "64") == 0)
{
#ifdef BFD64
ppc_obj64 = 1;
#else
as_fatal (_("%s unsupported"), "-a64");
#endif
}
else if (strcmp (arg, "32") == 0)
ppc_obj64 = 0;
else
return 0;
break;
case 'm':
if (parse_cpu (arg))
;
else if (strcmp (arg, "regnames") == 0)
reg_names_p = TRUE;
else if (strcmp (arg, "no-regnames") == 0)
reg_names_p = FALSE;
#ifdef OBJ_ELF
else if (strcmp (arg, "relocatable") == 0)
{
shlib = SHLIB_MRELOCATABLE;
ppc_flags |= EF_PPC_RELOCATABLE;
}
else if (strcmp (arg, "relocatable-lib") == 0)
{
shlib = SHLIB_MRELOCATABLE;
ppc_flags |= EF_PPC_RELOCATABLE_LIB;
}
else if (strcmp (arg, "emb") == 0)
ppc_flags |= EF_PPC_EMB;
else if (strcmp (arg, "little") == 0
|| strcmp (arg, "little-endian") == 0)
{
target_big_endian = 0;
set_target_endian = 1;
}
else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0)
{
target_big_endian = 1;
set_target_endian = 1;
}
else if (strcmp (arg, "solaris") == 0)
{
msolaris = TRUE;
ppc_comment_chars = ppc_solaris_comment_chars;
}
else if (strcmp (arg, "no-solaris") == 0)
{
msolaris = FALSE;
ppc_comment_chars = ppc_eabi_comment_chars;
}
#endif
else
{
as_bad (_("invalid switch -m%s"), arg);
return 0;
}
break;
#ifdef OBJ_ELF
case 'V':
print_version_id ();
break;
case 'Q':
break;
case 's':
if (arg)
return 0;
break;
#endif
default:
return 0;
}
return 1;
}
void
md_show_usage (stream)
FILE *stream;
{
fprintf (stream, _("\
PowerPC options:\n\
-a32 generate ELF32/XCOFF32\n\
-a64 generate ELF64/XCOFF64\n\
-u ignored\n\
-mpwrx, -mpwr2 generate code for POWER/2 (RIOS2)\n\
-mpwr generate code for POWER (RIOS1)\n\
-m601 generate code for PowerPC 601\n\
-mppc, -mppc32, -m603, -m604\n\
generate code for PowerPC 603/604\n\
-m403, -m405 generate code for PowerPC 403/405\n\
-m440 generate code for PowerPC 440\n\
-m7400, -m7410, -m7450, -m7455\n\
generate code For PowerPC 7400/7410/7450/7455\n"));
fprintf (stream, _("\
-mppc64, -m620 generate code for PowerPC 620/625/630\n\
-mppc64bridge generate code for PowerPC 64, including bridge insns\n\
-mbooke64 generate code for 64-bit PowerPC BookE\n\
-mbooke, mbooke32 generate code for 32-bit PowerPC BookE\n\
-mpower4 generate code for Power4 architecture\n\
-mcom generate code Power/PowerPC common instructions\n\
-many generate code for any architecture (PWR/PWRX/PPC)\n"));
fprintf (stream, _("\
-maltivec generate code for AltiVec\n\
-me500, -me500x2 generate code for Motorola e500 core complex\n\
-mspe generate code for Motorola SPE instructions\n\
-mregnames Allow symbolic names for registers\n\
-mno-regnames Do not allow symbolic names for registers\n"));
#ifdef OBJ_ELF
fprintf (stream, _("\
-mrelocatable support for GCC's -mrelocatble option\n\
-mrelocatable-lib support for GCC's -mrelocatble-lib option\n\
-memb set PPC_EMB bit in ELF flags\n\
-mlittle, -mlittle-endian, -l, -le\n\
generate code for a little endian machine\n\
-mbig, -mbig-endian, -b, -be\n\
generate code for a big endian machine\n\
-msolaris generate code for Solaris\n\
-mno-solaris do not generate code for Solaris\n\
-V print assembler version number\n\
-Qy, -Qn ignored\n"));
#endif
}
static void
ppc_set_cpu ()
{
const char *default_os = TARGET_OS;
const char *default_cpu = TARGET_CPU;
if ((ppc_cpu & ~PPC_OPCODE_ANY) == 0)
{
if (ppc_obj64)
ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64;
else if (strncmp (default_os, "aix", 3) == 0
&& default_os[3] >= '4' && default_os[3] <= '9')
ppc_cpu |= PPC_OPCODE_COMMON | PPC_OPCODE_32;
else if (strncmp (default_os, "aix3", 4) == 0)
ppc_cpu |= PPC_OPCODE_POWER | PPC_OPCODE_32;
else if (strcmp (default_cpu, "rs6000") == 0)
ppc_cpu |= PPC_OPCODE_POWER | PPC_OPCODE_32;
else if (strncmp (default_cpu, "powerpc", 7) == 0)
{
if (default_cpu[7] == '6' && default_cpu[8] == '4')
ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64;
else
ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32;
}
else
as_fatal (_("Unknown default cpu = %s, os = %s"),
default_cpu, default_os);
}
}
enum bfd_architecture
ppc_arch ()
{
const char *default_cpu = TARGET_CPU;
ppc_set_cpu ();
if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
return bfd_arch_powerpc;
else if ((ppc_cpu & PPC_OPCODE_POWER) != 0)
return bfd_arch_rs6000;
else if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
{
if (strcmp (default_cpu, "rs6000") == 0)
return bfd_arch_rs6000;
else if (strncmp (default_cpu, "powerpc", 7) == 0)
return bfd_arch_powerpc;
}
as_fatal (_("Neither Power nor PowerPC opcodes were selected."));
return bfd_arch_unknown;
}
unsigned long
ppc_mach ()
{
if (ppc_obj64)
return bfd_mach_ppc64;
else if (ppc_arch () == bfd_arch_rs6000)
return bfd_mach_rs6k;
else
return bfd_mach_ppc;
}
extern char*
ppc_target_format ()
{
#ifdef OBJ_COFF
#ifdef TE_PE
return target_big_endian ? "pe-powerpc" : "pe-powerpcle";
#elif TE_POWERMAC
return "xcoff-powermac";
#else
# ifdef TE_AIX5
return (ppc_obj64 ? "aix5coff64-rs6000" : "aixcoff-rs6000");
# else
return (ppc_obj64 ? "aixcoff64-rs6000" : "aixcoff-rs6000");
# endif
#endif
#endif
#ifdef OBJ_ELF
return (target_big_endian
? (ppc_obj64 ? "elf64-powerpc" : "elf32-powerpc")
: (ppc_obj64 ? "elf64-powerpcle" : "elf32-powerpcle"));
#endif
}
static void
ppc_setup_opcodes (void)
{
register const struct powerpc_opcode *op;
const struct powerpc_opcode *op_end;
const struct powerpc_macro *macro;
const struct powerpc_macro *macro_end;
bfd_boolean dup_insn = FALSE;
if (ppc_hash != NULL)
hash_die (ppc_hash);
if (ppc_macro_hash != NULL)
hash_die (ppc_macro_hash);
ppc_hash = hash_new ();
op_end = powerpc_opcodes + powerpc_num_opcodes;
for (op = powerpc_opcodes; op < op_end; op++)
{
know ((op->opcode & op->mask) == op->opcode);
if ((op->flags & ppc_cpu & ~(PPC_OPCODE_32 | PPC_OPCODE_64)) != 0
&& ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0
|| ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64))
== (ppc_cpu & (PPC_OPCODE_32 | PPC_OPCODE_64)))
|| (ppc_cpu & PPC_OPCODE_64_BRIDGE) != 0)
&& ((op->flags & PPC_OPCODE_BOOKE64) == 0
|| (ppc_cpu & PPC_OPCODE_BOOKE64) == PPC_OPCODE_BOOKE64
|| (ppc_cpu & PPC_OPCODE_BOOKE) == 0)
&& ((op->flags & (PPC_OPCODE_POWER4 | PPC_OPCODE_NOPOWER4)) == 0
|| ((op->flags & PPC_OPCODE_POWER4)
== (ppc_cpu & PPC_OPCODE_POWER4))))
{
const char *retval;
retval = hash_insert (ppc_hash, op->name, (PTR) op);
if (retval != NULL)
{
if ((ppc_cpu & PPC_OPCODE_601) != 0
&& (op->flags & PPC_OPCODE_POWER) != 0)
continue;
as_bad (_("Internal assembler error for instruction %s"),
op->name);
dup_insn = TRUE;
}
}
}
if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
for (op = powerpc_opcodes; op < op_end; op++)
hash_insert (ppc_hash, op->name, (PTR) op);
ppc_macro_hash = hash_new ();
macro_end = powerpc_macros + powerpc_num_macros;
for (macro = powerpc_macros; macro < macro_end; macro++)
{
if ((macro->flags & ppc_cpu) != 0)
{
const char *retval;
retval = hash_insert (ppc_macro_hash, macro->name, (PTR) macro);
if (retval != (const char *) NULL)
{
as_bad (_("Internal assembler error for macro %s"), macro->name);
dup_insn = TRUE;
}
}
}
if (dup_insn)
abort ();
}
void
md_begin ()
{
ppc_set_cpu ();
ppc_cie_data_alignment = ppc_obj64 ? -8 : -4;
#ifdef OBJ_ELF
if (ppc_flags && !msolaris)
bfd_set_private_flags (stdoutput, ppc_flags);
#endif
ppc_setup_opcodes ();
if (!set_target_endian)
{
set_target_endian = 1;
target_big_endian = PPC_BIG_ENDIAN;
}
#ifdef OBJ_XCOFF
ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG);
ppc_text_csects = symbol_make ("dummy\001");
symbol_get_tc (ppc_text_csects)->within = ppc_text_csects;
ppc_data_csects = symbol_make ("dummy\001");
symbol_get_tc (ppc_data_csects)->within = ppc_data_csects;
#endif
#ifdef TE_PE
ppc_current_section = text_section;
ppc_previous_section = 0;
#endif
}
void
ppc_cleanup ()
{
#ifdef OBJ_ELF
if (ppc_apuinfo_list == NULL)
return;
{
char *p;
asection *seg = now_seg;
subsegT subseg = now_subseg;
asection *apuinfo_secp = (asection *) NULL;
unsigned int i;
apuinfo_secp = subseg_new (".PPC.EMB.apuinfo", 0);
bfd_set_section_flags (stdoutput,
apuinfo_secp,
SEC_HAS_CONTENTS | SEC_READONLY);
p = frag_more (4);
md_number_to_chars (p, (valueT) 8, 4);
p = frag_more (4);
md_number_to_chars (p, (valueT) ppc_apuinfo_num * 4, 4);
p = frag_more (4);
md_number_to_chars (p, (valueT) 2, 4);
p = frag_more (8);
strcpy (p, "APUinfo");
for (i = 0; i < ppc_apuinfo_num; i++)
{
p = frag_more (4);
md_number_to_chars (p, (valueT) ppc_apuinfo_list[i], 4);
}
frag_align (2, 0, 0);
if (seg && subseg)
subseg_set (seg, subseg);
}
#endif
}
static unsigned long
ppc_insert_operand (insn, operand, val, file, line)
unsigned long insn;
const struct powerpc_operand *operand;
offsetT val;
char *file;
unsigned int line;
{
if (operand->bits != 32)
{
long min, max;
offsetT test;
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
{
if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0)
max = (1 << operand->bits) - 1;
else
max = (1 << (operand->bits - 1)) - 1;
min = - (1 << (operand->bits - 1));
if (!ppc_obj64)
{
if (val > 0
&& (val & (offsetT) 0x80000000) != 0
&& (val & (offsetT) 0xffffffff) == val)
{
val -= 0x80000000;
val -= 0x80000000;
}
}
}
else
{
max = (1 << operand->bits) - 1;
min = 0;
}
if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
test = - val;
else
test = val;
if (test < (offsetT) min || test > (offsetT) max)
as_bad_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
}
if (operand->insert)
{
const char *errmsg;
errmsg = NULL;
insn = (*operand->insert) (insn, (long) val, ppc_cpu, &errmsg);
if (errmsg != (const char *) NULL)
as_bad_where (file, line, errmsg);
}
else
insn |= (((long) val & ((1 << operand->bits) - 1))
<< operand->shift);
return insn;
}
#ifdef OBJ_ELF
static bfd_reloc_code_real_type
ppc_elf_suffix (str_p, exp_p)
char **str_p;
expressionS *exp_p;
{
struct map_bfd {
char *string;
unsigned int length : 8;
unsigned int valid32 : 1;
unsigned int valid64 : 1;
unsigned int reloc;
};
char ident[20];
char *str = *str_p;
char *str2;
int ch;
int len;
const struct map_bfd *ptr;
#define MAP(str, reloc) { str, sizeof (str) - 1, 1, 1, reloc }
#define MAP32(str, reloc) { str, sizeof (str) - 1, 1, 0, reloc }
#define MAP64(str, reloc) { str, sizeof (str) - 1, 0, 1, reloc }
static const struct map_bfd mapping[] = {
MAP ("l", BFD_RELOC_LO16),
MAP ("h", BFD_RELOC_HI16),
MAP ("ha", BFD_RELOC_HI16_S),
MAP ("brtaken", BFD_RELOC_PPC_B16_BRTAKEN),
MAP ("brntaken", BFD_RELOC_PPC_B16_BRNTAKEN),
MAP ("got", BFD_RELOC_16_GOTOFF),
MAP ("got@l", BFD_RELOC_LO16_GOTOFF),
MAP ("got@h", BFD_RELOC_HI16_GOTOFF),
MAP ("got@ha", BFD_RELOC_HI16_S_GOTOFF),
MAP ("plt@l", BFD_RELOC_LO16_PLTOFF),
MAP ("plt@h", BFD_RELOC_HI16_PLTOFF),
MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF),
MAP ("copy", BFD_RELOC_PPC_COPY),
MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT),
MAP ("sectoff", BFD_RELOC_16_BASEREL),
MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL),
MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL),
MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL),
MAP ("tls", BFD_RELOC_PPC_TLS),
MAP ("dtpmod", BFD_RELOC_PPC_DTPMOD),
MAP ("dtprel", BFD_RELOC_PPC_DTPREL),
MAP ("dtprel@l", BFD_RELOC_PPC_DTPREL16_LO),
MAP ("dtprel@h", BFD_RELOC_PPC_DTPREL16_HI),
MAP ("dtprel@ha", BFD_RELOC_PPC_DTPREL16_HA),
MAP ("tprel", BFD_RELOC_PPC_TPREL),
MAP ("tprel@l", BFD_RELOC_PPC_TPREL16_LO),
MAP ("tprel@h", BFD_RELOC_PPC_TPREL16_HI),
MAP ("tprel@ha", BFD_RELOC_PPC_TPREL16_HA),
MAP ("got@tlsgd", BFD_RELOC_PPC_GOT_TLSGD16),
MAP ("got@tlsgd@l", BFD_RELOC_PPC_GOT_TLSGD16_LO),
MAP ("got@tlsgd@h", BFD_RELOC_PPC_GOT_TLSGD16_HI),
MAP ("got@tlsgd@ha", BFD_RELOC_PPC_GOT_TLSGD16_HA),
MAP ("got@tlsld", BFD_RELOC_PPC_GOT_TLSLD16),
MAP ("got@tlsld@l", BFD_RELOC_PPC_GOT_TLSLD16_LO),
MAP ("got@tlsld@h", BFD_RELOC_PPC_GOT_TLSLD16_HI),
MAP ("got@tlsld@ha", BFD_RELOC_PPC_GOT_TLSLD16_HA),
MAP ("got@dtprel", BFD_RELOC_PPC_GOT_DTPREL16),
MAP ("got@dtprel@l", BFD_RELOC_PPC_GOT_DTPREL16_LO),
MAP ("got@dtprel@h", BFD_RELOC_PPC_GOT_DTPREL16_HI),
MAP ("got@dtprel@ha", BFD_RELOC_PPC_GOT_DTPREL16_HA),
MAP ("got@tprel", BFD_RELOC_PPC_GOT_TPREL16),
MAP ("got@tprel@l", BFD_RELOC_PPC_GOT_TPREL16_LO),
MAP ("got@tprel@h", BFD_RELOC_PPC_GOT_TPREL16_HI),
MAP ("got@tprel@ha", BFD_RELOC_PPC_GOT_TPREL16_HA),
MAP32 ("fixup", BFD_RELOC_CTOR),
MAP32 ("plt", BFD_RELOC_24_PLT_PCREL),
MAP32 ("pltrel24", BFD_RELOC_24_PLT_PCREL),
MAP32 ("local24pc", BFD_RELOC_PPC_LOCAL24PC),
MAP32 ("local", BFD_RELOC_PPC_LOCAL24PC),
MAP32 ("pltrel", BFD_RELOC_32_PLT_PCREL),
MAP32 ("sdarel", BFD_RELOC_GPREL16),
MAP32 ("naddr", BFD_RELOC_PPC_EMB_NADDR32),
MAP32 ("naddr16", BFD_RELOC_PPC_EMB_NADDR16),
MAP32 ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO),
MAP32 ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI),
MAP32 ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA),
MAP32 ("sdai16", BFD_RELOC_PPC_EMB_SDAI16),
MAP32 ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL),
MAP32 ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16),
MAP32 ("sda21", BFD_RELOC_PPC_EMB_SDA21),
MAP32 ("mrkref", BFD_RELOC_PPC_EMB_MRKREF),
MAP32 ("relsect", BFD_RELOC_PPC_EMB_RELSEC16),
MAP32 ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO),
MAP32 ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI),
MAP32 ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA),
MAP32 ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD),
MAP32 ("relsda", BFD_RELOC_PPC_EMB_RELSDA),
MAP32 ("xgot", BFD_RELOC_PPC_TOC16),
MAP64 ("higher", BFD_RELOC_PPC64_HIGHER),
MAP64 ("highera", BFD_RELOC_PPC64_HIGHER_S),
MAP64 ("highest", BFD_RELOC_PPC64_HIGHEST),
MAP64 ("highesta", BFD_RELOC_PPC64_HIGHEST_S),
MAP64 ("tocbase", BFD_RELOC_PPC64_TOC),
MAP64 ("toc", BFD_RELOC_PPC_TOC16),
MAP64 ("toc@l", BFD_RELOC_PPC64_TOC16_LO),
MAP64 ("toc@h", BFD_RELOC_PPC64_TOC16_HI),
MAP64 ("toc@ha", BFD_RELOC_PPC64_TOC16_HA),
MAP64 ("dtprel@higher", BFD_RELOC_PPC64_DTPREL16_HIGHER),
MAP64 ("dtprel@highera", BFD_RELOC_PPC64_DTPREL16_HIGHERA),
MAP64 ("dtprel@highest", BFD_RELOC_PPC64_DTPREL16_HIGHEST),
MAP64 ("dtprel@highesta", BFD_RELOC_PPC64_DTPREL16_HIGHESTA),
MAP64 ("tprel@higher", BFD_RELOC_PPC64_TPREL16_HIGHER),
MAP64 ("tprel@highera", BFD_RELOC_PPC64_TPREL16_HIGHERA),
MAP64 ("tprel@highest", BFD_RELOC_PPC64_TPREL16_HIGHEST),
MAP64 ("tprel@highesta", BFD_RELOC_PPC64_TPREL16_HIGHESTA),
{ (char *) 0, 0, 0, 0, BFD_RELOC_UNUSED }
};
if (*str++ != '@')
return BFD_RELOC_UNUSED;
for (ch = *str, str2 = ident;
(str2 < ident + sizeof (ident) - 1
&& (ISALNUM (ch) || ch == '@'));
ch = *++str)
{
*str2++ = TOLOWER (ch);
}
*str2 = '\0';
len = str2 - ident;
ch = ident[0];
for (ptr = &mapping[0]; ptr->length > 0; ptr++)
if (ch == ptr->string[0]
&& len == ptr->length
&& memcmp (ident, ptr->string, ptr->length) == 0
&& (ppc_obj64 ? ptr->valid64 : ptr->valid32))
{
int reloc = ptr->reloc;
if (!ppc_obj64)
if (exp_p->X_add_number != 0
&& (reloc == (int) BFD_RELOC_16_GOTOFF
|| reloc == (int) BFD_RELOC_LO16_GOTOFF
|| reloc == (int) BFD_RELOC_HI16_GOTOFF
|| reloc == (int) BFD_RELOC_HI16_S_GOTOFF))
as_warn (_("identifier+constant@got means identifier@got+constant"));
if (*str == '-' || *str == '+')
{
char *orig_line = input_line_pointer;
expressionS new_exp;
input_line_pointer = str;
expression (&new_exp);
if (new_exp.X_op == O_constant)
{
exp_p->X_add_number += new_exp.X_add_number;
str = input_line_pointer;
}
if (&input_line_pointer != str_p)
input_line_pointer = orig_line;
}
*str_p = str;
if (reloc == (int) BFD_RELOC_PPC64_TOC
&& exp_p->X_op == O_symbol
&& strcmp (S_GET_NAME (exp_p->X_add_symbol), ".TOC.") == 0)
{
exp_p->X_add_symbol = &abs_symbol;
}
return (bfd_reloc_code_real_type) reloc;
}
return BFD_RELOC_UNUSED;
}
static void
ppc_elf_cons (nbytes)
register int nbytes;
{
expressionS exp;
bfd_reloc_code_real_type reloc;
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
do
{
expression (&exp);
if (exp.X_op == O_symbol
&& *input_line_pointer == '@'
&& (reloc = ppc_elf_suffix (&input_line_pointer,
&exp)) != BFD_RELOC_UNUSED)
{
reloc_howto_type *reloc_howto;
int size;
reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
size = bfd_get_reloc_size (reloc_howto);
if (size > nbytes)
{
as_bad (_("%s relocations do not fit in %d bytes\n"),
reloc_howto->name, nbytes);
}
else
{
char *p;
int offset;
p = frag_more (nbytes);
offset = 0;
if (target_big_endian)
offset = nbytes - size;
fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
&exp, 0, reloc);
}
}
else
emit_expr (&exp, (unsigned int) nbytes);
}
while (*input_line_pointer++ == ',');
input_line_pointer--;
demand_empty_rest_of_line ();
}
static void
ppc_elf_rdata (xxx)
int xxx;
{
char *save_line = input_line_pointer;
static char section[] = ".rodata\n";
input_line_pointer = section;
obj_elf_section (xxx);
input_line_pointer = save_line;
}
static void
ppc_elf_lcomm (xxx)
int xxx ATTRIBUTE_UNUSED;
{
register char *name;
register char c;
register char *p;
offsetT size;
register symbolS *symbolP;
offsetT align;
segT old_sec;
int old_subsec;
char *pfrag;
int align2;
name = input_line_pointer;
c = get_symbol_end ();
p = input_line_pointer;
*p = 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++;
if ((size = get_absolute_expression ()) < 0)
{
as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
ignore_rest_of_line ();
return;
}
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;
}
old_sec = now_seg;
old_subsec = now_subseg;
if (align)
{
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 (bss_section, align2);
subseg_set (bss_section, 0);
if (align2)
frag_align (align2, 0, 0);
if (S_GET_SEGMENT (symbolP) == bss_section)
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, bss_section);
subseg_set (old_sec, old_subsec);
demand_empty_rest_of_line ();
}
static void
ppc_elf_validate_fix (fixp, seg)
fixS *fixp;
segT seg;
{
if (fixp->fx_done || fixp->fx_pcrel)
return;
switch (shlib)
{
case SHLIB_NONE:
case SHLIB_PIC:
return;
case SHLIB_MRELOCATABLE:
if (fixp->fx_r_type <= BFD_RELOC_UNUSED
&& fixp->fx_r_type != BFD_RELOC_16_GOTOFF
&& fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF
&& fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF
&& fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
&& fixp->fx_r_type != BFD_RELOC_16_BASEREL
&& fixp->fx_r_type != BFD_RELOC_LO16_BASEREL
&& fixp->fx_r_type != BFD_RELOC_HI16_BASEREL
&& fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL
&& (seg->flags & SEC_LOAD) != 0
&& strcmp (segment_name (seg), ".got2") != 0
&& strcmp (segment_name (seg), ".dtors") != 0
&& strcmp (segment_name (seg), ".ctors") != 0
&& strcmp (segment_name (seg), ".fixup") != 0
&& strcmp (segment_name (seg), ".gcc_except_table") != 0
&& strcmp (segment_name (seg), ".eh_frame") != 0
&& strcmp (segment_name (seg), ".ex_shared") != 0)
{
if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0
|| fixp->fx_r_type != BFD_RELOC_CTOR)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("Relocation cannot be done when using -mrelocatable"));
}
}
return;
}
}
void
ppc_frob_file_before_adjust ()
{
symbolS *symp;
asection *toc;
if (!ppc_obj64)
return;
for (symp = symbol_rootP; symp; symp = symbol_next (symp))
{
const char *name;
char *dotname;
symbolS *dotsym;
size_t len;
name = S_GET_NAME (symp);
if (name[0] == '.')
continue;
if (! S_IS_WEAK (symp)
|| S_IS_DEFINED (symp))
continue;
len = strlen (name) + 1;
dotname = xmalloc (len + 1);
dotname[0] = '.';
memcpy (dotname + 1, name, len);
dotsym = symbol_find (dotname);
free (dotname);
if (dotsym != NULL && (symbol_used_p (dotsym)
|| symbol_used_in_reloc_p (dotsym)))
symbol_mark_used (symp);
}
toc = bfd_get_section_by_name (stdoutput, ".toc");
if (toc != NULL
&& bfd_section_size (stdoutput, toc) > 0x10000)
as_warn (_("TOC section size exceeds 64k"));
symp = symbol_find (".TOC.");
if (symp != NULL)
symbol_remove (symp, &symbol_rootP, &symbol_lastP);
}
#endif
#ifdef TE_PE
enum toc_size_qualifier
{
default_toc,
data_in_toc,
must_be_32,
must_be_64
};
static int
parse_toc_entry (toc_kind)
enum toc_size_qualifier *toc_kind;
{
char *start;
char *toc_spec;
char c;
enum toc_size_qualifier t;
start = input_line_pointer;
++input_line_pointer;
SKIP_WHITESPACE ();
toc_spec = input_line_pointer;
c = get_symbol_end ();
if (strcmp (toc_spec, "toc") == 0)
{
t = default_toc;
}
else if (strcmp (toc_spec, "tocv") == 0)
{
t = data_in_toc;
}
else if (strcmp (toc_spec, "toc32") == 0)
{
t = must_be_32;
}
else if (strcmp (toc_spec, "toc64") == 0)
{
t = must_be_64;
}
else
{
as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec);
*input_line_pointer = c;
input_line_pointer = start;
return 0;
}
*input_line_pointer = c;
SKIP_WHITESPACE ();
c = *input_line_pointer++;
if (c != ']')
{
as_bad (_("syntax error: expected `]', found `%c'"), c);
input_line_pointer = start;
return 0;
}
*toc_kind = t;
return 1;
}
#endif
#ifdef OBJ_ELF
#define APUID(a,v) ((((a) & 0xffff) << 16) | ((v) & 0xffff))
static void
ppc_apuinfo_section_add (apu, version)
unsigned int apu, version;
{
unsigned int i;
for (i = 0; i < ppc_apuinfo_num; i++)
if (ppc_apuinfo_list[i] == APUID (apu, version))
return;
if (ppc_apuinfo_num == ppc_apuinfo_num_alloc)
{
if (ppc_apuinfo_num_alloc == 0)
{
ppc_apuinfo_num_alloc = 4;
ppc_apuinfo_list = (unsigned long *)
xmalloc (sizeof (unsigned long) * ppc_apuinfo_num_alloc);
}
else
{
ppc_apuinfo_num_alloc += 4;
ppc_apuinfo_list = (unsigned long *) xrealloc (ppc_apuinfo_list,
sizeof (unsigned long) * ppc_apuinfo_num_alloc);
}
}
ppc_apuinfo_list[ppc_apuinfo_num++] = APUID (apu, version);
}
#undef APUID
#endif
struct ppc_fixup
{
expressionS exp;
int opindex;
bfd_reloc_code_real_type reloc;
};
#define MAX_INSN_FIXUPS (5)
void
md_assemble (str)
char *str;
{
char *s;
const struct powerpc_opcode *opcode;
unsigned long insn;
const unsigned char *opindex_ptr;
int skip_optional;
int need_paren;
int next_opindex;
struct ppc_fixup fixups[MAX_INSN_FIXUPS];
int fc;
char *f;
int addr_mod;
int i;
#ifdef OBJ_ELF
bfd_reloc_code_real_type reloc;
#endif
for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
;
if (*s != '\0')
*s++ = '\0';
opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str);
if (opcode == (const struct powerpc_opcode *) NULL)
{
const struct powerpc_macro *macro;
macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str);
if (macro == (const struct powerpc_macro *) NULL)
as_bad (_("Unrecognized opcode: `%s'"), str);
else
ppc_macro (s, macro);
return;
}
insn = opcode->opcode;
str = s;
while (ISSPACE (*str))
++str;
skip_optional = 0;
for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
{
const struct powerpc_operand *operand;
operand = &powerpc_operands[*opindex_ptr];
if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
{
unsigned int opcount;
unsigned int num_operands_expected;
unsigned int i;
if (*str == '\0')
opcount = 0;
else
{
opcount = 1;
s = str;
while ((s = strchr (s, ',')) != (char *) NULL)
{
++opcount;
++s;
}
}
for (num_operands_expected = 0, i = 0; opcode->operands[i]; i ++)
if ((powerpc_operands [opcode->operands[i]].flags & PPC_OPERAND_FAKE) == 0)
++ num_operands_expected;
if (opcount < num_operands_expected)
skip_optional = 1;
break;
}
}
need_paren = 0;
next_opindex = 0;
fc = 0;
for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
{
const struct powerpc_operand *operand;
const char *errmsg;
char *hold;
expressionS ex;
char endc;
if (next_opindex == 0)
operand = &powerpc_operands[*opindex_ptr];
else
{
operand = &powerpc_operands[next_opindex];
next_opindex = 0;
}
errmsg = NULL;
if ((operand->flags & PPC_OPERAND_FAKE) != 0)
{
insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
if (errmsg != (const char *) NULL)
as_bad (errmsg);
continue;
}
if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
&& skip_optional)
{
if (operand->insert)
{
insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
if (errmsg != (const char *) NULL)
as_bad (errmsg);
}
if ((operand->flags & PPC_OPERAND_NEXT) != 0)
next_opindex = *opindex_ptr + 1;
continue;
}
hold = input_line_pointer;
input_line_pointer = str;
#ifdef TE_PE
if (*input_line_pointer == '[')
{
int valid_toc;
enum toc_size_qualifier toc_kind;
bfd_reloc_code_real_type toc_reloc;
valid_toc = parse_toc_entry (&toc_kind);
if (!valid_toc)
{
}
expression (&ex);
switch (toc_kind)
{
case default_toc:
toc_reloc = BFD_RELOC_PPC_TOC16;
break;
case data_in_toc:
if (ex.X_op == O_symbol)
{
assert (ex.X_add_symbol != NULL);
if (symbol_get_bfdsym (ex.X_add_symbol)->section
!= tocdata_section)
{
as_bad (_("[tocv] symbol is not a toc symbol"));
}
}
toc_reloc = BFD_RELOC_PPC_TOC16;
break;
case must_be_32:
toc_reloc = BFD_RELOC_UNUSED;
as_bad (_("Unimplemented toc32 expression modifier"));
break;
case must_be_64:
toc_reloc = BFD_RELOC_UNUSED;
as_bad (_("Unimplemented toc64 expression modifier"));
break;
default:
fprintf (stderr,
_("Unexpected return value [%d] from parse_toc_entry!\n"),
toc_kind);
abort ();
break;
}
if (fc >= MAX_INSN_FIXUPS)
as_fatal (_("too many fixups"));
fixups[fc].reloc = toc_reloc;
fixups[fc].exp = ex;
fixups[fc].opindex = *opindex_ptr;
++fc;
ex.X_unsigned = 1;
ex.X_op = O_constant;
ex.X_add_number = 0;
ex.X_add_symbol = NULL;
ex.X_op_symbol = NULL;
}
else
#endif
{
if (! register_name (&ex))
{
if ((operand->flags & PPC_OPERAND_CR) != 0)
cr_operand = TRUE;
expression (&ex);
cr_operand = FALSE;
}
}
str = input_line_pointer;
input_line_pointer = hold;
if (ex.X_op == O_illegal)
as_bad (_("illegal operand"));
else if (ex.X_op == O_absent)
as_bad (_("missing operand"));
else if (ex.X_op == O_register)
{
insn = ppc_insert_operand (insn, operand, ex.X_add_number,
(char *) NULL, 0);
}
else if (ex.X_op == O_constant)
{
#ifdef OBJ_ELF
char *orig_str = str;
if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
switch (reloc)
{
default:
str = orig_str;
break;
case BFD_RELOC_LO16:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number &= 0xffff;
else
ex.X_add_number = SEX16 (ex.X_add_number);
break;
case BFD_RELOC_HI16:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number = PPC_HI (ex.X_add_number);
else
ex.X_add_number = SEX16 (PPC_HI (ex.X_add_number));
break;
case BFD_RELOC_HI16_S:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number = PPC_HA (ex.X_add_number);
else
ex.X_add_number = SEX16 (PPC_HA (ex.X_add_number));
break;
case BFD_RELOC_PPC64_HIGHER:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number = PPC_HIGHER (ex.X_add_number);
else
ex.X_add_number = SEX16 (PPC_HIGHER (ex.X_add_number));
break;
case BFD_RELOC_PPC64_HIGHER_S:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
else
ex.X_add_number = SEX16 (PPC_HIGHERA (ex.X_add_number));
break;
case BFD_RELOC_PPC64_HIGHEST:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
else
ex.X_add_number = SEX16 (PPC_HIGHEST (ex.X_add_number));
break;
case BFD_RELOC_PPC64_HIGHEST_S:
if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
else
ex.X_add_number = SEX16 (PPC_HIGHESTA (ex.X_add_number));
break;
}
#endif
insn = ppc_insert_operand (insn, operand, ex.X_add_number,
(char *) NULL, 0);
}
#ifdef OBJ_ELF
else if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
{
switch (reloc)
{
default:
break;
case BFD_RELOC_PPC_TLS:
insn = ppc_insert_operand (insn, operand, ppc_obj64 ? 13 : 2,
(char *) NULL, 0);
break;
case BFD_RELOC_PPC_DTPREL:
reloc = BFD_RELOC_PPC_DTPREL16;
break;
case BFD_RELOC_PPC_TPREL:
reloc = BFD_RELOC_PPC_TPREL16;
break;
}
if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
{
switch (reloc)
{
case BFD_RELOC_PPC_B26:
reloc = BFD_RELOC_PPC_BA26;
break;
case BFD_RELOC_PPC_B16:
reloc = BFD_RELOC_PPC_BA16;
break;
case BFD_RELOC_PPC_B16_BRTAKEN:
reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
break;
case BFD_RELOC_PPC_B16_BRNTAKEN:
reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
break;
default:
break;
}
}
if (ppc_obj64
&& (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
{
switch (reloc)
{
case BFD_RELOC_16:
reloc = BFD_RELOC_PPC64_ADDR16_DS;
break;
case BFD_RELOC_LO16:
reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
break;
case BFD_RELOC_16_GOTOFF:
reloc = BFD_RELOC_PPC64_GOT16_DS;
break;
case BFD_RELOC_LO16_GOTOFF:
reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
break;
case BFD_RELOC_LO16_PLTOFF:
reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
break;
case BFD_RELOC_16_BASEREL:
reloc = BFD_RELOC_PPC64_SECTOFF_DS;
break;
case BFD_RELOC_LO16_BASEREL:
reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
break;
case BFD_RELOC_PPC_TOC16:
reloc = BFD_RELOC_PPC64_TOC16_DS;
break;
case BFD_RELOC_PPC64_TOC16_LO:
reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
break;
case BFD_RELOC_PPC64_PLTGOT16:
reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
break;
case BFD_RELOC_PPC64_PLTGOT16_LO:
reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
break;
case BFD_RELOC_PPC_DTPREL16:
reloc = BFD_RELOC_PPC64_DTPREL16_DS;
break;
case BFD_RELOC_PPC_DTPREL16_LO:
reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
break;
case BFD_RELOC_PPC_TPREL16:
reloc = BFD_RELOC_PPC64_TPREL16_DS;
break;
case BFD_RELOC_PPC_TPREL16_LO:
reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
break;
case BFD_RELOC_PPC_GOT_DTPREL16:
case BFD_RELOC_PPC_GOT_DTPREL16_LO:
case BFD_RELOC_PPC_GOT_TPREL16:
case BFD_RELOC_PPC_GOT_TPREL16_LO:
break;
default:
as_bad (_("unsupported relocation for DS offset field"));
break;
}
}
if (fc >= MAX_INSN_FIXUPS)
as_fatal (_("too many fixups"));
fixups[fc].exp = ex;
fixups[fc].opindex = 0;
fixups[fc].reloc = reloc;
++fc;
}
#endif
else
{
if (fc >= MAX_INSN_FIXUPS)
as_fatal (_("too many fixups"));
fixups[fc].exp = ex;
fixups[fc].opindex = *opindex_ptr;
fixups[fc].reloc = BFD_RELOC_UNUSED;
++fc;
}
if (need_paren)
{
endc = ')';
need_paren = 0;
}
else if ((operand->flags & PPC_OPERAND_PARENS) != 0)
{
endc = '(';
need_paren = 1;
}
else
endc = ',';
if (*str != endc
&& (endc != ',' || *str != '\0'))
{
as_bad (_("syntax error; found `%c' but expected `%c'"), *str, endc);
break;
}
if (*str != '\0')
++str;
}
while (ISSPACE (*str))
++str;
if (*str != '\0')
as_bad (_("junk at end of line: `%s'"), str);
#ifdef OBJ_ELF
if (ppc_cpu & (PPC_OPCODE_SPE
| PPC_OPCODE_ISEL | PPC_OPCODE_EFS
| PPC_OPCODE_BRLOCK | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK
| PPC_OPCODE_RFMCI))
{
if (opcode->flags & PPC_OPCODE_SPE)
ppc_apuinfo_section_add (PPC_APUINFO_SPE, 1);
if (opcode->flags & PPC_OPCODE_ISEL)
ppc_apuinfo_section_add (PPC_APUINFO_ISEL, 1);
if (opcode->flags & PPC_OPCODE_EFS)
ppc_apuinfo_section_add (PPC_APUINFO_EFS, 1);
if (opcode->flags & PPC_OPCODE_BRLOCK)
ppc_apuinfo_section_add (PPC_APUINFO_BRLOCK, 1);
if (opcode->flags & PPC_OPCODE_PMR)
ppc_apuinfo_section_add (PPC_APUINFO_PMR, 1);
if (opcode->flags & PPC_OPCODE_CACHELCK)
ppc_apuinfo_section_add (PPC_APUINFO_CACHELCK, 1);
if (opcode->flags & PPC_OPCODE_RFMCI)
ppc_apuinfo_section_add (PPC_APUINFO_RFMCI, 1);
}
#endif
f = frag_more (4);
addr_mod = frag_now_fix () & 3;
if (frag_now->has_code && frag_now->insn_addr != addr_mod)
as_bad (_("instruction address is not a multiple of 4"));
frag_now->insn_addr = addr_mod;
frag_now->has_code = 1;
md_number_to_chars (f, insn, 4);
#ifdef OBJ_ELF
dwarf2_emit_insn (4);
#endif
for (i = 0; i < fc; i++)
{
const struct powerpc_operand *operand;
operand = &powerpc_operands[fixups[i].opindex];
if (fixups[i].reloc != BFD_RELOC_UNUSED)
{
reloc_howto_type *reloc_howto;
int size;
int offset;
fixS *fixP;
reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
if (!reloc_howto)
abort ();
size = bfd_get_reloc_size (reloc_howto);
offset = target_big_endian ? (4 - size) : 0;
if (size < 1 || size > 4)
abort ();
fixP = fix_new_exp (frag_now,
f - frag_now->fr_literal + offset,
size,
&fixups[i].exp,
reloc_howto->pc_relative,
fixups[i].reloc);
switch (fixups[i].reloc)
{
case BFD_RELOC_16_GOTOFF:
case BFD_RELOC_PPC_TOC16:
case BFD_RELOC_LO16:
case BFD_RELOC_HI16:
case BFD_RELOC_HI16_S:
#ifdef OBJ_ELF
case BFD_RELOC_PPC64_HIGHER:
case BFD_RELOC_PPC64_HIGHER_S:
case BFD_RELOC_PPC64_HIGHEST:
case BFD_RELOC_PPC64_HIGHEST_S:
#endif
fixP->fx_no_overflow = 1;
break;
default:
break;
}
}
else
fix_new_exp (frag_now,
f - frag_now->fr_literal,
4,
&fixups[i].exp,
(operand->flags & PPC_OPERAND_RELATIVE) != 0,
((bfd_reloc_code_real_type)
(fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
}
}
static void
ppc_macro (str, macro)
char *str;
const struct powerpc_macro *macro;
{
char *operands[10];
unsigned int count;
char *s;
unsigned int len;
const char *format;
int arg;
char *send;
char *complete;
count = 0;
s = str;
while (1)
{
if (count >= sizeof operands / sizeof operands[0])
break;
operands[count++] = s;
s = strchr (s, ',');
if (s == (char *) NULL)
break;
*s++ = '\0';
}
if (count != macro->operands)
{
as_bad (_("wrong number of operands"));
return;
}
len = 0;
format = macro->format;
while (*format != '\0')
{
if (*format != '%')
{
++len;
++format;
}
else
{
arg = strtol (format + 1, &send, 10);
know (send != format && arg >= 0 && arg < count);
len += strlen (operands[arg]);
format = send;
}
}
complete = s = (char *) alloca (len + 1);
format = macro->format;
while (*format != '\0')
{
if (*format != '%')
*s++ = *format++;
else
{
arg = strtol (format + 1, &send, 10);
strcpy (s, operands[arg]);
s += strlen (s);
format = send;
}
}
*s = '\0';
md_assemble (complete);
}
#ifdef OBJ_ELF
int
ppc_section_letter (letter, ptr_msg)
int letter;
char **ptr_msg;
{
if (letter == 'e')
return SHF_EXCLUDE;
*ptr_msg = _("Bad .section directive: want a,e,w,x,M,S,G,T in string");
return -1;
}
int
ppc_section_word (str, len)
char *str;
size_t len;
{
if (len == 7 && strncmp (str, "exclude", 7) == 0)
return SHF_EXCLUDE;
return -1;
}
int
ppc_section_type (str, len)
char *str;
size_t len;
{
if (len == 7 && strncmp (str, "ordered", 7) == 0)
return SHT_ORDERED;
return -1;
}
int
ppc_section_flags (flags, attr, type)
int flags;
int attr;
int type;
{
if (type == SHT_ORDERED)
flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES;
if (attr & SHF_EXCLUDE)
flags |= SEC_EXCLUDE;
return flags;
}
#endif
static void
ppc_byte (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (*input_line_pointer != '\"')
{
cons (1);
return;
}
++input_line_pointer;
while (1)
{
char c;
c = *input_line_pointer++;
if (c == '\"')
{
if (*input_line_pointer != '\"')
break;
++input_line_pointer;
}
FRAG_APPEND_1_CHAR (c);
}
demand_empty_rest_of_line ();
}
#ifdef OBJ_XCOFF
static bfd_boolean ppc_stab_symbol;
static void
ppc_comm (lcomm)
int lcomm;
{
asection *current_seg = now_seg;
subsegT current_subseg = now_subseg;
char *name;
char endc;
char *end_name;
offsetT size;
offsetT align;
symbolS *lcomm_sym = NULL;
symbolS *sym;
char *pfrag;
name = input_line_pointer;
endc = get_symbol_end ();
end_name = input_line_pointer;
*end_name = endc;
if (*input_line_pointer != ',')
{
as_bad (_("missing size"));
ignore_rest_of_line ();
return;
}
++input_line_pointer;
size = get_absolute_expression ();
if (size < 0)
{
as_bad (_("negative size"));
ignore_rest_of_line ();
return;
}
if (! lcomm)
{
if (*input_line_pointer != ',')
align = 2;
else
{
++input_line_pointer;
align = get_absolute_expression ();
if (align <= 0)
{
as_warn (_("ignoring bad alignment"));
align = 2;
}
}
}
else
{
char *lcomm_name;
char lcomm_endc;
if (size <= 4)
align = 2;
else
align = 3;
if (*input_line_pointer != ',')
{
as_bad (_("missing real symbol name"));
ignore_rest_of_line ();
return;
}
++input_line_pointer;
lcomm_name = input_line_pointer;
lcomm_endc = get_symbol_end ();
lcomm_sym = symbol_find_or_make (lcomm_name);
*input_line_pointer = lcomm_endc;
}
*end_name = '\0';
sym = symbol_find_or_make (name);
*end_name = endc;
if (S_IS_DEFINED (sym)
|| S_GET_VALUE (sym) != 0)
{
as_bad (_("attempt to redefine symbol"));
ignore_rest_of_line ();
return;
}
record_alignment (bss_section, align);
if (! lcomm
|| ! S_IS_DEFINED (lcomm_sym))
{
symbolS *def_sym;
offsetT def_size;
if (! lcomm)
{
def_sym = sym;
def_size = size;
S_SET_EXTERNAL (sym);
}
else
{
symbol_get_tc (lcomm_sym)->output = 1;
def_sym = lcomm_sym;
def_size = 0;
}
subseg_set (bss_section, 1);
frag_align (align, 0, 0);
symbol_set_frag (def_sym, frag_now);
pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym,
def_size, (char *) NULL);
*pfrag = 0;
S_SET_SEGMENT (def_sym, bss_section);
symbol_get_tc (def_sym)->align = align;
}
else if (lcomm)
{
symbol_get_frag (lcomm_sym)->fr_offset =
((symbol_get_frag (lcomm_sym)->fr_offset + (1 << align) - 1)
&~ ((1 << align) - 1));
if (align > symbol_get_tc (lcomm_sym)->align)
symbol_get_tc (lcomm_sym)->align = align;
}
if (lcomm)
{
S_SET_SEGMENT (sym, bss_section);
symbol_set_frag (sym, symbol_get_frag (lcomm_sym));
S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset);
symbol_get_frag (lcomm_sym)->fr_offset += size;
}
subseg_set (current_seg, current_subseg);
demand_empty_rest_of_line ();
}
static void
ppc_csect (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
symbolS *sym;
offsetT align;
name = input_line_pointer;
endc = get_symbol_end ();
sym = symbol_find_or_make (name);
*input_line_pointer = endc;
if (S_GET_NAME (sym)[0] == '\0')
{
symbol_get_tc (sym)->class = XMC_PR;
}
align = 2;
if (*input_line_pointer == ',')
{
++input_line_pointer;
align = get_absolute_expression ();
}
ppc_change_csect (sym, align);
demand_empty_rest_of_line ();
}
static void
ppc_change_csect (sym, align)
symbolS *sym;
offsetT align;
{
if (S_IS_DEFINED (sym))
subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg);
else
{
symbolS **list_ptr;
int after_toc;
int hold_chunksize;
symbolS *list;
int is_code;
segT sec;
after_toc = 0;
is_code = 0;
switch (symbol_get_tc (sym)->class)
{
case XMC_PR:
case XMC_RO:
case XMC_DB:
case XMC_GL:
case XMC_XO:
case XMC_SV:
case XMC_TI:
case XMC_TB:
S_SET_SEGMENT (sym, text_section);
symbol_get_tc (sym)->subseg = ppc_text_subsegment;
++ppc_text_subsegment;
list_ptr = &ppc_text_csects;
is_code = 1;
break;
case XMC_RW:
case XMC_TC0:
case XMC_TC:
case XMC_DS:
case XMC_UA:
case XMC_BS:
case XMC_UC:
if (ppc_toc_csect != NULL
&& (symbol_get_tc (ppc_toc_csect)->subseg + 1
== ppc_data_subsegment))
after_toc = 1;
S_SET_SEGMENT (sym, data_section);
symbol_get_tc (sym)->subseg = ppc_data_subsegment;
++ppc_data_subsegment;
list_ptr = &ppc_data_csects;
break;
default:
abort ();
}
hold_chunksize = chunksize;
chunksize = 64;
sec = subseg_new (segment_name (S_GET_SEGMENT (sym)),
symbol_get_tc (sym)->subseg);
chunksize = hold_chunksize;
if (after_toc)
ppc_after_toc_frag = frag_now;
record_alignment (sec, align);
if (is_code)
frag_align_code (align, 0);
else
frag_align (align, 0, 0);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, (valueT) frag_now_fix ());
symbol_get_tc (sym)->align = align;
symbol_get_tc (sym)->output = 1;
symbol_get_tc (sym)->within = sym;
for (list = *list_ptr;
symbol_get_tc (list)->next != (symbolS *) NULL;
list = symbol_get_tc (list)->next)
;
symbol_get_tc (list)->next = sym;
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP,
&symbol_lastP);
}
ppc_current_csect = sym;
}
static void
ppc_section (type)
int type;
{
const char *name;
symbolS *sym;
if (type == 't')
name = ".text[PR]";
else if (type == 'd')
name = ".data[RW]";
else
abort ();
sym = symbol_find_or_make (name);
ppc_change_csect (sym, 2);
demand_empty_rest_of_line ();
}
static void
ppc_named_section (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *user_name;
const char *real_name;
char c;
symbolS *sym;
user_name = input_line_pointer;
c = get_symbol_end ();
if (strcmp (user_name, ".text") == 0)
real_name = ".text[PR]";
else if (strcmp (user_name, ".data") == 0)
real_name = ".data[RW]";
else
{
as_bad (_("The XCOFF file format does not support arbitrary sections"));
*input_line_pointer = c;
ignore_rest_of_line ();
return;
}
*input_line_pointer = c;
sym = symbol_find_or_make (real_name);
ppc_change_csect (sym, 2);
demand_empty_rest_of_line ();
}
static void
ppc_extern (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
name = input_line_pointer;
endc = get_symbol_end ();
(void) symbol_find_or_make (name);
*input_line_pointer = endc;
demand_empty_rest_of_line ();
}
static void
ppc_lglobl (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
symbolS *sym;
name = input_line_pointer;
endc = get_symbol_end ();
sym = symbol_find_or_make (name);
*input_line_pointer = endc;
symbol_get_tc (sym)->output = 1;
demand_empty_rest_of_line ();
}
static void
ppc_rename (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
symbolS *sym;
int len;
name = input_line_pointer;
endc = get_symbol_end ();
sym = symbol_find_or_make (name);
*input_line_pointer = endc;
if (*input_line_pointer != ',')
{
as_bad (_("missing rename string"));
ignore_rest_of_line ();
return;
}
++input_line_pointer;
symbol_get_tc (sym)->real_name = demand_copy_C_string (&len);
demand_empty_rest_of_line ();
}
static void
ppc_stabx (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
int len;
symbolS *sym;
expressionS exp;
name = demand_copy_C_string (&len);
if (*input_line_pointer != ',')
{
as_bad (_("missing value"));
return;
}
++input_line_pointer;
ppc_stab_symbol = TRUE;
sym = symbol_make (name);
ppc_stab_symbol = FALSE;
symbol_get_tc (sym)->real_name = name;
(void) expression (&exp);
switch (exp.X_op)
{
case O_illegal:
case O_absent:
case O_big:
as_bad (_("illegal .stabx expression; zero assumed"));
exp.X_add_number = 0;
case O_constant:
S_SET_VALUE (sym, (valueT) exp.X_add_number);
symbol_set_frag (sym, &zero_address_frag);
break;
case O_symbol:
if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section)
symbol_set_value_expression (sym, &exp);
else
{
S_SET_VALUE (sym,
exp.X_add_number + S_GET_VALUE (exp.X_add_symbol));
symbol_set_frag (sym, symbol_get_frag (exp.X_add_symbol));
}
break;
default:
symbol_set_value_expression (sym, &exp);
break;
}
S_SET_SEGMENT (sym, ppc_coff_debug_section);
symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
if (*input_line_pointer != ',')
{
as_bad (_("missing class"));
return;
}
++input_line_pointer;
S_SET_STORAGE_CLASS (sym, get_absolute_expression ());
if (*input_line_pointer != ',')
{
as_bad (_("missing type"));
return;
}
++input_line_pointer;
S_SET_DATA_TYPE (sym, get_absolute_expression ());
symbol_get_tc (sym)->output = 1;
if (S_GET_STORAGE_CLASS (sym) == C_STSYM) {
symbol_get_tc (sym)->within = ppc_current_block;
if (exp.X_op == O_symbol)
{
symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block;
}
}
if (exp.X_op != O_symbol
|| ! S_IS_EXTERNAL (exp.X_add_symbol)
|| S_GET_SEGMENT (exp.X_add_symbol) != bss_section)
ppc_frob_label (sym);
else
{
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
symbol_append (sym, exp.X_add_symbol, &symbol_rootP, &symbol_lastP);
if (symbol_get_tc (ppc_current_csect)->within == exp.X_add_symbol)
symbol_get_tc (ppc_current_csect)->within = sym;
}
demand_empty_rest_of_line ();
}
static void
ppc_function (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
char *s;
symbolS *ext_sym;
symbolS *lab_sym;
name = input_line_pointer;
endc = get_symbol_end ();
name = ppc_canonicalize_symbol_name (name);
s = strchr (name, '[');
if (s != (char *) NULL
&& strcmp (s + 1, "PR]") == 0)
*s = '\0';
ext_sym = symbol_find_or_make (name);
*input_line_pointer = endc;
if (*input_line_pointer != ',')
{
as_bad (_("missing symbol name"));
ignore_rest_of_line ();
return;
}
++input_line_pointer;
name = input_line_pointer;
endc = get_symbol_end ();
lab_sym = symbol_find_or_make (name);
*input_line_pointer = endc;
if (ext_sym != lab_sym)
{
expressionS exp;
exp.X_op = O_symbol;
exp.X_add_symbol = lab_sym;
exp.X_op_symbol = NULL;
exp.X_add_number = 0;
exp.X_unsigned = 0;
symbol_set_value_expression (ext_sym, &exp);
}
if (symbol_get_tc (ext_sym)->class == -1)
symbol_get_tc (ext_sym)->class = XMC_PR;
symbol_get_tc (ext_sym)->output = 1;
if (*input_line_pointer == ',')
{
expressionS ignore;
++input_line_pointer;
expression (&ignore);
if (*input_line_pointer == ',')
{
++input_line_pointer;
expression (&ignore);
if (*input_line_pointer == ',')
{
++input_line_pointer;
symbol_get_tc (ext_sym)->size = symbol_new ("L0\001",
absolute_section,
(valueT) 0,
&zero_address_frag);
pseudo_set (symbol_get_tc (ext_sym)->size);
}
}
}
S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
SF_SET_FUNCTION (ext_sym);
SF_SET_PROCESS (ext_sym);
coff_add_linesym (ext_sym);
demand_empty_rest_of_line ();
}
static symbolS *saved_bi_sym = 0;
static void
ppc_bf (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *sym;
sym = symbol_make (".bf");
S_SET_SEGMENT (sym, text_section);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, frag_now_fix ());
S_SET_STORAGE_CLASS (sym, C_FCN);
coff_line_base = get_absolute_expression ();
S_SET_NUMBER_AUXILIARY (sym, 1);
SA_SET_SYM_LNNO (sym, coff_line_base);
if (saved_bi_sym)
{
S_SET_VALUE (saved_bi_sym, coff_n_line_nos);
saved_bi_sym = 0;
}
symbol_get_tc (sym)->output = 1;
ppc_frob_label (sym);
demand_empty_rest_of_line ();
}
static void
ppc_ef (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *sym;
sym = symbol_make (".ef");
S_SET_SEGMENT (sym, text_section);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, frag_now_fix ());
S_SET_STORAGE_CLASS (sym, C_FCN);
S_SET_NUMBER_AUXILIARY (sym, 1);
SA_SET_SYM_LNNO (sym, get_absolute_expression ());
symbol_get_tc (sym)->output = 1;
ppc_frob_label (sym);
demand_empty_rest_of_line ();
}
static void
ppc_biei (ei)
int ei;
{
static symbolS *last_biei;
char *name;
int len;
symbolS *sym;
symbolS *look;
name = demand_copy_C_string (&len);
sym = symbol_make (name);
S_SET_SEGMENT (sym, text_section);
S_SET_VALUE (sym, coff_n_line_nos);
symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
S_SET_STORAGE_CLASS (sym, ei ? C_EINCL : C_BINCL);
symbol_get_tc (sym)->output = 1;
if (ei)
saved_bi_sym = 0;
else
saved_bi_sym = sym;
for (look = last_biei ? last_biei : symbol_rootP;
(look != (symbolS *) NULL
&& (S_GET_STORAGE_CLASS (look) == C_FILE
|| S_GET_STORAGE_CLASS (look) == C_BINCL
|| S_GET_STORAGE_CLASS (look) == C_EINCL));
look = symbol_next (look))
;
if (look != (symbolS *) NULL)
{
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
symbol_insert (sym, look, &symbol_rootP, &symbol_lastP);
last_biei = sym;
}
demand_empty_rest_of_line ();
}
static void
ppc_bs (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
symbolS *csect;
symbolS *sym;
if (ppc_current_block != NULL)
as_bad (_("nested .bs blocks"));
name = input_line_pointer;
endc = get_symbol_end ();
csect = symbol_find_or_make (name);
*input_line_pointer = endc;
sym = symbol_make (".bs");
S_SET_SEGMENT (sym, now_seg);
S_SET_STORAGE_CLASS (sym, C_BSTAT);
symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
symbol_get_tc (sym)->output = 1;
symbol_get_tc (sym)->within = csect;
ppc_frob_label (sym);
ppc_current_block = sym;
demand_empty_rest_of_line ();
}
static void
ppc_es (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *sym;
if (ppc_current_block == NULL)
as_bad (_(".es without preceding .bs"));
sym = symbol_make (".es");
S_SET_SEGMENT (sym, now_seg);
S_SET_STORAGE_CLASS (sym, C_ESTAT);
symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
symbol_get_tc (sym)->output = 1;
ppc_frob_label (sym);
ppc_current_block = NULL;
demand_empty_rest_of_line ();
}
static void
ppc_bb (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *sym;
sym = symbol_make (".bb");
S_SET_SEGMENT (sym, text_section);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, frag_now_fix ());
S_SET_STORAGE_CLASS (sym, C_BLOCK);
S_SET_NUMBER_AUXILIARY (sym, 1);
SA_SET_SYM_LNNO (sym, get_absolute_expression ());
symbol_get_tc (sym)->output = 1;
SF_SET_PROCESS (sym);
ppc_frob_label (sym);
demand_empty_rest_of_line ();
}
static void
ppc_eb (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *sym;
sym = symbol_make (".eb");
S_SET_SEGMENT (sym, text_section);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, frag_now_fix ());
S_SET_STORAGE_CLASS (sym, C_BLOCK);
S_SET_NUMBER_AUXILIARY (sym, 1);
SA_SET_SYM_LNNO (sym, get_absolute_expression ());
symbol_get_tc (sym)->output = 1;
SF_SET_PROCESS (sym);
ppc_frob_label (sym);
demand_empty_rest_of_line ();
}
static void
ppc_bc (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
int len;
symbolS *sym;
name = demand_copy_C_string (&len);
sym = symbol_make (name);
S_SET_SEGMENT (sym, ppc_coff_debug_section);
symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
S_SET_STORAGE_CLASS (sym, C_BCOMM);
S_SET_VALUE (sym, 0);
symbol_get_tc (sym)->output = 1;
ppc_frob_label (sym);
demand_empty_rest_of_line ();
}
static void
ppc_ec (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *sym;
sym = symbol_make (".ec");
S_SET_SEGMENT (sym, ppc_coff_debug_section);
symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
S_SET_STORAGE_CLASS (sym, C_ECOMM);
S_SET_VALUE (sym, 0);
symbol_get_tc (sym)->output = 1;
ppc_frob_label (sym);
demand_empty_rest_of_line ();
}
static void
ppc_toc (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (ppc_toc_csect != (symbolS *) NULL)
subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg);
else
{
subsegT subseg;
symbolS *sym;
symbolS *list;
subseg = ppc_data_subsegment;
++ppc_data_subsegment;
subseg_new (segment_name (data_section), subseg);
ppc_toc_frag = frag_now;
sym = symbol_find_or_make ("TOC[TC0]");
symbol_set_frag (sym, frag_now);
S_SET_SEGMENT (sym, data_section);
S_SET_VALUE (sym, (valueT) frag_now_fix ());
symbol_get_tc (sym)->subseg = subseg;
symbol_get_tc (sym)->output = 1;
symbol_get_tc (sym)->within = sym;
ppc_toc_csect = sym;
for (list = ppc_data_csects;
symbol_get_tc (list)->next != (symbolS *) NULL;
list = symbol_get_tc (list)->next)
;
symbol_get_tc (list)->next = sym;
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP,
&symbol_lastP);
}
ppc_current_csect = ppc_toc_csect;
demand_empty_rest_of_line ();
}
static void
ppc_xcoff_cons (log_size)
int log_size;
{
frag_align (log_size, 0, 0);
record_alignment (now_seg, log_size);
cons (1 << log_size);
}
static void
ppc_vbyte (dummy)
int dummy ATTRIBUTE_UNUSED;
{
expressionS exp;
int byte_count;
(void) expression (&exp);
if (exp.X_op != O_constant)
{
as_bad (_("non-constant byte count"));
return;
}
byte_count = exp.X_add_number;
if (*input_line_pointer != ',')
{
as_bad (_("missing value"));
return;
}
++input_line_pointer;
cons (byte_count);
}
#endif
#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
static void
ppc_tc (ignore)
int ignore ATTRIBUTE_UNUSED;
{
#ifdef OBJ_XCOFF
{
char *name;
char endc;
symbolS *sym;
if (ppc_toc_csect == (symbolS *) NULL
|| ppc_toc_csect != ppc_current_csect)
{
as_bad (_(".tc not in .toc section"));
ignore_rest_of_line ();
return;
}
name = input_line_pointer;
endc = get_symbol_end ();
sym = symbol_find_or_make (name);
*input_line_pointer = endc;
if (S_IS_DEFINED (sym))
{
symbolS *label;
label = symbol_get_tc (ppc_current_csect)->within;
if (symbol_get_tc (label)->class != XMC_TC0)
{
as_bad (_(".tc with no label"));
ignore_rest_of_line ();
return;
}
S_SET_SEGMENT (label, S_GET_SEGMENT (sym));
symbol_set_frag (label, symbol_get_frag (sym));
S_SET_VALUE (label, S_GET_VALUE (sym));
while (! is_end_of_line[(unsigned char) *input_line_pointer])
++input_line_pointer;
return;
}
S_SET_SEGMENT (sym, now_seg);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, (valueT) frag_now_fix ());
symbol_get_tc (sym)->class = XMC_TC;
symbol_get_tc (sym)->output = 1;
ppc_frob_label (sym);
}
#endif
#ifdef OBJ_ELF
int align;
while (is_part_of_name (*input_line_pointer)
|| *input_line_pointer == '['
|| *input_line_pointer == ']'
|| *input_line_pointer == '{'
|| *input_line_pointer == '}')
++input_line_pointer;
align = ppc_obj64 ? 3 : 2;
frag_align (align, 0, 0);
record_alignment (now_seg, align);
#endif
if (*input_line_pointer != ',')
demand_empty_rest_of_line ();
else
{
++input_line_pointer;
cons (ppc_obj64 ? 8 : 4);
}
}
static void
ppc_machine (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *cpu_string;
#define MAX_HISTORY 100
static unsigned long *cpu_history;
static int curr_hist;
SKIP_WHITESPACE ();
if (*input_line_pointer == '"')
{
int len;
cpu_string = demand_copy_C_string (&len);
}
else
{
char c;
cpu_string = input_line_pointer;
c = get_symbol_end ();
cpu_string = xstrdup (cpu_string);
*input_line_pointer = c;
}
if (cpu_string != NULL)
{
unsigned long old_cpu = ppc_cpu;
char *p;
for (p = cpu_string; *p != 0; p++)
*p = TOLOWER (*p);
if (strcmp (cpu_string, "push") == 0)
{
if (cpu_history == NULL)
cpu_history = xmalloc (MAX_HISTORY * sizeof (*cpu_history));
if (curr_hist >= MAX_HISTORY)
as_bad (_(".machine stack overflow"));
else
cpu_history[curr_hist++] = ppc_cpu;
}
else if (strcmp (cpu_string, "pop") == 0)
{
if (curr_hist <= 0)
as_bad (_(".machine stack underflow"));
else
ppc_cpu = cpu_history[--curr_hist];
}
else if (parse_cpu (cpu_string))
;
else
as_bad (_("invalid machine `%s'"), cpu_string);
if (ppc_cpu != old_cpu)
ppc_setup_opcodes ();
}
demand_empty_rest_of_line ();
}
static int
ppc_is_toc_sym (sym)
symbolS *sym;
{
#ifdef OBJ_XCOFF
return symbol_get_tc (sym)->class == XMC_TC;
#endif
#ifdef OBJ_ELF
const char *sname = segment_name (S_GET_SEGMENT (sym));
if (ppc_obj64)
return strcmp (sname, ".toc") == 0;
else
return strcmp (sname, ".got") == 0;
#endif
}
#endif
#ifdef TE_PE
static void
ppc_set_current_section (new)
segT new;
{
ppc_previous_section = ppc_current_section;
ppc_current_section = new;
}
static void
ppc_previous (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *tmp;
if (ppc_previous_section == NULL)
{
as_warn (_("No previous section to return to. Directive ignored."));
return;
}
subseg_set (ppc_previous_section, 0);
ppc_set_current_section (ppc_previous_section);
}
static void
ppc_pdata (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (pdata_section == 0)
{
pdata_section = subseg_new (".pdata", 0);
bfd_set_section_flags (stdoutput, pdata_section,
(SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_READONLY | SEC_DATA ));
bfd_set_section_alignment (stdoutput, pdata_section, 2);
}
else
{
pdata_section = subseg_new (".pdata", 0);
}
ppc_set_current_section (pdata_section);
}
static void
ppc_ydata (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (ydata_section == 0)
{
ydata_section = subseg_new (".ydata", 0);
bfd_set_section_flags (stdoutput, ydata_section,
(SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_READONLY | SEC_DATA ));
bfd_set_section_alignment (stdoutput, ydata_section, 3);
}
else
{
ydata_section = subseg_new (".ydata", 0);
}
ppc_set_current_section (ydata_section);
}
static void
ppc_reldata (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (reldata_section == 0)
{
reldata_section = subseg_new (".reldata", 0);
bfd_set_section_flags (stdoutput, reldata_section,
(SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_DATA));
bfd_set_section_alignment (stdoutput, reldata_section, 2);
}
else
{
reldata_section = subseg_new (".reldata", 0);
}
ppc_set_current_section (reldata_section);
}
static void
ppc_rdata (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (rdata_section == 0)
{
rdata_section = subseg_new (".rdata", 0);
bfd_set_section_flags (stdoutput, rdata_section,
(SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_READONLY | SEC_DATA ));
bfd_set_section_alignment (stdoutput, rdata_section, 2);
}
else
{
rdata_section = subseg_new (".rdata", 0);
}
ppc_set_current_section (rdata_section);
}
static void
ppc_ualong (ignore)
int ignore ATTRIBUTE_UNUSED;
{
cons (4);
}
static void
ppc_znop (ignore)
int ignore ATTRIBUTE_UNUSED;
{
unsigned long insn;
const struct powerpc_opcode *opcode;
expressionS ex;
char *f;
symbolS *sym;
char *symbol_name;
char c;
char *name;
unsigned int exp;
flagword flags;
asection *sec;
symbol_name = input_line_pointer;
c = get_symbol_end ();
name = xmalloc (input_line_pointer - symbol_name + 1);
strcpy (name, symbol_name);
sym = symbol_find_or_make (name);
*input_line_pointer = c;
SKIP_WHITESPACE ();
opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop");
insn = opcode->opcode;
f = frag_more (4);
md_number_to_chars (f, insn, 4);
fix_new (frag_now,
f - frag_now->fr_literal,
4,
sym,
0,
0,
BFD_RELOC_16_GOT_PCREL);
}
static void
ppc_pe_comm (lcomm)
int lcomm;
{
register char *name;
register char c;
register char *p;
offsetT temp;
register symbolS *symbolP;
offsetT align;
name = input_line_pointer;
c = get_symbol_end ();
p = input_line_pointer;
*p = 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++;
if ((temp = get_absolute_expression ()) < 0)
{
as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp);
ignore_rest_of_line ();
return;
}
if (! lcomm)
{
if (*input_line_pointer != ',')
align = 3;
else
{
++input_line_pointer;
align = get_absolute_expression ();
if (align <= 0)
{
as_warn (_("ignoring bad alignment"));
align = 3;
}
}
}
*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))
{
if (S_GET_VALUE (symbolP) != (valueT) temp)
as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
S_GET_NAME (symbolP),
(long) S_GET_VALUE (symbolP),
(long) temp);
}
else
{
S_SET_VALUE (symbolP, (valueT) temp);
S_SET_EXTERNAL (symbolP);
}
demand_empty_rest_of_line ();
}
void
ppc_pe_section (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *section_name;
char c;
char *name;
unsigned int exp;
flagword flags;
segT sec;
int align;
section_name = input_line_pointer;
c = get_symbol_end ();
name = xmalloc (input_line_pointer - section_name + 1);
strcpy (name, section_name);
*input_line_pointer = c;
SKIP_WHITESPACE ();
exp = 0;
flags = SEC_NO_FLAGS;
if (strcmp (name, ".idata$2") == 0)
{
align = 0;
}
else if (strcmp (name, ".idata$3") == 0)
{
align = 0;
}
else if (strcmp (name, ".idata$4") == 0)
{
align = 2;
}
else if (strcmp (name, ".idata$5") == 0)
{
align = 2;
}
else if (strcmp (name, ".idata$6") == 0)
{
align = 1;
}
else
align = 4;
if (*input_line_pointer == ',')
{
++input_line_pointer;
SKIP_WHITESPACE ();
if (*input_line_pointer != '"')
exp = get_absolute_expression ();
else
{
++input_line_pointer;
while (*input_line_pointer != '"'
&& ! is_end_of_line[(unsigned char) *input_line_pointer])
{
switch (*input_line_pointer)
{
case 'a':
as_bad (_("Unsupported section attribute -- 'a'"));
break;
case 'c':
flags |= SEC_CODE;
break;
case 'd':
flags |= SEC_DATA;
break;
case 'u':
flags |= SEC_ROM;
break;
case 'i':
flags |= SEC_HAS_CONTENTS;
break;
case 'n':
flags &=~ SEC_LOAD;
break;
case 'R':
flags |= SEC_NEVER_LOAD;
break;
case 'r':
flags |= IMAGE_SCN_MEM_READ;
break;
case 'w':
flags |= IMAGE_SCN_MEM_WRITE;
break;
case 'x':
flags |= IMAGE_SCN_MEM_EXECUTE;
break;
case 's':
flags |= IMAGE_SCN_MEM_SHARED;
break;
case '0':
flags |= IMAGE_SCN_ALIGN_1BYTES;
align = 0;
break;
case '1':
flags |= IMAGE_SCN_ALIGN_2BYTES;
align = 1;
break;
case '2':
flags |= IMAGE_SCN_ALIGN_4BYTES;
align = 2;
break;
case '3':
flags |= IMAGE_SCN_ALIGN_8BYTES;
align = 3;
break;
case '4':
flags |= IMAGE_SCN_ALIGN_16BYTES;
align = 4;
break;
case '5':
flags |= IMAGE_SCN_ALIGN_32BYTES;
align = 5;
break;
case '6':
flags |= IMAGE_SCN_ALIGN_64BYTES;
align = 6;
break;
default:
as_bad (_("unknown section attribute '%c'"),
*input_line_pointer);
break;
}
++input_line_pointer;
}
if (*input_line_pointer == '"')
++input_line_pointer;
}
}
sec = subseg_new (name, (subsegT) exp);
ppc_set_current_section (sec);
if (flags != SEC_NO_FLAGS)
{
if (! bfd_set_section_flags (stdoutput, sec, flags))
as_bad (_("error setting flags for \"%s\": %s"),
bfd_section_name (stdoutput, sec),
bfd_errmsg (bfd_get_error ()));
}
bfd_set_section_alignment (stdoutput, sec, align);
}
static void
ppc_pe_function (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *name;
char endc;
symbolS *ext_sym;
name = input_line_pointer;
endc = get_symbol_end ();
ext_sym = symbol_find_or_make (name);
*input_line_pointer = endc;
S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
SF_SET_FUNCTION (ext_sym);
SF_SET_PROCESS (ext_sym);
coff_add_linesym (ext_sym);
demand_empty_rest_of_line ();
}
static void
ppc_pe_tocd (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (tocdata_section == 0)
{
tocdata_section = subseg_new (".tocd", 0);
bfd_set_section_flags (stdoutput, tocdata_section,
(SEC_ALLOC | SEC_LOAD | SEC_RELOC
| SEC_READONLY | SEC_DATA));
bfd_set_section_alignment (stdoutput, tocdata_section, 2);
}
else
{
rdata_section = subseg_new (".tocd", 0);
}
ppc_set_current_section (tocdata_section);
demand_empty_rest_of_line ();
}
int
ppc_pe_fix_adjustable (fix)
fixS *fix;
{
return fix->fx_r_type != BFD_RELOC_PPC_TOC16;
}
#endif
#ifdef OBJ_XCOFF
char *
ppc_canonicalize_symbol_name (name)
char *name;
{
char *s;
if (ppc_stab_symbol)
return name;
for (s = name; *s != '\0' && *s != '{' && *s != '['; s++)
;
if (*s != '\0')
{
char brac;
if (*s == '[')
brac = ']';
else
{
*s = '[';
brac = '}';
}
for (s++; *s != '\0' && *s != brac; s++)
*s = TOUPPER (*s);
if (*s == '\0' || s[1] != '\0')
as_bad (_("bad symbol suffix"));
*s = ']';
}
return name;
}
void
ppc_symbol_new_hook (sym)
symbolS *sym;
{
struct ppc_tc_sy *tc;
const char *s;
tc = symbol_get_tc (sym);
tc->next = NULL;
tc->output = 0;
tc->class = -1;
tc->real_name = NULL;
tc->subseg = 0;
tc->align = 0;
tc->size = NULL;
tc->within = NULL;
if (ppc_stab_symbol)
return;
s = strchr (S_GET_NAME (sym), '[');
if (s == (const char *) NULL)
{
return;
}
++s;
switch (s[0])
{
case 'B':
if (strcmp (s, "BS]") == 0)
tc->class = XMC_BS;
break;
case 'D':
if (strcmp (s, "DB]") == 0)
tc->class = XMC_DB;
else if (strcmp (s, "DS]") == 0)
tc->class = XMC_DS;
break;
case 'G':
if (strcmp (s, "GL]") == 0)
tc->class = XMC_GL;
break;
case 'P':
if (strcmp (s, "PR]") == 0)
tc->class = XMC_PR;
break;
case 'R':
if (strcmp (s, "RO]") == 0)
tc->class = XMC_RO;
else if (strcmp (s, "RW]") == 0)
tc->class = XMC_RW;
break;
case 'S':
if (strcmp (s, "SV]") == 0)
tc->class = XMC_SV;
break;
case 'T':
if (strcmp (s, "TC]") == 0)
tc->class = XMC_TC;
else if (strcmp (s, "TI]") == 0)
tc->class = XMC_TI;
else if (strcmp (s, "TB]") == 0)
tc->class = XMC_TB;
else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0)
tc->class = XMC_TC0;
break;
case 'U':
if (strcmp (s, "UA]") == 0)
tc->class = XMC_UA;
else if (strcmp (s, "UC]") == 0)
tc->class = XMC_UC;
break;
case 'X':
if (strcmp (s, "XO]") == 0)
tc->class = XMC_XO;
break;
}
if (tc->class == -1)
as_bad (_("Unrecognized symbol suffix"));
}
void
ppc_frob_label (sym)
symbolS *sym;
{
if (ppc_current_csect != (symbolS *) NULL)
{
if (symbol_get_tc (sym)->class == -1)
symbol_get_tc (sym)->class = symbol_get_tc (ppc_current_csect)->class;
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
&symbol_rootP, &symbol_lastP);
symbol_get_tc (ppc_current_csect)->within = sym;
}
}
static bfd_boolean ppc_saw_abs;
int
ppc_frob_symbol (sym)
symbolS *sym;
{
static symbolS *ppc_last_function;
static symbolS *set_end;
if (! symbol_used_in_reloc_p (sym)
&& ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0
|| (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
&& ! symbol_get_tc (sym)->output
&& S_GET_STORAGE_CLASS (sym) != C_FILE)))
return 1;
if (sym == abs_section_sym)
return 1;
if (symbol_get_tc (sym)->real_name != (char *) NULL)
S_SET_NAME (sym, symbol_get_tc (sym)->real_name);
else
{
const char *name;
const char *s;
name = S_GET_NAME (sym);
s = strchr (name, '[');
if (s != (char *) NULL)
{
unsigned int len;
char *snew;
len = s - name;
snew = xmalloc (len + 1);
memcpy (snew, name, len);
snew[len] = '\0';
S_SET_NAME (sym, snew);
}
}
if (set_end != (symbolS *) NULL)
{
SA_SET_SYM_ENDNDX (set_end, sym);
set_end = NULL;
}
if (SF_GET_FUNCTION (sym))
{
if (ppc_last_function != (symbolS *) NULL)
as_bad (_("two .function pseudo-ops with no intervening .ef"));
ppc_last_function = sym;
if (symbol_get_tc (sym)->size != (symbolS *) NULL)
{
resolve_symbol_value (symbol_get_tc (sym)->size);
SA_SET_SYM_FSIZE (sym,
(long) S_GET_VALUE (symbol_get_tc (sym)->size));
}
}
else if (S_GET_STORAGE_CLASS (sym) == C_FCN
&& strcmp (S_GET_NAME (sym), ".ef") == 0)
{
if (ppc_last_function == (symbolS *) NULL)
as_bad (_(".ef with no preceding .function"));
else
{
set_end = ppc_last_function;
ppc_last_function = NULL;
coff_last_function = NULL;
}
}
if (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
&& (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) == 0
&& S_GET_STORAGE_CLASS (sym) != C_FILE
&& S_GET_STORAGE_CLASS (sym) != C_FCN
&& S_GET_STORAGE_CLASS (sym) != C_BLOCK
&& S_GET_STORAGE_CLASS (sym) != C_BSTAT
&& S_GET_STORAGE_CLASS (sym) != C_ESTAT
&& S_GET_STORAGE_CLASS (sym) != C_BINCL
&& S_GET_STORAGE_CLASS (sym) != C_EINCL
&& S_GET_SEGMENT (sym) != ppc_coff_debug_section)
S_SET_STORAGE_CLASS (sym, C_HIDEXT);
if (S_GET_STORAGE_CLASS (sym) == C_EXT
|| S_GET_STORAGE_CLASS (sym) == C_HIDEXT)
{
int i;
union internal_auxent *a;
i = S_GET_NUMBER_AUXILIARY (sym);
S_SET_NUMBER_AUXILIARY (sym, i + 1);
a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].u.auxent;
if (symbol_get_tc (sym)->class == XMC_TC0)
{
know (strcmp (S_GET_NAME (sym), "TOC") == 0);
a->x_csect.x_scnlen.l = 0;
a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
}
else if (symbol_get_tc (sym)->subseg != 0)
{
if (symbol_get_tc (sym)->next == (symbolS *) NULL)
a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
S_GET_SEGMENT (sym))
- S_GET_VALUE (sym));
else
{
resolve_symbol_value (symbol_get_tc (sym)->next);
a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next)
- S_GET_VALUE (sym));
}
a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD;
}
else if (S_GET_SEGMENT (sym) == bss_section)
{
a->x_csect.x_scnlen.l = symbol_get_frag (sym)->fr_offset;
a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM;
if (S_IS_EXTERNAL (sym))
symbol_get_tc (sym)->class = XMC_RW;
else
symbol_get_tc (sym)->class = XMC_BS;
}
else if (S_GET_SEGMENT (sym) == absolute_section)
{
ppc_saw_abs = TRUE;
a->x_csect.x_smtyp = XTY_LD;
if (symbol_get_tc (sym)->class == -1)
symbol_get_tc (sym)->class = XMC_XO;
}
else if (! S_IS_DEFINED (sym))
{
a->x_csect.x_scnlen.l = 0;
a->x_csect.x_smtyp = XTY_ER;
}
else if (symbol_get_tc (sym)->class == XMC_TC)
{
symbolS *next;
next = symbol_next (sym);
while (symbol_get_tc (next)->class == XMC_TC0)
next = symbol_next (next);
if (next == (symbolS *) NULL
|| symbol_get_tc (next)->class != XMC_TC)
{
if (ppc_after_toc_frag == (fragS *) NULL)
a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
data_section)
- S_GET_VALUE (sym));
else
a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address
- S_GET_VALUE (sym));
}
else
{
resolve_symbol_value (next);
a->x_csect.x_scnlen.l = (S_GET_VALUE (next)
- S_GET_VALUE (sym));
}
a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
}
else
{
symbolS *csect;
if (S_GET_SEGMENT (sym) == text_section)
csect = ppc_text_csects;
else if (S_GET_SEGMENT (sym) == data_section)
csect = ppc_data_csects;
else
abort ();
csect = symbol_get_tc (csect)->next;
if (csect == (symbolS *) NULL)
{
as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym));
a->x_csect.x_scnlen.l = 0;
}
else
{
while (symbol_get_tc (csect)->next != (symbolS *) NULL)
{
resolve_symbol_value (symbol_get_tc (csect)->next);
if (S_GET_VALUE (symbol_get_tc (csect)->next)
> S_GET_VALUE (sym))
break;
csect = symbol_get_tc (csect)->next;
}
a->x_csect.x_scnlen.p =
coffsymbol (symbol_get_bfdsym (csect))->native;
coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].fix_scnlen =
1;
}
a->x_csect.x_smtyp = XTY_LD;
}
a->x_csect.x_parmhash = 0;
a->x_csect.x_snhash = 0;
if (symbol_get_tc (sym)->class == -1)
a->x_csect.x_smclas = XMC_PR;
else
a->x_csect.x_smclas = symbol_get_tc (sym)->class;
a->x_csect.x_stab = 0;
a->x_csect.x_snstab = 0;
symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END;
}
else if (S_GET_STORAGE_CLASS (sym) == C_BSTAT)
{
asymbol *bsym = symbol_get_bfdsym (symbol_get_tc (sym)->within);
combined_entry_type *c = coffsymbol (bsym)->native;
S_SET_VALUE (sym, (valueT) (size_t) c);
coffsymbol (symbol_get_bfdsym (sym))->native->fix_value = 1;
}
else if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
{
symbolS *block;
symbolS *csect;
block = symbol_get_tc (sym)->within;
csect = symbol_get_tc (block)->within;
resolve_symbol_value (csect);
S_SET_VALUE (sym, S_GET_VALUE (sym) - S_GET_VALUE (csect));
}
else if (S_GET_STORAGE_CLASS (sym) == C_BINCL
|| S_GET_STORAGE_CLASS (sym) == C_EINCL)
{
coffsymbol (symbol_get_bfdsym (sym))->native->fix_line = 1;
}
return 0;
}
void
ppc_adjust_symtab ()
{
symbolS *sym;
if (! ppc_saw_abs)
return;
for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
{
symbolS *csect;
int i;
union internal_auxent *a;
if (S_GET_SEGMENT (sym) != absolute_section)
continue;
csect = symbol_create (".abs[XO]", absolute_section,
S_GET_VALUE (sym), &zero_address_frag);
symbol_get_bfdsym (csect)->value = S_GET_VALUE (sym);
S_SET_STORAGE_CLASS (csect, C_HIDEXT);
i = S_GET_NUMBER_AUXILIARY (csect);
S_SET_NUMBER_AUXILIARY (csect, i + 1);
a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1].u.auxent;
a->x_csect.x_scnlen.l = 0;
a->x_csect.x_smtyp = XTY_SD;
a->x_csect.x_parmhash = 0;
a->x_csect.x_snhash = 0;
a->x_csect.x_smclas = XMC_XO;
a->x_csect.x_stab = 0;
a->x_csect.x_snstab = 0;
symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP);
i = S_GET_NUMBER_AUXILIARY (sym);
a = &coffsymbol (symbol_get_bfdsym (sym))->native[i].u.auxent;
a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native;
coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1;
}
ppc_saw_abs = FALSE;
}
void
ppc_frob_section (sec)
asection *sec;
{
static bfd_vma vma = 0;
vma = md_section_align (sec, vma);
bfd_set_section_vma (stdoutput, sec, vma);
vma += bfd_section_size (stdoutput, sec);
}
#endif
char *
md_atof (type, litp, sizep)
int type;
char *litp;
int *sizep;
{
int prec;
LITTLENUM_TYPE words[4];
char *t;
int i;
switch (type)
{
case 'f':
prec = 2;
break;
case 'd':
prec = 4;
break;
default:
*sizep = 0;
return _("bad call to md_atof");
}
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
*sizep = prec * 2;
if (target_big_endian)
{
for (i = 0; i < prec; i++)
{
md_number_to_chars (litp, (valueT) words[i], 2);
litp += 2;
}
}
else
{
for (i = prec - 1; i >= 0; i--)
{
md_number_to_chars (litp, (valueT) words[i], 2);
litp += 2;
}
}
return NULL;
}
void
md_number_to_chars (buf, val, n)
char *buf;
valueT val;
int n;
{
if (target_big_endian)
number_to_chars_bigendian (buf, val, n);
else
number_to_chars_littleendian (buf, val, n);
}
valueT
md_section_align (seg, addr)
asection *seg;
valueT addr;
{
int align = bfd_get_section_alignment (stdoutput, seg);
return ((addr + (1 << align) - 1) & (-1 << align));
}
int
md_estimate_size_before_relax (fragp, seg)
fragS *fragp ATTRIBUTE_UNUSED;
asection *seg ATTRIBUTE_UNUSED;
{
abort ();
return 0;
}
void
md_convert_frag (abfd, sec, fragp)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
fragS *fragp ATTRIBUTE_UNUSED;
{
abort ();
}
symbolS *
md_undefined_symbol (name)
char *name ATTRIBUTE_UNUSED;
{
return 0;
}
long
md_pcrel_from_section (fixp, sec)
fixS *fixp;
segT sec ATTRIBUTE_UNUSED;
{
return fixp->fx_frag->fr_address + fixp->fx_where;
}
#ifdef OBJ_XCOFF
int
ppc_fix_adjustable (fix)
fixS *fix;
{
valueT val = resolve_symbol_value (fix->fx_addsy);
segT symseg = S_GET_SEGMENT (fix->fx_addsy);
TC_SYMFIELD_TYPE *tc;
if (symseg == absolute_section)
return 0;
if (ppc_toc_csect != (symbolS *) NULL
&& fix->fx_addsy != ppc_toc_csect
&& symseg == data_section
&& val >= ppc_toc_frag->fr_address
&& (ppc_after_toc_frag == (fragS *) NULL
|| val < ppc_after_toc_frag->fr_address))
{
symbolS *sy;
for (sy = symbol_next (ppc_toc_csect);
sy != (symbolS *) NULL;
sy = symbol_next (sy))
{
TC_SYMFIELD_TYPE *sy_tc = symbol_get_tc (sy);
if (sy_tc->class == XMC_TC0)
continue;
if (sy_tc->class != XMC_TC)
break;
if (val == resolve_symbol_value (sy))
{
fix->fx_addsy = sy;
fix->fx_addnumber = val - ppc_toc_frag->fr_address;
return 0;
}
}
as_bad_where (fix->fx_file, fix->fx_line,
_("symbol in .toc does not match any .tc"));
}
tc = symbol_get_tc (fix->fx_addsy);
if (tc->subseg == 0
&& tc->class != XMC_TC0
&& tc->class != XMC_TC
&& symseg != bss_section
&& (symseg != data_section
|| ppc_toc_csect == NULL
|| val < ppc_toc_frag->fr_address
|| (ppc_after_toc_frag != NULL
&& val >= ppc_after_toc_frag->fr_address)))
{
symbolS *csect;
symbolS *next_csect;
if (symseg == text_section)
csect = ppc_text_csects;
else if (symseg == data_section)
csect = ppc_data_csects;
else
abort ();
csect = symbol_get_tc (csect)->next;
if (csect != (symbolS *) NULL)
{
while ((next_csect = symbol_get_tc (csect)->next) != (symbolS *) NULL
&& (symbol_get_frag (next_csect)->fr_address <= val))
{
if (symbol_get_frag (csect)->fr_address == val
&& S_GET_VALUE (csect) == val)
{
symbolS *scan;
for (scan = symbol_next (csect);
scan != NULL;
scan = symbol_next (scan))
{
if (symbol_get_tc (scan)->subseg != 0)
break;
if (scan == fix->fx_addsy)
break;
}
if (scan == fix->fx_addsy)
break;
}
csect = next_csect;
}
fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
fix->fx_addsy = csect;
}
return 0;
}
if (symseg == bss_section
&& ! S_IS_EXTERNAL (fix->fx_addsy))
{
symbolS *sy = symbol_get_frag (fix->fx_addsy)->fr_symbol;
fix->fx_offset += val - resolve_symbol_value (sy);
fix->fx_addsy = sy;
}
return 0;
}
int
ppc_force_relocation (fix)
fixS *fix;
{
if (fix->fx_pcrel
&& fix->fx_addsy != NULL
&& symbol_get_tc (fix->fx_addsy)->subseg != 0
&& ((symbol_get_frag (fix->fx_addsy)->fr_address
> fix->fx_frag->fr_address)
|| (symbol_get_tc (fix->fx_addsy)->next != NULL
&& (symbol_get_frag (symbol_get_tc (fix->fx_addsy)->next)->fr_address
<= fix->fx_frag->fr_address))))
return 1;
return generic_force_reloc (fix);
}
#endif
#ifdef OBJ_ELF
int
ppc_force_relocation (fix)
fixS *fix;
{
switch (fix->fx_r_type)
{
case BFD_RELOC_PPC_B16_BRTAKEN:
case BFD_RELOC_PPC_B16_BRNTAKEN:
case BFD_RELOC_PPC_BA16_BRTAKEN:
case BFD_RELOC_PPC_BA16_BRNTAKEN:
case BFD_RELOC_PPC64_TOC:
return 1;
default:
break;
}
if (fix->fx_r_type >= BFD_RELOC_PPC_TLS
&& fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA)
return 1;
return generic_force_reloc (fix);
}
int
ppc_fix_adjustable (fix)
fixS *fix;
{
return (fix->fx_r_type != BFD_RELOC_16_GOTOFF
&& fix->fx_r_type != BFD_RELOC_LO16_GOTOFF
&& fix->fx_r_type != BFD_RELOC_HI16_GOTOFF
&& fix->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
&& fix->fx_r_type != BFD_RELOC_GPREL16
&& fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT
&& fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY
&& !(fix->fx_r_type >= BFD_RELOC_PPC_TLS
&& fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA));
}
#endif
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT * valP;
segT seg ATTRIBUTE_UNUSED;
{
valueT value = * valP;
#ifdef OBJ_ELF
if (fixP->fx_addsy != NULL)
{
if (fixP->fx_pcrel)
value += fixP->fx_frag->fr_address + fixP->fx_where;
}
else
fixP->fx_done = 1;
#else
if (fixP->fx_addsy == (symbolS *) NULL)
fixP->fx_done = 1;
else if (fixP->fx_pcrel)
;
else
value = fixP->fx_offset;
#endif
if (fixP->fx_subsy != (symbolS *) NULL)
{
as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
}
if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
{
int opindex;
const struct powerpc_operand *operand;
char *where;
unsigned long insn;
opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
operand = &powerpc_operands[opindex];
#ifdef OBJ_XCOFF
if ((operand->flags & PPC_OPERAND_PARENS) != 0
&& operand->bits == 16
&& operand->shift == 0
&& (operand->insert == NULL || ppc_obj64)
&& fixP->fx_addsy != NULL
&& symbol_get_tc (fixP->fx_addsy)->subseg != 0
&& symbol_get_tc (fixP->fx_addsy)->class != XMC_TC
&& symbol_get_tc (fixP->fx_addsy)->class != XMC_TC0
&& S_GET_SEGMENT (fixP->fx_addsy) != bss_section)
{
value = fixP->fx_offset;
fixP->fx_done = 1;
}
#endif
where = fixP->fx_frag->fr_literal + fixP->fx_where;
if (target_big_endian)
insn = bfd_getb32 ((unsigned char *) where);
else
insn = bfd_getl32 ((unsigned char *) where);
insn = ppc_insert_operand (insn, operand, (offsetT) value,
fixP->fx_file, fixP->fx_line);
if (target_big_endian)
bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
else
bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
if (fixP->fx_done)
return;
assert (fixP->fx_addsy != NULL);
if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
&& operand->bits == 26
&& operand->shift == 0)
fixP->fx_r_type = BFD_RELOC_PPC_B26;
else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
&& operand->bits == 16
&& operand->shift == 0)
{
fixP->fx_r_type = BFD_RELOC_PPC_B16;
#ifdef OBJ_XCOFF
fixP->fx_size = 2;
if (target_big_endian)
fixP->fx_where += 2;
#endif
}
else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
&& operand->bits == 26
&& operand->shift == 0)
fixP->fx_r_type = BFD_RELOC_PPC_BA26;
else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
&& operand->bits == 16
&& operand->shift == 0)
{
fixP->fx_r_type = BFD_RELOC_PPC_BA16;
#ifdef OBJ_XCOFF
fixP->fx_size = 2;
if (target_big_endian)
fixP->fx_where += 2;
#endif
}
#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
else if ((operand->flags & PPC_OPERAND_PARENS) != 0
&& operand->bits == 16
&& operand->shift == 0)
{
if (ppc_is_toc_sym (fixP->fx_addsy))
{
fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
#ifdef OBJ_ELF
if (ppc_obj64
&& (operand->flags & PPC_OPERAND_DS) != 0)
fixP->fx_r_type = BFD_RELOC_PPC64_TOC16_DS;
#endif
}
else
{
fixP->fx_r_type = BFD_RELOC_16;
#ifdef OBJ_ELF
if (ppc_obj64
&& (operand->flags & PPC_OPERAND_DS) != 0)
fixP->fx_r_type = BFD_RELOC_PPC64_ADDR16_DS;
#endif
}
fixP->fx_size = 2;
if (target_big_endian)
fixP->fx_where += 2;
}
#endif
else
{
char *sfile;
unsigned int sline;
if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("unresolved expression that must be resolved"));
else
as_bad_where (fixP->fx_file, fixP->fx_line,
_("unsupported relocation against %s"),
S_GET_NAME (fixP->fx_addsy));
fixP->fx_done = 1;
return;
}
}
else
{
#ifdef OBJ_ELF
ppc_elf_validate_fix (fixP, seg);
#endif
switch (fixP->fx_r_type)
{
case BFD_RELOC_CTOR:
if (ppc_obj64)
goto ctor64;
case BFD_RELOC_32:
if (fixP->fx_pcrel)
fixP->fx_r_type = BFD_RELOC_32_PCREL;
case BFD_RELOC_RVA:
case BFD_RELOC_32_PCREL:
case BFD_RELOC_PPC_EMB_NADDR32:
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 4);
break;
case BFD_RELOC_64:
ctor64:
if (fixP->fx_pcrel)
fixP->fx_r_type = BFD_RELOC_64_PCREL;
case BFD_RELOC_64_PCREL:
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 8);
break;
case BFD_RELOC_LO16:
case BFD_RELOC_16:
case BFD_RELOC_GPREL16:
case BFD_RELOC_16_GOT_PCREL:
case BFD_RELOC_16_GOTOFF:
case BFD_RELOC_LO16_GOTOFF:
case BFD_RELOC_HI16_GOTOFF:
case BFD_RELOC_HI16_S_GOTOFF:
case BFD_RELOC_16_BASEREL:
case BFD_RELOC_LO16_BASEREL:
case BFD_RELOC_HI16_BASEREL:
case BFD_RELOC_HI16_S_BASEREL:
case BFD_RELOC_PPC_EMB_NADDR16:
case BFD_RELOC_PPC_EMB_NADDR16_LO:
case BFD_RELOC_PPC_EMB_NADDR16_HI:
case BFD_RELOC_PPC_EMB_NADDR16_HA:
case BFD_RELOC_PPC_EMB_SDAI16:
case BFD_RELOC_PPC_EMB_SDA2REL:
case BFD_RELOC_PPC_EMB_SDA2I16:
case BFD_RELOC_PPC_EMB_RELSEC16:
case BFD_RELOC_PPC_EMB_RELST_LO:
case BFD_RELOC_PPC_EMB_RELST_HI:
case BFD_RELOC_PPC_EMB_RELST_HA:
case BFD_RELOC_PPC_EMB_RELSDA:
case BFD_RELOC_PPC_TOC16:
#ifdef OBJ_ELF
case BFD_RELOC_PPC64_TOC16_LO:
case BFD_RELOC_PPC64_TOC16_HI:
case BFD_RELOC_PPC64_TOC16_HA:
#endif
if (fixP->fx_pcrel)
{
if (fixP->fx_addsy != NULL)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("cannot emit PC relative %s relocation against %s"),
bfd_get_reloc_code_name (fixP->fx_r_type),
S_GET_NAME (fixP->fx_addsy));
else
as_bad_where (fixP->fx_file, fixP->fx_line,
_("cannot emit PC relative %s relocation"),
bfd_get_reloc_code_name (fixP->fx_r_type));
}
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 2);
break;
case BFD_RELOC_HI16:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
PPC_HI (value), 2);
break;
case BFD_RELOC_HI16_S:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
PPC_HA (value), 2);
break;
#ifdef OBJ_ELF
case BFD_RELOC_PPC64_HIGHER:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
PPC_HIGHER (value), 2);
break;
case BFD_RELOC_PPC64_HIGHER_S:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
PPC_HIGHERA (value), 2);
break;
case BFD_RELOC_PPC64_HIGHEST:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
PPC_HIGHEST (value), 2);
break;
case BFD_RELOC_PPC64_HIGHEST_S:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
PPC_HIGHESTA (value), 2);
break;
case BFD_RELOC_PPC64_ADDR16_DS:
case BFD_RELOC_PPC64_ADDR16_LO_DS:
case BFD_RELOC_PPC64_GOT16_DS:
case BFD_RELOC_PPC64_GOT16_LO_DS:
case BFD_RELOC_PPC64_PLT16_LO_DS:
case BFD_RELOC_PPC64_SECTOFF_DS:
case BFD_RELOC_PPC64_SECTOFF_LO_DS:
case BFD_RELOC_PPC64_TOC16_DS:
case BFD_RELOC_PPC64_TOC16_LO_DS:
case BFD_RELOC_PPC64_PLTGOT16_DS:
case BFD_RELOC_PPC64_PLTGOT16_LO_DS:
if (fixP->fx_pcrel)
abort ();
{
char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
unsigned long val, mask;
if (target_big_endian)
val = bfd_getb32 (where - 2);
else
val = bfd_getl32 (where);
mask = 0xfffc;
if ((ppc_cpu & PPC_OPCODE_POWER4) != 0
&& (val & (0x3f << 26)) == (56u << 26))
mask = 0xfff0;
val |= value & mask;
if (target_big_endian)
bfd_putb16 ((bfd_vma) val, where);
else
bfd_putl16 ((bfd_vma) val, where);
}
break;
case BFD_RELOC_PPC_B16_BRTAKEN:
case BFD_RELOC_PPC_B16_BRNTAKEN:
case BFD_RELOC_PPC_BA16_BRTAKEN:
case BFD_RELOC_PPC_BA16_BRNTAKEN:
break;
case BFD_RELOC_PPC_TLS:
break;
case BFD_RELOC_PPC_DTPMOD:
case BFD_RELOC_PPC_TPREL16:
case BFD_RELOC_PPC_TPREL16_LO:
case BFD_RELOC_PPC_TPREL16_HI:
case BFD_RELOC_PPC_TPREL16_HA:
case BFD_RELOC_PPC_TPREL:
case BFD_RELOC_PPC_DTPREL16:
case BFD_RELOC_PPC_DTPREL16_LO:
case BFD_RELOC_PPC_DTPREL16_HI:
case BFD_RELOC_PPC_DTPREL16_HA:
case BFD_RELOC_PPC_DTPREL:
case BFD_RELOC_PPC_GOT_TLSGD16:
case BFD_RELOC_PPC_GOT_TLSGD16_LO:
case BFD_RELOC_PPC_GOT_TLSGD16_HI:
case BFD_RELOC_PPC_GOT_TLSGD16_HA:
case BFD_RELOC_PPC_GOT_TLSLD16:
case BFD_RELOC_PPC_GOT_TLSLD16_LO:
case BFD_RELOC_PPC_GOT_TLSLD16_HI:
case BFD_RELOC_PPC_GOT_TLSLD16_HA:
case BFD_RELOC_PPC_GOT_TPREL16:
case BFD_RELOC_PPC_GOT_TPREL16_LO:
case BFD_RELOC_PPC_GOT_TPREL16_HI:
case BFD_RELOC_PPC_GOT_TPREL16_HA:
case BFD_RELOC_PPC_GOT_DTPREL16:
case BFD_RELOC_PPC_GOT_DTPREL16_LO:
case BFD_RELOC_PPC_GOT_DTPREL16_HI:
case BFD_RELOC_PPC_GOT_DTPREL16_HA:
case BFD_RELOC_PPC64_TPREL16_DS:
case BFD_RELOC_PPC64_TPREL16_LO_DS:
case BFD_RELOC_PPC64_TPREL16_HIGHER:
case BFD_RELOC_PPC64_TPREL16_HIGHERA:
case BFD_RELOC_PPC64_TPREL16_HIGHEST:
case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
case BFD_RELOC_PPC64_DTPREL16_DS:
case BFD_RELOC_PPC64_DTPREL16_LO_DS:
case BFD_RELOC_PPC64_DTPREL16_HIGHER:
case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
S_SET_THREAD_LOCAL (fixP->fx_addsy);
break;
#endif
case BFD_RELOC_PPC_EMB_SDA21:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where
+ ((target_big_endian) ? 2 : 0),
value, 2);
break;
case BFD_RELOC_8:
if (fixP->fx_pcrel)
{
if (fixP->fx_addsy)
as_bad (_("Unable to handle reference to symbol %s"),
S_GET_NAME (fixP->fx_addsy));
else
as_bad (_("Unable to resolve expression"));
fixP->fx_done = 1;
}
else
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 1);
break;
case BFD_RELOC_24_PLT_PCREL:
case BFD_RELOC_PPC_LOCAL24PC:
if (!fixP->fx_pcrel && !fixP->fx_done)
abort ();
if (fixP->fx_done)
{
char *where;
unsigned long insn;
where = fixP->fx_frag->fr_literal + fixP->fx_where;
if (target_big_endian)
insn = bfd_getb32 ((unsigned char *) where);
else
insn = bfd_getl32 ((unsigned char *) where);
if ((value & 3) != 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("must branch to an address a multiple of 4"));
if ((offsetT) value < -0x40000000
|| (offsetT) value >= 0x40000000)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("@local or @plt branch destination is too far away, %ld bytes"),
(long) value);
insn = insn | (value & 0x03fffffc);
if (target_big_endian)
bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
else
bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
}
break;
case BFD_RELOC_VTABLE_INHERIT:
fixP->fx_done = 0;
if (fixP->fx_addsy
&& !S_IS_DEFINED (fixP->fx_addsy)
&& !S_IS_WEAK (fixP->fx_addsy))
S_SET_WEAK (fixP->fx_addsy);
break;
case BFD_RELOC_VTABLE_ENTRY:
fixP->fx_done = 0;
break;
#ifdef OBJ_ELF
case BFD_RELOC_PPC64_TOC:
fixP->fx_done = 0;
break;
#endif
default:
fprintf (stderr,
_("Gas failure, reloc value %d\n"), fixP->fx_r_type);
fflush (stderr);
abort ();
}
}
#ifdef OBJ_ELF
fixP->fx_addnumber = value;
#else
if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16)
fixP->fx_addnumber = 0;
else
{
#ifdef TE_PE
fixP->fx_addnumber = 0;
#else
fixP->fx_addnumber =
- bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy));
#endif
}
#endif
}
arelent *
tc_gen_reloc (seg, fixp)
asection *seg ATTRIBUTE_UNUSED;
fixS *fixp;
{
arelent *reloc;
reloc = (arelent *) xmalloc (sizeof (arelent));
reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
if (reloc->howto == (reloc_howto_type *) NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("reloc %d not supported by object file format"),
(int) fixp->fx_r_type);
return NULL;
}
reloc->addend = fixp->fx_addnumber;
return reloc;
}
void
ppc_cfi_frame_initial_instructions ()
{
cfi_add_CFA_def_cfa (1, 0);
}
int
tc_ppc_regname_to_dw2regnum (const char *regname)
{
unsigned int regnum = -1;
unsigned int i;
const char *p;
char *q;
static struct { char *name; int dw2regnum; } regnames[] =
{
{ "sp", 1 }, { "r.sp", 1 }, { "rtoc", 2 }, { "r.toc", 2 },
{ "mq", 64 }, { "lr", 65 }, { "ctr", 66 }, { "ap", 67 },
{ "cr", 70 }, { "xer", 76 }, { "vrsave", 109 }, { "vscr", 110 },
{ "spe_acc", 111 }, { "spefscr", 112 }
};
for (i = 0; i < ARRAY_SIZE (regnames); ++i)
if (strcmp (regnames[i].name, regname) == 0)
return regnames[i].dw2regnum;
if (regname[0] == 'r' || regname[0] == 'f' || regname[0] == 'v')
{
p = regname + 1 + (regname[1] == '.');
regnum = strtoul (p, &q, 10);
if (p == q || *q || regnum >= 32)
return -1;
if (regname[0] == 'f')
regnum += 32;
else if (regname[0] == 'v')
regnum += 77;
}
else if (regname[0] == 'c' && regname[1] == 'r')
{
p = regname + 2 + (regname[2] == '.');
if (p[0] < '0' || p[0] > '7' || p[1])
return -1;
regnum = p[0] - '0' + 68;
}
return regnum;
}