authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-05-07 20:00:54-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:13-04:00
log8b8d6271371c66eb703bdbe6f13e0426c4c2436f
tree8615798f2e568606f0c589adbd8b2250a2ad608c
parent6c1d1aa45c7aa955c63c63b2bfbeb895dd694076

- add call frame instruction parser

- add register printing

3 files changed, 298 insertions(+), 2 deletions(-)

lib/std/dwarf.zig+7-2
...@@ -14,6 +14,8 @@ pub const LANG = @import("dwarf/LANG.zig");...@@ -14,6 +14,8 @@ pub const LANG = @import("dwarf/LANG.zig");
14pub const FORM = @import("dwarf/FORM.zig");14pub const FORM = @import("dwarf/FORM.zig");
15pub const ATE = @import("dwarf/ATE.zig");15pub 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");
18pub const call_frame = @import("dwarf/call_frame.zig");
1719
18pub const LLE = struct {20pub const LLE = struct {
19 pub const end_of_list = 0x00;21 pub const end_of_list = 0x00;
...@@ -1490,7 +1492,8 @@ pub const DwarfInfo = struct {...@@ -1490,7 +1492,8 @@ pub const DwarfInfo = struct {
1490 length = try reader.readInt(u64, di.endian);1492 length = try reader.readInt(u64, di.endian);
1491 }1493 }
14921494
1493 const entry_bytes = eh_frame[stream.pos..][0..length];1495 const id_len = @as(u8, if (is_64) 8 else 4);
1496 const entry_bytes = eh_frame[stream.pos..][0..length - id_len];
1494 const id = try reader.readInt(u32, di.endian);1497 const id = try reader.readInt(u32, di.endian);
14951498
1496 // TODO: Get section_offset here (pass in from headers)1499 // TODO: Get section_offset here (pass in from headers)
...@@ -1658,7 +1661,8 @@ pub const CommonInformationEntry = struct {...@@ -1658,7 +1661,8 @@ pub const CommonInformationEntry = struct {
1658 return false;1661 return false;
1659 }1662 }
16601663
1661 // The returned struct references memory backed by cie_bytes1664 // This function expects to read the CIE starting with the version field.
1665 // The returned struct references memory backed by cie_bytes.
1662 pub fn parse(1666 pub fn parse(
1663 cie_bytes: []const u8,1667 cie_bytes: []const u8,
1664 section_base: u64,1668 section_base: u64,
...@@ -1775,6 +1779,7 @@ pub const FrameDescriptionEntry = struct {...@@ -1775,6 +1779,7 @@ pub const FrameDescriptionEntry = struct {
1775 aug_data: []const u8,1779 aug_data: []const u8,
1776 instructions: []const u8,1780 instructions: []const u8,
17771781
1782 // This function expects to read the FDE starting with the PC Begin field
1778 pub fn parse(1783 pub fn parse(
1779 fde_bytes: []const u8,1784 fde_bytes: []const u8,
1780 section_base: u64,1785 section_base: u64,
lib/std/dwarf/abi.zig created+54
...@@ -0,0 +1,54 @@
1const std = @import("../std.zig");
2
3fn writeUnknownReg(writer: anytype, reg_number: u8) !void {
4 try writer.print("reg{}", .{ reg_number });
5}
6
7pub fn writeRegisterName(writer: anytype, arch: ?std.Target.Cpu.Arch, reg_number: u8) !void {
8 if (arch) |a| {
9 switch (a) {
10 .x86_64 => {
11 switch (reg_number) {
12 0 => try writer.writeAll("RAX"),
13 1 => try writer.writeAll("RDX"),
14 2 => try writer.writeAll("RCX"),
15 3 => try writer.writeAll("RBX"),
16 4 => try writer.writeAll("RSI"),
17 5 => try writer.writeAll("RDI"),
18 6 => try writer.writeAll("RBP"),
19 7 => try writer.writeAll("RSP"),
20 8...15 => try writer.print("R{}", .{ reg_number }),
21 16 => try writer.writeAll("RIP"),
22 17...32 => try writer.print("XMM{}", .{ reg_number - 17 }),
23 33...40 => try writer.print("ST{}", .{ reg_number - 33 }),
24 41...48 => try writer.print("MM{}", .{ reg_number - 41 }),
25 49 => try writer.writeAll("RFLAGS"),
26 50 => try writer.writeAll("ES"),
27 51 => try writer.writeAll("CS"),
28 52 => try writer.writeAll("SS"),
29 53 => try writer.writeAll("DS"),
30 54 => try writer.writeAll("FS"),
31 55 => try writer.writeAll("GS"),
32 // 56-57 Reserved
33 58 => try writer.writeAll("FS.BASE"),
34 59 => try writer.writeAll("GS.BASE"),
35 // 60-61 Reserved
36 62 => try writer.writeAll("TR"),
37 63 => try writer.writeAll("LDTR"),
38 64 => try writer.writeAll("MXCSR"),
39 65 => try writer.writeAll("FCW"),
40 66 => try writer.writeAll("FSW"),
41 67...82 => try writer.print("XMM{}", .{ reg_number - 51 }),
42 // 83-117 Reserved
43 118...125 => try writer.print("K{}", .{ reg_number - 118 }),
44 // 126-129 Reserved
45 else => try writeUnknownReg(writer, reg_number),
46 }
47 },
48
49 // TODO: Add x86, aarch64
50
51 else => try writeUnknownReg(writer, reg_number),
52 }
53 } else try writeUnknownReg(writer, reg_number);
54}
lib/std/dwarf/call_frame.zig created+237
...@@ -0,0 +1,237 @@
1const std = @import("../std.zig");
2const debug = std.debug;
3const leb = @import("../leb128.zig");
4const abi = @import("abi.zig");
5const 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.
10const 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
45const 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
105fn 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
148pub 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};