From: Jan Beulich Date: Mon, 14 Apr 2025 12:22:49 +0000 (+0200) Subject: bfd/COFF: propagate function size when copying/linking ELF objects X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=06d3221915577d421c4f3a1a9ee559d88e95acc2;p=thirdparty%2Fbinutils-gdb.git bfd/COFF: propagate function size when copying/linking ELF objects While COFF, unlike ELF, doesn't have a generic way to express symbol size, there is a means to do so for functions. When inputs are ELF, propagate function sizes, including the fact that a symbol denotes a function, to the output's symbol table. Note that this requires hackery (cross-object-format processing) in two places - when linking, global symbols are entered into a global hash table, and hence relevant information needs to be updated there in that case, while otherwise the original symbol structures can be consulted. For the setting of ->u.syment.n_type the later writing of the field to literal 0 needs to be dropped from coff_write_alien_symbol(). It was redundant anyway with an earlier write of the field using C_NUL. --- diff --git a/bfd/coffgen.c b/bfd/coffgen.c index f87e54f9722..ab4d790ad30 100644 --- a/bfd/coffgen.c +++ b/bfd/coffgen.c @@ -42,6 +42,7 @@ #include "libbfd.h" #include "coff/internal.h" #include "libcoff.h" +#include "elf-bfd.h" #include "hashtab.h" /* Extract a long section name at STRINDEX and copy it to the bfd objstack. @@ -1270,9 +1271,24 @@ coff_write_alien_symbol (bfd *abfd, if (c != (coff_symbol_type *) NULL) native->u.syment.n_flags = bfd_asymbol_bfd (&c->symbol)->flags; } + + const elf_symbol_type *elfsym = elf_symbol_from (symbol); + if (elfsym + && (symbol->flags & BSF_FUNCTION) + && elfsym->internal_elf_sym.st_size) + { + /* coff_data (abfd)->local_n_btshft is what ought to be used here, + just that it's set only when reading in COFF objects. */ + native->u.syment.n_type = DT_FCN << 4; + native->u.syment.n_numaux = 1; + native[1].u.auxent.x_sym.x_misc.x_fsize + = elfsym->internal_elf_sym.st_size; + /* FIXME .u.auxent.x_sym.x_fcnary.x_fcn.x_endndx would better also + be set, which would require updating the field once the next + function is seen. */ + } } - native->u.syment.n_type = 0; if (symbol->flags & BSF_FILE) native->u.syment.n_sclass = C_FILE; else if (symbol->flags & BSF_LOCAL) diff --git a/bfd/cofflink.c b/bfd/cofflink.c index 501731bd811..38278e230da 100644 --- a/bfd/cofflink.c +++ b/bfd/cofflink.c @@ -27,6 +27,7 @@ #include "libbfd.h" #include "coff/internal.h" #include "libcoff.h" +#include "elf-bfd.h" #include "safe-ctype.h" static bool coff_link_add_object_symbols (bfd *, struct bfd_link_info *); @@ -931,14 +932,52 @@ _bfd_coff_final_link (bfd *abfd, bfd_vma written = 0; bool rewrite = false; - if (! (sym->flags & BSF_LOCAL) - || (sym->flags & (BSF_SECTION_SYM | BSF_DEBUGGING_RELOC - | BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC - | BSF_SYNTHETIC)) + if ((sym->flags & (BSF_SECTION_SYM | BSF_DEBUGGING_RELOC + | BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC + | BSF_SYNTHETIC)) || ((sym->flags & BSF_DEBUGGING) && ! (sym->flags & BSF_FILE))) continue; + if (! (sym->flags & BSF_LOCAL)) + { + /* For ELF symbols try to represent their function-ness and + size, if available. */ + if (! (sym->flags & BSF_FUNCTION)) + continue; + + const elf_symbol_type *elfsym = elf_symbol_from (sym); + if (!elfsym) + continue; + + struct coff_link_hash_entry *hent + = (struct coff_link_hash_entry *) bfd_hash_lookup + (&info->hash->table, bfd_asymbol_name (sym), + false, false); + if (!hent) + continue; + + /* coff_data (abfd)->local_n_btshft is what ought to be used + here, just that it's set only when reading in COFF + objects. */ + hent->type = DT_FCN << 4; + if (!elfsym->internal_elf_sym.st_size) + continue; + + hent->aux = bfd_zalloc (abfd, sizeof (*hent->aux)); + if (!hent->aux) + continue; + + hent->numaux = 1; + hent->aux->x_sym.x_misc.x_fsize + = elfsym->internal_elf_sym.st_size; + /* FIXME ->x_sym.x_fcnary.x_fcn.x_endndx would better + also be set, yet that would likely need to happen + elsewhere anyway. */ + + continue; + } + /* See if we are discarding symbols with this name. */ if ((flaginfo.info->strip == strip_some && (bfd_hash_lookup (flaginfo.info->keep_hash,