blob: 56f4988b52b1d9f7a291560e84b0a811c63f9fbf [file] [log] [blame]
/* Load the given section: NULL on error. */
static void *PERBIT(load_section)(ElfPERBIT(Ehdr) *hdr,
const char *secname,
unsigned long *size,
int conv)
{
ElfPERBIT(Shdr) *sechdrs;
unsigned int i;
char *secnames;
/* Grab section headers and strings so we can tell who is who */
sechdrs = (void *)hdr + END(hdr->e_shoff, conv);
secnames = (void *)hdr
+ END(sechdrs[END(hdr->e_shstrndx, conv)].sh_offset, conv);
/* Find the section they want */
for (i = 1; i < END(hdr->e_shnum, conv); i++) {
if (streq(secnames+END(sechdrs[i].sh_name, conv), secname)) {
*size = END(sechdrs[i].sh_size, conv);
return (void *)hdr + END(sechdrs[i].sh_offset, conv);
}
}
*size = 0;
return NULL;
}
static void PERBIT(load_symbols)(struct module *module)
{
struct PERBIT(kernel_symbol) *ksyms;
char *ksymstrings;
unsigned long i, size;
/* New-style: strings are in this section. */
ksymstrings = PERBIT(load_section)(module->data, "__ksymtab_strings",
&size, module->conv);
if (ksymstrings) {
unsigned int i = 0;
for (;;) {
/* Skip any zero padding. */
while (!ksymstrings[i])
if (++i >= size)
return;
add_symbol(ksymstrings+i, module);
i += strlen(ksymstrings+i);
}
/* GPL symbols too */
ksymstrings = PERBIT(load_section)(module->data,
"__ksymtab_strings_gpl",
&size, module->conv);
for (;;) {
/* Skip any zero padding. */
while (!ksymstrings[i])
if (++i >= size)
return;
add_symbol(ksymstrings+i, module);
i += strlen(ksymstrings+i);
}
return;
}
/* Old-style. */
ksyms = PERBIT(load_section)(module->data, "__ksymtab", &size,
module->conv);
for (i = 0; i < size / sizeof(struct PERBIT(kernel_symbol)); i++)
add_symbol(ksyms[i].name, module);
ksyms = PERBIT(load_section)(module->data, "__gpl_ksymtab", &size,
module->conv);
for (i = 0; i < size / sizeof(struct PERBIT(kernel_symbol)); i++)
add_symbol(ksyms[i].name, module);
}
static char *PERBIT(get_aliases)(struct module *module, unsigned long *size)
{
return PERBIT(load_section)(module->data, ".modalias", size,
module->conv);
}
static char *PERBIT(get_modinfo)(struct module *module, unsigned long *size)
{
return PERBIT(load_section)(module->data, ".modinfo", size,
module->conv);
}
#ifndef STT_REGISTER
#define STT_REGISTER 13 /* Global register reserved to app. */
#endif
/* Calculate the dependencies for this module */
static void PERBIT(calculate_deps)(struct module *module, int verbose)
{
unsigned int i;
unsigned long size;
char *strings;
ElfPERBIT(Sym) *syms;
ElfPERBIT(Ehdr) *hdr;
int handle_register_symbols;
strings = PERBIT(load_section)(module->data, ".strtab", &size,
module->conv);
syms = PERBIT(load_section)(module->data, ".symtab", &size,
module->conv);
module->num_deps = 0;
module->deps = NULL;
if (!strings || !syms) {
warn("Couldn't find symtab and strtab in module %s\n",
module->pathname);
return;
}
hdr = module->data;
handle_register_symbols = 0;
if (END(hdr->e_machine, module->conv) == EM_SPARC ||
END(hdr->e_machine, module->conv) == EM_SPARCV9)
handle_register_symbols = 1;
for (i = 1; i < size / sizeof(syms[0]); i++) {
if (END(syms[i].st_shndx, module->conv) == SHN_UNDEF) {
/* Look for symbol */
const char *name;
struct module *owner;
int weak;
name = strings + END(syms[i].st_name, module->conv);
/* Not really undefined: sparc gcc 3.3 creates
U references when you have global asm
variables, to avoid anyone else misusing
them. */
if (handle_register_symbols
&& (ELFPERBIT(ST_TYPE)(END(syms[i].st_info,
module->conv))
== STT_REGISTER))
continue;
weak = (ELFPERBIT(ST_BIND)(END(syms[i].st_info,
module->conv))
== STB_WEAK);
owner = find_symbol(name, module->pathname, weak);
if (owner) {
if (verbose)
printf("%s needs \"%s\": %s\n",
module->pathname, name,
owner->pathname);
add_dep(module, owner);
}
}
}
}
static void *PERBIT(deref_sym)(ElfPERBIT(Ehdr) *hdr,
ElfPERBIT(Shdr) *sechdrs,
ElfPERBIT(Sym) *sym,
unsigned int *secsize,
int conv)
{
/* In BSS? Happens for empty device tables on
* recent GCC versions. */
if (END(sechdrs[END(sym->st_shndx, conv)].sh_type,conv) == SHT_NOBITS)
return NULL;
if (secsize)
*secsize = END(sym->st_size, conv);
return (void *)hdr
+ END(sechdrs[END(sym->st_shndx, conv)].sh_offset, conv)
+ END(sym->st_value, conv);
}
/* FIXME: Check size, unless we end up using aliases anyway --RR */
static void PERBIT(fetch_tables)(struct module *module)
{
unsigned int i;
unsigned long size;
char *strings;
ElfPERBIT(Ehdr) *hdr;
ElfPERBIT(Sym) *syms;
ElfPERBIT(Shdr) *sechdrs;
hdr = module->data;
sechdrs = (void *)hdr + END(hdr->e_shoff, module->conv);
strings = PERBIT(load_section)(hdr, ".strtab", &size, module->conv);
syms = PERBIT(load_section)(hdr, ".symtab", &size, module->conv);
/* Don't warn again: we already have above */
if (!strings || !syms)
return;
module->pci_table = NULL;
module->usb_table = NULL;
module->ccw_table = NULL;
module->ieee1394_table = NULL;
module->pnp_table = NULL;
module->pnp_card_table = NULL;
module->input_table = NULL;
module->serio_table = NULL;
module->of_table = NULL;
for (i = 0; i < size / sizeof(syms[0]); i++) {
char *name = strings + END(syms[i].st_name, module->conv);
if (!module->pci_table && streq(name, "__mod_pci_device_table")) {
module->pci_size = PERBIT(PCI_DEVICE_SIZE);
module->pci_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
else if (!module->usb_table && streq(name, "__mod_usb_device_table")) {
module->usb_size = PERBIT(USB_DEVICE_SIZE);
module->usb_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
else if (!module->ccw_table && streq(name, "__mod_ccw_device_table")) {
module->ccw_size = PERBIT(CCW_DEVICE_SIZE);
module->ccw_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
else if (!module->ieee1394_table && streq(name, "__mod_ieee1394_device_table")) {
module->ieee1394_size = PERBIT(IEEE1394_DEVICE_SIZE);
module->ieee1394_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
else if (!module->pnp_table && streq(name, "__mod_pnp_device_table")) {
module->pnp_size = PERBIT(PNP_DEVICE_SIZE);
module->pnp_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
else if (!module->pnp_card_table && streq(name, "__mod_pnp_card_device_table")) {
module->pnp_card_size = PERBIT(PNP_CARD_DEVICE_SIZE);
module->pnp_card_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
module->pnp_card_offset = PERBIT(PNP_CARD_DEVICE_OFFSET);
}
else if (!module->input_table && streq(name, "__mod_input_device_table")) {
module->input_size = PERBIT(INPUT_DEVICE_SIZE);
module->input_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
&module->input_table_size,
module->conv);
}
else if (!module->serio_table && streq(name, "__mod_serio_device_table")) {
module->serio_size = PERBIT(SERIO_DEVICE_SIZE);
module->serio_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
else if (!module->of_table && streq(name, "__mod_of_device_table")) {
module->of_size = PERBIT(OF_DEVICE_SIZE);
module->of_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i],
NULL, module->conv);
}
}
}
struct module_ops PERBIT(mod_ops) = {
.load_symbols = PERBIT(load_symbols),
.calculate_deps = PERBIT(calculate_deps),
.fetch_tables = PERBIT(fetch_tables),
.get_aliases = PERBIT(get_aliases),
.get_modinfo = PERBIT(get_modinfo),
};