authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-05-09 22:33:31-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:13-04:00
log7b4611cfb3eb0c1c3f4fcae8c0a4beaf28473803
tree7ce3d44583cf835c121c218a0d5c19dec86dc87d
parent338df862d147986c797902706967b3dae7c95cd6

- move writing code to zig-dwarfdump

- implement restore_state, restore_extended, offset_extended

2 files changed, 104 insertions(+), 166 deletions(-)

lib/std/dwarf.zig+1
...@@ -16,6 +16,7 @@ pub const ATE = @import("dwarf/ATE.zig");...@@ -16,6 +16,7 @@ pub const ATE = @import("dwarf/ATE.zig");
16pub const EH = @import("dwarf/EH.zig");16pub const EH = @import("dwarf/EH.zig");
17pub const abi = @import("dwarf/abi.zig");17pub const abi = @import("dwarf/abi.zig");
18pub const call_frame = @import("dwarf/call_frame.zig");18pub const call_frame = @import("dwarf/call_frame.zig");
19pub const expressions = @import("dwarf/expressions.zig");
1920
20pub const LLE = struct {21pub const LLE = struct {
21 pub const end_of_list = 0x00;22 pub const end_of_list = 0x00;
lib/std/dwarf/call_frame.zig+103-166
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const builtin = @import("builtin");
1const std = @import("../std.zig");2const std = @import("../std.zig");
2const debug = std.debug;3const debug = std.debug;
3const leb = @import("../leb128.zig");4const leb = @import("../leb128.zig");
4const abi = @import("abi.zig");5const abi = @import("abi.zig");
5const dwarf = @import("../dwarf.zig");6const dwarf = @import("../dwarf.zig");
6const expressions = @import("expressions.zig");7const expressions = @import("expressions.zig");
8const assert = std.debug.assert;
79
8const Opcode = enum(u8) {10const Opcode = enum(u8) {
9 advance_loc = 0x1 << 6,11 advance_loc = 0x1 << 6,
...@@ -36,7 +38,7 @@ const Opcode = enum(u8) {...@@ -36,7 +38,7 @@ const Opcode = enum(u8) {
3638
37 // These opcodes encode an operand in the lower 6 bits of the opcode itself39 // These opcodes encode an operand in the lower 6 bits of the opcode itself
38 pub const lo_inline = Opcode.advance_loc;40 pub const lo_inline = Opcode.advance_loc;
39 pub const hi_inline = Opcode.restore;41 pub const hi_inline = @enumToInt(Opcode.restore) | 0b111111;
4042
41 // These opcodes are trailed by zero or more operands43 // These opcodes are trailed by zero or more operands
42 pub const lo_reserved = Opcode.nop;44 pub const lo_reserved = Opcode.nop;
...@@ -61,7 +63,7 @@ const Operand = enum {...@@ -61,7 +63,7 @@ const Operand = enum {
6163
62 fn Storage(comptime self: Operand) type {64 fn Storage(comptime self: Operand) type {
63 return switch (self) {65 return switch (self) {
64 .opcode_delta, .opcode_register => u6,66 .opcode_delta, .opcode_register => u8,
65 .uleb128_register => u8,67 .uleb128_register => u8,
66 .uleb128_offset => u64,68 .uleb128_offset => u64,
67 .sleb128_offset => i64,69 .sleb128_offset => i64,
...@@ -110,7 +112,7 @@ const Operand = enum {...@@ -110,7 +112,7 @@ const Operand = enum {
110112
111fn InstructionType(comptime definition: anytype) type {113fn InstructionType(comptime definition: anytype) type {
112 const definition_type = @typeInfo(@TypeOf(definition));114 const definition_type = @typeInfo(@TypeOf(definition));
113 debug.assert(definition_type == .Struct);115 assert(definition_type == .Struct);
114116
115 const definition_len = definition_type.Struct.fields.len;117 const definition_len = definition_type.Struct.fields.len;
116 comptime var fields: [definition_len]std.builtin.Type.StructField = undefined;118 comptime var fields: [definition_len]std.builtin.Type.StructField = undefined;
...@@ -159,14 +161,14 @@ fn InstructionType(comptime definition: anytype) type {...@@ -159,14 +161,14 @@ fn InstructionType(comptime definition: anytype) type {
159pub const Instruction = union(Opcode) {161pub const Instruction = union(Opcode) {
160 advance_loc: InstructionType(.{ .delta = .opcode_delta }),162 advance_loc: InstructionType(.{ .delta = .opcode_delta }),
161 offset: InstructionType(.{ .register = .opcode_register, .offset = .uleb128_offset }),163 offset: InstructionType(.{ .register = .opcode_register, .offset = .uleb128_offset }),
164 offset_extended: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
162 restore: InstructionType(.{ .register = .opcode_register }),165 restore: InstructionType(.{ .register = .opcode_register }),
166 restore_extended: InstructionType(.{ .register = .uleb128_register }),
163 nop: InstructionType(.{}),167 nop: InstructionType(.{}),
164 set_loc: InstructionType(.{ .address = .address }),168 set_loc: InstructionType(.{ .address = .address }),
165 advance_loc1: InstructionType(.{ .delta = .u8_delta }),169 advance_loc1: InstructionType(.{ .delta = .u8_delta }),
166 advance_loc2: InstructionType(.{ .delta = .u16_delta }),170 advance_loc2: InstructionType(.{ .delta = .u16_delta }),
167 advance_loc4: InstructionType(.{ .delta = .u32_delta }),171 advance_loc4: InstructionType(.{ .delta = .u32_delta }),
168 offset_extended: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
169 restore_extended: InstructionType(.{ .register = .uleb128_register }),
170 undefined: InstructionType(.{ .register = .uleb128_register }),172 undefined: InstructionType(.{ .register = .uleb128_register }),
171 same_value: InstructionType(.{ .register = .uleb128_register }),173 same_value: InstructionType(.{ .register = .uleb128_register }),
172 register: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),174 register: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
...@@ -192,7 +194,7 @@ pub const Instruction = union(Opcode) {...@@ -192,7 +194,7 @@ pub const Instruction = union(Opcode) {
192 @setEvalBranchQuota(1800);194 @setEvalBranchQuota(1800);
193195
194 return switch (try stream.reader().readByte()) {196 return switch (try stream.reader().readByte()) {
195 inline @enumToInt(Opcode.lo_inline)...@enumToInt(Opcode.hi_inline) => |opcode| blk: {197 inline @enumToInt(Opcode.lo_inline)...Opcode.hi_inline => |opcode| blk: {
196 const e = @intToEnum(Opcode, opcode & 0b11000000);198 const e = @intToEnum(Opcode, opcode & 0b11000000);
197 const payload_type = std.meta.TagPayload(Instruction, e);199 const payload_type = std.meta.TagPayload(Instruction, e);
198 const value = try payload_type.read(stream, @intCast(u6, opcode & 0b111111), addr_size_bytes, endian);200 const value = try payload_type.read(stream, @intCast(u6, opcode & 0b111111), addr_size_bytes, endian);
...@@ -205,112 +207,15 @@ pub const Instruction = union(Opcode) {...@@ -205,112 +207,15 @@ pub const Instruction = union(Opcode) {
205 break :blk @unionInit(Instruction, @tagName(e), value);207 break :blk @unionInit(Instruction, @tagName(e), value);
206 },208 },
207 Opcode.lo_user...Opcode.hi_user => error.UnimplementedUserOpcode,209 Opcode.lo_user...Opcode.hi_user => error.UnimplementedUserOpcode,
208 else => error.InvalidOpcode,210 else => |opcode| blk: {
209 };211 std.debug.print("Opcode {x}\n", .{opcode});
210 }
211212
212 pub fn writeOperands(213 break :blk error.InvalidOpcode;
213 self: Instruction,
214 writer: anytype,
215 cie: dwarf.CommonInformationEntry,
216 arch: ?std.Target.Cpu.Arch,
217 addr_size_bytes: u8,
218 endian: std.builtin.Endian,
219 ) !void {
220 switch (self) {
221 inline .advance_loc, .advance_loc1, .advance_loc2, .advance_loc4 => |i| try writer.print("{}", .{i.operands.delta * cie.code_alignment_factor}),
222 .offset => |i| {
223 try abi.writeRegisterName(writer, arch, i.operands.register);
224 try writer.print(" {}", .{@intCast(i64, i.operands.offset) * cie.data_alignment_factor});
225 },214 },
226 .restore => {},215 };
227 .nop => {},
228 .set_loc => {},
229 .offset_extended => {},
230 .restore_extended => {},
231 .undefined => {},
232 .same_value => {},
233 .register => {},
234 .remember_state => {},
235 .restore_state => {},
236 .def_cfa => |i| try writer.print("{} {d:<1}", .{ abi.fmtRegister(i.operands.register, arch), @intCast(i64, i.operands.offset)}),
237 .def_cfa_register => |i| try abi.writeRegisterName(writer, arch, i.operands.register),
238 .def_cfa_offset => |i| try writer.print("{d:<1}", .{@intCast(i64, i.operands.offset)}),
239 .def_cfa_expression => |i| try writeExpression(writer, i.operands.block, arch, addr_size_bytes, endian),
240 .expression => {},
241 .offset_extended_sf => {},
242 .def_cfa_sf => {},
243 .def_cfa_offset_sf => {},
244 .val_offset => {},
245 .val_offset_sf => {},
246 .val_expression => {},
247 }
248 }216 }
249};217};
250218
251fn writeExpression(
252 writer: anytype,
253 block: []const u8,
254 arch: ?std.Target.Cpu.Arch,
255 addr_size_bytes: u8,
256 endian: std.builtin.Endian,
257) !void {
258 var stream = std.io.fixedBufferStream(block);
259
260 // Generate a lookup table from opcode value to name
261 const opcode_lut_len = 256;
262 const opcode_lut: [opcode_lut_len]?[]const u8 = comptime blk: {
263 var lut: [opcode_lut_len]?[]const u8 = [_]?[]const u8{null} ** opcode_lut_len;
264 for (@typeInfo(dwarf.OP).Struct.decls) |decl| {
265 lut[@as(u8, @field(dwarf.OP, decl.name))] = decl.name;
266 }
267
268 break :blk lut;
269 };
270
271 switch (endian) {
272 inline .Little, .Big => |e| {
273 switch (addr_size_bytes) {
274 inline 2, 4, 8 => |size| {
275 const StackMachine = expressions.StackMachine(.{
276 .addr_size = size,
277 .endian = e,
278 .call_frame_mode = true,
279 });
280
281 const reader = stream.reader();
282 while (stream.pos < stream.buffer.len) {
283 if (stream.pos > 0) try writer.writeAll(", ");
284
285 const opcode = try reader.readByte();
286 if (opcode_lut[opcode]) |opcode_name| {
287 try writer.print("DW_OP_{s}", .{opcode_name});
288 } else {
289 // TODO: See how llvm-dwarfdump prints these?
290 if (opcode >= dwarf.OP.lo_user and opcode <= dwarf.OP.lo_user) {
291 try writer.print("<unknown vendor opcode: 0x{x}>", .{opcode});
292 } else {
293 try writer.print("<invalid opcode: 0x{x}>", .{opcode});
294 }
295 }
296
297 if (try StackMachine.readOperand(&stream, opcode)) |value| {
298 switch (value) {
299 //.generic => |v| try writer.print("{d}", .{v}),
300 .generic => {}, // Constant values are implied by the opcode name
301 .register => |v| try writer.print(" {}", .{ abi.fmtRegister(v, arch) }),
302 .base_register => |v| try writer.print(" {}{d:<1}", .{ abi.fmtRegister(v.base_register, arch), v.offset }),
303 else => try writer.print(" TODO({s})", .{@tagName(value)}),
304 }
305 }
306 }
307 },
308 else => return error.InvalidAddrSize,
309 }
310 },
311 }
312}
313
314/// See section 6.4.1 of the DWARF5 specification219/// See section 6.4.1 of the DWARF5 specification
315pub const VirtualMachine = struct {220pub const VirtualMachine = struct {
316 const RegisterRule = union(enum) {221 const RegisterRule = union(enum) {
...@@ -324,52 +229,6 @@ pub const VirtualMachine = struct {...@@ -324,52 +229,6 @@ pub const VirtualMachine = struct {
324 architectural: void,229 architectural: void,
325 };230 };
326231
327 pub const Column = struct {
328 register: u8 = undefined,
329 rule: RegisterRule = .{ .undefined = {} },
330
331 pub fn writeRule(
332 self: Column,
333 writer: anytype,
334 is_cfa: bool,
335 arch: ?std.Target.Cpu.Arch,
336 addr_size_bytes: u8,
337 endian: std.builtin.Endian,
338 ) !void {
339 if (is_cfa) {
340 try writer.writeAll("CFA");
341 } else {
342 try abi.writeRegisterName(writer, arch, self.register);
343 }
344
345 try writer.writeByte('=');
346 switch (self.rule) {
347 .undefined => {},
348 .same_value => try writer.writeAll("S"),
349 .offset => |offset| {
350 if (is_cfa) {
351 try abi.writeRegisterName(writer, arch, self.register);
352 try writer.print("{d:<1}", .{offset});
353 } else {
354 try writer.print("[CFA{d:<1}]", .{offset});
355 }
356 },
357 .val_offset => |offset| {
358 if (is_cfa) {
359 try abi.writeRegisterName(writer, arch, self.register);
360 try writer.print("{d:<1}", .{offset});
361 } else {
362 try writer.print("CFA{d:<1}", .{offset});
363 }
364 },
365 .register => |register| try abi.writeRegisterName(writer, arch, register),
366 .expression => |expression| try writeExpression(writer, expression, arch, addr_size_bytes, endian),
367 .val_expression => try writer.writeAll("TODO(val_expression)"),
368 .architectural => try writer.writeAll("TODO(architectural)"),
369 }
370 }
371 };
372
373 /// Each row contains unwinding rules for a set of registers at a specific location in the232 /// Each row contains unwinding rules for a set of registers at a specific location in the
374 pub const Row = struct {233 pub const Row = struct {
375 /// Offset from pc_begin234 /// Offset from pc_begin
...@@ -381,6 +240,12 @@ pub const VirtualMachine = struct {...@@ -381,6 +240,12 @@ pub const VirtualMachine = struct {
381 columns: ColumnRange = .{},240 columns: ColumnRange = .{},
382 };241 };
383242
243 pub const Column = struct {
244 /// Register can only null in the case of the CFA column
245 register: ?u8 = null,
246 rule: RegisterRule = .{ .undefined = {} },
247 };
248
384 const ColumnRange = struct {249 const ColumnRange = struct {
385 /// Index into `columns` of the first column in this row.250 /// Index into `columns` of the first column in this row.
386 start: usize = undefined,251 start: usize = undefined,
...@@ -391,10 +256,14 @@ pub const VirtualMachine = struct {...@@ -391,10 +256,14 @@ pub const VirtualMachine = struct {
391 stack: std.ArrayListUnmanaged(ColumnRange) = .{},256 stack: std.ArrayListUnmanaged(ColumnRange) = .{},
392 current_row: Row = .{},257 current_row: Row = .{},
393258
259 /// The result of executing the CIE's initial_instructions
260 cie_row: ?Row = null,
261
394 pub fn reset(self: *VirtualMachine) void {262 pub fn reset(self: *VirtualMachine) void {
395 self.stack.clearRetainingCapacity();263 self.stack.clearRetainingCapacity();
396 self.columns.clearRetainingCapacity();264 self.columns.clearRetainingCapacity();
397 self.current_row = .{};265 self.current_row = .{};
266 self.cie_row = null;
398 }267 }
399268
400 pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {269 pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
...@@ -403,12 +272,14 @@ pub const VirtualMachine = struct {...@@ -403,12 +272,14 @@ pub const VirtualMachine = struct {
403 self.* = undefined;272 self.* = undefined;
404 }273 }
405274
406 pub fn getColumns(self: VirtualMachine, row: Row) []Column {275 /// Return a slice backed by the row's non-CFA columns
276 pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
407 return self.columns.items[row.columns.start..][0..row.columns.len];277 return self.columns.items[row.columns.start..][0..row.columns.len];
408 }278 }
409279
280 /// Either retrieves or adds a column for `register` (non-CFA) in the current row
410 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {281 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
411 for (self.getColumns(self.current_row)) |*c| {282 for (self.rowColumns(self.current_row)) |*c| {
412 if (c.register == register) return c;283 if (c.register == register) return c;
413 }284 }
414285
...@@ -425,20 +296,82 @@ pub const VirtualMachine = struct {...@@ -425,20 +296,82 @@ pub const VirtualMachine = struct {
425 return column;296 return column;
426 }297 }
427298
428 pub fn step(self: *VirtualMachine, allocator: std.mem.Allocator, cie: dwarf.CommonInformationEntry, instruction: Instruction) !void {299 /// Runs the CIE instructions, then the FDE instructions. Execution halts
300 /// once the row that corresponds to `pc` is known, and it is returned.
301 pub fn unwindTo(
302 self: *VirtualMachine,
303 allocator: std.mem.Allocator,
304 pc: u64,
305 cie: dwarf.CommonInformationEntry,
306 fde: dwarf.FrameDescriptionEntry,
307 addr_size_bytes: u8,
308 endian: std.builtin.Endian,
309 ) !Row {
310 assert(self.cie_row == null);
311 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
312
313 var prev_row: Row = self.current_row;
314 const streams = .{
315 std.io.fixedBufferStream(cie.initial_instructions),
316 std.io.fixedBufferStream(fde.instructions),
317 };
318
319 outer: for (streams, 0..) |*stream, i| {
320 while (stream.pos < stream.buffer.len) {
321 const instruction = try dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
322 prev_row = try self.step(allocator, cie, i == 0, instruction);
323 if (pc < fde.pc_begin + self.current_row.offset) {
324 break :outer;
325 }
326 }
327 }
328
329 return prev_row;
330 }
331
332 pub fn unwindToNative(
333 self: *VirtualMachine,
334 allocator: std.mem.Allocator,
335 pc: u64,
336 cie: dwarf.CommonInformationEntry,
337 fde: dwarf.FrameDescriptionEntry,
338 ) void {
339 self.stepTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());
340 }
341
342 /// Executes a single instruction.
343 /// If this instruction is from the CIE, `is_initial` should be set.
344 /// Returns the value of `current_row` before executing this instruction
345 pub fn step(
346 self: *VirtualMachine,
347 allocator: std.mem.Allocator,
348 cie: dwarf.CommonInformationEntry,
349 is_initial: bool,
350 instruction: Instruction,
351 ) !Row {
352 // CIE instructions must be run before FDE instructions
353 assert(!is_initial or self.cie_row == null);
354 if (!is_initial and self.cie_row == null) self.cie_row = self.current_row;
355
356 const prev_row = self.current_row;
429 switch (instruction) {357 switch (instruction) {
430 inline .advance_loc, .advance_loc1, .advance_loc2, .advance_loc4 => |i| {358 inline .advance_loc, .advance_loc1, .advance_loc2, .advance_loc4 => |i| {
431 self.current_row.offset += i.operands.delta * cie.code_alignment_factor;359 self.current_row.offset += i.operands.delta * cie.code_alignment_factor;
432 },360 },
433 .offset => |i| {361 inline .offset, .offset_extended => |i| {
434 const column = try self.getOrAddColumn(allocator, i.operands.register);362 const column = try self.getOrAddColumn(allocator, i.operands.register);
435 column.rule = .{ .offset = @intCast(i64, i.operands.offset) * cie.data_alignment_factor };363 column.rule = .{ .offset = @intCast(i64, i.operands.offset) * cie.data_alignment_factor };
436 },364 },
437 .restore => {},365 inline .restore, .restore_extended => |i| {
366 if (self.cie_row) |cie_row| {
367 const column = try self.getOrAddColumn(allocator, i.operands.register);
368 column.rule = for (self.rowColumns(cie_row)) |cie_column| {
369 if (cie_column.register == i.operands.register) break cie_column.rule;
370 } else .{ .undefined = {} };
371 } else return error.InvalidOperation;
372 },
438 .nop => {},373 .nop => {},
439 .set_loc => {},374 .set_loc => {},
440 .offset_extended => {},
441 .restore_extended => {},
442 .undefined => {},375 .undefined => {},
443 .same_value => {},376 .same_value => {},
444 .register => {},377 .register => {},
...@@ -448,17 +381,19 @@ pub const VirtualMachine = struct {...@@ -448,17 +381,19 @@ pub const VirtualMachine = struct {
448381
449 const new_start = self.columns.items.len;382 const new_start = self.columns.items.len;
450 if (self.current_row.columns.len > 0) {383 if (self.current_row.columns.len > 0) {
451 // Since we're copying from the same backing array, ensure it won't be reallocated
452 try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);384 try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);
453 self.columns.appendSliceAssumeCapacity(self.getColumns(self.current_row));385 self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
454 self.current_row.columns.start = new_start;386 self.current_row.columns.start = new_start;
455 }387 }
456 },388 },
457 .restore_state => {389 .restore_state => {
458 // TODO: Is it possible to remove the duplicate from above? Other instructions may have added columns since then though390 const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
459 const columns = self.stack.popOrNull() orelse return error.InvalidOperation;391 self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
460 self.current_row.columns.len = columns.len;392 try self.columns.ensureUnusedCapacity(allocator, restored_columns.len);
461 self.current_row.columns.start = columns.start;393
394 self.current_row.columns.start = self.columns.items.len;
395 self.current_row.columns.len = restored_columns.len;
396 self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
462 },397 },
463 .def_cfa => |i| {398 .def_cfa => |i| {
464 self.current_row.cfa = .{399 self.current_row.cfa = .{
...@@ -488,5 +423,7 @@ pub const VirtualMachine = struct {...@@ -488,5 +423,7 @@ pub const VirtualMachine = struct {
488 .val_offset_sf => {},423 .val_offset_sf => {},
489 .val_expression => {},424 .val_expression => {},
490 }425 }
426
427 return prev_row;
491 }428 }
492};429};