Class: Rubycc::Link::LibraryResolver
- Inherits:
-
Object
- Object
- Rubycc::Link::LibraryResolver
- Defined in:
- lib/rubycc/link/library_resolver.rb
Overview
Turns the command line's library requests (-l names and -L search
directories) into the two concrete input sets the final link consumes: the
shared objects whose exports satisfy imports (SharedLinker's needed:) and
the relocatable inputs pulled in for their definitions (SharedLinker's
inputs, where archives are taken lazily). It is the piece that stands
between a driver's flags and the linker core: nothing here reads a compiler
flag string — the caller has already split -lz into the name "z" and
-L/opt/lib into a directory — so this class only does the resolution the
ELF/GNU conventions prescribe.
Search follows the classic rule: each -L directory in command-line order,
then the platform's default library directories (only those that exist), and
within a single directory an unversioned lib<name>.so, a versioned
lib<name>.so.<version>, and then lib<name>.a are preferred in that order.
The first directory holding either form settles the request — the .so/.a
preference does not reach across directories, so an earlier directory's .a
wins over a later directory's .so, matching the traditional linker. A
:filename request (-l:libfoo.so.1) looks that exact name up instead of
composing lib…so/lib…a.
A resolved file is dispatched by what it actually is, read from its leading
bytes: an ELF shared object (ET_DYN) joins needed, an ELF relocatable or an
ar archive joins inputs, and anything else is treated as a GNU-ld text
linker script (glibc ships libc.so as GROUP ( libc.so.6 … )) whose
GROUP/INPUT file lists are expanded and each named file resolved in turn.
The transitive closure of shared dependencies is deliberately not followed:
a .so that itself needs other .sos records those as its own DT_NEEDED and
the runtime loader pulls them at load time; a static link only needs the
directly requested libraries. Deduplication is by real path so the same
library requested twice (or reached once directly and once through a script)
contributes a single entry, keeping the output deterministic (N4).
Defined Under Namespace
Classes: Resolution
Constant Summary collapse
- ELFMAG =
ELF and
armagics, and the e_type value that marks a shared object; a resolved file matching neither magic is taken to be a text linker script. "\x7FELF".b
- AR_MAGIC =
"!<arch>\n".b
- ET_DYN =
3- TARGET_SYSTEM_DIRS =
The target's default library directories, consulted after every
-Ldirectory. Debian's multiarch directory is target-specific; using the x86_64 spelling unconditionally made a native aarch64 build unable to resolve even libc's ordinary-lm/-lpthreaddependencies. { "x86_64" => [ "/usr/lib/x86_64-linux-gnu", "/usr/lib", "/lib/x86_64-linux-gnu", "/lib", "/usr/local/lib" ].freeze, "aarch64" => [ "/usr/lib/aarch64-linux-gnu", "/usr/lib", "/lib/aarch64-linux-gnu", "/lib", "/usr/aarch64-linux-gnu/lib", "/usr/local/lib" ].freeze }.freeze
- MUSL_LIBC_PROVIDED_LIBRARIES =
musl folds the libraries that glibc normally exposes as separate DSOs into libc. Runtime images therefore have no libm.so, libpthread.so, or libdl.so (and often no unversioned libc.so either), even though extconf-generated link flags still request them. Only these known libc-provided names are eligible for the fallback; an absent arbitrary
-lfoomust remain an error rather than silently depending on libc. %w[c m pthread rt dl resolv util crypt nsl].freeze
- MUSL_LIBC_FILE =
Alpine/musl runtime names. The loader is also the libc implementation, so it is a valid last-resort target when a libc.musl-* compatibility symlink is not present in a stripped runtime.
/\Alibc\.musl(?:-[^.]*)?\.so\.\d[\w.-]*\z/- MUSL_LOADER_FILE =
/\Ald-musl-[^.]+\.so\.\d[\w.-]*\z/- VERSIONED_SHARED_SUFFIX =
/\A\d[\w.-]*\z/- DEFAULT_SYSTEM_DIRS =
Kept as a public host-layout constant for callers and tests that need to inspect the default search path without selecting a cross target.
default_system_dirs.freeze
Instance Attribute Summary collapse
-
#dirs ⇒ Object
readonly
The ordered search path actually consulted (existing
-Ldirs followed by the existing default directories); exposed so a driver can report it.
Class Method Summary collapse
-
.default_system_dirs(target: nil) ⇒ Object
Return the conventional system library directories for
target. -
.link(inputs, libraries: [], search_dirs: [], soname: nil, target: nil) ⇒ Object
High-level convenience for a driver: resolves
librariesand links them together with the objectinputsinto a shared object. -
.link_to(inputs, path, libraries: [], search_dirs: [], soname: nil, target: nil) ⇒ Object
Convenience: link and write the shared object to
path. - .normalize_target(spec) ⇒ Object
-
.resolve(libraries, search_dirs: [], target: nil) ⇒ Object
Resolves
libraries(an ordered array of-largument strings, each the text after-l, e.g."z"or":libfoo.so.1") againstsearch_dirs(the-Ldirectories, in order) and returns a Resolution.
Instance Method Summary collapse
-
#initialize(search_dirs: [], target: nil) ⇒ LibraryResolver
constructor
A new instance of LibraryResolver.
- #resolve(libraries) ⇒ Object
Constructor Details
#initialize(search_dirs: [], target: nil) ⇒ LibraryResolver
Returns a new instance of LibraryResolver.
141 142 143 144 145 |
# File 'lib/rubycc/link/library_resolver.rb', line 141 def initialize(search_dirs: [], target: nil) @dirs = (search_dirs + self.class.default_system_dirs(target: target)).select do |d| File.directory?(d) end end |
Instance Attribute Details
#dirs ⇒ Object (readonly)
The ordered search path actually consulted (existing -L dirs followed by
the existing default directories); exposed so a driver can report it.
149 150 151 |
# File 'lib/rubycc/link/library_resolver.rb', line 149 def dirs @dirs end |
Class Method Details
.default_system_dirs(target: nil) ⇒ Object
Return the conventional system library directories for target.
A target triple is accepted because the driver normalizes one before
passing it here. Unknown targets fall back to the host layout so that
the resolver remains useful for diagnostics instead of inventing a
multiarch path for an unsupported architecture.
90 91 92 93 94 95 96 97 |
# File 'lib/rubycc/link/library_resolver.rb', line 90 def default_system_dirs(target: nil) arch = normalize_target(target || RbConfig::CONFIG["host_cpu"]) TARGET_SYSTEM_DIRS.fetch(arch) do TARGET_SYSTEM_DIRS.fetch(normalize_target(RbConfig::CONFIG["host_cpu"])) do TARGET_SYSTEM_DIRS.fetch("x86_64") end end end |
.link(inputs, libraries: [], search_dirs: [], soname: nil, target: nil) ⇒ Object
High-level convenience for a driver: resolves libraries and links them
together with the object inputs into a shared object. The resolved
archives follow the objects so the lazy pull-in sees the objects'
undefined symbols first; the resolved shared objects become the
dependencies imports bind against.
120 121 122 123 |
# File 'lib/rubycc/link/library_resolver.rb', line 120 def link(inputs, libraries: [], search_dirs: [], soname: nil, target: nil) r = resolve(libraries, search_dirs: search_dirs, target: target) SharedLinker.link(inputs + r.inputs, needed: r.needed, soname: soname) end |
.link_to(inputs, path, libraries: [], search_dirs: [], soname: nil, target: nil) ⇒ Object
Convenience: link and write the shared object to path.
126 127 128 129 |
# File 'lib/rubycc/link/library_resolver.rb', line 126 def link_to(inputs, path, libraries: [], search_dirs: [], soname: nil, target: nil) File.binwrite(path, link(inputs, libraries: libraries, search_dirs: search_dirs, soname: soname, target: target)) end |
.normalize_target(spec) ⇒ Object
99 100 101 102 103 104 105 106 |
# File 'lib/rubycc/link/library_resolver.rb', line 99 def normalize_target(spec) cpu = spec.to_s.split("-", 2).first.to_s case cpu when "amd64", "x64" then "x86_64" when "arm64" then "aarch64" else cpu end end |
.resolve(libraries, search_dirs: [], target: nil) ⇒ Object
Resolves libraries (an ordered array of -l argument strings, each the
text after -l, e.g. "z" or ":libfoo.so.1") against search_dirs
(the -L directories, in order) and returns a Resolution.
111 112 113 |
# File 'lib/rubycc/link/library_resolver.rb', line 111 def resolve(libraries, search_dirs: [], target: nil) new(search_dirs: search_dirs, target: target).resolve(libraries) end |
Instance Method Details
#resolve(libraries) ⇒ Object
151 152 153 154 155 156 157 |
# File 'lib/rubycc/link/library_resolver.rb', line 151 def resolve(libraries) @needed = [] @inputs = [] @seen = Set.new libraries.each { |spec| resolve_spec(spec) } Resolution.new(needed: @needed, inputs: @inputs) end |