| ... | ... | @@ -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 | 158 | base: File, |
| 68 | 159 | |
| 69 | 160 | /// Table of all load commands |
| ... | ... | @@ -1533,3 +1624,48 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 1533 | 1624 | const T = @TypeOf(a, b); |
| 1534 | 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 | } |