Landin library reference discuss source

platform/posix/libraries.ldn

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