PR binutils/13897

* elf.c (elf_find_function): Cache last function sym info.
	(_bfd_elf_maybe_function_sym): Return function size, pass in
	section of interest.
	* elf-bfd.h (struct elf_backend_data <maybe_function_sym>): Likewise.
	(_bfd_elf_maybe_function_sym): Likewise.
	* elf64-ppc.c (ppc64_elf_maybe_function_sym): Likewise.
	(opd_entry_value): Add in_code_sec param.  Revert caching code.
	Return -1 if in_code_sec and function found in wrong section.
	Update all calls.
This commit is contained in:
Alan Modra 2012-06-03 04:08:07 +00:00
parent 94bf648691
commit aef36ac1fc
4 changed files with 185 additions and 112 deletions

View file

@ -1,3 +1,16 @@
2012-06-03 Alan Modra <amodra@gmail.com>
PR binutils/13897
* elf.c (elf_find_function): Cache last function sym info.
(_bfd_elf_maybe_function_sym): Return function size, pass in
section of interest.
* elf-bfd.h (struct elf_backend_data <maybe_function_sym>): Likewise.
(_bfd_elf_maybe_function_sym): Likewise.
* elf64-ppc.c (ppc64_elf_maybe_function_sym): Likewise.
(opd_entry_value): Add in_code_sec param. Revert caching code.
Return -1 if in_code_sec and function found in wrong section.
Update all calls.
2012-06-01 Siddhesh Poyarekar <siddhesh@redhat.com>
* bfd-in.h (bfd_elf_bfd_from_remote_memory): Make LEN argument

View file

@ -1222,10 +1222,11 @@ struct elf_backend_data
/* Return TRUE if type is a function symbol type. */
bfd_boolean (*is_function_type) (unsigned int type);
/* Return TRUE if symbol may be a function. Set *CODE_SEC and *CODE_VAL
to the function's entry point. */
bfd_boolean (*maybe_function_sym) (const asymbol *sym,
asection **code_sec, bfd_vma *code_off);
/* If the ELF symbol SYM might be a function in SEC, return the
function size and set *CODE_OFF to the function's entry point,
otherwise return zero. */
bfd_size_type (*maybe_function_sym) (const asymbol *sym, asection *sec,
bfd_vma *code_off);
/* Used to handle bad SHF_LINK_ORDER input. */
bfd_error_handler_type link_order_error_handler;
@ -2207,8 +2208,8 @@ extern bfd_boolean _bfd_elf_map_sections_to_segments
extern bfd_boolean _bfd_elf_is_function_type (unsigned int);
extern bfd_boolean _bfd_elf_maybe_function_sym (const asymbol *,
asection **, bfd_vma *);
extern bfd_size_type _bfd_elf_maybe_function_sym (const asymbol *, asection *,
bfd_vma *);
extern int bfd_elf_get_default_section_type (flagword);

131
bfd/elf.c
View file

