| ... | @@ -64,6 +64,97 @@ const LoadCommand = union(enum) { | ... | @@ -64,6 +64,97 @@ const LoadCommand = union(enum) { |
| 64 | } | 64 | } |
| 65 | }; | 65 | }; |
| 66 | | 66 | |
| | 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. |
| | 72 | const 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 | |
| 67 | base: File, | 158 | base: File, |
| 68 | | 159 | |
| 69 | /// Table of all load commands | 160 | /// Table of all load commands |
| ... | @@ -1533,3 +1624,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | ... | @@ -1533,3 +1624,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 1533 | const T = @TypeOf(a, b); | 1624 | const T = @TypeOf(a, b); |
| 1534 | return std.math.mul(T, a, b) catch std.math.maxInt(T); | 1625 | return std.math.mul(T, a, b) catch std.math.maxInt(T); |
| 1535 | } | 1626 | } |
| | 1627 | |
| | 1628 | test "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 | } |