| ... | @@ -3,6 +3,7 @@ const io = @import("io.zig"); | ... | @@ -3,6 +3,7 @@ const io = @import("io.zig"); |
| 3 | const os = @import("os.zig"); | 3 | const os = @import("os.zig"); |
| 4 | const elf = @import("elf.zig"); | 4 | const elf = @import("elf.zig"); |
| 5 | const DW = @import("dwarf.zig"); | 5 | const DW = @import("dwarf.zig"); |
| | 6 | const List = @import("list.zig").List; |
| 6 | | 7 | |
| 7 | pub error MissingDebugInfo; | 8 | pub error MissingDebugInfo; |
| 8 | pub error InvalidDebugInfo; | 9 | pub error InvalidDebugInfo; |
| ... | @@ -29,6 +30,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | ... | @@ -29,6 +30,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 29 | | 30 | |
| 30 | st.aranges = %return st.elf.findSection(".debug_aranges"); | 31 | st.aranges = %return st.elf.findSection(".debug_aranges"); |
| 31 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; | 32 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| | 33 | st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; |
| 32 | | 34 | |
| 33 | var maybe_fp: ?&const u8 = @frameAddress(); | 35 | var maybe_fp: ?&const u8 = @frameAddress(); |
| 34 | while (true) { | 36 | while (true) { |
| ... | @@ -62,6 +64,140 @@ struct ElfStackTrace { | ... | @@ -62,6 +64,140 @@ struct ElfStackTrace { |
| 62 | elf: elf.Elf, | 64 | elf: elf.Elf, |
| 63 | aranges: ?&elf.SectionHeader, | 65 | aranges: ?&elf.SectionHeader, |
| 64 | debug_info: &elf.SectionHeader, | 66 | debug_info: &elf.SectionHeader, |
| | 67 | debug_abbrev: &elf.SectionHeader, |
| | 68 | } |
| | 69 | |
| | 70 | enum FormValue { |
| | 71 | Address: u64, |
| | 72 | Block: []u8, |
| | 73 | Const: Constant, |
| | 74 | ExprLoc: []u8, |
| | 75 | Flag: bool, |
| | 76 | SecOffset: u64, |
| | 77 | Ref: []u8, |
| | 78 | RefAddr: u64, |
| | 79 | RefSig8: u64, |
| | 80 | String: []u8, |
| | 81 | StrPtr: u64, |
| | 82 | } |
| | 83 | |
| | 84 | struct Constant { |
| | 85 | payload: []u8, |
| | 86 | signed: bool, |
| | 87 | } |
| | 88 | |
| | 89 | |
| | 90 | fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { |
| | 91 | const buf = %return global_allocator.alloc(u8, size); |
| | 92 | %defer global_allocator.free(u8, buf); |
| | 93 | %return in_stream.read(buf); |
| | 94 | return buf; |
| | 95 | } |
| | 96 | |
| | 97 | fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { |
| | 98 | const buf = %return readAllocBytes(in_stream, size); |
| | 99 | return FormValue.Block { buf }; |
| | 100 | } |
| | 101 | |
| | 102 | fn parseFormValueBlock(in_stream: &io.InStream, inline T: type) -> %FormValue { |
| | 103 | const block_len = %return in_stream.readIntLe(T); |
| | 104 | return parseFormValueBlockLen(in_stream, block_len); |
| | 105 | } |
| | 106 | |
| | 107 | fn parseFormValueConstantLen(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { |
| | 108 | const buf = %return readAllocBytes(in_stream, size); |
| | 109 | return FormValue.Const { Constant { |
| | 110 | .signed = signed, |
| | 111 | .payload = buf, |
| | 112 | }}; |
| | 113 | } |
| | 114 | |
| | 115 | fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, inline T: type) -> %FormValue { |
| | 116 | const block_len = %return in_stream.readIntLe(T); |
| | 117 | return parseFormValueConstantLen(in_stream, signed, block_len); |
| | 118 | } |
| | 119 | |
| | 120 | fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 { |
| | 121 | return if (is_64) { |
| | 122 | %return in_stream.readIntLe(u64) |
| | 123 | } else { |
| | 124 | u64(%return in_stream.readIntLe(u32)) |
| | 125 | }; |
| | 126 | } |
| | 127 | |
| | 128 | fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { |
| | 129 | const buf = %return readAllocBytes(in_stream, size); |
| | 130 | return FormValue.Ref { buf }; |
| | 131 | } |
| | 132 | |
| | 133 | fn parseFormValueRef(in_stream: &io.InStream, inline T: type) -> %FormValue { |
| | 134 | const block_len = %return in_stream.readIntLe(T); |
| | 135 | return parseFormValueRefLen(in_stream, block_len); |
| | 136 | } |
| | 137 | |
| | 138 | fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { |
| | 139 | return switch (form_id) { |
| | 140 | DW.FORM_addr => FormValue.Address { |
| | 141 | %return parseFormValueAddrSize(in_stream, is_64) |
| | 142 | }, |
| | 143 | DW.FORM_block1 => parseFormValueBlock(in_stream, u8), |
| | 144 | DW.FORM_block2 => parseFormValueBlock(in_stream, u16), |
| | 145 | DW.FORM_block4 => parseFormValueBlock(in_stream, u32), |
| | 146 | DW.FORM_block => { |
| | 147 | const block_len = %return readULeb128(in_stream); |
| | 148 | parseFormValueBlockLen(in_stream, block_len) |
| | 149 | }, |
| | 150 | DW.FORM_data1 => parseFormValueConstant(in_stream, false, u8), |
| | 151 | DW.FORM_data2 => parseFormValueConstant(in_stream, false, u16), |
| | 152 | DW.FORM_data4 => parseFormValueConstant(in_stream, false, u32), |
| | 153 | DW.FORM_data8 => parseFormValueConstant(in_stream, false, u64), |
| | 154 | DW.FORM_udata, DW.FORM_sdata => { |
| | 155 | const block_len = %return readULeb128(in_stream); |
| | 156 | const signed = form_id == DW.FORM_sdata; |
| | 157 | parseFormValueConstantLen(in_stream, signed, block_len) |
| | 158 | }, |
| | 159 | DW.FORM_exprloc => { |
| | 160 | const size = %return readULeb128(in_stream); |
| | 161 | const buf = %return readAllocBytes(in_stream, size); |
| | 162 | return FormValue.ExprLoc { buf }; |
| | 163 | }, |
| | 164 | DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, |
| | 165 | DW.FORM_flag_present => FormValue.Flag { true }, |
| | 166 | DW.FORM_sec_offset => FormValue.SecOffset { |
| | 167 | %return parseFormValueAddrSize(in_stream, is_64) |
| | 168 | }, |
| | 169 | |
| | 170 | DW.FORM_ref1 => parseFormValueRef(in_stream, u8), |
| | 171 | DW.FORM_ref2 => parseFormValueRef(in_stream, u16), |
| | 172 | DW.FORM_ref4 => parseFormValueRef(in_stream, u32), |
| | 173 | DW.FORM_ref8 => parseFormValueRef(in_stream, u64), |
| | 174 | DW.FORM_ref_udata => { |
| | 175 | const ref_len = %return readULeb128(in_stream); |
| | 176 | parseFormValueRefLen(in_stream, ref_len) |
| | 177 | }, |
| | 178 | |
| | 179 | DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueAddrSize(in_stream, is_64) }, |
| | 180 | DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, |
| | 181 | |
| | 182 | DW.FORM_string => { |
| | 183 | var buf: List(u8) = undefined; |
| | 184 | buf.init(&global_allocator); |
| | 185 | while (true) { |
| | 186 | const byte = %return in_stream.readByte(); |
| | 187 | if (byte == 0) |
| | 188 | break; |
| | 189 | %return buf.append(byte); |
| | 190 | } |
| | 191 | |
| | 192 | FormValue.String { buf.items } |
| | 193 | }, |
| | 194 | DW.FORM_strp => FormValue.StrPtr { %return parseFormValueAddrSize(in_stream, is_64) }, |
| | 195 | DW.FORM_indirect => { |
| | 196 | const child_form_id = %return readULeb128(in_stream); |
| | 197 | parseFormValue(in_stream, child_form_id, is_64) |
| | 198 | }, |
| | 199 | else => return error.InvalidDebugInfo, |
| | 200 | } |
| 65 | } | 201 | } |
| 66 | | 202 | |
| 67 | fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { | 203 | fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| ... | @@ -72,8 +208,78 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { | ... | @@ -72,8 +208,78 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| 72 | %return st.elf.seekToSection(st.debug_info); | 208 | %return st.elf.seekToSection(st.debug_info); |
| 73 | | 209 | |
| 74 | while (true) { | 210 | while (true) { |
| 75 | const tag_id = %return st.self_exe_stream.readByte(); | 211 | var is_64: bool = undefined; |
| 76 | // TODO iterate until we find the relevant compile unit | 212 | const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); |
| | 213 | |
| | 214 | const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16); |
| | 215 | if (version != 4) return error.InvalidDebugInfo; |
| | 216 | |
| | 217 | const debug_abbrev_offset = if (is_64) { |
| | 218 | %return st.self_exe_stream.readInt(st.elf.is_big_endian, u64) |
| | 219 | } else { |
| | 220 | %return st.self_exe_stream.readInt(st.elf.is_big_endian, u32) |
| | 221 | }; |
| | 222 | |
| | 223 | const address_size = %return st.self_exe_stream.readByte(); |
| | 224 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| | 225 | |
| | 226 | const abbrev_tag_id = %return st.self_exe_stream.readByte(); |
| | 227 | |
| | 228 | |
| | 229 | } |
| | 230 | } |
| | 231 | |
| | 232 | fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 { |
| | 233 | const first_32_bits = %return in_stream.readIntLe(u32); |
| | 234 | *is_64 = (first_32_bits == 0xffffffff); |
| | 235 | return if (*is_64) { |
| | 236 | %return in_stream.readIntLe(u64) |
| | 237 | } else { |
| | 238 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; |
| | 239 | u64(first_32_bits) |
| | 240 | }; |
| | 241 | } |
| | 242 | |
| | 243 | fn readULeb128(in_stream: &io.InStream) -> %u64 { |
| | 244 | var result: u64 = 0; |
| | 245 | var shift: u64 = 0; |
| | 246 | |
| | 247 | while (true) { |
| | 248 | const byte = %return in_stream.readByte(); |
| | 249 | var operand: u64 = undefined; |
| | 250 | |
| | 251 | if (@shlWithOverflow(u64, byte & 0b01111111, shift, &operand)) |
| | 252 | return error.InvalidDebugInfo; |
| | 253 | |
| | 254 | result |= operand; |
| | 255 | |
| | 256 | if ((byte & 0b10000000) == 0) |
| | 257 | return result; |
| | 258 | |
| | 259 | shift += 7; |
| | 260 | } |
| | 261 | } |
| | 262 | |
| | 263 | fn readILeb128(in_stream: &io.InStream) -> %i64 { |
| | 264 | var result: i64 = 0; |
| | 265 | var shift: i64 = 0; |
| | 266 | |
| | 267 | while (true) { |
| | 268 | const byte = %return in_stream.readByte(); |
| | 269 | var operand: i64 = undefined; |
| | 270 | |
| | 271 | if (@shlWithOverflow(i64, byte & 0b01111111, shift, &operand)) |
| | 272 | return error.InvalidDebugInfo; |
| | 273 | |
| | 274 | result |= operand; |
| | 275 | shift += 7; |
| | 276 | |
| | 277 | if ((byte & 0b10000000) == 0) { |
| | 278 | if (shift < @sizeOf(i64) * 8 && (byte & 0b01000000) != 0) |
| | 279 | result |= -(i64(1) << shift); |
| | 280 | |
| | 281 | return result; |
| | 282 | } |
| 77 | } | 283 | } |
| 78 | } | 284 | } |
| 79 | | 285 | |
| ... | @@ -84,13 +290,8 @@ fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 { | ... | @@ -84,13 +290,8 @@ fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 { |
| 84 | %return st.elf.seekToSection(aranges); | 290 | %return st.elf.seekToSection(aranges); |
| 85 | | 291 | |
| 86 | const first_32_bits = %return st.self_exe_stream.readIntLe(u32); | 292 | const first_32_bits = %return st.self_exe_stream.readIntLe(u32); |
| 87 | const is_64 = (first_32_bits == 0xffffffff); | 293 | var is_64: bool = undefined; |
| 88 | const unit_length = if (is_64) { | 294 | const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); |
| 89 | %return st.self_exe_stream.readIntLe(u64) | | |
| 90 | } else { | | |
| 91 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; | | |
| 92 | first_32_bits | | |
| 93 | }; | | |
| 94 | var unit_index: u64 = 0; | 295 | var unit_index: u64 = 0; |
| 95 | | 296 | |
| 96 | while (unit_index < unit_length) { | 297 | while (unit_index < unit_length) { |