Kernel linker and undefined references in KLD
Kostik Belousov
kostikbel at gmail.com
Thu Jul 15 15:31:33 UTC 2010
On Thu, Jul 15, 2010 at 05:32:35PM +0300, Kostik Belousov wrote:
> On Thu, Jul 15, 2010 at 06:07:36PM +0400, Dmitry Krivenok wrote:
> > Hello Hackers,
> >
> > I have a question about kernel linker.
> > Please take a look at an example of simple module:
> >
> > ############################################################################
> > #include <sys/param.h>
> > #include <sys/module.h>
> > #include <sys/kernel.h>
> > #include <sys/systm.h>
> > #include <sys/proc.h>
> > #include <sys/sched.h>
> > #include <sys/pcpu.h>
> >
> > /* DECLARING A FUNCTION JUST TO AVOID WARNING WHICH IS TREAT AS ERROR
> > BY DEFAULT */
> > void seltdinit(struct thread* t);
> >
> > static int event_handler(struct module *module, int event, void *arg)
> > {
> > int e = 0;
> > switch (event)
> > {
> > case MOD_LOAD:
> > /* CALLING A FUNCTION DECLARED AS STATIC IN kern/sys_generic.c */
> > seltdinit(curthread);
> > break;
> > case MOD_QUIESCE:
> > break;
> > case MOD_UNLOAD:
> > break;
> > case MOD_SHUTDOWN:
> > break;
> > default:
> > e = EOPNOTSUPP;
> > break;
> > }
> >
> > return(e);
> > };
> > ############################################################################
> >
> > As you can see, this module calls seltdinit function declared as
> > _static_ in kern/sys_generic.c file.
> > I added a declaration of seltdinit function w/o static just to avoid
> > compilation error since -Werror is used
> > by default in FreeBSD.
> >
> > Then I successfully built the module:
> >
> > $ make
> > Warning: Object directory not changed from original
> > /usr/home/krived/work/freebsd/trouble
> > cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE
> > -nostdinc -I. -I@ -I@/contrib/altq -finline-limit=8000 --param
> > inline-unit-growth=100 --param large-function-growth=1000 -fno-common
> > -fno-omit-frame-pointer -mcmodel=kernel -mno-red-zone -mfpmath=387
> > -mno-sse -mno-sse2 -mno-sse3 -mno-mmx -mno-3dnow -msoft-float
> > -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector
> > -std=iso9899:1999 -fstack-protector -Wall -Wredundant-decls
> > -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes
> > -Wpointer-arith -Winline -Wcast-qual -Wundef -Wno-pointer-sign
> > -fformat-extensions -c trouble.c
> > ld -d -warn-common -r -d -o trouble.ko trouble.o
> > :> export_syms
> > awk -f /sys/conf/kmod_syms.awk trouble.ko export_syms | xargs -J%
> > objcopy % trouble.ko
> > objcopy --strip-debug trouble.ko
> > $
> >
> > As expected, seltdinit symbol is undefined in trouble.ko file:
> > $ nm trouble.ko | grep seltdinit
> > U seltdinit
> > $
> > and is local in kernel binary file:
> > $ nm /boot/kernel/kernel | grep seltdinit
> > ffffffff805fda50 t seltdinit
> > $
> >
> > I expected to get something like "undefined reference to seltdinit"
> > error, but the module was loaded w/o any errors:
> >
> > $ sudo make load
> > /sbin/kldload -v /usr/home/krived/work/freebsd/trouble/trouble.ko
> > Loaded /usr/home/krived/work/freebsd/trouble/trouble.ko, id=2
> > $ kldstat | grep trouble
> > 2 1 0xffffffff81212000 126 trouble.ko
> > $ sudo make unload
> > /sbin/kldunload -v trouble.ko
> > Unloading trouble.ko, id=2
> > $
> >
> > Could you please explain why the linker (or whatever is loading
> > modules in FreeBSD) doesn't complain?
> >
> > For example, ld tool complains for the similar user-space example:
> >
> > ############################################################################
> > $ cat seltdinit.c
> > static void seltdinit(void* td)
> > {
> > return;
> > }
> > $ cat main.c
> > void seltdinit(void* td);
> >
> > int main()
> > {
> > seltdinit(0);
> > return 0;
> > }
> > $ gcc -Wall -c seltdinit.c
> > seltdinit.c:2: warning: 'seltdinit' defined but not used
> > $ gcc -Wall -c main.c
> > $ gcc seltdinit.o main.o -o sel
> > main.o: In function `main':
> > main.c:(.text+0xa): undefined reference to `seltdinit'
> > collect2: ld returned 1 exit status
> > $
> > ############################################################################
> >
> > Thanks in advance!
> The kernel linker ignores weak attribute of the symbol, as you see.
> There is more bugs in this department, in regard of the list of
> exported symbols from the modules.
>
> I have a patch that fixes the issues, but I am leery to commit it, since
> the fix effectively breaks significant set of the modules.
Ok, below is the patch. I updated it to the latest HEAD, but did not
verified even the build.
diff --git a/sys/conf/kmod_syms.awk b/sys/conf/kmod_syms.awk
index 677d813..98940c6 100644
--- a/sys/conf/kmod_syms.awk
+++ b/sys/conf/kmod_syms.awk
@@ -12,7 +12,10 @@ BEGIN {
# De-list symbols from the export list.
{
- delete syms[$0]
+ split($0, exports)
+ for (member in exports) {
+ delete syms[$member]
+ }
}
# Strip commons, make everything else local.
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index dd29302..f3da962 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -855,7 +855,7 @@ linker_debug_lookup(const char *symstr, c_linker_sym_t *sym)
linker_file_t lf;
TAILQ_FOREACH(lf, &linker_files, link) {
- if (LINKER_LOOKUP_SYMBOL(lf, symstr, sym) == 0)
+ if (LINKER_LOOKUP_DEBUG_SYMBOL(lf, symstr, sym) == 0)
return (0);
}
return (ENOENT);
@@ -899,7 +899,7 @@ linker_debug_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
linker_file_t lf;
TAILQ_FOREACH(lf, &linker_files, link) {
- if (LINKER_SYMBOL_VALUES(lf, sym, symval) == 0)
+ if (LINKER_DEBUG_SYMBOL_VALUES(lf, sym, symval) == 0)
return (0);
}
return (ENOENT);
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
index b389ace..f494fa5 100644
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -127,25 +127,28 @@ typedef struct elf_file {
static int link_elf_link_common_finish(linker_file_t);
static int link_elf_link_preload(linker_class_t cls,
- const char*, linker_file_t*);
+ const char*, linker_file_t*);
static int link_elf_link_preload_finish(linker_file_t);
static int link_elf_load_file(linker_class_t, const char*, linker_file_t*);
static int link_elf_lookup_symbol(linker_file_t, const char*,
- c_linker_sym_t*);
-static int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*);
+ c_linker_sym_t*);
+static int link_elf_lookup_debug_symbol(linker_file_t, const char*,
+ c_linker_sym_t*);
+static int link_elf_symbol_values(linker_file_t, c_linker_sym_t,
+ linker_symval_t*);
+static int link_elf_debug_symbol_values(linker_file_t, c_linker_sym_t,
+ linker_symval_t*);
static int link_elf_search_symbol(linker_file_t, caddr_t value,
- c_linker_sym_t* sym, long* diffp);
+ c_linker_sym_t* sym, long* diffp);
static void link_elf_unload_file(linker_file_t);
static void link_elf_unload_preload(linker_file_t);
static int link_elf_lookup_set(linker_file_t, const char *,
void ***, void ***, int *);
static int link_elf_each_function_name(linker_file_t,
- int (*)(const char *, void *),
- void *);
+ int (*)(const char *, void *), void *);
static int link_elf_each_function_nameval(linker_file_t,
- linker_function_nameval_callback_t,
- void *);
+ linker_function_nameval_callback_t, void *);
static void link_elf_reloc_local(linker_file_t);
static long link_elf_symtab_get(linker_file_t, const Elf_Sym **);
static long link_elf_strtab_get(linker_file_t, caddr_t *);
@@ -153,7 +156,9 @@ static Elf_Addr elf_lookup(linker_file_t lf, Elf_Size symidx, int deps);
static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol),
+ KOBJMETHOD(linker_lookup_debug_symbol, link_elf_lookup_debug_symbol),
KOBJMETHOD(linker_symbol_values, link_elf_symbol_values),
+ KOBJMETHOD(linker_debug_symbol_values, link_elf_debug_symbol_values),
KOBJMETHOD(linker_search_symbol, link_elf_search_symbol),
KOBJMETHOD(linker_unload, link_elf_unload_file),
KOBJMETHOD(linker_load_file, link_elf_load_file),
@@ -1160,14 +1165,14 @@ elf_hash(const char *name)
}
static int
-link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
+link_elf_lookup_symbol1(linker_file_t lf, const char* name, c_linker_sym_t* sym,
+ int see_local)
{
elf_file_t ef = (elf_file_t) lf;
unsigned long symnum;
const Elf_Sym* symp;
const char *strp;
unsigned long hash;
- int i;
/* If we don't have a hash, bail. */
if (ef->buckets == NULL || ef->nbuckets == 0) {
@@ -1194,60 +1199,101 @@ link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
strp = ef->strtab + symp->st_name;
if (strcmp(name, strp) == 0) {
- if (symp->st_shndx != SHN_UNDEF ||
- (symp->st_value != 0 &&
- ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
- *sym = (c_linker_sym_t) symp;
- return 0;
- } else
- return ENOENT;
+ if (symp->st_shndx != SHN_UNDEF ||
+ (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
+ if (see_local || ELF_ST_BIND(symp->st_info) != STB_LOCAL) {
+ *sym = (c_linker_sym_t) symp;
+ return (0);
+ } else
+ return (ENOENT);
+ } else
+ return (ENOENT);
}
symnum = ef->chains[symnum];
}
- /* If we have not found it, look at the full table (if loaded) */
- if (ef->symtab == ef->ddbsymtab)
- return ENOENT;
+ return (ENOENT);
+}
- /* Exhaustive search */
- for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
- strp = ef->ddbstrtab + symp->st_name;
- if (strcmp(name, strp) == 0) {
- if (symp->st_shndx != SHN_UNDEF ||
- (symp->st_value != 0 &&
- ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
- *sym = (c_linker_sym_t) symp;
- return 0;
- } else
- return ENOENT;
+static int
+link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
+{
+
+ return (link_elf_lookup_symbol1(lf, name, sym, 0));
+}
+
+static int
+link_elf_lookup_debug_symbol(linker_file_t lf, const char *name,
+ c_linker_sym_t *sym)
+{
+ elf_file_t ef = (elf_file_t) lf;
+ const Elf_Sym* symp;
+ const char *strp;
+ int i;
+
+ if (link_elf_lookup_symbol1(lf, name, sym, 1) == 0)
+ return (0);
+
+ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
+ strp = ef->ddbstrtab + symp->st_name;
+ if (strcmp(name, strp) == 0) {
+ if (symp->st_shndx != SHN_UNDEF ||
+ (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
+ *sym = (c_linker_sym_t) symp;
+ return (0);
+ } else
+ return (ENOENT);
+ }
}
- }
- return ENOENT;
+ return (ENOENT);
}
static int
-link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval)
+link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
+ linker_symval_t *symval, int see_local)
{
elf_file_t ef = (elf_file_t) lf;
const Elf_Sym* es = (const Elf_Sym*) sym;
if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) {
- symval->name = ef->strtab + es->st_name;
- symval->value = (caddr_t) ef->address + es->st_value;
- symval->size = es->st_size;
- return 0;
+ if (!see_local && ELF_ST_BIND(es->st_info) == STB_LOCAL)
+ return (ENOENT);
+ symval->name = ef->strtab + es->st_name;
+ symval->value = (caddr_t) ef->address + es->st_value;
+ symval->size = es->st_size;
+ return (0);
}
- if (ef->symtab == ef->ddbsymtab)
- return ENOENT;
+ return (ENOENT);
+}
+
+static int
+link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+ linker_symval_t *symval)
+{
+
+ return (link_elf_symbol_values1(lf, sym, symval, 0));
+}
+
+static int
+link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+ linker_symval_t* symval)
+{
+ elf_file_t ef = (elf_file_t) lf;
+ const Elf_Sym* es = (const Elf_Sym*) sym;
+
+ if (link_elf_symbol_values1(lf, sym, symval, 1) == 0)
+ return (0);
if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
- symval->name = ef->ddbstrtab + es->st_name;
- symval->value = (caddr_t) ef->address + es->st_value;
- symval->size = es->st_size;
- return 0;
+ symval->name = ef->ddbstrtab + es->st_name;
+ symval->value = (caddr_t) ef->address + es->st_value;
+ symval->size = es->st_size;
+ return (0);
}
- return ENOENT;
+ return (ENOENT);
}
static int
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
index a337fd0..ae8ea04 100644
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -127,8 +127,12 @@ static int link_elf_link_preload_finish(linker_file_t);
static int link_elf_load_file(linker_class_t, const char *, linker_file_t *);
static int link_elf_lookup_symbol(linker_file_t, const char *,
c_linker_sym_t *);
+static int link_elf_lookup_debug_symbol(linker_file_t, const char *,
+ c_linker_sym_t *);
static int link_elf_symbol_values(linker_file_t, c_linker_sym_t,
linker_symval_t *);
+static int link_elf_debug_symbol_values(linker_file_t, c_linker_sym_t,
+ linker_symval_t *);
static int link_elf_search_symbol(linker_file_t, caddr_t value,
c_linker_sym_t *sym, long *diffp);
@@ -148,7 +152,9 @@ static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps);
static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol),
+ KOBJMETHOD(linker_lookup_debug_symbol, link_elf_lookup_debug_symbol),
KOBJMETHOD(linker_symbol_values, link_elf_symbol_values),
+ KOBJMETHOD(linker_debug_symbol_values, link_elf_debug_symbol_values),
KOBJMETHOD(linker_search_symbol, link_elf_search_symbol),
KOBJMETHOD(linker_unload, link_elf_unload_file),
KOBJMETHOD(linker_load_file, link_elf_load_file),
@@ -1067,7 +1073,8 @@ relocate_file(elf_file_t ef)
}
static int
-link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
+link_elf_lookup_symbol1(linker_file_t lf, const char *name, c_linker_sym_t *sym,
+ int see_local)
{
elf_file_t ef = (elf_file_t) lf;
const Elf_Sym *symp;
@@ -1077,27 +1084,64 @@ link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
strp = ef->ddbstrtab + symp->st_name;
if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
- *sym = (c_linker_sym_t) symp;
- return 0;
+ if (see_local ||
+ ELF_ST_BIND(symp->st_info) == STB_GLOBAL) {
+ *sym = (c_linker_sym_t) symp;
+ return (0);
+ } else
+ return (ENOENT);
}
}
- return ENOENT;
+ return (ENOENT);
}
static int
-link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
- linker_symval_t *symval)
+link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
+{
+
+ return (link_elf_lookup_symbol1(lf, name, sym, 0));
+}
+
+static int
+link_elf_lookup_debug_symbol(linker_file_t lf, const char *name,
+ c_linker_sym_t *sym)
+{
+
+ return (link_elf_lookup_symbol1(lf, name, sym, 1));
+}
+
+static int
+link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
+ linker_symval_t *symval, int see_local)
{
elf_file_t ef = (elf_file_t) lf;
const Elf_Sym *es = (const Elf_Sym*) sym;
if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
+ if (!see_local && ELF_ST_BIND(es->st_info) == STB_LOCAL)
+ return (ENOENT);
symval->name = ef->ddbstrtab + es->st_name;
symval->value = (caddr_t)es->st_value;
symval->size = es->st_size;
- return 0;
+ return (0);
}
- return ENOENT;
+ return (ENOENT);
+}
+
+static int
+link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+ linker_symval_t *symval)
+{
+
+ return (link_elf_symbol_values1(lf, sym, symval, 0));
+}
+
+static int
+link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym,
+ linker_symval_t *symval)
+{
+
+ return (link_elf_symbol_values1(lf, sym, symval, 1));
}
static int
diff --git a/sys/kern/linker_if.m b/sys/kern/linker_if.m
index 3df592c..030e8eb 100644
--- a/sys/kern/linker_if.m
+++ b/sys/kern/linker_if.m
@@ -40,12 +40,24 @@ METHOD int lookup_symbol {
c_linker_sym_t* symp;
};
+METHOD int lookup_debug_symbol {
+ linker_file_t file;
+ const char* name;
+ c_linker_sym_t* symp;
+};
+
METHOD int symbol_values {
linker_file_t file;
c_linker_sym_t sym;
linker_symval_t* valp;
};
+METHOD int debug_symbol_values {
+ linker_file_t file;
+ c_linker_sym_t sym;
+ linker_symval_t* valp;
+};
+
METHOD int search_symbol {
linker_file_t file;
caddr_t value;
@@ -63,6 +75,12 @@ METHOD int each_function_name {
void* opaque;
};
+METHOD int each_debug_function_name {
+ linker_file_t file;
+ linker_function_name_callback_t callback;
+ void* opaque;
+};
+
#
# Call the callback with each specified function and it's value
# defined in the file.
@@ -74,6 +92,12 @@ METHOD int each_function_nameval {
void* opaque;
};
+METHOD int each_debug_function_nameval {
+ linker_file_t file;
+ linker_function_nameval_callback_t callback;
+ void* opaque;
+};
+
#
# Search for a linker set in a file. Return a pointer to the first
# entry (which is itself a pointer), and the number of entries.
diff --git a/sys/modules/ata/atacore/Makefile b/sys/modules/ata/atacore/Makefile
index 7b8d9e2..30a4ccb 100644
--- a/sys/modules/ata/atacore/Makefile
+++ b/sys/modules/ata/atacore/Makefile
@@ -6,4 +6,6 @@ KMOD= ata
SRCS= ata-all.c ata-lowlevel.c ata-queue.c ata_if.c
SRCS+= opt_ata.h ata_if.h device_if.h bus_if.h
+EXPORT_SYMS= YES
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/mii/Makefile b/sys/modules/mii/Makefile
index 6232b5e..9740cf2 100644
--- a/sys/modules/mii/Makefile
+++ b/sys/modules/mii/Makefile
@@ -12,7 +12,11 @@ SRCS+= rgephy.c rlphy.c ruephy.c tdkphy.c tlphy.c truephy.c ukphy.c
SRCS+= ukphy_subr.c
SRCS+= xmphy.c
-EXPORT_SYMS= mii_mediachg \
+EXPORT_SYMS= miibus_devclass \
+ miibus_driver \
+ miibus_readreg_desc \
+ miibus_writereg_desc \
+ mii_mediachg \
mii_phy_probe \
mii_phy_reset \
mii_pollstat \
diff --git a/sys/modules/pseudofs/Makefile b/sys/modules/pseudofs/Makefile
index d5696c5..ea7b112 100644
--- a/sys/modules/pseudofs/Makefile
+++ b/sys/modules/pseudofs/Makefile
@@ -11,6 +11,7 @@ SRCS= opt_pseudofs.h \
pseudofs_vnops.c
EXPORT_SYMS= pfs_mount \
+ pfs_cmount \
pfs_unmount \
pfs_root \
pfs_statfs \
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 196 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20100715/410343b0/attachment.pgp
More information about the freebsd-hackers
mailing list