@ -1532,32 +1532,32 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
FALSE ) , /* pcrel_offset */
/* Phony relocs to handle branch stubs. */
HOWTO ( R_PPC_RELAX32 , /* type */
0 , /* rightshift */
0 , /* size */
HOWTO ( R_PPC_RELAX32 , /* type */
0 , /* rightshift */
0 , /* size */
0 , /* bitsize */
FALSE , /* pc_relative */
0 , /* bitpos */
complain_overflow_dont , /* complain_on_overflow */
bfd_elf_generic_reloc , /* special_function */
" R_PPC_RELAX32 " , /* name */
" R_PPC_RELAX32 " , /* name */
FALSE , /* partial_inplace */
0 , /* src_mask */
0 , /* dst_mask */
0 , /* dst_mask */
FALSE ) , /* pcrel_offset */
HOWTO ( R_PPC_RELAX32PC , /* type */
0 , /* rightshift */
0 , /* size */
HOWTO ( R_PPC_RELAX32PC , /* type */
0 , /* rightshift */
0 , /* size */
0 , /* bitsize */
FALSE , /* pc_relative */
0 , /* bitpos */
complain_overflow_dont , /* complain_on_overflow */
bfd_elf_generic_reloc , /* special_function */
" R_PPC_RELAX32PC " , /* name */
" R_PPC_RELAX32PC " , /* name */
FALSE , /* partial_inplace */
0 , /* src_mask */
0 , /* dst_mask */
0 , /* dst_mask */
FALSE ) , /* pcrel_offset */
/* GNU extension to record C++ vtable hierarchy. */
@ -1625,84 +1625,26 @@ ppc_elf_howto_init (void)
}
}
static bfd_reloc_status_type
ppc_elf_install_value ( bfd * abfd ,
bfd_byte * hit_addr ,
bfd_vma v ,
unsigned int r_type )
{
bfd_vma t0 , t1 ;
# ifdef BFD_HOST_U_64_BIT
BFD_HOST_U_64_BIT val = ( BFD_HOST_U_64_BIT ) v ;
# else
bfd_vma val = v ;
# endif
switch ( r_type )
{
case R_PPC_RELAX32 :
case R_PPC_RELAX32PC :
t0 = bfd_get_32 ( abfd , hit_addr ) ;
t1 = bfd_get_32 ( abfd , hit_addr + 4 ) ;
/* We're clearing the bits for R_PPC_ADDR16_HA
and R_PPC_ADDR16_LO here . */
t0 & = ~ 0xffff ;
t1 & = ~ 0xffff ;
/* t0 is HA, t1 is lo */
t0 | = ( ( val + 0x8000 ) > > 16 ) & 0xffff ;
t1 | = val & 0xffff ;
bfd_put_32 ( abfd , t0 , hit_addr ) ;
bfd_put_32 ( abfd , t1 , hit_addr + 4 ) ;
break ;
# define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
case R_PPC_REL24 :
case R_PPC_LOCAL24PC :
case R_PPC_PLTREL24 :
t0 = bfd_get_32 ( abfd , hit_addr ) ;
t0 & = ~ 0x3fffffc ;
t0 | = val & 0x3fffffc ;
bfd_put_32 ( abfd , t0 , hit_addr ) ;
break ;
case R_PPC_REL14 :
case R_PPC_REL14_BRTAKEN :
case R_PPC_REL14_BRNTAKEN :
t0 = bfd_get_32 ( abfd , hit_addr ) ;
t0 & = ~ 0xfffc ;
t0 | = val & 0xfffc ;
bfd_put_32 ( abfd , t0 , hit_addr ) ;
break ;
default :
return bfd_reloc_notsupported ;
}
return bfd_reloc_ok ;
}
static const bfd_byte shared_stub_entry [ ] =
static const int shared_stub_entry [ ] =
{
0x48 , 0x00 , 0x00 , 0x24 , /* b .+36 */
0x7c , 0x08 , 0x02 , 0xa6 , /* mflr 0 */
0x42 , 0x9f , 0x00 , 0x05 , /* bcl 20, 31, .Lxxx */
0x7d , 0x68 , 0x02 , 0xa6 , /* mflr 11 */
0x3d , 0x6b , 0x00 , 0x00 , /* addis 11, 11, (xxx-.Lxxx)@ha */
0x39 , 0x6b , 0x00 , 0x18 , /* addi 11, 11, (xxx-.Lxxx)@l */
0x7c , 0x08 , 0x03 , 0xa6 , /* mtlr 0 */
0x7d , 0x69 , 0x03 , 0xa6 , /* mtctr 11 */
0x4e , 0x80 , 0x04 , 0x20 , /* bctr */
0x7c0802a6 , /* mflr 0 */
0x429f0005 , /* bcl 20, 31, .Lxxx */
0x7d6802a6 , /* mflr 11 */
0x3d6b0000 , /* addis 11, 11, (xxx-.Lxxx)@ha */
0x396b0018 , /* addi 11, 11, (xxx-.Lxxx)@l */
0x7c0803a6 , /* mtlr 0 */
0x7d6903a6 , /* mtctr 11 */
0x4e800420 , /* bctr */
} ;
static const bfd_byte stub_entry [ ] =
static const int stub_entry [ ] =
{
0x48 , 0x00 , 0x00 , 0x14 , /* b .+20 */
0x3d , 0x60 , 0x00 , 0x00 , /* lis 11,xxx@ha */
0x39 , 0x6b , 0x00 , 0x00 , /* addi 11,11,xxx@l */
0x7d , 0x69 , 0x03 , 0xa6 , /* mtctr 11 */
0x4e , 0x80 , 0x04 , 0x20 , /* bctr */
0x3d600000 , /* lis 11,xxx@ha */
0x396b0000 , /* addi 11,11,xxx@l */
0x7d6903a6 , /* mtctr 11 */
0x4e800420 , /* bctr */
} ;
@ -1726,9 +1668,9 @@ ppc_elf_relax_section (bfd *abfd,
Elf_Internal_Rela * internal_relocs = NULL ;
Elf_Internal_Rela * irel , * irelend ;
struct one_fixup * fixups = NULL ;
bfd_boolean changed_contents = FALSE ;
bfd_boolean changed_relocs = FALSE ;
bfd_boolean changed ;
struct ppc_elf_link_hash_table * ppc_info ;
bfd_size_type trampoff ;
* again = FALSE ;
@ -1743,6 +1685,10 @@ ppc_elf_relax_section (bfd *abfd,
if ( isec - > _cooked_size = = 0 )
isec - > _cooked_size = isec - > _raw_size ;
trampoff = ( isec - > _cooked_size + 3 ) & ( bfd_vma ) - 4 ;
/* Space for a branch around any trampolines. */
trampoff + = 4 ;
symtab_hdr = & elf_tdata ( abfd ) - > symtab_hdr ;
/* Get a copy of the native relocations. */
@ -1772,21 +1718,26 @@ ppc_elf_relax_section (bfd *abfd,
for ( irel = internal_relocs ; irel < irelend ; irel + + )
{
unsigned long r_type = ELF32_R_TYPE ( irel - > r_info ) ;
bfd_vma symaddr , reladdr , trampoff , t off , roff ;
bfd_vma symaddr , reladdr , toff , roff ;
asection * tsec ;
bfd_size_type amt ;
struct one_fixup * f ;
size_t insn_offset = 0 ;
bfd_vma max_branch_offset ;
bfd_vma max_branch_offset , val ;
bfd_byte * hit_addr ;
unsigned long t0 ;
switch ( r_type )
{
case R_PPC_REL24 :
case R_PPC_LOCAL24PC :
case R_PPC_PLTREL24 :
max_branch_offset = 1 < < 25 ;
break ;
case R_PPC_REL14 :
case R_PPC_REL14_BRTAKEN :
case R_PPC_REL14_BRNTAKEN :
case R_PPC_PLTREL24 :
max_branch_offset = 1 < < 15 ;
break ;
default :
@ -1824,7 +1775,7 @@ ppc_elf_relax_section (bfd *abfd,
}
else
{
/* Need dynamic symbol handling. */
/* Global symbol handling. */
unsigned long indx ;
struct elf_link_hash_entry * h ;
@ -1835,62 +1786,34 @@ ppc_elf_relax_section (bfd *abfd,
| | h - > root . type = = bfd_link_hash_warning )
h = ( struct elf_link_hash_entry * ) h - > root . u . i . link ;
if ( r_type = = R_PPC_PLTREL24 )
if ( r_type = = R_PPC_PLTREL24
& & ppc_info - > plt ! = NULL
& & h - > plt . offset ! = ( bfd_vma ) - 1 )
{
Elf_Internal_Sym * isym ;
if ( h - > plt . offset = = ( bfd_vma ) - 1
| | ppc_info - > plt = = NULL )
{
/* Read this BFD's local symbols. */
if ( isymbuf = = NULL )
{
isymbuf = ( Elf_Internal_Sym * ) symtab_hdr - > contents ;
if ( isymbuf = = NULL )
isymbuf = bfd_elf_get_elf_syms ( abfd , symtab_hdr ,
symtab_hdr - > sh_info , 0 ,
NULL , NULL , NULL ) ;
if ( isymbuf = = 0 )
goto error_return ;
}
isym = isymbuf + ELF32_R_SYM ( irel - > r_info ) ;
if ( isym - > st_shndx = = SHN_UNDEF )
/* We can't do anthing with undefined symbols. */
continue ;
else if ( isym - > st_shndx = = SHN_ABS )
tsec = bfd_abs_section_ptr ;
else if ( isym - > st_shndx = = SHN_COMMON )
tsec = bfd_com_section_ptr ;
else
tsec = h - > root . u . def . section ;
toff = h - > root . u . def . value ;
}
else
{
tsec = ppc_info - > plt ;
toff = h - > plt . offset ;
}
tsec = ppc_info - > plt ;
toff = h - > plt . offset ;
}
else if ( h - > root . type = = bfd_link_hash_undefined
| | h - > root . type = = bfd_link_hash_undefweak )
continue ;
else
else if ( h - > root . type = = bfd_link_hash_defined
| | h - > root . type = = bfd_link_hash_defweak )
{
tsec = h - > root . u . def . section ;
toff = h - > root . u . def . value ;
}
else
continue ;
}
/* If the branch and target are in the same section, you have
no hope of adding stubs . We ' ll error out later should the
branch overflow . */
if ( tsec = = isec )
continue ;
toff + = irel - > r_addend ;
if ( tsec - > sec_info_type = = ELF_INFO_TYPE_MERGE )
toff = _bfd_merged_section_offset ( abfd , & tsec ,
elf_section_data ( tsec ) - > sec_info ,
toff + irel - > r_addend , 0 ) ;
else
toff + = irel - > r_addend ;
toff , 0 ) ;
symaddr = tsec - > output_section - > vma + tsec - > output_offset + toff ;
@ -1898,70 +1821,41 @@ ppc_elf_relax_section (bfd *abfd,
reladdr = ( isec - > output_section - > vma
+ isec - > output_offset
+ roff ) & ( bfd_vma ) - 4 ;
+ roff ) ;
/* If the branch is in range, no need to do anything. */
max_branch_offset = 1 < < 25 ;
if ( r_type ! = R_PPC_REL24
& & r_type ! = R_PPC_LOCAL24PC
& & r_type ! = R_PPC_PLTREL24 )
max_branch_offset = 1 < < 15 ;
if ( ( bfd_vma ) ( symaddr - reladdr ) + max_branch_offset
< = 2 * max_branch_offset )
if ( symaddr - reladdr + max_branch_offset < 2 * max_branch_offset )
continue ;
/* If the branch and target are in the same section, you have
no hope . We ' ll error out later . */
if ( tsec = = isec )
continue ;
/* Look for an existing fixup to this address.
? ? ? What if the existing fixup is for a non - pic stub , and the
new one requires a pic stub ? This presumably could happen with
a static link and a mix of R_PPC_LOCAL24PC and R_PPC_REL24
relocs to a symbol needing long branch stubs .
? ? ? Why do we require R_PPC_LOCAL24PC and branches to the plt
to have a shared branch stub ? Shared branch stubs should only
be needed when info - > shared . */
/* Look for an existing fixup to this address. */
for ( f = fixups ; f ; f = f - > next )
if ( f - > tsec = = tsec & & f - > toff = = toff )
break ;
if ( f = = NULL )
{
const bfd_byte * stub ;
size_t size ;
unsigned long stub_rtype ;
if ( link_info - > shared
| | tsec = = ppc_info - > plt
| | r_type = = R_PPC_LOCAL24PC )
val = trampoff - roff ;
if ( val > = max_branch_offset )
/* Oh dear, we can't reach a trampoline. Don't try to add
one . We ' ll report an error later . */
continue ;
if ( link_info - > shared )
{
stub = shared_stub_entry ;
size = sizeof ( shared_stub_entry ) ;
insn_offset = 16 ;
size = 4 * ARRAY_SIZE ( shared_stub_entry ) ;
insn_offset = 12 ;
stub_rtype = R_PPC_RELAX32PC ;
}
else
{
stub = stub_entry ;
size = sizeof ( stub_entry ) ;
insn_offset = 4 ;
size = 4 * ARRAY_SIZE ( stub_entry ) ;
insn_offset = 0 ;
stub_rtype = R_PPC_RELAX32 ;
}
/* Resize the current section to make room for the new branch. */
trampoff = ( isec - > _cooked_size + 3 ) & ( bfd_vma ) - 4 ;
amt = trampoff + size ;
contents = bfd_realloc ( contents , amt ) ;
if ( contents = = NULL )
abort ( ) ;
isec - > _cooked_size = amt ;
memcpy ( contents + trampoff , stub , size ) ;
/* Hijack the old relocation. Since we need two
relocations for this use a " composite " reloc . */
irel - > r_info = ELF32_R_INFO ( ELF32_R_SYM ( irel - > r_info ) ,
@ -1975,31 +1869,95 @@ ppc_elf_relax_section (bfd *abfd,
f - > toff = toff ;
f - > trampoff = trampoff ;
fixups = f ;
trampoff + = size ;
}
else
{
val = f - > trampoff - roff ;
if ( val > = max_branch_offset )
continue ;
/* Nop out the reloc, since we're finalizing things here. */
irel - > r_info = ELF32_R_INFO ( 0 , R_PPC_NONE ) ;
}
/* Fix up the existing branch to hit the trampoline. Hope like
hell this doesn ' t overflow too . */
if ( ppc_elf_install_value ( abfd , contents + roff ,
f - > trampoff - ( roff & ( bfd_vma ) - 3 ) + 4 ,
r_type ) ! = bfd_reloc_ok )
abort ( ) ;
/* Fix up the existing branch to hit the trampoline. */
hit_addr = contents + roff ;
switch ( r_type )
{
case R_PPC_REL24 :
case R_PPC_LOCAL24PC :
case R_PPC_PLTREL24 :
t0 = bfd_get_32 ( abfd , hit_addr ) ;
t0 & = ~ 0x3fffffc ;
t0 | = val & 0x3fffffc ;
bfd_put_32 ( abfd , t0 , hit_addr ) ;
break ;
changed_contents = TRUE ;
changed_relocs = TRUE ;
case R_PPC_REL14 :
case R_PPC_REL14_BRTAKEN :
case R_PPC_REL14_BRNTAKEN :
t0 = bfd_get_32 ( abfd , hit_addr ) ;
t0 & = ~ 0xfffc ;
t0 | = val & 0xfffc ;
bfd_put_32 ( abfd , t0 , hit_addr ) ;
break ;
}
}
/* Clean up. */
while ( fixups )
/* Write out the trampolines. */
changed = fixups ! = NULL ;
if ( fixups ! = NULL )
{
struct one_fixup * f = fixups ;
fixups = fixups - > next ;
free ( f ) ;
const int * stub ;
bfd_byte * dest ;
bfd_vma val ;
int i , size ;
do
{
struct one_fixup * f = fixups ;
fixups = fixups - > next ;
free ( f ) ;
}
while ( fixups ) ;
contents = bfd_realloc ( contents , trampoff ) ;
if ( contents = = NULL )
goto error_return ;
isec - > _cooked_size = ( isec - > _cooked_size + 3 ) & ( bfd_vma ) - 4 ;
/* Branch around the trampolines. */
val = trampoff - isec - > _cooked_size + 0x48000000 ;
dest = contents + isec - > _cooked_size ;
isec - > _cooked_size = trampoff ;
bfd_put_32 ( abfd , val , dest ) ;
dest + = 4 ;
if ( link_info - > shared )
{
stub = shared_stub_entry ;
size = ARRAY_SIZE ( shared_stub_entry ) ;
}
else
{
stub = stub_entry ;
size = ARRAY_SIZE ( stub_entry ) ;
}
i = 0 ;
while ( dest < contents + trampoff )
{
bfd_put_32 ( abfd , stub [ i ] , dest ) ;
i + + ;
if ( i = = size )
i = 0 ;
dest + = 4 ;
}
BFD_ASSERT ( i = = 0 ) ;
}
if ( isymbuf ! = NULL
& & symtab_hdr - > contents ! = ( unsigned char * ) isymbuf )
{
@ -2015,7 +1973,7 @@ ppc_elf_relax_section (bfd *abfd,
if ( contents ! = NULL
& & elf_section_data ( isec ) - > this_hdr . contents ! = contents )
{
if ( ! changed_contents & & ! link_info - > keep_memory )
if ( ! changed & & ! link_info - > keep_memory )
free ( contents ) ;
else
{
@ -2026,13 +1984,13 @@ ppc_elf_relax_section (bfd *abfd,
if ( elf_section_data ( isec ) - > relocs ! = internal_relocs )
{
if ( ! changed_relocs )
if ( ! changed )
free ( internal_relocs ) ;
else
elf_section_data ( isec ) - > relocs = internal_relocs ;
}
* again = changed_contents | | changed_relocs ;
* again = changed ;
return TRUE ;
error_return :
@ -4739,7 +4697,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
symtab_hdr , relocation , sec ,
unresolved_reloc , info ,
warned ) ;
sym_name = h - > root . root . string ;
}
@ -5462,8 +5420,26 @@ ppc_elf_relocate_section (bfd *output_bfd,
+ rel - > r_offset - 4 ) ;
/* Fall thru */
case R_PPC_RELAX32 :
ppc_elf_install_value ( output_bfd , contents + rel - > r_offset ,
relocation + addend , r_type ) ;
{
unsigned long t0 ;
unsigned long t1 ;
t0 = bfd_get_32 ( output_bfd , contents + rel - > r_offset ) ;
t1 = bfd_get_32 ( output_bfd , contents + rel - > r_offset + 4 ) ;
/* We're clearing the bits for R_PPC_ADDR16_HA
and R_PPC_ADDR16_LO here . */
t0 & = ~ 0xffff ;
t1 & = ~ 0xffff ;
/* t0 is HA, t1 is LO */
relocation + = addend ;
t0 | = ( ( relocation + 0x8000 ) > > 16 ) & 0xffff ;
t1 | = relocation & 0xffff ;
bfd_put_32 ( output_bfd , t0 , contents + rel - > r_offset ) ;
bfd_put_32 ( output_bfd , t1 , contents + rel - > r_offset + 4 ) ;
}
continue ;
/* Indirect .sdata relocation. */