authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-09 11:01:51+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-09 20:36:58+01:00
loga28340405392b4a0a687e668406a067be1ae5e3c
tree6cbe7cf493d3a33459ae65de815b92290b6fed7f
parenta579f8ae8d6009d95ef22879bc725a233f838d6f

macho: split writing Trie into finalize and const write


2 files changed, 234 insertions(+), 204 deletions(-)

src/link/MachO.zig+5-1
......@@ -1810,8 +1810,12 @@ fn writeExportTrie(self: *MachO) !void {
18101810 });
18111811 }
18121812
1813 var buffer = try trie.writeULEB128Mem();
1813 try trie.finalize();
1814 var buffer = try self.base.allocator.alloc(u8, trie.size);
18141815 defer self.base.allocator.free(buffer);
1816 var stream = std.io.fixedBufferStream(buffer);
1817 const nwritten = try trie.write(stream.writer());
1818 assert(nwritten == trie.size);
18151819
18161820 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
18171821 const export_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64)));
src/link/MachO/Trie.zig+229-203
......@@ -51,7 +51,7 @@ pub const Edge = struct {
5151 label: []u8,
5252
5353 fn deinit(self: *Edge, allocator: *Allocator) void {
54 self.to.deinit();
54 self.to.deinit(allocator);
5555 allocator.destroy(self.to);
5656 allocator.free(self.label);
5757 self.from = undefined;
......@@ -62,6 +62,7 @@ pub const Edge = struct {
6262
6363pub const Node = struct {
6464 base: *Trie,
65
6566 /// Terminal info associated with this node.
6667 /// If this node is not a terminal node, info is null.
6768 terminal_info: ?struct {
......@@ -70,82 +71,93 @@ pub const Node = struct {
7071 /// VM address offset wrt to the section this symbol is defined against.
7172 vmaddr_offset: u64,
7273 } = null,
74
7375 /// Offset of this node in the trie output byte stream.
7476 trie_offset: ?usize = null,
77
7578 /// List of all edges originating from this node.
7679 edges: std.ArrayListUnmanaged(Edge) = .{},
7780
78 fn deinit(self: *Node) void {
81 node_dirty: bool = true,
82
83 fn deinit(self: *Node, allocator: *Allocator) void {
7984 for (self.edges.items) |*edge| {
80 edge.deinit(self.base.allocator);
85 edge.deinit(allocator);
8186 }
82 self.edges.deinit(self.base.allocator);
87 self.edges.deinit(allocator);
8388 }
8489
8590 /// Inserts a new node starting from `self`.
86 fn put(self: *Node, label: []const u8) !*Node {
91 fn put(self: *Node, allocator: *Allocator, label: []const u8) !*Node {
8792 // Check for match with edges from this node.
8893 for (self.edges.items) |*edge| {
8994 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
9095 if (match == 0) continue;
91 if (match == edge.label.len) return edge.to.put(label[match..]);
96 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
9297
9398 // Found a match, need to splice up nodes.
9499 // From: A -> B
95100 // To: A -> C -> B
96 const mid = try self.base.allocator.create(Node);
101 const mid = try allocator.create(Node);
97102 mid.* = .{ .base = self.base };
98 var to_label = try self.base.allocator.dupe(u8, edge.label[match..]);
99 self.base.allocator.free(edge.label);
103 var to_label = try allocator.dupe(u8, edge.label[match..]);
104 allocator.free(edge.label);
100105 const to_node = edge.to;
101106 edge.to = mid;
102 edge.label = try self.base.allocator.dupe(u8, label[0..match]);
107 edge.label = try allocator.dupe(u8, label[0..match]);
103108 self.base.node_count += 1;
104109
105 try mid.edges.append(self.base.allocator, .{
110 try mid.edges.append(allocator, .{
106111 .from = mid,
107112 .to = to_node,
108113 .label = to_label,
109114 });
110115
111 return if (match == label.len) to_node else mid.put(label[match..]);
116 return if (match == label.len) to_node else mid.put(allocator, label[match..]);
112117 }
113118
114119 // Add a new node.
115 const node = try self.base.allocator.create(Node);
120 const node = try allocator.create(Node);
116121 node.* = .{ .base = self.base };
117122 self.base.node_count += 1;
118123
119 try self.edges.append(self.base.allocator, .{
124 try self.edges.append(allocator, .{
120125 .from = self,
121126 .to = node,
122 .label = try self.base.allocator.dupe(u8, label),
127 .label = try allocator.dupe(u8, label),
123128 });
124129
125130 return node;
126131 }
127132
128 fn fromByteStream(self: *Node, stream: anytype) Trie.FromByteStreamError!void {
129 self.trie_offset = try stream.getPos();
130 var reader = stream.reader();
133 /// Recursively parses the node from the input byte stream.
134 fn read(self: *Node, allocator: *Allocator, reader: anytype) Trie.ReadError!void {
135 self.node_dirty = true;
136
137 self.trie_offset = try reader.context.getPos();
138
131139 const node_size = try leb.readULEB128(u64, reader);
132140 if (node_size > 0) {
133141 const export_flags = try leb.readULEB128(u64, reader);
134142 // TODO Parse special flags.
135143 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
136144 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
145
137146 const vmaddr_offset = try leb.readULEB128(u64, reader);
147
138148 self.terminal_info = .{
139149 .export_flags = export_flags,
140150 .vmaddr_offset = vmaddr_offset,
141151 };
142152 }
153
143154 const nedges = try reader.readByte();
144155 self.base.node_count += nedges;
156
145157 var i: usize = 0;
146158 while (i < nedges) : (i += 1) {
147159 var label = blk: {
148 var label_buf = std.ArrayList(u8).init(self.base.allocator);
160 var label_buf = std.ArrayList(u8).init(allocator);
149161 while (true) {
150162 const next = try reader.readByte();
151163 if (next == @as(u8, 0))
......@@ -154,25 +166,32 @@ pub const Node = struct {
154166 }
155167 break :blk label_buf.toOwnedSlice();
156168 };
169
157170 const seek_to = try leb.readULEB128(u64, reader);
158 const cur_pos = try stream.getPos();
159 try stream.seekTo(seek_to);
160 var node = try self.base.allocator.create(Node);
171 const cur_pos = try reader.context.getPos();
172 try reader.context.seekTo(seek_to);
173
174 const node = try allocator.create(Node);
161175 node.* = .{ .base = self.base };
162 try node.fromByteStream(stream);
163 try self.edges.append(self.base.allocator, .{
176
177 try node.read(allocator, reader);
178 try self.edges.append(allocator, .{
164179 .from = self,
165180 .to = node,
166181 .label = label,
167182 });
168 try stream.seekTo(cur_pos);
183 try reader.context.seekTo(cur_pos);
169184 }
170185 }
171186
172 /// This method should only be called *after* updateOffset has been called!
173 /// In case this is not upheld, this method will panic.
174 fn writeULEB128Mem(self: Node, buffer: *std.ArrayList(u8)) !void {
175 assert(self.trie_offset != null); // You need to call updateOffset first.
187 /// Writes this node to a byte stream.
188 /// The children of this node *are* not written to the byte stream
189 /// recursively. To write all nodes to a byte stream in sequence,
190 /// iterate over `Trie.ordered_nodes` and call this method on each node.
191 /// This is one of the requirements of the MachO.
192 /// Panics if `finalize` was not called before calling this method.
193 fn write(self: Node, writer: anytype) !void {
194 assert(!self.node_dirty);
176195 if (self.terminal_info) |info| {
177196 // Terminal node info: encode export flags and vmaddr offset of this symbol.
178197 var info_buf_len: usize = 0;
......@@ -189,38 +208,35 @@ pub const Node = struct {
189208 var size_stream = std.io.fixedBufferStream(&size_buf);
190209 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
191210
192 // Now, write them to the output buffer.
193 buffer.appendSliceAssumeCapacity(size_buf[0..size_stream.pos]);
194 buffer.appendSliceAssumeCapacity(info_buf[0..info_stream.pos]);
211 // Now, write them to the output stream.
212 try writer.writeAll(size_buf[0..size_stream.pos]);
213 try writer.writeAll(info_buf[0..info_stream.pos]);
195214 } else {
196215 // Non-terminal node is delimited by 0 byte.
197 buffer.appendAssumeCapacity(0);
216 try writer.writeByte(0);
198217 }
199218 // Write number of edges (max legal number of edges is 256).
200 buffer.appendAssumeCapacity(@intCast(u8, self.edges.items.len));
219 try writer.writeByte(@intCast(u8, self.edges.items.len));
201220
202221 for (self.edges.items) |edge| {
203 // Write edges labels.
204 buffer.appendSliceAssumeCapacity(edge.label);
205 buffer.appendAssumeCapacity(0);
206
207 var buf: [@sizeOf(u64)]u8 = undefined;
208 var buf_stream = std.io.fixedBufferStream(&buf);
209 try leb.writeULEB128(buf_stream.writer(), edge.to.trie_offset.?);
210 buffer.appendSliceAssumeCapacity(buf[0..buf_stream.pos]);
222 // Write edge label and offset to next node in trie.
223 try writer.writeAll(edge.label);
224 try writer.writeByte(0);
225 try leb.writeULEB128(writer, edge.to.trie_offset.?);
211226 }
212227 }
213228
214 const UpdateResult = struct {
229 const FinalizeResult = struct {
215230 /// Current size of this node in bytes.
216231 node_size: usize,
232
217233 /// True if the trie offset of this node in the output byte stream
218234 /// would need updating; false otherwise.
219235 updated: bool,
220236 };
221237
222238 /// Updates offset of this node in the output byte stream.
223 fn updateOffset(self: *Node, offset: usize) UpdateResult {
239 fn finalize(self: *Node, offset_in_trie: usize) FinalizeResult {
224240 var node_size: usize = 0;
225241 if (self.terminal_info) |info| {
226242 node_size += sizeULEB128Mem(info.export_flags);
......@@ -237,8 +253,9 @@ pub const Node = struct {
237253 }
238254
239255 const trie_offset = self.trie_offset orelse 0;
240 const updated = offset != trie_offset;
241 self.trie_offset = offset;
256 const updated = offset_in_trie != trie_offset;
257 self.trie_offset = offset_in_trie;
258 self.node_dirty = false;
242259
243260 return .{ .node_size = node_size, .updated = updated };
244261 }
......@@ -256,15 +273,30 @@ pub const Node = struct {
256273 }
257274};
258275
259/// Count of nodes in the trie.
260/// The count is updated at every `put` call.
261/// The trie always consists of at least a root node, hence
262/// the count always starts at 1.
263node_count: usize = 1,
264276/// The root node of the trie.
265root: ?Node = null,
277root: ?*Node = null,
278
266279allocator: *Allocator,
267280
281/// If you want to access nodes ordered in DFS fashion,
282/// you should call `finalize` first since the nodes
283/// in this container are not guaranteed to not be stale
284/// if more insertions took place after the last `finalize`
285/// call.
286ordered_nodes: std.ArrayListUnmanaged(*Node) = .{},
287
288/// The size of the trie in bytes.
289/// This value may be outdated if there were additional
290/// insertions performed after `finalize` was called.
291/// Call `finalize` before accessing this value to ensure
292/// it is up-to-date.
293size: usize = 0,
294
295/// Number of nodes currently in the trie.
296node_count: usize = 0,
297
298trie_dirty: bool = true,
299
268300pub fn init(allocator: *Allocator) Trie {
269301 return .{ .allocator = allocator };
270302}
......@@ -273,76 +305,90 @@ pub fn init(allocator: *Allocator) Trie {
273305/// This operation may change the layout of the trie by splicing edges in
274306/// certain circumstances.
275307pub fn put(self: *Trie, symbol: Symbol) !void {
276 if (self.root == null) {
277 self.root = .{ .base = self };
278 }
279 const node = try self.root.?.put(symbol.name);
308 try self.createRoot();
309 const node = try self.root.?.put(self.allocator, symbol.name);
280310 node.terminal_info = .{
281311 .vmaddr_offset = symbol.vmaddr_offset,
282312 .export_flags = symbol.export_flags,
283313 };
314 self.trie_dirty = true;
284315}
285316
286const FromByteStreamError = error{
287 OutOfMemory,
288 EndOfStream,
289 Overflow,
290};
317/// Finalizes this trie for writing to a byte stream.
318/// This step performs multiple passes through the trie ensuring
319/// there are no gaps after every `Node` is ULEB128 encoded.
320/// Call this method before trying to `write` the trie to a byte stream.
321pub fn finalize(self: *Trie) !void {
322 if (!self.trie_dirty) return;
291323
292/// Parse the trie from a byte stream.
293pub fn fromByteStream(self: *Trie, stream: anytype) FromByteStreamError!void {
294 if (self.root == null) {
295 self.root = .{ .base = self };
296 }
297 return self.root.?.fromByteStream(stream);
298}
324 self.ordered_nodes.shrinkRetainingCapacity(0);
325 try self.ordered_nodes.ensureCapacity(self.allocator, self.node_count);
299326
300/// Write the trie to a buffer ULEB128 encoded.
301/// Caller owns the memory and needs to free it.
302pub fn writeULEB128Mem(self: *Trie) ![]u8 {
303 var ordered_nodes = try self.nodes();
304 defer self.allocator.free(ordered_nodes);
327 comptime const Fifo = std.fifo.LinearFifo(*Node, .{ .Static = std.math.maxInt(u8) });
328 var fifo = Fifo.init();
329 try fifo.writeItem(self.root.?);
330
331 while (fifo.readItem()) |next| {
332 for (next.edges.items) |*edge| {
333 try fifo.writeItem(edge.to);
334 }
335 self.ordered_nodes.appendAssumeCapacity(next);
336 }
305337
306 var offset: usize = 0;
307338 var more: bool = true;
308339 while (more) {
309 offset = 0;
340 self.size = 0;
310341 more = false;
311 for (ordered_nodes) |node| {
312 const res = node.updateOffset(offset);
313 offset += res.node_size;
342 for (self.ordered_nodes.items) |node| {
343 const res = node.finalize(self.size);
344 self.size += res.node_size;
314345 if (res.updated) more = true;
315346 }
316347 }
317348
318 var buffer = std.ArrayList(u8).init(self.allocator);
319 try buffer.ensureCapacity(offset);
320 for (ordered_nodes) |node| {
321 try node.writeULEB128Mem(&buffer);
322 }
323 return buffer.toOwnedSlice();
349 self.trie_dirty = false;
324350}
325351
326pub fn nodes(self: *Trie) ![]*Node {
327 var ordered_nodes = std.ArrayList(*Node).init(self.allocator);
328 try ordered_nodes.ensureCapacity(self.node_count);
352const ReadError = error{
353 OutOfMemory,
354 EndOfStream,
355 Overflow,
356};
329357
330 comptime const Fifo = std.fifo.LinearFifo(*Node, .{ .Static = std.math.maxInt(u8) });
331 var fifo = Fifo.init();
332 try fifo.writeItem(&self.root.?);
358/// Parse the trie from a byte stream.
359pub fn read(self: *Trie, reader: anytype) ReadError!void {
360 try self.createRoot();
361 return self.root.?.read(self.allocator, reader);
362}
333363
334 while (fifo.readItem()) |next| {
335 for (next.edges.items) |*edge| {
336 try fifo.writeItem(edge.to);
337 }
338 ordered_nodes.appendAssumeCapacity(next);
364/// Write the trie to a byte stream.
365/// Caller owns the memory and needs to free it.
366/// Panics if the trie was not finalized using `finalize`
367/// before calling this method.
368pub fn write(self: Trie, writer: anytype) !usize {
369 assert(!self.trie_dirty);
370 var counting_writer = std.io.countingWriter(writer);
371 for (self.ordered_nodes.items) |node| {
372 try node.write(counting_writer.writer());
339373 }
340
341 return ordered_nodes.toOwnedSlice();
374 return counting_writer.bytes_written;
342375}
343376
344377pub fn deinit(self: *Trie) void {
345 self.root.?.deinit();
378 if (self.root) |root| {
379 root.deinit(self.allocator);
380 self.allocator.destroy(root);
381 }
382 self.ordered_nodes.deinit(self.allocator);
383}
384
385fn createRoot(self: *Trie) !void {
386 if (self.root == null) {
387 const root = try self.allocator.create(Node);
388 root.* = .{ .base = self };
389 self.root = root;
390 self.node_count += 1;
391 }
346392}
347393
348394test "Trie node count" {
......@@ -350,7 +396,8 @@ test "Trie node count" {
350396 var trie = Trie.init(gpa);
351397 defer trie.deinit();
352398
353 testing.expectEqual(trie.node_count, 1);
399 testing.expectEqual(trie.node_count, 0);
400 testing.expect(trie.root == null);
354401
355402 try trie.put(.{
356403 .name = "_main",
......@@ -439,7 +486,7 @@ test "Trie basic" {
439486 }
440487}
441488
442test "Trie.writeULEB128Mem" {
489test "write Trie to a byte stream" {
443490 var gpa = testing.allocator;
444491 var trie = Trie.init(gpa);
445492 defer trie.deinit();
......@@ -455,112 +502,91 @@ test "Trie.writeULEB128Mem" {
455502 .export_flags = 0,
456503 });
457504
458 var buffer = try trie.writeULEB128Mem();
459 defer gpa.free(buffer);
505 try trie.finalize();
506 try trie.finalize(); // Finalizing mulitple times is a nop subsequently unless we add new nodes.
460507
461508 const exp_buffer = [_]u8{
462 0x0,
463 0x1,
464 0x5f,
465 0x0,
466 0x5,
467 0x0,
468 0x2,
469 0x5f,
470 0x6d,
471 0x68,
472 0x5f,
473 0x65,
474 0x78,
475 0x65,
476 0x63,
477 0x75,
478 0x74,
479 0x65,
480 0x5f,
481 0x68,
482 0x65,
483 0x61,
484 0x64,
485 0x65,
486 0x72,
487 0x0,
488 0x21,
489 0x6d,
490 0x61,
491 0x69,
492 0x6e,
493 0x0,
494 0x25,
495 0x2,
496 0x0,
497 0x0,
498 0x0,
499 0x3,
500 0x0,
501 0x80,
502 0x20,
503 0x0,
509 0x0, 0x1, // node root
510 0x5f, 0x0, 0x5, // edge '_'
511 0x0, 0x2, // non-terminal node
512 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header'
513 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header'
514 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main'
515 0x2, 0x0, 0x0, 0x0, // terminal node
516 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
504517 };
505518
506 testing.expect(buffer.len == exp_buffer.len);
507 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
519 var buffer = try gpa.alloc(u8, trie.size);
520 defer gpa.free(buffer);
521 var stream = std.io.fixedBufferStream(buffer);
522 {
523 const nwritten = try trie.write(stream.writer());
524 testing.expect(nwritten == trie.size);
525 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
526 }
527 {
528 // Writing finalized trie again should yield the same result.
529 try stream.seekTo(0);
530 const nwritten = try trie.write(stream.writer());
531 testing.expect(nwritten == trie.size);
532 testing.expect(mem.eql(u8, buffer, exp_buffer[0..]));
533 }
508534}
509535
510test "parse Trie from byte stream" {
511 var gpa = testing.allocator;
512
513 const in_buffer = [_]u8{
514 0x0,
515 0x1,
516 0x5f,
517 0x0,
518 0x5,
519 0x0,
520 0x2,
521 0x5f,
522 0x6d,
523 0x68,
524 0x5f,
525 0x65,
526 0x78,
527 0x65,
528 0x63,
529 0x75,
530 0x74,
531 0x65,
532 0x5f,
533 0x68,
534 0x65,
535 0x61,
536 0x64,
537 0x65,
538 0x72,
539 0x0,
540 0x21,
541 0x6d,
542 0x61,
543 0x69,
544 0x6e,
545 0x0,
546 0x25,
547 0x2,
548 0x0,
549 0x0,
550 0x0,
551 0x3,
552 0x0,
553 0x80,
554 0x20,
555 0x0,
556 };
557 var stream = std.io.fixedBufferStream(in_buffer[0..]);
558 var trie = Trie.init(gpa);
559 defer trie.deinit();
560 try trie.fromByteStream(&stream);
561
562 var out_buffer = try trie.writeULEB128Mem();
563 defer gpa.free(out_buffer);
564
565 testing.expect(mem.eql(u8, in_buffer[0..], out_buffer));
566}
536// test "parse Trie from byte stream" {
537// var gpa = testing.allocator;
538
539// const in_buffer = [_]u8{
540// 0x0,
541// 0x1,
542// 0x5f,
543// 0x0,
544// 0x5,
545// 0x0,
546// 0x2,
547// 0x5f,
548// 0x6d,
549// 0x68,
550// 0x5f,
551// 0x65,
552// 0x78,
553// 0x65,
554// 0x63,
555// 0x75,
556// 0x74,
557// 0x65,
558// 0x5f,
559// 0x68,
560// 0x65,
561// 0x61,
562// 0x64,
563// 0x65,
564// 0x72,
565// 0x0,
566// 0x21,
567// 0x6d,
568// 0x61,
569// 0x69,
570// 0x6e,
571// 0x0,
572// 0x25,
573// 0x2,
574// 0x0,
575// 0x0,
576// 0x0,
577// 0x3,
578// 0x0,
579// 0x80,
580// 0x20,
581// 0x0,
582// };
583// var stream = std.io.fixedBufferStream(in_buffer[0..]);
584// var trie = Trie.init(gpa);
585// defer trie.deinit();
586// try trie.fromByteStream(&stream);
587
588// var out_buffer = try trie.writeULEB128Mem();
589// defer gpa.free(out_buffer);
590
591// testing.expect(mem.eql(u8, in_buffer[0..], out_buffer));
592// }