FALSE, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MN10300_ALIGN, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ NULL, /* special handler. */
+ "R_MN10300_ALIGN", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
FALSE) /* pcrel_offset */
};
{ BFD_RELOC_MN10300_GLOB_DAT, R_MN10300_GLOB_DAT },
{ BFD_RELOC_MN10300_JMP_SLOT, R_MN10300_JMP_SLOT },
{ BFD_RELOC_MN10300_RELATIVE, R_MN10300_RELATIVE },
- { BFD_RELOC_MN10300_SYM_DIFF, R_MN10300_SYM_DIFF }
+ { BFD_RELOC_MN10300_SYM_DIFF, R_MN10300_SYM_DIFF },
+ { BFD_RELOC_MN10300_ALIGN, R_MN10300_ALIGN }
};
/* Create the GOT section. */
{
bfd_boolean sym_diff_reloc_seen;
Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Sym * isymbuf = NULL;
struct elf_link_hash_entry **sym_hashes;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection * sgot;
asection * srelgot;
asection * sreloc;
+ bfd_boolean result = FALSE;
sgot = NULL;
srelgot = NULL;
return TRUE;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
sym_hashes = elf_sym_hashes (abfd);
dynobj = elf_hash_table (info)->dynobj;
{
struct elf_link_hash_entry *h;
unsigned long r_symndx;
+ unsigned int r_type;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
+ r_type = ELF32_R_TYPE (rel->r_info);
+
/* Some relocs require a global offset table. */
if (dynobj == NULL)
{
- switch (ELF32_R_TYPE (rel->r_info))
+ switch (r_type)
{
case R_MN10300_GOT32:
case R_MN10300_GOT24:
case R_MN10300_GOTPC16:
elf_hash_table (info)->dynobj = dynobj = abfd;
if (! _bfd_mn10300_elf_create_got_section (dynobj, info))
- return FALSE;
+ goto fail;
break;
default:
}
}
- switch (ELF32_R_TYPE (rel->r_info))
+ switch (r_type)
{
/* This relocation describes the C++ object vtable hierarchy.
Reconstruct it for later use during GC. */
case R_MN10300_GNU_VTINHERIT:
if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
- return FALSE;
+ goto fail;
break;
/* This relocation describes which C++ vtable entries are actually
BFD_ASSERT (h != NULL);
if (h != NULL
&& !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
- return FALSE;
+ goto fail;
break;
+
case R_MN10300_GOT32:
case R_MN10300_GOT24:
case R_MN10300_GOT16:
| SEC_READONLY));
if (srelgot == NULL
|| ! bfd_set_section_alignment (dynobj, srelgot, 2))
- return FALSE;
+ goto fail;
}
}
if (h->dynindx == -1)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
+ goto fail;
}
srelgot->size += sizeof (Elf32_External_Rela);
local_got_offsets = bfd_alloc (abfd, size);
if (local_got_offsets == NULL)
- return FALSE;
+ goto fail;
+
elf_local_got_offsets (abfd) = local_got_offsets;
for (i = 0; i < symtab_hdr->sh_info; i++)
symbol involved in the relocation. */
if (h == NULL)
{
- Elf_Internal_Sym * isymbuf;
Elf_Internal_Sym * isym;
- isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
if (isymbuf == NULL)
isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
symtab_hdr->sh_info, 0,
section in dynobj and make room for this reloc. */
if (sreloc == NULL)
{
- const char * name;
-
- name = (bfd_elf_string_from_elf_section
- (abfd,
- elf_elfheader (abfd)->e_shstrndx,
- elf_section_data (sec)->rel_hdr.sh_name));
- if (name == NULL)
- return FALSE;
-
- BFD_ASSERT (CONST_STRNEQ (name, ".rela")
- && streq (bfd_get_section_name (abfd, sec), name + 5));
-
- sreloc = bfd_get_section_by_name (dynobj, name);
+ sreloc = _bfd_elf_make_dynamic_reloc_section
+ (sec, dynobj, 2, abfd, /*rela?*/ TRUE);
if (sreloc == NULL)
- {
- flagword flags;
-
- flags = (SEC_HAS_CONTENTS | SEC_READONLY
- | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- if ((sec->flags & SEC_ALLOC) != 0)
- flags |= SEC_ALLOC | SEC_LOAD;
- sreloc = bfd_make_section_with_flags (dynobj, name, flags);
- if (sreloc == NULL
- || ! bfd_set_section_alignment (dynobj, sreloc, 2))
- return FALSE;
- }
+ goto fail;
}
sreloc->size += sizeof (Elf32_External_Rela);
sym_diff_reloc_seen = FALSE;
}
- return TRUE;
+ result = TRUE;
+ fail:
+ if (isymbuf != NULL)
+ free (isymbuf);
+
+ return result;
}
/* Return the section that should be marked against GC for a given
case R_MN10300_16:
case R_MN10300_8:
value -= sym_diff_value;
+ /* If we are computing a 32-bit value for the location lists
+ and the result is 0 then we add one to the value. A zero
+ value can result because of linker relaxation deleteing
+ prologue instructions and using a value of 1 (for the begin
+ and end offsets in the location list entry) results in a
+ nul entry which does not prevent the following entries from
+ being parsed. */
+ if (r_type == R_MN10300_32
+ && value == 0
+ && strcmp (input_section->name, ".debug_loc") == 0)
+ value = 1;
sym_diff_section = NULL;
is_sym_diff_reloc = TRUE;
break;
sym_diff_value = value;
return bfd_reloc_ok;
+ case R_MN10300_ALIGN:
case R_MN10300_NONE:
return bfd_reloc_ok;
time. */
if (sreloc == NULL)
{
- const char * name;
-
- name = (bfd_elf_string_from_elf_section
- (input_bfd,
- elf_elfheader (input_bfd)->e_shstrndx,
- elf_section_data (input_section)->rel_hdr.sh_name));
- if (name == NULL)
+ sreloc = _bfd_elf_get_dynamic_reloc_section
+ (input_bfd, input_section, /*rela?*/ TRUE);
+ if (sreloc == NULL)
return FALSE;
-
- BFD_ASSERT (CONST_STRNEQ (name, ".rela")
- && streq (bfd_get_section_name (input_bfd,
- input_section),
- name + 5));
-
- sreloc = bfd_get_section_by_name (dynobj, name);
- BFD_ASSERT (sreloc != NULL);
}
skip = FALSE;
value -= offset;
value += addend;
- if ((long) value > 0xff || (long) value < -0x100)
+ if ((long) value > 0x7f || (long) value < -0x80)
return bfd_reloc_overflow;
bfd_put_8 (input_bfd, value, hit_data);
value -= offset;
value += addend;
- if ((long) value > 0xffff || (long) value < -0x10000)
+ if ((long) value > 0x7fff || (long) value < -0x8000)
return bfd_reloc_overflow;
bfd_put_16 (input_bfd, value, hit_data);
value -= offset;
value += addend;
- if ((long) value > 0xffff || (long) value < -0x10000)
+ if ((long) value > 0x7fff || (long) value < -0x8000)
return bfd_reloc_overflow;
bfd_put_16 (input_bfd, value, hit_data);
".got")->output_section->vma;
value += addend;
- if ((long) value > 0xffff || (long) value < -0x10000)
+ if ((long) value > 0x7fff || (long) value < -0x8000)
return bfd_reloc_overflow;
bfd_put_16 (input_bfd, value, hit_data);
value -= offset;
value += addend;
- if ((long) value > 0xffff || (long) value < -0x10000)
+ if ((long) value > 0x7fff || (long) value < -0x8000)
return bfd_reloc_overflow;
bfd_put_16 (input_bfd, value, hit_data);
}
else if (r_type == R_MN10300_GOT16)
{
- if ((long) value > 0xffff || (long) value < -0x10000)
+ if ((long) value > 0x7fff || (long) value < -0x8000)
return bfd_reloc_overflow;
bfd_put_16 (input_bfd, value, hit_data);
contents = elf_section_data (sec)->this_hdr.contents;
- /* The deletion must stop at the next ALIGN reloc for an aligment
- power larger than the number of bytes we are deleting. */
-
irelalign = NULL;
toaddr = sec->size;
irel = elf_section_data (sec)->relocs;
irelend = irel + sec->reloc_count;
+ if (sec->reloc_count > 0)
+ {
+ /* If there is an align reloc at the end of the section ignore it.
+ GAS creates these relocs for reasons of its own, and they just
+ serve to keep the section artifically inflated. */
+ if (ELF32_R_TYPE ((irelend - 1)->r_info) == (int) R_MN10300_ALIGN)
+ --irelend;
+
+ /* The deletion must stop at the next ALIGN reloc for an aligment
+ power larger than, or not a multiple of, the number of bytes we
+ are deleting. */
+ for (; irel < irelend; irel++)
+ {
+ int alignment = 1 << irel->r_addend;
+
+ if (ELF32_R_TYPE (irel->r_info) == (int) R_MN10300_ALIGN
+ && irel->r_offset > addr
+ && irel->r_offset < toaddr
+ && (count < alignment
+ || alignment % count != 0))
+ {
+ irelalign = irel;
+ toaddr = irel->r_offset;
+ break;
+ }
+ }
+ }
+
/* Actually delete the bytes. */
memmove (contents + addr, contents + addr + count,
(size_t) (toaddr - addr - count));
- sec->size -= count;
+
+ /* Adjust the section's size if we are shrinking it, or else
+ pad the bytes between the end of the shrunken region and
+ the start of the next region with NOP codes. */
+ if (irelalign == NULL)
+ {
+ sec->size -= count;
+ /* Include symbols at the end of the section, but
+ not at the end of a sub-region of the section. */
+ toaddr ++;
+ }
+ else
+ {
+ int i;
+
+#define NOP_OPCODE 0xcb
+
+ for (i = 0; i < count; i ++)
+ bfd_put_8 (abfd, (bfd_vma) NOP_OPCODE, contents + toaddr - count + i);
+ }
/* Adjust all the relocs. */
for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++)
{
/* Get the new reloc address. */
if ((irel->r_offset > addr
- && irel->r_offset < toaddr))
+ && irel->r_offset < toaddr)
+ || (ELF32_R_TYPE (irel->r_info) == (int) R_MN10300_ALIGN
+ && irel->r_offset == toaddr))
irel->r_offset -= count;
}
- /* Adjust the local symbols defined in this section. */
+ /* Adjust the local symbols in the section, reducing their value
+ by the number of bytes deleted. Note - symbols within the deleted
+ region are moved to the address of the start of the region, which
+ actually means that they will address the byte beyond the end of
+ the region once the deletion has been completed. */
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
isym = (Elf_Internal_Sym *) symtab_hdr->contents;
for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++)
{
if (isym->st_shndx == sec_shndx
&& isym->st_value > addr
- && isym->st_value <= toaddr)
- isym->st_value -= count;
+ && isym->st_value < toaddr)
+ {
+ if (isym->st_value < addr + count)
+ isym->st_value = addr;
+ else
+ isym->st_value -= count;
+ }
/* Adjust the function symbol's size as well. */
else if (isym->st_shndx == sec_shndx
&& ELF_ST_TYPE (isym->st_info) == STT_FUNC
&& isym->st_value + isym->st_size > addr
- && isym->st_value + isym->st_size <= toaddr)
+ && isym->st_value + isym->st_size < toaddr)
isym->st_size -= count;
}
|| sym_hash->root.type == bfd_link_hash_defweak)
&& sym_hash->root.u.def.section == sec
&& sym_hash->root.u.def.value > addr
- && sym_hash->root.u.def.value <= toaddr)
- sym_hash->root.u.def.value -= count;
+ && sym_hash->root.u.def.value < toaddr)
+ {
+ if (sym_hash->root.u.def.value < addr + count)
+ sym_hash->root.u.def.value = addr;
+ else
+ sym_hash->root.u.def.value -= count;
+ }
/* Adjust the function symbol's size as well. */
else if (sym_hash->root.type == bfd_link_hash_defined
&& sym_hash->root.u.def.section == sec
&& sym_hash->type == STT_FUNC
&& sym_hash->root.u.def.value + sym_hash->size > addr
- && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
+ && sym_hash->root.u.def.value + sym_hash->size < toaddr)
sym_hash->size -= count;
}
+ /* See if we can move the ALIGN reloc forward.
+ We have adjusted r_offset for it already. */
+ if (irelalign != NULL)
+ {
+ bfd_vma alignto, alignaddr;
+
+ if ((int) irelalign->r_addend > 0)
+ {
+ /* This is the old address. */
+ alignto = BFD_ALIGN (toaddr, 1 << irelalign->r_addend);
+ /* This is where the align points to now. */
+ alignaddr = BFD_ALIGN (irelalign->r_offset,
+ 1 << irelalign->r_addend);
+ if (alignaddr < alignto)
+ /* Tail recursion. */
+ return mn10300_elf_relax_delete_bytes (abfd, sec, alignaddr,
+ (int) (alignto - alignaddr));
+ }
+ }
+
return TRUE;
}
Elf_Internal_Sym *isymbuf = NULL;
struct elf32_mn10300_link_hash_table *hash_table;
asection *section = sec;
+ bfd_vma align_gap_adjustment;
/* Assume nothing changes. */
*again = FALSE;
if (internal_relocs == NULL)
goto error_return;
+ /* Scan for worst case alignment gap changes. Note that this logic
+ is not ideal; what we should do is run this scan for every
+ opcode/address range and adjust accordingly, but that's
+ expensive. Worst case is that for an alignment of N bytes, we
+ move by 2*N-N-1 bytes, assuming we have aligns of 1, 2, 4, 8, etc
+ all before it. Plus, this still doesn't cover cross-section
+ jumps with section alignment. */
+ irelend = internal_relocs + sec->reloc_count;
+ align_gap_adjustment = 0;
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ if (ELF32_R_TYPE (irel->r_info) == (int) R_MN10300_ALIGN)
+ {
+ bfd_vma adj = 1 << irel->r_addend;
+ bfd_vma aend = irel->r_offset;
+
+ aend = BFD_ALIGN (aend, 1 << irel->r_addend);
+ adj = 2 * adj - adj - 1;
+
+ /* Record the biggest adjustmnet. Skip any alignment at the
+ end of our section. */
+ if (align_gap_adjustment < adj
+ && aend < sec->output_section->vma + sec->output_offset + sec->size)
+ align_gap_adjustment = adj;
+ }
+ }
+
/* Walk through them looking for relaxing opportunities. */
irelend = internal_relocs + sec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++)
{
bfd_vma symval;
+ bfd_signed_vma jump_offset;
+ asection *sym_sec = NULL;
struct elf32_mn10300_link_hash_entry *h = NULL;
/* If this isn't something that can be relaxed, then ignore
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
- asection *sym_sec = NULL;
const char *sym_name;
char *new_name;
&& ELF_ST_TYPE (isym->st_info) == STT_SECTION
&& sym_sec->sec_info_type == ELF_INFO_TYPE_MERGE)
{
- bfd_vma saved_addend;
-
- saved_addend = irel->r_addend;
- symval = _bfd_elf_rela_local_sym (abfd, isym, & sym_sec, irel);
- symval += irel->r_addend;
- irel->r_addend = saved_addend;
+ symval = isym->st_value + irel->r_addend;
+ symval = _bfd_merged_section_offset (abfd, & sym_sec,
+ elf_section_data (sym_sec)->sec_info,
+ symval);
+ symval += sym_sec->output_section->vma + sym_sec->output_offset - irel->r_addend;
}
else
symval = (isym->st_value
if (h->root.root.u.def.section->output_section == NULL)
continue;
+ sym_sec = h->root.root.u.def.section->output_section;
+
symval = (h->root.root.u.def.value
+ h->root.root.u.def.section->output_section->vma
+ h->root.root.u.def.section->output_offset);
/* See if the value will fit in 16 bits, note the high value is
0x7fff + 2 as the target will be two bytes closer if we are
- able to relax. */
- if ((long) value < 0x8001 && (long) value > -0x8000)
+ able to relax, if it's in the same section. */
+ if (sec->output_section == sym_sec->output_section)
+ jump_offset = 0x8001;
+ else
+ jump_offset = 0x7fff;
+
+ /* Account for jumps across alignment boundaries using
+ align_gap_adjustment. */
+ if ((bfd_signed_vma) value < jump_offset - (bfd_signed_vma) align_gap_adjustment
+ && ((bfd_signed_vma) value > -0x8000 + (bfd_signed_vma) align_gap_adjustment))
{
unsigned char code;