1cea6c86cSDoug Rabson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni *
4326e27d8SDoug Rabson * Copyright (c) 1997-2000 Doug Rabson
5cea6c86cSDoug Rabson * All rights reserved.
6cea6c86cSDoug Rabson *
7cea6c86cSDoug Rabson * Redistribution and use in source and binary forms, with or without
8cea6c86cSDoug Rabson * modification, are permitted provided that the following conditions
9cea6c86cSDoug Rabson * are met:
10cea6c86cSDoug Rabson * 1. Redistributions of source code must retain the above copyright
11cea6c86cSDoug Rabson * notice, this list of conditions and the following disclaimer.
12cea6c86cSDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright
13cea6c86cSDoug Rabson * notice, this list of conditions and the following disclaimer in the
14cea6c86cSDoug Rabson * documentation and/or other materials provided with the distribution.
15cea6c86cSDoug Rabson *
16cea6c86cSDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17cea6c86cSDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18cea6c86cSDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19cea6c86cSDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20cea6c86cSDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21cea6c86cSDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22cea6c86cSDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23cea6c86cSDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24cea6c86cSDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25cea6c86cSDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26cea6c86cSDoug Rabson * SUCH DAMAGE.
27cea6c86cSDoug Rabson */
28cea6c86cSDoug Rabson
29677b542eSDavid E. O'Brien #include <sys/cdefs.h>
3051f3fe7aSPeter Wemm #include "opt_ddb.h"
314e313b69SMax Khon #include "opt_kld.h"
3249874f6eSJoseph Koshy #include "opt_hwpmc_hooks.h"
33*df114daeSRuslan Bukin #include "opt_hwt_hooks.h"
3451f3fe7aSPeter Wemm
35cea6c86cSDoug Rabson #include <sys/param.h>
36cea6c86cSDoug Rabson #include <sys/systm.h>
375a8fceb3SMitchell Horne #include <sys/boottrace.h>
38c9b645b5SMark Johnston #include <sys/eventhandler.h>
3951f3fe7aSPeter Wemm #include <sys/fcntl.h>
400304c731SJamie Gritton #include <sys/jail.h>
41877eea42SMitchell Horne #include <sys/kernel.h>
4251f3fe7aSPeter Wemm #include <sys/libkern.h>
43877eea42SMitchell Horne #include <sys/linker.h>
44877eea42SMitchell Horne #include <sys/lock.h>
45877eea42SMitchell Horne #include <sys/malloc.h>
46877eea42SMitchell Horne #include <sys/module.h>
47877eea42SMitchell Horne #include <sys/mount.h>
48877eea42SMitchell Horne #include <sys/mutex.h>
4951f3fe7aSPeter Wemm #include <sys/namei.h>
50877eea42SMitchell Horne #include <sys/priv.h>
51877eea42SMitchell Horne #include <sys/proc.h>
52877eea42SMitchell Horne #include <sys/sx.h>
53d5388587SJohn Baldwin #include <sys/syscallsubr.h>
5451f3fe7aSPeter Wemm #include <sys/sysctl.h>
55877eea42SMitchell Horne #include <sys/sysproto.h>
56877eea42SMitchell Horne #include <sys/vnode.h>
57530c0060SRobert Watson
588a3aeac2SConrad Meyer #ifdef DDB
598a3aeac2SConrad Meyer #include <ddb/ddb.h>
608a3aeac2SConrad Meyer #endif
618a3aeac2SConrad Meyer
62530c0060SRobert Watson #include <net/vnet.h>
63cea6c86cSDoug Rabson
64aed55708SRobert Watson #include <security/mac/mac_framework.h>
65aed55708SRobert Watson
66326e27d8SDoug Rabson #include "linker_if.h"
67326e27d8SDoug Rabson
68*df114daeSRuslan Bukin #if defined(HWPMC_HOOKS) || defined(HWT_HOOKS)
6949874f6eSJoseph Koshy #include <sys/pmckern.h>
7049874f6eSJoseph Koshy #endif
7149874f6eSJoseph Koshy
7221ce23ebSPeter Wemm #ifdef KLD_DEBUG
7321ce23ebSPeter Wemm int kld_debug = 0;
74af3b2549SHans Petter Selasky SYSCTL_INT(_debug, OID_AUTO, kld_debug, CTLFLAG_RWTUN,
753ea6157eSOleksandr Tymoshenko &kld_debug, 0, "Set various levels of KLD debug");
7621ce23ebSPeter Wemm #endif
7721ce23ebSPeter Wemm
78645743eaSJohn Baldwin /* These variables are used by kernel debuggers to enumerate loaded files. */
79645743eaSJohn Baldwin const int kld_off_address = offsetof(struct linker_file, address);
80645743eaSJohn Baldwin const int kld_off_filename = offsetof(struct linker_file, filename);
81645743eaSJohn Baldwin const int kld_off_pathname = offsetof(struct linker_file, pathname);
82645743eaSJohn Baldwin const int kld_off_next = offsetof(struct linker_file, link.tqe_next);
83645743eaSJohn Baldwin
848e92b63cSAndrew R. Reiter /*
858e92b63cSAndrew R. Reiter * static char *linker_search_path(const char *name, struct mod_depend
868e92b63cSAndrew R. Reiter * *verinfo);
878e92b63cSAndrew R. Reiter */
8854823af2SPeter Wemm static const char *linker_basename(const char *path);
8954823af2SPeter Wemm
90f462ce3eSJohn Baldwin /*
91f462ce3eSJohn Baldwin * Find a currently loaded file given its filename.
92f462ce3eSJohn Baldwin */
93f462ce3eSJohn Baldwin static linker_file_t linker_find_file_by_name(const char* _filename);
94f462ce3eSJohn Baldwin
95f462ce3eSJohn Baldwin /*
96f462ce3eSJohn Baldwin * Find a currently loaded file given its file id.
97f462ce3eSJohn Baldwin */
98f462ce3eSJohn Baldwin static linker_file_t linker_find_file_by_id(int _fileid);
99f462ce3eSJohn Baldwin
100f41325dbSPeter Wemm /* Metadata from the static kernel */
101f41325dbSPeter Wemm SET_DECLARE(modmetadata_set, struct mod_metadata);
102f41325dbSPeter Wemm
10354823af2SPeter Wemm MALLOC_DEFINE(M_LINKER, "linker", "kernel linker");
10454823af2SPeter Wemm
10584e40f56SPeter Wemm linker_file_t linker_kernel_file;
106d73424aaSBruce Evans
10770f37788SJohn Baldwin static struct sx kld_sx; /* kernel linker lock */
1084f924a78SKonstantin Belousov static u_int kld_busy;
1094f924a78SKonstantin Belousov static struct thread *kld_busy_owner;
1102eb7b21bSAndrew R. Reiter
1111676805cSJohn Birrell /*
1121676805cSJohn Birrell * Load counter used by clients to determine if a linker file has been
1131676805cSJohn Birrell * re-loaded. This counter is incremented for each file load.
1141676805cSJohn Birrell */
1151676805cSJohn Birrell static int loadcnt;
1161676805cSJohn Birrell
117cea6c86cSDoug Rabson static linker_class_list_t classes;
1184033a962SGreg Lehey static linker_file_list_t linker_files;
119cea6c86cSDoug Rabson static int next_file_id = 1;
1202eb7b21bSAndrew R. Reiter static int linker_no_more_classes = 0;
121cea6c86cSDoug Rabson
122b489b407SAndrew R. Reiter #define LINKER_GET_NEXT_FILE_ID(a) do { \
123b489b407SAndrew R. Reiter linker_file_t lftmp; \
124b489b407SAndrew R. Reiter \
1253a277424SMark Johnston if (!cold) \
1263a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED); \
127b489b407SAndrew R. Reiter retry: \
128b489b407SAndrew R. Reiter TAILQ_FOREACH(lftmp, &linker_files, link) { \
129b489b407SAndrew R. Reiter if (next_file_id == lftmp->id) { \
130b489b407SAndrew R. Reiter next_file_id++; \
131b489b407SAndrew R. Reiter goto retry; \
132b489b407SAndrew R. Reiter } \
133b489b407SAndrew R. Reiter } \
134b489b407SAndrew R. Reiter (a) = next_file_id; \
135b489b407SAndrew R. Reiter } while (0)
136b489b407SAndrew R. Reiter
13754823af2SPeter Wemm /* XXX wrong name; we're looking at version provision tags here, not modules */
138e3975643SJake Burkholder typedef TAILQ_HEAD(, modlist) modlisthead_t;
13954823af2SPeter Wemm struct modlist {
140e3975643SJake Burkholder TAILQ_ENTRY(modlist) link; /* chain together all modules */
14154823af2SPeter Wemm linker_file_t container;
14254823af2SPeter Wemm const char *name;
143a91f68bcSBoris Popov int version;
14454823af2SPeter Wemm };
14554823af2SPeter Wemm typedef struct modlist *modlist_t;
14654823af2SPeter Wemm static modlisthead_t found_modules;
14754823af2SPeter Wemm
1481c7307cfSZhenlei Huang static void linker_file_add_dependency(linker_file_t file,
149aaf31705SJohn Baldwin linker_file_t dep);
15070f37788SJohn Baldwin static caddr_t linker_file_lookup_symbol_internal(linker_file_t file,
15170f37788SJohn Baldwin const char* name, int deps);
152aaf31705SJohn Baldwin static int linker_load_module(const char *kldname,
153aaf31705SJohn Baldwin const char *modname, struct linker_file *parent,
154d0b6da08SWarner Losh const struct mod_depend *verinfo, struct linker_file **lfpp);
155d0b6da08SWarner Losh static modlist_t modlist_lookup2(const char *name, const struct mod_depend *verinfo);
15696987c74SBrian Somers
157cea6c86cSDoug Rabson static void
linker_init(void * arg)158cea6c86cSDoug Rabson linker_init(void *arg)
159cea6c86cSDoug Rabson {
1608e92b63cSAndrew R. Reiter
16170f37788SJohn Baldwin sx_init(&kld_sx, "kernel linker");
162cea6c86cSDoug Rabson TAILQ_INIT(&classes);
1634033a962SGreg Lehey TAILQ_INIT(&linker_files);
164cea6c86cSDoug Rabson }
165cea6c86cSDoug Rabson
166891cf3edSEd Maste SYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, NULL);
167cea6c86cSDoug Rabson
1682eb7b21bSAndrew R. Reiter static void
linker_stop_class_add(void * arg)1692eb7b21bSAndrew R. Reiter linker_stop_class_add(void *arg)
1702eb7b21bSAndrew R. Reiter {
1712eb7b21bSAndrew R. Reiter
1722eb7b21bSAndrew R. Reiter linker_no_more_classes = 1;
1732eb7b21bSAndrew R. Reiter }
1742eb7b21bSAndrew R. Reiter
175237fdd78SRobert Watson SYSINIT(linker_class, SI_SUB_KLD, SI_ORDER_ANY, linker_stop_class_add, NULL);
1762eb7b21bSAndrew R. Reiter
177cea6c86cSDoug Rabson int
linker_add_class(linker_class_t lc)178326e27d8SDoug Rabson linker_add_class(linker_class_t lc)
179cea6c86cSDoug Rabson {
1808e92b63cSAndrew R. Reiter
1812eb7b21bSAndrew R. Reiter /*
182ea2b9b3eSJohn Baldwin * We disallow any class registration past SI_ORDER_ANY
183ea2b9b3eSJohn Baldwin * of SI_SUB_KLD. We bump the reference count to keep the
184ea2b9b3eSJohn Baldwin * ops from being freed.
1852eb7b21bSAndrew R. Reiter */
1862eb7b21bSAndrew R. Reiter if (linker_no_more_classes == 1)
1872eb7b21bSAndrew R. Reiter return (EPERM);
188326e27d8SDoug Rabson kobj_class_compile((kobj_class_t) lc);
189ea2b9b3eSJohn Baldwin ((kobj_class_t)lc)->refs++; /* XXX: kobj_mtx */
19054823af2SPeter Wemm TAILQ_INSERT_TAIL(&classes, lc, link);
1918e92b63cSAndrew R. Reiter return (0);
192cea6c86cSDoug Rabson }
193cea6c86cSDoug Rabson
194cea6c86cSDoug Rabson static void
linker_file_sysinit(linker_file_t lf)195cea6c86cSDoug Rabson linker_file_sysinit(linker_file_t lf)
196cea6c86cSDoug Rabson {
1978e92b63cSAndrew R. Reiter struct sysinit **start, **stop, **sipp, **xipp, *save;
1985a8fceb3SMitchell Horne int last;
199cea6c86cSDoug Rabson
200cea6c86cSDoug Rabson KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
201cea6c86cSDoug Rabson lf->filename));
202cea6c86cSDoug Rabson
2030770f164SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
2040770f164SMark Johnston
20515c2b301SJohn Baldwin if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0)
206cea6c86cSDoug Rabson return;
207cea6c86cSDoug Rabson /*
208cea6c86cSDoug Rabson * Perform a bubble sort of the system initialization objects by
209cea6c86cSDoug Rabson * their subsystem (primary key) and order (secondary key).
210cea6c86cSDoug Rabson *
2118e92b63cSAndrew R. Reiter * Since some things care about execution order, this is the operation
2128e92b63cSAndrew R. Reiter * which ensures continued function.
213cea6c86cSDoug Rabson */
214f41325dbSPeter Wemm for (sipp = start; sipp < stop; sipp++) {
215f41325dbSPeter Wemm for (xipp = sipp + 1; xipp < stop; xipp++) {
2162ff08731SBoris Popov if ((*sipp)->subsystem < (*xipp)->subsystem ||
217cea6c86cSDoug Rabson ((*sipp)->subsystem == (*xipp)->subsystem &&
218edfbe150SPeter Wemm (*sipp)->order <= (*xipp)->order))
219cea6c86cSDoug Rabson continue; /* skip */
220cea6c86cSDoug Rabson save = *sipp;
221cea6c86cSDoug Rabson *sipp = *xipp;
222cea6c86cSDoug Rabson *xipp = save;
223cea6c86cSDoug Rabson }
224cea6c86cSDoug Rabson }
225cea6c86cSDoug Rabson
226cea6c86cSDoug Rabson /*
227cea6c86cSDoug Rabson * Traverse the (now) ordered list of system initialization tasks.
228cea6c86cSDoug Rabson * Perform each task, and continue on to the next task.
229cea6c86cSDoug Rabson */
2305a8fceb3SMitchell Horne last = SI_SUB_DUMMY;
2310770f164SMark Johnston sx_xunlock(&kld_sx);
232398c993bSJohn Baldwin mtx_lock(&Giant);
233f41325dbSPeter Wemm for (sipp = start; sipp < stop; sipp++) {
234cea6c86cSDoug Rabson if ((*sipp)->subsystem == SI_SUB_DUMMY)
235cea6c86cSDoug Rabson continue; /* skip dummy task(s) */
236cea6c86cSDoug Rabson
2375a8fceb3SMitchell Horne if ((*sipp)->subsystem > last)
2385a8fceb3SMitchell Horne BOOTTRACE("%s: sysinit 0x%7x", lf->filename,
2395a8fceb3SMitchell Horne (*sipp)->subsystem);
2405a8fceb3SMitchell Horne
2419c8b8baaSPeter Wemm /* Call function */
242cea6c86cSDoug Rabson (*((*sipp)->func)) ((*sipp)->udata);
2435a8fceb3SMitchell Horne last = (*sipp)->subsystem;
244cea6c86cSDoug Rabson }
245398c993bSJohn Baldwin mtx_unlock(&Giant);
2460770f164SMark Johnston sx_xlock(&kld_sx);
247cea6c86cSDoug Rabson }
248cea6c86cSDoug Rabson
249edfbe150SPeter Wemm static void
linker_file_sysuninit(linker_file_t lf)250edfbe150SPeter Wemm linker_file_sysuninit(linker_file_t lf)
251edfbe150SPeter Wemm {
2528e92b63cSAndrew R. Reiter struct sysinit **start, **stop, **sipp, **xipp, *save;
2535a8fceb3SMitchell Horne int last;
254edfbe150SPeter Wemm
255edfbe150SPeter Wemm KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n",
256edfbe150SPeter Wemm lf->filename));
257edfbe150SPeter Wemm
2580770f164SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
2590770f164SMark Johnston
26015c2b301SJohn Baldwin if (linker_file_lookup_set(lf, "sysuninit_set", &start, &stop,
2618e92b63cSAndrew R. Reiter NULL) != 0)
262edfbe150SPeter Wemm return;
263edfbe150SPeter Wemm
264edfbe150SPeter Wemm /*
265edfbe150SPeter Wemm * Perform a reverse bubble sort of the system initialization objects
266edfbe150SPeter Wemm * by their subsystem (primary key) and order (secondary key).
267edfbe150SPeter Wemm *
2688e92b63cSAndrew R. Reiter * Since some things care about execution order, this is the operation
2698e92b63cSAndrew R. Reiter * which ensures continued function.
270edfbe150SPeter Wemm */
271f41325dbSPeter Wemm for (sipp = start; sipp < stop; sipp++) {
272f41325dbSPeter Wemm for (xipp = sipp + 1; xipp < stop; xipp++) {
2732ff08731SBoris Popov if ((*sipp)->subsystem > (*xipp)->subsystem ||
274edfbe150SPeter Wemm ((*sipp)->subsystem == (*xipp)->subsystem &&
275edfbe150SPeter Wemm (*sipp)->order >= (*xipp)->order))
276edfbe150SPeter Wemm continue; /* skip */
277edfbe150SPeter Wemm save = *sipp;
278edfbe150SPeter Wemm *sipp = *xipp;
279edfbe150SPeter Wemm *xipp = save;
280edfbe150SPeter Wemm }
281edfbe150SPeter Wemm }
282edfbe150SPeter Wemm
283edfbe150SPeter Wemm /*
284edfbe150SPeter Wemm * Traverse the (now) ordered list of system initialization tasks.
285edfbe150SPeter Wemm * Perform each task, and continue on to the next task.
286edfbe150SPeter Wemm */
2870770f164SMark Johnston sx_xunlock(&kld_sx);
288398c993bSJohn Baldwin mtx_lock(&Giant);
2895a8fceb3SMitchell Horne last = SI_SUB_DUMMY;
290f41325dbSPeter Wemm for (sipp = start; sipp < stop; sipp++) {
291edfbe150SPeter Wemm if ((*sipp)->subsystem == SI_SUB_DUMMY)
292edfbe150SPeter Wemm continue; /* skip dummy task(s) */
293edfbe150SPeter Wemm
2945a8fceb3SMitchell Horne if ((*sipp)->subsystem > last)
2955a8fceb3SMitchell Horne BOOTTRACE("%s: sysuninit 0x%7x", lf->filename,
2965a8fceb3SMitchell Horne (*sipp)->subsystem);
2975a8fceb3SMitchell Horne
2989c8b8baaSPeter Wemm /* Call function */
299edfbe150SPeter Wemm (*((*sipp)->func)) ((*sipp)->udata);
3005a8fceb3SMitchell Horne last = (*sipp)->subsystem;
301edfbe150SPeter Wemm }
302398c993bSJohn Baldwin mtx_unlock(&Giant);
3030770f164SMark Johnston sx_xlock(&kld_sx);
304edfbe150SPeter Wemm }
305edfbe150SPeter Wemm
306ce02431fSDoug Rabson static void
linker_file_register_sysctls(linker_file_t lf,bool enable)307693593b6SAndriy Gapon linker_file_register_sysctls(linker_file_t lf, bool enable)
308ce02431fSDoug Rabson {
309f41325dbSPeter Wemm struct sysctl_oid **start, **stop, **oidp;
310ce02431fSDoug Rabson
3118e92b63cSAndrew R. Reiter KLD_DPF(FILE,
3128e92b63cSAndrew R. Reiter ("linker_file_register_sysctls: registering SYSCTLs for %s\n",
313ce02431fSDoug Rabson lf->filename));
314ce02431fSDoug Rabson
3150770f164SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
3160770f164SMark Johnston
31715c2b301SJohn Baldwin if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
318ce02431fSDoug Rabson return;
319ce02431fSDoug Rabson
3200770f164SMark Johnston sx_xunlock(&kld_sx);
3217665e341SMateusz Guzik sysctl_wlock();
322693593b6SAndriy Gapon for (oidp = start; oidp < stop; oidp++) {
323693593b6SAndriy Gapon if (enable)
324550374efSAndriy Gapon sysctl_register_oid(*oidp);
325693593b6SAndriy Gapon else
326693593b6SAndriy Gapon sysctl_register_disabled_oid(*oidp);
327693593b6SAndriy Gapon }
328693593b6SAndriy Gapon sysctl_wunlock();
329693593b6SAndriy Gapon sx_xlock(&kld_sx);
330693593b6SAndriy Gapon }
331693593b6SAndriy Gapon
332c21bc6f3SBojan Novković /*
333c21bc6f3SBojan Novković * Invoke the LINKER_CTF_GET implementation for this file. Existing
334c21bc6f3SBojan Novković * implementations will load CTF info from the filesystem upon the first call
335c21bc6f3SBojan Novković * and cache it in the kernel thereafter.
336c21bc6f3SBojan Novković */
337c21bc6f3SBojan Novković static void
linker_ctf_load_file(linker_file_t file)338c21bc6f3SBojan Novković linker_ctf_load_file(linker_file_t file)
339c21bc6f3SBojan Novković {
340c21bc6f3SBojan Novković linker_ctf_t lc;
341c21bc6f3SBojan Novković int error;
342c21bc6f3SBojan Novković
343c21bc6f3SBojan Novković error = linker_ctf_get(file, &lc);
344c21bc6f3SBojan Novković if (error == 0)
345c21bc6f3SBojan Novković return;
346c21bc6f3SBojan Novković if (bootverbose) {
347c21bc6f3SBojan Novković printf("failed to load CTF for %s: %d\n", file->filename,
348c21bc6f3SBojan Novković error);
349c21bc6f3SBojan Novković }
350c21bc6f3SBojan Novković }
351c21bc6f3SBojan Novković
352693593b6SAndriy Gapon static void
linker_file_enable_sysctls(linker_file_t lf)353693593b6SAndriy Gapon linker_file_enable_sysctls(linker_file_t lf)
354693593b6SAndriy Gapon {
355693593b6SAndriy Gapon struct sysctl_oid **start, **stop, **oidp;
356693593b6SAndriy Gapon
357693593b6SAndriy Gapon KLD_DPF(FILE,
358693593b6SAndriy Gapon ("linker_file_enable_sysctls: enable SYSCTLs for %s\n",
359693593b6SAndriy Gapon lf->filename));
360693593b6SAndriy Gapon
361693593b6SAndriy Gapon sx_assert(&kld_sx, SA_XLOCKED);
362693593b6SAndriy Gapon
363693593b6SAndriy Gapon if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
364693593b6SAndriy Gapon return;
365693593b6SAndriy Gapon
366693593b6SAndriy Gapon sx_xunlock(&kld_sx);
367693593b6SAndriy Gapon sysctl_wlock();
368693593b6SAndriy Gapon for (oidp = start; oidp < stop; oidp++)
369693593b6SAndriy Gapon sysctl_enable_oid(*oidp);
3707665e341SMateusz Guzik sysctl_wunlock();
3710770f164SMark Johnston sx_xlock(&kld_sx);
372ce02431fSDoug Rabson }
373ce02431fSDoug Rabson
374ce02431fSDoug Rabson static void
linker_file_unregister_sysctls(linker_file_t lf)375ce02431fSDoug Rabson linker_file_unregister_sysctls(linker_file_t lf)
376ce02431fSDoug Rabson {
377f41325dbSPeter Wemm struct sysctl_oid **start, **stop, **oidp;
378ce02431fSDoug Rabson
3795b0da85aSAndrey V. Elsukov KLD_DPF(FILE, ("linker_file_unregister_sysctls: unregistering SYSCTLs"
3808e92b63cSAndrew R. Reiter " for %s\n", lf->filename));
381ce02431fSDoug Rabson
3820770f164SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
3830770f164SMark Johnston
38415c2b301SJohn Baldwin if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
385ce02431fSDoug Rabson return;
386ce02431fSDoug Rabson
3870770f164SMark Johnston sx_xunlock(&kld_sx);
3887665e341SMateusz Guzik sysctl_wlock();
389f41325dbSPeter Wemm for (oidp = start; oidp < stop; oidp++)
390f41325dbSPeter Wemm sysctl_unregister_oid(*oidp);
3917665e341SMateusz Guzik sysctl_wunlock();
3920770f164SMark Johnston sx_xlock(&kld_sx);
393ce02431fSDoug Rabson }
394ce02431fSDoug Rabson
39554823af2SPeter Wemm static int
linker_file_register_modules(linker_file_t lf)39654823af2SPeter Wemm linker_file_register_modules(linker_file_t lf)
39754823af2SPeter Wemm {
3988e92b63cSAndrew R. Reiter struct mod_metadata **start, **stop, **mdp;
39954823af2SPeter Wemm const moduledata_t *moddata;
400885fec3eSPawel Jakub Dawidek int first_error, error;
40154823af2SPeter Wemm
4028e92b63cSAndrew R. Reiter KLD_DPF(FILE, ("linker_file_register_modules: registering modules"
4038e92b63cSAndrew R. Reiter " in %s\n", lf->filename));
40454823af2SPeter Wemm
4050770f164SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
4060770f164SMark Johnston
407615a9fb5SMark Johnston if (linker_file_lookup_set(lf, MDT_SETNAME, &start, &stop, NULL) != 0) {
408f41325dbSPeter Wemm /*
4098e92b63cSAndrew R. Reiter * This fallback should be unnecessary, but if we get booted
4108e92b63cSAndrew R. Reiter * from boot2 instead of loader and we are missing our
4118e92b63cSAndrew R. Reiter * metadata then we have to try the best we can.
412f41325dbSPeter Wemm */
413f41325dbSPeter Wemm if (lf == linker_kernel_file) {
414f41325dbSPeter Wemm start = SET_BEGIN(modmetadata_set);
415f41325dbSPeter Wemm stop = SET_LIMIT(modmetadata_set);
4168e92b63cSAndrew R. Reiter } else
4178e92b63cSAndrew R. Reiter return (0);
418f41325dbSPeter Wemm }
419885fec3eSPawel Jakub Dawidek first_error = 0;
420f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
421f41325dbSPeter Wemm if ((*mdp)->md_type != MDT_MODULE)
42254823af2SPeter Wemm continue;
423f41325dbSPeter Wemm moddata = (*mdp)->md_data;
42454823af2SPeter Wemm KLD_DPF(FILE, ("Registering module %s in %s\n",
42554823af2SPeter Wemm moddata->name, lf->filename));
42654823af2SPeter Wemm error = module_register(moddata, lf);
427870fba26SPawel Jakub Dawidek if (error) {
4288e92b63cSAndrew R. Reiter printf("Module %s failed to register: %d\n",
4298e92b63cSAndrew R. Reiter moddata->name, error);
430885fec3eSPawel Jakub Dawidek if (first_error == 0)
431885fec3eSPawel Jakub Dawidek first_error = error;
432870fba26SPawel Jakub Dawidek }
43354823af2SPeter Wemm }
434885fec3eSPawel Jakub Dawidek return (first_error);
43554823af2SPeter Wemm }
43654823af2SPeter Wemm
43754823af2SPeter Wemm static void
linker_init_kernel_modules(void)43854823af2SPeter Wemm linker_init_kernel_modules(void)
43954823af2SPeter Wemm {
4408e92b63cSAndrew R. Reiter
4413a277424SMark Johnston sx_xlock(&kld_sx);
44254823af2SPeter Wemm linker_file_register_modules(linker_kernel_file);
4433a277424SMark Johnston sx_xunlock(&kld_sx);
44454823af2SPeter Wemm }
44554823af2SPeter Wemm
446237fdd78SRobert Watson SYSINIT(linker_kernel, SI_SUB_KLD, SI_ORDER_ANY, linker_init_kernel_modules,
447891cf3edSEd Maste NULL);
44854823af2SPeter Wemm
449f2b17113SMaxime Henrion static int
linker_load_file(const char * filename,linker_file_t * result)450cea6c86cSDoug Rabson linker_load_file(const char *filename, linker_file_t *result)
451cea6c86cSDoug Rabson {
452cea6c86cSDoug Rabson linker_class_t lc;
453cea6c86cSDoug Rabson linker_file_t lf;
4547582954eSJohn Baldwin int foundfile, error, modules;
455cea6c86cSDoug Rabson
4566c66bbedSArchie Cobbs /* Refuse to load modules if securelevel raised */
4570304c731SJamie Gritton if (prison0.pr_securelevel > 0)
4588e92b63cSAndrew R. Reiter return (EPERM);
4596c66bbedSArchie Cobbs
4603a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
461cea6c86cSDoug Rabson lf = linker_find_file_by_name(filename);
462cea6c86cSDoug Rabson if (lf) {
4638e92b63cSAndrew R. Reiter KLD_DPF(FILE, ("linker_load_file: file %s is already loaded,"
4648e92b63cSAndrew R. Reiter " incrementing refs\n", filename));
465cea6c86cSDoug Rabson *result = lf;
466cea6c86cSDoug Rabson lf->refs++;
4673a600aeaSJohn Baldwin return (0);
468cea6c86cSDoug Rabson }
469e99f57c3SPeter Wemm foundfile = 0;
4703a600aeaSJohn Baldwin error = 0;
4712eb7b21bSAndrew R. Reiter
4722eb7b21bSAndrew R. Reiter /*
4732eb7b21bSAndrew R. Reiter * We do not need to protect (lock) classes here because there is
4742eb7b21bSAndrew R. Reiter * no class registration past startup (SI_SUB_KLD, SI_ORDER_ANY)
4752eb7b21bSAndrew R. Reiter * and there is no class deregistration mechanism at this time.
4762eb7b21bSAndrew R. Reiter */
477fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lc, &classes, link) {
47898b0e9d5SJake Burkholder KLD_DPF(FILE, ("linker_load_file: trying to load %s\n",
47998b0e9d5SJake Burkholder filename));
48054823af2SPeter Wemm error = LINKER_LOAD_FILE(lc, filename, &lf);
481e99f57c3SPeter Wemm /*
4828e92b63cSAndrew R. Reiter * If we got something other than ENOENT, then it exists but
4838e92b63cSAndrew R. Reiter * we cannot load it for some other reason.
484e99f57c3SPeter Wemm */
485ecf710f0SZhenlei Huang if (error != ENOENT) {
486e99f57c3SPeter Wemm foundfile = 1;
487ecf710f0SZhenlei Huang if (error == EEXIST)
488ecf710f0SZhenlei Huang break;
489ecf710f0SZhenlei Huang }
490cea6c86cSDoug Rabson if (lf) {
491870fba26SPawel Jakub Dawidek error = linker_file_register_modules(lf);
492870fba26SPawel Jakub Dawidek if (error == EEXIST) {
493870fba26SPawel Jakub Dawidek linker_file_unload(lf, LINKER_UNLOAD_FORCE);
4943a600aeaSJohn Baldwin return (error);
495870fba26SPawel Jakub Dawidek }
4967582954eSJohn Baldwin modules = !TAILQ_EMPTY(&lf->modules);
497693593b6SAndriy Gapon linker_file_register_sysctls(lf, false);
498110113bcSZhenlei Huang #ifdef VIMAGE
499110113bcSZhenlei Huang LINKER_PROPAGATE_VNETS(lf);
500110113bcSZhenlei Huang #endif
501a199ed3cSDoug Rabson linker_file_sysinit(lf);
50254823af2SPeter Wemm lf->flags |= LINKER_FILE_LINKED;
5037582954eSJohn Baldwin
5047582954eSJohn Baldwin /*
5057582954eSJohn Baldwin * If all of the modules in this file failed
5067582954eSJohn Baldwin * to load, unload the file and return an
5077582954eSJohn Baldwin * error of ENOEXEC.
5087582954eSJohn Baldwin */
5097582954eSJohn Baldwin if (modules && TAILQ_EMPTY(&lf->modules)) {
5107582954eSJohn Baldwin linker_file_unload(lf, LINKER_UNLOAD_FORCE);
5117582954eSJohn Baldwin return (ENOEXEC);
5127582954eSJohn Baldwin }
513693593b6SAndriy Gapon linker_file_enable_sysctls(lf);
514c21bc6f3SBojan Novković
515c21bc6f3SBojan Novković /*
516c21bc6f3SBojan Novković * Ask the linker to load CTF data for this file.
517c21bc6f3SBojan Novković */
518c21bc6f3SBojan Novković linker_ctf_load_file(lf);
5198f725462SMark Johnston EVENTHANDLER_INVOKE(kld_load, lf);
520cea6c86cSDoug Rabson *result = lf;
5213a600aeaSJohn Baldwin return (0);
522cea6c86cSDoug Rabson }
523cea6c86cSDoug Rabson }
524e99f57c3SPeter Wemm /*
525e99f57c3SPeter Wemm * Less than ideal, but tells the user whether it failed to load or
526e99f57c3SPeter Wemm * the module was not found.
527e99f57c3SPeter Wemm */
5283b132a61SSam Leffler if (foundfile) {
529a1d7ce03SAttilio Rao /*
530a1d7ce03SAttilio Rao * If the file type has not been recognized by the last try
531a1d7ce03SAttilio Rao * printout a message before to fail.
532a1d7ce03SAttilio Rao */
533a1d7ce03SAttilio Rao if (error == ENOSYS)
534d0147e10SGleb Smirnoff printf("%s: %s - unsupported file type\n",
535d0147e10SGleb Smirnoff __func__, filename);
536a1d7ce03SAttilio Rao
5373b132a61SSam Leffler /*
5383b132a61SSam Leffler * Format not recognized or otherwise unloadable.
5393b132a61SSam Leffler * When loading a module that is statically built into
5403b132a61SSam Leffler * the kernel EEXIST percolates back up as the return
5413b132a61SSam Leffler * value. Preserve this so that apps like sysinstall
5423b132a61SSam Leffler * can recognize this special case and not post bogus
5433b132a61SSam Leffler * dialog boxes.
5443b132a61SSam Leffler */
5453b132a61SSam Leffler if (error != EEXIST)
546e68baa70SAndrew R. Reiter error = ENOEXEC;
5473b132a61SSam Leffler } else
548e99f57c3SPeter Wemm error = ENOENT; /* Nothing found */
5498e92b63cSAndrew R. Reiter return (error);
550cea6c86cSDoug Rabson }
551cea6c86cSDoug Rabson
55209dbb404SBrian Somers int
linker_reference_module(const char * modname,struct mod_depend * verinfo,linker_file_t * result)55396987c74SBrian Somers linker_reference_module(const char *modname, struct mod_depend *verinfo,
55496987c74SBrian Somers linker_file_t *result)
55509dbb404SBrian Somers {
55696987c74SBrian Somers modlist_t mod;
557aeeb017bSJohn Baldwin int error;
55896987c74SBrian Somers
5593a277424SMark Johnston sx_xlock(&kld_sx);
56096987c74SBrian Somers if ((mod = modlist_lookup2(modname, verinfo)) != NULL) {
56196987c74SBrian Somers *result = mod->container;
56296987c74SBrian Somers (*result)->refs++;
5633a277424SMark Johnston sx_xunlock(&kld_sx);
56496987c74SBrian Somers return (0);
56596987c74SBrian Somers }
56696987c74SBrian Somers
567aeeb017bSJohn Baldwin error = linker_load_module(NULL, modname, NULL, verinfo, result);
5683a277424SMark Johnston sx_xunlock(&kld_sx);
569aeeb017bSJohn Baldwin return (error);
570aeeb017bSJohn Baldwin }
571aeeb017bSJohn Baldwin
572aeeb017bSJohn Baldwin int
linker_release_module(const char * modname,struct mod_depend * verinfo,linker_file_t lf)573aeeb017bSJohn Baldwin linker_release_module(const char *modname, struct mod_depend *verinfo,
574aeeb017bSJohn Baldwin linker_file_t lf)
575aeeb017bSJohn Baldwin {
576aeeb017bSJohn Baldwin modlist_t mod;
577aeeb017bSJohn Baldwin int error;
578aeeb017bSJohn Baldwin
5793a277424SMark Johnston sx_xlock(&kld_sx);
580aeeb017bSJohn Baldwin if (lf == NULL) {
581aeeb017bSJohn Baldwin KASSERT(modname != NULL,
582aeeb017bSJohn Baldwin ("linker_release_module: no file or name"));
583aeeb017bSJohn Baldwin mod = modlist_lookup2(modname, verinfo);
584aeeb017bSJohn Baldwin if (mod == NULL) {
5853a277424SMark Johnston sx_xunlock(&kld_sx);
586aeeb017bSJohn Baldwin return (ESRCH);
587aeeb017bSJohn Baldwin }
588aeeb017bSJohn Baldwin lf = mod->container;
589aeeb017bSJohn Baldwin } else
590aeeb017bSJohn Baldwin KASSERT(modname == NULL && verinfo == NULL,
591aeeb017bSJohn Baldwin ("linker_release_module: both file and name"));
592aeeb017bSJohn Baldwin error = linker_file_unload(lf, LINKER_UNLOAD_NORMAL);
5933a277424SMark Johnston sx_xunlock(&kld_sx);
594aeeb017bSJohn Baldwin return (error);
59509dbb404SBrian Somers }
59609dbb404SBrian Somers
597f462ce3eSJohn Baldwin static linker_file_t
linker_find_file_by_name(const char * filename)598cea6c86cSDoug Rabson linker_find_file_by_name(const char *filename)
599cea6c86cSDoug Rabson {
6003a600aeaSJohn Baldwin linker_file_t lf;
60178377454SPeter Wemm char *koname;
60278377454SPeter Wemm
603a163d034SWarner Losh koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
60478377454SPeter Wemm sprintf(koname, "%s.ko", filename);
605cea6c86cSDoug Rabson
6063a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
607fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &linker_files, link) {
6086c75a65aSDavid Malone if (strcmp(lf->filename, koname) == 0)
60978377454SPeter Wemm break;
6106c75a65aSDavid Malone if (strcmp(lf->filename, filename) == 0)
611cea6c86cSDoug Rabson break;
61278377454SPeter Wemm }
61378377454SPeter Wemm free(koname, M_LINKER);
6148e92b63cSAndrew R. Reiter return (lf);
615cea6c86cSDoug Rabson }
616cea6c86cSDoug Rabson
617f462ce3eSJohn Baldwin static linker_file_t
linker_find_file_by_id(int fileid)618cea6c86cSDoug Rabson linker_find_file_by_id(int fileid)
619cea6c86cSDoug Rabson {
6203a600aeaSJohn Baldwin linker_file_t lf;
621cea6c86cSDoug Rabson
6223a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
623fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &linker_files, link)
624498eccc9SJohn Baldwin if (lf->id == fileid && lf->flags & LINKER_FILE_LINKED)
625cea6c86cSDoug Rabson break;
6268e92b63cSAndrew R. Reiter return (lf);
627cea6c86cSDoug Rabson }
628cea6c86cSDoug Rabson
62993215106SJohn Baldwin int
linker_file_foreach(linker_predicate_t * predicate,void * context)63093215106SJohn Baldwin linker_file_foreach(linker_predicate_t *predicate, void *context)
63193215106SJohn Baldwin {
63293215106SJohn Baldwin linker_file_t lf;
63393215106SJohn Baldwin int retval = 0;
63493215106SJohn Baldwin
6353a277424SMark Johnston sx_xlock(&kld_sx);
63693215106SJohn Baldwin TAILQ_FOREACH(lf, &linker_files, link) {
63793215106SJohn Baldwin retval = predicate(lf, context);
63893215106SJohn Baldwin if (retval != 0)
63993215106SJohn Baldwin break;
64093215106SJohn Baldwin }
6413a277424SMark Johnston sx_xunlock(&kld_sx);
64293215106SJohn Baldwin return (retval);
64393215106SJohn Baldwin }
64493215106SJohn Baldwin
645cea6c86cSDoug Rabson linker_file_t
linker_make_file(const char * pathname,linker_class_t lc)646326e27d8SDoug Rabson linker_make_file(const char *pathname, linker_class_t lc)
647cea6c86cSDoug Rabson {
6488e92b63cSAndrew R. Reiter linker_file_t lf;
64951f3fe7aSPeter Wemm const char *filename;
65051f3fe7aSPeter Wemm
6513a277424SMark Johnston if (!cold)
6523a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
65354823af2SPeter Wemm filename = linker_basename(pathname);
654cea6c86cSDoug Rabson
6551676805cSJohn Birrell KLD_DPF(FILE, ("linker_make_file: new file, filename='%s' for pathname='%s'\n", filename, pathname));
656a163d034SWarner Losh lf = (linker_file_t)kobj_create((kobj_class_t)lc, M_LINKER, M_WAITOK);
6578e92b63cSAndrew R. Reiter if (lf == NULL)
6583a600aeaSJohn Baldwin return (NULL);
6590067051fSMarcel Moolenaar lf->ctors_addr = 0;
6600067051fSMarcel Moolenaar lf->ctors_size = 0;
6617ef5c19bSMark Johnston lf->ctors_invoked = LF_NONE;
6629e575fadSMark Johnston lf->dtors_addr = 0;
6639e575fadSMark Johnston lf->dtors_size = 0;
664cea6c86cSDoug Rabson lf->refs = 1;
665cea6c86cSDoug Rabson lf->userrefs = 0;
666149a155cSDoug Rabson lf->flags = 0;
667196f2f42SMark Johnston lf->filename = strdup(filename, M_LINKER);
668196f2f42SMark Johnston lf->pathname = strdup(pathname, M_LINKER);
669b489b407SAndrew R. Reiter LINKER_GET_NEXT_FILE_ID(lf->id);
670cea6c86cSDoug Rabson lf->ndeps = 0;
671cea6c86cSDoug Rabson lf->deps = NULL;
6721676805cSJohn Birrell lf->loadcnt = ++loadcnt;
673b19c9deaSIan Lepore #ifdef __arm__
674b19c9deaSIan Lepore lf->exidx_addr = 0;
675b19c9deaSIan Lepore lf->exidx_size = 0;
676b19c9deaSIan Lepore #endif
677cea6c86cSDoug Rabson STAILQ_INIT(&lf->common);
678cea6c86cSDoug Rabson TAILQ_INIT(&lf->modules);
6794033a962SGreg Lehey TAILQ_INSERT_TAIL(&linker_files, lf, link);
6808e92b63cSAndrew R. Reiter return (lf);
681cea6c86cSDoug Rabson }
682cea6c86cSDoug Rabson
683cea6c86cSDoug Rabson int
linker_file_unload(linker_file_t file,int flags)68465a311fcSPoul-Henning Kamp linker_file_unload(linker_file_t file, int flags)
685cea6c86cSDoug Rabson {
686cea6c86cSDoug Rabson module_t mod, next;
68754823af2SPeter Wemm modlist_t ml, nextml;
688cea6c86cSDoug Rabson struct common_symbol *cp;
6898e92b63cSAndrew R. Reiter int error, i;
690cea6c86cSDoug Rabson
6918e92b63cSAndrew R. Reiter /* Refuse to unload modules if securelevel raised. */
6920304c731SJamie Gritton if (prison0.pr_securelevel > 0)
6938e92b63cSAndrew R. Reiter return (EPERM);
6946c66bbedSArchie Cobbs
6953a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
69651f3fe7aSPeter Wemm KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
6972fa6cc80SJohn Baldwin
6982fa6cc80SJohn Baldwin /* Easy case of just dropping a reference. */
6992fa6cc80SJohn Baldwin if (file->refs > 1) {
7002fa6cc80SJohn Baldwin file->refs--;
7012fa6cc80SJohn Baldwin return (0);
7022fa6cc80SJohn Baldwin }
7032fa6cc80SJohn Baldwin
7048f725462SMark Johnston /* Give eventhandlers a chance to prevent the unload. */
7058f725462SMark Johnston error = 0;
7068f725462SMark Johnston EVENTHANDLER_INVOKE(kld_unload_try, file, &error);
7078f725462SMark Johnston if (error != 0)
7088f725462SMark Johnston return (EBUSY);
7098f725462SMark Johnston
7108e92b63cSAndrew R. Reiter KLD_DPF(FILE, ("linker_file_unload: file is unloading,"
7118e92b63cSAndrew R. Reiter " informing modules\n"));
7128e92b63cSAndrew R. Reiter
713cea6c86cSDoug Rabson /*
714b4824b48SJohn Baldwin * Quiesce all the modules to give them a chance to veto the unload.
715b4824b48SJohn Baldwin */
716b4824b48SJohn Baldwin MOD_SLOCK;
717b4824b48SJohn Baldwin for (mod = TAILQ_FIRST(&file->modules); mod;
718b4824b48SJohn Baldwin mod = module_getfnext(mod)) {
719b4824b48SJohn Baldwin error = module_quiesce(mod);
720b4824b48SJohn Baldwin if (error != 0 && flags != LINKER_UNLOAD_FORCE) {
721b4824b48SJohn Baldwin KLD_DPF(FILE, ("linker_file_unload: module %s"
722b4824b48SJohn Baldwin " vetoed unload\n", module_getname(mod)));
723b4824b48SJohn Baldwin /*
724b4824b48SJohn Baldwin * XXX: Do we need to tell all the quiesced modules
725b4824b48SJohn Baldwin * that they can resume work now via a new module
726b4824b48SJohn Baldwin * event?
727b4824b48SJohn Baldwin */
728b4824b48SJohn Baldwin MOD_SUNLOCK;
729b4824b48SJohn Baldwin return (error);
730b4824b48SJohn Baldwin }
731b4824b48SJohn Baldwin }
732b4824b48SJohn Baldwin MOD_SUNLOCK;
733b4824b48SJohn Baldwin
734b4824b48SJohn Baldwin /*
735b4824b48SJohn Baldwin * Inform any modules associated with this file that they are
7367582954eSJohn Baldwin * being unloaded.
737cea6c86cSDoug Rabson */
7389b3851e9SAndrew R. Reiter MOD_XLOCK;
739cea6c86cSDoug Rabson for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) {
740cea6c86cSDoug Rabson next = module_getfnext(mod);
7419b3851e9SAndrew R. Reiter MOD_XUNLOCK;
742cea6c86cSDoug Rabson
743cea6c86cSDoug Rabson /*
744cea6c86cSDoug Rabson * Give the module a chance to veto the unload.
745cea6c86cSDoug Rabson */
746b4824b48SJohn Baldwin if ((error = module_unload(mod)) != 0) {
747c5e7f064SAndrey V. Elsukov #ifdef KLD_DEBUG
748c5e7f064SAndrey V. Elsukov MOD_SLOCK;
749b4824b48SJohn Baldwin KLD_DPF(FILE, ("linker_file_unload: module %s"
7503ea6157eSOleksandr Tymoshenko " failed unload\n", module_getname(mod)));
751c5e7f064SAndrey V. Elsukov MOD_SUNLOCK;
752c5e7f064SAndrey V. Elsukov #endif
7532fa6cc80SJohn Baldwin return (error);
7542fa6cc80SJohn Baldwin }
7559b3851e9SAndrew R. Reiter MOD_XLOCK;
756cea6c86cSDoug Rabson module_release(mod);
757cea6c86cSDoug Rabson }
7589b3851e9SAndrew R. Reiter MOD_XUNLOCK;
7592fa6cc80SJohn Baldwin
760b904477cSJohn Baldwin TAILQ_FOREACH_SAFE(ml, &found_modules, link, nextml) {
76124554d00SPeter Edwards if (ml->container == file) {
76254823af2SPeter Wemm TAILQ_REMOVE(&found_modules, ml, link);
76324554d00SPeter Edwards free(ml, M_LINKER);
76424554d00SPeter Edwards }
76554823af2SPeter Wemm }
76654823af2SPeter Wemm
7678e92b63cSAndrew R. Reiter /*
7688e92b63cSAndrew R. Reiter * Don't try to run SYSUNINITs if we are unloaded due to a
7698e92b63cSAndrew R. Reiter * link error.
7708e92b63cSAndrew R. Reiter */
771ce02431fSDoug Rabson if (file->flags & LINKER_FILE_LINKED) {
772e4d9b9ebSJohn Baldwin file->flags &= ~LINKER_FILE_LINKED;
773550374efSAndriy Gapon linker_file_unregister_sysctls(file);
774693593b6SAndriy Gapon linker_file_sysuninit(file);
775ce02431fSDoug Rabson }
7764033a962SGreg Lehey TAILQ_REMOVE(&linker_files, file, link);
777cea6c86cSDoug Rabson
77854823af2SPeter Wemm if (file->deps) {
779cea6c86cSDoug Rabson for (i = 0; i < file->ndeps; i++)
78065a311fcSPoul-Henning Kamp linker_file_unload(file->deps[i], flags);
781cea6c86cSDoug Rabson free(file->deps, M_LINKER);
78254823af2SPeter Wemm file->deps = NULL;
78354823af2SPeter Wemm }
7840f8e0c3dSJohn Baldwin while ((cp = STAILQ_FIRST(&file->common)) != NULL) {
7850f8e0c3dSJohn Baldwin STAILQ_REMOVE_HEAD(&file->common, link);
786cea6c86cSDoug Rabson free(cp, M_LINKER);
787cea6c86cSDoug Rabson }
788cea6c86cSDoug Rabson
789326e27d8SDoug Rabson LINKER_UNLOAD(file);
7908f725462SMark Johnston
7918f725462SMark Johnston EVENTHANDLER_INVOKE(kld_unload, file->filename, file->address,
7928f725462SMark Johnston file->size);
7938f725462SMark Johnston
79454823af2SPeter Wemm if (file->filename) {
795326e27d8SDoug Rabson free(file->filename, M_LINKER);
79654823af2SPeter Wemm file->filename = NULL;
79754823af2SPeter Wemm }
7981676805cSJohn Birrell if (file->pathname) {
7991676805cSJohn Birrell free(file->pathname, M_LINKER);
8001676805cSJohn Birrell file->pathname = NULL;
8011676805cSJohn Birrell }
802326e27d8SDoug Rabson kobj_delete((kobj_t) file, M_LINKER);
8032fa6cc80SJohn Baldwin return (0);
804cea6c86cSDoug Rabson }
805cea6c86cSDoug Rabson
8064b3d6093SJohn Birrell int
linker_ctf_get(linker_file_t file,linker_ctf_t * lc)8074b3d6093SJohn Birrell linker_ctf_get(linker_file_t file, linker_ctf_t *lc)
8084b3d6093SJohn Birrell {
8094b3d6093SJohn Birrell return (LINKER_CTF_GET(file, lc));
8104b3d6093SJohn Birrell }
8114b3d6093SJohn Birrell
812c21bc6f3SBojan Novković int
linker_ctf_lookup_typename_ddb(linker_ctf_t * lc,const char * typename)813c21bc6f3SBojan Novković linker_ctf_lookup_typename_ddb(linker_ctf_t *lc, const char *typename)
814c21bc6f3SBojan Novković {
815c21bc6f3SBojan Novković #ifdef DDB
816c21bc6f3SBojan Novković linker_file_t lf;
817c21bc6f3SBojan Novković
818c21bc6f3SBojan Novković TAILQ_FOREACH (lf, &linker_files, link){
819c21bc6f3SBojan Novković if (LINKER_CTF_LOOKUP_TYPENAME(lf, lc, typename) == 0)
820c21bc6f3SBojan Novković return (0);
821c21bc6f3SBojan Novković }
822c21bc6f3SBojan Novković #endif
823c21bc6f3SBojan Novković return (ENOENT);
824c21bc6f3SBojan Novković }
825c21bc6f3SBojan Novković
826c21bc6f3SBojan Novković int
linker_ctf_lookup_sym_ddb(const char * symname,c_linker_sym_t * sym,linker_ctf_t * lc)827c21bc6f3SBojan Novković linker_ctf_lookup_sym_ddb(const char *symname, c_linker_sym_t *sym,
828c21bc6f3SBojan Novković linker_ctf_t *lc)
829c21bc6f3SBojan Novković {
830c21bc6f3SBojan Novković #ifdef DDB
831c21bc6f3SBojan Novković linker_file_t lf;
832c21bc6f3SBojan Novković
833c21bc6f3SBojan Novković TAILQ_FOREACH (lf, &linker_files, link){
834c21bc6f3SBojan Novković if (LINKER_LOOKUP_DEBUG_SYMBOL_CTF(lf, symname, sym, lc) == 0)
835c21bc6f3SBojan Novković return (0);
836c21bc6f3SBojan Novković }
837c21bc6f3SBojan Novković #endif
838c21bc6f3SBojan Novković return (ENOENT);
839c21bc6f3SBojan Novković }
840c21bc6f3SBojan Novković
8411c7307cfSZhenlei Huang static void
linker_file_add_dependency(linker_file_t file,linker_file_t dep)8427b9716baSIan Dowse linker_file_add_dependency(linker_file_t file, linker_file_t dep)
843cea6c86cSDoug Rabson {
844cea6c86cSDoug Rabson linker_file_t *newdeps;
845cea6c86cSDoug Rabson
8463a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
84714fcb4b4SKonstantin Belousov file->deps = realloc(file->deps, (file->ndeps + 1) * sizeof(*newdeps),
84814fcb4b4SKonstantin Belousov M_LINKER, M_WAITOK | M_ZERO);
849cea6c86cSDoug Rabson file->deps[file->ndeps] = dep;
850cea6c86cSDoug Rabson file->ndeps++;
8513ea6157eSOleksandr Tymoshenko KLD_DPF(FILE, ("linker_file_add_dependency:"
8523ea6157eSOleksandr Tymoshenko " adding %s as dependency for %s\n",
8533ea6157eSOleksandr Tymoshenko dep->filename, file->filename));
854cea6c86cSDoug Rabson }
855cea6c86cSDoug Rabson
856f41325dbSPeter Wemm /*
8578e92b63cSAndrew R. Reiter * Locate a linker set and its contents. This is a helper function to avoid
8589dd44bd7SJohn Baldwin * linker_if.h exposure elsewhere. Note: firstp and lastp are really void **.
8599dd44bd7SJohn Baldwin * This function is used in this file so we can avoid having lots of (void **)
8609dd44bd7SJohn Baldwin * casts.
861f41325dbSPeter Wemm */
86215c2b301SJohn Baldwin int
linker_file_lookup_set(linker_file_t file,const char * name,void * firstp,void * lastp,int * countp)86315c2b301SJohn Baldwin linker_file_lookup_set(linker_file_t file, const char *name,
864f41325dbSPeter Wemm void *firstp, void *lastp, int *countp)
865f41325dbSPeter Wemm {
866f41325dbSPeter Wemm
8670770f164SMark Johnston sx_assert(&kld_sx, SA_LOCKED);
8680770f164SMark Johnston return (LINKER_LOOKUP_SET(file, name, firstp, lastp, countp));
869f41325dbSPeter Wemm }
870f41325dbSPeter Wemm
871f6c15301SJohn Birrell /*
872f6c15301SJohn Birrell * List all functions in a file.
873f6c15301SJohn Birrell */
874f6c15301SJohn Birrell int
linker_file_function_listall(linker_file_t lf,linker_function_nameval_callback_t callback_func,void * arg)875f6c15301SJohn Birrell linker_file_function_listall(linker_file_t lf,
8764b3d6093SJohn Birrell linker_function_nameval_callback_t callback_func, void *arg)
877f6c15301SJohn Birrell {
878f6c15301SJohn Birrell return (LINKER_EACH_FUNCTION_NAMEVAL(lf, callback_func, arg));
879f6c15301SJohn Birrell }
880f6c15301SJohn Birrell
881cea6c86cSDoug Rabson caddr_t
linker_file_lookup_symbol(linker_file_t file,const char * name,int deps)882cea6c86cSDoug Rabson linker_file_lookup_symbol(linker_file_t file, const char *name, int deps)
883cea6c86cSDoug Rabson {
88470f37788SJohn Baldwin caddr_t sym;
88570f37788SJohn Baldwin int locked;
88670f37788SJohn Baldwin
8873a277424SMark Johnston locked = sx_xlocked(&kld_sx);
88870f37788SJohn Baldwin if (!locked)
8893a277424SMark Johnston sx_xlock(&kld_sx);
89070f37788SJohn Baldwin sym = linker_file_lookup_symbol_internal(file, name, deps);
89170f37788SJohn Baldwin if (!locked)
8923a277424SMark Johnston sx_xunlock(&kld_sx);
89370f37788SJohn Baldwin return (sym);
89470f37788SJohn Baldwin }
89570f37788SJohn Baldwin
89670f37788SJohn Baldwin static caddr_t
linker_file_lookup_symbol_internal(linker_file_t file,const char * name,int deps)89770f37788SJohn Baldwin linker_file_lookup_symbol_internal(linker_file_t file, const char *name,
89870f37788SJohn Baldwin int deps)
89970f37788SJohn Baldwin {
900fe08c21aSMatthew Dillon c_linker_sym_t sym;
901a2c99e3eSDoug Rabson linker_symval_t symval;
902cea6c86cSDoug Rabson caddr_t address;
903cea6c86cSDoug Rabson size_t common_size = 0;
9046c75a65aSDavid Malone int i;
905cea6c86cSDoug Rabson
9063a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
9077251b4bfSJake Burkholder KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%p, name=%s, deps=%d\n",
908cea6c86cSDoug Rabson file, name, deps));
909cea6c86cSDoug Rabson
91028a59100SAustin Shafer /*
91128a59100SAustin Shafer * Treat the __this_linker_file as a special symbol. This is a
91228a59100SAustin Shafer * global that linuxkpi uses to populate the THIS_MODULE
91328a59100SAustin Shafer * value. In this case we can simply return the linker_file_t.
91428a59100SAustin Shafer *
91528a59100SAustin Shafer * Modules compiled statically into the kernel are assigned NULL.
91628a59100SAustin Shafer */
91728a59100SAustin Shafer if (strcmp(name, "__this_linker_file") == 0) {
91828a59100SAustin Shafer address = (file == linker_kernel_file) ? NULL : (caddr_t)file;
91928a59100SAustin Shafer KLD_DPF(SYM, ("linker_file_lookup_symbol: resolving special "
92028a59100SAustin Shafer "symbol __this_linker_file to %p\n", address));
92128a59100SAustin Shafer return (address);
92228a59100SAustin Shafer }
92328a59100SAustin Shafer
924326e27d8SDoug Rabson if (LINKER_LOOKUP_SYMBOL(file, name, &sym) == 0) {
925326e27d8SDoug Rabson LINKER_SYMBOL_VALUES(file, sym, &symval);
926a2c99e3eSDoug Rabson if (symval.value == 0)
927cea6c86cSDoug Rabson /*
9288e92b63cSAndrew R. Reiter * For commons, first look them up in the
9298e92b63cSAndrew R. Reiter * dependencies and only allocate space if not found
9308e92b63cSAndrew R. Reiter * there.
931cea6c86cSDoug Rabson */
932a2c99e3eSDoug Rabson common_size = symval.size;
93351f3fe7aSPeter Wemm else {
9348e92b63cSAndrew R. Reiter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol"
9357251b4bfSJake Burkholder ".value=%p\n", symval.value));
9368e92b63cSAndrew R. Reiter return (symval.value);
937a2c99e3eSDoug Rabson }
93851f3fe7aSPeter Wemm }
939d7dfdda2SPeter Wemm if (deps) {
940cea6c86cSDoug Rabson for (i = 0; i < file->ndeps; i++) {
94170f37788SJohn Baldwin address = linker_file_lookup_symbol_internal(
94270f37788SJohn Baldwin file->deps[i], name, 0);
94351f3fe7aSPeter Wemm if (address) {
9448e92b63cSAndrew R. Reiter KLD_DPF(SYM, ("linker_file_lookup_symbol:"
9457251b4bfSJake Burkholder " deps value=%p\n", address));
9468e92b63cSAndrew R. Reiter return (address);
947cea6c86cSDoug Rabson }
94851f3fe7aSPeter Wemm }
949d7dfdda2SPeter Wemm }
950cea6c86cSDoug Rabson if (common_size > 0) {
951cea6c86cSDoug Rabson /*
952cea6c86cSDoug Rabson * This is a common symbol which was not found in the
9537b9716baSIan Dowse * dependencies. We maintain a simple common symbol table in
954cea6c86cSDoug Rabson * the file object.
955cea6c86cSDoug Rabson */
956cea6c86cSDoug Rabson struct common_symbol *cp;
957cea6c86cSDoug Rabson
9588e92b63cSAndrew R. Reiter STAILQ_FOREACH(cp, &file->common, link) {
9596c75a65aSDavid Malone if (strcmp(cp->name, name) == 0) {
9608e92b63cSAndrew R. Reiter KLD_DPF(SYM, ("linker_file_lookup_symbol:"
9617251b4bfSJake Burkholder " old common value=%p\n", cp->address));
9628e92b63cSAndrew R. Reiter return (cp->address);
96351f3fe7aSPeter Wemm }
9648e92b63cSAndrew R. Reiter }
965cea6c86cSDoug Rabson /*
966cea6c86cSDoug Rabson * Round the symbol size up to align.
967cea6c86cSDoug Rabson */
968cea6c86cSDoug Rabson common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
969cea6c86cSDoug Rabson cp = malloc(sizeof(struct common_symbol)
9708e92b63cSAndrew R. Reiter + common_size + strlen(name) + 1, M_LINKER,
971a163d034SWarner Losh M_WAITOK | M_ZERO);
972cea6c86cSDoug Rabson cp->address = (caddr_t)(cp + 1);
973cea6c86cSDoug Rabson cp->name = cp->address + common_size;
974cea6c86cSDoug Rabson strcpy(cp->name, name);
975cea6c86cSDoug Rabson bzero(cp->address, common_size);
976cea6c86cSDoug Rabson STAILQ_INSERT_TAIL(&file->common, cp, link);
977cea6c86cSDoug Rabson
9788e92b63cSAndrew R. Reiter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common"
9797251b4bfSJake Burkholder " value=%p\n", cp->address));
9808e92b63cSAndrew R. Reiter return (cp->address);
981cea6c86cSDoug Rabson }
98251f3fe7aSPeter Wemm KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n"));
9838e92b63cSAndrew R. Reiter return (0);
984cea6c86cSDoug Rabson }
985cea6c86cSDoug Rabson
98651f3fe7aSPeter Wemm /*
987cdd475b3SRobert Watson * Both DDB and stack(9) rely on the kernel linker to provide forward and
988cdd475b3SRobert Watson * backward lookup of symbols. However, DDB and sometimes stack(9) need to
989cdd475b3SRobert Watson * do this in a lockfree manner. We provide a set of internal helper
990cdd475b3SRobert Watson * routines to perform these operations without locks, and then wrappers that
991cdd475b3SRobert Watson * optionally lock.
99251f3fe7aSPeter Wemm *
993cdd475b3SRobert Watson * linker_debug_lookup() is ifdef DDB as currently it's only used by DDB.
99451f3fe7aSPeter Wemm */
995cdd475b3SRobert Watson #ifdef DDB
996cdd475b3SRobert Watson static int
linker_debug_lookup(const char * symstr,c_linker_sym_t * sym)997cdd475b3SRobert Watson linker_debug_lookup(const char *symstr, c_linker_sym_t *sym)
99851f3fe7aSPeter Wemm {
99951f3fe7aSPeter Wemm linker_file_t lf;
100051f3fe7aSPeter Wemm
1001fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &linker_files, link) {
100295c20fafSKonstantin Belousov if (LINKER_LOOKUP_DEBUG_SYMBOL(lf, symstr, sym) == 0)
10038e92b63cSAndrew R. Reiter return (0);
100451f3fe7aSPeter Wemm }
10058e92b63cSAndrew R. Reiter return (ENOENT);
100651f3fe7aSPeter Wemm }
1007cdd475b3SRobert Watson #endif
100851f3fe7aSPeter Wemm
1009cdd475b3SRobert Watson static int
linker_debug_search_symbol(caddr_t value,c_linker_sym_t * sym,long * diffp)1010cdd475b3SRobert Watson linker_debug_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp)
101151f3fe7aSPeter Wemm {
101251f3fe7aSPeter Wemm linker_file_t lf;
10138e92b63cSAndrew R. Reiter c_linker_sym_t best, es;
10148e92b63cSAndrew R. Reiter u_long diff, bestdiff, off;
101551f3fe7aSPeter Wemm
101651f3fe7aSPeter Wemm best = 0;
10178e92b63cSAndrew R. Reiter off = (uintptr_t)value;
101851f3fe7aSPeter Wemm bestdiff = off;
1019fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &linker_files, link) {
1020326e27d8SDoug Rabson if (LINKER_SEARCH_SYMBOL(lf, value, &es, &diff) != 0)
102151f3fe7aSPeter Wemm continue;
102251f3fe7aSPeter Wemm if (es != 0 && diff < bestdiff) {
102351f3fe7aSPeter Wemm best = es;
102451f3fe7aSPeter Wemm bestdiff = diff;
102551f3fe7aSPeter Wemm }
102651f3fe7aSPeter Wemm if (bestdiff == 0)
102751f3fe7aSPeter Wemm break;
102851f3fe7aSPeter Wemm }
102951f3fe7aSPeter Wemm if (best) {
103051f3fe7aSPeter Wemm *sym = best;
103151f3fe7aSPeter Wemm *diffp = bestdiff;
10328e92b63cSAndrew R. Reiter return (0);
103351f3fe7aSPeter Wemm } else {
103451f3fe7aSPeter Wemm *sym = 0;
103551f3fe7aSPeter Wemm *diffp = off;
10368e92b63cSAndrew R. Reiter return (ENOENT);
103751f3fe7aSPeter Wemm }
103851f3fe7aSPeter Wemm }
103951f3fe7aSPeter Wemm
1040cdd475b3SRobert Watson static int
linker_debug_symbol_values(c_linker_sym_t sym,linker_symval_t * symval)1041cdd475b3SRobert Watson linker_debug_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
104251f3fe7aSPeter Wemm {
104351f3fe7aSPeter Wemm linker_file_t lf;
104451f3fe7aSPeter Wemm
1045fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &linker_files, link) {
104672f66626SKonstantin Belousov if (LINKER_DEBUG_SYMBOL_VALUES(lf, sym, symval) == 0)
10478e92b63cSAndrew R. Reiter return (0);
104851f3fe7aSPeter Wemm }
10498e92b63cSAndrew R. Reiter return (ENOENT);
105051f3fe7aSPeter Wemm }
1051cdd475b3SRobert Watson
1052cdd475b3SRobert Watson static int
linker_debug_search_symbol_name(caddr_t value,char * buf,u_int buflen,long * offset)1053cdd475b3SRobert Watson linker_debug_search_symbol_name(caddr_t value, char *buf, u_int buflen,
1054cdd475b3SRobert Watson long *offset)
1055cdd475b3SRobert Watson {
1056cdd475b3SRobert Watson linker_symval_t symval;
1057cdd475b3SRobert Watson c_linker_sym_t sym;
1058cdd475b3SRobert Watson int error;
1059cdd475b3SRobert Watson
1060cdd475b3SRobert Watson *offset = 0;
1061cdd475b3SRobert Watson error = linker_debug_search_symbol(value, &sym, offset);
1062cdd475b3SRobert Watson if (error)
1063cdd475b3SRobert Watson return (error);
1064cdd475b3SRobert Watson error = linker_debug_symbol_values(sym, &symval);
1065cdd475b3SRobert Watson if (error)
1066cdd475b3SRobert Watson return (error);
1067cdd475b3SRobert Watson strlcpy(buf, symval.name, buflen);
1068cdd475b3SRobert Watson return (0);
1069cdd475b3SRobert Watson }
1070cdd475b3SRobert Watson
1071cdd475b3SRobert Watson /*
1072cdd475b3SRobert Watson * DDB Helpers. DDB has to look across multiple files with their own symbol
1073cdd475b3SRobert Watson * tables and string tables.
1074cdd475b3SRobert Watson *
1075cdd475b3SRobert Watson * Note that we do not obey list locking protocols here. We really don't need
1076cdd475b3SRobert Watson * DDB to hang because somebody's got the lock held. We'll take the chance
1077e3043798SPedro F. Giffuni * that the files list is inconsistent instead.
1078cdd475b3SRobert Watson */
107961548876SAndriy Gapon #ifdef DDB
1080cdd475b3SRobert Watson int
linker_ddb_lookup(const char * symstr,c_linker_sym_t * sym)1081cdd475b3SRobert Watson linker_ddb_lookup(const char *symstr, c_linker_sym_t *sym)
1082cdd475b3SRobert Watson {
1083cdd475b3SRobert Watson
1084cdd475b3SRobert Watson return (linker_debug_lookup(symstr, sym));
1085cdd475b3SRobert Watson }
108661548876SAndriy Gapon #endif
1087cdd475b3SRobert Watson
1088cdd475b3SRobert Watson int
linker_ddb_search_symbol(caddr_t value,c_linker_sym_t * sym,long * diffp)1089cdd475b3SRobert Watson linker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp)
1090cdd475b3SRobert Watson {
1091cdd475b3SRobert Watson
1092cdd475b3SRobert Watson return (linker_debug_search_symbol(value, sym, diffp));
1093cdd475b3SRobert Watson }
1094cdd475b3SRobert Watson
1095cdd475b3SRobert Watson int
linker_ddb_symbol_values(c_linker_sym_t sym,linker_symval_t * symval)1096cdd475b3SRobert Watson linker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
1097cdd475b3SRobert Watson {
1098cdd475b3SRobert Watson
1099cdd475b3SRobert Watson return (linker_debug_symbol_values(sym, symval));
1100cdd475b3SRobert Watson }
1101cdd475b3SRobert Watson
1102cdd475b3SRobert Watson int
linker_ddb_search_symbol_name(caddr_t value,char * buf,u_int buflen,long * offset)1103cdd475b3SRobert Watson linker_ddb_search_symbol_name(caddr_t value, char *buf, u_int buflen,
1104cdd475b3SRobert Watson long *offset)
1105cdd475b3SRobert Watson {
1106cdd475b3SRobert Watson
1107cdd475b3SRobert Watson return (linker_debug_search_symbol_name(value, buf, buflen, offset));
1108cdd475b3SRobert Watson }
110951f3fe7aSPeter Wemm
1110cea6c86cSDoug Rabson /*
1111cdd475b3SRobert Watson * stack(9) helper for non-debugging environemnts. Unlike DDB helpers, we do
1112cdd475b3SRobert Watson * obey locking protocols, and offer a significantly less complex interface.
1113cdd475b3SRobert Watson */
1114cdd475b3SRobert Watson int
linker_search_symbol_name_flags(caddr_t value,char * buf,u_int buflen,long * offset,int flags)1115d158fa4aSConrad Meyer linker_search_symbol_name_flags(caddr_t value, char *buf, u_int buflen,
1116d158fa4aSConrad Meyer long *offset, int flags)
1117cdd475b3SRobert Watson {
1118d90d4eb2SPawel Jakub Dawidek int error;
1119cdd475b3SRobert Watson
1120d158fa4aSConrad Meyer KASSERT((flags & (M_NOWAIT | M_WAITOK)) != 0 &&
1121d158fa4aSConrad Meyer (flags & (M_NOWAIT | M_WAITOK)) != (M_NOWAIT | M_WAITOK),
1122d158fa4aSConrad Meyer ("%s: bad flags: 0x%x", __func__, flags));
1123d158fa4aSConrad Meyer
1124d158fa4aSConrad Meyer if (flags & M_NOWAIT) {
1125d158fa4aSConrad Meyer if (!sx_try_slock(&kld_sx))
1126d158fa4aSConrad Meyer return (EWOULDBLOCK);
1127d158fa4aSConrad Meyer } else
11282afec8edSMateusz Guzik sx_slock(&kld_sx);
1129d158fa4aSConrad Meyer
1130cdd475b3SRobert Watson error = linker_debug_search_symbol_name(value, buf, buflen, offset);
11312afec8edSMateusz Guzik sx_sunlock(&kld_sx);
1132cdd475b3SRobert Watson return (error);
1133cdd475b3SRobert Watson }
1134cdd475b3SRobert Watson
1135d158fa4aSConrad Meyer int
linker_search_symbol_name(caddr_t value,char * buf,u_int buflen,long * offset)1136d158fa4aSConrad Meyer linker_search_symbol_name(caddr_t value, char *buf, u_int buflen,
1137d158fa4aSConrad Meyer long *offset)
1138d158fa4aSConrad Meyer {
1139d158fa4aSConrad Meyer
1140d158fa4aSConrad Meyer return (linker_search_symbol_name_flags(value, buf, buflen, offset,
1141d158fa4aSConrad Meyer M_WAITOK));
1142d158fa4aSConrad Meyer }
1143d158fa4aSConrad Meyer
1144e266a0f7SKonstantin Belousov int
linker_kldload_busy(int flags)1145e266a0f7SKonstantin Belousov linker_kldload_busy(int flags)
1146e266a0f7SKonstantin Belousov {
1147e266a0f7SKonstantin Belousov int error;
1148e266a0f7SKonstantin Belousov
1149e266a0f7SKonstantin Belousov MPASS((flags & ~(LINKER_UB_UNLOCK | LINKER_UB_LOCKED |
1150e266a0f7SKonstantin Belousov LINKER_UB_PCATCH)) == 0);
1151e266a0f7SKonstantin Belousov if ((flags & LINKER_UB_LOCKED) != 0)
1152e266a0f7SKonstantin Belousov sx_assert(&kld_sx, SA_XLOCKED);
1153e266a0f7SKonstantin Belousov
1154e266a0f7SKonstantin Belousov if ((flags & LINKER_UB_LOCKED) == 0)
1155e266a0f7SKonstantin Belousov sx_xlock(&kld_sx);
11564f924a78SKonstantin Belousov while (kld_busy > 0) {
11574f924a78SKonstantin Belousov if (kld_busy_owner == curthread)
11584f924a78SKonstantin Belousov break;
1159e266a0f7SKonstantin Belousov error = sx_sleep(&kld_busy, &kld_sx,
1160e266a0f7SKonstantin Belousov (flags & LINKER_UB_PCATCH) != 0 ? PCATCH : 0,
1161e266a0f7SKonstantin Belousov "kldbusy", 0);
1162e266a0f7SKonstantin Belousov if (error != 0) {
1163e266a0f7SKonstantin Belousov if ((flags & LINKER_UB_UNLOCK) != 0)
1164e266a0f7SKonstantin Belousov sx_xunlock(&kld_sx);
1165e266a0f7SKonstantin Belousov return (error);
1166e266a0f7SKonstantin Belousov }
1167e266a0f7SKonstantin Belousov }
11684f924a78SKonstantin Belousov kld_busy++;
11694f924a78SKonstantin Belousov kld_busy_owner = curthread;
1170e266a0f7SKonstantin Belousov if ((flags & LINKER_UB_UNLOCK) != 0)
1171e266a0f7SKonstantin Belousov sx_xunlock(&kld_sx);
1172e266a0f7SKonstantin Belousov return (0);
1173e266a0f7SKonstantin Belousov }
1174e266a0f7SKonstantin Belousov
1175e266a0f7SKonstantin Belousov void
linker_kldload_unbusy(int flags)1176e266a0f7SKonstantin Belousov linker_kldload_unbusy(int flags)
1177e266a0f7SKonstantin Belousov {
1178e266a0f7SKonstantin Belousov MPASS((flags & ~LINKER_UB_LOCKED) == 0);
1179e266a0f7SKonstantin Belousov if ((flags & LINKER_UB_LOCKED) != 0)
1180e266a0f7SKonstantin Belousov sx_assert(&kld_sx, SA_XLOCKED);
1181e266a0f7SKonstantin Belousov
1182e266a0f7SKonstantin Belousov if ((flags & LINKER_UB_LOCKED) == 0)
1183e266a0f7SKonstantin Belousov sx_xlock(&kld_sx);
11844f924a78SKonstantin Belousov MPASS(kld_busy > 0);
11854f924a78SKonstantin Belousov if (kld_busy_owner != curthread)
11864f924a78SKonstantin Belousov panic("linker_kldload_unbusy done by not owning thread %p",
11874f924a78SKonstantin Belousov kld_busy_owner);
11884f924a78SKonstantin Belousov kld_busy--;
11894f924a78SKonstantin Belousov if (kld_busy == 0) {
11904f924a78SKonstantin Belousov kld_busy_owner = NULL;
1191e266a0f7SKonstantin Belousov wakeup(&kld_busy);
11924f924a78SKonstantin Belousov }
1193e266a0f7SKonstantin Belousov sx_xunlock(&kld_sx);
1194e266a0f7SKonstantin Belousov }
1195e266a0f7SKonstantin Belousov
1196cdd475b3SRobert Watson /*
1197cea6c86cSDoug Rabson * Syscalls.
1198cea6c86cSDoug Rabson */
1199cea6c86cSDoug Rabson int
kern_kldload(struct thread * td,const char * file,int * fileid)1200d5388587SJohn Baldwin kern_kldload(struct thread *td, const char *file, int *fileid)
1201cea6c86cSDoug Rabson {
1202d5388587SJohn Baldwin const char *kldname, *modname;
1203cea6c86cSDoug Rabson linker_file_t lf;
1204d5388587SJohn Baldwin int error;
1205c457a440SAndrew R. Reiter
1206517f30c2SAndrew R. Reiter if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
1207d5388587SJohn Baldwin return (error);
1208517f30c2SAndrew R. Reiter
1209acd3428bSRobert Watson if ((error = priv_check(td, PRIV_KLD_LOAD)) != 0)
1210d5388587SJohn Baldwin return (error);
1211cea6c86cSDoug Rabson
1212505222d3SPeter Wemm /*
12139dd44bd7SJohn Baldwin * If file does not contain a qualified name or any dot in it
12149dd44bd7SJohn Baldwin * (kldname.ko, or kldname.ver.ko) treat it as an interface
12158e92b63cSAndrew R. Reiter * name.
1216505222d3SPeter Wemm */
1217dc15eac0SEd Schouten if (strchr(file, '/') || strchr(file, '.')) {
1218d5388587SJohn Baldwin kldname = file;
1219505222d3SPeter Wemm modname = NULL;
1220505222d3SPeter Wemm } else {
1221505222d3SPeter Wemm kldname = NULL;
1222d5388587SJohn Baldwin modname = file;
122354823af2SPeter Wemm }
1224d5388587SJohn Baldwin
1225e266a0f7SKonstantin Belousov error = linker_kldload_busy(LINKER_UB_PCATCH);
1226e266a0f7SKonstantin Belousov if (error != 0) {
12273a277424SMark Johnston sx_xunlock(&kld_sx);
1228e266a0f7SKonstantin Belousov return (error);
122905a4755eSRyan Stone }
1230e266a0f7SKonstantin Belousov
1231e266a0f7SKonstantin Belousov /*
1232e266a0f7SKonstantin Belousov * It is possible that kldloaded module will attach a new ifnet,
1233e266a0f7SKonstantin Belousov * so vnet context must be set when this ocurs.
1234e266a0f7SKonstantin Belousov */
1235e266a0f7SKonstantin Belousov CURVNET_SET(TD_TO_VNET(td));
1236e266a0f7SKonstantin Belousov
1237e266a0f7SKonstantin Belousov error = linker_load_module(kldname, modname, NULL, NULL, &lf);
1238e266a0f7SKonstantin Belousov CURVNET_RESTORE();
1239e266a0f7SKonstantin Belousov
1240e266a0f7SKonstantin Belousov if (error == 0) {
1241cea6c86cSDoug Rabson lf->userrefs++;
1242d5388587SJohn Baldwin if (fileid != NULL)
1243d5388587SJohn Baldwin *fileid = lf->id;
1244e266a0f7SKonstantin Belousov }
1245e266a0f7SKonstantin Belousov linker_kldload_unbusy(LINKER_UB_LOCKED);
1246835a82eeSMatthew Dillon return (error);
1247cea6c86cSDoug Rabson }
1248cea6c86cSDoug Rabson
1249d5388587SJohn Baldwin int
sys_kldload(struct thread * td,struct kldload_args * uap)12508451d0ddSKip Macy sys_kldload(struct thread *td, struct kldload_args *uap)
1251d5388587SJohn Baldwin {
1252d5388587SJohn Baldwin char *pathname = NULL;
1253e1684acfSMarcel Moolenaar int error, fileid;
1254d5388587SJohn Baldwin
1255d5388587SJohn Baldwin td->td_retval[0] = -1;
1256d5388587SJohn Baldwin
1257d5388587SJohn Baldwin pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
1258d5388587SJohn Baldwin error = copyinstr(uap->file, pathname, MAXPATHLEN, NULL);
1259e1684acfSMarcel Moolenaar if (error == 0) {
1260e1684acfSMarcel Moolenaar error = kern_kldload(td, pathname, &fileid);
1261d5388587SJohn Baldwin if (error == 0)
1262e1684acfSMarcel Moolenaar td->td_retval[0] = fileid;
1263e1684acfSMarcel Moolenaar }
1264d5388587SJohn Baldwin free(pathname, M_TEMP);
1265d5388587SJohn Baldwin return (error);
1266d5388587SJohn Baldwin }
1267d5388587SJohn Baldwin
1268d5388587SJohn Baldwin int
kern_kldunload(struct thread * td,int fileid,int flags)126965a311fcSPoul-Henning Kamp kern_kldunload(struct thread *td, int fileid, int flags)
1270cea6c86cSDoug Rabson {
1271cea6c86cSDoug Rabson linker_file_t lf;
1272cea6c86cSDoug Rabson int error = 0;
1273cea6c86cSDoug Rabson
1274517f30c2SAndrew R. Reiter if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
1275d5388587SJohn Baldwin return (error);
1276517f30c2SAndrew R. Reiter
1277acd3428bSRobert Watson if ((error = priv_check(td, PRIV_KLD_UNLOAD)) != 0)
1278d5388587SJohn Baldwin return (error);
1279cea6c86cSDoug Rabson
1280e266a0f7SKonstantin Belousov error = linker_kldload_busy(LINKER_UB_PCATCH);
1281e266a0f7SKonstantin Belousov if (error != 0) {
1282e266a0f7SKonstantin Belousov sx_xunlock(&kld_sx);
1283e266a0f7SKonstantin Belousov return (error);
1284e266a0f7SKonstantin Belousov }
1285e266a0f7SKonstantin Belousov
128621ca7b57SMarko Zec CURVNET_SET(TD_TO_VNET(td));
128765a311fcSPoul-Henning Kamp lf = linker_find_file_by_id(fileid);
1288cea6c86cSDoug Rabson if (lf) {
1289cea6c86cSDoug Rabson KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
12901676805cSJohn Birrell
12918f725462SMark Johnston if (lf->userrefs == 0) {
129265a311fcSPoul-Henning Kamp /*
129365a311fcSPoul-Henning Kamp * XXX: maybe LINKER_UNLOAD_FORCE should override ?
129465a311fcSPoul-Henning Kamp */
12958e92b63cSAndrew R. Reiter printf("kldunload: attempt to unload file that was"
12968e92b63cSAndrew R. Reiter " loaded by the kernel\n");
1297cea6c86cSDoug Rabson error = EBUSY;
1298f43ff3e1SZhenlei Huang } else if (lf->refs > 1) {
1299f43ff3e1SZhenlei Huang error = EBUSY;
1300d5388587SJohn Baldwin } else {
1301d5388587SJohn Baldwin lf->userrefs--;
130265a311fcSPoul-Henning Kamp error = linker_file_unload(lf, flags);
1303e75a9dc0SPeter Wemm if (error)
1304461b36abSPeter Wemm lf->userrefs++;
1305d5388587SJohn Baldwin }
13068e92b63cSAndrew R. Reiter } else
1307cea6c86cSDoug Rabson error = ENOENT;
130821ca7b57SMarko Zec CURVNET_RESTORE();
1309e266a0f7SKonstantin Belousov linker_kldload_unbusy(LINKER_UB_LOCKED);
1310835a82eeSMatthew Dillon return (error);
1311cea6c86cSDoug Rabson }
1312cea6c86cSDoug Rabson
1313cea6c86cSDoug Rabson int
sys_kldunload(struct thread * td,struct kldunload_args * uap)13148451d0ddSKip Macy sys_kldunload(struct thread *td, struct kldunload_args *uap)
131565a311fcSPoul-Henning Kamp {
131665a311fcSPoul-Henning Kamp
131765a311fcSPoul-Henning Kamp return (kern_kldunload(td, uap->fileid, LINKER_UNLOAD_NORMAL));
131865a311fcSPoul-Henning Kamp }
131965a311fcSPoul-Henning Kamp
132065a311fcSPoul-Henning Kamp int
sys_kldunloadf(struct thread * td,struct kldunloadf_args * uap)13218451d0ddSKip Macy sys_kldunloadf(struct thread *td, struct kldunloadf_args *uap)
132265a311fcSPoul-Henning Kamp {
132365a311fcSPoul-Henning Kamp
132465a311fcSPoul-Henning Kamp if (uap->flags != LINKER_UNLOAD_NORMAL &&
132565a311fcSPoul-Henning Kamp uap->flags != LINKER_UNLOAD_FORCE)
132665a311fcSPoul-Henning Kamp return (EINVAL);
132765a311fcSPoul-Henning Kamp return (kern_kldunload(td, uap->fileid, uap->flags));
132865a311fcSPoul-Henning Kamp }
132965a311fcSPoul-Henning Kamp
133065a311fcSPoul-Henning Kamp int
sys_kldfind(struct thread * td,struct kldfind_args * uap)13318451d0ddSKip Macy sys_kldfind(struct thread *td, struct kldfind_args *uap)
1332cea6c86cSDoug Rabson {
133354823af2SPeter Wemm char *pathname;
133454823af2SPeter Wemm const char *filename;
1335cea6c86cSDoug Rabson linker_file_t lf;
133673a2437aSJohn Baldwin int error;
1337cea6c86cSDoug Rabson
1338a3df768bSRobert Watson #ifdef MAC
133930d239bcSRobert Watson error = mac_kld_check_stat(td->td_ucred);
1340a3df768bSRobert Watson if (error)
1341a3df768bSRobert Watson return (error);
1342a3df768bSRobert Watson #endif
1343a3df768bSRobert Watson
1344b40ce416SJulian Elischer td->td_retval[0] = -1;
1345cea6c86cSDoug Rabson
1346a163d034SWarner Losh pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
1347f97182acSAlfred Perlstein if ((error = copyinstr(uap->file, pathname, MAXPATHLEN, NULL)) != 0)
1348cea6c86cSDoug Rabson goto out;
1349cea6c86cSDoug Rabson
135054823af2SPeter Wemm filename = linker_basename(pathname);
13513a277424SMark Johnston sx_xlock(&kld_sx);
135254823af2SPeter Wemm lf = linker_find_file_by_name(filename);
1353cea6c86cSDoug Rabson if (lf)
1354b40ce416SJulian Elischer td->td_retval[0] = lf->id;
1355cea6c86cSDoug Rabson else
1356cea6c86cSDoug Rabson error = ENOENT;
13573a277424SMark Johnston sx_xunlock(&kld_sx);
1358cea6c86cSDoug Rabson out:
135954823af2SPeter Wemm free(pathname, M_TEMP);
1360835a82eeSMatthew Dillon return (error);
1361cea6c86cSDoug Rabson }
1362cea6c86cSDoug Rabson
1363cea6c86cSDoug Rabson int
sys_kldnext(struct thread * td,struct kldnext_args * uap)13648451d0ddSKip Macy sys_kldnext(struct thread *td, struct kldnext_args *uap)
1365cea6c86cSDoug Rabson {
1366cea6c86cSDoug Rabson linker_file_t lf;
1367cea6c86cSDoug Rabson int error = 0;
1368cea6c86cSDoug Rabson
1369a3df768bSRobert Watson #ifdef MAC
137030d239bcSRobert Watson error = mac_kld_check_stat(td->td_ucred);
1371a3df768bSRobert Watson if (error)
1372a3df768bSRobert Watson return (error);
1373a3df768bSRobert Watson #endif
1374a3df768bSRobert Watson
13753a277424SMark Johnston sx_xlock(&kld_sx);
1376498eccc9SJohn Baldwin if (uap->fileid == 0)
1377498eccc9SJohn Baldwin lf = TAILQ_FIRST(&linker_files);
1378498eccc9SJohn Baldwin else {
1379498eccc9SJohn Baldwin lf = linker_find_file_by_id(uap->fileid);
1380498eccc9SJohn Baldwin if (lf == NULL) {
1381498eccc9SJohn Baldwin error = ENOENT;
1382835a82eeSMatthew Dillon goto out;
1383cea6c86cSDoug Rabson }
1384498eccc9SJohn Baldwin lf = TAILQ_NEXT(lf, link);
1385498eccc9SJohn Baldwin }
1386498eccc9SJohn Baldwin
1387498eccc9SJohn Baldwin /* Skip partially loaded files. */
1388498eccc9SJohn Baldwin while (lf != NULL && !(lf->flags & LINKER_FILE_LINKED))
1389498eccc9SJohn Baldwin lf = TAILQ_NEXT(lf, link);
1390498eccc9SJohn Baldwin
1391498eccc9SJohn Baldwin if (lf)
1392498eccc9SJohn Baldwin td->td_retval[0] = lf->id;
1393cea6c86cSDoug Rabson else
1394b40ce416SJulian Elischer td->td_retval[0] = 0;
1395835a82eeSMatthew Dillon out:
13963a277424SMark Johnston sx_xunlock(&kld_sx);
1397835a82eeSMatthew Dillon return (error);
1398cea6c86cSDoug Rabson }
1399cea6c86cSDoug Rabson
1400cea6c86cSDoug Rabson int
sys_kldstat(struct thread * td,struct kldstat_args * uap)14018451d0ddSKip Macy sys_kldstat(struct thread *td, struct kldstat_args *uap)
1402cea6c86cSDoug Rabson {
1403edb01d11SGordon Tetlow struct kld_file_stat *stat;
140486665509SKonstantin Belousov int error, version;
14056b3d277aSJohn Baldwin
14066b3d277aSJohn Baldwin /*
14076b3d277aSJohn Baldwin * Check the version of the user's structure.
14086b3d277aSJohn Baldwin */
140986665509SKonstantin Belousov if ((error = copyin(&uap->stat->version, &version, sizeof(version)))
141086665509SKonstantin Belousov != 0)
14116b3d277aSJohn Baldwin return (error);
141286665509SKonstantin Belousov if (version != sizeof(struct kld_file_stat_1) &&
141386665509SKonstantin Belousov version != sizeof(struct kld_file_stat))
14146b3d277aSJohn Baldwin return (EINVAL);
1415cea6c86cSDoug Rabson
1416edb01d11SGordon Tetlow stat = malloc(sizeof(*stat), M_TEMP, M_WAITOK | M_ZERO);
1417edb01d11SGordon Tetlow error = kern_kldstat(td, uap->fileid, stat);
1418edb01d11SGordon Tetlow if (error == 0)
1419edb01d11SGordon Tetlow error = copyout(stat, uap->stat, version);
1420edb01d11SGordon Tetlow free(stat, M_TEMP);
142186665509SKonstantin Belousov return (error);
142286665509SKonstantin Belousov }
142386665509SKonstantin Belousov
142486665509SKonstantin Belousov int
kern_kldstat(struct thread * td,int fileid,struct kld_file_stat * stat)142586665509SKonstantin Belousov kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat)
142686665509SKonstantin Belousov {
142786665509SKonstantin Belousov linker_file_t lf;
142886665509SKonstantin Belousov int namelen;
1429a3df768bSRobert Watson #ifdef MAC
143086665509SKonstantin Belousov int error;
143186665509SKonstantin Belousov
143230d239bcSRobert Watson error = mac_kld_check_stat(td->td_ucred);
1433a3df768bSRobert Watson if (error)
1434a3df768bSRobert Watson return (error);
1435a3df768bSRobert Watson #endif
1436a3df768bSRobert Watson
14373a277424SMark Johnston sx_xlock(&kld_sx);
143886665509SKonstantin Belousov lf = linker_find_file_by_id(fileid);
14398e92b63cSAndrew R. Reiter if (lf == NULL) {
14403a277424SMark Johnston sx_xunlock(&kld_sx);
14416b3d277aSJohn Baldwin return (ENOENT);
1442cea6c86cSDoug Rabson }
1443cea6c86cSDoug Rabson
14441676805cSJohn Birrell /* Version 1 fields: */
1445cea6c86cSDoug Rabson namelen = strlen(lf->filename) + 1;
1446ca3fec50SConrad Meyer if (namelen > sizeof(stat->name))
1447ca3fec50SConrad Meyer namelen = sizeof(stat->name);
144886665509SKonstantin Belousov bcopy(lf->filename, &stat->name[0], namelen);
144986665509SKonstantin Belousov stat->refs = lf->refs;
145086665509SKonstantin Belousov stat->id = lf->id;
145186665509SKonstantin Belousov stat->address = lf->address;
145286665509SKonstantin Belousov stat->size = lf->size;
14531676805cSJohn Birrell /* Version 2 fields: */
14541676805cSJohn Birrell namelen = strlen(lf->pathname) + 1;
1455ca3fec50SConrad Meyer if (namelen > sizeof(stat->pathname))
1456ca3fec50SConrad Meyer namelen = sizeof(stat->pathname);
145786665509SKonstantin Belousov bcopy(lf->pathname, &stat->pathname[0], namelen);
14583a277424SMark Johnston sx_xunlock(&kld_sx);
1459cea6c86cSDoug Rabson
1460b40ce416SJulian Elischer td->td_retval[0] = 0;
146186665509SKonstantin Belousov return (0);
1462cea6c86cSDoug Rabson }
1463cea6c86cSDoug Rabson
14648a3aeac2SConrad Meyer #ifdef DDB
DB_COMMAND_FLAGS(kldstat,db_kldstat,DB_CMD_MEMSAFE)1465c84c5e00SMitchell Horne DB_COMMAND_FLAGS(kldstat, db_kldstat, DB_CMD_MEMSAFE)
14668a3aeac2SConrad Meyer {
14678a3aeac2SConrad Meyer linker_file_t lf;
14688a3aeac2SConrad Meyer
14698a3aeac2SConrad Meyer #define POINTER_WIDTH ((int)(sizeof(void *) * 2 + 2))
14708a3aeac2SConrad Meyer db_printf("Id Refs Address%*c Size Name\n", POINTER_WIDTH - 7, ' ');
14718a3aeac2SConrad Meyer #undef POINTER_WIDTH
14728a3aeac2SConrad Meyer TAILQ_FOREACH(lf, &linker_files, link) {
14738a3aeac2SConrad Meyer if (db_pager_quit)
14748a3aeac2SConrad Meyer return;
14758a3aeac2SConrad Meyer db_printf("%2d %4d %p %-8zx %s\n", lf->id, lf->refs,
14768a3aeac2SConrad Meyer lf->address, lf->size, lf->filename);
14778a3aeac2SConrad Meyer }
14788a3aeac2SConrad Meyer }
14798a3aeac2SConrad Meyer #endif /* DDB */
14808a3aeac2SConrad Meyer
1481cea6c86cSDoug Rabson int
sys_kldfirstmod(struct thread * td,struct kldfirstmod_args * uap)14828451d0ddSKip Macy sys_kldfirstmod(struct thread *td, struct kldfirstmod_args *uap)
1483cea6c86cSDoug Rabson {
1484cea6c86cSDoug Rabson linker_file_t lf;
14858e92b63cSAndrew R. Reiter module_t mp;
1486cea6c86cSDoug Rabson int error = 0;
1487cea6c86cSDoug Rabson
1488a3df768bSRobert Watson #ifdef MAC
148930d239bcSRobert Watson error = mac_kld_check_stat(td->td_ucred);
1490a3df768bSRobert Watson if (error)
1491a3df768bSRobert Watson return (error);
1492a3df768bSRobert Watson #endif
1493a3df768bSRobert Watson
14943a277424SMark Johnston sx_xlock(&kld_sx);
1495d1e405c5SAlfred Perlstein lf = linker_find_file_by_id(uap->fileid);
1496cea6c86cSDoug Rabson if (lf) {
14979b3851e9SAndrew R. Reiter MOD_SLOCK;
14988e92b63cSAndrew R. Reiter mp = TAILQ_FIRST(&lf->modules);
14998e92b63cSAndrew R. Reiter if (mp != NULL)
15008e92b63cSAndrew R. Reiter td->td_retval[0] = module_getid(mp);
1501cea6c86cSDoug Rabson else
1502b40ce416SJulian Elischer td->td_retval[0] = 0;
15039b3851e9SAndrew R. Reiter MOD_SUNLOCK;
15048e92b63cSAndrew R. Reiter } else
1505cea6c86cSDoug Rabson error = ENOENT;
15063a277424SMark Johnston sx_xunlock(&kld_sx);
1507835a82eeSMatthew Dillon return (error);
1508cea6c86cSDoug Rabson }
150951f3fe7aSPeter Wemm
1510ba031106SPeter Wemm int
sys_kldsym(struct thread * td,struct kldsym_args * uap)15118451d0ddSKip Macy sys_kldsym(struct thread *td, struct kldsym_args *uap)
1512ba031106SPeter Wemm {
1513ba031106SPeter Wemm char *symstr = NULL;
1514fe08c21aSMatthew Dillon c_linker_sym_t sym;
1515ba031106SPeter Wemm linker_symval_t symval;
1516ba031106SPeter Wemm linker_file_t lf;
1517ba031106SPeter Wemm struct kld_sym_lookup lookup;
1518ba031106SPeter Wemm int error = 0;
1519ba031106SPeter Wemm
1520a3df768bSRobert Watson #ifdef MAC
152130d239bcSRobert Watson error = mac_kld_check_stat(td->td_ucred);
1522a3df768bSRobert Watson if (error)
1523a3df768bSRobert Watson return (error);
1524a3df768bSRobert Watson #endif
1525a3df768bSRobert Watson
1526d1e405c5SAlfred Perlstein if ((error = copyin(uap->data, &lookup, sizeof(lookup))) != 0)
1527cbda6f95SJohn Baldwin return (error);
15288e92b63cSAndrew R. Reiter if (lookup.version != sizeof(lookup) ||
1529cbda6f95SJohn Baldwin uap->cmd != KLDSYM_LOOKUP)
1530cbda6f95SJohn Baldwin return (EINVAL);
1531a163d034SWarner Losh symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
1532d254af07SMatthew Dillon if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0)
1533ba031106SPeter Wemm goto out;
15343a277424SMark Johnston sx_xlock(&kld_sx);
1535d1e405c5SAlfred Perlstein if (uap->fileid != 0) {
1536d1e405c5SAlfred Perlstein lf = linker_find_file_by_id(uap->fileid);
1537cbda6f95SJohn Baldwin if (lf == NULL)
1538ba031106SPeter Wemm error = ENOENT;
1539cbda6f95SJohn Baldwin else if (LINKER_LOOKUP_SYMBOL(lf, symstr, &sym) == 0 &&
1540326e27d8SDoug Rabson LINKER_SYMBOL_VALUES(lf, sym, &symval) == 0) {
15410921e488SBruce Evans lookup.symvalue = (uintptr_t) symval.value;
1542ba031106SPeter Wemm lookup.symsize = symval.size;
1543f97182acSAlfred Perlstein error = copyout(&lookup, uap->data, sizeof(lookup));
1544ba031106SPeter Wemm } else
1545ba031106SPeter Wemm error = ENOENT;
1546ba031106SPeter Wemm } else {
1547fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &linker_files, link) {
1548326e27d8SDoug Rabson if (LINKER_LOOKUP_SYMBOL(lf, symstr, &sym) == 0 &&
1549326e27d8SDoug Rabson LINKER_SYMBOL_VALUES(lf, sym, &symval) == 0) {
15500921e488SBruce Evans lookup.symvalue = (uintptr_t)symval.value;
1551ba031106SPeter Wemm lookup.symsize = symval.size;
1552d1e405c5SAlfred Perlstein error = copyout(&lookup, uap->data,
15538e92b63cSAndrew R. Reiter sizeof(lookup));
1554ba031106SPeter Wemm break;
1555ba031106SPeter Wemm }
1556ba031106SPeter Wemm }
15578e92b63cSAndrew R. Reiter if (lf == NULL)
1558ba031106SPeter Wemm error = ENOENT;
1559ba031106SPeter Wemm }
15603a277424SMark Johnston sx_xunlock(&kld_sx);
1561cbda6f95SJohn Baldwin out:
1562cbda6f95SJohn Baldwin free(symstr, M_TEMP);
1563835a82eeSMatthew Dillon return (error);
1564ba031106SPeter Wemm }
1565ba031106SPeter Wemm
156651f3fe7aSPeter Wemm /*
156751f3fe7aSPeter Wemm * Preloaded module support
156851f3fe7aSPeter Wemm */
156951f3fe7aSPeter Wemm
157054823af2SPeter Wemm static modlist_t
modlist_lookup(const char * name,int ver)1571a91f68bcSBoris Popov modlist_lookup(const char *name, int ver)
157254823af2SPeter Wemm {
157354823af2SPeter Wemm modlist_t mod;
157454823af2SPeter Wemm
1575fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(mod, &found_modules, link) {
15766c75a65aSDavid Malone if (strcmp(mod->name, name) == 0 &&
15776c75a65aSDavid Malone (ver == 0 || mod->version == ver))
15788e92b63cSAndrew R. Reiter return (mod);
157954823af2SPeter Wemm }
15808e92b63cSAndrew R. Reiter return (NULL);
158154823af2SPeter Wemm }
158254823af2SPeter Wemm
1583a91f68bcSBoris Popov static modlist_t
modlist_lookup2(const char * name,const struct mod_depend * verinfo)1584d0b6da08SWarner Losh modlist_lookup2(const char *name, const struct mod_depend *verinfo)
1585505222d3SPeter Wemm {
1586505222d3SPeter Wemm modlist_t mod, bestmod;
15876c75a65aSDavid Malone int ver;
1588505222d3SPeter Wemm
1589505222d3SPeter Wemm if (verinfo == NULL)
15908e92b63cSAndrew R. Reiter return (modlist_lookup(name, 0));
1591505222d3SPeter Wemm bestmod = NULL;
1592b904477cSJohn Baldwin TAILQ_FOREACH(mod, &found_modules, link) {
15936c75a65aSDavid Malone if (strcmp(mod->name, name) != 0)
1594505222d3SPeter Wemm continue;
1595505222d3SPeter Wemm ver = mod->version;
1596505222d3SPeter Wemm if (ver == verinfo->md_ver_preferred)
15978e92b63cSAndrew R. Reiter return (mod);
1598505222d3SPeter Wemm if (ver >= verinfo->md_ver_minimum &&
1599505222d3SPeter Wemm ver <= verinfo->md_ver_maximum &&
1600b15572e3SMax Khon (bestmod == NULL || ver > bestmod->version))
1601505222d3SPeter Wemm bestmod = mod;
1602505222d3SPeter Wemm }
16038e92b63cSAndrew R. Reiter return (bestmod);
1604505222d3SPeter Wemm }
1605505222d3SPeter Wemm
1606505222d3SPeter Wemm static modlist_t
modlist_newmodule(const char * modname,int version,linker_file_t container)16070e79fe6fSDag-Erling Smørgrav modlist_newmodule(const char *modname, int version, linker_file_t container)
1608a91f68bcSBoris Popov {
1609a91f68bcSBoris Popov modlist_t mod;
1610a91f68bcSBoris Popov
161108a54da7SAndrew R. Reiter mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT | M_ZERO);
1612a91f68bcSBoris Popov if (mod == NULL)
1613a91f68bcSBoris Popov panic("no memory for module list");
1614a91f68bcSBoris Popov mod->container = container;
1615a91f68bcSBoris Popov mod->name = modname;
1616a91f68bcSBoris Popov mod->version = version;
1617a91f68bcSBoris Popov TAILQ_INSERT_TAIL(&found_modules, mod, link);
16188e92b63cSAndrew R. Reiter return (mod);
1619a91f68bcSBoris Popov }
1620a91f68bcSBoris Popov
1621a91f68bcSBoris Popov static void
linker_addmodules(linker_file_t lf,struct mod_metadata ** start,struct mod_metadata ** stop,int preload)1622f41325dbSPeter Wemm linker_addmodules(linker_file_t lf, struct mod_metadata **start,
1623f41325dbSPeter Wemm struct mod_metadata **stop, int preload)
1624a91f68bcSBoris Popov {
1625f41325dbSPeter Wemm struct mod_metadata *mp, **mdp;
16260e79fe6fSDag-Erling Smørgrav const char *modname;
1627f41325dbSPeter Wemm int ver;
1628a91f68bcSBoris Popov
1629f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
1630f41325dbSPeter Wemm mp = *mdp;
1631a91f68bcSBoris Popov if (mp->md_type != MDT_VERSION)
1632a91f68bcSBoris Popov continue;
1633a91f68bcSBoris Popov modname = mp->md_cval;
1634da82615aSWarner Losh ver = ((const struct mod_version *)mp->md_data)->mv_version;
1635a91f68bcSBoris Popov if (modlist_lookup(modname, ver) != NULL) {
1636a91f68bcSBoris Popov printf("module %s already present!\n", modname);
1637a91f68bcSBoris Popov /* XXX what can we do? this is a build error. :-( */
1638a91f68bcSBoris Popov continue;
1639a91f68bcSBoris Popov }
1640a91f68bcSBoris Popov modlist_newmodule(modname, ver, lf);
1641a91f68bcSBoris Popov }
1642a91f68bcSBoris Popov }
1643a91f68bcSBoris Popov
164451f3fe7aSPeter Wemm static void
linker_preload(void * arg)164551f3fe7aSPeter Wemm linker_preload(void *arg)
164651f3fe7aSPeter Wemm {
164751f3fe7aSPeter Wemm caddr_t modptr;
16480e79fe6fSDag-Erling Smørgrav const char *modname, *nmodname;
164926decebaSPeter Wemm char *modtype;
16500bf8969cSJohn Baldwin linker_file_t lf, nlf;
165151f3fe7aSPeter Wemm linker_class_t lc;
16526c75a65aSDavid Malone int error;
165354823af2SPeter Wemm linker_file_list_t loaded_files;
165454823af2SPeter Wemm linker_file_list_t depended_files;
16552c7f8b4eSPeter Wemm struct mod_metadata *mp, *nmp;
1656f41325dbSPeter Wemm struct mod_metadata **start, **stop, **mdp, **nmdp;
1657d0b6da08SWarner Losh const struct mod_depend *verinfo;
1658f41325dbSPeter Wemm int nver;
165954823af2SPeter Wemm int resolves;
166054823af2SPeter Wemm modlist_t mod;
1661f41325dbSPeter Wemm struct sysinit **si_start, **si_stop;
166254823af2SPeter Wemm
166354823af2SPeter Wemm TAILQ_INIT(&loaded_files);
166454823af2SPeter Wemm TAILQ_INIT(&depended_files);
166554823af2SPeter Wemm TAILQ_INIT(&found_modules);
166654823af2SPeter Wemm error = 0;
166751f3fe7aSPeter Wemm
166851f3fe7aSPeter Wemm modptr = NULL;
16693a277424SMark Johnston sx_xlock(&kld_sx);
167051f3fe7aSPeter Wemm while ((modptr = preload_search_next_name(modptr)) != NULL) {
167151f3fe7aSPeter Wemm modname = (char *)preload_search_info(modptr, MODINFO_NAME);
167226decebaSPeter Wemm modtype = (char *)preload_search_info(modptr, MODINFO_TYPE);
167351f3fe7aSPeter Wemm if (modname == NULL) {
16748e92b63cSAndrew R. Reiter printf("Preloaded module at %p does not have a"
16758e92b63cSAndrew R. Reiter " name!\n", modptr);
167651f3fe7aSPeter Wemm continue;
167751f3fe7aSPeter Wemm }
167826decebaSPeter Wemm if (modtype == NULL) {
16798e92b63cSAndrew R. Reiter printf("Preloaded module at %p does not have a type!\n",
16808e92b63cSAndrew R. Reiter modptr);
168126decebaSPeter Wemm continue;
168226decebaSPeter Wemm }
168339981fedSJohn Baldwin if (bootverbose)
16848e92b63cSAndrew R. Reiter printf("Preloaded %s \"%s\" at %p.\n", modtype, modname,
16858e92b63cSAndrew R. Reiter modptr);
168651f3fe7aSPeter Wemm lf = NULL;
1687fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lc, &classes, link) {
168854823af2SPeter Wemm error = LINKER_LINK_PRELOAD(lc, modname, &lf);
16890ca311f6SIan Dowse if (!error)
169051f3fe7aSPeter Wemm break;
16910ca311f6SIan Dowse lf = NULL;
169251f3fe7aSPeter Wemm }
169354823af2SPeter Wemm if (lf)
169454823af2SPeter Wemm TAILQ_INSERT_TAIL(&loaded_files, lf, loaded);
169554823af2SPeter Wemm }
169651f3fe7aSPeter Wemm
169754823af2SPeter Wemm /*
169854823af2SPeter Wemm * First get a list of stuff in the kernel.
169954823af2SPeter Wemm */
170015c2b301SJohn Baldwin if (linker_file_lookup_set(linker_kernel_file, MDT_SETNAME, &start,
170115c2b301SJohn Baldwin &stop, NULL) == 0)
1702f41325dbSPeter Wemm linker_addmodules(linker_kernel_file, start, stop, 1);
170354823af2SPeter Wemm
170454823af2SPeter Wemm /*
17054a0f58d2SJohn Baldwin * This is a once-off kinky bubble sort to resolve relocation
17064a0f58d2SJohn Baldwin * dependency requirements.
170754823af2SPeter Wemm */
170854823af2SPeter Wemm restart:
1709fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(lf, &loaded_files, loaded) {
171015c2b301SJohn Baldwin error = linker_file_lookup_set(lf, MDT_SETNAME, &start,
17118e92b63cSAndrew R. Reiter &stop, NULL);
171254823af2SPeter Wemm /*
17138e92b63cSAndrew R. Reiter * First, look to see if we would successfully link with this
17148e92b63cSAndrew R. Reiter * stuff.
171554823af2SPeter Wemm */
171654823af2SPeter Wemm resolves = 1; /* unless we know otherwise */
1717f41325dbSPeter Wemm if (!error) {
1718f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
17197251b4bfSJake Burkholder mp = *mdp;
172054823af2SPeter Wemm if (mp->md_type != MDT_DEPEND)
172154823af2SPeter Wemm continue;
17227251b4bfSJake Burkholder modname = mp->md_cval;
17237251b4bfSJake Burkholder verinfo = mp->md_data;
1724f41325dbSPeter Wemm for (nmdp = start; nmdp < stop; nmdp++) {
17257251b4bfSJake Burkholder nmp = *nmdp;
17262c7f8b4eSPeter Wemm if (nmp->md_type != MDT_VERSION)
17272c7f8b4eSPeter Wemm continue;
17287251b4bfSJake Burkholder nmodname = nmp->md_cval;
17296c75a65aSDavid Malone if (strcmp(modname, nmodname) == 0)
17302c7f8b4eSPeter Wemm break;
17312c7f8b4eSPeter Wemm }
1732f41325dbSPeter Wemm if (nmdp < stop) /* it's a self reference */
17332c7f8b4eSPeter Wemm continue;
17348e92b63cSAndrew R. Reiter
17358e92b63cSAndrew R. Reiter /*
17368e92b63cSAndrew R. Reiter * ok, the module isn't here yet, we
17378e92b63cSAndrew R. Reiter * are not finished
17388e92b63cSAndrew R. Reiter */
17398e92b63cSAndrew R. Reiter if (modlist_lookup2(modname, verinfo) == NULL)
174054823af2SPeter Wemm resolves = 0;
174154823af2SPeter Wemm }
174254823af2SPeter Wemm }
174354823af2SPeter Wemm /*
17448e92b63cSAndrew R. Reiter * OK, if we found our modules, we can link. So, "provide"
17458e92b63cSAndrew R. Reiter * the modules inside and add it to the end of the link order
17468e92b63cSAndrew R. Reiter * list.
174754823af2SPeter Wemm */
174854823af2SPeter Wemm if (resolves) {
1749f41325dbSPeter Wemm if (!error) {
1750f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
17517251b4bfSJake Burkholder mp = *mdp;
175254823af2SPeter Wemm if (mp->md_type != MDT_VERSION)
175354823af2SPeter Wemm continue;
17547251b4bfSJake Burkholder modname = mp->md_cval;
1755da82615aSWarner Losh nver = ((const struct mod_version *)
17567251b4bfSJake Burkholder mp->md_data)->mv_version;
17578e92b63cSAndrew R. Reiter if (modlist_lookup(modname,
17588e92b63cSAndrew R. Reiter nver) != NULL) {
17598e92b63cSAndrew R. Reiter printf("module %s already"
17608e92b63cSAndrew R. Reiter " present!\n", modname);
17618e92b63cSAndrew R. Reiter TAILQ_REMOVE(&loaded_files,
17628e92b63cSAndrew R. Reiter lf, loaded);
17630bf8969cSJohn Baldwin linker_file_unload(lf,
17640bf8969cSJohn Baldwin LINKER_UNLOAD_FORCE);
17658e92b63cSAndrew R. Reiter /* we changed tailq next ptr */
17668e92b63cSAndrew R. Reiter goto restart;
176754823af2SPeter Wemm }
1768a91f68bcSBoris Popov modlist_newmodule(modname, nver, lf);
176954823af2SPeter Wemm }
177054823af2SPeter Wemm }
177154823af2SPeter Wemm TAILQ_REMOVE(&loaded_files, lf, loaded);
177254823af2SPeter Wemm TAILQ_INSERT_TAIL(&depended_files, lf, loaded);
177354823af2SPeter Wemm /*
17748e92b63cSAndrew R. Reiter * Since we provided modules, we need to restart the
17758e92b63cSAndrew R. Reiter * sort so that the previous files that depend on us
17768e92b63cSAndrew R. Reiter * have a chance. Also, we've busted the tailq next
17778e92b63cSAndrew R. Reiter * pointer with the REMOVE.
177854823af2SPeter Wemm */
177954823af2SPeter Wemm goto restart;
178054823af2SPeter Wemm }
178154823af2SPeter Wemm }
178254823af2SPeter Wemm
178354823af2SPeter Wemm /*
178454823af2SPeter Wemm * At this point, we check to see what could not be resolved..
178554823af2SPeter Wemm */
17866b5b470aSJohn Baldwin while ((lf = TAILQ_FIRST(&loaded_files)) != NULL) {
17876b5b470aSJohn Baldwin TAILQ_REMOVE(&loaded_files, lf, loaded);
178854823af2SPeter Wemm printf("KLD file %s is missing dependencies\n", lf->filename);
178965a311fcSPoul-Henning Kamp linker_file_unload(lf, LINKER_UNLOAD_FORCE);
179054823af2SPeter Wemm }
179154823af2SPeter Wemm
179254823af2SPeter Wemm /*
179354823af2SPeter Wemm * We made it. Finish off the linking in the order we determined.
179454823af2SPeter Wemm */
17950bf8969cSJohn Baldwin TAILQ_FOREACH_SAFE(lf, &depended_files, loaded, nlf) {
179654823af2SPeter Wemm if (linker_kernel_file) {
179754823af2SPeter Wemm linker_kernel_file->refs++;
17981c7307cfSZhenlei Huang linker_file_add_dependency(lf, linker_kernel_file);
179954823af2SPeter Wemm }
180015c2b301SJohn Baldwin error = linker_file_lookup_set(lf, MDT_SETNAME, &start,
18018e92b63cSAndrew R. Reiter &stop, NULL);
1802f41325dbSPeter Wemm if (!error) {
1803f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
18047251b4bfSJake Burkholder mp = *mdp;
180554823af2SPeter Wemm if (mp->md_type != MDT_DEPEND)
180654823af2SPeter Wemm continue;
18077251b4bfSJake Burkholder modname = mp->md_cval;
18087251b4bfSJake Burkholder verinfo = mp->md_data;
1809505222d3SPeter Wemm mod = modlist_lookup2(modname, verinfo);
18107b956487SEdward Tomasz Napierala if (mod == NULL) {
18117b956487SEdward Tomasz Napierala printf("KLD file %s - cannot find "
18127b956487SEdward Tomasz Napierala "dependency \"%s\"\n",
18137b956487SEdward Tomasz Napierala lf->filename, modname);
18147b956487SEdward Tomasz Napierala goto fail;
18157b956487SEdward Tomasz Napierala }
1816135c43dcSJohn Polstra /* Don't count self-dependencies */
1817135c43dcSJohn Polstra if (lf == mod->container)
1818135c43dcSJohn Polstra continue;
181954823af2SPeter Wemm mod->container->refs++;
18201c7307cfSZhenlei Huang linker_file_add_dependency(lf, mod->container);
182154823af2SPeter Wemm }
182254823af2SPeter Wemm }
1823f41325dbSPeter Wemm /*
18248e92b63cSAndrew R. Reiter * Now do relocation etc using the symbol search paths
18258e92b63cSAndrew R. Reiter * established by the dependencies
1826f41325dbSPeter Wemm */
182754823af2SPeter Wemm error = LINKER_LINK_PRELOAD_FINISH(lf);
182854823af2SPeter Wemm if (error) {
18298e92b63cSAndrew R. Reiter printf("KLD file %s - could not finalize loading\n",
18308e92b63cSAndrew R. Reiter lf->filename);
18317b956487SEdward Tomasz Napierala goto fail;
183254823af2SPeter Wemm }
1833804f2729SBoris Popov linker_file_register_modules(lf);
1834d9ce8a41SConrad Meyer if (!TAILQ_EMPTY(&lf->modules))
1835d9ce8a41SConrad Meyer lf->flags |= LINKER_FILE_MODULES;
183615c2b301SJohn Baldwin if (linker_file_lookup_set(lf, "sysinit_set", &si_start,
18378e92b63cSAndrew R. Reiter &si_stop, NULL) == 0)
1838f41325dbSPeter Wemm sysinit_add(si_start, si_stop);
1839693593b6SAndriy Gapon linker_file_register_sysctls(lf, true);
184054823af2SPeter Wemm lf->flags |= LINKER_FILE_LINKED;
18417b956487SEdward Tomasz Napierala continue;
18427b956487SEdward Tomasz Napierala fail:
18437b956487SEdward Tomasz Napierala TAILQ_REMOVE(&depended_files, lf, loaded);
18447b956487SEdward Tomasz Napierala linker_file_unload(lf, LINKER_UNLOAD_FORCE);
184551f3fe7aSPeter Wemm }
18463a277424SMark Johnston sx_xunlock(&kld_sx);
184754823af2SPeter Wemm /* woohoo! we made it! */
184851f3fe7aSPeter Wemm }
1849891cf3edSEd Maste SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, NULL);
185051f3fe7aSPeter Wemm
1851c21bc6f3SBojan Novković static void
linker_mountroot(void * arg __unused)1852c21bc6f3SBojan Novković linker_mountroot(void *arg __unused)
1853c21bc6f3SBojan Novković {
1854c21bc6f3SBojan Novković linker_file_t lf;
1855c21bc6f3SBojan Novković
1856c21bc6f3SBojan Novković sx_xlock(&kld_sx);
1857c21bc6f3SBojan Novković TAILQ_FOREACH (lf, &linker_files, link) {
1858c21bc6f3SBojan Novković linker_ctf_load_file(lf);
1859c21bc6f3SBojan Novković }
1860c21bc6f3SBojan Novković sx_xunlock(&kld_sx);
1861c21bc6f3SBojan Novković }
1862c21bc6f3SBojan Novković EVENTHANDLER_DEFINE(mountroot, linker_mountroot, NULL, 0);
1863c21bc6f3SBojan Novković
186451f3fe7aSPeter Wemm /*
1865d9ce8a41SConrad Meyer * Handle preload files that failed to load any modules.
1866d9ce8a41SConrad Meyer */
1867d9ce8a41SConrad Meyer static void
linker_preload_finish(void * arg)1868d9ce8a41SConrad Meyer linker_preload_finish(void *arg)
1869d9ce8a41SConrad Meyer {
1870d9ce8a41SConrad Meyer linker_file_t lf, nlf;
1871d9ce8a41SConrad Meyer
1872d9ce8a41SConrad Meyer sx_xlock(&kld_sx);
1873d9ce8a41SConrad Meyer TAILQ_FOREACH_SAFE(lf, &linker_files, link, nlf) {
187439450ebaSZhenlei Huang if (lf == linker_kernel_file)
187539450ebaSZhenlei Huang continue;
187639450ebaSZhenlei Huang
1877d9ce8a41SConrad Meyer /*
1878d9ce8a41SConrad Meyer * If all of the modules in this file failed to load, unload
1879d9ce8a41SConrad Meyer * the file and return an error of ENOEXEC. (Parity with
1880d9ce8a41SConrad Meyer * linker_load_file.)
1881d9ce8a41SConrad Meyer */
1882d9ce8a41SConrad Meyer if ((lf->flags & LINKER_FILE_MODULES) != 0 &&
1883d9ce8a41SConrad Meyer TAILQ_EMPTY(&lf->modules)) {
1884d9ce8a41SConrad Meyer linker_file_unload(lf, LINKER_UNLOAD_FORCE);
1885d9ce8a41SConrad Meyer continue;
1886d9ce8a41SConrad Meyer }
1887d9ce8a41SConrad Meyer
1888d9ce8a41SConrad Meyer lf->flags &= ~LINKER_FILE_MODULES;
1889d9ce8a41SConrad Meyer lf->userrefs++; /* so we can (try to) kldunload it */
1890d9ce8a41SConrad Meyer }
1891d9ce8a41SConrad Meyer sx_xunlock(&kld_sx);
1892d9ce8a41SConrad Meyer }
1893d9ce8a41SConrad Meyer
1894d9ce8a41SConrad Meyer /*
1895d9ce8a41SConrad Meyer * Attempt to run after all DECLARE_MODULE SYSINITs. Unfortunately they can be
1896d9ce8a41SConrad Meyer * scheduled at any subsystem and order, so run this as late as possible. init
1897d9ce8a41SConrad Meyer * becomes runnable in SI_SUB_KTHREAD_INIT, so go slightly before that.
1898d9ce8a41SConrad Meyer */
1899d9ce8a41SConrad Meyer SYSINIT(preload_finish, SI_SUB_KTHREAD_INIT - 100, SI_ORDER_MIDDLE,
1900891cf3edSEd Maste linker_preload_finish, NULL);
1901d9ce8a41SConrad Meyer
1902d9ce8a41SConrad Meyer /*
190351f3fe7aSPeter Wemm * Search for a not-loaded module by name.
190451f3fe7aSPeter Wemm *
190551f3fe7aSPeter Wemm * Modules may be found in the following locations:
190651f3fe7aSPeter Wemm *
19078e92b63cSAndrew R. Reiter * - preloaded (result is just the module name) - on disk (result is full path
19088e92b63cSAndrew R. Reiter * to module)
190951f3fe7aSPeter Wemm *
19108e92b63cSAndrew R. Reiter * If the module name is qualified in any way (contains path, etc.) the we
19118e92b63cSAndrew R. Reiter * simply return a copy of it.
191251f3fe7aSPeter Wemm *
191351f3fe7aSPeter Wemm * The search path can be manipulated via sysctl. Note that we use the ';'
191451f3fe7aSPeter Wemm * character as a separator to be consistent with the bootloader.
191551f3fe7aSPeter Wemm */
191651f3fe7aSPeter Wemm
1917505222d3SPeter Wemm static char linker_hintfile[] = "linker.hints";
19186de61153SRuslan Ermilov static char linker_path[MAXPATHLEN] = "/boot/kernel;/boot/modules";
191951f3fe7aSPeter Wemm
1920af3b2549SHans Petter Selasky SYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RWTUN, linker_path,
192151f3fe7aSPeter Wemm sizeof(linker_path), "module load search path");
192251f3fe7aSPeter Wemm
192381930014SPeter Wemm TUNABLE_STR("module_path", linker_path, sizeof(linker_path));
19244058c0f0SPeter Wemm
1925fe20aaecSRyan Libby static const char * const linker_ext_list[] = {
192654823af2SPeter Wemm "",
1927505222d3SPeter Wemm ".ko",
192854823af2SPeter Wemm NULL
192954823af2SPeter Wemm };
193054823af2SPeter Wemm
1931505222d3SPeter Wemm /*
19328e92b63cSAndrew R. Reiter * Check if file actually exists either with or without extension listed in
19338e92b63cSAndrew R. Reiter * the linker_ext_list. (probably should be generic for the rest of the
19348e92b63cSAndrew R. Reiter * kernel)
1935505222d3SPeter Wemm */
193654823af2SPeter Wemm static char *
linker_lookup_file(const char * path,int pathlen,const char * name,int namelen,struct vattr * vap)19378e92b63cSAndrew R. Reiter linker_lookup_file(const char *path, int pathlen, const char *name,
19388e92b63cSAndrew R. Reiter int namelen, struct vattr *vap)
193951f3fe7aSPeter Wemm {
194051f3fe7aSPeter Wemm struct nameidata nd;
1941b40ce416SJulian Elischer struct thread *td = curthread; /* XXX */
1942fe20aaecSRyan Libby const char * const *cpp, *sep;
1943fe20aaecSRyan Libby char *result;
19445050aa86SKonstantin Belousov int error, len, extlen, reclen, flags;
1945ba8cc6d7SMateusz Guzik __enum_uint8(vtype) type;
194651f3fe7aSPeter Wemm
1947505222d3SPeter Wemm extlen = 0;
1948505222d3SPeter Wemm for (cpp = linker_ext_list; *cpp; cpp++) {
1949505222d3SPeter Wemm len = strlen(*cpp);
1950505222d3SPeter Wemm if (len > extlen)
1951505222d3SPeter Wemm extlen = len;
1952505222d3SPeter Wemm }
1953505222d3SPeter Wemm extlen++; /* trailing '\0' */
19548ee6d9e9SPeter Wemm sep = (path[pathlen - 1] != '/') ? "/" : "";
1955505222d3SPeter Wemm
19568ee6d9e9SPeter Wemm reclen = pathlen + strlen(sep) + namelen + extlen + 1;
1957a163d034SWarner Losh result = malloc(reclen, M_LINKER, M_WAITOK);
1958505222d3SPeter Wemm for (cpp = linker_ext_list; *cpp; cpp++) {
19598ee6d9e9SPeter Wemm snprintf(result, reclen, "%.*s%s%.*s%s", pathlen, path, sep,
19608ee6d9e9SPeter Wemm namelen, name, *cpp);
1961505222d3SPeter Wemm /*
19628e92b63cSAndrew R. Reiter * Attempt to open the file, and return the path if
19638e92b63cSAndrew R. Reiter * we succeed and it's a regular file.
1964505222d3SPeter Wemm */
19657e1d3eefSMateusz Guzik NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result);
1966505222d3SPeter Wemm flags = FREAD;
19679e223287SKonstantin Belousov error = vn_open(&nd, &flags, 0, NULL);
1968505222d3SPeter Wemm if (error == 0) {
1969bb92cd7bSMateusz Guzik NDFREE_PNBUF(&nd);
1970505222d3SPeter Wemm type = nd.ni_vp->v_type;
1971505222d3SPeter Wemm if (vap)
19720359a12eSAttilio Rao VOP_GETATTR(nd.ni_vp, vap, td->td_ucred);
1973b249ce48SMateusz Guzik VOP_UNLOCK(nd.ni_vp);
1974a854ed98SJohn Baldwin vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
1975505222d3SPeter Wemm if (type == VREG)
1976505222d3SPeter Wemm return (result);
1977505222d3SPeter Wemm }
1978505222d3SPeter Wemm }
1979505222d3SPeter Wemm free(result, M_LINKER);
1980505222d3SPeter Wemm return (NULL);
1981505222d3SPeter Wemm }
1982505222d3SPeter Wemm
1983505222d3SPeter Wemm #define INT_ALIGN(base, ptr) ptr = \
1984d9c9c81cSPedro F. Giffuni (base) + roundup2((ptr) - (base), sizeof(int))
1985505222d3SPeter Wemm
1986505222d3SPeter Wemm /*
19878e92b63cSAndrew R. Reiter * Lookup KLD which contains requested module in the "linker.hints" file. If
19888e92b63cSAndrew R. Reiter * version specification is available, then try to find the best KLD.
1989505222d3SPeter Wemm * Otherwise just find the latest one.
1990505222d3SPeter Wemm */
1991505222d3SPeter Wemm static char *
linker_hints_lookup(const char * path,int pathlen,const char * modname,int modnamelen,const struct mod_depend * verinfo)19928e92b63cSAndrew R. Reiter linker_hints_lookup(const char *path, int pathlen, const char *modname,
1993d0b6da08SWarner Losh int modnamelen, const struct mod_depend *verinfo)
1994505222d3SPeter Wemm {
1995b40ce416SJulian Elischer struct thread *td = curthread; /* XXX */
1996a854ed98SJohn Baldwin struct ucred *cred = td ? td->td_ucred : NULL;
1997505222d3SPeter Wemm struct nameidata nd;
1998505222d3SPeter Wemm struct vattr vattr, mattr;
1999fe20aaecSRyan Libby const char *best, *sep;
2000505222d3SPeter Wemm u_char *hints = NULL;
2001fe20aaecSRyan Libby u_char *cp, *recptr, *bufend, *result, *pathbuf;
2002526d0bd5SKonstantin Belousov int error, ival, bestver, *intp, found, flags, clen, blen;
2003526d0bd5SKonstantin Belousov ssize_t reclen;
2004505222d3SPeter Wemm
2005505222d3SPeter Wemm result = NULL;
2006505222d3SPeter Wemm bestver = found = 0;
2007505222d3SPeter Wemm
20088ee6d9e9SPeter Wemm sep = (path[pathlen - 1] != '/') ? "/" : "";
20098ee6d9e9SPeter Wemm reclen = imax(modnamelen, strlen(linker_hintfile)) + pathlen +
20108ee6d9e9SPeter Wemm strlen(sep) + 1;
2011a163d034SWarner Losh pathbuf = malloc(reclen, M_LINKER, M_WAITOK);
20128e92b63cSAndrew R. Reiter snprintf(pathbuf, reclen, "%.*s%s%s", pathlen, path, sep,
20138e92b63cSAndrew R. Reiter linker_hintfile);
2014505222d3SPeter Wemm
20157e1d3eefSMateusz Guzik NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, pathbuf);
2016505222d3SPeter Wemm flags = FREAD;
20179e223287SKonstantin Belousov error = vn_open(&nd, &flags, 0, NULL);
2018505222d3SPeter Wemm if (error)
2019505222d3SPeter Wemm goto bad;
2020bb92cd7bSMateusz Guzik NDFREE_PNBUF(&nd);
2021505222d3SPeter Wemm if (nd.ni_vp->v_type != VREG)
2022505222d3SPeter Wemm goto bad;
2023505222d3SPeter Wemm best = cp = NULL;
20240359a12eSAttilio Rao error = VOP_GETATTR(nd.ni_vp, &vattr, cred);
2025505222d3SPeter Wemm if (error)
2026505222d3SPeter Wemm goto bad;
2027505222d3SPeter Wemm /*
2028505222d3SPeter Wemm * XXX: we need to limit this number to some reasonable value
2029505222d3SPeter Wemm */
2030fac92ae1SWarner Losh if (vattr.va_size > LINKER_HINTS_MAX) {
2031694f3fc8SPeter Wemm printf("linker.hints file too large %ld\n", (long)vattr.va_size);
2032505222d3SPeter Wemm goto bad;
2033505222d3SPeter Wemm }
2034b5149b26SMark Johnston if (vattr.va_size < sizeof(ival)) {
2035b5149b26SMark Johnston printf("linker.hints file truncated\n");
2036b5149b26SMark Johnston goto bad;
2037b5149b26SMark Johnston }
2038a163d034SWarner Losh hints = malloc(vattr.va_size, M_TEMP, M_WAITOK);
2039505222d3SPeter Wemm error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)hints, vattr.va_size, 0,
20409ca43589SRobert Watson UIO_SYSSPACE, IO_NODELOCKED, cred, NOCRED, &reclen, td);
2041505222d3SPeter Wemm if (error)
2042505222d3SPeter Wemm goto bad;
2043b249ce48SMateusz Guzik VOP_UNLOCK(nd.ni_vp);
2044b40ce416SJulian Elischer vn_close(nd.ni_vp, FREAD, cred, td);
2045505222d3SPeter Wemm nd.ni_vp = NULL;
2046505222d3SPeter Wemm if (reclen != 0) {
2047526d0bd5SKonstantin Belousov printf("can't read %zd\n", reclen);
2048505222d3SPeter Wemm goto bad;
2049505222d3SPeter Wemm }
2050505222d3SPeter Wemm intp = (int *)hints;
2051505222d3SPeter Wemm ival = *intp++;
2052505222d3SPeter Wemm if (ival != LINKER_HINTS_VERSION) {
2053694f3fc8SPeter Wemm printf("linker.hints file version mismatch %d\n", ival);
2054505222d3SPeter Wemm goto bad;
2055505222d3SPeter Wemm }
2056505222d3SPeter Wemm bufend = hints + vattr.va_size;
2057505222d3SPeter Wemm recptr = (u_char *)intp;
2058505222d3SPeter Wemm clen = blen = 0;
2059505222d3SPeter Wemm while (recptr < bufend && !found) {
2060505222d3SPeter Wemm intp = (int *)recptr;
2061505222d3SPeter Wemm reclen = *intp++;
2062505222d3SPeter Wemm ival = *intp++;
2063505222d3SPeter Wemm cp = (char *)intp;
2064505222d3SPeter Wemm switch (ival) {
2065505222d3SPeter Wemm case MDT_VERSION:
2066505222d3SPeter Wemm clen = *cp++;
2067505222d3SPeter Wemm if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
2068505222d3SPeter Wemm break;
2069505222d3SPeter Wemm cp += clen;
2070505222d3SPeter Wemm INT_ALIGN(hints, cp);
2071505222d3SPeter Wemm ival = *(int *)cp;
2072505222d3SPeter Wemm cp += sizeof(int);
2073505222d3SPeter Wemm clen = *cp++;
20748e92b63cSAndrew R. Reiter if (verinfo == NULL ||
20758e92b63cSAndrew R. Reiter ival == verinfo->md_ver_preferred) {
2076505222d3SPeter Wemm found = 1;
2077505222d3SPeter Wemm break;
2078505222d3SPeter Wemm }
2079505222d3SPeter Wemm if (ival >= verinfo->md_ver_minimum &&
2080505222d3SPeter Wemm ival <= verinfo->md_ver_maximum &&
2081505222d3SPeter Wemm ival > bestver) {
2082505222d3SPeter Wemm bestver = ival;
2083505222d3SPeter Wemm best = cp;
2084505222d3SPeter Wemm blen = clen;
2085505222d3SPeter Wemm }
2086505222d3SPeter Wemm break;
2087505222d3SPeter Wemm default:
2088505222d3SPeter Wemm break;
2089505222d3SPeter Wemm }
2090505222d3SPeter Wemm recptr += reclen + sizeof(int);
2091505222d3SPeter Wemm }
2092505222d3SPeter Wemm /*
2093505222d3SPeter Wemm * Finally check if KLD is in the place
2094505222d3SPeter Wemm */
2095505222d3SPeter Wemm if (found)
2096505222d3SPeter Wemm result = linker_lookup_file(path, pathlen, cp, clen, &mattr);
2097505222d3SPeter Wemm else if (best)
2098505222d3SPeter Wemm result = linker_lookup_file(path, pathlen, best, blen, &mattr);
20998e92b63cSAndrew R. Reiter
2100505222d3SPeter Wemm /*
2101505222d3SPeter Wemm * KLD is newer than hints file. What we should do now?
2102505222d3SPeter Wemm */
21038e92b63cSAndrew R. Reiter if (result && timespeccmp(&mattr.va_mtime, &vattr.va_mtime, >))
21048e92b63cSAndrew R. Reiter printf("warning: KLD '%s' is newer than the linker.hints"
21058e92b63cSAndrew R. Reiter " file\n", result);
2106505222d3SPeter Wemm bad:
21077c61d785SPoul-Henning Kamp free(pathbuf, M_LINKER);
2108505222d3SPeter Wemm if (hints)
2109505222d3SPeter Wemm free(hints, M_TEMP);
211031965a72SJeff Roberson if (nd.ni_vp != NULL) {
2111b249ce48SMateusz Guzik VOP_UNLOCK(nd.ni_vp);
2112b40ce416SJulian Elischer vn_close(nd.ni_vp, FREAD, cred, td);
211331965a72SJeff Roberson }
2114505222d3SPeter Wemm /*
21158e92b63cSAndrew R. Reiter * If nothing found or hints is absent - fallback to the old
21168e92b63cSAndrew R. Reiter * way by using "kldname[.ko]" as module name.
2117505222d3SPeter Wemm */
2118505222d3SPeter Wemm if (!found && !bestver && result == NULL)
21198e92b63cSAndrew R. Reiter result = linker_lookup_file(path, pathlen, modname,
21208e92b63cSAndrew R. Reiter modnamelen, NULL);
21218e92b63cSAndrew R. Reiter return (result);
2122505222d3SPeter Wemm }
2123505222d3SPeter Wemm
2124505222d3SPeter Wemm /*
2125505222d3SPeter Wemm * Lookup KLD which contains requested module in the all directories.
2126505222d3SPeter Wemm */
2127505222d3SPeter Wemm static char *
linker_search_module(const char * modname,int modnamelen,const struct mod_depend * verinfo)2128505222d3SPeter Wemm linker_search_module(const char *modname, int modnamelen,
2129d0b6da08SWarner Losh const struct mod_depend *verinfo)
2130505222d3SPeter Wemm {
2131505222d3SPeter Wemm char *cp, *ep, *result;
2132505222d3SPeter Wemm
2133505222d3SPeter Wemm /*
2134505222d3SPeter Wemm * traverse the linker path
2135505222d3SPeter Wemm */
2136505222d3SPeter Wemm for (cp = linker_path; *cp; cp = ep + 1) {
2137505222d3SPeter Wemm /* find the end of this component */
21388e92b63cSAndrew R. Reiter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++);
21398e92b63cSAndrew R. Reiter result = linker_hints_lookup(cp, ep - cp, modname,
21408e92b63cSAndrew R. Reiter modnamelen, verinfo);
2141505222d3SPeter Wemm if (result != NULL)
2142505222d3SPeter Wemm return (result);
2143505222d3SPeter Wemm if (*ep == 0)
2144505222d3SPeter Wemm break;
2145505222d3SPeter Wemm }
2146505222d3SPeter Wemm return (NULL);
2147505222d3SPeter Wemm }
2148505222d3SPeter Wemm
2149505222d3SPeter Wemm /*
2150505222d3SPeter Wemm * Search for module in all directories listed in the linker_path.
2151505222d3SPeter Wemm */
2152505222d3SPeter Wemm static char *
linker_search_kld(const char * name)2153505222d3SPeter Wemm linker_search_kld(const char *name)
2154505222d3SPeter Wemm {
2155e38c7f3eSXin LI char *cp, *ep, *result;
2156e38c7f3eSXin LI int len;
2157505222d3SPeter Wemm
215851f3fe7aSPeter Wemm /* qualified at all? */
2159dc15eac0SEd Schouten if (strchr(name, '/'))
2160196f2f42SMark Johnston return (strdup(name, M_LINKER));
216151f3fe7aSPeter Wemm
216251f3fe7aSPeter Wemm /* traverse the linker path */
216354823af2SPeter Wemm len = strlen(name);
2164505222d3SPeter Wemm for (ep = linker_path; *ep; ep++) {
2165505222d3SPeter Wemm cp = ep;
216651f3fe7aSPeter Wemm /* find the end of this component */
21678e92b63cSAndrew R. Reiter for (; *ep != 0 && *ep != ';'; ep++);
2168505222d3SPeter Wemm result = linker_lookup_file(cp, ep - cp, name, len, NULL);
2169505222d3SPeter Wemm if (result != NULL)
217051f3fe7aSPeter Wemm return (result);
217151f3fe7aSPeter Wemm }
217251f3fe7aSPeter Wemm return (NULL);
217351f3fe7aSPeter Wemm }
217454823af2SPeter Wemm
217554823af2SPeter Wemm static const char *
linker_basename(const char * path)217654823af2SPeter Wemm linker_basename(const char *path)
217754823af2SPeter Wemm {
217854823af2SPeter Wemm const char *filename;
217954823af2SPeter Wemm
2180dc15eac0SEd Schouten filename = strrchr(path, '/');
218154823af2SPeter Wemm if (filename == NULL)
218254823af2SPeter Wemm return path;
218354823af2SPeter Wemm if (filename[1])
218454823af2SPeter Wemm filename++;
21858e92b63cSAndrew R. Reiter return (filename);
218654823af2SPeter Wemm }
218754823af2SPeter Wemm
2188*df114daeSRuslan Bukin #if defined(HWPMC_HOOKS) || defined(HWT_HOOKS)
218949874f6eSJoseph Koshy /*
219049874f6eSJoseph Koshy * Inform hwpmc about the set of kernel modules currently loaded.
219149874f6eSJoseph Koshy */
219249874f6eSJoseph Koshy void *
linker_hwpmc_list_objects(void)219349874f6eSJoseph Koshy linker_hwpmc_list_objects(void)
219449874f6eSJoseph Koshy {
21955257ff7eSAttilio Rao linker_file_t lf;
21965257ff7eSAttilio Rao struct pmckern_map_in *kobase;
21975257ff7eSAttilio Rao int i, nmappings;
219849874f6eSJoseph Koshy
21995257ff7eSAttilio Rao nmappings = 0;
22003a277424SMark Johnston sx_slock(&kld_sx);
22015257ff7eSAttilio Rao TAILQ_FOREACH(lf, &linker_files, link)
22025257ff7eSAttilio Rao nmappings++;
220349874f6eSJoseph Koshy
22045257ff7eSAttilio Rao /* Allocate nmappings + 1 entries. */
22055257ff7eSAttilio Rao kobase = malloc((nmappings + 1) * sizeof(struct pmckern_map_in),
2206e11e3f18SDag-Erling Smørgrav M_LINKER, M_WAITOK | M_ZERO);
22075257ff7eSAttilio Rao i = 0;
22085257ff7eSAttilio Rao TAILQ_FOREACH(lf, &linker_files, link) {
22095257ff7eSAttilio Rao /* Save the info for this linker file. */
221053d0b9e4SJessica Clarke kobase[i].pm_file = lf->pathname;
22115257ff7eSAttilio Rao kobase[i].pm_address = (uintptr_t)lf->address;
22125257ff7eSAttilio Rao i++;
221349874f6eSJoseph Koshy }
22143a277424SMark Johnston sx_sunlock(&kld_sx);
221549874f6eSJoseph Koshy
22165257ff7eSAttilio Rao KASSERT(i > 0, ("linker_hpwmc_list_objects: no kernel objects?"));
221749874f6eSJoseph Koshy
221849874f6eSJoseph Koshy /* The last entry of the malloced area comprises of all zeros. */
22195257ff7eSAttilio Rao KASSERT(kobase[i].pm_file == NULL,
222049874f6eSJoseph Koshy ("linker_hwpmc_list_objects: last object not NULL"));
222149874f6eSJoseph Koshy
22225257ff7eSAttilio Rao return ((void *)kobase);
222349874f6eSJoseph Koshy }
222449874f6eSJoseph Koshy #endif
222549874f6eSJoseph Koshy
2226d2222aa0SMateusz Guzik /* check if root file system is not mounted */
2227d2222aa0SMateusz Guzik static bool
linker_root_mounted(void)2228d2222aa0SMateusz Guzik linker_root_mounted(void)
2229d2222aa0SMateusz Guzik {
2230d2222aa0SMateusz Guzik struct pwd *pwd;
2231d2222aa0SMateusz Guzik bool ret;
2232d2222aa0SMateusz Guzik
2233d2222aa0SMateusz Guzik if (rootvnode == NULL)
2234d2222aa0SMateusz Guzik return (false);
2235d2222aa0SMateusz Guzik
2236d2222aa0SMateusz Guzik pwd = pwd_hold(curthread);
2237d2222aa0SMateusz Guzik ret = pwd->pwd_rdir != NULL;
2238d2222aa0SMateusz Guzik pwd_drop(pwd);
2239d2222aa0SMateusz Guzik return (ret);
2240d2222aa0SMateusz Guzik }
2241d2222aa0SMateusz Guzik
224254823af2SPeter Wemm /*
22438e92b63cSAndrew R. Reiter * Find a file which contains given module and load it, if "parent" is not
22448e92b63cSAndrew R. Reiter * NULL, register a reference to it.
224554823af2SPeter Wemm */
2246aaf31705SJohn Baldwin static int
linker_load_module(const char * kldname,const char * modname,struct linker_file * parent,const struct mod_depend * verinfo,struct linker_file ** lfpp)2247505222d3SPeter Wemm linker_load_module(const char *kldname, const char *modname,
2248d0b6da08SWarner Losh struct linker_file *parent, const struct mod_depend *verinfo,
2249505222d3SPeter Wemm struct linker_file **lfpp)
225054823af2SPeter Wemm {
225154823af2SPeter Wemm linker_file_t lfdep;
225254823af2SPeter Wemm const char *filename;
225354823af2SPeter Wemm char *pathname;
225454823af2SPeter Wemm int error;
225554823af2SPeter Wemm
22563a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
2257505222d3SPeter Wemm if (modname == NULL) {
225854823af2SPeter Wemm /*
2259505222d3SPeter Wemm * We have to load KLD
226054823af2SPeter Wemm */
2261e68baa70SAndrew R. Reiter KASSERT(verinfo == NULL, ("linker_load_module: verinfo"
2262e68baa70SAndrew R. Reiter " is not NULL"));
2263d2222aa0SMateusz Guzik if (!linker_root_mounted())
2264aa4612d1SHans Petter Selasky return (ENXIO);
2265505222d3SPeter Wemm pathname = linker_search_kld(kldname);
2266505222d3SPeter Wemm } else {
2267505222d3SPeter Wemm if (modlist_lookup2(modname, verinfo) != NULL)
2268505222d3SPeter Wemm return (EEXIST);
2269d2222aa0SMateusz Guzik if (!linker_root_mounted())
2270aa4612d1SHans Petter Selasky return (ENXIO);
2271f1e4a6e9SBrian Somers if (kldname != NULL)
2272196f2f42SMark Johnston pathname = strdup(kldname, M_LINKER);
2273f1e4a6e9SBrian Somers else
2274505222d3SPeter Wemm /*
2275505222d3SPeter Wemm * Need to find a KLD with required module
2276505222d3SPeter Wemm */
22778e92b63cSAndrew R. Reiter pathname = linker_search_module(modname,
22788e92b63cSAndrew R. Reiter strlen(modname), verinfo);
2279505222d3SPeter Wemm }
228054823af2SPeter Wemm if (pathname == NULL)
2281505222d3SPeter Wemm return (ENOENT);
228254823af2SPeter Wemm
2283505222d3SPeter Wemm /*
22848e92b63cSAndrew R. Reiter * Can't load more than one file with the same basename XXX:
22858e92b63cSAndrew R. Reiter * Actually it should be possible to have multiple KLDs with
22868e92b63cSAndrew R. Reiter * the same basename but different path because they can
22878e92b63cSAndrew R. Reiter * provide different versions of the same modules.
2288505222d3SPeter Wemm */
228954823af2SPeter Wemm filename = linker_basename(pathname);
2290e5bb3a01SJohn Baldwin if (linker_find_file_by_name(filename))
229154823af2SPeter Wemm error = EEXIST;
2292e5bb3a01SJohn Baldwin else do {
229354823af2SPeter Wemm error = linker_load_file(pathname, &lfdep);
229454823af2SPeter Wemm if (error)
229554823af2SPeter Wemm break;
22968e92b63cSAndrew R. Reiter if (modname && verinfo &&
22978e92b63cSAndrew R. Reiter modlist_lookup2(modname, verinfo) == NULL) {
229865a311fcSPoul-Henning Kamp linker_file_unload(lfdep, LINKER_UNLOAD_FORCE);
2299505222d3SPeter Wemm error = ENOENT;
2300505222d3SPeter Wemm break;
2301505222d3SPeter Wemm }
23021c7307cfSZhenlei Huang if (parent)
23031c7307cfSZhenlei Huang linker_file_add_dependency(parent, lfdep);
2304505222d3SPeter Wemm if (lfpp)
2305505222d3SPeter Wemm *lfpp = lfdep;
230654823af2SPeter Wemm } while (0);
230754823af2SPeter Wemm free(pathname, M_LINKER);
23088e92b63cSAndrew R. Reiter return (error);
230954823af2SPeter Wemm }
231054823af2SPeter Wemm
231154823af2SPeter Wemm /*
23128e92b63cSAndrew R. Reiter * This routine is responsible for finding dependencies of userland initiated
23138e92b63cSAndrew R. Reiter * kldload(2)'s of files.
231454823af2SPeter Wemm */
231554823af2SPeter Wemm int
linker_load_dependencies(linker_file_t lf)23167b9716baSIan Dowse linker_load_dependencies(linker_file_t lf)
231754823af2SPeter Wemm {
231854823af2SPeter Wemm linker_file_t lfdep;
2319f41325dbSPeter Wemm struct mod_metadata **start, **stop, **mdp, **nmdp;
232054823af2SPeter Wemm struct mod_metadata *mp, *nmp;
2321d0b6da08SWarner Losh const struct mod_depend *verinfo;
232254823af2SPeter Wemm modlist_t mod;
23230e79fe6fSDag-Erling Smørgrav const char *modname, *nmodname;
23240455a92bSBjoern A. Zeeb int ver, error = 0;
232554823af2SPeter Wemm
232654823af2SPeter Wemm /*
2327e3043798SPedro F. Giffuni * All files are dependent on /kernel.
232854823af2SPeter Wemm */
23293a277424SMark Johnston sx_assert(&kld_sx, SA_XLOCKED);
233054823af2SPeter Wemm if (linker_kernel_file) {
233154823af2SPeter Wemm linker_kernel_file->refs++;
23321c7307cfSZhenlei Huang linker_file_add_dependency(lf, linker_kernel_file);
233354823af2SPeter Wemm }
233415c2b301SJohn Baldwin if (linker_file_lookup_set(lf, MDT_SETNAME, &start, &stop,
23350455a92bSBjoern A. Zeeb NULL) != 0)
23368e92b63cSAndrew R. Reiter return (0);
2337f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
23387251b4bfSJake Burkholder mp = *mdp;
233954823af2SPeter Wemm if (mp->md_type != MDT_VERSION)
234054823af2SPeter Wemm continue;
23417251b4bfSJake Burkholder modname = mp->md_cval;
2342da82615aSWarner Losh ver = ((const struct mod_version *)mp->md_data)->mv_version;
2343a91f68bcSBoris Popov mod = modlist_lookup(modname, ver);
2344a91f68bcSBoris Popov if (mod != NULL) {
23458e92b63cSAndrew R. Reiter printf("interface %s.%d already present in the KLD"
23468e92b63cSAndrew R. Reiter " '%s'!\n", modname, ver,
23478e92b63cSAndrew R. Reiter mod->container->filename);
23488e92b63cSAndrew R. Reiter return (EEXIST);
234954823af2SPeter Wemm }
235054823af2SPeter Wemm }
2351a91f68bcSBoris Popov
2352f41325dbSPeter Wemm for (mdp = start; mdp < stop; mdp++) {
23537251b4bfSJake Burkholder mp = *mdp;
235454823af2SPeter Wemm if (mp->md_type != MDT_DEPEND)
235554823af2SPeter Wemm continue;
23567251b4bfSJake Burkholder modname = mp->md_cval;
23577251b4bfSJake Burkholder verinfo = mp->md_data;
235854823af2SPeter Wemm nmodname = NULL;
2359f41325dbSPeter Wemm for (nmdp = start; nmdp < stop; nmdp++) {
23607251b4bfSJake Burkholder nmp = *nmdp;
236154823af2SPeter Wemm if (nmp->md_type != MDT_VERSION)
236254823af2SPeter Wemm continue;
23637251b4bfSJake Burkholder nmodname = nmp->md_cval;
23646c75a65aSDavid Malone if (strcmp(modname, nmodname) == 0)
236554823af2SPeter Wemm break;
236654823af2SPeter Wemm }
2367f41325dbSPeter Wemm if (nmdp < stop)/* early exit, it's a self reference */
236854823af2SPeter Wemm continue;
2369505222d3SPeter Wemm mod = modlist_lookup2(modname, verinfo);
237054823af2SPeter Wemm if (mod) { /* woohoo, it's loaded already */
237154823af2SPeter Wemm lfdep = mod->container;
237254823af2SPeter Wemm lfdep->refs++;
23731c7307cfSZhenlei Huang linker_file_add_dependency(lf, lfdep);
237454823af2SPeter Wemm continue;
237554823af2SPeter Wemm }
2376505222d3SPeter Wemm error = linker_load_module(NULL, modname, lf, verinfo, NULL);
237754823af2SPeter Wemm if (error) {
2378fcdd3d32SXin LI printf("KLD %s: depends on %s - not available or"
23795b4d5f9eSRui Paulo " version mismatch\n", lf->filename, modname);
238054823af2SPeter Wemm break;
238154823af2SPeter Wemm }
238254823af2SPeter Wemm }
238354823af2SPeter Wemm
2384303b15f1SBoris Popov if (error)
23858e92b63cSAndrew R. Reiter return (error);
2386f41325dbSPeter Wemm linker_addmodules(lf, start, stop, 0);
23878e92b63cSAndrew R. Reiter return (error);
238854823af2SPeter Wemm }
2389bb9fe9ddSBrian Feldman
2390bb9fe9ddSBrian Feldman static int
sysctl_kern_function_list_iterate(const char * name,void * opaque)2391bb9fe9ddSBrian Feldman sysctl_kern_function_list_iterate(const char *name, void *opaque)
2392bb9fe9ddSBrian Feldman {
2393bb9fe9ddSBrian Feldman struct sysctl_req *req;
2394bb9fe9ddSBrian Feldman
2395bb9fe9ddSBrian Feldman req = opaque;
2396bb9fe9ddSBrian Feldman return (SYSCTL_OUT(req, name, strlen(name) + 1));
2397bb9fe9ddSBrian Feldman }
2398bb9fe9ddSBrian Feldman
2399bb9fe9ddSBrian Feldman /*
2400bb9fe9ddSBrian Feldman * Export a nul-separated, double-nul-terminated list of all function names
2401bb9fe9ddSBrian Feldman * in the kernel.
2402bb9fe9ddSBrian Feldman */
2403bb9fe9ddSBrian Feldman static int
sysctl_kern_function_list(SYSCTL_HANDLER_ARGS)2404bb9fe9ddSBrian Feldman sysctl_kern_function_list(SYSCTL_HANDLER_ARGS)
2405bb9fe9ddSBrian Feldman {
2406bb9fe9ddSBrian Feldman linker_file_t lf;
2407bb9fe9ddSBrian Feldman int error;
2408bb9fe9ddSBrian Feldman
2409a3df768bSRobert Watson #ifdef MAC
241030d239bcSRobert Watson error = mac_kld_check_stat(req->td->td_ucred);
2411a3df768bSRobert Watson if (error)
2412a3df768bSRobert Watson return (error);
2413a3df768bSRobert Watson #endif
241447934cefSDon Lewis error = sysctl_wire_old_buffer(req, 0);
241547934cefSDon Lewis if (error != 0)
241647934cefSDon Lewis return (error);
24173a277424SMark Johnston sx_xlock(&kld_sx);
2418bb9fe9ddSBrian Feldman TAILQ_FOREACH(lf, &linker_files, link) {
2419bb9fe9ddSBrian Feldman error = LINKER_EACH_FUNCTION_NAME(lf,
2420bb9fe9ddSBrian Feldman sysctl_kern_function_list_iterate, req);
24212eb7b21bSAndrew R. Reiter if (error) {
24223a277424SMark Johnston sx_xunlock(&kld_sx);
2423bb9fe9ddSBrian Feldman return (error);
2424bb9fe9ddSBrian Feldman }
24252eb7b21bSAndrew R. Reiter }
24263a277424SMark Johnston sx_xunlock(&kld_sx);
2427bb9fe9ddSBrian Feldman return (SYSCTL_OUT(req, "", 1));
2428bb9fe9ddSBrian Feldman }
2429bb9fe9ddSBrian Feldman
24307029da5cSPawel Biernacki SYSCTL_PROC(_kern, OID_AUTO, function_list,
24317029da5cSPawel Biernacki CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
24327029da5cSPawel Biernacki sysctl_kern_function_list, "",
24337029da5cSPawel Biernacki "kernel function list");
2434