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
This commit is contained in:
Conrad Meyer 2019-05-27 17:33:20 +00:00
parent fcd0c06eee
commit 9c1fa7a429

View File

@ -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);