lib/tldr/src/object.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const sys = @import("sys");
  3 const incremental = @import("incremental/root.zig");
  4 const model = @import("model.zig");
  5 
  6 const Target = model.Target;
  7 
  8 pub const LinkedImage = struct {
  9     bytes: []u8,
 10     storage: Storage = .heap,
 11     manifest: incremental.Manifest,
 12 
 13     pub const Storage = union(enum) {
 14         heap,
 15         mapped_file: []align(std.heap.page_size_min) u8,
 16 
 17         pub const Allocation = struct {
 18             bytes: []u8,
 19             storage: Storage,
 20 
 21             pub fn deinit(self: *Allocation, allocator: std.mem.Allocator) void {
 22                 switch (self.storage) {
 23                     .heap => allocator.free(self.bytes),
 24                     .mapped_file => |mapping| sys.memory.unmap(mapping),
 25                 }
 26                 self.* = undefined;
 27             }
 28 
 29             pub fn needsZeroFill(self: Allocation) bool {
 30                 return switch (self.storage) {
 31                     .heap => true,
 32                     .mapped_file => false,
 33                 };
 34             }
 35         };
 36 
 37         pub fn allocate(
 38             allocator: std.mem.Allocator,
 39             len: usize,
 40             options: model.LinkOptions,
 41         ) model.Error!Allocation {
 42             if (options.mapped_output_file) |file| {
 43                 if (len >= options.mapped_output_min_size) {
 44                     const file_len = std.math.cast(u64, len) orelse return error.OutputFileError;
 45                     file.setLength(sys.fs.debugIo(), 0) catch return error.OutputFileError;
 46                     file.setLength(sys.fs.debugIo(), file_len) catch return error.OutputFileError;
 47                     const mapping = sys.memory.mapSharedFile(file.handle, len, .{ .read = true, .write = true }, 0) catch |err| switch (err) {
 48                         error.UnsupportedPlatform => return heapAllocation(allocator, len),
 49                         else => return error.OutputFileError,
 50                     };
 51                     return .{
 52                         .bytes = mapping[0..len],
 53                         .storage = .{ .mapped_file = mapping },
 54                     };
 55                 }
 56             }
 57             return heapAllocation(allocator, len);
 58         }
 59 
 60         fn heapAllocation(allocator: std.mem.Allocator, len: usize) model.Error!Allocation {
 61             return .{
 62                 .bytes = try allocator.alloc(u8, len),
 63                 .storage = .heap,
 64             };
 65         }
 66     };
 67 
 68     pub fn deinit(self: *LinkedImage, allocator: std.mem.Allocator) void {
 69         switch (self.storage) {
 70             .heap => allocator.free(self.bytes),
 71             .mapped_file => |mapping| sys.memory.unmap(mapping),
 72         }
 73         self.manifest.deinit(allocator);
 74         self.* = undefined;
 75     }
 76 
 77     pub fn isMaterialized(self: LinkedImage) bool {
 78         return switch (self.storage) {
 79             .heap => false,
 80             .mapped_file => true,
 81         };
 82     }
 83 
 84     pub fn prepareIncrementalState(
 85         self: *LinkedImage,
 86         allocator: std.mem.Allocator,
 87     ) std.mem.Allocator.Error!incremental.PreparedState {
 88         return try incremental.PreparedState.fromOwnedManifest(allocator, self.manifest.take());
 89     }
 90 };
 91 
 92 pub const Object = struct {
 93     target: Target,
 94     sections: []ObjectSection,
 95     symbols: []ObjectSymbol,
 96 
 97     pub fn deinit(self: *Object, allocator: std.mem.Allocator) void {
 98         if (self.sections.len != 0) allocator.free(self.sections);
 99         if (self.symbols.len != 0) allocator.free(self.symbols);
100         self.* = undefined;
101     }
102 };
103 
104 pub const ObjectSection = struct {
105     segment_name: []const u8 = "",
106     name: []const u8,
107     address: u64 = 0,
108     size: u64,
109     offset: u64,
110     alignment: u64,
111     flags: u64,
112     section_type: u32 = 0,
113     relocation_count: u32,
114 };
115 
116 pub const ObjectSymbol = struct {
117     name: []const u8,
118     section_index: i32,
119     value: u64,
120     size: u64 = 0,
121     kind: u32,
122     binding: u32 = 0,
123     external: bool,
124     undefined: bool,
125 };
126 
127 test {
128     std.testing.refAllDecls(@This());
129 }
130 
131 test "linked image moves manifest into prepared incremental state" {
132     const allocator = std.testing.allocator;
133     var builder = try incremental.Builder.init(allocator, .{});
134     defer builder.deinit();
135     try builder.addInput(.{ .name = "a.o", .bytes = "abc" });
136     try builder.addContribution("a.o", 0, .section, ".text", 1, ".text", 0x401000, 4, 3, 8, 16);
137 
138     var linked: LinkedImage = .{
139         .bytes = try allocator.dupe(u8, "fake"),
140         .manifest = try builder.finish(),
141     };
142     defer linked.deinit(allocator);
143 
144     var state = try linked.prepareIncrementalState(allocator);
145     defer state.deinit(allocator);
146 
147     try std.testing.expectEqual(@as(usize, 0), linked.manifest.inputs.len);
148     try std.testing.expect(state.canReuseFor(.{}, &.{.{ .name = "a.o", .bytes = "abc" }}));
149     var image = @as([12]u8, @splat(0xaa));
150     const application = try state.applyContributionReplacement(image[0..], &.{
151         .{ .input_name = "a.o", .input_index = 0, .kind = .section, .name = ".text", .ordinal = 1, .size = 5, .alignment = 16, .payload = "patch" },
152     });
153     try std.testing.expectEqual(incremental.PatchDecision.in_place, application.plan.decision);
154 }