Author: cem
Date: Mon May 27 17:33:20 2019
New Revision: 348309
URL: https://svnweb.freebsd.org/changeset/base/348309

Log:
  kldxref(8): Sort MDT_MODULE info first in linker.hints output
  
  MDT_MODULE info is required to be ordered before any other MDT metadata for
  a given kld because it serves as an implicit record boundary between
  distinct klds for linker.hints consumers.  kldxref(8) has previously relied
  on the assumption that MDT_MODULE was ordered relative to other module
  metadata in kld objects by source code ordering.
  
  However, C does not require implementations to emit file scope objects in
  any particular order, and it seems that GCC 6.4.0 and/or binutils 2.32 ld
  may reorder emitted objects with respect to source code ordering.
  
  So: just take two passes over a given .ko's module metadata, scanning for
  the MDT_MODULE on the first pass and the other metadata on subsequent
  passes.  It's not super expensive and not exactly a performance-critical
  piece of code.  This ensures MDT_MODULE is always ordered before
  MDT_PNP_INFO and other MDTs, regardless of compiler/linker movement.  As a
  fringe benefit, it removes the requirement that care be taken to always
  order MODULE_PNP_INFO after DRIVER_MODULE in source code.
  
  Reviewed by:  emaste, imp
  Differential Revision:        https://reviews.freebsd.org/D20405

Modified:
  head/usr.sbin/kldxref/kldxref.c

Modified: head/usr.sbin/kldxref/kldxref.c
==============================================================================
--- head/usr.sbin/kldxref/kldxref.c     Mon May 27 17:14:46 2019        
(r348308)
+++ head/usr.sbin/kldxref/kldxref.c     Mon May 27 17:33:20 2019        
(r348309)
@@ -549,9 +549,9 @@ read_kld(char *filename, char *kldname)
 {
        struct mod_metadata md;
        struct elf_file ef;
-       void **p, **orgp;
+       void **p;
        int error, eftype;
-       long start, finish, entries;
+       long start, finish, entries, i;
        char cval[MAXMODNAME + 1];
 
        if (verbose || dflag)
@@ -575,18 +575,53 @@ read_kld(char *filename, char *kldname)
                    &entries));
                check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries,
                    (void *)&p));
-               orgp = p;
-               while(entries--) {
-                       check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md),
+               /*
+                * Do a first pass to find MDT_MODULE.  It is required to be
+                * ordered first in the output linker.hints stream because it
+                * serves as an implicit record boundary between distinct klds
+                * in the stream.  Other MDTs only make sense in the context of
+                * a specific MDT_MODULE.
+                *
+                * Some compilers (e.g., GCC 6.4.0 xtoolchain) or binutils
+                * (e.g., GNU binutils 2.32 objcopy/ld.bfd) can reorder
+                * MODULE_METADATA set entries relative to the source ordering.
+                * This is permitted by the C standard; memory layout of
+                * file-scope objects is left implementation-defined.  There is
+                * no requirement that source code ordering is retained.
+                *
+                * Handle that here by taking two passes to ensure MDT_MODULE
+                * records are emitted to linker.hints before other MDT records
+                * in the same kld.
+                */
+               for (i = 0; i < entries; i++) {
+                       check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md),
                            &md));
-                       p++;
                        check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval,
                            sizeof(cval), cval));
-                       parse_entry(&md, cval, &ef, kldname);
+                       if (md.md_type == MDT_MODULE) {
+                               parse_entry(&md, cval, &ef, kldname);
+                               break;
+                       }
                }
+               if (error != 0) {
+                       warnc(error, "error while reading %s", filename);
+                       break;
+               }
+
+               /*
+                * Second pass for all !MDT_MODULE entries.
+                */
+               for (i = 0; i < entries; i++) {
+                       check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md),
+                           &md));
+                       check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval,
+                           sizeof(cval), cval));
+                       if (md.md_type != MDT_MODULE)
+                               parse_entry(&md, cval, &ef, kldname);
+               }
                if (error != 0)
                        warnc(error, "error while reading %s", filename);
-               free(orgp);
+               free(p);
        } while(0);
        EF_CLOSE(&ef);
        return (error);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to