📄 tc-ppc.c
字号:
&& 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_32_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 && 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), ".stab") != 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; }}#endif /* OBJ_ELF */#ifdef TE_PE/* * Summary of parse_toc_entry(). * * in: Input_line_pointer points to the '[' in one of: * * [toc] [tocv] [toc32] [toc64] * * Anything else is an error of one kind or another. * * out: * return value: success or failure * toc_kind: kind of toc reference * input_line_pointer: * success: first char after the ']' * failure: unchanged * * settings: * * [toc] - rv == success, toc_kind = default_toc * [tocv] - rv == success, toc_kind = data_in_toc * [toc32] - rv == success, toc_kind = must_be_32 * [toc64] - rv == success, toc_kind = must_be_64 * */enum toc_size_qualifier{ default_toc, /* The toc cell constructed should be the system default size */ data_in_toc, /* This is a direct reference to a toc cell */ must_be_32, /* The toc cell constructed must be 32 bits wide */ must_be_64 /* The toc cell constructed must be 64 bits wide */};static intparse_toc_entry(toc_kind) enum toc_size_qualifier *toc_kind;{ char *start; char *toc_spec; char c; enum toc_size_qualifier t; /* save the input_line_pointer */ start = input_line_pointer; /* skip over the '[' , and whitespace */ ++input_line_pointer; SKIP_WHITESPACE (); /* find the spelling of the operand */ 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; /* put back the delimiting char */ input_line_pointer = start; /* reset input_line pointer */ return 0; } /* now find the ']' */ *input_line_pointer = c; /* put back the delimiting char */ SKIP_WHITESPACE (); /* leading whitespace could be there. */ c = *input_line_pointer++; /* input_line_pointer->past char in c. */ if (c != ']') { as_bad (_("syntax error: expected `]', found `%c'"), c); input_line_pointer = start; /* reset input_line pointer */ return 0; } *toc_kind = t; /* set return value */ return 1;}#endif/* We need to keep a list of fixups. We can't simply generate them as we go, because that would require us to first create the frag, and that would screw up references to ``.''. */struct ppc_fixup{ expressionS exp; int opindex; bfd_reloc_code_real_type reloc;};#define MAX_INSN_FIXUPS (5)/* This routine is called for each instruction to be assembled. */voidmd_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 i;#ifdef OBJ_ELF bfd_reloc_code_real_type reloc;#endif /* Get the opcode. */ for (s = str; *s != '\0' && ! isspace (*s); s++) ; if (*s != '\0') *s++ = '\0'; /* Look up the opcode in the hash table. */ 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; /* PowerPC operands are just expressions. The only real issue is that a few operand types are optional. All cases which might use an optional operand separate the operands only with commas (in some cases parentheses are used, as in ``lwz 1,0(1)'' but such cases never have optional operands). There is never more than one optional operand for an instruction. So, before we start seriously parsing the operands, we check to see if we have an optional operand, and, if we do, we count the number of commas to see whether the operand should be omitted. */ 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; /* There is an optional operand. Count the number of commas in the input line. */ if (*str == '\0') opcount = 0; else { opcount = 1; s = str; while ((s = strchr (s, ',')) != (char *) NULL) { ++opcount; ++s; } } /* If there are fewer operands in the line then are called for by the instruction, we want to skip the optional operand. */ if (opcount < strlen (opcode->operands)) skip_optional = 1; break; } } /* Gather the operands. */ 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 this is a fake operand, then we do not expect anything from the input. */ if ((operand->flags & PPC_OPERAND_FAKE) != 0) { insn = (*operand->insert) (insn, 0L, &errmsg); if (errmsg != (const char *) NULL) as_bad (errmsg); continue; } /* If this is an optional operand, and we are skipping it, just insert a zero. */ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 && skip_optional) { if (operand->insert) { insn = (*operand->insert) (insn, 0L, &errmsg); if (errmsg != (const char *) NULL) as_bad (errmsg); } if ((operand->flags & PPC_OPERAND_NEXT) != 0) next_opindex = *opindex_ptr + 1; continue; } /* Gather the operand. */ hold = input_line_pointer; input_line_pointer = str;#ifdef TE_PE if (*input_line_pointer == '[') { /* We are expecting something like the second argument here: lwz r4,[toc].GS.0.static_int(rtoc) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The argument following the `]' must be a symbol name, and the register must be the toc register: 'rtoc' or '2' The effect is to 0 as the displacement field in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or the appropriate variation) reloc against it based on the symbol. The linker will build the toc, and insert the resolved toc offset. Note: o The size of the toc entry is currently assumed to be 32 bits. This should not be assumed to be a hard coded number. o In an effort to cope with a change from 32 to 64 bits, there are also toc entries that are specified to be either 32 or 64 bits: lwz r4,[toc32].GS.0.static_int(rtoc) lwz r4,[toc64].GS.0.static_int(rtoc) These demand toc entries of the specified size, and the instruction probably requires it. */ int valid_toc; enum toc_size_qualifier toc_kind; bfd_reloc_code_real_type toc_reloc; /* go parse off the [tocXX] part */ valid_toc = parse_toc_entry(&toc_kind); if (!valid_toc) { /* Note: message has already been issued. */ /* FIXME: what sort of recovery should we do? */ /* demand_rest_of_line(); return; ? */ } /* Now get the symbol following the ']' */ expression(&ex); switch (toc_kind) { case default_toc: /* In this case, we may not have seen the symbol yet, since */ /* it is allowed to appear on a .extern or .globl or just be */ /* a label in the .data section. */ toc_reloc = BFD_RELOC_PPC_TOC16; break; case data_in_toc: /* 1. The symbol must be defined and either in the toc */ /* section, or a global. */ /* 2. The reloc generated must have the TOCDEFN flag set in */ /* upper bit mess of the reloc type. */ /* FIXME: It's a little confusing what the tocv qualifier can */ /* be used for. At the very least, I've seen three */ /* uses, only one of which I'm sure I can explain. */ 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: /* FIXME: these next two specifically specify 32/64 bit toc */ /* entries. We don't support them today. Is this the */ /* right way to say that? */ toc_reloc = BFD_RELOC_UNUSED; as_bad (_("Unimplemented toc32 expression modifier")); break; case must_be_64: /* FIXME: see above */ 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; } /* We need to generate a fixup for this expression. */ 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; /* Ok. We've set up the fixup for the instruction. Now make it look like the constant 0 was found here */ 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 /* TE_PE */ { 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 /* Allow @HA, @L, @H on constants. */ 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: /* X_unsigned is the default, so if the user has done something which cleared it, we always produce a signed value. */ if (ex.X_unsigned && (operand->flags & PPC_OPERAND_SIGNED) == 0) ex.X_add_number &= 0xffff; else ex.X_add_number = (((ex.X_add_number & 0xffff) ^ 0x8000) - 0x8000); break; case BFD_RELOC_HI16: ex.X_add_number = (ex.X_add_number >> 16) & 0xffff; break; case BFD_RELOC_HI16_S: ex.X_add_number = ((((ex.X_add_number >> 16) & 0xffff) + ((ex.X_add_number >> 15) & 1)) & 0xffff); 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) { /* For the absoulte forms of branchs, convert the PC relative form back into the absolute. */ 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; } } /* We need to generate a fixup for this expression. */ if (fc >= MAX_INSN_FIXUPS) as_fatal (_("too many fixups")); fixups[fc].exp = ex; fixups[fc].opindex = 0; fixups[fc].reloc = reloc; ++fc; }#endif /* OBJ_ELF */ else { /* We need to generate a fixup for this expression. */ 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 = ','; /* The call to expression should have advanced str past any
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -