| ... | @@ -14,6 +14,7 @@ const trace = @import("tracy.zig").trace; | ... | @@ -14,6 +14,7 @@ const trace = @import("tracy.zig").trace; |
| 14 | const leb128 = std.debug.leb; | 14 | const leb128 = std.debug.leb; |
| 15 | const Package = @import("Package.zig"); | 15 | const Package = @import("Package.zig"); |
| 16 | const Value = @import("value.zig").Value; | 16 | const Value = @import("value.zig").Value; |
| | 17 | const Type = @import("type.zig").Type; |
| 17 | | 18 | |
| 18 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | 19 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. |
| 19 | // zig fmt: off | 20 | // zig fmt: off |
| ... | @@ -985,6 +986,12 @@ pub const File = struct { | ... | @@ -985,6 +986,12 @@ pub const File = struct { |
| 985 | } | 986 | } |
| 986 | } | 987 | } |
| 987 | | 988 | |
| | 989 | pub const abbrev_compile_unit = 1; |
| | 990 | pub const abbrev_subprogram = 2; |
| | 991 | pub const abbrev_subprogram_retvoid = 3; |
| | 992 | pub const abbrev_base_type = 4; |
| | 993 | pub const abbrev_pad1 = 5; |
| | 994 | |
| 988 | /// Commit pending changes and write headers. | 995 | /// Commit pending changes and write headers. |
| 989 | pub fn flush(self: *Elf) !void { | 996 | pub fn flush(self: *Elf) !void { |
| 990 | const target_endian = self.base.options.target.cpu.arch.endian(); | 997 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| ... | @@ -1005,7 +1012,7 @@ pub const File = struct { | ... | @@ -1005,7 +1012,7 @@ pub const File = struct { |
| 1005 | // These are LEB encoded but since the values are all less than 127 | 1012 | // These are LEB encoded but since the values are all less than 127 |
| 1006 | // we can simply append these bytes. | 1013 | // we can simply append these bytes. |
| 1007 | const abbrev_buf = [_]u8{ | 1014 | const abbrev_buf = [_]u8{ |
| 1008 | 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header | 1015 | abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header |
| 1009 | DW.AT_stmt_list, DW.FORM_sec_offset, | 1016 | DW.AT_stmt_list, DW.FORM_sec_offset, |
| 1010 | DW.AT_low_pc , DW.FORM_addr, | 1017 | DW.AT_low_pc , DW.FORM_addr, |
| 1011 | DW.AT_high_pc , DW.FORM_addr, | 1018 | DW.AT_high_pc , DW.FORM_addr, |
| ... | @@ -1015,6 +1022,28 @@ pub const File = struct { | ... | @@ -1015,6 +1022,28 @@ pub const File = struct { |
| 1015 | DW.AT_language , DW.FORM_data2, | 1022 | DW.AT_language , DW.FORM_data2, |
| 1016 | 0, 0, // table sentinel | 1023 | 0, 0, // table sentinel |
| 1017 | | 1024 | |
| | 1025 | abbrev_subprogram, DW.TAG_subprogram, DW.CHILDREN_yes, // header |
| | 1026 | DW.AT_low_pc , DW.FORM_addr, |
| | 1027 | DW.AT_high_pc , DW.FORM_data4, |
| | 1028 | DW.AT_type , DW.FORM_ref4, |
| | 1029 | DW.AT_name , DW.FORM_string, |
| | 1030 | 0, 0, // table sentinel |
| | 1031 | |
| | 1032 | abbrev_subprogram_retvoid, DW.TAG_subprogram, DW.CHILDREN_yes, // header |
| | 1033 | DW.AT_low_pc , DW.FORM_addr, |
| | 1034 | DW.AT_high_pc , DW.FORM_data4, |
| | 1035 | DW.AT_name , DW.FORM_string, |
| | 1036 | 0, 0, // table sentinel |
| | 1037 | |
| | 1038 | abbrev_base_type, DW.TAG_base_type, DW.CHILDREN_no, // header |
| | 1039 | DW.AT_encoding , DW.FORM_data1, |
| | 1040 | DW.AT_byte_size, DW.FORM_data1, |
| | 1041 | DW.AT_name , DW.FORM_string, |
| | 1042 | 0, 0, // table sentinel |
| | 1043 | |
| | 1044 | abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header |
| | 1045 | 0, 0, // table sentinel |
| | 1046 | |
| 1018 | 0, 0, 0, // section sentinel | 1047 | 0, 0, 0, // section sentinel |
| 1019 | }; | 1048 | }; |
| 1020 | | 1049 | |
| ... | @@ -1092,7 +1121,7 @@ pub const File = struct { | ... | @@ -1092,7 +1121,7 @@ pub const File = struct { |
| 1092 | const low_pc = text_phdr.p_vaddr; | 1121 | const low_pc = text_phdr.p_vaddr; |
| 1093 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; | 1122 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; |
| 1094 | | 1123 | |
| 1095 | di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header | 1124 | di_buf.appendAssumeCapacity(abbrev_compile_unit); |
| 1096 | self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset | 1125 | self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset |
| 1097 | self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); | 1126 | self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); |
| 1098 | self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); | 1127 | self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); |
| ... | @@ -1834,6 +1863,7 @@ pub const File = struct { | ... | @@ -1834,6 +1863,7 @@ pub const File = struct { |
| 1834 | .Fn => true, | 1863 | .Fn => true, |
| 1835 | else => false, | 1864 | else => false, |
| 1836 | }; | 1865 | }; |
| | 1866 | var fn_ret_has_bits: bool = undefined; |
| 1837 | if (is_fn) { | 1867 | if (is_fn) { |
| 1838 | // For functions we need to add a prologue to the debug line program. | 1868 | // For functions we need to add a prologue to the debug line program. |
| 1839 | try dbg_line_buffer.ensureCapacity(26); | 1869 | try dbg_line_buffer.ensureCapacity(26); |
| ... | @@ -1884,6 +1914,31 @@ pub const File = struct { | ... | @@ -1884,6 +1914,31 @@ pub const File = struct { |
| 1884 | // Emit a line for the begin curly with prologue_end=false. The codegen will | 1914 | // Emit a line for the begin curly with prologue_end=false. The codegen will |
| 1885 | // do the work of setting prologue_end=true and epilogue_begin=true. | 1915 | // do the work of setting prologue_end=true and epilogue_begin=true. |
| 1886 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); | 1916 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); |
| | 1917 | |
| | 1918 | // .debug_info subprogram |
| | 1919 | const decl_name_with_null = decl.name[0..mem.lenZ(decl.name) + 1]; |
| | 1920 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len); |
| | 1921 | |
| | 1922 | fn_ret_has_bits = typed_value.ty.fnReturnType().hasCodeGenBits(); |
| | 1923 | if (fn_ret_has_bits) { |
| | 1924 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); |
| | 1925 | } else { |
| | 1926 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); |
| | 1927 | } |
| | 1928 | // These get overwritten after generating the machine code. These values are |
| | 1929 | // "relocations" and have to be in this fixed place so that functions can be |
| | 1930 | // moved in virtual address space. |
| | 1931 | assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); |
| | 1932 | dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr |
| | 1933 | assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); |
| | 1934 | dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4 |
| | 1935 | if (fn_ret_has_bits) { |
| | 1936 | assert(self.getRelocDbgInfoSubprogramRetType() == dbg_info_buffer.items.len); |
| | 1937 | dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4 |
| | 1938 | } |
| | 1939 | dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string |
| | 1940 | } else { |
| | 1941 | // TODO implement .debug_info for global variables |
| 1887 | } | 1942 | } |
| 1888 | const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer); | 1943 | const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer); |
| 1889 | const code = switch (res) { | 1944 | const code = switch (res) { |
| ... | @@ -1951,22 +2006,40 @@ pub const File = struct { | ... | @@ -1951,22 +2006,40 @@ pub const File = struct { |
| 1951 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; | 2006 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; |
| 1952 | try self.base.file.?.pwriteAll(code, file_offset); | 2007 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1953 | | 2008 | |
| 1954 | try self.updateDeclDebugInfo(module, decl, dbg_info_buffer.items); | 2009 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| | 2010 | |
| | 2011 | const text_block = &decl.link.elf; |
| 1955 | | 2012 | |
| 1956 | // If the Decl is a function, we need to update the .debug_line program. | 2013 | // If the Decl is a function, we need to update the .debug_line program. |
| | 2014 | var fn_ret_type_index: usize = undefined; |
| 1957 | if (is_fn) { | 2015 | if (is_fn) { |
| 1958 | // Perform the relocation based on vaddr. | 2016 | // Perform the relocations based on vaddr. |
| 1959 | const target_endian = self.base.options.target.cpu.arch.endian(); | | |
| 1960 | switch (self.ptr_width) { | 2017 | switch (self.ptr_width) { |
| 1961 | .p32 => { | 2018 | .p32 => { |
| 1962 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; | 2019 | { |
| 1963 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); | 2020 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; |
| | 2021 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); |
| | 2022 | } |
| | 2023 | { |
| | 2024 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4]; |
| | 2025 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); |
| | 2026 | } |
| 1964 | }, | 2027 | }, |
| 1965 | .p64 => { | 2028 | .p64 => { |
| 1966 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; | 2029 | { |
| 1967 | mem.writeInt(u64, ptr, local_sym.st_value, target_endian); | 2030 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; |
| | 2031 | mem.writeInt(u64, ptr, local_sym.st_value, target_endian); |
| | 2032 | } |
| | 2033 | { |
| | 2034 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; |
| | 2035 | mem.writeInt(u64, ptr, local_sym.st_value, target_endian); |
| | 2036 | } |
| 1968 | }, | 2037 | }, |
| 1969 | } | 2038 | } |
| | 2039 | { |
| | 2040 | const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4]; |
| | 2041 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian); |
| | 2042 | } |
| 1970 | | 2043 | |
| 1971 | try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence }); | 2044 | try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence }); |
| 1972 | | 2045 | |
| ... | @@ -2043,14 +2116,69 @@ pub const File = struct { | ... | @@ -2043,14 +2116,69 @@ pub const File = struct { |
| 2043 | // from the .debug_line section. | 2116 | // from the .debug_line section. |
| 2044 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | 2117 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 2045 | try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); | 2118 | try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); |
| | 2119 | |
| | 2120 | // .debug_info |
| | 2121 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 2); |
| | 2122 | // End the TAG_subprogram children. |
| | 2123 | dbg_info_buffer.appendAssumeCapacity(0); |
| | 2124 | if (fn_ret_has_bits) { |
| | 2125 | // Now we do the return type of the function. The relocation must be performed |
| | 2126 | // later after the offset for this subprogram is computed. |
| | 2127 | fn_ret_type_index = dbg_info_buffer.items.len; |
| | 2128 | try self.addDbgInfoType(typed_value.ty.fnReturnType(), &dbg_info_buffer); |
| | 2129 | } |
| 2046 | } | 2130 | } |
| 2047 | | 2131 | |
| | 2132 | try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len)); |
| | 2133 | |
| | 2134 | if (is_fn and fn_ret_has_bits) { |
| | 2135 | // Perform function return type relocation. |
| | 2136 | mem.writeInt( |
| | 2137 | u32, |
| | 2138 | dbg_info_buffer.items[self.getRelocDbgInfoSubprogramRetType()..][0..4], |
| | 2139 | text_block.dbg_info_off + @intCast(u32, fn_ret_type_index), |
| | 2140 | target_endian, |
| | 2141 | ); |
| | 2142 | } |
| | 2143 | |
| | 2144 | try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items); |
| | 2145 | |
| 2048 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 2146 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 2049 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | 2147 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; |
| 2050 | return self.updateDeclExports(module, decl, decl_exports); | 2148 | return self.updateDeclExports(module, decl, decl_exports); |
| 2051 | } | 2149 | } |
| 2052 | | 2150 | |
| 2053 | pub fn updateDeclDebugInfo(self: *Elf, module: *Module, decl: *Module.Decl, dbg_info_buf: []const u8) !void { | 2151 | /// Asserts the type has codegen bits. |
| | 2152 | fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void { |
| | 2153 | switch (ty.zigTypeTag()) { |
| | 2154 | .Void, .NoReturn => unreachable, |
| | 2155 | .Bool => { |
| | 2156 | try dbg_info_buffer.appendSlice(&[_]u8{ |
| | 2157 | abbrev_base_type, |
| | 2158 | DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 |
| | 2159 | 1, // DW.AT_byte_size, DW.FORM_data1 |
| | 2160 | 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string |
| | 2161 | }); |
| | 2162 | }, |
| | 2163 | .Int => { |
| | 2164 | const info = ty.intInfo(self.base.options.target); |
| | 2165 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); |
| | 2166 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); |
| | 2167 | // DW.AT_encoding, DW.FORM_data1 |
| | 2168 | dbg_info_buffer.appendAssumeCapacity(if (info.signed) DW.ATE_signed else DW.ATE_unsigned); |
| | 2169 | // DW.AT_byte_size, DW.FORM_data1 |
| | 2170 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); |
| | 2171 | // DW.AT_name, DW.FORM_string |
| | 2172 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); |
| | 2173 | }, |
| | 2174 | else => { |
| | 2175 | log.err(.compiler, "TODO implement .debug_info for type '{}'", .{ty}); |
| | 2176 | try dbg_info_buffer.append(abbrev_pad1); |
| | 2177 | }, |
| | 2178 | } |
| | 2179 | } |
| | 2180 | |
| | 2181 | fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void { |
| 2054 | const tracy = trace(@src()); | 2182 | const tracy = trace(@src()); |
| 2055 | defer tracy.end(); | 2183 | defer tracy.end(); |
| 2056 | | 2184 | |
| ... | @@ -2059,7 +2187,7 @@ pub const File = struct { | ... | @@ -2059,7 +2187,7 @@ pub const File = struct { |
| 2059 | // probably need to edit that logic too. | 2187 | // probably need to edit that logic too. |
| 2060 | | 2188 | |
| 2061 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; | 2189 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; |
| 2062 | const text_block = &decl.link.elf; | 2190 | text_block.dbg_info_len = len; |
| 2063 | if (self.dbg_info_decl_last) |last| { | 2191 | if (self.dbg_info_decl_last) |last| { |
| 2064 | if (text_block.dbg_info_next) |next| { | 2192 | if (text_block.dbg_info_next) |next| { |
| 2065 | // Update existing Decl - non-last item. | 2193 | // Update existing Decl - non-last item. |
| ... | @@ -2097,6 +2225,17 @@ pub const File = struct { | ... | @@ -2097,6 +2225,17 @@ pub const File = struct { |
| 2097 | | 2225 | |
| 2098 | text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den; | 2226 | text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den; |
| 2099 | } | 2227 | } |
| | 2228 | } |
| | 2229 | |
| | 2230 | fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void { |
| | 2231 | const tracy = trace(@src()); |
| | 2232 | defer tracy.end(); |
| | 2233 | |
| | 2234 | // This logic is nearly identical to the logic above in `updateDecl` for |
| | 2235 | // `SrcFn` and the line number programs. If you are editing this logic, you |
| | 2236 | // probably need to edit that logic too. |
| | 2237 | |
| | 2238 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; |
| 2100 | | 2239 | |
| 2101 | const last_decl = self.dbg_info_decl_last.?; | 2240 | const last_decl = self.dbg_info_decl_last.?; |
| 2102 | const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len; | 2241 | const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len; |
| ... | @@ -2441,6 +2580,9 @@ pub const File = struct { | ... | @@ -2441,6 +2580,9 @@ pub const File = struct { |
| 2441 | /// The reloc offset for the virtual address of a function in its Line Number Program. | 2580 | /// The reloc offset for the virtual address of a function in its Line Number Program. |
| 2442 | /// Size is a virtual address integer. | 2581 | /// Size is a virtual address integer. |
| 2443 | const dbg_line_vaddr_reloc_index = 3; | 2582 | const dbg_line_vaddr_reloc_index = 3; |
| | 2583 | /// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram. |
| | 2584 | /// Size is a virtual address integer. |
| | 2585 | const dbg_info_low_pc_reloc_index = 1; |
| 2444 | | 2586 | |
| 2445 | /// The reloc offset for the line offset of a function from the previous function's line. | 2587 | /// The reloc offset for the line offset of a function from the previous function's line. |
| 2446 | /// It's a fixed-size 4-byte ULEB128. | 2588 | /// It's a fixed-size 4-byte ULEB128. |
| ... | @@ -2452,6 +2594,14 @@ pub const File = struct { | ... | @@ -2452,6 +2594,14 @@ pub const File = struct { |
| 2452 | return self.getRelocDbgLineOff() + 5; | 2594 | return self.getRelocDbgLineOff() + 5; |
| 2453 | } | 2595 | } |
| 2454 | | 2596 | |
| | 2597 | fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 { |
| | 2598 | return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); |
| | 2599 | } |
| | 2600 | |
| | 2601 | fn getRelocDbgInfoSubprogramRetType(self: Elf) u32 { |
| | 2602 | return self.getRelocDbgInfoSubprogramHighPC() + 4; |
| | 2603 | } |
| | 2604 | |
| 2455 | fn dbgLineNeededHeaderBytes(self: Elf) u32 { | 2605 | fn dbgLineNeededHeaderBytes(self: Elf) u32 { |
| 2456 | const directory_entry_format_count = 1; | 2606 | const directory_entry_format_count = 1; |
| 2457 | const file_name_entry_format_count = 1; | 2607 | const file_name_entry_format_count = 1; |
| ... | @@ -2565,7 +2715,7 @@ pub const File = struct { | ... | @@ -2565,7 +2715,7 @@ pub const File = struct { |
| 2565 | const tracy = trace(@src()); | 2715 | const tracy = trace(@src()); |
| 2566 | defer tracy.end(); | 2716 | defer tracy.end(); |
| 2567 | | 2717 | |
| 2568 | const page_of_nops = [1]u8{0} ** 4096; | 2718 | const page_of_nops = [1]u8{abbrev_pad1} ** 4096; |
| 2569 | var vecs: [32]std.os.iovec_const = undefined; | 2719 | var vecs: [32]std.os.iovec_const = undefined; |
| 2570 | var vec_index: usize = 0; | 2720 | var vec_index: usize = 0; |
| 2571 | { | 2721 | { |