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 }