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