authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-05 13:36:28-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
log424b1299a88b4ac3ae50128118ed510a79c3ab4b
tree46fa21887b39dc2e616940385d5fd4ea84aefc84
parentad5f74c0b1762e16d98115d7cc7c7f58c40aee7b

dwarf: add expression writer


1 files changed, 483 insertions(+), 20 deletions(-)

lib/std/dwarf/expressions.zig+483-20
......@@ -5,17 +5,7 @@ const leb = @import("../leb128.zig");
55const dwarf = @import("../dwarf.zig");
66const abi = dwarf.abi;
77const mem = std.mem;
8
9pub const StackMachineOptions = struct {
10 /// The address size of the target architecture
11 addr_size: u8 = @sizeOf(usize),
12
13 /// Endianess of the target architecture
14 endian: std.builtin.Endian = .Little,
15
16 /// Restrict the stack machine to a subset of opcodes used in call frame instructions
17 call_frame_mode: bool = false,
18};
8const assert = std.debug.assert;
199
2010/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
2111/// Callers should specify all the fields relevant to their context. If a field is required
......@@ -35,10 +25,21 @@ pub const ExpressionContext = struct {
3525 cfa: ?usize,
3626};
3727
28pub const ExpressionOptions = struct {
29 /// The address size of the target architecture
30 addr_size: u8 = @sizeOf(usize),
31
32 /// Endianess of the target architecture
33 endian: std.builtin.Endian = .Little,
34
35 /// Restrict the stack machine to a subset of opcodes used in call frame instructions
36 call_frame_mode: bool = false,
37};
38
3839/// A stack machine that can decode and run DWARF expressions.
3940/// Expressions can be decoded for non-native address size and endianness,
4041/// but can only be executed if the current target matches the configuration.
41pub fn StackMachine(comptime options: StackMachineOptions) type {
42pub fn StackMachine(comptime options: ExpressionOptions) type {
4243 const addr_type = switch (options.addr_size) {
4344 2 => u16,
4445 4 => u32,
......@@ -60,6 +61,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
6061 generic: addr_type,
6162 register: u8,
6263 type_size: u8,
64 branch_offset: i16,
6365 base_register: struct {
6466 base_register: u8,
6567 offset: i64,
......@@ -114,7 +116,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
114116 2 => mem.readIntSliceNative(u16, const_type.value_bytes),
115117 4 => mem.readIntSliceNative(u32, const_type.value_bytes),
116118 8 => mem.readIntSliceNative(u64, const_type.value_bytes),
117 else => return error.InvalidIntegralTypeLength,
119 else => error.InvalidIntegralTypeLength,
118120 };
119121 },
120122 };
......@@ -163,10 +165,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
163165 OP.call2,
164166 => generic(try reader.readInt(u16, options.endian)),
165167 OP.call4 => generic(try reader.readInt(u32, options.endian)),
166 OP.const2s,
168 OP.const2s => generic(try reader.readInt(i16, options.endian)),
167169 OP.bra,
168170 OP.skip,
169 => generic(try reader.readInt(i16, options.endian)),
171 => .{ .branch_offset = try reader.readInt(i16, options.endian) },
170172 OP.const4u => generic(try reader.readInt(u32, options.endian)),
171173 OP.const4s => generic(try reader.readInt(i32, options.endian)),
172174 OP.const8u => generic(try reader.readInt(u64, options.endian)),
......@@ -362,13 +364,21 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
362364 if (context.ucontext == null) return error.IncompleteExpressionContext;
363365
364366 const base_register = (try readOperand(stream, opcode)).?.base_register;
365 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(context.ucontext.?, base_register.base_register, context.reg_ctx)));
367 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
368 context.ucontext.?,
369 base_register.base_register,
370 context.reg_ctx,
371 )));
366372 value += base_register.offset;
367373 try self.stack.append(allocator, .{ .generic = @intCast(value) });
368374 },
369375 OP.regval_type => {
370376 const register_type = (try readOperand(stream, opcode)).?.register_type;
371 const value = mem.readIntSliceNative(usize, try abi.regBytes(context.ucontext.?, register_type.register, context.reg_ctx));
377 const value = mem.readIntSliceNative(usize, try abi.regBytes(
378 context.ucontext.?,
379 register_type.register,
380 context.reg_ctx,
381 ));
372382 try self.stack.append(allocator, .{
373383 .regval_type = .{
374384 .type_offset = register_type.type_offset,
......@@ -472,18 +482,242 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
472482 OP.abs => {
473483 if (self.stack.items.len == 0) return error.InvalidExpression;
474484 const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
475 self.stack.items[self.stack.items.len - 1] = .{ .generic = std.math.absCast(value) };
485 self.stack.items[self.stack.items.len - 1] = .{
486 .generic = std.math.absCast(value),
487 };
476488 },
477489 OP.@"and" => {
478490 if (self.stack.items.len < 2) return error.InvalidExpression;
479491 const a = try self.stack.pop().asIntegral();
480 self.stack.items[self.stack.items.len - 1] = .{ .generic = a & try self.stack.items[self.stack.items.len - 1].asIntegral() };
492 self.stack.items[self.stack.items.len - 1] = .{
493 .generic = a & try self.stack.items[self.stack.items.len - 1].asIntegral(),
494 };
495 },
496 OP.div => {
497 if (self.stack.items.len < 2) return error.InvalidExpression;
498 const a: isize = @bitCast(try self.stack.pop().asIntegral());
499 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
500 self.stack.items[self.stack.items.len - 1] = .{
501 .generic = @bitCast(try std.math.divTrunc(isize, b, a)),
502 };
503 },
504 OP.minus => {
505 if (self.stack.items.len < 2) return error.InvalidExpression;
506 const b = try self.stack.pop().asIntegral();
507 self.stack.items[self.stack.items.len - 1] = .{
508 .generic = try std.math.sub(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
509 };
510 },
511 OP.mod => {
512 if (self.stack.items.len < 2) return error.InvalidExpression;
513 const a: isize = @bitCast(try self.stack.pop().asIntegral());
514 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
515 self.stack.items[self.stack.items.len - 1] = .{
516 .generic = @bitCast(@mod(b, a)),
517 };
518 },
519 OP.mul => {
520 if (self.stack.items.len < 2) return error.InvalidExpression;
521 const a: isize = @bitCast(try self.stack.pop().asIntegral());
522 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
523 self.stack.items[self.stack.items.len - 1] = .{
524 .generic = @bitCast(@mulWithOverflow(a, b)[0]),
525 };
526 },
527 OP.neg => {
528 if (self.stack.items.len == 0) return error.InvalidExpression;
529 self.stack.items[self.stack.items.len - 1] = .{
530 .generic = @bitCast(
531 try std.math.negate(
532 @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
533 ),
534 ),
535 };
536 },
537 OP.not => {
538 if (self.stack.items.len == 0) return error.InvalidExpression;
539 self.stack.items[self.stack.items.len - 1] = .{
540 .generic = ~try self.stack.items[self.stack.items.len - 1].asIntegral(),
541 };
542 },
543 OP.@"or" => {
544 if (self.stack.items.len < 2) return error.InvalidExpression;
545 const a = try self.stack.pop().asIntegral();
546 self.stack.items[self.stack.items.len - 1] = .{
547 .generic = a | try self.stack.items[self.stack.items.len - 1].asIntegral(),
548 };
549 },
550 OP.plus => {
551 if (self.stack.items.len < 2) return error.InvalidExpression;
552 const b = try self.stack.pop().asIntegral();
553 self.stack.items[self.stack.items.len - 1] = .{
554 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
555 };
556 },
557 OP.plus_uconst => {
558 if (self.stack.items.len == 0) return error.InvalidExpression;
559 const constant = (try readOperand(stream, opcode)).?.generic;
560 self.stack.items[self.stack.items.len - 1] = .{
561 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
562 };
563 },
564 OP.shl => {
565 if (self.stack.items.len < 2) return error.InvalidExpression;
566 const a = try self.stack.pop().asIntegral();
567 const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
568 self.stack.items[self.stack.items.len - 1] = .{
569 .generic = std.math.shl(usize, b, a),
570 };
571 },
572 OP.shr => {
573 if (self.stack.items.len < 2) return error.InvalidExpression;
574 const a = try self.stack.pop().asIntegral();
575 const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
576 self.stack.items[self.stack.items.len - 1] = .{
577 .generic = std.math.shr(usize, b, a),
578 };
579 },
580 OP.shra => {
581 if (self.stack.items.len < 2) return error.InvalidExpression;
582 const a = try self.stack.pop().asIntegral();
583 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
584 self.stack.items[self.stack.items.len - 1] = .{
585 .generic = @bitCast(std.math.shr(isize, b, a)),
586 };
587 },
588 OP.xor => {
589 if (self.stack.items.len < 2) return error.InvalidExpression;
590 const a = try self.stack.pop().asIntegral();
591 self.stack.items[self.stack.items.len - 1] = .{
592 .generic = a ^ try self.stack.items[self.stack.items.len - 1].asIntegral(),
593 };
594 },
595
596 // 2.5.1.5: Control Flow Operations
597 OP.le,
598 OP.ge,
599 OP.eq,
600 OP.lt,
601 OP.gt,
602 OP.ne,
603 => {
604 if (self.stack.items.len < 2) return error.InvalidExpression;
605 const a = self.stack.pop();
606 const b = self.stack.items[self.stack.items.len - 1];
607
608 if (a == .generic and b == .generic) {
609 const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
610 const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
611 const result = @intFromBool(switch (opcode) {
612 OP.le => b_int < a_int,
613 OP.ge => b_int >= a_int,
614 OP.eq => b_int == a_int,
615 OP.lt => b_int < a_int,
616 OP.gt => b_int > a_int,
617 OP.ne => b_int != a_int,
618 else => unreachable,
619 });
620
621 self.stack.items[self.stack.items.len - 1] = .{ .generic = result };
622 } else {
623 // TODO: Load the types referenced by these values, find their comparison operator, and run it
624 return error.UnimplementedTypedComparison;
625 }
626 },
627 OP.skip, OP.bra => {
628 const branch_offset = (try readOperand(stream, opcode)).?.branch_offset;
629 const condition = if (opcode == OP.bra) blk: {
630 if (self.stack.items.len == 0) return error.InvalidExpression;
631 break :blk try self.stack.pop().asIntegral() != 0;
632 } else true;
633
634 if (condition) {
635 const new_pos = std.math.cast(
636 usize,
637 try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
638 ) orelse return error.InvalidExpression;
639
640 if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression;
641 stream.pos = new_pos;
642 }
643 },
644 OP.call2,
645 OP.call4,
646 OP.call_ref,
647 => {
648 const debug_info_offset = (try readOperand(stream, opcode)).?.generic;
649 _ = debug_info_offset;
650
651 // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it
652 // can be in a separate exe / shared object from the one containing this expression).
653 // Transfer control to the DW_AT_location attribute, with the current stack as input.
654
655 return error.UnimplementedExpressionCall;
656 },
657
658 // 2.5.1.6: Type Conversions
659 OP.convert => {
660 if (self.stack.items.len == 0) return error.InvalidExpression;
661 const type_offset = (try readOperand(stream, opcode)).?.generic;
662 _ = type_offset;
663
664 // TODO: Load the DW_TAG_base_type entry in context.compile_unit, find a conversion operator
665 // from the old type to the new type, run it.
666
667 return error.UnimplementedTypeConversion;
668 },
669 OP.reinterpret => {
670 if (self.stack.items.len == 0) return error.InvalidExpression;
671 const type_offset = (try readOperand(stream, opcode)).?.generic;
672
673 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
674 const value = self.stack.items[self.stack.items.len - 1];
675 if (type_offset == 0) {
676 self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
677 } else {
678 self.stack.items[self.stack.items.len - 1] = switch (value) {
679 .generic => |v| .{
680 .regval_type = .{
681 .type_offset = type_offset,
682 .type_size = @sizeOf(addr_type),
683 .value = v,
684 },
685 },
686 .regval_type => |r| .{
687 .regval_type = .{
688 .type_offset = type_offset,
689 .type_size = r.type_size,
690 .value = r.value,
691 },
692 },
693 .const_type => |c| .{
694 .const_type = .{
695 .type_offset = type_offset,
696 .value_bytes = c.value_bytes,
697 },
698 },
699 };
700 }
701 },
702
703 // 2.5.1.7: Special Operations
704 OP.nop => {},
705 OP.entry_value => {
706 const block = (try readOperand(stream, opcode)).?.block;
707 _ = block;
708
709 // TODO: If block is an expression, run it on a new stack. Push the resulting value onto this stack.
710 // TODO: If block is a register location, push the value that location had before running this program onto this stack.
711 // This implies capturing all register values before executing this block, in case this program modifies them.
712 // TODO: If the block contains, OP.push_object_address, treat it as OP.nop
713
714 return error.UnimplementedSubExpression;
481715 },
482716
483717 // These have already been handled by readOperand
484718 OP.lo_user...OP.hi_user => unreachable,
485719 else => {
486 //std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode});
720 //std.debug.print("Unknown DWARF expression opcode: {x}\n", .{opcode});
487721 return error.UnknownExpressionOpcode;
488722 },
489723 }
......@@ -492,3 +726,232 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
492726 }
493727 };
494728}
729
730pub fn Writer(options: ExpressionOptions) type {
731 const addr_type = switch (options.addr_size) {
732 2 => u16,
733 4 => u32,
734 8 => u64,
735 else => @compileError("Unsupported address size of " ++ options.addr_size),
736 };
737
738 return struct {
739 /// Zero-operand instructions
740 pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {
741 switch (opcode) {
742 OP.dup,
743 OP.drop,
744 OP.over,
745 OP.swap,
746 OP.rot,
747 OP.deref,
748 OP.xderef,
749 OP.push_object_address,
750 OP.form_tls_address,
751 OP.call_frame_cfa,
752 OP.abs,
753 OP.@"and",
754 OP.div,
755 OP.minus,
756 OP.mod,
757 OP.mul,
758 OP.neg,
759 OP.not,
760 OP.@"or",
761 OP.plus,
762 OP.shl,
763 OP.shr,
764 OP.shra,
765 OP.xor,
766 OP.le,
767 OP.ge,
768 OP.eq,
769 OP.lt,
770 OP.gt,
771 OP.ne,
772 OP.nop,
773 => try writer.writeByte(opcode),
774 else => @compileError("This opcode requires operands, use write<Opcode>() instead"),
775 }
776 }
777
778 // 2.5.1.1: Literal Encodings
779 pub fn writeLiteral(writer: anytype, literal: u8) !void {
780 switch (literal) {
781 0...31 => |n| try writer.writeByte(n + OP.lit0),
782 else => return error.InvalidLiteral,
783 }
784 }
785
786 pub fn writeConst(writer: anytype, comptime T: type, value: T) !void {
787 if (@typeInfo(T) != .Int) @compileError("Constants must be integers");
788
789 switch (T) {
790 u8, i8, u16, i16, u32, i32, u64, i64 => {
791 try writer.writeByte(switch (T) {
792 u8 => OP.const1u,
793 i8 => OP.const1s,
794 u16 => OP.const2u,
795 i16 => OP.const2s,
796 u32 => OP.const4u,
797 i32 => OP.const4s,
798 u64 => OP.const8u,
799 i64 => OP.const8s,
800 });
801
802 try writer.writeInt(T, value, options.endian);
803 },
804 else => switch (@typeInfo(T).Int.signedness) {
805 .unsigned => {
806 try writer.writeByte(OP.constu);
807 try leb.writeULEB128(writer, value);
808 },
809 .signed => {
810 try writer.writeByte(OP.consts);
811 try leb.writeILEB128(writer, value);
812 },
813 },
814 }
815 }
816
817 pub fn writeConstx(writer: anytype, debug_addr_offset: anytype) !void {
818 try writer.writeByte(OP.constx);
819 try leb.writeULEB128(writer, debug_addr_offset);
820 }
821
822 pub fn writeConstType(writer: anytype, die_offset: anytype, size: u8, value_bytes: []const u8) !void {
823 if (size != value_bytes.len) return error.InvalidValueSize;
824 try writer.writeByte(OP.const_type);
825 try leb.writeULEB128(writer, die_offset);
826 try writer.writeByte(size);
827 try writer.writeAll(value_bytes);
828 }
829
830 pub fn writeAddr(writer: anytype, value: addr_type) !void {
831 try writer.writeByte(OP.addr);
832 try writer.writeInt(addr_type, value, options.endian);
833 }
834
835 pub fn writeAddrx(writer: anytype, debug_addr_offset: anytype) !void {
836 try writer.writeByte(OP.addrx);
837 try leb.writeULEB128(writer, debug_addr_offset);
838 }
839
840 // 2.5.1.2: Register Values
841 pub fn writeFbreg(writer: anytype, offset: anytype) !void {
842 try writer.writeByte(OP.fbreg);
843 try leb.writeILEB128(writer, offset);
844 }
845
846 pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {
847 if (register > 31) return error.InvalidRegister;
848 try writer.writeByte(OP.reg0 + register);
849 try leb.writeILEB128(writer, offset);
850 }
851
852 pub fn writeBregx(writer: anytype, register: anytype, offset: anytype) !void {
853 try writer.writeByte(OP.bregx);
854 try leb.writeULEB128(writer, register);
855 try leb.writeILEB128(writer, offset);
856 }
857
858 pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {
859 try writer.writeByte(OP.bregx);
860 try leb.writeULEB128(writer, register);
861 try leb.writeULEB128(writer, offset);
862 }
863
864 // 2.5.1.3: Stack Operations
865 pub fn writePick(writer: anytype, index: u8) !void {
866 try writer.writeByte(OP.pick);
867 try writer.writeByte(index);
868 }
869
870 pub fn writeDerefSize(writer: anytype, size: u8) !void {
871 try writer.writeByte(OP.deref_size);
872 try writer.writeByte(size);
873 }
874
875 pub fn writeXDerefSize(writer: anytype, size: u8) !void {
876 try writer.writeByte(OP.xderef_size);
877 try writer.writeByte(size);
878 }
879
880 pub fn writeDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
881 try writer.writeByte(OP.deref_type);
882 try writer.writeByte(size);
883 try leb.writeULEB128(writer, die_offset);
884 }
885
886 pub fn writeXDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
887 try writer.writeByte(OP.xderef_type);
888 try writer.writeByte(size);
889 try leb.writeULEB128(writer, die_offset);
890 }
891
892 // 2.5.1.4: Arithmetic and Logical Operations
893
894 pub fn writePlusUconst(writer: anytype, uint_value: anytype) !void {
895 try writer.writeByte(OP.plus_uconst);
896 try leb.writeULEB128(writer, uint_value);
897 }
898
899 // 2.5.1.5: Control Flow Operations
900
901 pub fn writeSkip(writer: anytype, offset: i16) !void {
902 try writer.writeByte(OP.skip);
903 try writer.writeInt(i16, offset, options.endian);
904 }
905
906 pub fn writeBra(writer: anytype, offset: i16) !void {
907 try writer.writeByte(OP.bra);
908 try writer.writeInt(i16, offset, options.endian);
909 }
910
911 pub fn writeCall(writer: anytype, comptime T: type, offset: T) !void {
912 switch (T) {
913 u16 => try writer.writeByte(OP.call2),
914 u32 => try writer.writeByte(OP.call4),
915 else => @compileError("Call operand must be a 2 or 4 byte offset"),
916 }
917
918 try writer.writeInt(T, offset, options.endian);
919 }
920
921 pub fn writeCallRef(writer: anytype, debug_info_offset: addr_type) !void {
922 try writer.writeByte(OP.call_ref);
923 try writer.writeInt(addr_type, debug_info_offset, options.endian);
924 }
925
926 pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
927 try writer.writeByte(OP.convert);
928 try leb.writeULEB128(writer, die_offset);
929 }
930
931 pub fn writeReinterpret(writer: anytype, die_offset: anytype) !void {
932 try writer.writeByte(OP.reinterpret);
933 try leb.writeULEB128(writer, die_offset);
934 }
935
936 // 2.5.1.7: Special Operations
937
938 pub fn writeEntryValue(writer: anytype, expression: []const u8) !void {
939 try writer.writeByte(OP.entry_value);
940 try leb.writeULEB128(writer, expression.len);
941 try writer.writeAll(expression);
942 }
943
944 // 2.6: Location Descriptions
945
946 // TODO
947
948 };
949}
950
951test "DWARF expressions" {
952 const allocator = std.testing.allocator;
953
954 const options = ExpressionOptions{};
955 const stack_machine = StackMachine(options){};
956 defer stack_machine.deinit(allocator);
957}