@ -25,6 +25,7 @@
# include "elf-bfd.h"
# include "elf/avr.h"
# include "elf32-avr.h"
# include "bfd_stdint.h"
/* Enable debugging printout at stdout with this variable. */
static bfd_boolean debug_relax = FALSE ;
@ -1935,6 +1936,118 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
return TRUE ;
}
static Elf_Internal_Sym *
retrieve_local_syms ( bfd * input_bfd )
{
Elf_Internal_Shdr * symtab_hdr ;
Elf_Internal_Sym * isymbuf ;
size_t locsymcount ;
symtab_hdr = & elf_tdata ( input_bfd ) - > symtab_hdr ;
locsymcount = symtab_hdr - > sh_info ;
isymbuf = ( Elf_Internal_Sym * ) symtab_hdr - > contents ;
if ( isymbuf = = NULL & & locsymcount ! = 0 )
isymbuf = bfd_elf_get_elf_syms ( input_bfd , symtab_hdr , locsymcount , 0 ,
NULL , NULL , NULL ) ;
/* Save the symbols for this input file so they won't be read again. */
if ( isymbuf & & isymbuf ! = ( Elf_Internal_Sym * ) symtab_hdr - > contents )
symtab_hdr - > contents = ( unsigned char * ) isymbuf ;
return isymbuf ;
}
/* Get the input section for a given symbol index.
If the symbol is :
. a section symbol , return the section ;
. a common symbol , return the common section ;
. an undefined symbol , return the undefined section ;
. an indirect symbol , follow the links ;
. an absolute value , return the absolute section . */
static asection *
get_elf_r_symndx_section ( bfd * abfd , unsigned long r_symndx )
{
Elf_Internal_Shdr * symtab_hdr = & elf_tdata ( abfd ) - > symtab_hdr ;
asection * target_sec = NULL ;
if ( r_symndx < symtab_hdr - > sh_info )
{
Elf_Internal_Sym * isymbuf ;
unsigned int section_index ;
isymbuf = retrieve_local_syms ( abfd ) ;
section_index = isymbuf [ r_symndx ] . st_shndx ;
if ( section_index = = SHN_UNDEF )
target_sec = bfd_und_section_ptr ;
else if ( section_index = = SHN_ABS )
target_sec = bfd_abs_section_ptr ;
else if ( section_index = = SHN_COMMON )
target_sec = bfd_com_section_ptr ;
else
target_sec = bfd_section_from_elf_index ( abfd , section_index ) ;
}
else
{
unsigned long indx = r_symndx - symtab_hdr - > sh_info ;
struct elf_link_hash_entry * h = elf_sym_hashes ( abfd ) [ indx ] ;
while ( h - > root . type = = bfd_link_hash_indirect
| | h - > root . type = = bfd_link_hash_warning )
h = ( struct elf_link_hash_entry * ) h - > root . u . i . link ;
switch ( h - > root . type )
{
case bfd_link_hash_defined :
case bfd_link_hash_defweak :
target_sec = h - > root . u . def . section ;
break ;
case bfd_link_hash_common :
target_sec = bfd_com_section_ptr ;
break ;
case bfd_link_hash_undefined :
case bfd_link_hash_undefweak :
target_sec = bfd_und_section_ptr ;
break ;
default : /* New indirect warning. */
target_sec = bfd_und_section_ptr ;
break ;
}
}
return target_sec ;
}
/* Get the section-relative offset for a symbol number. */
static bfd_vma
get_elf_r_symndx_offset ( bfd * abfd , unsigned long r_symndx )
{
Elf_Internal_Shdr * symtab_hdr = & elf_tdata ( abfd ) - > symtab_hdr ;
bfd_vma offset = 0 ;
if ( r_symndx < symtab_hdr - > sh_info )
{
Elf_Internal_Sym * isymbuf ;
isymbuf = retrieve_local_syms ( abfd ) ;
offset = isymbuf [ r_symndx ] . st_value ;
}
else
{
unsigned long indx = r_symndx - symtab_hdr - > sh_info ;
struct elf_link_hash_entry * h =
elf_sym_hashes ( abfd ) [ indx ] ;
while ( h - > root . type = = bfd_link_hash_indirect
| | h - > root . type = = bfd_link_hash_warning )
h = ( struct elf_link_hash_entry * ) h - > root . u . i . link ;
if ( h - > root . type = = bfd_link_hash_defined
| | h - > root . type = = bfd_link_hash_defweak )
offset = h - > root . u . def . value ;
}
return offset ;
}
/* This function handles relaxing for the avr.
Many important relaxing opportunities within functions are already
realized by the compiler itself .
@ -3347,6 +3460,332 @@ elf32_avr_build_stubs (struct bfd_link_info *info)
return TRUE ;
}
/* Callback used by QSORT to order relocations AP and BP. */
static int
internal_reloc_compare ( const void * ap , const void * bp )
{
const Elf_Internal_Rela * a = ( const Elf_Internal_Rela * ) ap ;
const Elf_Internal_Rela * b = ( const Elf_Internal_Rela * ) bp ;
if ( a - > r_offset ! = b - > r_offset )
return ( a - > r_offset - b - > r_offset ) ;
/* We don't need to sort on these criteria for correctness,
but enforcing a more strict ordering prevents unstable qsort
from behaving differently with different implementations .
Without the code below we get correct but different results
on Solaris 2.7 and 2.8 . We would like to always produce the
same results no matter the host . */
if ( a - > r_info ! = b - > r_info )
return ( a - > r_info - b - > r_info ) ;
return ( a - > r_addend - b - > r_addend ) ;
}
/* Return true if ADDRESS is within the vma range of SECTION from ABFD. */
static bfd_boolean
avr_is_section_for_address ( bfd * abfd , asection * section , bfd_vma address )
{
bfd_vma vma ;
bfd_size_type size ;
vma = bfd_get_section_vma ( abfd , section ) ;
if ( address < vma )
return FALSE ;
size = section - > size ;
if ( address > = vma + size )
return FALSE ;
return TRUE ;
}
/* Data structure used by AVR_FIND_SECTION_FOR_ADDRESS. */
struct avr_find_section_data
{
/* The address we're looking for. */
bfd_vma address ;
/* The section we've found. */
asection * section ;
} ;
/* Helper function to locate the section holding a certain virtual memory
address . This is called via bfd_map_over_sections . The DATA is an
instance of STRUCT AVR_FIND_SECTION_DATA , the address field of which
has been set to the address to search for , and the section field has
been set to NULL . If SECTION from ABFD contains ADDRESS then the
section field in DATA will be set to SECTION . As an optimisation , if
the section field is already non - null then this function does not
perform any checks , and just returns . */
static void
avr_find_section_for_address ( bfd * abfd ,
asection * section , void * data )
{
struct avr_find_section_data * fs_data
= ( struct avr_find_section_data * ) data ;
/* Return if already found. */
if ( fs_data - > section ! = NULL )
return ;
/* If this section isn't part of the addressable code content, skip it. */
if ( ( bfd_get_section_flags ( abfd , section ) & SEC_ALLOC ) = = 0
& & ( bfd_get_section_flags ( abfd , section ) & SEC_CODE ) = = 0 )
return ;
if ( avr_is_section_for_address ( abfd , section , fs_data - > address ) )
fs_data - > section = section ;
}
/* Load all of the property records from SEC, a section from ABFD. Return
a STRUCT AVR_PROPERTY_RECORD_LIST containing all the records . The
memory for the returned structure , and all of the records pointed too by
the structure are allocated with a single call to malloc , so , only the
pointer returned needs to be free ' d . */
static struct avr_property_record_list *
avr_elf32_load_records_from_section ( bfd * abfd , asection * sec )
{
char * contents = NULL , * ptr ;
bfd_size_type size , mem_size ;
bfd_byte version , flags ;
uint16_t record_count , i ;
struct avr_property_record_list * r_list = NULL ;
Elf_Internal_Rela * internal_relocs = NULL , * rel , * rel_end ;
struct avr_find_section_data fs_data ;
fs_data . section = NULL ;
size = bfd_get_section_size ( sec ) ;
contents = bfd_malloc ( size ) ;
bfd_get_section_contents ( abfd , sec , contents , 0 , size ) ;
ptr = contents ;
/* Load the relocations for the '.avr.prop' section if there are any, and
sort them . */
internal_relocs = ( _bfd_elf_link_read_relocs
( abfd , sec , NULL , NULL , FALSE ) ) ;
if ( internal_relocs )
qsort ( internal_relocs , sec - > reloc_count ,
sizeof ( Elf_Internal_Rela ) , internal_reloc_compare ) ;
/* There is a header at the start of the property record section SEC, the
format of this header is :
uint8_t : version number
uint8_t : flags
uint16_t : record counter
*/
/* Check we have at least got a headers worth of bytes. */
if ( size < AVR_PROPERTY_SECTION_HEADER_SIZE )
goto load_failed ;
version = * ( ( bfd_byte * ) ptr ) ;
ptr + + ;
flags = * ( ( bfd_byte * ) ptr ) ;
ptr + + ;
record_count = * ( ( uint16_t * ) ptr ) ;
ptr + = 2 ;
BFD_ASSERT ( ptr - contents = = AVR_PROPERTY_SECTION_HEADER_SIZE ) ;
/* Now allocate space for the list structure, and all of the list
elements in a single block . */
mem_size = sizeof ( struct avr_property_record_list )
+ sizeof ( struct avr_property_record ) * record_count ;
r_list = bfd_malloc ( mem_size ) ;
if ( r_list = = NULL )
goto load_failed ;
r_list - > version = version ;
r_list - > flags = flags ;
r_list - > section = sec ;
r_list - > record_count = record_count ;
r_list - > records = ( struct avr_property_record * ) ( & r_list [ 1 ] ) ;
size - = AVR_PROPERTY_SECTION_HEADER_SIZE ;
/* Check that we understand the version number. There is only one
version number right now , anything else is an error . */
if ( r_list - > version ! = AVR_PROPERTY_RECORDS_VERSION )
goto load_failed ;
rel = internal_relocs ;
rel_end = rel + sec - > reloc_count ;
for ( i = 0 ; i < record_count ; + + i )
{
bfd_vma address ;
/* Each entry is a 32-bit address, followed by a single byte type.
After that is the type specific data . We must take care to
ensure that we don ' t read beyond the end of the section data . */
if ( size < 5 )
goto load_failed ;
r_list - > records [ i ] . section = NULL ;
r_list - > records [ i ] . offset = 0 ;
if ( rel )
{
/* The offset of the address within the .avr.prop section. */
size_t offset = ptr - contents ;
while ( rel < rel_end & & rel - > r_offset < offset )
+ + rel ;
if ( rel = = rel_end )
rel = NULL ;
else if ( rel - > r_offset = = offset )
{
/* Find section and section offset. */
unsigned long r_symndx ;
asection * rel_sec ;
bfd_vma sec_offset ;
r_symndx = ELF32_R_SYM ( rel - > r_info ) ;
rel_sec = get_elf_r_symndx_section ( abfd , r_symndx ) ;
sec_offset = get_elf_r_symndx_offset ( abfd , r_symndx )
+ rel - > r_addend ;
r_list - > records [ i ] . section = rel_sec ;
r_list - > records [ i ] . offset = sec_offset ;
}
}
address = * ( ( uint32_t * ) ptr ) ;
ptr + = 4 ;
size - = 4 ;
if ( r_list - > records [ i ] . section = = NULL )
{
/* Try to find section and offset from address. */
if ( fs_data . section ! = NULL
& & ! avr_is_section_for_address ( abfd , fs_data . section ,
address ) )
fs_data . section = NULL ;
if ( fs_data . section = = NULL )
{
fs_data . address = address ;
bfd_map_over_sections ( abfd , avr_find_section_for_address ,
& fs_data ) ;
}
if ( fs_data . section = = NULL )
{
fprintf ( stderr , " Failed to find matching section. \n " ) ;
goto load_failed ;
}
r_list - > records [ i ] . section = fs_data . section ;
r_list - > records [ i ] . offset
= address - bfd_get_section_vma ( abfd , fs_data . section ) ;
}
r_list - > records [ i ] . type = * ( ( bfd_byte * ) ptr ) ;
ptr + = 1 ;
size - = 1 ;
switch ( r_list - > records [ i ] . type )
{
case RECORD_ORG :
/* Nothing else to load. */
break ;
case RECORD_ORG_AND_FILL :
/* Just a 4-byte fill to load. */
if ( size < 4 )
goto load_failed ;
r_list - > records [ i ] . data . org . fill = * ( ( uint32_t * ) ptr ) ;
ptr + = 4 ;
size - = 4 ;
break ;
case RECORD_ALIGN :
/* Just a 4-byte alignment to load. */
if ( size < 4 )
goto load_failed ;
r_list - > records [ i ] . data . align . bytes = * ( ( uint32_t * ) ptr ) ;
ptr + = 4 ;
size - = 4 ;
/* Just initialise PRECEDING_DELETED field, this field is
used during linker relaxation . */
r_list - > records [ i ] . data . align . preceding_deleted = 0 ;
break ;
case RECORD_ALIGN_AND_FILL :
/* A 4-byte alignment, and a 4-byte fill to load. */
if ( size < 8 )
goto load_failed ;
r_list - > records [ i ] . data . align . bytes = * ( ( uint32_t * ) ptr ) ;
ptr + = 4 ;
r_list - > records [ i ] . data . align . fill = * ( ( uint32_t * ) ptr ) ;
ptr + = 4 ;
size - = 8 ;
/* Just initialise PRECEDING_DELETED field, this field is
used during linker relaxation . */
r_list - > records [ i ] . data . align . preceding_deleted = 0 ;
break ;
default :
goto load_failed ;
}
}
free ( contents ) ;
free ( internal_relocs ) ;
return r_list ;
load_failed :
free ( internal_relocs ) ;
free ( contents ) ;
free ( r_list ) ;
return NULL ;
}
/* Load all of the property records from ABFD. See
AVR_ELF32_LOAD_RECORDS_FROM_SECTION for details of the return value . */
struct avr_property_record_list *
avr_elf32_load_property_records ( bfd * abfd )
{
asection * sec ;
/* Find the '.avr.prop' section and load the contents into memory. */
sec = bfd_get_section_by_name ( abfd , AVR_PROPERTY_RECORD_SECTION_NAME ) ;
if ( sec = = NULL )
return NULL ;
return avr_elf32_load_records_from_section ( abfd , sec ) ;
}
const char *
avr_elf32_property_record_name ( struct avr_property_record * rec )
{
const char * str ;
switch ( rec - > type )
{
case RECORD_ORG :
str = " ORG " ;
break ;
case RECORD_ORG_AND_FILL :
str = " ORG+FILL " ;
break ;
case RECORD_ALIGN :
str = " ALIGN " ;
break ;
case RECORD_ALIGN_AND_FILL :
str = " ALIGN+FILL " ;
break ;
default :
str = " unknown " ;
}
return str ;
}
# define ELF_ARCH bfd_arch_avr
# define ELF_TARGET_ID AVR_ELF_DATA
# define ELF_MACHINE_CODE EM_AVR