lib/tldr/src/formats/elf/archive/member.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const root = @import("../../../root.zig");
3 const elf = @import("../root.zig");
4 const prefetch = @import("prefetch.zig");
5
6 const Allocator = std.mem.Allocator;
7 const archive_format = root.archive;
8 const model = root.model;
9 const trace = root.trace;
10 const ObjectParseOptions = elf.parser.ObjectParseOptions;
11 const ObjectSelectionSummary = elf.parser.ObjectSelectionSummary;
12 const isElf = elf.format.isElf;
13 const parseObjectWithOptions = elf.parser.parseObjectWithOptions;
14
15 pub fn addCandidates(
16 allocator: Allocator,
17 selection: Allocator,
18 input: model.Input,
19 input_index: usize,
20 state: anytype,
21 parse_options: ObjectParseOptions,
22 prefetched: ?prefetch.Prefetched,
23 ) model.Error!void {
24 const parsed = if (prefetched) |ready| ready.parsed else parse_table: {
25 const table_phase = trace.product(.archive_table_parse);
26 defer table_phase.end();
27 break :parse_table try archive_format.parseDetailed(selection, input);
28 };
29
30 if (!parsed.has_symbol_index) {
31 const first_candidate_index = state.candidates.items.len;
32 try buildScannedCandidates(allocator, input.name, input_index, parsed.members, state, parse_options);
33 try indexDefinitionsFromObjects(
34 allocator,
35 state,
36 first_candidate_index,
37 );
38 } else {
39 const indexing_phase = trace.product(.archive_definition_indexing);
40 defer indexing_phase.end();
41 try buildIndexedCandidates(
42 allocator,
43 input.name,
44 input_index,
45 parsed.members,
46 parsed.symbol_index,
47 state,
48 if (prefetched) |ready| ready.summaries else &.{},
49 if (prefetched) |ready| ready.failures else &.{},
50 );
51 }
52 }
53
54 fn buildScannedCandidates(
55 allocator: Allocator,
56 archive_name: []const u8,
57 archive_input_index: usize,
58 members: []const archive_format.Member,
59 state: anytype,
60 parse_options: ObjectParseOptions,
61 ) model.Error!void {
62 for (members) |member| {
63 if (!isElf(member.bytes)) continue;
64 var object = try parseObjectWithOptions(allocator, .{
65 .name = member.name,
66 .bytes = member.bytes,
67 }, parse_options);
68 object.input_index = archive_input_index;
69 errdefer object.deinit(allocator);
70 _ = try state.appendCandidate(allocator, .{
71 .archive_name = archive_name,
72 .archive_input_index = archive_input_index,
73 .member = member,
74 .object = object,
75 });
76 }
77 }
78
79 fn buildIndexedCandidates(
80 allocator: Allocator,
81 archive_name: []const u8,
82 archive_input_index: usize,
83 members: []const archive_format.Member,
84 symbol_index: []const archive_format.SymbolIndexEntry,
85 state: anytype,
86 summaries: []const ?ObjectSelectionSummary,
87 failures: []const ?model.Error,
88 ) model.Error!void {
89 try state.candidates.ensureUnusedCapacity(allocator, symbol_index.len);
90 try state.active_candidate_counts.ensureUnusedCapacity(allocator, symbol_index.len);
91 const definition_capacity = std.math.cast(u32, symbol_index.len) orelse return error.InvalidArchive;
92 try state.definitions.ensureUnusedCapacity(allocator, definition_capacity);
93
94 const missing_candidate = std.math.maxInt(usize);
95 const candidate_by_member = try allocator.alloc(usize, members.len);
96 defer allocator.free(candidate_by_member);
97 @memset(candidate_by_member, missing_candidate);
98
99 var offsets_ordered = true;
100 var previous_member_offset: u64 = 0;
101 var member_cursor: usize = 0;
102 for (symbol_index) |entry| {
103 if (entry.member_offset < previous_member_offset) offsets_ordered = false;
104 previous_member_offset = entry.member_offset;
105 const member_index = if (offsets_ordered)
106 archive_format.memberIndexByOffsetWithCursor(members, entry.member_offset, &member_cursor) orelse return error.InvalidArchive
107 else
108 archive_format.memberIndexByOffset(members, entry.member_offset) orelse return error.InvalidArchive;
109 var candidate_index = candidate_by_member[member_index];
110 if (candidate_index == missing_candidate) {
111 candidate_index = state.candidates.items.len;
112 candidate_by_member[member_index] = candidate_index;
113 state.active_candidate_counts.appendAssumeCapacity(0);
114 state.candidates.appendAssumeCapacity(.{
115 .archive_name = archive_name,
116 .archive_input_index = archive_input_index,
117 .member = members[member_index],
118 .summary = if (summaries.len != 0) summaries[member_index] else null,
119 .summary_failure = if (failures.len != 0) failures[member_index] else null,
120 });
121 }
122 try state.addDefinition(allocator, entry.name, candidate_index);
123 }
124 }
125
126 fn indexDefinitionsFromObjects(
127 allocator: Allocator,
128 state: anytype,
129 first_candidate_index: usize,
130 ) model.Error!void {
131 for (state.candidates.items[first_candidate_index..], 0..) |candidate, index| {
132 const parsed_object = candidate.object orelse return error.InvalidObject;
133 for (parsed_object.symbols) |symbol| {
134 if (!symbol.isGlobalDefinition()) continue;
135 try state.addDefinition(allocator, symbol.name, first_candidate_index + index);
136 }
137 }
138 }