@ -7407,59 +7407,74 @@ elf_find_function (bfd *abfd,
const char **filename_ptr,
const char **functionname_ptr)
{
const char *filename;
asymbol *func, *file;
bfd_vma low_func;
asymbol **p;
/* ??? Given multiple file symbols, it is impossible to reliably
choose the right file name for global symbols. File symbols are
local symbols, and thus all file symbols must sort before any
global symbols. The ELF spec may be interpreted to say that a
file symbol must sort before other local symbols, but currently
ld -r doesn't do this. So, for ld -r output, it is possible to
make a better choice of file name for local symbols by ignoring
file symbols appearing after a given local symbol. */
enum { nothing_seen, symbol_seen, file_after_symbol_seen } state;
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
static asection *last_section;
static asymbol *func;
static const char *filename;
static bfd_size_type func_size;
if (symbols == NULL)
return FALSE;
filename = NULL;
func = NULL;
file = NULL;
low_func = 0;
state = nothing_seen;
for (p = symbols; *p != NULL; p++)
if (last_section != section
|| func == NULL
|| offset < func->value
|| offset >= func->value + func_size)
{
asymbol *sym = *p;
asection *code_sec;
bfd_vma code_off;
asymbol *file;
bfd_vma low_func;
asymbol **p;
/* ??? Given multiple file symbols, it is impossible to reliably
choose the right file name for global symbols. File symbols are
local symbols, and thus all file symbols must sort before any
global symbols. The ELF spec may be interpreted to say that a
file symbol must sort before other local symbols, but currently
ld -r doesn't do this. So, for ld -r output, it is possible to
make a better choice of file name for local symbols by ignoring
file symbols appearing after a given local symbol. */
enum { nothing_seen, symbol_seen, file_after_symbol_seen } state;
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
if ((sym->flags & BSF_FILE) != 0)
{
file = sym;
if (state == symbol_seen)
state = file_after_symbol_seen;
continue;
}
filename = NULL;
func = NULL;
file = NULL;
low_func = 0;
state = nothing_seen;
func_size = 0;
last_section = section;
if (bed->maybe_function_sym (sym, &code_sec, &code_off)
&& code_sec == section
&& code_off >= low_func
&& code_off <= offset)
for (p = symbols; *p != NULL; p++)
{
func = sym;
low_func = code_off;
filename = NULL;
if (file != NULL
&& ((sym->flags & BSF_LOCAL) != 0
|| state != file_after_symbol_seen))
filename = bfd_asymbol_name (file);
asymbol *sym = *p;
bfd_vma code_off;
bfd_size_type size;
if ((sym->flags & BSF_FILE) != 0)
{
file = sym;
if (state == symbol_seen)
state = file_after_symbol_seen;
continue;
}
size = bed->maybe_function_sym (sym, section, &code_off);
if (size != 0
&& code_off <= offset
&& (code_off > low_func
|| (code_off == low_func
&& size > func_size)))
{
func = sym;
func_size = size;
low_func = code_off;
filename = NULL;
if (file != NULL
&& ((sym->flags & BSF_LOCAL) != 0
|| state != file_after_symbol_seen))
filename = bfd_asymbol_name (file);
}
if (state == nothing_seen)
state = symbol_seen;
}
if (state == nothing_seen)
state = symbol_seen;
}
if (func == NULL)
@ -9714,18 +9729,26 @@ _bfd_elf_is_function_type (unsigned int type)
|| type == STT_GNU_IFUNC);
}
/* Return TRUE iff the ELF symbol SYM might be a function. Set *CODE_SEC
and *CODE_OFF to the function's entry point. */
/* If the ELF symbol SYM might be a function in SEC, return the
function size and set *CODE_OFF to the function's entry point,
otherwise return zero. */
bfd_boolean
_bfd_elf_maybe_function_sym (const asymbol *sym,
asection **code_sec, bfd_vma *code_off)
bfd_size_type
_bfd_elf_maybe_function_sym (const asymbol *sym, asection *sec,
bfd_vma *code_off)
{
if ((sym->flags & (BSF_SECTION_SYM | BSF_FILE | BSF_OBJECT
| BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC)) != 0)
return FALSE;
bfd_size_type size;
if ((sym->flags & (BSF_SECTION_SYM | BSF_FILE | BSF_OBJECT
| BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC)) != 0
|| sym->section != sec)
return 0;
*code_sec = sym->section;
*code_off = sym->value;
return TRUE;
size = 0;
if (!(sym->flags & BSF_SYNTHETIC))
size = ((elf_symbol_type *) sym)->internal_elf_sym.st_size;
if (size == 0)
size = 1;
return size;
}

View file

