@ -153,7 +153,7 @@ struct alpha_elf_link_hash_entry
/* Cumulative flags for all the .got entries. */
/* Cumulative flags for all the .got entries. */
int flags ;
int flags ;
/* Contexts (LITUSE) in which a literal was referenced. */
/* Contexts in which a literal was referenced. */
# define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01
# define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01
# define ALPHA_ELF_LINK_HASH_LU_MEM 0x02
# define ALPHA_ELF_LINK_HASH_LU_MEM 0x02
# define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04
# define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04
@ -161,6 +161,7 @@ struct alpha_elf_link_hash_entry
# define ALPHA_ELF_LINK_HASH_LU_TLSGD 0x10
# define ALPHA_ELF_LINK_HASH_LU_TLSGD 0x10
# define ALPHA_ELF_LINK_HASH_LU_TLSLDM 0x20
# define ALPHA_ELF_LINK_HASH_LU_TLSLDM 0x20
# define ALPHA_ELF_LINK_HASH_LU_FUNC 0x38
# define ALPHA_ELF_LINK_HASH_LU_FUNC 0x38
# define ALPHA_ELF_LINK_HASH_TLS_IE 0x40
/* Used to implement multiple .got subsections. */
/* Used to implement multiple .got subsections. */
struct alpha_elf_got_entry
struct alpha_elf_got_entry
@ -1165,6 +1166,15 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst)
/* These two relocations create a two-word entry in the got. */
/* These two relocations create a two-word entry in the got. */
# define alpha_got_entry_size(r_type) \
# define alpha_got_entry_size(r_type) \
(r_type == R_ALPHA_TLSGD || r_type == R_ALPHA_TLSLDM ? 16 : 8)
(r_type == R_ALPHA_TLSGD || r_type == R_ALPHA_TLSLDM ? 16 : 8)
/* This is PT_TLS segment p_vaddr. */
# define alpha_get_dtprel_base(tlss) \
((tlss)->start)
/* Main program TLS (whose template starts at PT_TLS p_vaddr)
is assigned offset round(16, PT_TLS p_align). */
# define alpha_get_tprel_base(tlss) \
((tlss)->start - align_power ((bfd_vma) 16, (tlss)->align))
/* These functions do relaxation for Alpha ELF.
/* These functions do relaxation for Alpha ELF.
@ -1187,35 +1197,45 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst)
# define OP_BR 0x30
# define OP_BR 0x30
# define OP_BSR 0x34
# define OP_BSR 0x34
# define INSN_UNOP 0x2ffe0000
# define INSN_UNOP 0x2ffe0000
# define INSN_ADDQ 0x40000400
# define INSN_RDUNIQ 0x0000009e
struct alpha_relax_info
struct alpha_relax_info
{
{
bfd * abfd ;
bfd * abfd ;
asection * sec ;
asection * sec ;
bfd_byte * contents ;
bfd_byte * contents ;
Elf_Internal_Shdr * symtab_hdr ;
Elf_Internal_Rela * relocs , * relend ;
Elf_Internal_Rela * relocs , * relend ;
struct bfd_link_info * link_info ;
struct bfd_link_info * link_info ;
boolean changed_contents ;
struct elf_link_tls_segment * tls_segment ;
boolean changed_relocs ;
bfd_vma gp ;
bfd_vma gp ;
bfd * gotobj ;
bfd * gotobj ;
asection * tsec ;
asection * tsec ;
struct alpha_elf_link_hash_entry * h ;
struct alpha_elf_link_hash_entry * h ;
struct alpha_elf_got_entry * * first_gotent ;
struct alpha_elf_got_entry * gotent ;
struct alpha_elf_got_entry * gotent ;
boolean changed_contents ;
boolean changed_relocs ;
unsigned char other ;
unsigned char other ;
} ;
} ;
static Elf_Internal_Rela * elf64_alpha_relax_with_lituse
static boolean elf64_alpha_relax_with_lituse
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ,
Elf_Internal_Rela * irel , Elf_Internal_Rela * irelend ) ) ;
static boolean elf64_alpha_relax_without_lituse
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ,
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ,
Elf_Internal_Rela * irel ) ) ;
Elf_Internal_Rela * irel ) ) ;
static bfd_vma elf64_alpha_relax_opt_call
static bfd_vma elf64_alpha_relax_opt_call
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ) ) ;
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ) ) ;
static boolean elf64_alpha_relax_got_load
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ,
Elf_Internal_Rela * irel , unsigned long ) ) ;
static boolean elf64_alpha_relax_gprelhilo
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ,
Elf_Internal_Rela * irel , boolean ) ) ;
static boolean elf64_alpha_relax_tls_get_addr
PARAMS ( ( struct alpha_relax_info * info , bfd_vma symval ,
Elf_Internal_Rela * irel , boolean ) ) ;
static struct elf_link_tls_segment * elf64_alpha_relax_find_tls_segment
PARAMS ( ( struct alpha_relax_info * , struct elf_link_tls_segment * ) ) ;
static boolean elf64_alpha_relax_section
static boolean elf64_alpha_relax_section
PARAMS ( ( bfd * abfd , asection * sec , struct bfd_link_info * link_info ,
PARAMS ( ( bfd * abfd , asection * sec , struct bfd_link_info * link_info ,
boolean * again ) ) ;
boolean * again ) ) ;
@ -1236,13 +1256,13 @@ elf64_alpha_find_reloc_at_ofs (rel, relend, offset, type)
return NULL ;
return NULL ;
}
}
static Elf_Internal_Rela *
static boolean
elf64_alpha_relax_with_lituse ( info , symval , irel , irelend )
elf64_alpha_relax_with_lituse ( info , symval , irel )
struct alpha_relax_info * info ;
struct alpha_relax_info * info ;
bfd_vma symval ;
bfd_vma symval ;
Elf_Internal_Rela * irel , * irelend ;
Elf_Internal_Rela * irel ;
{
{
Elf_Internal_Rela * urel ;
Elf_Internal_Rela * urel , * irelend = info - > relend ;
int flags , count , i ;
int flags , count , i ;
bfd_signed_vma disp ;
bfd_signed_vma disp ;
boolean fits16 ;
boolean fits16 ;
@ -1258,9 +1278,13 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
( " %s: %s+0x%lx: warning: LITERAL relocation against unexpected insn " ,
( " %s: %s+0x%lx: warning: LITERAL relocation against unexpected insn " ,
bfd_archive_filename ( info - > abfd ) , info - > sec - > name ,
bfd_archive_filename ( info - > abfd ) , info - > sec - > name ,
( unsigned long ) irel - > r_offset ) ) ;
( unsigned long ) irel - > r_offset ) ) ;
return irel ;
return true ;
}
}
/* Can't relax dynamic symbols. */
if ( alpha_elf_dynamic_symbol_p ( & info - > h - > root , info - > link_info ) )
return true ;
/* Summarize how this particular LITERAL is used. */
/* Summarize how this particular LITERAL is used. */
for ( urel = irel + 1 , flags = count = 0 ; urel < irelend ; + + urel , + + count )
for ( urel = irel + 1 , flags = count = 0 ; urel < irelend ; + + urel , + + count )
{
{
@ -1283,25 +1307,27 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
switch ( urel - > r_addend )
switch ( urel - > r_addend )
{
{
default : /* 0 = ADDRESS FORMAT */
case LITUSE_ALPHA_ADDR :
default :
/* This type is really just a placeholder to note that all
/* This type is really just a placeholder to note that all
uses cannot be optimized, but to still allow some. */
uses cannot be optimized, but to still allow some. */
all_optimized = false ;
all_optimized = false ;
break ;
break ;
case 1 : /* MEM FORMAT */
case LITUSE_ALPHA_BASE :
/* We can always optimize 16-bit displacements. */
/* We can always optimize 16-bit displacements. */
/* Extract the displacement from the instruction, sign-extending
/* Extract the displacement from the instruction, sign-extending
it if necessary, then test whether it is within 16 or 32 bits
it if necessary, then test whether it is within 16 or 32 bits
displacement from GP. */
displacement from GP. */
insn_disp = insn & 0x0000ffff ;
insn_disp = insn & 0x0000ffff ;
if ( insn_disp & 0x0000 8000 )
if ( insn_disp & 0x8000 )
insn_disp | = 0xffff0000 ; /* Negative: sign-extend. */
insn_disp | = ~ 0xffff ; /* Negative: sign-extend. */
xdisp = disp + insn_disp ;
xdisp = disp + insn_disp ;
fits16 = ( xdisp > = - ( bfd_signed_vma ) 0x0000 8000 & & xdisp < 0x0000 8000 ) ;
fits16 = ( xdisp > = - ( bfd_signed_vma ) 0x8000 & & xdisp < 0x8000 ) ;
fits32 = ( xdisp > = - ( bfd_signed_vma ) 0x80000000 & & xdisp < 0x7fff8000 ) ;
fits32 = ( xdisp > = - ( bfd_signed_vma ) 0x80000000
& & xdisp < 0x7fff8000 ) ;
if ( fits16 )
if ( fits16 )
{
{
@ -1340,7 +1366,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
all_optimized = false ;
all_optimized = false ;
break ;
break ;
case 2 : /* BYTE OFFSET FORMAT */
case LITUSE_ALPHA_ BYTOFF:
/* We can always optimize byte instructions. */
/* We can always optimize byte instructions. */
/* FIXME: sanity check the insn for byte op. Check that the
/* FIXME: sanity check the insn for byte op. Check that the
@ -1358,7 +1384,9 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
info - > changed_contents = true ;
info - > changed_contents = true ;
break ;
break ;
case 3 : /* CALL FORMAT */
case LITUSE_ALPHA_JSR :
case LITUSE_ALPHA_TLSGD :
case LITUSE_ALPHA_TLSLDM :
{
{
/* If not zero, place to jump without needing pv. */
/* If not zero, place to jump without needing pv. */
bfd_vma optdest = elf64_alpha_relax_opt_call ( info , symval ) ;
bfd_vma optdest = elf64_alpha_relax_opt_call ( info , symval ) ;
@ -1409,7 +1437,8 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
{
{
Elf_Internal_Rela * gpdisp
Elf_Internal_Rela * gpdisp
= ( elf64_alpha_find_reloc_at_ofs
= ( elf64_alpha_find_reloc_at_ofs
( irel , irelend , urel - > r_offset + 4 , R_ALPHA_GPDISP ) ) ;
( info - > relocs , irelend , urel - > r_offset + 4 ,
R_ALPHA_GPDISP ) ) ;
if ( gpdisp )
if ( gpdisp )
{
{
bfd_byte * p_ldah = info - > contents + gpdisp - > r_offset ;
bfd_byte * p_ldah = info - > contents + gpdisp - > r_offset ;
@ -1444,10 +1473,10 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
{
{
if ( - - info - > gotent - > use_count = = 0 )
if ( - - info - > gotent - > use_count = = 0 )
{
{
int sz = alpha_got_entry_size ( info - > gotent - > reloc_type ) ;
int sz = alpha_got_entry_size ( R_ALPHA_LITERAL ) ;
alpha_elf_tdata ( info - > gotent - > gotobj) - > total_got_size - = sz ;
alpha_elf_tdata ( info - > gotobj ) - > total_got_size - = sz ;
if ( ! info - > h )
if ( ! info - > h )
alpha_elf_tdata ( info - > gotent - > gotobj) - > local_got_size - = sz ;
alpha_elf_tdata ( info - > gotobj ) - > local_got_size - = sz ;
}
}
/* If the literal instruction is no longer needed (it may have been
/* If the literal instruction is no longer needed (it may have been
@ -1465,7 +1494,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
}
}
}
}
return irel + count ;
return true ;
}
}
static bfd_vma
static bfd_vma
@ -1541,10 +1570,11 @@ elf64_alpha_relax_opt_call (info, symval)
}
}
static boolean
static boolean
elf64_alpha_relax_without_lituse ( info , symval , irel )
elf64_alpha_relax_got_load ( info , symval , irel , r_type )
struct alpha_relax_info * info ;
struct alpha_relax_info * info ;
bfd_vma symval ;
bfd_vma symval ;
Elf_Internal_Rela * irel ;
Elf_Internal_Rela * irel ;
unsigned long r_type ;
{
{
unsigned int insn ;
unsigned int insn ;
bfd_signed_vma disp ;
bfd_signed_vma disp ;
@ -1554,39 +1584,73 @@ elf64_alpha_relax_without_lituse (info, symval, irel)
if ( insn > > 26 ! = OP_LDQ )
if ( insn > > 26 ! = OP_LDQ )
{
{
reloc_howto_type * howto = elf64_alpha_howto_table + r_type ;
( ( * _bfd_error_handler )
( ( * _bfd_error_handler )
( " %s: %s+0x%lx: warning: LITERAL relocation against unexpected insn " ,
( " %s: %s+0x%lx: warning: %s relocation against unexpected insn " ,
bfd_archive_filename ( info - > abfd ) , info - > sec - > name ,
bfd_archive_filename ( info - > abfd ) , info - > sec - > name ,
( unsigned long ) irel - > r_offset ) ) ;
( unsigned long ) irel - > r_offset , howto - > name )) ;
return true ;
return true ;
}
}
/* So we aren't told much. Do what we can with the address load and
/* Can't relax dynamic symbols. */
fake the rest. All of the optimizations here require that the
if ( alpha_elf_dynamic_symbol_p ( & info - > h - > root , info - > link_info ) )
offset from the GP fit in 16 bits. */
return true ;
/* Can't use local-exec relocations in shared libraries. */
if ( r_type = = R_ALPHA_GOTTPREL & & info - > link_info - > shared )
return true ;
if ( r_type = = R_ALPHA_LITERAL )
disp = symval - info - > gp ;
disp = symval - info - > gp ;
else
{
bfd_vma dtp_base , tp_base ;
BFD_ASSERT ( info - > tls_segment ! = NULL ) ;
dtp_base = alpha_get_dtprel_base ( info - > tls_segment ) ;
tp_base = alpha_get_tprel_base ( info - > tls_segment ) ;
disp = symval - ( r_type = = R_ALPHA_GOTDTPREL ? dtp_base : tp_base ) ;
}
if ( disp < - 0x8000 | | disp > = 0x8000 )
if ( disp < - 0x8000 | | disp > = 0x8000 )
return true ;
return true ;
/* On the LITERAL instruction itself, consider exchang ing
/* Exchange LDQ for LDA. In the case of the TLS relocs, we're load ing
`ldq R,X(gp)' for `lda R,Y(gp)' . */
a constant, so force the base register to be $31 . */
if ( r_type = = R_ALPHA_LITERAL )
insn = ( OP_LDA < < 26 ) | ( insn & 0x03ff0000 ) ;
insn = ( OP_LDA < < 26 ) | ( insn & 0x03ff0000 ) ;
else
insn = ( OP_LDA < < 26 ) | ( insn & ( 31 < < 21 ) ) | ( 31 < < 16 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , info - > contents + irel - > r_offset ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , info - > contents + irel - > r_offset ) ;
info - > changed_contents = true ;
info - > changed_contents = true ;
irel - > r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) , R_ALPHA_GPREL16 ) ;
switch ( r_type )
{
case R_ALPHA_LITERAL :
r_type = R_ALPHA_GPREL16 ;
break ;
case R_ALPHA_GOTDTPREL :
r_type = R_ALPHA_DTPREL16 ;
break ;
case R_ALPHA_GOTTPREL :
r_type = R_ALPHA_TPREL16 ;
break ;
default :
BFD_ASSERT ( 0 ) ;
return false ;
}
irel - > r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) , r_type ) ;
info - > changed_relocs = true ;
info - > changed_relocs = true ;
/* Reduce the use count on this got entry by one, possibly
/* Reduce the use count on this got entry by one, possibly
eliminating it. */
eliminating it. */
if ( - - info - > gotent - > use_count = = 0 )
if ( - - info - > gotent - > use_count = = 0 )
{
{
int sz = alpha_got_entry_size ( info - > gotent - > reloc _type) ;
int sz = alpha_got_entry_size ( r _type) ;
alpha_elf_tdata ( info - > gotent - > gotobj) - > total_got_size - = sz ;
alpha_elf_tdata ( info - > gotobj ) - > total_got_size - = sz ;
if ( ! info - > h )
if ( ! info - > h )
alpha_elf_tdata ( info - > gotent - > gotobj) - > local_got_size - = sz ;
alpha_elf_tdata ( info - > gotobj ) - > local_got_size - = sz ;
}
}
/* ??? Search forward through this basic block looking for insns
/* ??? Search forward through this basic block looking for insns
@ -1605,6 +1669,354 @@ elf64_alpha_relax_without_lituse (info, symval, irel)
return true ;
return true ;
}
}
static boolean
elf64_alpha_relax_gprelhilo ( info , symval , irel , hi )
struct alpha_relax_info * info ;
bfd_vma symval ;
Elf_Internal_Rela * irel ;
boolean hi ;
{
unsigned int insn ;
bfd_signed_vma disp ;
bfd_byte * pos = info - > contents + irel - > r_offset ;
/* ??? This assumes that the compiler doesn't render
array[i]
as
ldah t, array(gp) !gprelhigh
s8addl i, t, t
ldq r, array(t) !gprellow
which would indeed be the most efficient way to implement this. */
return true ;
disp = symval - info - > gp ;
if ( disp < - 0x8000 | | disp > = 0x8000 )
return true ;
if ( hi )
{
/* Nop out the high instruction. */
bfd_put_32 ( info - > abfd , ( bfd_vma ) INSN_UNOP , pos ) ;
info - > changed_contents = true ;
irel - > r_info = ELF64_R_INFO ( 0 , R_ALPHA_NONE ) ;
irel - > r_addend = 0 ;
info - > changed_relocs = true ;
}
else
{
/* Adjust the low instruction to reference GP directly. */
insn = bfd_get_32 ( info - > abfd , pos ) ;
insn = ( insn & 0xffe00000 ) | ( 29 < < 16 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , pos ) ;
info - > changed_contents = true ;
irel - > r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) ,
R_ALPHA_GPREL16 ) ;
info - > changed_relocs = true ;
}
return true ;
}
static boolean
elf64_alpha_relax_tls_get_addr ( info , symval , irel , is_gd )
struct alpha_relax_info * info ;
bfd_vma symval ;
Elf_Internal_Rela * irel ;
boolean is_gd ;
{
bfd_byte * pos [ 5 ] ;
unsigned int insn ;
Elf_Internal_Rela * gpdisp , * hint ;
boolean dynamic , use_gottprel ;
dynamic = alpha_elf_dynamic_symbol_p ( & info - > h - > root , info - > link_info ) ;
/* ??? For LD relaxation, we need a symbol referencing the beginning
of the TLS segment. */
if ( ! is_gd )
return true ;
/* If a TLS symbol is accessed using IE at least once, there is no point
to use dynamic model for it. */
if ( is_gd & & info - > h & & ( info - > h - > flags & ALPHA_ELF_LINK_HASH_TLS_IE ) )
;
/* If the symbol is local, and we've already committed to DF_STATIC_TLS,
then we might as well relax to IE. */
else if ( info - > link_info - > shared & & ! dynamic
& & ( info - > link_info - > flags & DF_STATIC_TLS ) )
;
/* Otherwise we must be building an executable to do anything. */
else if ( info - > link_info - > shared )
return true ;
/* The TLSGD/TLSLDM relocation must be followed by a LITERAL and
the matching LITUSE_TLS relocations. */
if ( irel + 2 > = info - > relend )
return true ;
if ( ELF64_R_TYPE ( irel [ 1 ] . r_info ) ! = R_ALPHA_LITERAL
| | ELF64_R_TYPE ( irel [ 2 ] . r_info ) ! = R_ALPHA_LITUSE
| | irel [ 2 ] . r_addend ! = ( is_gd ? LITUSE_ALPHA_TLSGD : LITUSE_ALPHA_TLSLDM ) )
return true ;
/* There must be a GPDISP relocation positioned immediately after the
LITUSE relocation. */
gpdisp = elf64_alpha_find_reloc_at_ofs ( info - > relocs , info - > relend ,
irel [ 2 ] . r_offset + 4 , R_ALPHA_GPDISP ) ;
if ( ! gpdisp )
return true ;
pos [ 0 ] = info - > contents + irel [ 0 ] . r_offset ;
pos [ 1 ] = info - > contents + irel [ 1 ] . r_offset ;
pos [ 2 ] = info - > contents + irel [ 2 ] . r_offset ;
pos [ 3 ] = info - > contents + gpdisp - > r_offset ;
pos [ 4 ] = pos [ 3 ] + gpdisp - > r_addend ;
/* Only positions 0 and 1 are allowed to be out of order. */
if ( pos [ 1 ] < pos [ 0 ] )
{
bfd_byte * tmp = pos [ 0 ] ;
pos [ 0 ] = pos [ 1 ] ;
pos [ 1 ] = tmp ;
}
if ( pos [ 1 ] > = pos [ 2 ] | | pos [ 2 ] > = pos [ 3 ] | | pos [ 3 ] > = pos [ 4 ] )
return true ;
/* Reduce the use count on the LITERAL relocation. Do this before we
smash the symndx when we adjust the relocations below. */
{
struct alpha_elf_got_entry * lit_gotent ;
struct alpha_elf_link_hash_entry * lit_h ;
unsigned long indx ;
BFD_ASSERT ( ELF64_R_SYM ( irel [ 1 ] . r_info ) > = info - > symtab_hdr - > sh_info ) ;
indx = ELF64_R_SYM ( irel [ 1 ] . r_info ) - info - > symtab_hdr - > sh_info ;
lit_h = alpha_elf_sym_hashes ( info - > abfd ) [ indx ] ;
while ( lit_h - > root . root . type = = bfd_link_hash_indirect
| | lit_h - > root . root . type = = bfd_link_hash_warning )
lit_h = ( struct alpha_elf_link_hash_entry * ) lit_h - > root . root . u . i . link ;
for ( lit_gotent = lit_h - > got_entries ; lit_gotent ;
lit_gotent = lit_gotent - > next )
if ( lit_gotent - > gotobj = = info - > gotobj
& & lit_gotent - > reloc_type = = R_ALPHA_LITERAL
& & lit_gotent - > addend = = irel [ 1 ] . r_addend )
break ;
BFD_ASSERT ( lit_gotent ) ;
if ( - - lit_gotent - > use_count = = 0 )
{
int sz = alpha_got_entry_size ( R_ALPHA_LITERAL ) ;
alpha_elf_tdata ( info - > gotobj ) - > total_got_size - = sz ;
}
}
/* Change
lda $16,x($gp) !tlsgd!1
ldq $27,__tls_get_addr($gp) !literal!1
jsr $26,($27)__tls_get_addr !lituse_tlsgd!1
ldah $29,0($26) !gpdisp!2
lda $29,0($29) !gpdisp!2
to
ldq $16,x($gp) !gottprel
unop
call_pal rduniq
addq $16,$0,$0
unop
or the first pair to
lda $16,x($gp) !tprel
unop
or
ldah $16,x($gp) !tprelhi
lda $16,x($16) !tprello
as appropriate. */
use_gottprel = false ;
switch ( ! dynamic & & ! info - > link_info - > shared )
{
case 1 :
{
bfd_vma tp_base ;
bfd_signed_vma disp ;
BFD_ASSERT ( info - > tls_segment ! = NULL ) ;
tp_base = alpha_get_tprel_base ( info - > tls_segment ) ;
disp = symval - tp_base ;
if ( disp > = - 0x8000 & & disp < 0x8000 )
{
insn = ( OP_LDA < < 26 ) | ( 16 < < 21 ) | ( 31 < < 16 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , pos [ 0 ] ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) INSN_UNOP , pos [ 1 ] ) ;
irel [ 0 ] . r_offset = pos [ 0 ] - info - > contents ;
irel [ 0 ] . r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) ,
R_ALPHA_TPREL16 ) ;
irel [ 1 ] . r_info = ELF64_R_INFO ( 0 , R_ALPHA_NONE ) ;
break ;
}
else if ( disp > = - ( bfd_signed_vma ) 0x80000000
& & disp < ( bfd_signed_vma ) 0x7fff8000 )
{
insn = ( OP_LDAH < < 26 ) | ( 16 < < 21 ) | ( 31 < < 16 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , pos [ 0 ] ) ;
insn = ( OP_LDA < < 26 ) | ( 16 < < 21 ) | ( 16 < < 16 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , pos [ 1 ] ) ;
irel [ 0 ] . r_offset = pos [ 0 ] - info - > contents ;
irel [ 0 ] . r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) ,
R_ALPHA_TPRELHI ) ;
irel [ 1 ] . r_offset = pos [ 1 ] - info - > contents ;
irel [ 1 ] . r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) ,
R_ALPHA_TPRELLO ) ;
break ;
}
}
/* FALLTHRU */
default :
use_gottprel = true ;
insn = ( OP_LDQ < < 26 ) | ( 16 < < 21 ) | ( 29 < < 16 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , pos [ 0 ] ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) INSN_UNOP , pos [ 1 ] ) ;
irel [ 0 ] . r_offset = pos [ 0 ] - info - > contents ;
irel [ 0 ] . r_info = ELF64_R_INFO ( ELF64_R_SYM ( irel - > r_info ) ,
R_ALPHA_GOTTPREL ) ;
irel [ 1 ] . r_info = ELF64_R_INFO ( 0 , R_ALPHA_NONE ) ;
break ;
}
bfd_put_32 ( info - > abfd , ( bfd_vma ) INSN_RDUNIQ , pos [ 2 ] ) ;
insn = INSN_ADDQ | ( 16 < < 21 ) | ( 0 < < 16 ) | ( 0 < < 0 ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) insn , pos [ 3 ] ) ;
bfd_put_32 ( info - > abfd , ( bfd_vma ) INSN_UNOP , pos [ 4 ] ) ;
irel [ 2 ] . r_info = ELF64_R_INFO ( 0 , R_ALPHA_NONE ) ;
gpdisp - > r_info = ELF64_R_INFO ( 0 , R_ALPHA_NONE ) ;
hint = elf64_alpha_find_reloc_at_ofs ( info - > relocs , info - > relend ,
irel [ 2 ] . r_offset , R_ALPHA_HINT ) ;
if ( hint )
hint - > r_info = ELF64_R_INFO ( 0 , R_ALPHA_NONE ) ;
info - > changed_contents = true ;
info - > changed_relocs = true ;
/* Reduce the use count on the TLSGD/TLSLDM relocation. */
if ( - - info - > gotent - > use_count = = 0 )
{
int sz = alpha_got_entry_size ( info - > gotent - > reloc_type ) ;
alpha_elf_tdata ( info - > gotobj ) - > total_got_size - = sz ;
if ( ! info - > h )
alpha_elf_tdata ( info - > gotobj ) - > local_got_size - = sz ;
}
/* If we've switched to a GOTTPREL relocation, increment the reference
count on that got entry. */
if ( use_gottprel )
{
struct alpha_elf_got_entry * tprel_gotent ;
for ( tprel_gotent = * info - > first_gotent ; tprel_gotent ;
tprel_gotent = tprel_gotent - > next )
if ( tprel_gotent - > gotobj = = info - > gotobj
& & tprel_gotent - > reloc_type = = R_ALPHA_GOTTPREL
& & tprel_gotent - > addend = = irel - > r_addend )
break ;
if ( tprel_gotent )
tprel_gotent - > use_count + + ;
else
{
if ( info - > gotent - > use_count = = 0 )
tprel_gotent = info - > gotent ;
else
{
tprel_gotent = ( struct alpha_elf_got_entry * )
bfd_alloc ( info - > abfd , sizeof ( struct alpha_elf_got_entry ) ) ;
if ( ! tprel_gotent )
return false ;
tprel_gotent - > next = * info - > first_gotent ;
* info - > first_gotent = tprel_gotent ;
tprel_gotent - > gotobj = info - > gotobj ;
tprel_gotent - > addend = irel - > r_addend ;
tprel_gotent - > got_offset = - 1 ;
tprel_gotent - > reloc_done = 0 ;
tprel_gotent - > reloc_xlated = 0 ;
}
tprel_gotent - > use_count = 1 ;
tprel_gotent - > reloc_type = R_ALPHA_GOTTPREL ;
}
}
return true ;
}
static struct elf_link_tls_segment *
elf64_alpha_relax_find_tls_segment ( info , seg )
struct alpha_relax_info * info ;
struct elf_link_tls_segment * seg ;
{
bfd * output_bfd = info - > sec - > output_section - > owner ;
asection * first_tls_sec = NULL , * o ;
unsigned int align ;
bfd_vma base , end ;
for ( o = output_bfd - > sections ; o ; o = o - > next )
if ( ( o - > flags & SEC_THREAD_LOCAL ) ! = 0
& & ( o - > flags & SEC_LOAD ) ! = 0 )
{
first_tls_sec = o ;
break ;
}
if ( ! first_tls_sec )
return NULL ;
base = first_tls_sec - > vma ;
align = 0 ;
for ( o = first_tls_sec ; o & & ( o - > flags & SEC_THREAD_LOCAL ) ; o = o - > next )
{
bfd_vma size ;
if ( bfd_get_section_alignment ( output_bfd , o ) > align )
align = bfd_get_section_alignment ( output_bfd , o ) ;
size = o - > _raw_size ;
if ( size = = 0 & & ( o - > flags & SEC_HAS_CONTENTS ) = = 0 )
{
struct bfd_link_order * lo ;
for ( lo = o - > link_order_head ; lo ; lo = lo - > next )
if ( size < lo - > offset + lo - > size )
size = lo - > offset + lo - > size ;
}
end = o - > vma + size ;
}
seg - > start = base ;
seg - > size = end - base ;
seg - > align = align ;
return seg ;
}
static boolean
static boolean
elf64_alpha_relax_section ( abfd , sec , link_info , again )
elf64_alpha_relax_section ( abfd , sec , link_info , again )
bfd * abfd ;
bfd * abfd ;
@ -1618,11 +2030,12 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
Elf_Internal_Rela * free_relocs = NULL ;
Elf_Internal_Rela * free_relocs = NULL ;
Elf_Internal_Rela * irel , * irelend ;
Elf_Internal_Rela * irel , * irelend ;
bfd_byte * free_contents = NULL ;
bfd_byte * free_contents = NULL ;
Elf64_External_Sym * extsyms = NULL ;
Elf64_External_Sym * extsyms ;
Elf64_External_Sym * free_extsyms = NULL ;
Elf64_External_Sym * free_extsyms = NULL ;
Elf_External_Sym_Shndx * shndx_buf = NULL ;
Elf_External_Sym_Shndx * shndx_buf = NULL ;
struct alpha_elf_got_entry * * local_got_entries ;
struct alpha_elf_got_entry * * local_got_entries ;
struct alpha_relax_info info ;
struct alpha_relax_info info ;
struct elf_link_tls_segment tls_segment ;
/* We are not currently changing any sizes, so only one pass. */
/* We are not currently changing any sizes, so only one pass. */
* again = false ;
* again = false ;
@ -1653,6 +2066,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
info . abfd = abfd ;
info . abfd = abfd ;
info . sec = sec ;
info . sec = sec ;
info . link_info = link_info ;
info . link_info = link_info ;
info . symtab_hdr = symtab_hdr ;
info . relocs = internal_relocs ;
info . relocs = internal_relocs ;
info . relend = irelend = internal_relocs + sec - > reloc_count ;
info . relend = irelend = internal_relocs + sec - > reloc_count ;
@ -1667,18 +2081,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
+ 0x8000 ) ;
+ 0x8000 ) ;
}
}
for ( irel = internal_relocs ; irel < irelend ; irel + + )
{
bfd_vma symval ;
Elf_Internal_Sym isym ;
struct alpha_elf_got_entry * gotent ;
if ( ELF64_R_TYPE ( irel - > r_info ) ! = ( int ) R_ALPHA_LITERAL )
continue ;
/* Get the section contents. */
/* Get the section contents. */
if ( info . contents = = NULL )
{
if ( elf_section_data ( sec ) - > this_hdr . contents ! = NULL )
if ( elf_section_data ( sec ) - > this_hdr . contents ! = NULL )
info . contents = elf_section_data ( sec ) - > this_hdr . contents ;
info . contents = elf_section_data ( sec ) - > this_hdr . contents ;
else
else
@ -1692,19 +2095,13 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
( file_ptr ) 0 , sec - > _raw_size ) )
( file_ptr ) 0 , sec - > _raw_size ) )
goto error_return ;
goto error_return ;
}
}
}
/* Read this BFD's symbols if we haven't done so already. */
if ( extsyms = = NULL )
{
bfd_size_type amt ;
/* Read this BFD's symbols. */
if ( symtab_hdr - > contents ! = NULL )
if ( symtab_hdr - > contents ! = NULL )
extsyms = ( Elf64_External_Sym * ) symtab_hdr - > contents ;
extsyms = ( Elf64_External_Sym * ) symtab_hdr - > contents ;
else
else
{
{
amt = symtab_hdr - > sh_info ;
bfd_size_type amt = symtab_hdr - > sh_info * sizeof ( Elf64_External_Sym ) ;
amt * = sizeof ( Elf64_External_Sym ) ;
extsyms = ( Elf64_External_Sym * ) bfd_malloc ( amt ) ;
extsyms = ( Elf64_External_Sym * ) bfd_malloc ( amt ) ;
if ( extsyms = = NULL )
if ( extsyms = = NULL )
goto error_return ;
goto error_return ;
@ -1717,8 +2114,8 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
shndx_hdr = & elf_tdata ( abfd ) - > symtab_shndx_hdr ;
shndx_hdr = & elf_tdata ( abfd ) - > symtab_shndx_hdr ;
if ( shndx_hdr - > sh_size ! = 0 )
if ( shndx_hdr - > sh_size ! = 0 )
{
{
amt = symtab_hdr - > sh_info ;
bfd_size_type amt ;
amt * = sizeof ( Elf_External_Sym_Shndx ) ;
amt = symtab_hdr - > sh_info * sizeof ( Elf_External_Sym_Shndx ) ;
shndx_buf = ( Elf_External_Sym_Shndx * ) bfd_malloc ( amt ) ;
shndx_buf = ( Elf_External_Sym_Shndx * ) bfd_malloc ( amt ) ;
if ( shndx_buf = = NULL )
if ( shndx_buf = = NULL )
goto error_return ;
goto error_return ;
@ -1726,6 +2123,32 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
| | bfd_bread ( ( PTR ) shndx_buf , amt , abfd ) ! = amt )
| | bfd_bread ( ( PTR ) shndx_buf , amt , abfd ) ! = amt )
goto error_return ;
goto error_return ;
}
}
/* Compute the TLS segment information. The version normally found in
elf_hash_table (link_info)->tls_segment isn't built until final_link.
??? Probably should look into extracting this into a common function. */
info . tls_segment = elf64_alpha_relax_find_tls_segment ( & info , & tls_segment ) ;
for ( irel = internal_relocs ; irel < irelend ; irel + + )
{
bfd_vma symval ;
Elf_Internal_Sym isym ;
struct alpha_elf_got_entry * gotent ;
unsigned long r_type = ELF64_R_TYPE ( irel - > r_info ) ;
/* Early exit for unhandled or unrelaxable relocations. */
switch ( r_type )
{
case R_ALPHA_LITERAL :
case R_ALPHA_GPRELHIGH :
case R_ALPHA_GPRELLOW :
case R_ALPHA_GOTDTPREL :
case R_ALPHA_GOTTPREL :
case R_ALPHA_TLSGD :
case R_ALPHA_TLSLDM :
break ;
default :
continue ;
}
}
/* Get the value of the symbol referred to by the reloc. */
/* Get the value of the symbol referred to by the reloc. */
@ -1749,7 +2172,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
info . h = NULL ;
info . h = NULL ;
info . other = isym . st_other ;
info . other = isym . st_other ;
gotent = local_got_entries [ ELF64_R_SYM ( irel - > r_info ) ] ;
info . first_ gotent = & local_got_entries [ ELF64_R_SYM ( irel - > r_info ) ] ;
symval = isym . st_value ;
symval = isym . st_value ;
}
}
else
else
@ -1765,62 +2188,79 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
| | h - > root . root . type = = bfd_link_hash_warning )
| | h - > root . root . type = = bfd_link_hash_warning )
h = ( struct alpha_elf_link_hash_entry * ) h - > root . root . u . i . link ;
h = ( struct alpha_elf_link_hash_entry * ) h - > root . root . u . i . link ;
/* We can't do anthing with undefined or dynamic symbols. */
if ( h - > root . root . type = = bfd_link_hash_undefined
| | h - > root . root . type = = bfd_link_hash_undefweak
| | alpha_elf_dynamic_symbol_p ( & h - > root , link_info ) )
continue ;
info . h = h ;
info . h = h ;
info . tsec = h - > root . root . u . def . section ;
info . tsec = h - > root . root . u . def . section ;
info . other = h - > root . other ;
info . other = h - > root . other ;
gotent = h - > got_entries ;
info . first_ gotent = & h - > got_entries ;
symval = h - > root . root . u . def . value ;
symval = h - > root . root . u . def . value ;
}
}
/* Search for the got entry to be used by this relocation. */
/* Search for the got entry to be used by this relocation. */
while ( gotent - > gotobj ! = info . gotobj | | gotent - > add end ! = irel - > r_addend )
for ( gotent = * info . first_gotent ; gotent ; got ent = gotent - > next )
gotent = gotent - > next ;
if ( gotent - > gotobj = = info . gotobj
& & gotent - > reloc_type = = r_type
& & gotent - > addend = = irel - > r_addend )
break ;
info . gotent = gotent ;
info . gotent = gotent ;
symval + = info . tsec - > output_section - > vma + info . tsec - > output_offset ;
symval + = info . tsec - > output_section - > vma + info . tsec - > output_offset ;
symval + = irel - > r_addend ;
symval + = irel - > r_addend ;
switch ( r_type )
{
case R_ALPHA_LITERAL :
BFD_ASSERT ( info . gotent ! = NULL ) ;
BFD_ASSERT ( info . gotent ! = NULL ) ;
/* If there exist LITUSE relocations immediately following, this
/* If there exist LITUSE relocations immediately following, this
opens up all sorts of interesting optimizations, because we
opens up all sorts of interesting optimizations, because we
now know every location that this address load is used. */
now know every location that this address load is used. */
if ( irel + 1 < irelend
if ( irel + 1 < irelend & & ELF64_R_TYPE ( irel [ 1 ] . r_info ) = = R_ALPHA_LITUSE )
& & ELF64_R_TYPE ( irel [ 1 ] . r_info ) = = R_ALPHA_LITUSE )
{
{
irel = elf64_alpha_relax_with_lituse ( & info , symval , irel , irelend ) ;
if ( ! elf64_alpha_relax_with_lituse ( & info , symval , irel ) )
if ( irel = = NULL )
goto error_return ;
goto error_return ;
}
}
else
else
{
{
if ( ! elf64_alpha_relax_without_lituse ( & info , symval , irel ) )
if ( ! elf64_alpha_relax_got_load ( & info , symval , irel , r_type ))
goto error_return ;
goto error_return ;
}
}
break ;
case R_ALPHA_GPRELHIGH :
case R_ALPHA_GPRELLOW :
if ( ! elf64_alpha_relax_gprelhilo ( & info , symval , irel ,
r_type = = R_ALPHA_GPRELHIGH ) )
goto error_return ;
break ;
case R_ALPHA_GOTDTPREL :
case R_ALPHA_GOTTPREL :
BFD_ASSERT ( info . gotent ! = NULL ) ;
if ( ! elf64_alpha_relax_got_load ( & info , symval , irel , r_type ) )
goto error_return ;
break ;
case R_ALPHA_TLSGD :
case R_ALPHA_TLSLDM :
BFD_ASSERT ( info . gotent ! = NULL ) ;
if ( ! elf64_alpha_relax_tls_get_addr ( & info , symval , irel ,
r_type = = R_ALPHA_TLSGD ) )
goto error_return ;
break ;
}
}
}
if ( ! elf64_alpha_size_got_sections ( abfd , link_info ) )
if ( ! elf64_alpha_size_got_sections ( abfd , link_info ) )
return false ;
return false ;
if ( info . changed_relocs )
if ( info . changed_relocs )
{
elf_section_data ( sec ) - > relocs = internal_relocs ;
elf_section_data ( sec ) - > relocs = internal_relocs ;
}
else if ( free_relocs ! = NULL )
else if ( free_relocs ! = NULL )
{
free ( free_relocs ) ;
free ( free_relocs ) ;
}
if ( info . changed_contents )
if ( info . changed_contents )
{
elf_section_data ( sec ) - > this_hdr . contents = info . contents ;
elf_section_data ( sec ) - > this_hdr . contents = info . contents ;
}
else if ( free_contents ! = NULL )
else if ( free_contents ! = NULL )
{
{
if ( ! link_info - > keep_memory )
if ( ! link_info - > keep_memory )
@ -2674,6 +3114,7 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
case R_ALPHA_GOTTPREL :
case R_ALPHA_GOTTPREL :
need = NEED_GOT | NEED_GOT_ENTRY ;
need = NEED_GOT | NEED_GOT_ENTRY ;
gotent_flags = ALPHA_ELF_LINK_HASH_TLS_IE ;
if ( info - > shared )
if ( info - > shared )
info - > flags | = DF_STATIC_TLS ;
info - > flags | = DF_STATIC_TLS ;
break ;
break ;
@ -3653,12 +4094,8 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
tls_segment = elf_hash_table ( info ) - > tls_segment ;
tls_segment = elf_hash_table ( info ) - > tls_segment ;
if ( tls_segment )
if ( tls_segment )
{
{
/* This is PT_TLS segment p_vaddr. */
dtp_base = alpha_get_dtprel_base ( tls_segment ) ;
d tp_base = tls_segment - > start ;
tp_base = alpha_get_tprel_base ( tls_segment ) ;
/* Main program TLS (whose template starts at PT_TLS p_vaddr)
is assigned offset round(16, PT_TLS p_align). */
tp_base = dtp_base - align_power ( 16 , tls_segment - > align ) ;
}
}
}
}
@ -4156,12 +4593,21 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
}
}
BFD_ASSERT ( tls_segment ! = NULL ) ;
BFD_ASSERT ( tls_segment ! = NULL ) ;
value - = dtp_base ;
value - = dtp_base ;
if ( r_type = = R_ALPHA_DTPRELHI )
value = ( ( bfd_signed_vma ) value > > 16 ) + ( ( value > > 15 ) & 1 ) ;
goto default_reloc ;
goto default_reloc ;
case R_ALPHA_TPRELHI :
case R_ALPHA_TPRELHI :
case R_ALPHA_TPRELLO :
case R_ALPHA_TPRELLO :
case R_ALPHA_TPREL16 :
case R_ALPHA_TPREL16 :
if ( dynamic_symbol_p )
if ( info - > shared )
{
( * _bfd_error_handler )
( _ ( " %s: TLS local exec code cannot be linked into shared objects " ) ,
bfd_archive_filename ( input_bfd ) ) ;
ret_val = false ;
}
else if ( dynamic_symbol_p )
{
{
( * _bfd_error_handler )
( * _bfd_error_handler )
( _ ( " %s: tp-relative relocation against dynamic symbol %s " ) ,
( _ ( " %s: tp-relative relocation against dynamic symbol %s " ) ,
@ -4170,6 +4616,8 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
}
}
BFD_ASSERT ( tls_segment ! = NULL ) ;
BFD_ASSERT ( tls_segment ! = NULL ) ;
value - = tp_base ;
value - = tp_base ;
if ( r_type = = R_ALPHA_TPRELHI )
value = ( ( bfd_signed_vma ) value > > 16 ) + ( ( value > > 15 ) & 1 ) ;
goto default_reloc ;
goto default_reloc ;
case R_ALPHA_GOTDTPREL :
case R_ALPHA_GOTDTPREL :