| ... | ... | @@ -344,8 +344,11 @@ pub const File = struct { |
| 344 | 344 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){}, |
| 345 | 345 | last_text_block: ?*TextBlock = null, |
| 346 | 346 | |
| 347 | | first_dbg_line_file: ?*SrcFile = null, |
| 348 | | last_dbg_line_file: ?*SrcFile = null, |
| 347 | /// A list of `SrcFn` whose Line Number Programs have surplus capacity. |
| 348 | /// This is the same concept as `text_block_free_list`; see those doc comments. |
| 349 | dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, |
| 350 | dbg_line_fn_first: ?*SrcFn = null, |
| 351 | dbg_line_fn_last: ?*SrcFn = null, |
| 349 | 352 | |
| 350 | 353 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 351 | 354 | const alloc_num = 4; |
| ... | ... | @@ -411,46 +414,20 @@ pub const File = struct { |
| 411 | 414 | }; |
| 412 | 415 | |
| 413 | 416 | pub const SrcFn = struct { |
| 414 | | /// Offset from the `SrcFile` that contains this function. |
| 417 | /// Offset from the beginning of the Debug Line Program header that contains this function. |
| 415 | 418 | off: u32, |
| 416 | 419 | /// Size of the line number program component belonging to this function, not |
| 417 | 420 | /// including padding. |
| 418 | 421 | len: u32, |
| 419 | 422 | |
| 420 | | pub const empty: SrcFn = .{ |
| 421 | | .off = 0, |
| 422 | | .len = 0, |
| 423 | | }; |
| 424 | | }; |
| 425 | | |
| 426 | | pub const SrcFile = struct { |
| 427 | | /// Byte offset from the start of the Line Number Program that contains this file. |
| 428 | | off: u32, |
| 429 | | /// Length in bytes, not including padding, of this file component within the |
| 430 | | /// Line Number Program that contains it. |
| 431 | | len: u32, |
| 432 | | |
| 433 | | /// An ordered list of all the `SrcFn` in this file. This list is not redundant with |
| 434 | | /// the source Decl list, for two reasons: |
| 435 | | /// * Lazy decl analysis: some source functions do not correspond to any compiled functions. |
| 436 | | /// * Generic functions: some source functions correspond to many compiled functions. |
| 437 | | /// This list corresponds to the file data in the Line Number Program. When a new `SrcFn` |
| 438 | | /// is inserted, the list must be shifted to accomodate it, and likewise the Line |
| 439 | | /// Number Program data must be shifted within the ELF file to accomodate (if there is |
| 440 | | /// not enough padding). |
| 441 | | /// It is a hash map so that we can look up the index based on the `*SrcFn` and therefore |
| 442 | | /// find the next and previous functions. |
| 443 | | fns: std.AutoHashMapUnmanaged(*SrcFn, void), |
| 444 | | |
| 445 | 423 | /// Points to the previous and next neighbors, based on the offset from .debug_line. |
| 446 | | /// This can be used to find, for example, the capacity of this `SrcFile`. |
| 447 | | prev: ?*SrcFile, |
| 448 | | next: ?*SrcFile, |
| 424 | /// This can be used to find, for example, the capacity of this `SrcFn`. |
| 425 | prev: ?*SrcFn, |
| 426 | next: ?*SrcFn, |
| 449 | 427 | |
| 450 | | pub const empty: SrcFile = .{ |
| 428 | pub const empty: SrcFn = .{ |
| 451 | 429 | .off = 0, |
| 452 | 430 | .len = 0, |
| 453 | | .fns = .{}, |
| 454 | 431 | .prev = null, |
| 455 | 432 | .next = null, |
| 456 | 433 | }; |
| ... | ... | @@ -593,11 +570,11 @@ pub const File = struct { |
| 593 | 570 | } |
| 594 | 571 | |
| 595 | 572 | fn getDebugLineProgramOff(self: Elf) u32 { |
| 596 | | return self.first_dbg_line_file.?.off; |
| 573 | return self.dbg_line_fn_first.?.off; |
| 597 | 574 | } |
| 598 | 575 | |
| 599 | 576 | fn getDebugLineProgramEnd(self: Elf) u32 { |
| 600 | | return self.last_dbg_line_file.?.off + self.last_dbg_line_file.?.len; |
| 577 | return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len; |
| 601 | 578 | } |
| 602 | 579 | |
| 603 | 580 | /// Returns end pos of collision, if any. |
| ... | ... | @@ -1207,7 +1184,7 @@ pub const File = struct { |
| 1207 | 1184 | |
| 1208 | 1185 | // The size of this header is variable, depending on the number of directories, |
| 1209 | 1186 | // files, and padding. We have a function to compute the upper bound size, however, |
| 1210 | | // because it's needed for determining where to put the offset of the first `SrcFile`. |
| 1187 | // because it's needed for determining where to put the offset of the first `SrcFn`. |
| 1211 | 1188 | try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes()); |
| 1212 | 1189 | |
| 1213 | 1190 | // initial length - length of the .debug_line contribution for this compilation unit, |
| ... | ... | @@ -1280,18 +1257,13 @@ pub const File = struct { |
| 1280 | 1257 | }, |
| 1281 | 1258 | } |
| 1282 | 1259 | |
| 1283 | | // We use a NOP jmp because consumers empirically do not respect the header length field. |
| 1284 | | const after_jmp = di_buf.items.len + 6; |
| 1285 | | if (after_jmp > dbg_line_prg_off) { |
| 1260 | // We use NOPs because consumers empirically do not respect the header length field. |
| 1261 | if (di_buf.items.len > dbg_line_prg_off) { |
| 1286 | 1262 | // Move the first N files to the end to make more padding for the header. |
| 1287 | 1263 | @panic("TODO: handle .debug_line header exceeding its padding"); |
| 1288 | 1264 | } |
| 1289 | | const jmp_amt = dbg_line_prg_off - after_jmp + 1; |
| 1290 | | di_buf.appendAssumeCapacity(DW.LNS_extended_op); |
| 1291 | | leb128.writeUnsignedFixed(4, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u28, jmp_amt)); |
| 1292 | | di_buf.appendAssumeCapacity(DW.LNE_hi_user); |
| 1293 | | |
| 1294 | | try self.file.?.pwriteAll(di_buf.items, debug_line_sect.sh_offset); |
| 1265 | const jmp_amt = dbg_line_prg_off - di_buf.items.len; |
| 1266 | try self.pwriteWithNops(di_buf.items, jmp_amt, debug_line_sect.sh_offset); |
| 1295 | 1267 | self.debug_line_header_dirty = false; |
| 1296 | 1268 | } |
| 1297 | 1269 | |
| ... | ... | @@ -1826,6 +1798,16 @@ pub const File = struct { |
| 1826 | 1798 | // For functions we need to add a prologue to the debug line program. |
| 1827 | 1799 | try dbg_line_buffer.ensureCapacity(26); |
| 1828 | 1800 | |
| 1801 | const scope_file = decl.scope.cast(Module.Scope.File).?; |
| 1802 | const tree = scope_file.contents.tree; |
| 1803 | const file_ast_decls = tree.root_node.decls(); |
| 1804 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 1805 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 1806 | const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; |
| 1807 | const block = fn_proto.body().?.castTag(.Block).?; |
| 1808 | const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); |
| 1809 | const casted_line_off = @intCast(u28, line_delta); |
| 1810 | |
| 1829 | 1811 | const ptr_width_bytes = self.ptrWidthBytes(); |
| 1830 | 1812 | dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ |
| 1831 | 1813 | DW.LNS_extended_op, |
| ... | ... | @@ -1840,9 +1822,15 @@ pub const File = struct { |
| 1840 | 1822 | // This is the "relocatable" relative line offset from the previous function's end curly |
| 1841 | 1823 | // to this function's begin curly. |
| 1842 | 1824 | assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); |
| 1843 | | // Here we allocate 4 bytes for the relocation. This field is a ULEB128, however, |
| 1844 | | // it is possible to encode small values as still taking up 4 bytes. |
| 1845 | | dbg_line_buffer.items.len += 4; |
| 1825 | // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. |
| 1826 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), casted_line_off); |
| 1827 | |
| 1828 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file); |
| 1829 | assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); |
| 1830 | // Once we support more than one source file, this will have the ability to be more |
| 1831 | // than one possible value. |
| 1832 | const file_index = 1; |
| 1833 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); |
| 1846 | 1834 | |
| 1847 | 1835 | // Emit a line for the begin curly with prologue_end=false. The codegen will |
| 1848 | 1836 | // do the work of setting prologue_end=true and epilogue_begin=true. |
| ... | ... | @@ -1916,14 +1904,6 @@ pub const File = struct { |
| 1916 | 1904 | |
| 1917 | 1905 | // If the Decl is a function, we need to update the .debug_line program. |
| 1918 | 1906 | if (is_fn) { |
| 1919 | | // For padding between functions, we terminate with `LNS_extended_op` with sub-op |
| 1920 | | // `LNE_hi_user`, using a fixed 4-byte ULEB128 for the opcode size. This is always |
| 1921 | | // found at the very end of the SrcFile's Line Number Program component. |
| 1922 | | try dbg_line_buffer.ensureCapacity(dbg_line_buffer.items.len + 6); |
| 1923 | | dbg_line_buffer.appendAssumeCapacity(DW.LNS_extended_op); |
| 1924 | | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), 1); |
| 1925 | | dbg_line_buffer.appendAssumeCapacity(DW.LNE_hi_user); |
| 1926 | | |
| 1927 | 1907 | // Perform the relocation based on vaddr. |
| 1928 | 1908 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1929 | 1909 | switch (self.ptr_width) { |
| ... | ... | @@ -1937,94 +1917,53 @@ pub const File = struct { |
| 1937 | 1917 | }, |
| 1938 | 1918 | } |
| 1939 | 1919 | |
| 1940 | | // Now we want to write the line offset relocation, however, first we must |
| 1941 | | // "plug in" the SrcFn into its parent SrcFile, so that we know what function the line |
| 1942 | | // number is offset from. It must go in the same order as the functions are found |
| 1943 | | // in the Zig source. When we insert a function before another one, the latter one |
| 1944 | | // must have its line offset relocation updated. |
| 1920 | try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence }); |
| 1921 | |
| 1922 | // Now we have the full contents and may allocate a region to store it. |
| 1945 | 1923 | |
| 1946 | 1924 | const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; |
| 1947 | | const scope_file = decl.scope.cast(Module.Scope.File).?; |
| 1948 | | const src_file = &scope_file.link; |
| 1949 | 1925 | const src_fn = &typed_value.val.cast(Value.Payload.Function).?.func.link; |
| 1950 | | var src_fn_index: usize = undefined; |
| 1951 | | if (src_file.len == 0) { |
| 1952 | | // This is the first function of the SrcFile. |
| 1953 | | assert(src_file.fns.entries.items.len == 0); |
| 1954 | | src_fn_index = 0; |
| 1955 | | try src_file.fns.put(self.allocator, src_fn, {}); |
| 1956 | | |
| 1957 | | if (self.last_dbg_line_file) |last| { |
| 1958 | | src_file.prev = last; |
| 1959 | | self.last_dbg_line_file = src_file; |
| 1960 | | |
| 1961 | | // Update the previous last SrcFile's terminating NOP to skip to the start |
| 1962 | | // of the new last SrcFile's start. |
| 1963 | | @panic("TODO updateDecl for .debug_line: add new SrcFile: append"); |
| 1964 | | } else { |
| 1965 | | // This is the first file (and function) of the Line Number Program. |
| 1966 | | self.first_dbg_line_file = src_file; |
| 1967 | | self.last_dbg_line_file = src_file; |
| 1968 | | |
| 1969 | | src_fn.off = dbg_line_file_header_len; |
| 1926 | if (self.dbg_line_fn_last) |last| { |
| 1927 | if (src_fn.prev == null and src_fn.next == null) { |
| 1928 | // Append new function. |
| 1929 | src_fn.prev = last; |
| 1930 | last.next = src_fn; |
| 1931 | self.dbg_line_fn_last = src_fn; |
| 1932 | |
| 1933 | src_fn.off = last.off + (last.len * alloc_num / alloc_den); |
| 1970 | 1934 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); |
| 1971 | | |
| 1972 | | src_file.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den; |
| 1973 | | src_file.len = src_fn.off + src_fn.len + dbg_line_file_trailer_len; |
| 1974 | | |
| 1975 | | const needed_size = src_file.off + src_file.len; |
| 1976 | | if (needed_size > debug_line_sect.sh_size) { |
| 1977 | | debug_line_sect.sh_offset = self.findFreeSpace(needed_size, 1); |
| 1978 | | } |
| 1979 | | debug_line_sect.sh_size = needed_size; |
| 1980 | | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| 1981 | | self.debug_line_header_dirty = true; |
| 1982 | | |
| 1983 | | try self.updateDbgLineFile(src_file); |
| 1935 | } else { |
| 1936 | // Update existing function. |
| 1937 | @panic("TODO updateDecl for .debug_line: add new SrcFn: update"); |
| 1984 | 1938 | } |
| 1985 | 1939 | } else { |
| 1986 | | @panic("TODO updateDecl for .debug_line: update existing SrcFile"); |
| 1987 | | //src_fn_index = @panic("TODO"); |
| 1940 | // This is the first function of the Line Number Program. |
| 1941 | self.dbg_line_fn_first = src_fn; |
| 1942 | self.dbg_line_fn_last = src_fn; |
| 1943 | |
| 1944 | src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den; |
| 1945 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); |
| 1988 | 1946 | } |
| 1989 | | const line_off: u28 = blk: { |
| 1990 | | const tree = scope_file.contents.tree; |
| 1991 | | const file_ast_decls = tree.root_node.decls(); |
| 1992 | | // TODO Look into improving the performance here by adding a token-index-to-line |
| 1993 | | // lookup table. Currently this involves scanning over the source code for newlines |
| 1994 | | // (but only from the previous decl to the current one). |
| 1995 | | if (src_fn_index == 0) { |
| 1996 | | // Since it's the first function in the file, the line number delta is just the |
| 1997 | | // line number of the open curly from the beginning of the file. |
| 1998 | | const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; |
| 1999 | | const block = fn_proto.body().?.castTag(.Block).?; |
| 2000 | | const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); |
| 2001 | | // No need to add one; this is a delta from DWARF's starting line number (1). |
| 2002 | | break :blk @intCast(u28, line_delta); |
| 2003 | | } else { |
| 2004 | | const prev_src_fn = src_file.fns.entries.items[src_fn_index - 1].key; |
| 2005 | | const mod_fn = @fieldParentPtr(Module.Fn, "link", prev_src_fn); |
| 2006 | | const prev_fn_proto = file_ast_decls[mod_fn.owner_decl.src_index].castTag(.FnProto).?; |
| 2007 | | const this_fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; |
| 2008 | | const prev_block = prev_fn_proto.body().?.castTag(.Block).?; |
| 2009 | | const this_block = this_fn_proto.body().?.castTag(.Block).?; |
| 2010 | | // Find the difference between prev decl end curly and this decl begin curly. |
| 2011 | | const line_delta = std.zig.lineDelta(tree.source, |
| 2012 | | tree.token_locs[prev_block.rbrace].start, |
| 2013 | | tree.token_locs[this_block.lbrace].start, |
| 2014 | | ); |
| 2015 | | // No need to add one; this is a delta from the previous line number. |
| 2016 | | break :blk @intCast(u28, line_delta); |
| 2017 | | } |
| 2018 | | }; |
| 2019 | 1947 | |
| 2020 | | // Here we use a ULEB128 but we write 4 bytes regardless (possibly wasting space) because |
| 2021 | | // that is the amount of space we allocated for this field. |
| 2022 | | leb128.writeUnsignedFixed(4, dbg_line_buffer.items[self.getRelocDbgLineOff()..][0..4], line_off); |
| 1948 | const needed_size = src_fn.off + src_fn.len; |
| 1949 | if (needed_size != debug_line_sect.sh_size) { |
| 1950 | if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) { |
| 1951 | const new_offset = self.findFreeSpace(needed_size, 1); |
| 1952 | const existing_size = src_fn.off; |
| 1953 | const amt = try self.file.?.copyRangeAll(debug_line_sect.sh_offset, self.file.?, new_offset, existing_size); |
| 1954 | if (amt != existing_size) return error.InputOutput; |
| 1955 | debug_line_sect.sh_offset = new_offset; |
| 1956 | } |
| 1957 | debug_line_sect.sh_size = needed_size; |
| 1958 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| 1959 | self.debug_line_header_dirty = true; |
| 1960 | } |
| 1961 | const padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; |
| 2023 | 1962 | |
| 2024 | 1963 | // We only have support for one compilation unit so far, so the offsets are directly |
| 2025 | 1964 | // from the .debug_line section. |
| 2026 | | const file_pos = debug_line_sect.sh_offset + src_file.off + src_fn.off; |
| 2027 | | try self.file.?.pwriteAll(dbg_line_buffer.items, file_pos); |
| 1965 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 1966 | try self.pwriteWithNops(dbg_line_buffer.items, padding_size, file_pos); |
| 2028 | 1967 | } |
| 2029 | 1968 | |
| 2030 | 1969 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| ... | ... | @@ -2116,47 +2055,6 @@ pub const File = struct { |
| 2116 | 2055 | self.global_symbols.items[sym_index].st_info = 0; |
| 2117 | 2056 | } |
| 2118 | 2057 | |
| 2119 | | const dbg_line_file_header_len = 5; // DW.LNS_set_file + ULEB128-fixed-4 file_index |
| 2120 | | const dbg_line_file_trailer_len = 9; // DW.LNE_end_sequence + 6-byte terminating NOP |
| 2121 | | |
| 2122 | | fn updateDbgLineFile(self: *Elf, src_file: *SrcFile) !void { |
| 2123 | | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 2124 | | const shdr = &self.sections.items[self.debug_line_section_index.?]; |
| 2125 | | const header_off = shdr.sh_offset + src_file.off; |
| 2126 | | { |
| 2127 | | var header: [dbg_line_file_header_len]u8 = undefined; |
| 2128 | | header[0] = DW.LNS_set_file; |
| 2129 | | // Once we support more than one source file, this will have the ability to be more |
| 2130 | | // than one possible value. |
| 2131 | | const file_index = 1; |
| 2132 | | leb128.writeUnsignedFixed(4, header[1..5], file_index); |
| 2133 | | try self.file.?.pwriteAll(&header, header_off); |
| 2134 | | } |
| 2135 | | { |
| 2136 | | const last_src_fn = src_file.fns.entries.items[src_file.fns.entries.items.len - 1].key; |
| 2137 | | const trailer_off = header_off + last_src_fn.off + last_src_fn.len; |
| 2138 | | const padding_to_next = blk: { |
| 2139 | | if (src_file.next) |next| { |
| 2140 | | break :blk next.off - (src_file.off + src_file.len); |
| 2141 | | } else { |
| 2142 | | // No need for padding after this one; we will add padding to it when a SrcFile |
| 2143 | | // is added after it. |
| 2144 | | break :blk 0; |
| 2145 | | } |
| 2146 | | }; |
| 2147 | | var trailer: [dbg_line_file_trailer_len]u8 = undefined; |
| 2148 | | |
| 2149 | | trailer[0] = DW.LNS_extended_op; |
| 2150 | | trailer[1] = 1; |
| 2151 | | trailer[2] = DW.LNE_end_sequence; |
| 2152 | | |
| 2153 | | trailer[3] = DW.LNS_extended_op; |
| 2154 | | leb128.writeUnsignedFixed(4, trailer[4..8], @intCast(u28, padding_to_next + 1)); |
| 2155 | | trailer[8] = DW.LNE_hi_user; |
| 2156 | | try self.file.?.pwriteAll(&trailer, trailer_off); |
| 2157 | | } |
| 2158 | | } |
| 2159 | | |
| 2160 | 2058 | fn writeProgHeader(self: *Elf, index: usize) !void { |
| 2161 | 2059 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 2162 | 2060 | const offset = self.program_headers.items[index].p_offset; |
| ... | ... | @@ -2366,6 +2264,10 @@ pub const File = struct { |
| 2366 | 2264 | return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1; |
| 2367 | 2265 | } |
| 2368 | 2266 | |
| 2267 | fn getRelocDbgFileIndex(self: Elf) usize { |
| 2268 | return self.getRelocDbgLineOff() + 5; |
| 2269 | } |
| 2270 | |
| 2369 | 2271 | fn dbgLineNeededHeaderBytes(self: Elf) u32 { |
| 2370 | 2272 | const directory_entry_format_count = 1; |
| 2371 | 2273 | const file_name_entry_format_count = 1; |
| ... | ... | @@ -2376,9 +2278,50 @@ pub const File = struct { |
| 2376 | 2278 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like |
| 2377 | 2279 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. |
| 2378 | 2280 | self.base.options.root_pkg.root_src_dir_path.len + |
| 2379 | | self.base.options.root_pkg.root_src_path.len * 2); |
| 2281 | self.base.options.root_pkg.root_src_path.len); |
| 2380 | 2282 | |
| 2381 | 2283 | } |
| 2284 | |
| 2285 | /// Writes to the file a buffer, followed by the specified number of bytes of NOPs. |
| 2286 | /// Asserts `padding_size >= 2` and less than 126,976 bytes (if this limit is ever |
| 2287 | /// reached, this function can be improved to make more than one pwritev call). |
| 2288 | fn pwriteWithNops(self: *Elf, buf: []const u8, padding_size: usize, offset: usize) !void { |
| 2289 | const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; |
| 2290 | const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0}; |
| 2291 | var vecs: [32]std.os.iovec_const = undefined; |
| 2292 | var vec_index: usize = 0; |
| 2293 | vecs[vec_index] = .{ |
| 2294 | .iov_base = buf.ptr, |
| 2295 | .iov_len = buf.len, |
| 2296 | }; |
| 2297 | vec_index += 1; |
| 2298 | var padding_left = padding_size; |
| 2299 | if (padding_left % 2 != 0) { |
| 2300 | vecs[vec_index] = .{ |
| 2301 | .iov_base = &three_byte_nop, |
| 2302 | .iov_len = three_byte_nop.len, |
| 2303 | }; |
| 2304 | vec_index += 1; |
| 2305 | padding_left -= three_byte_nop.len; |
| 2306 | } |
| 2307 | while (padding_left > page_of_nops.len) { |
| 2308 | vecs[vec_index] = .{ |
| 2309 | .iov_base = &page_of_nops, |
| 2310 | .iov_len = page_of_nops.len, |
| 2311 | }; |
| 2312 | vec_index += 1; |
| 2313 | padding_left -= page_of_nops.len; |
| 2314 | } |
| 2315 | if (padding_left > 0) { |
| 2316 | vecs[vec_index] = .{ |
| 2317 | .iov_base = &page_of_nops, |
| 2318 | .iov_len = padding_left, |
| 2319 | }; |
| 2320 | vec_index += 1; |
| 2321 | } |
| 2322 | try self.file.?.pwritevAll(vecs[0..vec_index], offset); |
| 2323 | } |
| 2324 | |
| 2382 | 2325 | }; |
| 2383 | 2326 | }; |
| 2384 | 2327 | |