1-- Shared objects loaded while the program runs, by the system's own
2-- loader: POSIX's dlopen, dlsym, dlclose and dlerror. The loader is the
3-- one that started the process, so this is there wherever a C runtime
4-- started the program: on Darwin and FreeBSD always, and on Linux under
5-- --link=libc. A static Linux program has no loader, and one that loads
6-- a library is refused before it links, naming --link=libc (D280).
7--
8-- What is loaded is C, or code of any language reached only through its
9-- C entry points. A symbol is found as an address with no type; the
10-- program names the C function type it is, `adder(address)`, and nothing
11-- can check that it is right, as a C cast cannot. A function found in a
12-- library is the program's to call only while the library stays loaded:
13-- once its last use is closed, every address found in it may name
14-- nothing, and no check of the language follows a function value back
15-- to the library it came from.
16
17import core/text
18import platform/libc
19
20library_value: type = struct
21 handle: ptr mut u8
22end library_value
23
24--- A loaded shared object: a handle the system's loader gives, which
25--- counts one use of it until `close_library` gives that use back.
26--- Loading an object already loaded gives the same handle and counts
27--- another use.
28public library: type = library_value
29
30--- The object's symbols resolve no other object's references: POSIX's
31--- `RTLD_LOCAL`, the usual choice, so two libraries that define one name
32--- do not meet. Darwin's own default is the other one; this layer always
33--- says which.
34public local_symbols: atom
35
36--- The object's symbols also resolve the references of every object
37--- loaded after it: POSIX's `RTLD_GLOBAL`, for a library whose own
38--- plugins call back into it by name.
39public global_symbols: atom
40
41--- Whose references a loaded object's symbols resolve.
42public symbol_scope: type = local_symbols | global_symbols
43
44--- The loader loaded nothing: the file is missing or is not a shared
45--- object for this system, or a symbol it needs is defined nowhere.
46--- `library_failure` gives the loader's own words.
47public no_library: atom
48
49--- The library defines no symbol of that name, nor do the objects it
50--- loaded with it. `library_failure` gives the loader's own words.
51public no_symbol: atom
52
53-- The longest symbol name `find_symbol` takes, with its NUL.
54symbol_room: type = [4096]u8
55
56--- Load the shared object at `path`, or the one the system's search finds
57--- when `path` holds no slash, with its symbols visible as `scope` says.
58--- Every symbol the object needs is bound as it is loaded, POSIX's
59--- `RTLD_NOW`, so a missing one is `no_library` here and never a failure
60--- at a later call. The library, or `no_library`; a path holding a NUL
61--- is `einval` and one longer than the system takes `enametoolong`, as
62--- every path of this module is.
63public link(symbol: "_landin_posix_open_library") open_library:
64 (path: text.host_string, scope: symbol_scope)
65 -> (loaded: library) ! errno | no_library =
66 mut room: path_room = zeroed
67 terminated: ptr u8 = try terminate(path, room[0..<lenof room])
68 match libc.dlopen(terminated, mode_of(scope))
69 libc.not_loaded: fail no_library
70 ptr (handle): loaded = (handle: handle)
71 end match
72end open_library
73
74-- The dlopen mode: every symbol bound as the object is loaded, and the
75-- scope asked for, which is always given because Darwin's default is
76-- not the other systems'.
77mode_of: (scope: symbol_scope) -> (mode: i32) =
78 mode = libc.rtld_now | libc.rtld_local
79 if scope == global_symbols then
80 mode = libc.rtld_now | libc.rtld_global
81 end if
82end mode_of
83
84--- The address of the symbol `name` in `loaded`, or in an object it
85--- loaded with it: never zero, or `no_symbol`. A name holding a NUL is
86--- `einval`, and one of 4096 bytes or more `enametoolong`. What is at
87--- the address is the program's to know: a function is made a value by
88--- naming its C function type, `adder(address)`, and is the program's to
89--- call only while `loaded` stays loaded.
90public link(symbol: "_landin_posix_find_symbol") find_symbol:
91 (loaded: library, name: []u8) -> (address: usize) ! errno | no_symbol =
92 fail enametoolong when lenof name >= sizeof symbol_room
93 mut room: symbol_room = zeroed
94 for at in usize(0)..<lenof name do
95 fail einval when name[at] == 0
96 room[at] = name[at]
97 end for
98 match libc.dlsym(loaded.handle, addr room[0])
99 libc.no_address: fail no_symbol
100 ptr (found): address = usize(found)
101 end match
102end find_symbol
103
104--- Give back the use of the library `loaded` counts. Once no use is
105--- left the loader may unload it, and every address found in it may then
106--- name nothing. `no_library` when the loader refuses. A handle whose
107--- every use was already given back names nothing either: closing it again
108--- is not this module's to catch, as it is not C's.
109public link(symbol: "_landin_posix_close_library") close_library:
110 (loaded: library) -> none ! no_library =
111 fail no_library when libc.dlclose(loaded.handle) <> 0
112end close_library
113
114--- The loader's own words for the last `no_library` or `no_symbol` on
115--- this thread, copied into `into` and lent from it, cut at its length:
116--- empty when there has been none since they were last asked for. Asking
117--- clears them, and the next call of this module that loads, finds or
118--- closes may replace them, so they are asked for at once. They are for
119--- a person to read; their wording is each system's own.
120public link(symbol: "_landin_posix_library_failure") library_failure:
121 (into: []mut u8) -> (message: []u8 from into) =
122 message = into[0..<0]
123 match libc.dlerror()
124 libc.no_address: return
125 ptr (text): message = into[0..<copied(text, into)]
126 end match
127end library_failure
128
129-- Copy the NUL-terminated TEXT into INTO, as much as fits: how much.
130copied: (text: ptr u8, into: []mut u8) -> (count: usize) =
131 length: usize = libc.strlen(text)
132 count = if length < lenof into then length else lenof into end if
133 for at in usize(0)..<count do
134 byte: ptr u8 = ptr(usize(text) + at)
135 into[at] = byte.val
136 end for
137end copied