+/* Special handling for RL78 complex relocs. Returns the
+ value of the reloc, or 0 for relocs which do not generate
+ a result. SYMVAL is the value of the symbol for relocs
+ which use a symbolic argument. */
+
+static bfd_vma
+rl78_compute_complex_reloc (unsigned long r_type,
+ bfd_vma symval,
+ asection * input_section)
+{
+ int32_t tmp1, tmp2;
+ bfd_vma relocation;
+
+ switch (r_type)
+ {
+ default:
+ return 0;
+
+ case R_RL78_ABS24S_PCREL:
+ case R_RL78_ABS16S_PCREL:
+ case R_RL78_ABS8S_PCREL:
+ RL78_STACK_POP (relocation);
+ relocation -= input_section->output_section->vma + input_section->output_offset;
+ return relocation;
+
+ case R_RL78_ABS32:
+ case R_RL78_ABS32_REV:
+ case R_RL78_ABS16:
+ case R_RL78_ABS16_REV:
+ case R_RL78_ABS16S:
+ case R_RL78_ABS16U:
+ case R_RL78_ABS8:
+ case R_RL78_ABS8U:
+ case R_RL78_ABS8S:
+ RL78_STACK_POP (relocation);
+ return relocation;
+
+ case R_RL78_ABS16UL:
+ case R_RL78_ABS8UL:
+ RL78_STACK_POP (relocation);
+ return relocation >> 2;
+
+ case R_RL78_ABS16UW:
+ case R_RL78_ABS8UW:
+ RL78_STACK_POP (relocation);
+ return relocation >> 1;
+
+ /* The rest of the relocs compute values and then push them onto the stack. */
+ case R_RL78_OPramtop:
+ case R_RL78_OPromtop:
+ case R_RL78_SYM:
+ RL78_STACK_PUSH (symval);
+ return 0;
+
+ case R_RL78_OPneg:
+ RL78_STACK_POP (tmp1);
+ tmp1 = - tmp1;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPadd:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 += tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPsub:
+ /* For the expression "A - B", the assembler pushes A,
+ then B, then OPSUB. So the first op we pop is B, not A. */
+ RL78_STACK_POP (tmp2); /* B */
+ RL78_STACK_POP (tmp1); /* A */
+ tmp1 -= tmp2; /* A - B */
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPmul:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 *= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPdiv:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 /= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPshla:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 <<= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPshra:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 >>= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPsctsize:
+ RL78_STACK_PUSH (input_section->size);
+ return 0;
+
+ case R_RL78_OPscttop:
+ RL78_STACK_PUSH (input_section->output_section->vma);
+ return 0;
+
+ case R_RL78_OPand:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 &= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPor:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 |= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPxor:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 ^= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPnot:
+ RL78_STACK_POP (tmp1);
+ tmp1 = ~ tmp1;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+
+ case R_RL78_OPmod:
+ RL78_STACK_POP (tmp2);
+ RL78_STACK_POP (tmp1);
+ tmp1 %= tmp2;
+ RL78_STACK_PUSH (tmp1);
+ return 0;
+ }
+}
+
+#undef RL78_STACK_PUSH
+#undef RL78_STACK_POP
+
+#define OP(i) (contents[reloc->address + (i)])
+
+static bfd_reloc_status_type
+rl78_special_reloc (bfd * input_bfd,
+ arelent * reloc,
+ asymbol * symbol,
+ void * data,
+ asection * input_section,
+ bfd * output_bfd ATTRIBUTE_UNUSED,
+ char ** error_message ATTRIBUTE_UNUSED)
+{
+ bfd_reloc_status_type r = bfd_reloc_ok;
+ bfd_vma relocation = 0;
+ unsigned long r_type = reloc->howto->type;
+ bfd_byte * contents = data;
+
+ /* If necessary, compute the symbolic value of the relocation. */
+ switch (r_type)
+ {
+ case R_RL78_SYM:
+ relocation = (symbol->value
+ + symbol->section->output_section->vma
+ + symbol->section->output_offset
+ + reloc->addend);
+ break;
+
+ case R_RL78_OPromtop:
+ relocation = get_romstart (NULL, input_bfd, input_section,
+ reloc->address);
+ break;
+
+ case R_RL78_OPramtop:
+ relocation = get_ramstart (NULL, input_bfd, input_section,
+ reloc->address);
+ break;
+ }
+
+ /* Get the value of the relocation. */
+ relocation = rl78_compute_complex_reloc (r_type, relocation, input_section);
+
+ /* If the relocation alters the contents of the section then apply it now.
+ Note - since this function is called from
+ bfd_generic_get_relocated_section_contents via bfd_perform_relocation,
+ and not from the linker, we do not perform any range checking. The
+ clients who are calling us are only interested in some relocated section
+ contents, and not any linkage problems that might occur later. */
+ switch (r_type)
+ {
+ case R_RL78_ABS32:
+ OP (0) = relocation;
+ OP (1) = relocation >> 8;
+ OP (2) = relocation >> 16;
+ OP (3) = relocation >> 24;
+ break;
+
+ case R_RL78_ABS32_REV:
+ OP (3) = relocation;
+ OP (2) = relocation >> 8;
+ OP (1) = relocation >> 16;
+ OP (0) = relocation >> 24;
+ break;
+
+ case R_RL78_ABS24S_PCREL:
+ case R_RL78_ABS24S:
+ OP (0) = relocation;
+ OP (1) = relocation >> 8;
+ OP (2) = relocation >> 16;
+ break;
+
+ case R_RL78_ABS16_REV:
+ OP (1) = relocation;
+ OP (0) = relocation >> 8;
+ break;
+
+ case R_RL78_ABS16S_PCREL:
+ case R_RL78_ABS16:
+ case R_RL78_ABS16S:
+ case R_RL78_ABS16U:
+ case R_RL78_ABS16UL:
+ case R_RL78_ABS16UW:
+ OP (0) = relocation;
+ OP (1) = relocation >> 8;
+ break;
+
+ case R_RL78_ABS8S_PCREL:
+ case R_RL78_ABS8:
+ case R_RL78_ABS8U:
+ case R_RL78_ABS8UL:
+ case R_RL78_ABS8UW:
+ case R_RL78_ABS8S:
+ OP (0) = relocation;
+ break;
+
+ default:
+ break;
+ }
+
+ return r;
+}
+
+#undef OP
+#define OP(i) (contents[rel->r_offset + (i)])
+