+replace_tls_insn (Elf_Internal_Rela *rel,
+ bfd *abfd,
+ asection *input_section,
+ bfd_byte *contents,
+ bfd_boolean is_ld_model,
+ char **error_message)
+{
+ static xtensa_insnbuf ibuff = NULL;
+ static xtensa_insnbuf sbuff = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt;
+ xtensa_opcode old_op, new_op;
+ bfd_size_type input_size;
+ int r_type;
+ unsigned dest_reg, src_reg;
+
+ if (ibuff == NULL)
+ {
+ ibuff = xtensa_insnbuf_alloc (isa);
+ sbuff = xtensa_insnbuf_alloc (isa);
+ }
+
+ input_size = bfd_get_section_limit (abfd, input_section);
+
+ /* Read the instruction into a buffer and decode the opcode. */
+ xtensa_insnbuf_from_chars (isa, ibuff, contents + rel->r_offset,
+ input_size - rel->r_offset);
+ fmt = xtensa_format_decode (isa, ibuff);
+ if (fmt == XTENSA_UNDEFINED)
+ {
+ *error_message = "cannot decode instruction format";
+ return FALSE;
+ }
+
+ BFD_ASSERT (xtensa_format_num_slots (isa, fmt) == 1);
+ xtensa_format_get_slot (isa, fmt, 0, ibuff, sbuff);
+
+ old_op = xtensa_opcode_decode (isa, fmt, 0, sbuff);
+ if (old_op == XTENSA_UNDEFINED)
+ {
+ *error_message = "cannot decode instruction opcode";
+ return FALSE;
+ }
+
+ r_type = ELF32_R_TYPE (rel->r_info);
+ switch (r_type)
+ {
+ case R_XTENSA_TLS_FUNC:
+ case R_XTENSA_TLS_ARG:
+ if (old_op != get_l32r_opcode ()
+ || xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
+ sbuff, &dest_reg) != 0)
+ {
+ *error_message = "cannot extract L32R destination for TLS access";
+ return FALSE;
+ }
+ break;
+
+ case R_XTENSA_TLS_CALL:
+ if (! get_indirect_call_dest_reg (old_op, &dest_reg)
+ || xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
+ sbuff, &src_reg) != 0)
+ {
+ *error_message = "cannot extract CALLXn operands for TLS access";
+ return FALSE;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (is_ld_model)
+ {
+ switch (r_type)
+ {
+ case R_XTENSA_TLS_FUNC:
+ case R_XTENSA_TLS_ARG:
+ /* Change the instruction to a NOP (or "OR a1, a1, a1" for older
+ versions of Xtensa). */
+ new_op = xtensa_opcode_lookup (isa, "nop");
+ if (new_op == XTENSA_UNDEFINED)
+ {
+ new_op = xtensa_opcode_lookup (isa, "or");
+ if (new_op == XTENSA_UNDEFINED
+ || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+ || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+ sbuff, 1) != 0
+ || xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
+ sbuff, 1) != 0
+ || xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
+ sbuff, 1) != 0)
+ {
+ *error_message = "cannot encode OR for TLS access";
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0)
+ {
+ *error_message = "cannot encode NOP for TLS access";
+ return FALSE;
+ }
+ }
+ break;
+
+ case R_XTENSA_TLS_CALL:
+ /* Read THREADPTR into the CALLX's return value register. */
+ new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
+ if (new_op == XTENSA_UNDEFINED
+ || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+ || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+ sbuff, dest_reg + 2) != 0)
+ {
+ *error_message = "cannot encode RUR.THREADPTR for TLS access";
+ return FALSE;
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (r_type)
+ {
+ case R_XTENSA_TLS_FUNC:
+ new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
+ if (new_op == XTENSA_UNDEFINED
+ || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+ || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+ sbuff, dest_reg) != 0)
+ {
+ *error_message = "cannot encode RUR.THREADPTR for TLS access";
+ return FALSE;
+ }
+ break;
+
+ case R_XTENSA_TLS_ARG:
+ /* Nothing to do. Keep the original L32R instruction. */
+ return TRUE;
+
+ case R_XTENSA_TLS_CALL:
+ /* Add the CALLX's src register (holding the THREADPTR value)
+ to the first argument register (holding the offset) and put
+ the result in the CALLX's return value register. */
+ new_op = xtensa_opcode_lookup (isa, "add");
+ if (new_op == XTENSA_UNDEFINED
+ || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+ || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+ sbuff, dest_reg + 2) != 0
+ || xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
+ sbuff, dest_reg + 2) != 0
+ || xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
+ sbuff, src_reg) != 0)
+ {
+ *error_message = "cannot encode ADD for TLS access";
+ return FALSE;
+ }
+ break;
+ }
+ }
+
+ xtensa_format_set_slot (isa, fmt, 0, ibuff, sbuff);
+ xtensa_insnbuf_to_chars (isa, ibuff, contents + rel->r_offset,
+ input_size - rel->r_offset);
+
+ return TRUE;
+}
+
+
+#define IS_XTENSA_TLS_RELOC(R_TYPE) \
+ ((R_TYPE) == R_XTENSA_TLSDESC_FN \
+ || (R_TYPE) == R_XTENSA_TLSDESC_ARG \
+ || (R_TYPE) == R_XTENSA_TLS_DTPOFF \
+ || (R_TYPE) == R_XTENSA_TLS_TPOFF \
+ || (R_TYPE) == R_XTENSA_TLS_FUNC \
+ || (R_TYPE) == R_XTENSA_TLS_ARG \
+ || (R_TYPE) == R_XTENSA_TLS_CALL)
+
+/* Relocate an Xtensa ELF section. This is invoked by the linker for
+ both relocatable and final links. */
+
+static bfd_boolean
+elf_xtensa_relocate_section (bfd *output_bfd,
+ struct bfd_link_info *info,
+ bfd *input_bfd,
+ asection *input_section,