lib/tldr/src/formats/elf/image/symbols.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const root = @import("../../../root.zig");
3 const elf = @import("../root.zig");
4
5 const Allocator = std.mem.Allocator;
6 const model = root.model;
7 const parallel = root.parallel;
8 const StringTable = elf.string.Table;
9 const ObjectFile = elf.parser.ObjectFile;
10 const ObjectLayout = elf.layout.ObjectLayout;
11 const OutputSection = elf.layout.OutputSection;
12 const GlobalSymbol = elf.layout.GlobalSymbol;
13 const SymbolRef = elf.layout.SymbolRef;
14 const Access = elf.addressing.Access;
15 const SymbolAddressCache = elf.addressing.Cache;
16 const SymbolRecord = elf.format.SymbolRecord;
17
18 const ResolvedEntry = struct {
19 found: bool = false,
20 name: []const u8 = "",
21 entry: SymbolRecord = .{},
22 };
23
24 const EntryFailure = struct {
25 found: bool = false,
26 ref_index: usize = 0,
27 err: model.Error = error.InvalidObject,
28
29 fn isFound(failure: EntryFailure) bool {
30 return failure.found;
31 }
32
33 fn before(left: EntryFailure, right: EntryFailure) bool {
34 return left.ref_index < right.ref_index;
35 }
36 };
37
38 const EntryFailures = parallel.FailureSlots(EntryFailure);
39
40 const ParallelEntryContext = struct {
41 objects: []const ObjectFile,
42 layouts: []const ObjectLayout,
43 output_sections: []const OutputSection,
44 globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
45 global_symbol_refs: []const SymbolRef,
46 symbol_addresses: *SymbolAddressCache,
47 resolved_entries: []ResolvedEntry,
48 failures: *EntryFailures,
49 };
50
51 pub const parallel_entry_threshold = 32768;
52 const entries_per_worker = 8192;
53
54 pub fn appendExecutable(
55 allocator: Allocator,
56 out: *std.ArrayListUnmanaged(SymbolRecord),
57 strtab: *StringTable,
58 objects: []const ObjectFile,
59 layouts: []const ObjectLayout,
60 output_sections: []const OutputSection,
61 globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
62 global_symbol_refs: []const SymbolRef,
63 symbol_addresses: *SymbolAddressCache,
64 max_link_jobs: usize,
65 ) model.Error!void {
66 const requested_workers = if (max_link_jobs != 0)
67 max_link_jobs
68 else
69 global_symbol_refs.len / entries_per_worker;
70 const workers = if (global_symbol_refs.len >= parallel_entry_threshold)
71 parallel.chooseWorkers(global_symbol_refs.len, requested_workers)
72 else
73 1;
74 if (workers > 1) {
75 return appendExecutableParallel(allocator, out, strtab, objects, layouts, output_sections, globals, global_symbol_refs, symbol_addresses, workers);
76 }
77 try appendExecutableSerial(allocator, out, strtab, objects, layouts, output_sections, globals, global_symbol_refs, symbol_addresses);
78 }
79
80 fn appendExecutableSerial(
81 allocator: Allocator,
82 out: *std.ArrayListUnmanaged(SymbolRecord),
83 strtab: *StringTable,
84 objects: []const ObjectFile,
85 layouts: []const ObjectLayout,
86 output_sections: []const OutputSection,
87 globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
88 global_symbol_refs: []const SymbolRef,
89 symbol_addresses: *SymbolAddressCache,
90 ) model.Error!void {
91 for (global_symbol_refs) |current_ref| {
92 if (try resolveExecutableEntry(.serial, objects, layouts, output_sections, globals, symbol_addresses, current_ref)) |resolved| {
93 try appendResolvedEntry(allocator, out, strtab, resolved);
94 }
95 }
96 }
97
98 fn appendExecutableParallel(
99 allocator: Allocator,
100 out: *std.ArrayListUnmanaged(SymbolRecord),
101 strtab: *StringTable,
102 objects: []const ObjectFile,
103 layouts: []const ObjectLayout,
104 output_sections: []const OutputSection,
105 globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
106 global_symbol_refs: []const SymbolRef,
107 symbol_addresses: *SymbolAddressCache,
108 workers: usize,
109 ) model.Error!void {
110 const resolved_entries = try allocator.alloc(ResolvedEntry, global_symbol_refs.len);
111 defer allocator.free(resolved_entries);
112 for (resolved_entries) |*entry| entry.* = .{};
113
114 var failures = try EntryFailures.init(allocator, workers, .{});
115 defer failures.deinit(allocator);
116
117 var context = ParallelEntryContext{
118 .objects = objects,
119 .layouts = layouts,
120 .output_sections = output_sections,
121 .globals = globals,
122 .global_symbol_refs = global_symbol_refs,
123 .symbol_addresses = symbol_addresses,
124 .resolved_entries = resolved_entries,
125 .failures = &failures,
126 };
127 parallel.forChunks(global_symbol_refs.len, workers, &context, resolveEntryChunk);
128 if (failures.earliest(EntryFailure.isFound, EntryFailure.before)) |failure| return failure.err;
129
130 for (resolved_entries) |resolved| {
131 if (!resolved.found) continue;
132 try appendResolvedEntry(allocator, out, strtab, resolved);
133 }
134 }
135
136 fn resolveEntryChunk(context: *ParallelEntryContext, worker: usize, start: usize, end: usize) void {
137 var index = start;
138 while (index < end) : (index += 1) {
139 const resolved = resolveExecutableEntry(.concurrent, context.objects, context.layouts, context.output_sections, context.globals, context.symbol_addresses, context.global_symbol_refs[index]) catch |err| {
140 const current = context.failures.items[worker];
141 const failure = EntryFailure{
142 .found = true,
143 .ref_index = index,
144 .err = err,
145 };
146 if (!current.found or EntryFailure.before(failure, current)) {
147 context.failures.record(worker, failure);
148 }
149 continue;
150 };
151 context.resolved_entries[index] = resolved orelse .{};
152 }
153 }
154
155 fn resolveExecutableEntry(
156 comptime access: Access,
157 objects: []const ObjectFile,
158 layouts: []const ObjectLayout,
159 output_sections: []const OutputSection,
160 globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
161 symbol_addresses: *SymbolAddressCache,
162 current_ref: SymbolRef,
163 ) model.Error!?ResolvedEntry {
164 const object = objects[current_ref.object_index];
165 const symbol = object.symbols[current_ref.symbol_index];
166 if (elf.section_state.symbolSectionDiscarded(object, symbol)) return null;
167 if (elf.addressing.symbolIsTls(object, symbol)) {
168 const value = try elf.addressing.tlsOffset(objects, layouts, output_sections, globals, symbol_addresses, current_ref.object_index, current_ref.symbol_index);
169 const contribution = try elf.addressing.contribution(objects, layouts, current_ref.object_index, current_ref.symbol_index);
170 const output = output_sections[contribution.outputIndex()];
171 return .{
172 .found = true,
173 .name = symbol.name,
174 .entry = .{
175 .info = symbol.info,
176 .other = symbol.other,
177 .section_index = output.section_index,
178 .value = value,
179 .size = symbol.size,
180 },
181 };
182 }
183 if (symbol.isAbsolute()) {
184 return .{
185 .found = true,
186 .name = symbol.name,
187 .entry = .{
188 .info = symbol.info,
189 .other = symbol.other,
190 .section_index = std.elf.SHN_ABS,
191 .value = symbol.value,
192 .size = symbol.size,
193 },
194 };
195 }
196 const value = try elf.addressing.symbolAddress(access, objects, layouts, output_sections, globals, symbol_addresses, current_ref.object_index, current_ref.symbol_index);
197 const contribution = try elf.addressing.contribution(objects, layouts, current_ref.object_index, current_ref.symbol_index);
198 const output = output_sections[contribution.outputIndex()];
199 return .{
200 .found = true,
201 .name = symbol.name,
202 .entry = .{
203 .info = symbol.info,
204 .other = symbol.other,
205 .section_index = output.section_index,
206 .value = value,
207 .size = symbol.size,
208 },
209 };
210 }
211
212 fn appendResolvedEntry(
213 allocator: Allocator,
214 out: *std.ArrayListUnmanaged(SymbolRecord),
215 strtab: *StringTable,
216 resolved: ResolvedEntry,
217 ) model.Error!void {
218 var entry = resolved.entry;
219 entry.name_offset = try strtab.add(allocator, resolved.name);
220 try out.append(allocator, entry);
221 }