| ... | @@ -34,6 +34,7 @@ const std = @import("std"); | ... | @@ -34,6 +34,7 @@ const std = @import("std"); |
| 34 | const mem = std.mem; | 34 | const mem = std.mem; |
| 35 | const leb = std.leb; | 35 | const leb = std.leb; |
| 36 | const log = std.log.scoped(.link); | 36 | const log = std.log.scoped(.link); |
| | 37 | const macho = std.macho; |
| 37 | const testing = std.testing; | 38 | const testing = std.testing; |
| 38 | const assert = std.debug.assert; | 39 | const assert = std.debug.assert; |
| 39 | const Allocator = mem.Allocator; | 40 | const Allocator = mem.Allocator; |
| ... | @@ -61,10 +62,14 @@ pub const Edge = struct { | ... | @@ -61,10 +62,14 @@ pub const Edge = struct { |
| 61 | | 62 | |
| 62 | pub const Node = struct { | 63 | pub const Node = struct { |
| 63 | base: *Trie, | 64 | base: *Trie, |
| 64 | /// Export flags associated with this exported symbol (if any). | 65 | /// Terminal info associated with this node. |
| 65 | export_flags: ?u64 = null, | 66 | /// If this node is not a terminal node, info is null. |
| 66 | /// VM address offset wrt to the section this symbol is defined against (if any). | 67 | terminal_info: ?struct { |
| 67 | vmaddr_offset: ?u64 = null, | 68 | /// Export flags associated with this exported symbol. |
| | 69 | export_flags: u64, |
| | 70 | /// VM address offset wrt to the section this symbol is defined against. |
| | 71 | vmaddr_offset: u64, |
| | 72 | } = null, |
| 68 | /// Offset of this node in the trie output byte stream. | 73 | /// Offset of this node in the trie output byte stream. |
| 69 | trie_offset: ?usize = null, | 74 | trie_offset: ?usize = null, |
| 70 | /// List of all edges originating from this node. | 75 | /// List of all edges originating from this node. |
| ... | @@ -125,9 +130,15 @@ pub const Node = struct { | ... | @@ -125,9 +130,15 @@ pub const Node = struct { |
| 125 | var reader = stream.reader(); | 130 | var reader = stream.reader(); |
| 126 | const node_size = try leb.readULEB128(u64, reader); | 131 | const node_size = try leb.readULEB128(u64, reader); |
| 127 | if (node_size > 0) { | 132 | if (node_size > 0) { |
| 128 | self.export_flags = try leb.readULEB128(u64, reader); | 133 | const export_flags = try leb.readULEB128(u64, reader); |
| 129 | // TODO Parse flags. | 134 | // TODO Parse special flags. |
| 130 | self.vmaddr_offset = try leb.readULEB128(u64, reader); | 135 | assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and |
| | 136 | export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); |
| | 137 | const vmaddr_offset = try leb.readULEB128(u64, reader); |
| | 138 | self.terminal_info = .{ |
| | 139 | .export_flags = export_flags, |
| | 140 | .vmaddr_offset = vmaddr_offset, |
| | 141 | }; |
| 131 | } | 142 | } |
| 132 | const nedges = try reader.readByte(); | 143 | const nedges = try reader.readByte(); |
| 133 | self.base.node_count += nedges; | 144 | self.base.node_count += nedges; |
| ... | @@ -162,13 +173,16 @@ pub const Node = struct { | ... | @@ -162,13 +173,16 @@ pub const Node = struct { |
| 162 | /// In case this is not upheld, this method will panic. | 173 | /// In case this is not upheld, this method will panic. |
| 163 | fn writeULEB128Mem(self: Node, buffer: *std.ArrayList(u8)) !void { | 174 | fn writeULEB128Mem(self: Node, buffer: *std.ArrayList(u8)) !void { |
| 164 | assert(self.trie_offset != null); // You need to call updateOffset first. | 175 | assert(self.trie_offset != null); // You need to call updateOffset first. |
| 165 | if (self.vmaddr_offset) |offset| { | 176 | if (self.terminal_info) |info| { |
| 166 | // Terminal node info: encode export flags and vmaddr offset of this symbol. | 177 | // Terminal node info: encode export flags and vmaddr offset of this symbol. |
| 167 | var info_buf_len: usize = 0; | 178 | var info_buf_len: usize = 0; |
| 168 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; | 179 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; |
| 169 | var info_stream = std.io.fixedBufferStream(&info_buf); | 180 | var info_stream = std.io.fixedBufferStream(&info_buf); |
| 170 | try leb.writeULEB128(info_stream.writer(), self.export_flags.?); | 181 | // TODO Implement for special flags. |
| 171 | try leb.writeULEB128(info_stream.writer(), offset); | 182 | assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and |
| | 183 | info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); |
| | 184 | try leb.writeULEB128(info_stream.writer(), info.export_flags); |
| | 185 | try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset); |
| 172 | | 186 | |
| 173 | // Encode the size of the terminal node info. | 187 | // Encode the size of the terminal node info. |
| 174 | var size_buf: [@sizeOf(u64)]u8 = undefined; | 188 | var size_buf: [@sizeOf(u64)]u8 = undefined; |
| ... | @@ -208,9 +222,9 @@ pub const Node = struct { | ... | @@ -208,9 +222,9 @@ pub const Node = struct { |
| 208 | /// Updates offset of this node in the output byte stream. | 222 | /// Updates offset of this node in the output byte stream. |
| 209 | fn updateOffset(self: *Node, offset: usize) UpdateResult { | 223 | fn updateOffset(self: *Node, offset: usize) UpdateResult { |
| 210 | var node_size: usize = 0; | 224 | var node_size: usize = 0; |
| 211 | if (self.vmaddr_offset) |vmaddr| { | 225 | if (self.terminal_info) |info| { |
| 212 | node_size += sizeULEB128Mem(self.export_flags.?); | 226 | node_size += sizeULEB128Mem(info.export_flags); |
| 213 | node_size += sizeULEB128Mem(vmaddr); | 227 | node_size += sizeULEB128Mem(info.vmaddr_offset); |
| 214 | node_size += sizeULEB128Mem(node_size); | 228 | node_size += sizeULEB128Mem(node_size); |
| 215 | } else { | 229 | } else { |
| 216 | node_size += 1; // 0x0 for non-terminal nodes | 230 | node_size += 1; // 0x0 for non-terminal nodes |
| ... | @@ -263,8 +277,10 @@ pub fn put(self: *Trie, symbol: Symbol) !void { | ... | @@ -263,8 +277,10 @@ pub fn put(self: *Trie, symbol: Symbol) !void { |
| 263 | self.root = .{ .base = self }; | 277 | self.root = .{ .base = self }; |
| 264 | } | 278 | } |
| 265 | const node = try self.root.?.put(symbol.name); | 279 | const node = try self.root.?.put(symbol.name); |
| 266 | node.vmaddr_offset = symbol.vmaddr_offset; | 280 | node.terminal_info = .{ |
| 267 | node.export_flags = symbol.export_flags; | 281 | .vmaddr_offset = symbol.vmaddr_offset, |
| | 282 | .export_flags = symbol.export_flags, |
| | 283 | }; |
| 268 | } | 284 | } |
| 269 | | 285 | |
| 270 | const FromByteStreamError = error{ | 286 | const FromByteStreamError = error{ |