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 };