///|
/// One undefined reference to a symbol.
pub(all) struct SymbolReference {
object_index : UInt32
object_name : String
symbol_index : UInt32
}
///|
/// One symbol selected for the link.
pub(all) struct ResolvedSymbol {
name : String
binding : LinkBinding
kind : LinkSymbolType
object_index : UInt32
object_name : String
symbol_index : UInt32
value : UInt64
size : UInt64
}
///|
/// Human-readable binding name for a resolved symbol.
pub fn ResolvedSymbol::binding_name(self : ResolvedSymbol) -> String {
self.binding.name()
}
///|
/// Human-readable symbol kind for a resolved symbol.
pub fn ResolvedSymbol::kind_name(self : ResolvedSymbol) -> String {
self.kind.name()
}
///|
/// A symbol referenced but not defined by any input object.
pub(all) struct UnresolvedSymbol {
name : String
references : Array[SymbolReference]
}
///|
/// Two or more strong definitions of the same global symbol.
pub(all) struct DuplicateDefinition {
name : String
objects : Array[String]
}
///|
/// The result of resolving global and weak symbols across input objects.
pub(all) struct LinkResolution {
symbols : Array[ResolvedSymbol]
unresolved : Array[UnresolvedSymbol]
duplicates : Array[DuplicateDefinition]
object_count : Int32
input_symbol_count : Int32
}
///|
/// Whether the resolution has no conflicts or missing definitions.
pub fn LinkResolution::is_ok(self : LinkResolution) -> Bool {
self.unresolved.is_empty() && self.duplicates.is_empty()
}
///|
/// A deterministic one-line summary.
pub fn LinkResolution::summary(self : LinkResolution) -> String {
self.object_count.to_string() +
" objects, " +
self.symbols.length().to_string() +
" resolved, " +
self.unresolved.length().to_string() +
" unresolved, " +
self.duplicates.length().to_string() +
" duplicate definitions"
}
///|
/// Render a deterministic link map suitable for terminal output and CI logs.
pub fn LinkResolution::to_link_map(self : LinkResolution) -> String {
let builder = StringBuilder()
builder.write_string("Link map\n")
builder.write_string("objects: ")
builder.write_string(self.object_count.to_string())
builder.write_string("\ninput symbols: ")
builder.write_string(self.input_symbol_count.to_string())
builder.write_string("\n\nresolved:\n")
if self.symbols.is_empty() {
builder.write_string(" \n")
} else {
for symbol in self.symbols {
builder.write_string(" ")
builder.write_string(symbol.name)
builder.write_string(" <- ")
builder.write_string(symbol.object_name)
builder.write_string(" [")
builder.write_string(symbol.binding_name())
builder.write_string(" ")
builder.write_string(symbol.kind_name())
builder.write_string(" value=0x")
builder.write_string(symbol.value.to_string(radix=16))
builder.write_string(" size=")
builder.write_string(symbol.size.to_string())
builder.write_string("]\n")
}
}
builder.write_string("\nduplicate definitions:\n")
if self.duplicates.is_empty() {
builder.write_string(" \n")
} else {
for duplicate in self.duplicates {
builder.write_string(" ")
builder.write_string(duplicate.name)
builder.write_string(": ")
for index in 0.. 0 {
builder.write_string(", ")
}
builder.write_string(duplicate.objects[index])
}
builder.write_string("\n")
}
}
builder.write_string("\nunresolved:\n")
if self.unresolved.is_empty() {
builder.write_string(" \n")
} else {
for unresolved in self.unresolved {
builder.write_string(" ")
builder.write_string(unresolved.name)
builder.write_string(" <- ")
for index in 0.. 0 {
builder.write_string(", ")
}
builder.write_string(unresolved.references[index].object_name)
}
builder.write_string("\n")
}
}
builder.to_string()
}
///|
fn add_definition(
definitions : Map[String, Array[DefinitionCandidate]],
name : String,
candidate : DefinitionCandidate,
) -> Unit {
let entries = match definitions.get(name) {
Some(value) => value
None => {
let created : Array[DefinitionCandidate] = []
definitions.set(name, created)
created
}
}
entries.push(candidate)
}
///|
fn add_reference(
references : Map[String, Array[SymbolReference]],
name : String,
reference : SymbolReference,
) -> Unit {
let entries = match references.get(name) {
Some(value) => value
None => {
let created : Array[SymbolReference] = []
references.set(name, created)
created
}
}
entries.push(reference)
}
///|
priv struct DefinitionCandidate {
object_index : UInt32
symbol : LinkSymbol
}
///|
fn resolved_from(
candidate : DefinitionCandidate,
inputs : Array[LinkInput],
) -> ResolvedSymbol {
let symbol = candidate.symbol
{
name: symbol.name,
binding: symbol.binding,
kind: symbol.kind,
object_index: candidate.object_index,
object_name: inputs[candidate.object_index.reinterpret_as_int()].name,
symbol_index: symbol.index,
value: symbol.value,
size: symbol.size,
}
}
///|
/// Resolve global and weak definitions across a set of input objects.
///
/// Resolution order is caller order: inputs and symbols are visited in the
/// order supplied. A strong definition wins over weak and common definitions;
/// two strong definitions are reported as a duplicate instead of being silently
/// chosen. Local symbols never participate in cross-object resolution.
pub fn resolve_objects(inputs : Array[LinkInput]) -> LinkResolution {
let names : Array[String] = []
let definitions : Map[String, Array[DefinitionCandidate]] = Map([])
let references : Map[String, Array[SymbolReference]] = Map([])
let mut input_symbol_count = 0
for object_index in 0.. value
None => []
}
let references_for_name = match references.get(name) {
Some(value) => value
None => []
}
let strong : Array[DefinitionCandidate] = []
let weak : Array[DefinitionCandidate] = []
for candidate in candidates {
match candidate.symbol.binding {
Global =>
if candidate.symbol.is_common() {
weak.push(candidate)
} else {
strong.push(candidate)
}
Weak => weak.push(candidate)
Local | Other(_) => ()
}
}
if strong.length() > 1 {
let objects : Array[String] = []
for candidate in strong {
objects.push(inputs[candidate.object_index.reinterpret_as_int()].name)
}
duplicates.push({ name, objects, })
} else if strong.length() == 1 {
resolved.push(resolved_from(strong[0], inputs))
} else if weak.length() > 0 {
resolved.push(resolved_from(weak[0], inputs))
} else if references_for_name.length() > 0 {
unresolved.push({ name, references: references_for_name, })
}
}
{
symbols: resolved,
unresolved,
duplicates,
object_count: inputs.length(),
input_symbol_count,
}
}