@ -55,7 +55,7 @@ static bfd_reloc_status_type ppc64_elf_toc64_reloc
static bfd_reloc_status_type ppc64_elf_unhandled_reloc
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
static bfd_vma opd_entry_value
(asection *, bfd_vma, asection **, bfd_vma *);
(asection *, bfd_vma, asection **, bfd_vma *, bfd_boolean);
#define TARGET_LITTLE_SYM bfd_elf64_powerpcle_vec
#define TARGET_LITTLE_NAME "elf64-powerpcle"
@ -2347,7 +2347,7 @@ ppc64_elf_branch_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
{
bfd_vma dest = opd_entry_value (symbol->section,
symbol->value + reloc_entry->addend,
NULL, NULL);
NULL, NULL, FALSE);
if (dest != (bfd_vma) -1)
reloc_entry->addend = dest - (symbol->value
+ symbol->section->output_section->vma
@ -5522,7 +5522,8 @@ static bfd_vma
opd_entry_value (asection *opd_sec,
bfd_vma offset,
asection **code_sec,
bfd_vma *code_off)
bfd_vma *code_off,
bfd_boolean in_code_sec)
{
bfd *opd_bfd = opd_sec->owner;
Elf_Internal_Rela *relocs;
@ -5533,43 +5534,39 @@ opd_entry_value (asection *opd_sec,
at a final linked executable with addr2line or somesuch. */
if (opd_sec->reloc_count == 0)
{
static asection *last_opd_sec, *last_code_sec;
static bfd_vma last_opd_off, last_entry_vma;
static bfd_boolean sec_search_done;
char buf[8];
if (last_opd_sec != opd_sec
|| last_opd_off != offset
|| (code_sec != NULL && !sec_search_done))
if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8))
return (bfd_vma) -1;
val = bfd_get_64 (opd_bfd, buf);
if (code_sec != NULL)
{
char buf[8];
asection *sec, *likely = NULL;
if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8))
return (bfd_vma) -1;
last_opd_sec = opd_sec;
last_opd_off = offset;
last_entry_vma = bfd_get_64 (opd_bfd, buf);
sec_search_done = FALSE;
if (code_sec != NULL)
if (in_code_sec)
{
asection *sec;
sec_search_done = TRUE;
last_code_sec = NULL;
for (sec = opd_bfd->sections; sec != NULL; sec = sec->next)
if (sec->vma <= last_entry_vma
&& (sec->flags & SEC_LOAD) != 0
&& (sec->flags & SEC_ALLOC) != 0)
last_code_sec = sec;
sec = *code_sec;
if (sec->vma <= val
&& val < sec->vma + sec->size)
likely = sec;
else
val = -1;
}
else
for (sec = opd_bfd->sections; sec != NULL; sec = sec->next)
if (sec->vma <= val
&& (sec->flags & SEC_LOAD) != 0
&& (sec->flags & SEC_ALLOC) != 0)
likely = sec;
if (likely != NULL)
{
*code_sec = likely;
if (code_off != NULL)
*code_off = val - likely->vma;
}
}
if (code_sec != NULL && last_code_sec != NULL)
{
*code_sec = last_code_sec;
if (code_off != NULL)
*code_off = last_entry_vma - last_code_sec->vma;
}
return last_entry_vma;
return val;
}
BFD_ASSERT (is_ppc64_elf (opd_bfd));
@ -5640,7 +5637,12 @@ opd_entry_value (asection *opd_sec,
if (code_off != NULL)
*code_off = val;
if (code_sec != NULL)
*code_sec = sec;
{
if (in_code_sec && *code_sec != sec)
return -1;
else
*code_sec = sec;
}
if (sec != NULL && sec->output_section != NULL)
val += sec->output_section->vma + sec->output_offset;
}
@ -5651,20 +5653,53 @@ opd_entry_value (asection *opd_sec,
return val;
}
/* Return TRUE iff the ELF symbol SYM might be a function. Set *CODE_SEC
and *CODE_OFF to the function's entry point. */
/* If the ELF symbol SYM might be a function in SEC, return the
function size and set *CODE_OFF to the function's entry point,
otherwise return zero. */
static bfd_boolean
ppc64_elf_maybe_function_sym (const asymbol *sym,
asection **code_sec, bfd_vma *code_off)
static bfd_size_type
ppc64_elf_maybe_function_sym (const asymbol *sym, asection *sec,
bfd_vma *code_off)
{
if (_bfd_elf_maybe_function_sym (sym, code_sec, code_off))
bfd_size_type size;
if ((sym->flags & (BSF_SECTION_SYM | BSF_FILE | BSF_OBJECT
| BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC)) != 0)
return 0;
size = 0;
if (!(sym->flags & BSF_SYNTHETIC))
size = ((elf_symbol_type *) sym)->internal_elf_sym.st_size;
if (strcmp (sym->section->name, ".opd") == 0)
{
if (strcmp (sym->section->name, ".opd") == 0)
opd_entry_value (sym->section, sym->value, code_sec, code_off);
return TRUE;
if (opd_entry_value (sym->section, sym->value,
&sec, code_off, TRUE) == (bfd_vma) -1)
return 0;
/* An old ABI binary with dot-syms has a size of 24 on the .opd
symbol. This size has nothing to do with the code size of the
function, which is what we're supposed to return, but the
code size isn't available without looking up the dot-sym.
However, doing that would be a waste of time particularly
since elf_find_function will look at the dot-sym anyway.
Now, elf_find_function will keep the largest size of any
function sym found at the code address of interest, so return
1 here to avoid it incorrectly caching a larger function size
for a small function. This does mean we return the wrong
size for a new-ABI function of size 24, but all that does is
disable caching for such functions. */
if (size == 24)
size = 1;
}
return FALSE;
else
{
if (sym->section != sec)
return 0;
*code_off = sym->value;
}
if (size == 0)
size = 1;
return size;
}
/* Return true if symbol is defined in a regular object file. */
@ -5744,7 +5779,7 @@ ppc64_elf_gc_keep (struct bfd_link_info *info)
else if (get_opd_info (eh->elf.root.u.def.section) != NULL
&& opd_entry_value (eh->elf.root.u.def.section,
eh->elf.root.u.def.value,
&sec, NULL) != (bfd_vma) -1)
&sec, NULL, FALSE) != (bfd_vma) -1)
sec->flags |= SEC_KEEP;
sec = eh->elf.root.u.def.section;
@ -5795,7 +5830,7 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf)
else if (get_opd_info (eh->elf.root.u.def.section) != NULL
&& opd_entry_value (eh->elf.root.u.def.section,
eh->elf.root.u.def.value,
&code_sec, NULL) != (bfd_vma) -1)
&code_sec, NULL, FALSE) != (bfd_vma) -1)
code_sec->flags |= SEC_KEEP;
}
@ -5855,7 +5890,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
else if (get_opd_info (eh->elf.root.u.def.section) != NULL
&& opd_entry_value (eh->elf.root.u.def.section,
eh->elf.root.u.def.value,
&rsec, NULL) != (bfd_vma) -1)
&rsec, NULL, FALSE) != (bfd_vma) -1)
eh->elf.root.u.def.section->gc_mark = 1;
else
rsec = h->root.u.def.section;
@ -6324,7 +6359,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
&& opd_entry_value (fdh->elf.root.u.def.section,
fdh->elf.root.u.def.value,
&fh->elf.root.u.def.section,
&fh->elf.root.u.def.value) != (bfd_vma) -1)
&fh->elf.root.u.def.value, FALSE) != (bfd_vma) -1)
{
fh->elf.root.type = fdh->elf.root.type;
fh->elf.forced_local = 1;
@ -10915,7 +10950,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
sym_value += adjust;
}
dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL);
dest = opd_entry_value (sym_sec, sym_value,
&sym_sec, NULL, FALSE);
if (dest == (bfd_vma) -1)
continue;
}
@ -11490,7 +11526,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
sym_value += adjust;
}
dest = opd_entry_value (sym_sec, sym_value,
&code_sec, &code_value);
&code_sec, &code_value, FALSE);
if (dest != (bfd_vma) -1)
{
destination = dest;
@ -12904,7 +12940,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
bfd_vma off = (relocation + addend
- sec->output_section->vma
- sec->output_offset);
bfd_vma dest = opd_entry_value (sec, off, NULL, NULL);
bfd_vma dest = opd_entry_value (sec, off, NULL, NULL, FALSE);
if (dest != (bfd_vma) -1)
{
relocation = dest;