PowerPC non-PIC to PIC editing for protected var access

This is a linker-only solution to the incompatibility between shared
library protected visibility variables and using .dynbss and copy
relocs for non-PIC access to shared library variables.

bfd/
	* elf32-ppc.c (struct ppc_elf_link_hash_entry): Add has_addr16_ha
	and has_addr16_lo.  Make has_sda_refs a bitfield.
	(ppc_elf_check_relocs): Set new flags.
	(ppc_elf_link_hash_table_create): Update default_params.
	(ppc_elf_adjust_dynamic_symbol): Clear protected_def in cases
	where we won't be making .dynbss entries or editing code.  Set
	params->pic_fixup when we'll edit code for protected var access.
	(allocate_dynrelocs): Allocate got entry for edited code and
	discard dyn_relocs.
	(struct ppc_elf_relax_info): Add picfixup_size.
	(ppc_elf_relax_section): Rename struct one_fixup to struct
	one_branch_fixup.  Rename fixups to branch_fixups.  Size space for
	pic fixups.
	(ppc_elf_relocate_section): Edit non-PIC accessing protected
	visibility variables to PIC.  Don't emit dyn_relocs for code
	we've edited.
	* elf32-ppc.h (struct ppc_elf_params): Add pic_fixup.
ld/
	* emultempl/ppc32elf.em: Handle --no-pic-fixup.
	(params): Init new field.
	(ppc_before_allocation): Enable relaxation for pic_fixup.
This commit is contained in:
Alan Modra 2015-04-01 09:59:46 +10:30
parent d9b67d9f41
commit d3e454b956
5 changed files with 257 additions and 41 deletions

View File

@ -1,3 +1,23 @@
2015-04-07 Alan Modra <amodra@gmail.com>
* elf32-ppc.c (struct ppc_elf_link_hash_entry): Add has_addr16_ha
and has_addr16_lo. Make has_sda_refs a bitfield.
(ppc_elf_check_relocs): Set new flags.
(ppc_elf_link_hash_table_create): Update default_params.
(ppc_elf_adjust_dynamic_symbol): Clear protected_def in cases
where we won't be making .dynbss entries or editing code. Set
params->pic_fixup when we'll edit code for protected var access.
(allocate_dynrelocs): Allocate got entry for edited code and
discard dyn_relocs.
(struct ppc_elf_relax_info): Add picfixup_size.
(ppc_elf_relax_section): Rename struct one_fixup to struct
one_branch_fixup. Rename fixups to branch_fixups. Size space for
pic fixups.
(ppc_elf_relocate_section): Edit non-PIC accessing protected
visibility variables to PIC. Don't emit dyn_relocs for code
we've edited.
* elf32-ppc.h (struct ppc_elf_params): Add pic_fixup.
2015-04-07 Nick Clifton <nickc@redhat.com>
* elf.c (_bfd_elf_is_local_label_name): Treat assembler generated

View File

