lib/tldr/src/formats/elf/merge/fixed.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 ObjectFile = elf.parser.ObjectFile;
8 const OutputSection = elf.layout.OutputSection;
9 const SectionContribution = elf.layout.SectionContribution;
10 const MergePieceLayout = elf.layout.MergePieceLayout;
11 const MergeSectionLayout = elf.layout.MergeSectionLayout;
12 const SectionHeader = elf.format.SectionHeader;
13 const output_section_count = elf.output_section.output_section_count;
14 const hashU64 = elf.format.hashU64;
15 const sectionBytes = elf.format.sectionBytes;
16 const validateAlignment = elf.format.validateAlignment;
17 const alignForwardU64 = elf.format.alignForwardU64;
18 const outputIndexForAlloc = elf.output_section.indexForAllocated;
19 const contributionReserveSize = elf.layout.contributionReserveSize;
20
21 const Key = struct {
22 section_type: u32,
23 output_index: u8,
24 entry_size: u64,
25 alignment: u64,
26 bytes: []const u8,
27
28 const Context = struct {
29 pub fn hash(_: Context, key: Key) u64 {
30 var value = hashU64(0x4d6f3a27c994f64b, key.section_type);
31 value = hashU64(value, key.output_index);
32 value = hashU64(value, key.entry_size);
33 value = hashU64(value, key.alignment);
34 return std.hash.Wyhash.hash(value, key.bytes);
35 }
36
37 pub fn eql(_: Context, left: Key, right: Key) bool {
38 return left.section_type == right.section_type and
39 left.output_index == right.output_index and
40 left.entry_size == right.entry_size and
41 left.alignment == right.alignment and
42 std.mem.eql(u8, left.bytes, right.bytes);
43 }
44 };
45 };
46
47 const Target = struct {
48 contribution: SectionContribution,
49 intra_offset: u64 = 0,
50 };
51
52 const context = Key.Context{};
53 const Map = std.HashMapUnmanaged(
54 Key,
55 Target,
56 Key.Context,
57 80,
58 );
59
60 pub const Fixed = struct {
61 pieces: Map = .empty,
62
63 pub fn deinit(self: *Fixed, allocator: Allocator) void {
64 self.pieces.deinit(allocator);
65 }
66
67 pub fn collect(
68 self: *Fixed,
69 allocator: Allocator,
70 object: ObjectFile,
71 section: SectionHeader,
72 section_index: usize,
73 output_sections: *[output_section_count]OutputSection,
74 merge_sections: []MergeSectionLayout,
75 options: model.LinkOptions,
76 ) model.Error!void {
77 if (section.size % section.entry_size != 0) return error.InvalidObject;
78 if (object.relocationsForSection(section_index).len != 0) return error.UnsupportedRelocation;
79
80 try validateAlignment(section.alignment);
81 const entry_size: usize = @intCast(section.entry_size);
82 const entry_count: usize = @intCast(section.size / section.entry_size);
83 const layouts = try allocator.alloc(MergePieceLayout, entry_count);
84 errdefer allocator.free(layouts);
85
86 const output_index = outputIndexForAlloc(section);
87 const output_index_u8: u8 = @intCast(output_index);
88 var output = &output_sections[output_index];
89 const alignment = @max(section.alignment, 1);
90 output.alignment = @max(output.alignment, alignment);
91
92 const bytes = try sectionBytes(object.bytes, section);
93 for (layouts, 0..) |*piece_layout, index| {
94 const start = index * entry_size;
95 const piece_bytes = bytes[start..][0..entry_size];
96 piece_layout.* = .{
97 .input_offset = @intCast(start),
98 .size = section.entry_size,
99 };
100 const key: Key = .{
101 .section_type = section.section_type,
102 .output_index = output_index_u8,
103 .entry_size = section.entry_size,
104 .alignment = alignment,
105 .bytes = piece_bytes,
106 };
107 if (self.pieces.getContext(key, context)) |existing| {
108 piece_layout.contribution = existing.contribution;
109 piece_layout.output_intra_offset = existing.intra_offset;
110 continue;
111 }
112
113 const aligned_offset = alignForwardU64(output.file_size, alignment);
114 const reserved_size = contributionReserveSize(section.entry_size, alignment, options);
115 const contribution = SectionContribution.init(
116 output_index,
117 aligned_offset,
118 section.entry_size,
119 reserved_size,
120 alignment,
121 );
122 try self.pieces.putContext(allocator, key, .{ .contribution = contribution }, context);
123 piece_layout.contribution = contribution;
124 output.file_size = aligned_offset + reserved_size;
125 output.memory_size = output.file_size;
126 }
127
128 merge_sections[section_index] = .{
129 .pieces = layouts,
130 .fixed_piece_size = section.entry_size,
131 };
132 }
133 };