| ... | ... | @@ -3,6 +3,7 @@ const io = @import("io.zig"); |
| 3 | 3 | const os = @import("os.zig"); |
| 4 | 4 | const elf = @import("elf.zig"); |
| 5 | 5 | const DW = @import("dwarf.zig"); |
| 6 | const List = @import("list.zig").List; |
| 6 | 7 | |
| 7 | 8 | pub error MissingDebugInfo; |
| 8 | 9 | pub error InvalidDebugInfo; |
| ... | ... | @@ -29,6 +30,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 29 | 30 | |
| 30 | 31 | st.aranges = %return st.elf.findSection(".debug_aranges"); |
| 31 | 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 | 35 | var maybe_fp: ?&const u8 = @frameAddress(); |
| 34 | 36 | while (true) { |
| ... | ... | @@ -62,6 +64,140 @@ struct ElfStackTrace { |
| 62 | 64 | elf: elf.Elf, |
| 63 | 65 | aranges: ?&elf.SectionHeader, |
| 64 | 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 | 203 | fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| ... | ... | @@ -72,8 +208,78 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| 72 | 208 | %return st.elf.seekToSection(st.debug_info); |
| 73 | 209 | |
| 74 | 210 | while (true) { |
| 75 | | const tag_id = %return st.self_exe_stream.readByte(); |
| 76 | | // TODO iterate until we find the relevant compile unit |
| 211 | var is_64: bool = undefined; |
| 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 | 290 | %return st.elf.seekToSection(aranges); |
| 85 | 291 | |
| 86 | 292 | const first_32_bits = %return st.self_exe_stream.readIntLe(u32); |
| 87 | | const is_64 = (first_32_bits == 0xffffffff); |
| 88 | | const unit_length = if (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 | | }; |
| 293 | var is_64: bool = undefined; |
| 294 | const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); |
| 94 | 295 | var unit_index: u64 = 0; |
| 95 | 296 | |
| 96 | 297 | while (unit_index < unit_length) { |