@ -3135,7 +3135,11 @@ struct ppc_elf_link_hash_entry
/* Nonzero if we have seen a small data relocation referring to this
symbol. */
unsigned char has_sda_refs;
unsigned char has_sda_refs : 1;
/* Flag use of given relocations. */
unsigned char has_addr16_ha : 1;
unsigned char has_addr16_lo : 1;
};
#define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
@ -3259,7 +3263,7 @@ static struct bfd_link_hash_table *
ppc_elf_link_hash_table_create (bfd *abfd)
{
struct ppc_elf_link_hash_table *ret;
static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 12 };
static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 12, 0 };
ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
if (ret == NULL)
@ -4376,6 +4380,10 @@ ppc_elf_check_relocs (bfd *abfd,
/* We may need a copy reloc too. */
h->non_got_ref = 1;
h->pointer_equality_needed = 1;
if (r_type == R_PPC_ADDR16_HA)
ppc_elf_hash_entry (h)->has_addr16_ha = 1;
if (r_type == R_PPC_ADDR16_LO)
ppc_elf_hash_entry (h)->has_addr16_lo = 1;
}
goto dodyn;
@ -5554,6 +5562,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
&& !readonly_dynrelocs (h))
h->non_got_ref = 0;
}
h->protected_def = 0;
return TRUE;
}
else
@ -5581,12 +5590,34 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
For such cases we need not do anything here; the relocations will
be handled correctly by relocate_section. */
if (info->shared)
return TRUE;
{
h->protected_def = 0;
return TRUE;
}
/* If there are no references to this symbol that do not use the
GOT, we don't need to generate a copy reloc. */
if (!h->non_got_ref)
return TRUE;
{
h->protected_def = 0;
return TRUE;
}
/* Protected variables do not work with .dynbss. The copy in
.dynbss won't be used by the shared library with the protected
definition for the variable. Editing to PIC, or text relocations
are preferable to an incorrect program. */
if (h->protected_def)
{
if (ELIMINATE_COPY_RELOCS
&& ppc_elf_hash_entry (h)->has_addr16_ha
&& ppc_elf_hash_entry (h)->has_addr16_lo
&& htab->params->pic_fixup == 0
&& info->disable_target_specific_optimizations <= 1)
htab->params->pic_fixup = 1;
h->non_got_ref = 0;
return TRUE;
}
/* If -z nocopyreloc was given, we won't generate them either. */
if (info->nocopyreloc)
@ -5611,16 +5642,6 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
return TRUE;
}
/* Protected variables do not work with .dynbss. The copy in
.dynbss won't be used by the shared library with the protected
definition for the variable. Text relocations are preferable
to an incorrect program. */
if (h->protected_def)
{
h->non_got_ref = 0;
return TRUE;
}
/* We must allocate the symbol in our .dynbss section, which will
become part of the .bss section of the executable. There will be
an entry for this symbol in the .dynsym section. The dynamic
@ -5929,7 +5950,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
}
eh = (struct ppc_elf_link_hash_entry *) h;
if (eh->elf.got.refcount > 0)
if (eh->elf.got.refcount > 0
|| (ELIMINATE_COPY_RELOCS
&& !eh->elf.def_regular
&& eh->elf.protected_def
&& eh->has_addr16_ha
&& eh->has_addr16_lo
&& htab->params->pic_fixup > 0))
{
bfd_boolean dyn;
unsigned int need;
@ -6072,7 +6099,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
dynamic. */
if (!h->non_got_ref
&& !h->def_regular)
&& !h->def_regular
&& !(h->protected_def
&& eh->has_addr16_ha
&& eh->has_addr16_lo
&& htab->params->pic_fixup > 0))
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
@ -6713,6 +6744,7 @@ static const int stub_entry[] =
struct ppc_elf_relax_info
{
unsigned int workaround_size;
unsigned int picfixup_size;
};
/* This function implements long branch trampolines, and the ppc476
@ -6726,9 +6758,9 @@ ppc_elf_relax_section (bfd *abfd,
struct bfd_link_info *link_info,
bfd_boolean *again)
{
struct one_fixup
struct one_branch_fixup
{
struct one_fixup *next;
struct one_branch_fixup *next;
asection *tsec;
/* Final link, can use the symbol offset. For a
relocatable link we use the symbol's index. */
@ -6741,12 +6773,12 @@ ppc_elf_relax_section (bfd *abfd,
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Rela *irel, *irelend = NULL;
struct one_fixup *fixups = NULL;
struct one_branch_fixup *branch_fixups = NULL;
struct ppc_elf_relax_info *relax_info = NULL;
unsigned changes = 0;
bfd_boolean workaround_change;
struct ppc_elf_link_hash_table *htab;
bfd_size_type trampbase, trampoff, newsize;
bfd_size_type trampbase, trampoff, newsize, picfixup_size;
asection *got2;
bfd_boolean maybe_pasted;
@ -6778,7 +6810,8 @@ ppc_elf_relax_section (bfd *abfd,
|| isec->sec_info_type == SEC_INFO_TYPE_TARGET);
isec->sec_info_type = SEC_INFO_TYPE_TARGET;
if (htab->params->ppc476_workaround)
if (htab->params->ppc476_workaround
|| htab->params->pic_fixup > 0)
{
if (elf_section_data (isec)->sec_info == NULL)
{
@ -6799,8 +6832,9 @@ ppc_elf_relax_section (bfd *abfd,
trampoff += 4;
symtab_hdr = &elf_symtab_hdr (abfd);
if (htab->params->branch_trampolines)
picfixup_size = 0;
if (htab->params->branch_trampolines
|| htab->params->pic_fixup > 0)
{
/* Get a copy of the native relocations. */
if (isec->reloc_count != 0)
@ -6819,9 +6853,9 @@ ppc_elf_relax_section (bfd *abfd,
unsigned long r_type = ELF32_R_TYPE (irel->r_info);
bfd_vma toff, roff;
asection *tsec;
struct one_fixup *f;
struct one_branch_fixup *f;
size_t insn_offset = 0;
bfd_vma max_branch_offset, val;
bfd_vma max_branch_offset = 0, val;
bfd_byte *hit_addr;
unsigned long t0;
struct elf_link_hash_entry *h;
@ -6842,6 +6876,11 @@ ppc_elf_relax_section (bfd *abfd,
max_branch_offset = 1 << 15;
break;
case R_PPC_ADDR16_HA:
if (htab->params->pic_fixup > 0)
break;
continue;
default:
continue;
}
@ -6963,6 +7002,17 @@ ppc_elf_relax_section (bfd *abfd,
sym_type = h->type;
}
if (r_type == R_PPC_ADDR16_HA)
{
if (h != NULL
&& !h->def_regular
&& h->protected_def
&& ppc_elf_hash_entry (h)->has_addr16_ha
&& ppc_elf_hash_entry (h)->has_addr16_lo)
picfixup_size += 12;
continue;
}
/* The condition here under which we call find_plt_ent must
match that in relocate_section. If we call find_plt_ent here
but not in relocate_section, or vice versa, then the branch
@ -7080,7 +7130,7 @@ ppc_elf_relax_section (bfd *abfd,
}
/* Look for an existing fixup to this address. */
for (f = fixups; f ; f = f->next)
for (f = branch_fixups; f ; f = f->next)
if (f->tsec == tsec && f->toff == toff)
break;
@ -7125,11 +7175,11 @@ ppc_elf_relax_section (bfd *abfd,
/* Record the fixup so we don't do it again this section. */
f = bfd_malloc (sizeof (*f));
f->next = fixups;
f->next = branch_fixups;
f->tsec = tsec;
f->toff = toff;
f->trampoff = trampoff;
fixups = f;
branch_fixups = f;
trampoff += size;
changes++;
@ -7179,10 +7229,10 @@ ppc_elf_relax_section (bfd *abfd,
}
}
while (fixups != NULL)
while (branch_fixups != NULL)
{
struct one_fixup *f = fixups;
fixups = fixups->next;
struct one_branch_fixup *f = branch_fixups;
branch_fixups = branch_fixups->next;
free (f);
}
}
@ -7219,7 +7269,15 @@ ppc_elf_relax_section (bfd *abfd,
newsize = trampoff + relax_info->workaround_size;
}
if (changes || workaround_change)
if (htab->params->pic_fixup > 0)
{
picfixup_size -= relax_info->picfixup_size;
if (picfixup_size != 0)
relax_info->picfixup_size += picfixup_size;
newsize += relax_info->picfixup_size;
}
if (changes != 0 || picfixup_size != 0 || workaround_change)
isec->size = newsize;
if (isymbuf != NULL
@ -7246,6 +7304,7 @@ ppc_elf_relax_section (bfd *abfd,
}
}
changes += picfixup_size;
if (changes != 0)
{
/* Append sufficient NOP relocs so we can write out relocation
@ -7280,10 +7339,10 @@ ppc_elf_relax_section (bfd *abfd,
return TRUE;
error_return:
while (fixups != NULL)
while (branch_fixups != NULL)
{
struct one_fixup *f = fixups;
fixups = fixups->next;
struct one_branch_fixup *f = branch_fixups;
branch_fixups = branch_fixups->next;
free (f);
}
if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
@ -7599,6 +7658,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
bfd_boolean ret = TRUE;
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
bfd_boolean is_vxworks_tls;
unsigned int picfixup_size = 0;
struct ppc_elf_relax_info *relax_info = NULL;
#ifdef DEBUG
_bfd_error_handler ("ppc_elf_relocate_section called for %B section %A, "
@ -7623,6 +7684,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
is_vxworks_tls = (htab->is_vxworks && info->shared
&& !strcmp (input_section->output_section->name,
".tls_vars"));
if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET)
relax_info = elf_section_data (input_section)->sec_info;
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
@ -7980,6 +8043,114 @@ ppc_elf_relocate_section (bfd *output_bfd,
}
}
if (ELIMINATE_COPY_RELOCS
&& h != NULL
&& !h->def_regular
&& h->protected_def
&& ppc_elf_hash_entry (h)->has_addr16_ha
&& ppc_elf_hash_entry (h)->has_addr16_lo
&& htab->params->pic_fixup > 0)
{
/* Convert lis;addi or lis;load/store accessing a protected
variable defined in a shared library to PIC. */
unsigned int insn;
if (r_type == R_PPC_ADDR16_HA)
{
insn = bfd_get_32 (output_bfd,
contents + rel->r_offset - d_offset);
if ((insn & (0x3f << 26)) == (15u << 26)
&& (insn & (0x1f << 16)) == 0 /* lis */)
{
bfd_byte *p;
bfd_vma off;
bfd_vma got_addr;
p = (contents + input_section->size
- relax_info->workaround_size
- relax_info->picfixup_size
+ picfixup_size);
off = (p - contents) - (rel->r_offset - d_offset);
if (off > 0x1fffffc || (off & 3) != 0)
info->callbacks->einfo
(_("%P: %H: fixup branch overflow\n"),
input_bfd, input_section, rel->r_offset);
bfd_put_32 (output_bfd, B | off,
contents + rel->r_offset - d_offset);
got_addr = (htab->got->output_section->vma
+ htab->got->output_offset
+ (h->got.offset & ~1));
rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_HA);
rel->r_addend = got_addr;
rel->r_offset = (p - contents) + d_offset;
insn &= ~0xffff;
insn |= ((unsigned int )(got_addr + 0x8000) >> 16) & 0xffff;
bfd_put_32 (output_bfd, insn, p);
/* Convert lis to lwz, loading address from GOT. */
insn &= ~0xffff;
insn ^= (32u ^ 15u) << 26;
insn |= (insn & (0x1f << 21)) >> 5;
insn |= got_addr & 0xffff;
bfd_put_32 (output_bfd, insn, p + 4);
bfd_put_32 (output_bfd, B | ((-4 - off) & 0x3ffffff), p + 8);
picfixup_size += 12;
/* Use one of the spare relocs, so --emit-relocs
output is reasonable. */
memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
rel++;
rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_LO);
rel->r_offset += 4;
/* Continue on as if we had a got reloc, to output
dynamic reloc. */
r_type = R_PPC_GOT16_LO;
}
else
info->callbacks->einfo
(_("%P: %H: error: %s with unexpected instruction %x\n"),
input_bfd, input_section, rel->r_offset,
"R_PPC_ADDR16_HA", insn);
}
else if (r_type == R_PPC_ADDR16_LO)
{
insn = bfd_get_32 (output_bfd,
contents + rel->r_offset - d_offset);
if ((insn & (0x3f << 26)) == 14u << 26 /* addi */
|| (insn & (0x3f << 26)) == 32u << 26 /* lwz */
|| (insn & (0x3f << 26)) == 34u << 26 /* lbz */
|| (insn & (0x3f << 26)) == 36u << 26 /* stw */
|| (insn & (0x3f << 26)) == 38u << 26 /* stb */
|| (insn & (0x3f << 26)) == 40u << 26 /* lhz */
|| (insn & (0x3f << 26)) == 42u << 26 /* lha */
|| (insn & (0x3f << 26)) == 44u << 26 /* sth */
|| (insn & (0x3f << 26)) == 46u << 26 /* lmw */
|| (insn & (0x3f << 26)) == 47u << 26 /* stmw */
|| (insn & (0x3f << 26)) == 48u << 26 /* lfs */
|| (insn & (0x3f << 26)) == 50u << 26 /* lfd */
|| (insn & (0x3f << 26)) == 52u << 26 /* stfs */
|| (insn & (0x3f << 26)) == 54u << 26 /* stfd */
|| ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
&& (insn & 3) != 1)
|| ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
&& ((insn & 3) == 0 || (insn & 3) == 3)))
{
/* Arrange to apply the reloc addend, if any. */
relocation = 0;
unresolved_reloc = FALSE;
rel->r_info = ELF32_R_INFO (0, r_type);
}
else
info->callbacks->einfo
(_("%P: %H: error: %s with unexpected instruction %x\n"),
input_bfd, input_section, rel->r_offset,
"R_PPC_ADDR16_LO", insn);
}
}
ifunc = NULL;
if (!htab->is_vxworks)
{
@ -8324,6 +8495,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
}
}
/* If here for a picfixup, we're done. */
if (r_type != ELF32_R_TYPE (rel->r_info))
continue;
relocation = (htab->got->output_section->vma
+ htab->got->output_offset
+ off
@ -8471,7 +8646,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
&& h != NULL
&& h->dynindx != -1
&& !h->non_got_ref
&& !h->def_regular))
&& !h->def_regular
&& !(h->protected_def
&& ppc_elf_hash_entry (h)->has_addr16_ha
&& ppc_elf_hash_entry (h)->has_addr16_lo
&& htab->params->pic_fixup > 0)))
{
int skip;
bfd_byte *loc;
@ -9296,11 +9475,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
|| (input_section->output_section->alignment_power
>= htab->params->pagesize_p2)))
{
struct ppc_elf_relax_info *relax_info;
bfd_vma start_addr, end_addr, addr;
bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
relax_info = elf_section_data (input_section)->sec_info;
if (relax_info->workaround_size != 0)
{
bfd_byte *p;

View File

@ -44,6 +44,10 @@ struct ppc_elf_params
/* Avoid execution falling into new page. */
int ppc476_workaround;
unsigned int pagesize_p2;
/* The bfd backend detected a non-PIC reference to a protected symbol
defined in a shared library. */
int pic_fixup;
};
void ppc_elf_link_params (struct bfd_link_info *, struct ppc_elf_params *);

View File

@ -1,3 +1,9 @@
2015-04-07 Alan Modra <amodra@gmail.com>
* emultempl/ppc32elf.em: Handle --no-pic-fixup.
(params): Init new field.
(ppc_before_allocation): Enable relaxation for pic_fixup.
2015-04-02 Andrew Turner <andrew@freebsd.org>
* Makefile.am: Add FreeBSD aarch64 files.

View File

@ -41,7 +41,7 @@ static int old_got = 0;
static bfd_vma pagesize = 0;
static struct ppc_elf_params params = { PLT_UNSET, -1, 0, 0, 0, 0 };
static struct ppc_elf_params params = { PLT_UNSET, -1, 0, 0, 0, 0, 0 };
static void
ppc_after_open_output (void)
@ -172,7 +172,9 @@ ppc_before_allocation (void)
params.branch_trampolines = 1;
}
if (params.ppc476_workaround || params.branch_trampolines)
if (params.branch_trampolines
|| params.ppc476_workaround
|| params.pic_fixup > 0)
ENABLE_RELAXATION;
}
@ -249,6 +251,7 @@ PARSE_AND_LIST_PROLOGUE=${PARSE_AND_LIST_PROLOGUE}'
#define OPTION_NO_STUBSYMS (OPTION_STUBSYMS + 1)
#define OPTION_PPC476_WORKAROUND (OPTION_NO_STUBSYMS + 1)
#define OPTION_NO_PPC476_WORKAROUND (OPTION_PPC476_WORKAROUND + 1)
#define OPTION_NO_PICFIXUP (OPTION_NO_PPC476_WORKAROUND + 1)
'
PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
@ -265,6 +268,7 @@ fi
PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
{ "ppc476-workaround", optional_argument, NULL, OPTION_PPC476_WORKAROUND },
{ "no-ppc476-workaround", no_argument, NULL, OPTION_NO_PPC476_WORKAROUND },
{ "no-pic-fixup", no_argument, NULL, OPTION_NO_PICFIXUP },
'
PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
@ -282,7 +286,8 @@ fi
PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'\
--ppc476-workaround [=pagesize]\n\
Avoid a cache bug on ppc476.\n\
--no-ppc476-workaround Disable workaround.\n"
--no-ppc476-workaround Disable workaround.\n\
--no-pic-fixup Don'\''t edit non-pic to pic.\n"
));
'
@ -336,6 +341,10 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
case OPTION_NO_PPC476_WORKAROUND:
params.ppc476_workaround = 0;
break;
case OPTION_NO_PICFIXUP:
params.pic_fixup = -1;
break;
'
# Put these extra ppc32elf routines in ld_${EMULATION_NAME}_emulation