| ... | ... | @@ -0,0 +1,237 @@ |
| 1 | const std = @import("../std.zig"); |
| 2 | const debug = std.debug; |
| 3 | const leb = @import("../leb128.zig"); |
| 4 | const abi = @import("abi.zig"); |
| 5 | const dwarf = @import("../dwarf.zig"); |
| 6 | |
| 7 | // These enum values correspond to the opcode encoding itself, with |
| 8 | // the exception of the opcodes that include data in the opcode itself. |
| 9 | // For those, the enum value is the opcode with the lower 6 bits (the data) masked to 0. |
| 10 | const Opcode = enum(u8) { |
| 11 | // These are placeholders that define the range of vendor-specific opcodes |
| 12 | const lo_user = 0x1c; |
| 13 | const hi_user = 0x3f; |
| 14 | |
| 15 | advance_loc = 0x1 << 6, |
| 16 | offset = 0x2 << 6, |
| 17 | restore = 0x3 << 6, |
| 18 | nop = 0x00, |
| 19 | set_loc = 0x01, |
| 20 | advance_loc1 = 0x02, |
| 21 | advance_loc2 = 0x03, |
| 22 | advance_loc4 = 0x04, |
| 23 | offset_extended = 0x05, |
| 24 | restore_extended = 0x06, |
| 25 | undefined = 0x07, |
| 26 | same_value = 0x08, |
| 27 | register = 0x09, |
| 28 | remember_state = 0x0a, |
| 29 | restore_state = 0x0b, |
| 30 | def_cfa = 0x0c, |
| 31 | def_cfa_register = 0x0d, |
| 32 | def_cfa_offset = 0x0e, |
| 33 | def_cfa_expression = 0x0f, |
| 34 | expression = 0x10, |
| 35 | offset_extended_sf = 0x11, |
| 36 | def_cfa_sf = 0x12, |
| 37 | def_cfa_offset_sf = 0x13, |
| 38 | val_offset = 0x14, |
| 39 | val_offset_sf = 0x15, |
| 40 | val_expression = 0x16, |
| 41 | |
| 42 | _, |
| 43 | }; |
| 44 | |
| 45 | const Operand = enum { |
| 46 | opcode_delta, |
| 47 | opcode_register, |
| 48 | uleb128_register, |
| 49 | uleb128_offset, |
| 50 | sleb128_offset, |
| 51 | address, |
| 52 | u8_delta, |
| 53 | u16_delta, |
| 54 | u32_delta, |
| 55 | block, |
| 56 | |
| 57 | fn Storage(comptime self: Operand) type { |
| 58 | return switch (self) { |
| 59 | .opcode_delta, .opcode_register => u6, |
| 60 | .uleb128_register => u8, |
| 61 | .uleb128_offset => u64, |
| 62 | .sleb128_offset => i64, |
| 63 | .address => u64, |
| 64 | .u8_delta => u8, |
| 65 | .u16_delta => u16, |
| 66 | .u32_delta => u32, |
| 67 | .block => []const u8, |
| 68 | }; |
| 69 | } |
| 70 | |
| 71 | fn read( |
| 72 | comptime self: Operand, |
| 73 | reader: anytype, |
| 74 | opcode_value: ?u6, |
| 75 | addr_size_bytes: u8, |
| 76 | endian: std.builtin.Endian, |
| 77 | ) !Storage(self) { |
| 78 | return switch (self) { |
| 79 | .opcode_delta, .opcode_register => opcode_value orelse return error.InvalidOperand, |
| 80 | .uleb128_register => try leb.readULEB128(u8, reader), |
| 81 | .uleb128_offset => try leb.readULEB128(u64, reader), |
| 82 | .sleb128_offset => try leb.readILEB128(i64, reader), |
| 83 | .address => switch (addr_size_bytes) { |
| 84 | 2 => try reader.readInt(u16, endian), |
| 85 | 4 => try reader.readInt(u32, endian), |
| 86 | 8 => try reader.readInt(u64, endian), |
| 87 | else => return error.InvalidAddrSize, |
| 88 | }, |
| 89 | .u8_delta => try reader.readByte(), |
| 90 | .u16_delta => try reader.readInt(u16, endian), |
| 91 | .u32_delta => try reader.readInt(u32, endian), |
| 92 | .block => { |
| 93 | const block_len = try leb.readULEB128(u64, reader); |
| 94 | |
| 95 | // TODO: This feels like a kludge, change to FixedBufferStream param? |
| 96 | const block = reader.context.buffer[reader.context.pos..][0..block_len]; |
| 97 | reader.context.pos += block_len; |
| 98 | |
| 99 | return block; |
| 100 | } |
| 101 | }; |
| 102 | } |
| 103 | }; |
| 104 | |
| 105 | fn InstructionType(comptime definition: anytype) type { |
| 106 | const definition_type = @typeInfo(@TypeOf(definition)); |
| 107 | debug.assert(definition_type == .Struct); |
| 108 | |
| 109 | const definition_len = definition_type.Struct.fields.len; |
| 110 | comptime var fields: [definition_len]std.builtin.Type.StructField = undefined; |
| 111 | inline for (definition_type.Struct.fields, &fields) |definition_field, *operands_field| { |
| 112 | const opcode = std.enums.nameCast(Operand, @field(definition, definition_field.name)); |
| 113 | const storage_type = opcode.Storage(); |
| 114 | operands_field.* = .{ |
| 115 | .name = definition_field.name, |
| 116 | .type = storage_type, |
| 117 | .default_value = null, |
| 118 | .is_comptime = false, |
| 119 | .alignment = @alignOf(storage_type), |
| 120 | }; |
| 121 | } |
| 122 | |
| 123 | const InstructionOperands = @Type(.{ |
| 124 | .Struct = .{ |
| 125 | .layout = .Auto, |
| 126 | .fields = &fields, |
| 127 | .decls = &.{}, |
| 128 | .is_tuple = false, |
| 129 | }, |
| 130 | }); |
| 131 | |
| 132 | return struct { |
| 133 | const Self = @This(); |
| 134 | operands: InstructionOperands, |
| 135 | |
| 136 | pub fn read(reader: anytype, opcode_value: ?u6, addr_size_bytes: u8, endian: std.builtin.Endian) !Self { |
| 137 | var operands: InstructionOperands = undefined; |
| 138 | inline for (definition_type.Struct.fields) |definition_field| { |
| 139 | const operand = comptime std.enums.nameCast(Operand, @field(definition, definition_field.name)); |
| 140 | @field(operands, definition_field.name) = try operand.read(reader, opcode_value, addr_size_bytes, endian); |
| 141 | } |
| 142 | |
| 143 | return .{ .operands = operands }; |
| 144 | } |
| 145 | }; |
| 146 | } |
| 147 | |
| 148 | pub const Instruction = union(Opcode) { |
| 149 | advance_loc: InstructionType(.{ .delta = .opcode_delta }), |
| 150 | offset: InstructionType(.{ .register = .opcode_register, .offset = .uleb128_offset }), |
| 151 | restore: InstructionType(.{ .register = .opcode_register }), |
| 152 | nop: InstructionType(.{}), |
| 153 | set_loc: InstructionType(.{ .address = .address }), |
| 154 | advance_loc1: InstructionType(.{ .delta = .u8_delta }), |
| 155 | advance_loc2: InstructionType(.{ .delta = .u16_delta }), |
| 156 | advance_loc4: InstructionType(.{ .delta = .u32_delta }), |
| 157 | offset_extended: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), |
| 158 | restore_extended: InstructionType(.{ .register = .uleb128_register }), |
| 159 | undefined: InstructionType(.{ .register = .uleb128_register }), |
| 160 | same_value: InstructionType(.{ .register = .uleb128_register }), |
| 161 | register: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), |
| 162 | remember_state: InstructionType(.{}), |
| 163 | restore_state: InstructionType(.{}), |
| 164 | def_cfa: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), |
| 165 | def_cfa_register: InstructionType(.{ .register = .uleb128_register }), |
| 166 | def_cfa_offset: InstructionType(.{ .offset = .uleb128_offset }), |
| 167 | def_cfa_expression: InstructionType(.{ .block = .block }), |
| 168 | expression: InstructionType(.{ .register = .uleb128_register, .block = .block }), |
| 169 | offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 170 | def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 171 | def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }), |
| 172 | val_offset: InstructionType(.{ .a = .uleb128_offset, .b = .uleb128_offset }), |
| 173 | val_offset_sf: InstructionType(.{ .a = .uleb128_offset, .b = .sleb128_offset }), |
| 174 | val_expression: InstructionType(.{ .a = .uleb128_offset, .block = .block }), |
| 175 | |
| 176 | pub fn read(reader: anytype, addr_size_bytes: u8, endian: std.builtin.Endian) !Instruction { |
| 177 | const opcode = try reader.readByte(); |
| 178 | const upper = opcode & 0b11000000; |
| 179 | return switch (upper) { |
| 180 | inline @enumToInt(Opcode.advance_loc), @enumToInt(Opcode.offset), @enumToInt(Opcode.restore) => |u| @unionInit( |
| 181 | Instruction, |
| 182 | @tagName(@intToEnum(Opcode, u)), |
| 183 | try std.meta.TagPayload(Instruction, @intToEnum(Opcode, u)).read(reader, @intCast(u6, opcode & 0b111111), addr_size_bytes, endian), |
| 184 | ), |
| 185 | 0 => blk: { |
| 186 | inline for (@typeInfo(Opcode).Enum.fields) |field| { |
| 187 | if (field.value == opcode) { |
| 188 | break :blk @unionInit( |
| 189 | Instruction, |
| 190 | @tagName(@intToEnum(Opcode, field.value)), |
| 191 | try std.meta.TagPayload(Instruction, @intToEnum(Opcode, field.value)).read(reader, null, addr_size_bytes, endian), |
| 192 | ); |
| 193 | } |
| 194 | } |
| 195 | break :blk error.UnknownOpcode; |
| 196 | }, |
| 197 | else => error.UnknownOpcode, |
| 198 | }; |
| 199 | } |
| 200 | |
| 201 | pub fn writeOperands(self: Instruction, writer: anytype, cie: dwarf.CommonInformationEntry, arch: ?std.Target.Cpu.Arch) !void { |
| 202 | switch (self) { |
| 203 | inline .advance_loc, .advance_loc1, .advance_loc2, .advance_loc4 => |i| try writer.print("{}", .{ i.operands.delta * cie.code_alignment_factor }), |
| 204 | .offset => |i| { |
| 205 | try abi.writeRegisterName(writer, arch, i.operands.register); |
| 206 | try writer.print(" {}", .{ @intCast(i64, i.operands.offset) * cie.data_alignment_factor }); |
| 207 | }, |
| 208 | .restore => {}, |
| 209 | .nop => {}, |
| 210 | .set_loc => {}, |
| 211 | .offset_extended => {}, |
| 212 | .restore_extended => {}, |
| 213 | .undefined => {}, |
| 214 | .same_value => {}, |
| 215 | .register => {}, |
| 216 | .remember_state => {}, |
| 217 | .restore_state => {}, |
| 218 | .def_cfa => |i| { |
| 219 | try abi.writeRegisterName(writer, arch, i.operands.register); |
| 220 | try writer.print(" +{}", .{ i.operands.offset }); |
| 221 | }, |
| 222 | .def_cfa_register => {}, |
| 223 | .def_cfa_offset => {}, |
| 224 | .def_cfa_expression => |i| { |
| 225 | try writer.print("TODO parse expressions: {x}", .{ std.fmt.fmtSliceHexLower(i.operands.block) }); |
| 226 | }, |
| 227 | .expression => {}, |
| 228 | .offset_extended_sf => {}, |
| 229 | .def_cfa_sf => {}, |
| 230 | .def_cfa_offset_sf => {}, |
| 231 | .val_offset => {}, |
| 232 | .val_offset_sf => {}, |
| 233 | .val_expression => {}, |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | }; |