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 }