authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-05 21:59:07+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-07 18:31:02+02:00
logf0a73df8e72e156bd95fa6c7f4de9512513d01b3
treeee6e643ee63396637268e1cd6d849eede1ac7034
parent07c33dfc951af5e7bf1c29c94142d49e3acf08b0

Add prototype for export trie generation in MachO linker

Signed-off-by: Jakub Konka <kubkon@jakubkonka.com>

1 files changed, 136 insertions(+), 0 deletions(-)

src/link/MachO.zig+136
......@@ -64,6 +64,97 @@ const LoadCommand = union(enum) {
6464 }
6565};
6666
67/// Represents export trie used in MachO executables and dynamic libraries.
68/// The purpose of an export trie is to encode as compactly as possible all
69/// export symbols for the loader `dyld`.
70/// The export trie encodes offset and other information using ULEB128
71/// encoding, and is part of the __LINKEDIT segment.
72const Trie = struct {
73 const Node = struct {
74 const Edge = struct {
75 from: *Node,
76 to: *Node,
77 label: []const u8,
78
79 pub fn deinit(self: *Edge, alloc: *Allocator) void {
80 self.to.deinit(alloc);
81 alloc.destroy(self.to);
82 self.from = undefined;
83 self.to = undefined;
84 }
85 };
86
87 edges: std.ArrayListUnmanaged(Edge) = .{},
88
89 pub fn deinit(self: *Node, alloc: *Allocator) void {
90 for (self.edges.items) |*edge| {
91 edge.deinit(alloc);
92 }
93 self.edges.deinit(alloc);
94 }
95
96 pub fn put(self: *Node, alloc: *Allocator, fromEdge: ?*Edge, prefix: usize, label: []const u8) !void {
97 // Traverse all edges.
98 for (self.edges.items) |*edge| {
99 const match = mem.indexOfDiff(u8, edge.label, label) orelse return; // Got a full match, don't do anything.
100 if (match - prefix > 0) {
101 // If we match, we advance further down the trie.
102 return edge.to.put(alloc, edge, match, label);
103 }
104 }
105
106 if (fromEdge) |from| {
107 if (mem.eql(u8, from.label, label[0..prefix])) {
108 if (prefix == label.len) return;
109 } else {
110 // Fixup nodes. We need to insert an intermediate node between
111 // from.to and self.
112 const mid = try alloc.create(Node);
113 mid.* = .{};
114 const to_label = from.label;
115 from.to = mid;
116 from.label = label[0..prefix];
117
118 try mid.edges.append(alloc, .{
119 .from = mid,
120 .to = self,
121 .label = to_label,
122 });
123
124 if (prefix == label.len) return; // We're done.
125
126 const new_node = try alloc.create(Node);
127 new_node.* = .{};
128 return mid.edges.append(alloc, .{
129 .from = mid,
130 .to = new_node,
131 .label = label,
132 });
133 }
134 }
135
136 // Add a new edge.
137 const node = try alloc.create(Node);
138 node.* = .{};
139 return self.edges.append(alloc, .{
140 .from = self,
141 .to = node,
142 .label = label,
143 });
144 }
145 };
146
147 root: Node,
148
149 pub fn put(self: *Trie, alloc: *Allocator, word: []const u8) !void {
150 return self.root.put(alloc, null, 0, word);
151 }
152
153 pub fn deinit(self: *Trie, alloc: *Allocator) void {
154 self.root.deinit(alloc);
155 }
156};
157
67158base: File,
68159
69160/// Table of all load commands
......@@ -1533,3 +1624,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
15331624 const T = @TypeOf(a, b);
15341625 return std.math.mul(T, a, b) catch std.math.maxInt(T);
15351626}
1627
1628test "Trie basic" {
1629 const testing = @import("std").testing;
1630 var gpa = testing.allocator;
1631
1632 var trie: Trie = .{
1633 .root = .{},
1634 };
1635 defer trie.deinit(gpa);
1636
1637 // root
1638 testing.expect(trie.root.edges.items.len == 0);
1639
1640 // root --- _st ---> node
1641 try trie.put(gpa, "_st");
1642 testing.expect(trie.root.edges.items.len == 1);
1643 testing.expect(mem.eql(u8, trie.root.edges.items[0].label, "_st"));
1644
1645 {
1646 // root --- _st ---> node --- _start ---> node
1647 try trie.put(gpa, "_start");
1648 testing.expect(trie.root.edges.items.len == 1);
1649
1650 const nextEdge = &trie.root.edges.items[0];
1651 testing.expect(mem.eql(u8, nextEdge.label, "_st"));
1652 testing.expect(nextEdge.to.edges.items.len == 1);
1653 testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "_start"));
1654 }
1655 {
1656 // root --- _ ---> node --- _st ---> node --- _start ---> node
1657 // |
1658 // | --- _main ---> node
1659 try trie.put(gpa, "_main");
1660 testing.expect(trie.root.edges.items.len == 1);
1661
1662 const nextEdge = &trie.root.edges.items[0];
1663 testing.expect(mem.eql(u8, nextEdge.label, "_"));
1664 testing.expect(nextEdge.to.edges.items.len == 2);
1665 testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "_st"));
1666 testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "_main"));
1667
1668 const nextNextEdge = &nextEdge.to.edges.items[0];
1669 testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "_start"));
1670 }
1671}