Coff: implement threadlocal variables

This commit is contained in:
Jacob Young 2025-10-08 16:08:05 -04:00 committed by Andrew Kelley
parent b2bc6073c8
commit 2e31077fe0
13 changed files with 953 additions and 553 deletions

View file

@ -249,55 +249,6 @@ pub const OptionalHeader = extern struct {
pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
pub const DirectoryEntry = enum(u16) {
/// Export Directory
EXPORT = 0,
/// Import Directory
IMPORT = 1,
/// Resource Directory
RESOURCE = 2,
/// Exception Directory
EXCEPTION = 3,
/// Security Directory
SECURITY = 4,
/// Base Relocation Table
BASERELOC = 5,
/// Debug Directory
DEBUG = 6,
/// Architecture Specific Data
ARCHITECTURE = 7,
/// RVA of GP
GLOBALPTR = 8,
/// TLS Directory
TLS = 9,
/// Load Configuration Directory
LOAD_CONFIG = 10,
/// Bound Import Directory in headers
BOUND_IMPORT = 11,
/// Import Address Table
IAT = 12,
/// Delay Load Import Descriptors
DELAY_IMPORT = 13,
/// COM Runtime descriptor
COM_DESCRIPTOR = 14,
_,
};
pub const ImageDataDirectory = extern struct { pub const ImageDataDirectory = extern struct {
virtual_address: u32, virtual_address: u32,
size: u32, size: u32,
@ -1054,9 +1005,9 @@ pub const Coff = struct {
assert(self.is_image); assert(self.is_image);
const data_dirs = self.getDataDirectories(); const data_dirs = self.getDataDirectories();
if (@intFromEnum(DirectoryEntry.DEBUG) >= data_dirs.len) return null; if (@intFromEnum(IMAGE.DIRECTORY_ENTRY.DEBUG) >= data_dirs.len) return null;
const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)]; const debug_dir = data_dirs[@intFromEnum(IMAGE.DIRECTORY_ENTRY.DEBUG)];
var reader: std.Io.Reader = .fixed(self.data); var reader: std.Io.Reader = .fixed(self.data);
if (self.is_loaded) { if (self.is_loaded) {
@ -1400,6 +1351,44 @@ pub const Relocation = extern struct {
}; };
pub const IMAGE = struct { pub const IMAGE = struct {
pub const DIRECTORY_ENTRY = enum(u32) {
/// Export Directory
EXPORT = 0,
/// Import Directory
IMPORT = 1,
/// Resource Directory
RESOURCE = 2,
/// Exception Directory
EXCEPTION = 3,
/// Security Directory
SECURITY = 4,
/// Base Relocation Table
BASERELOC = 5,
/// Debug Directory
DEBUG = 6,
/// Architecture Specific Data
ARCHITECTURE = 7,
/// RVA of GP
GLOBALPTR = 8,
/// TLS Directory
TLS = 9,
/// Load Configuration Directory
LOAD_CONFIG = 10,
/// Bound Import Directory in headers
BOUND_IMPORT = 11,
/// Import Address Table
IAT = 12,
/// Delay Load Import Descriptors
DELAY_IMPORT = 13,
/// COM Runtime descriptor
COM_DESCRIPTOR = 14,
/// must be zero
RESERVED = 15,
_,
pub const len = @typeInfo(IMAGE.DIRECTORY_ENTRY).@"enum".fields.len;
};
pub const FILE = struct { pub const FILE = struct {
/// Machine Types /// Machine Types
/// The Machine field has one of the following values, which specify the CPU type. /// The Machine field has one of the following values, which specify the CPU type.

View file

@ -468,10 +468,6 @@ const use_trap_panic = switch (builtin.zig_backend) {
.stage2_wasm, .stage2_wasm,
.stage2_x86, .stage2_x86,
=> true, => true,
.stage2_x86_64 => switch (builtin.target.ofmt) {
.elf, .macho => false,
else => true,
},
else => false, else => false,
}; };
@ -484,22 +480,6 @@ pub fn defaultPanic(
if (use_trap_panic) @trap(); if (use_trap_panic) @trap();
switch (builtin.zig_backend) {
.stage2_aarch64,
.stage2_arm,
.stage2_powerpc,
.stage2_riscv64,
.stage2_spirv,
.stage2_wasm,
.stage2_x86,
=> @trap(),
.stage2_x86_64 => switch (builtin.target.ofmt) {
.elf, .macho => {},
else => @trap(),
},
else => {},
}
switch (builtin.os.tag) { switch (builtin.os.tag) {
.freestanding, .other => { .freestanding, .other => {
@trap(); @trap();

View file

@ -28,6 +28,8 @@ test isNan {
} }
test isSignalNan { test isSignalNan {
if (builtin.zig_backend == .stage2_x86_64 and builtin.object_format == .coff and builtin.abi != .gnu) return error.SkipZigTest;
inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
// TODO: Signalling NaN values get converted to quiet NaN values in // TODO: Signalling NaN values get converted to quiet NaN values in
// some cases where they shouldn't such that this can fail. // some cases where they shouldn't such that this can fail.

View file

@ -120,12 +120,6 @@ const Value = extern struct {
} }
pub fn format(value: Value, writer: *std.Io.Writer) std.Io.Writer.Error!void { pub fn format(value: Value, writer: *std.Io.Writer) std.Io.Writer.Error!void {
// Work around x86_64 backend limitation.
if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) {
try writer.writeAll("(unknown)");
return;
}
switch (value.td.kind) { switch (value.td.kind) {
.integer => { .integer => {
if (value.td.isSigned()) { if (value.td.isSigned()) {
@ -624,10 +618,11 @@ fn exportHandler(
handler: anytype, handler: anytype,
comptime sym_name: []const u8, comptime sym_name: []const u8,
) void { ) void {
// Work around x86_64 backend limitation. @export(handler, .{
const linkage = if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) .internal else .weak; .name = "__ubsan_handle_" ++ sym_name,
const N = "__ubsan_handle_" ++ sym_name; .linkage = .weak,
@export(handler, .{ .name = N, .linkage = linkage, .visibility = if (linkage == .internal) .default else .hidden }); .visibility = .hidden,
});
} }
fn exportHandlerWithAbort( fn exportHandlerWithAbort(
@ -635,16 +630,16 @@ fn exportHandlerWithAbort(
abort_handler: anytype, abort_handler: anytype,
comptime sym_name: []const u8, comptime sym_name: []const u8,
) void { ) void {
// Work around x86_64 backend limitation. @export(handler, .{
const linkage = if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) .internal else .weak; .name = "__ubsan_handle_" ++ sym_name,
{ .linkage = .weak,
const N = "__ubsan_handle_" ++ sym_name; .visibility = .hidden,
@export(handler, .{ .name = N, .linkage = linkage, .visibility = if (linkage == .internal) .default else .hidden }); });
} @export(abort_handler, .{
{ .name = "__ubsan_handle_" ++ sym_name ++ "_abort",
const N = "__ubsan_handle_" ++ sym_name ++ "_abort"; .linkage = .weak,
@export(abort_handler, .{ .name = N, .linkage = linkage, .visibility = if (linkage == .internal) .default else .hidden }); .visibility = .hidden,
} });
} }
const can_build_ubsan = switch (builtin.zig_backend) { const can_build_ubsan = switch (builtin.zig_backend) {

View file

@ -1985,7 +1985,7 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
switch (target_util.zigBackend(target, use_llvm)) { switch (target_util.zigBackend(target, use_llvm)) {
else => {}, else => {},
.stage2_aarch64, .stage2_x86_64 => if (target.ofmt == .coff) { .stage2_aarch64, .stage2_x86_64 => if (target.ofmt == .coff) {
break :s if (is_exe_or_dyn_lib) .dyn_lib else .zcu; break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu;
}, },
} }
if (options.config.use_new_linker) break :s .zcu; if (options.config.use_new_linker) break :s .zcu;

View file

@ -173685,7 +173685,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; const ty_nav = air_datas[@intFromEnum(inst)].ty_nav;
const nav = ip.getNav(ty_nav.nav); const nav = ip.getNav(ty_nav.nav);
const is_threadlocal = zcu.comp.config.any_non_single_threaded and nav.isThreadlocal(ip); const is_threadlocal = zcu.comp.config.any_non_single_threaded and nav.isThreadlocal(ip);
if (is_threadlocal) if (cg.mod.pic) { if (is_threadlocal) if (cg.target.ofmt == .coff or cg.mod.pic) {
try cg.spillRegisters(&.{ .rdi, .rax }); try cg.spillRegisters(&.{ .rdi, .rax });
} else { } else {
try cg.spillRegisters(&.{.rax}); try cg.spillRegisters(&.{.rax});

View file

@ -386,6 +386,82 @@ pub fn emitMir(emit: *Emit) Error!void {
}, emit.lower.target), &.{}); }, emit.lower.target), &.{});
}, },
else => unreachable, else => unreachable,
} else if (emit.bin_file.cast(.coff2)) |coff| {
switch (emit.lower.target.cpu.arch) {
else => unreachable,
.x86 => {
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .eax },
.{ .mem = .initSib(.qword, .{
.base = .{ .reg = .fs },
.disp = 4 * 11,
}) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .edi },
.{ .mem = .initSib(.dword, .{}) },
}, emit.lower.target), &.{.{
.op_index = 1,
.target = .{
.index = @intFromEnum(
try coff.globalSymbol("__tls_index", null),
),
.is_extern = false,
.type = .symbol,
},
}});
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .eax },
.{ .mem = .initSib(.dword, .{
.base = .{ .reg = .eax },
.scale_index = .{ .index = .edi, .scale = 4 },
}) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(lowered_inst.ops[1].mem.sib.ptr_size, .{
.base = .{ .reg = .eax },
.disp = std.math.minInt(i32),
}) },
}, emit.lower.target), reloc_info);
},
.x86_64 => {
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .rax },
.{ .mem = .initSib(.qword, .{
.base = .{ .reg = .gs },
.disp = 8 * 11,
}) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .edi },
.{ .mem = .initRip(.dword, 0) },
}, emit.lower.target), &.{.{
.op_index = 1,
.target = .{
.index = @intFromEnum(
try coff.globalSymbol("_tls_index", null),
),
.is_extern = false,
.type = .symbol,
},
}});
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .rax },
.{ .mem = .initSib(.qword, .{
.base = .{ .reg = .rax },
.scale_index = .{ .index = .rdi, .scale = 8 },
}) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(lowered_inst.ops[1].mem.sib.ptr_size, .{
.base = .{ .reg = .rax },
.disp = std.math.minInt(i32),
}) },
}, emit.lower.target), reloc_info);
},
}
} else return emit.fail("TODO implement relocs for {s}", .{ } else return emit.fail("TODO implement relocs for {s}", .{
@tagName(emit.bin_file.tag), @tagName(emit.bin_file.tag),
}); });
@ -870,7 +946,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
.symbolnum = @intCast(reloc.target.index), .symbolnum = @intCast(reloc.target.index),
}, },
}); });
} else return emit.fail("TODO implement {s} reloc for {s}", .{ } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
@enumFromInt(emit.atom_index),
end_offset - 4,
@enumFromInt(reloc.target.index),
reloc.off,
.{ .AMD64 = .SECREL },
) else return emit.fail("TODO implement {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag), @tagName(reloc.target.type), @tagName(emit.bin_file.tag),
}), }),
}; };

View file

@ -9,7 +9,9 @@ strings: std.HashMapUnmanaged(
std.hash_map.default_max_load_percentage, std.hash_map.default_max_load_percentage,
), ),
string_bytes: std.ArrayList(u8), string_bytes: std.ArrayList(u8),
section_table: std.ArrayList(Symbol.Index), image_section_table: std.ArrayList(Symbol.Index),
pseudo_section_table: std.AutoArrayHashMapUnmanaged(String, Symbol.Index),
object_section_table: std.AutoArrayHashMapUnmanaged(String, Symbol.Index),
symbol_table: std.ArrayList(Symbol), symbol_table: std.ArrayList(Symbol),
globals: std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index), globals: std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index),
global_pending_index: u32, global_pending_index: u32,
@ -24,8 +26,6 @@ pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
src_loc: Zcu.LazySrcLoc, src_loc: Zcu.LazySrcLoc,
}), }),
relocs: std.ArrayList(Reloc), relocs: std.ArrayList(Reloc),
/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
entry_hack: Symbol.Index,
pub const default_file_alignment: u16 = 0x200; pub const default_file_alignment: u16 = 0x200;
pub const default_size_of_stack_reserve: u32 = 0x1000000; pub const default_size_of_stack_reserve: u32 = 0x1000000;
@ -102,17 +102,45 @@ pub const Node = union(enum) {
optional_header, optional_header,
data_directories, data_directories,
section_table, section_table,
section: Symbol.Index, image_section: Symbol.Index,
import_directory_table, import_directory_table,
import_lookup_table: ImportTable.Index, import_lookup_table: ImportTable.Index,
import_address_table: ImportTable.Index, import_address_table: ImportTable.Index,
import_hint_name_table: ImportTable.Index, import_hint_name_table: ImportTable.Index,
pseudo_section: PseudoSectionMapIndex,
object_section: ObjectSectionMapIndex,
global: GlobalMapIndex, global: GlobalMapIndex,
nav: NavMapIndex, nav: NavMapIndex,
uav: UavMapIndex, uav: UavMapIndex,
lazy_code: LazyMapRef.Index(.code), lazy_code: LazyMapRef.Index(.code),
lazy_const_data: LazyMapRef.Index(.const_data), lazy_const_data: LazyMapRef.Index(.const_data),
pub const PseudoSectionMapIndex = enum(u32) {
_,
pub fn name(psmi: PseudoSectionMapIndex, coff: *const Coff) String {
return coff.pseudo_section_table.keys()[@intFromEnum(psmi)];
}
pub fn symbol(psmi: PseudoSectionMapIndex, coff: *const Coff) Symbol.Index {
return coff.pseudo_section_table.values()[@intFromEnum(psmi)];
}
};
pub const ObjectSectionMapIndex = enum(u32) {
_,
pub fn name(osmi: ObjectSectionMapIndex, coff: *const Coff) String {
return coff.object_section_table.keys()[@intFromEnum(osmi)];
}
pub fn symbol(osmi: ObjectSectionMapIndex, coff: *const Coff) Symbol.Index {
return coff.object_section_table.values()[@intFromEnum(osmi)];
}
};
pub const GlobalMapIndex = enum(u32) { pub const GlobalMapIndex = enum(u32) {
_, _,
@ -204,27 +232,6 @@ pub const Node = union(enum) {
} }
}; };
pub const DataDirectory = enum {
export_table,
import_table,
resorce_table,
exception_table,
certificate_table,
base_relocation_table,
debug,
architecture,
global_ptr,
tls_table,
load_config_table,
bound_import,
import_address_table,
delay_import_descriptor,
clr_runtime_header,
reserved,
pub const len = @typeInfo(DataDirectory).@"enum".fields.len;
};
pub const ImportTable = struct { pub const ImportTable = struct {
ni: MappedFile.Node.Index, ni: MappedFile.Node.Index,
entries: std.AutoArrayHashMapUnmanaged(void, Entry), entries: std.AutoArrayHashMapUnmanaged(void, Entry),
@ -264,9 +271,18 @@ pub const ImportTable = struct {
}; };
pub const String = enum(u32) { pub const String = enum(u32) {
@".data" = 0,
@".idata" = 6,
@".rdata" = 13,
@".text" = 20,
@".tls$" = 26,
_, _,
pub const Optional = enum(u32) { pub const Optional = enum(u32) {
@".data" = @intFromEnum(String.@".data"),
@".rdata" = @intFromEnum(String.@".rdata"),
@".text" = @intFromEnum(String.@".text"),
@".tls$" = @intFromEnum(String.@".tls$"),
none = std.math.maxInt(u32), none = std.math.maxInt(u32),
_, _,
@ -318,7 +334,7 @@ pub const Symbol = struct {
} }
pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index { pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
return coff.section_table.items[sn.toIndex()]; return coff.image_section_table.items[sn.toIndex()];
} }
pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader { pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
@ -329,7 +345,6 @@ pub const Symbol = struct {
pub const Index = enum(u32) { pub const Index = enum(u32) {
null, null,
data, data,
idata,
rdata, rdata,
text, text,
_, _,
@ -349,10 +364,6 @@ pub const Symbol = struct {
pub fn flushMoved(si: Symbol.Index, coff: *Coff) void { pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
const sym = si.get(coff); const sym = si.get(coff);
sym.rva = coff.computeNodeRva(sym.ni); sym.rva = coff.computeNodeRva(sym.ni);
if (si == coff.entry_hack) {
@branchHint(.unlikely);
coff.targetStore(&coff.optionalHeaderStandardPtr().address_of_entry_point, sym.rva);
}
si.applyLocationRelocs(coff); si.applyLocationRelocs(coff);
si.applyTargetRelocs(coff); si.applyTargetRelocs(coff);
} }
@ -493,6 +504,12 @@ pub const Reloc = extern struct {
@intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))), @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))),
target_endian, target_endian,
), ),
.SECREL => std.mem.writeInt(
u32,
loc_slice[0..4],
coff.computeNodeSectionOffset(target_sym.ni),
target_endian,
),
}, },
.I386 => switch (reloc.type.I386) { .I386 => switch (reloc.type.I386) {
else => |kind| @panic(@tagName(kind)), else => |kind| @panic(@tagName(kind)),
@ -527,6 +544,12 @@ pub const Reloc = extern struct {
@intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))), @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
target_endian, target_endian,
), ),
.SECREL => std.mem.writeInt(
u32,
loc_slice[0..4],
coff.computeNodeSectionOffset(target_sym.ni),
target_endian,
),
}, },
} }
} }
@ -634,7 +657,9 @@ fn create(
}, },
.strings = .empty, .strings = .empty,
.string_bytes = .empty, .string_bytes = .empty,
.section_table = .empty, .image_section_table = .empty,
.pseudo_section_table = .empty,
.object_section_table = .empty,
.symbol_table = .empty, .symbol_table = .empty,
.globals = .empty, .globals = .empty,
.global_pending_index = 0, .global_pending_index = 0,
@ -646,10 +671,17 @@ fn create(
}), }),
.pending_uavs = .empty, .pending_uavs = .empty,
.relocs = .empty, .relocs = .empty,
.entry_hack = .null,
}; };
errdefer coff.deinit(); errdefer coff.deinit();
{
const strings = std.enums.values(String);
try coff.strings.ensureTotalCapacityContext(comp.gpa, @intCast(strings.len), .{
.bytes = &coff.string_bytes,
});
for (strings) |string| assert(try coff.getOrPutString(@tagName(string)) == string);
}
try coff.initHeaders( try coff.initHeaders(
is_image, is_image,
machine, machine,
@ -669,7 +701,9 @@ pub fn deinit(coff: *Coff) void {
coff.import_table.entries.deinit(gpa); coff.import_table.entries.deinit(gpa);
coff.strings.deinit(gpa); coff.strings.deinit(gpa);
coff.string_bytes.deinit(gpa); coff.string_bytes.deinit(gpa);
coff.section_table.deinit(gpa); coff.image_section_table.deinit(gpa);
coff.pseudo_section_table.deinit(gpa);
coff.object_section_table.deinit(gpa);
coff.symbol_table.deinit(gpa); coff.symbol_table.deinit(gpa);
coff.globals.deinit(gpa); coff.globals.deinit(gpa);
coff.navs.deinit(gpa); coff.navs.deinit(gpa);
@ -692,19 +726,21 @@ fn initHeaders(
) !void { ) !void {
const comp = coff.base.comp; const comp = coff.base.comp;
const gpa = comp.gpa; const gpa = comp.gpa;
const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
const target_endian = coff.targetEndian(); const target_endian = coff.targetEndian();
const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
const optional_header_size: u16 = if (is_image) switch (magic) { const optional_header_size: u16 = if (is_image) switch (magic) {
_ => unreachable, _ => unreachable,
inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))), inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
} else 0; } else 0;
const data_directories_size: u16 = if (is_image) const data_directories_size: u16 = if (is_image)
@sizeOf(std.coff.ImageDataDirectory) * DataDirectory.len @sizeOf(std.coff.ImageDataDirectory) * std.coff.IMAGE.DIRECTORY_ENTRY.len
else else
0; 0;
try coff.nodes.ensureTotalCapacity(gpa, Node.known_count); const expected_nodes_len = Node.known_count + 6 +
@as(usize, @intFromBool(comp.config.any_non_single_threaded)) * 2;
try coff.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
coff.nodes.appendAssumeCapacity(.file); coff.nodes.appendAssumeCapacity(.file);
const header_ni = Node.known.header; const header_ni = Node.known.header;
@ -762,8 +798,9 @@ fn initHeaders(
.fixed = true, .fixed = true,
})); }));
coff.nodes.appendAssumeCapacity(.optional_header); coff.nodes.appendAssumeCapacity(.optional_header);
if (is_image) {
coff.targetStore(&coff.optionalHeaderStandardPtr().magic, magic); coff.targetStore(&coff.optionalHeaderStandardPtr().magic, magic);
if (is_image) switch (coff.optionalHeaderPtr()) { switch (coff.optionalHeaderPtr()) {
.PE32 => |optional_header| { .PE32 => |optional_header| {
optional_header.* = .{ optional_header.* = .{
.standard = .{ .standard = .{
@ -809,7 +846,7 @@ fn initHeaders(
.size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_reserve = default_size_of_heap_reserve,
.size_of_heap_commit = default_size_of_heap_commit, .size_of_heap_commit = default_size_of_heap_commit,
.loader_flags = 0, .loader_flags = 0,
.number_of_rva_and_sizes = DataDirectory.len, .number_of_rva_and_sizes = std.coff.IMAGE.DIRECTORY_ENTRY.len,
}; };
if (target_endian != native_endian) if (target_endian != native_endian)
std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header); std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header);
@ -858,12 +895,13 @@ fn initHeaders(
.size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_reserve = default_size_of_heap_reserve,
.size_of_heap_commit = default_size_of_heap_commit, .size_of_heap_commit = default_size_of_heap_commit,
.loader_flags = 0, .loader_flags = 0,
.number_of_rva_and_sizes = DataDirectory.len, .number_of_rva_and_sizes = std.coff.IMAGE.DIRECTORY_ENTRY.len,
}; };
if (target_endian != native_endian) if (target_endian != native_endian)
std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", optional_header); std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", optional_header);
}, },
}; }
}
const data_directories_ni = Node.known.data_directories; const data_directories_ni = Node.known.data_directories;
assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{ assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
@ -875,8 +913,10 @@ fn initHeaders(
{ {
const data_directories = coff.dataDirectorySlice(); const data_directories = coff.dataDirectorySlice();
@memset(data_directories, .{ .virtual_address = 0, .size = 0 }); @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
if (target_endian != native_endian) if (target_endian != native_endian) std.mem.byteSwapAllFields(
std.mem.byteSwapAllFields([DataDirectory.len]std.coff.ImageDataDirectory, data_directories); [std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory,
data_directories,
);
} }
const section_table_ni = Node.known.section_table; const section_table_ni = Node.known.section_table;
@ -902,10 +942,6 @@ fn initHeaders(
.MEM_READ = true, .MEM_READ = true,
.MEM_WRITE = true, .MEM_WRITE = true,
}) == .data); }) == .data);
assert(try coff.addSection(".idata", .{
.CNT_INITIALIZED_DATA = true,
.MEM_READ = true,
}) == .idata);
assert(try coff.addSection(".rdata", .{ assert(try coff.addSection(".rdata", .{
.CNT_INITIALIZED_DATA = true, .CNT_INITIALIZED_DATA = true,
.MEM_READ = true, .MEM_READ = true,
@ -915,13 +951,26 @@ fn initHeaders(
.MEM_EXECUTE = true, .MEM_EXECUTE = true,
.MEM_READ = true, .MEM_READ = true,
}) == .text); }) == .text);
coff.import_table.ni = try coff.mf.addLastChildNode( coff.import_table.ni = try coff.mf.addLastChildNode(
gpa, gpa,
Symbol.Index.idata.node(coff), (try coff.objectSectionMapIndex(
.{ .alignment = .@"4" }, .@".idata",
coff.mf.flags.block_size,
.{ .read = true },
)).symbol(coff).node(coff),
.{ .alignment = .@"4", .moved = true },
); );
coff.nodes.appendAssumeCapacity(.import_directory_table); coff.nodes.appendAssumeCapacity(.import_directory_table);
assert(coff.symbol_table.items.len == Symbol.Index.known_count);
// While tls variables allocated at runtime are writable, the template itself is not
if (comp.config.any_non_single_threaded) _ = try coff.objectSectionMapIndex(
.@".tls$",
coff.mf.flags.block_size,
.{ .read = true },
);
assert(coff.nodes.len == expected_nodes_len);
} }
fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node { fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
@ -938,8 +987,10 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
.data_directories, .data_directories,
.section_table, .section_table,
=> unreachable, => unreachable,
.section => |si| si, .image_section => |si| si,
.import_directory_table => unreachable, .import_directory_table => break :parent_rva coff.targetLoad(
&coff.dataDirectoryPtr(.IMPORT).virtual_address,
),
.import_lookup_table => |import_index| break :parent_rva coff.targetLoad( .import_lookup_table => |import_index| break :parent_rva coff.targetLoad(
&coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva, &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
), ),
@ -949,13 +1000,34 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
.import_hint_name_table => |import_index| break :parent_rva coff.targetLoad( .import_hint_name_table => |import_index| break :parent_rva coff.targetLoad(
&coff.importDirectoryEntryPtr(import_index).name_rva, &coff.importDirectoryEntryPtr(import_index).name_rva,
), ),
inline .global, .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(coff), inline .pseudo_section,
.object_section,
.global,
.nav,
.uav,
.lazy_code,
.lazy_const_data,
=> |mi| mi.symbol(coff),
}; };
break :parent_rva parent_si.get(coff).rva; break :parent_rva parent_si.get(coff).rva;
}; };
const offset, _ = ni.location(&coff.mf).resolve(&coff.mf); const offset, _ = ni.location(&coff.mf).resolve(&coff.mf);
return @intCast(parent_rva + offset); return @intCast(parent_rva + offset);
} }
fn computeNodeSectionOffset(coff: *Coff, ni: MappedFile.Node.Index) u32 {
var section_offset: u32 = 0;
var parent_ni = ni;
while (true) {
const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
section_offset += @intCast(offset);
parent_ni = parent_ni.parent(&coff.mf);
switch (coff.getNode(parent_ni)) {
else => unreachable,
.image_section, .pseudo_section => return section_offset,
.object_section => {},
}
}
}
pub inline fn targetEndian(_: *const Coff) std.builtin.Endian { pub inline fn targetEndian(_: *const Coff) std.builtin.Endian {
return .little; return .little;
@ -1021,11 +1093,16 @@ pub fn optionalHeaderField(
}; };
} }
pub fn dataDirectorySlice(coff: *Coff) *[DataDirectory.len]std.coff.ImageDataDirectory { pub fn dataDirectorySlice(
coff: *Coff,
) *[std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory {
return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf))); return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
} }
pub fn dataDirectoryPtr(coff: *Coff, data_directory: DataDirectory) *std.coff.ImageDataDirectory { pub fn dataDirectoryPtr(
return &coff.dataDirectorySlice()[@intFromEnum(data_directory)]; coff: *Coff,
entry: std.coff.IMAGE.DIRECTORY_ENTRY,
) *std.coff.ImageDataDirectory {
return &coff.dataDirectorySlice()[@intFromEnum(entry)];
} }
pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader { pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
@ -1060,13 +1137,22 @@ fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
} }
fn getOrPutString(coff: *Coff, string: []const u8) !String { fn getOrPutString(coff: *Coff, string: []const u8) !String {
try coff.ensureUnusedStringCapacity(string.len);
return coff.getOrPutStringAssumeCapacity(string);
}
fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
return (try coff.getOrPutString(string orelse return .none)).toOptional();
}
fn ensureUnusedStringCapacity(coff: *Coff, len: usize) !void {
const gpa = coff.base.comp.gpa; const gpa = coff.base.comp.gpa;
try coff.string_bytes.ensureUnusedCapacity(gpa, string.len + 1); try coff.strings.ensureUnusedCapacityContext(gpa, 1, .{ .bytes = &coff.string_bytes });
const gop = try coff.strings.getOrPutContextAdapted( try coff.string_bytes.ensureUnusedCapacity(gpa, len + 1);
gpa, }
fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
const gop = coff.strings.getOrPutAssumeCapacityAdapted(
string, string,
std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes }, std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
.{ .bytes = &coff.string_bytes },
); );
if (!gop.found_existing) { if (!gop.found_existing) {
gop.key_ptr.* = @intCast(coff.string_bytes.items.len); gop.key_ptr.* = @intCast(coff.string_bytes.items.len);
@ -1077,10 +1163,6 @@ fn getOrPutString(coff: *Coff, string: []const u8) !String {
return @enumFromInt(gop.key_ptr.*); return @enumFromInt(gop.key_ptr.*);
} }
fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
return (try coff.getOrPutString(string orelse return .none)).toOptional();
}
pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index { pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
const gpa = coff.base.comp.gpa; const gpa = coff.base.comp.gpa;
try coff.symbol_table.ensureUnusedCapacity(gpa, 1); try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
@ -1095,6 +1177,43 @@ pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbo
return sym_gop.value_ptr.*; return sym_gop.value_ptr.*;
} }
fn navSection(
coff: *Coff,
zcu: *Zcu,
nav_fr: @FieldType(@FieldType(InternPool.Nav, "status"), "fully_resolved"),
) !Symbol.Index {
const ip = &zcu.intern_pool;
const default: String, const attributes: ObjectSectionAttributes =
switch (ip.indexToKey(nav_fr.val)) {
else => .{ .@".rdata", .{ .read = true } },
.variable => |variable| if (variable.is_threadlocal and
coff.base.comp.config.any_non_single_threaded)
.{ .@".tls$", .{ .read = true, .write = true } }
else
.{ .@".data", .{ .read = true, .write = true } },
.@"extern" => |@"extern"| if (@"extern".is_threadlocal and
coff.base.comp.config.any_non_single_threaded)
.{ .@".tls$", .{ .read = true, .write = true } }
else if (ip.isFunctionType(@"extern".ty))
.{ .@".text", .{ .read = true, .execute = true } }
else if (@"extern".is_const)
.{ .@".rdata", .{ .read = true } }
else
.{ .@".data", .{ .read = true, .write = true } },
.func => .{ .@".text", .{ .read = true, .execute = true } },
};
return (try coff.objectSectionMapIndex(
(try coff.getOrPutOptionalString(nav_fr.@"linksection".toSlice(ip))).unwrap() orelse default,
switch (nav_fr.@"linksection") {
.none => coff.mf.flags.block_size,
else => switch (nav_fr.alignment) {
.none => Type.fromInterned(ip.typeOf(nav_fr.val)).abiAlignment(zcu),
else => |alignment| alignment,
}.toStdMem(),
},
attributes,
)).symbol(coff);
}
fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
const gpa = zcu.gpa; const gpa = zcu.gpa;
try coff.symbol_table.ensureUnusedCapacity(gpa, 1); try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
@ -1171,7 +1290,7 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.
fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index { fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
const gpa = coff.base.comp.gpa; const gpa = coff.base.comp.gpa;
try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.nodes.ensureUnusedCapacity(gpa, 1);
try coff.section_table.ensureUnusedCapacity(gpa, 1); try coff.image_section_table.ensureUnusedCapacity(gpa, 1);
try coff.symbol_table.ensureUnusedCapacity(gpa, 1); try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
const coff_header = coff.headerPtr(); const coff_header = coff.headerPtr();
@ -1189,13 +1308,13 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
.bubbles_moved = false, .bubbles_moved = false,
}); });
const si = coff.addSymbolAssumeCapacity(); const si = coff.addSymbolAssumeCapacity();
coff.section_table.appendAssumeCapacity(si); coff.image_section_table.appendAssumeCapacity(si);
coff.nodes.appendAssumeCapacity(.{ .section = si }); coff.nodes.appendAssumeCapacity(.{ .image_section = si });
const section_table = coff.sectionTableSlice(); const section_table = coff.sectionTableSlice();
const virtual_size = coff.optionalHeaderField(.section_alignment); const virtual_size = coff.optionalHeaderField(.section_alignment);
const rva: u32 = switch (section_index) { const rva: u32 = switch (section_index) {
0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]), 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
else => coff.section_table.items[section_index - 1].get(coff).rva + else => coff.image_section_table.items[section_index - 1].get(coff).rva +
coff.targetLoad(&section_table[section_index - 1].virtual_size), coff.targetLoad(&section_table[section_index - 1].virtual_size),
}; };
{ {
@ -1230,6 +1349,99 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
return si; return si;
} }
const ObjectSectionAttributes = packed struct {
read: bool = false,
write: bool = false,
execute: bool = false,
shared: bool = false,
nopage: bool = false,
nocache: bool = false,
discard: bool = false,
remove: bool = false,
};
fn pseudoSectionMapIndex(
coff: *Coff,
name: String,
alignment: std.mem.Alignment,
attributes: ObjectSectionAttributes,
) !Node.PseudoSectionMapIndex {
const gpa = coff.base.comp.gpa;
const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);
const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);
if (!pseudo_section_gop.found_existing) {
const parent: Symbol.Index = if (attributes.execute)
.text
else if (attributes.write)
.data
else
.rdata;
try coff.nodes.ensureUnusedCapacity(gpa, 1);
try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment });
const si = coff.addSymbolAssumeCapacity();
pseudo_section_gop.value_ptr.* = si;
const sym = si.get(coff);
sym.ni = ni;
sym.rva = coff.computeNodeRva(ni);
sym.section_number = parent.get(coff).section_number;
assert(sym.loc_relocs == .none);
sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
coff.nodes.appendAssumeCapacity(.{ .pseudo_section = psmi });
}
return psmi;
}
fn objectSectionMapIndex(
coff: *Coff,
name: String,
alignment: std.mem.Alignment,
attributes: ObjectSectionAttributes,
) !Node.ObjectSectionMapIndex {
const gpa = coff.base.comp.gpa;
const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
if (!object_section_gop.found_existing) {
try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);
const name_slice = name.toSlice(coff);
const parent = (try coff.pseudoSectionMapIndex(coff.getOrPutStringAssumeCapacity(
name_slice[0 .. std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len],
), alignment, attributes)).symbol(coff);
try coff.nodes.ensureUnusedCapacity(gpa, 1);
try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
const parent_ni = parent.node(coff);
var prev_ni: MappedFile.Node.Index = .none;
var next_it = parent_ni.children(&coff.mf);
while (next_it.next()) |next_ni| switch (std.mem.order(
u8,
name_slice,
coff.getNode(next_ni).object_section.name(coff).toSlice(coff),
)) {
.lt => break,
.eq => unreachable,
.gt => prev_ni = next_ni,
};
const ni = switch (prev_ni) {
.none => try coff.mf.addFirstChildNode(gpa, parent_ni, .{
.alignment = alignment,
.fixed = true,
}),
else => try coff.mf.addNodeAfter(gpa, prev_ni, .{
.alignment = alignment,
.fixed = true,
}),
};
const si = coff.addSymbolAssumeCapacity();
object_section_gop.value_ptr.* = si;
const sym = si.get(coff);
sym.ni = ni;
sym.rva = coff.computeNodeRva(ni);
sym.section_number = parent.get(coff).section_number;
assert(sym.loc_relocs == .none);
sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
}
return osmi;
}
pub fn addReloc( pub fn addReloc(
coff: *Coff, coff: *Coff,
loc_si: Symbol.Index, loc_si: Symbol.Index,
@ -1279,37 +1491,38 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
const nav_val = nav.status.fully_resolved.val; const nav_val = nav.status.fully_resolved.val;
const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) { const nav_init = switch (ip.indexToKey(nav_val)) {
else => .{ nav_val, false }, else => nav_val,
.variable => |variable| .{ variable.init, variable.is_threadlocal }, .variable => |variable| variable.init,
.@"extern" => return, .@"extern", .func => .none,
.func => .{ .none, false },
}; };
if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return; if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
const nmi = try coff.navMapIndex(zcu, nav_index); const nmi = try coff.navMapIndex(zcu, nav_index);
const si = nmi.symbol(coff); const si = nmi.symbol(coff);
const ni = ni: { const ni = ni: {
const sym = si.get(coff); switch (si.get(coff).ni) {
switch (sym.ni) {
.none => { .none => {
const sec_si = try coff.navSection(zcu, nav.status.fully_resolved);
try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.nodes.ensureUnusedCapacity(gpa, 1);
_ = is_threadlocal; const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{
const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
.alignment = pt.navAlignment(nav_index).toStdMem(), .alignment = pt.navAlignment(nav_index).toStdMem(),
.moved = true, .moved = true,
}); });
coff.nodes.appendAssumeCapacity(.{ .nav = nmi }); coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
const sym = si.get(coff);
sym.ni = ni; sym.ni = ni;
sym.section_number = Symbol.Index.data.get(coff).section_number; sym.section_number = sec_si.get(coff).section_number;
}, },
else => si.deleteLocationRelocs(coff), else => si.deleteLocationRelocs(coff),
} }
const sym = si.get(coff);
assert(sym.loc_relocs == .none); assert(sym.loc_relocs == .none);
sym.loc_relocs = @enumFromInt(coff.relocs.items.len); sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
break :ni sym.ni; break :ni sym.ni;
}; };
{
var nw: MappedFile.Node.Writer = undefined; var nw: MappedFile.Node.Writer = undefined;
ni.writer(&coff.mf, gpa, &nw); ni.writer(&coff.mf, gpa, &nw);
defer nw.deinit(); defer nw.deinit();
@ -1328,6 +1541,25 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
si.applyLocationRelocs(coff); si.applyLocationRelocs(coff);
} }
if (nav.status.fully_resolved.@"linksection".unwrap()) |_| {
try ni.resize(&coff.mf, gpa, si.get(coff).size);
var parent_ni = ni;
while (true) {
parent_ni = parent_ni.parent(&coff.mf);
switch (coff.getNode(parent_ni)) {
else => unreachable,
.image_section, .pseudo_section => break,
.object_section => {
var child_it = parent_ni.reverseChildren(&coff.mf);
const last_offset, const last_size =
child_it.next().?.location(&coff.mf).resolve(&coff.mf);
try parent_ni.resize(&coff.mf, gpa, last_offset + last_size);
},
}
}
}
}
pub fn lowerUav( pub fn lowerUav(
coff: *Coff, coff: *Coff,
pt: Zcu.PerThread, pt: Zcu.PerThread,
@ -1394,13 +1626,13 @@ fn updateFuncInner(
const si = nmi.symbol(coff); const si = nmi.symbol(coff);
log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si }); log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
const ni = ni: { const ni = ni: {
const sym = si.get(coff); switch (si.get(coff).ni) {
switch (sym.ni) {
.none => { .none => {
const sec_si = try coff.navSection(zcu, nav.status.fully_resolved);
try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.nodes.ensureUnusedCapacity(gpa, 1);
const mod = zcu.navFileScope(func.owner_nav).mod.?; const mod = zcu.navFileScope(func.owner_nav).mod.?;
const target = &mod.resolved_target.result; const target = &mod.resolved_target.result;
const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{ const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{
.alignment = switch (nav.status.fully_resolved.alignment) { .alignment = switch (nav.status.fully_resolved.alignment) {
.none => switch (mod.optimize_mode) { .none => switch (mod.optimize_mode) {
.Debug, .Debug,
@ -1414,11 +1646,13 @@ fn updateFuncInner(
.moved = true, .moved = true,
}); });
coff.nodes.appendAssumeCapacity(.{ .nav = nmi }); coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
const sym = si.get(coff);
sym.ni = ni; sym.ni = ni;
sym.section_number = Symbol.Index.text.get(coff).section_number; sym.section_number = sec_si.get(coff).section_number;
}, },
else => si.deleteLocationRelocs(coff), else => si.deleteLocationRelocs(coff),
} }
const sym = si.get(coff);
assert(sym.loc_relocs == .none); assert(sym.loc_relocs == .none);
sym.loc_relocs = @enumFromInt(coff.relocs.items.len); sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
break :ni sym.ni; break :ni sym.ni;
@ -1492,11 +1726,8 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
const comp = coff.base.comp; const comp = coff.base.comp;
task: { task: {
while (coff.pending_uavs.pop()) |pending_uav| { while (coff.pending_uavs.pop()) |pending_uav| {
const sub_prog_node = coff.idleProgNode( const sub_prog_node =
tid, coff.idleProgNode(tid, comp.link_const_prog_node, .{ .uav = pending_uav.key });
comp.link_const_prog_node,
.{ .uav = pending_uav.key },
);
defer sub_prog_node.end(); defer sub_prog_node.end();
coff.flushUav( coff.flushUav(
.{ .zcu = coff.base.comp.zcu.?, .tid = tid }, .{ .zcu = coff.base.comp.zcu.?, .tid = tid },
@ -1561,7 +1792,8 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
const clean_moved = ni.cleanMoved(&coff.mf); const clean_moved = ni.cleanMoved(&coff.mf);
const clean_resized = ni.cleanResized(&coff.mf); const clean_resized = ni.cleanResized(&coff.mf);
if (clean_moved or clean_resized) { if (clean_moved or clean_resized) {
const sub_prog_node = coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni)); const sub_prog_node =
coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni));
defer sub_prog_node.end(); defer sub_prog_node.end();
if (clean_moved) try coff.flushMoved(ni); if (clean_moved) try coff.flushMoved(ni);
if (clean_resized) try coff.flushResized(ni); if (clean_resized) try coff.flushResized(ni);
@ -1584,7 +1816,9 @@ fn idleProgNode(
var name: [std.Progress.Node.max_name_len]u8 = undefined; var name: [std.Progress.Node.max_name_len]u8 = undefined;
return prog_node.start(name: switch (node) { return prog_node.start(name: switch (node) {
else => |tag| @tagName(tag), else => |tag| @tagName(tag),
.section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0), .image_section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
inline .pseudo_section, .object_section => |smi| smi.name(coff).toSlice(coff),
.global => |gmi| gmi.globalName(coff).name.toSlice(coff),
.nav => |nmi| { .nav => |nmi| {
const ip = &coff.base.comp.zcu.?.intern_pool; const ip = &coff.base.comp.zcu.?.intern_pool;
break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip); break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
@ -1611,23 +1845,30 @@ fn flushUav(
const uav_val = umi.uavValue(coff); const uav_val = umi.uavValue(coff);
const si = umi.symbol(coff); const si = umi.symbol(coff);
const ni = ni: { const ni = ni: {
const sym = si.get(coff); switch (si.get(coff).ni) {
switch (sym.ni) {
.none => { .none => {
const sec_si = (try coff.objectSectionMapIndex(
.@".rdata",
coff.mf.flags.block_size,
.{ .read = true },
)).symbol(coff);
try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.nodes.ensureUnusedCapacity(gpa, 1);
const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{ const sym = si.get(coff);
const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{
.alignment = uav_align.toStdMem(), .alignment = uav_align.toStdMem(),
.moved = true, .moved = true,
}); });
coff.nodes.appendAssumeCapacity(.{ .uav = umi }); coff.nodes.appendAssumeCapacity(.{ .uav = umi });
sym.ni = ni; sym.ni = ni;
sym.section_number = Symbol.Index.data.get(coff).section_number; sym.section_number = sec_si.get(coff).section_number;
}, },
else => { else => {
if (sym.ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte)) return; if (si.get(coff).ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte))
return;
si.deleteLocationRelocs(coff); si.deleteLocationRelocs(coff);
}, },
} }
const sym = si.get(coff);
assert(sym.loc_relocs == .none); assert(sym.loc_relocs == .none);
sym.loc_relocs = @enumFromInt(coff.relocs.items.len); sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
break :ni sym.ni; break :ni sym.ni;
@ -1684,7 +1925,7 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
); );
const import_hint_name_table_len = const import_hint_name_table_len =
import_hint_name_align.forward(lib_name.len + ".dll".len + 1); import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
const idata_section_ni = Symbol.Index.idata.node(coff); const idata_section_ni = coff.import_table.ni.parent(&coff.mf);
const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{ const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
.size = addr_size * 2, .size = addr_size * 2,
.alignment = addr_align, .alignment = addr_align,
@ -1701,7 +1942,8 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
import_address_table_sym.ni = import_address_table_ni; import_address_table_sym.ni = import_address_table_ni;
assert(import_address_table_sym.loc_relocs == .none); assert(import_address_table_sym.loc_relocs == .none);
import_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len); import_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
import_address_table_sym.section_number = Symbol.Index.idata.get(coff).section_number; import_address_table_sym.section_number =
coff.getNode(idata_section_ni).object_section.symbol(coff).get(coff).section_number;
} }
const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{ const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
.size = import_hint_name_table_len, .size = import_hint_name_table_len,
@ -1873,12 +2115,12 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
.data_directories, .data_directories,
.section_table, .section_table,
=> unreachable, => unreachable,
.section => |si| return coff.targetStore( .image_section => |si| return coff.targetStore(
&si.get(coff).section_number.header(coff).pointer_to_raw_data, &si.get(coff).section_number.header(coff).pointer_to_raw_data,
@intCast(ni.fileLocation(&coff.mf, false).offset), @intCast(ni.fileLocation(&coff.mf, false).offset),
), ),
.import_directory_table => coff.targetStore( .import_directory_table => coff.targetStore(
&coff.dataDirectoryPtr(.import_table).virtual_address, &coff.dataDirectoryPtr(.IMPORT).virtual_address,
coff.computeNodeRva(ni), coff.computeNodeRva(ni),
), ),
.import_lookup_table => |import_index| coff.targetStore( .import_lookup_table => |import_index| coff.targetStore(
@ -1939,7 +2181,9 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
import_hint_name_index += 2; import_hint_name_index += 2;
} }
}, },
inline .global, inline .pseudo_section,
.object_section,
.global,
.nav, .nav,
.uav, .uav,
.lazy_code, .lazy_code,
@ -1960,7 +2204,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
@intCast(size), @intCast(size),
), ),
} }
if (size > coff.section_table.items[0].get(coff).rva) try coff.virtualSlide( if (size > coff.image_section_table.items[0].get(coff).rva) try coff.virtualSlide(
0, 0,
std.mem.alignForward( std.mem.alignForward(
u32, u32,
@ -1971,7 +2215,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
}, },
.signature, .coff_header, .optional_header, .data_directories => unreachable, .signature, .coff_header, .optional_header, .data_directories => unreachable,
.section_table => {}, .section_table => {},
.section => |si| { .image_section => |si| {
const sym = si.get(coff); const sym = si.get(coff);
const section_index = sym.section_number.toIndex(); const section_index = sym.section_number.toIndex();
const section = &coff.sectionTableSlice()[section_index]; const section = &coff.sectionTableSlice()[section_index];
@ -1987,24 +2231,20 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
} }
}, },
.import_directory_table => coff.targetStore( .import_directory_table => coff.targetStore(
&coff.dataDirectoryPtr(.import_table).size, &coff.dataDirectoryPtr(.IMPORT).size,
@intCast(size), @intCast(size),
), ),
.import_lookup_table, .import_lookup_table, .import_address_table, .import_hint_name_table => {},
.import_address_table, inline .pseudo_section,
.import_hint_name_table, .object_section,
.global, => |smi| smi.symbol(coff).get(coff).size = @intCast(size),
.nav, .global, .nav, .uav, .lazy_code, .lazy_const_data => {},
.uav,
.lazy_code,
.lazy_const_data,
=> {},
} }
} }
fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
var rva = start_rva; var rva = start_rva;
for ( for (
coff.section_table.items[start_section_index..], coff.image_section_table.items[start_section_index..],
coff.sectionTableSlice()[start_section_index..], coff.sectionTableSlice()[start_section_index..],
) |section_si, *section| { ) |section_si, *section| {
const section_sym = section_si.get(coff); const section_sym = section_si.get(coff);
@ -2078,8 +2318,12 @@ fn updateExportsInner(
export_sym.section_number = exported_sym.section_number; export_sym.section_number = exported_sym.section_number;
export_si.applyTargetRelocs(coff); export_si.applyTargetRelocs(coff);
if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) { if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
coff.entry_hack = exported_si;
coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva; coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
} else if (@"export".opts.name.eqlSlice("_tls_used", ip)) {
const tls_directory = coff.dataDirectoryPtr(.TLS);
tls_directory.* = .{ .virtual_address = exported_sym.rva, .size = exported_sym.size };
if (coff.targetEndian() != native_endian)
std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
} }
} }
} }
@ -2108,7 +2352,7 @@ pub fn printNode(
try w.writeAll(@tagName(node)); try w.writeAll(@tagName(node));
switch (node) { switch (node) {
else => {}, else => {},
.section => |si| try w.print("({s})", .{ .image_section => |si| try w.print("({s})", .{
std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0), std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
}), }),
.import_lookup_table, .import_lookup_table,
@ -2117,6 +2361,9 @@ pub fn printNode(
=> |import_index| try w.print("({s})", .{ => |import_index| try w.print("({s})", .{
std.mem.sliceTo(import_index.get(coff).import_hint_name_table_ni.sliceConst(&coff.mf), 0), std.mem.sliceTo(import_index.get(coff).import_hint_name_table_ni.sliceConst(&coff.mf), 0),
}), }),
inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
smi.name(coff).toSlice(coff),
}),
.global => |gmi| { .global => |gmi| {
const gn = gmi.globalName(coff); const gn = gmi.globalName(coff);
try w.writeByte('('); try w.writeByte('(');

View file

@ -546,7 +546,7 @@ fn initHeaders(
break :phndx phnum; break :phndx phnum;
} else undefined; } else undefined;
const expected_nodes_len = 15; const expected_nodes_len = 5 + phnum * 2;
try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len); try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
try elf.phdrs.resize(gpa, phnum); try elf.phdrs.resize(gpa, phnum);
elf.nodes.appendAssumeCapacity(.file); elf.nodes.appendAssumeCapacity(.file);
@ -808,25 +808,6 @@ fn initHeaders(
Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0; Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0;
Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0; Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0;
if (maybe_interp) |interp| {
try elf.nodes.ensureUnusedCapacity(gpa, 1);
const interp_ni = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
.size = interp.len + 1,
.moved = true,
.resized = true,
});
elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
elf.phdrs.items[interp_phndx] = interp_ni;
const sec_interp_si = try elf.addSection(interp_ni, .{
.name = ".interp",
.size = @intCast(interp.len + 1),
.flags = .{ .ALLOC = true },
});
const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
@memcpy(sec_interp[0..interp.len], interp);
sec_interp[interp.len] = 0;
}
assert(try elf.addSection(Node.Known.rodata, .{ assert(try elf.addSection(Node.Known.rodata, .{
.name = ".rodata", .name = ".rodata",
.flags = .{ .ALLOC = true }, .flags = .{ .ALLOC = true },
@ -857,6 +838,25 @@ fn initHeaders(
.addralign = elf.mf.flags.block_size, .addralign = elf.mf.flags.block_size,
}) == .tdata); }) == .tdata);
} }
if (maybe_interp) |interp| {
try elf.nodes.ensureUnusedCapacity(gpa, 1);
const interp_ni = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
.size = interp.len + 1,
.moved = true,
.resized = true,
});
elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
elf.phdrs.items[interp_phndx] = interp_ni;
const sec_interp_si = try elf.addSection(interp_ni, .{
.name = ".interp",
.size = @intCast(interp.len + 1),
.flags = .{ .ALLOC = true },
});
const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
@memcpy(sec_interp[0..interp.len], interp);
sec_interp[interp.len] = 0;
}
assert(elf.nodes.len == expected_nodes_len); assert(elf.nodes.len == expected_nodes_len);
} }
@ -1072,6 +1072,32 @@ fn navType(
}, },
}; };
} }
fn navSection(
elf: *Elf,
ip: *const InternPool,
nav_fr: @FieldType(@FieldType(InternPool.Nav, "status"), "fully_resolved"),
) Symbol.Index {
if (nav_fr.@"linksection".toSlice(ip)) |@"linksection"| {
if (std.mem.eql(u8, @"linksection", ".rodata") or
std.mem.startsWith(u8, @"linksection", ".rodata.")) return .rodata;
if (std.mem.eql(u8, @"linksection", ".text") or
std.mem.startsWith(u8, @"linksection", ".text.")) return .text;
if (std.mem.eql(u8, @"linksection", ".data") or
std.mem.startsWith(u8, @"linksection", ".data.")) return .data;
if (std.mem.eql(u8, @"linksection", ".tdata") or
std.mem.startsWith(u8, @"linksection", ".tdata.")) return .tdata;
}
return switch (navType(
ip,
.{ .fully_resolved = nav_fr },
elf.base.comp.config.any_non_single_threaded,
)) {
else => unreachable,
.FUNC => .text,
.OBJECT => .data,
.TLS => .tdata,
};
}
fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
const gpa = zcu.gpa; const gpa = zcu.gpa;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
@ -1312,18 +1338,16 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
}; };
} }
fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
const comp = elf.base.comp;
const zcu = pt.zcu; const zcu = pt.zcu;
const gpa = zcu.gpa; const gpa = zcu.gpa;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
const nav_val = nav.status.fully_resolved.val; const nav_val = nav.status.fully_resolved.val;
const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) { const nav_init = switch (ip.indexToKey(nav_val)) {
else => .{ nav_val, false }, else => nav_val,
.variable => |variable| .{ variable.init, variable.is_threadlocal }, .variable => |variable| variable.init,
.@"extern" => return, .@"extern", .func => .none,
.func => .{ .none, false },
}; };
if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return; if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
@ -1334,8 +1358,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
switch (sym.ni) { switch (sym.ni) {
.none => { .none => {
try elf.nodes.ensureUnusedCapacity(gpa, 1); try elf.nodes.ensureUnusedCapacity(gpa, 1);
const sec_si: Symbol.Index = const sec_si = elf.navSection(ip, nav.status.fully_resolved);
if (is_threadlocal and comp.config.any_non_single_threaded) .tdata else .data;
const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{ const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
.alignment = pt.navAlignment(nav_index).toStdMem(), .alignment = pt.navAlignment(nav_index).toStdMem(),
.moved = true, .moved = true,
@ -1452,9 +1475,10 @@ fn updateFuncInner(
switch (sym.ni) { switch (sym.ni) {
.none => { .none => {
try elf.nodes.ensureUnusedCapacity(gpa, 1); try elf.nodes.ensureUnusedCapacity(gpa, 1);
const sec_si = elf.navSection(ip, nav.status.fully_resolved);
const mod = zcu.navFileScope(func.owner_nav).mod.?; const mod = zcu.navFileScope(func.owner_nav).mod.?;
const target = &mod.resolved_target.result; const target = &mod.resolved_target.result;
const ni = try elf.mf.addLastChildNode(gpa, Symbol.Index.text.node(elf), .{ const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
.alignment = switch (nav.status.fully_resolved.alignment) { .alignment = switch (nav.status.fully_resolved.alignment) {
.none => switch (mod.optimize_mode) { .none => switch (mod.optimize_mode) {
.Debug, .Debug,
@ -1471,7 +1495,7 @@ fn updateFuncInner(
sym.ni = ni; sym.ni = ni;
switch (elf.symPtr(si)) { switch (elf.symPtr(si)) {
inline else => |sym_ptr, class| sym_ptr.shndx = inline else => |sym_ptr, class| sym_ptr.shndx =
@field(elf.symPtr(.text), @tagName(class)).shndx, @field(elf.symPtr(sec_si), @tagName(class)).shndx,
} }
}, },
else => si.deleteLocationRelocs(elf), else => si.deleteLocationRelocs(elf),

View file

@ -14,6 +14,8 @@ updates: std.ArrayList(Node.Index),
update_prog_node: std.Progress.Node, update_prog_node: std.Progress.Node,
writers: std.SinglyLinkedList, writers: std.SinglyLinkedList,
pub const growth_factor = 4;
pub const Error = std.posix.MMapError || pub const Error = std.posix.MMapError ||
std.posix.MRemapError || std.posix.MRemapError ||
std.fs.File.SetEndPosError || std.fs.File.SetEndPosError ||
@ -64,6 +66,7 @@ pub fn init(file: std.fs.File, gpa: std.mem.Allocator) !MappedFile {
assert(try mf.addNode(gpa, .{ assert(try mf.addNode(gpa, .{
.add_node = .{ .add_node = .{
.size = size, .size = size,
.alignment = mf.flags.block_size,
.fixed = true, .fixed = true,
}, },
}) == Node.Index.root); }) == Node.Index.root);
@ -153,20 +156,24 @@ pub const Node = extern struct {
return ni.get(mf).parent; return ni.get(mf).parent;
} }
pub const ChildIterator = struct { pub fn ChildIterator(comptime direction: enum { prev, next }) type {
return struct {
mf: *const MappedFile, mf: *const MappedFile,
ni: Node.Index, ni: Node.Index,
pub fn next(it: *@This()) ?Node.Index {
pub fn next(it: *ChildIterator) ?Node.Index {
const ni = it.ni; const ni = it.ni;
if (ni == .none) return null; if (ni == .none) return null;
it.ni = ni.get(it.mf).next; it.ni = @field(ni.get(it.mf), @tagName(direction));
return ni; return ni;
} }
}; };
pub fn children(ni: Node.Index, mf: *const MappedFile) ChildIterator { }
pub fn children(ni: Node.Index, mf: *const MappedFile) ChildIterator(.next) {
return .{ .mf = mf, .ni = ni.get(mf).first }; return .{ .mf = mf, .ni = ni.get(mf).first };
} }
pub fn reverseChildren(ni: Node.Index, mf: *const MappedFile) ChildIterator(.prev) {
return .{ .mf = mf, .ni = ni.get(mf).last };
}
pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void { pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
var child_ni = ni.get(mf).last; var child_ni = ni.get(mf).last;
@ -274,7 +281,8 @@ pub const Node = extern struct {
if (set_has_content) parent_node.flags.has_content = true; if (set_has_content) parent_node.flags.has_content = true;
if (parent_ni == .none) break; if (parent_ni == .none) break;
parent_ni = parent_node.parent; parent_ni = parent_node.parent;
offset += parent_ni.location(mf).resolve(mf)[0]; const parent_offset, _ = parent_ni.location(mf).resolve(mf);
offset += parent_offset;
} }
return .{ .offset = offset, .size = size }; return .{ .offset = offset, .size = size };
} }
@ -428,7 +436,7 @@ pub const Node = extern struct {
const total_capacity = interface.end + unused_capacity; const total_capacity = interface.end + unused_capacity;
if (interface.buffer.len >= total_capacity) return; if (interface.buffer.len >= total_capacity) return;
const w: *Writer = @fieldParentPtr("interface", interface); const w: *Writer = @fieldParentPtr("interface", interface);
w.ni.resize(w.mf, w.gpa, total_capacity +| total_capacity / 2) catch |err| { w.ni.resize(w.mf, w.gpa, total_capacity +| total_capacity / growth_factor) catch |err| {
w.err = err; w.err = err;
return error.WriteFailed; return error.WriteFailed;
}; };
@ -487,7 +495,8 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
free_node.flags.moved = false; free_node.flags.moved = false;
free_node.flags.resized = false; free_node.flags.resized = false;
} }
if (offset > opts.parent.location(mf).resolve(mf)[1]) try opts.parent.resize(mf, gpa, offset); _, const parent_size = opts.parent.location(mf).resolve(mf);
if (offset > parent_size) try opts.parent.resize(mf, gpa, offset);
try free_ni.resize(mf, gpa, opts.add_node.size); try free_ni.resize(mf, gpa, opts.add_node.size);
} }
if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf); if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
@ -522,6 +531,27 @@ pub fn addOnlyChildNode(
return ni; return ni;
} }
pub fn addFirstChildNode(
mf: *MappedFile,
gpa: std.mem.Allocator,
parent_ni: Node.Index,
opts: AddNodeOptions,
) !Node.Index {
try mf.nodes.ensureUnusedCapacity(gpa, 1);
const parent = parent_ni.get(mf);
const ni = try mf.addNode(gpa, .{
.parent = parent_ni,
.next = parent.first,
.add_node = opts,
});
switch (parent.first) {
.none => parent.last = ni,
else => |first_ni| first_ni.get(mf).prev = ni,
}
parent.first = ni;
return ni;
}
pub fn addLastChildNode( pub fn addLastChildNode(
mf: *MappedFile, mf: *MappedFile,
gpa: std.mem.Allocator, gpa: std.mem.Allocator,
@ -577,7 +607,7 @@ pub fn addNodeAfter(
fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) !void { fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) !void {
const node = ni.get(mf); const node = ni.get(mf);
var old_offset, const old_size = node.location().resolve(mf); const old_offset, const old_size = node.location().resolve(mf);
const new_size = node.flags.alignment.forward(@intCast(requested_size)); const new_size = node.flags.alignment.forward(@intCast(requested_size));
// Resize the entire file // Resize the entire file
if (ni == Node.Index.root) { if (ni == Node.Index.root) {
@ -587,41 +617,44 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
ni.setLocationAssumeCapacity(mf, old_offset, new_size); ni.setLocationAssumeCapacity(mf, old_offset, new_size);
return; return;
} }
while (true) {
const parent = node.parent.get(mf); const parent = node.parent.get(mf);
_, const old_parent_size = parent.location().resolve(mf); _, var old_parent_size = parent.location().resolve(mf);
const trailing_end = switch (node.next) { const trailing_end = trailing_end: switch (node.next) {
.none => parent.location().resolve(mf)[1], .none => old_parent_size,
else => |next_ni| next_ni.location(mf).resolve(mf)[0], else => |next_ni| {
const next_offset, _ = next_ni.location(mf).resolve(mf);
break :trailing_end next_offset;
},
}; };
assert(old_offset + old_size <= trailing_end); assert(old_offset + old_size <= trailing_end);
// Expand the node into available trailing free space
if (old_offset + new_size <= trailing_end) { if (old_offset + new_size <= trailing_end) {
// Expand the node into trailing free space
try mf.ensureCapacityForSetLocation(gpa); try mf.ensureCapacityForSetLocation(gpa);
ni.setLocationAssumeCapacity(mf, old_offset, new_size); ni.setLocationAssumeCapacity(mf, old_offset, new_size);
return; return;
} }
// Ask the filesystem driver to insert an extent into the file without copying any data
if (is_linux and !mf.flags.fallocate_insert_range_unsupported and if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
node.flags.alignment.order(mf.flags.block_size).compare(.gte)) node.flags.alignment.order(mf.flags.block_size).compare(.gte))
insert_range: { insert_range: {
// Ask the filesystem driver to insert extents into the file without copying any data
const last_offset, const last_size = parent.last.location(mf).resolve(mf); const last_offset, const last_size = parent.last.location(mf).resolve(mf);
const last_end = last_offset + last_size; const last_end = last_offset + last_size;
assert(last_end <= old_parent_size); assert(last_end <= old_parent_size);
const range_size =
node.flags.alignment.forward(@intCast(requested_size +| requested_size / 2)) - old_size;
const new_parent_size = last_end + range_size;
if (new_parent_size > old_parent_size) {
try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / 2);
continue;
}
const range_file_offset = ni.fileLocation(mf, false).offset + old_size; const range_file_offset = ni.fileLocation(mf, false).offset + old_size;
while (true) switch (linux.E.init(linux.fallocate( const range_size = node.flags.alignment.forward(
@intCast(requested_size +| requested_size / growth_factor),
) - old_size;
_, const file_size = Node.Index.root.location(mf).resolve(mf);
while (true) switch (linux.E.init(switch (std.math.order(range_file_offset, file_size)) {
.lt => linux.fallocate(
mf.file.handle, mf.file.handle,
linux.FALLOC.FL_INSERT_RANGE, linux.FALLOC.FL_INSERT_RANGE,
@intCast(range_file_offset), @intCast(range_file_offset),
@intCast(range_size), @intCast(range_size),
))) { ),
.eq => linux.ftruncate(mf.file.handle, @intCast(range_file_offset + range_size)),
.gt => unreachable,
})) {
.SUCCESS => { .SUCCESS => {
var enclosing_ni = ni; var enclosing_ni = ni;
while (true) { while (true) {
@ -667,28 +700,24 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
else => |e| return std.posix.unexpectedErrno(e), else => |e| return std.posix.unexpectedErrno(e),
}; };
} }
switch (node.next) { if (node.next == .none) {
.none => {
// As this is the last node, we simply need more space in the parent // As this is the last node, we simply need more space in the parent
const new_parent_size = old_offset + new_size; const new_parent_size = old_offset + new_size;
try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / 2); try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / growth_factor);
}, try mf.ensureCapacityForSetLocation(gpa);
else => |*next_ni_ptr| switch (node.flags.fixed) { ni.setLocationAssumeCapacity(mf, old_offset, new_size);
false => { return;
}
if (!node.flags.fixed) {
// Make space at the end of the parent for this floating node // Make space at the end of the parent for this floating node
const last = parent.last.get(mf); const last = parent.last.get(mf);
const last_offset, const last_size = last.location().resolve(mf); const last_offset, const last_size = last.location().resolve(mf);
const new_offset = node.flags.alignment.forward(@intCast(last_offset + last_size)); const new_offset = node.flags.alignment.forward(@intCast(last_offset + last_size));
const new_parent_size = new_offset + new_size; const new_parent_size = new_offset + new_size;
if (new_parent_size > old_parent_size) { if (new_parent_size > old_parent_size)
try mf.resizeNode( try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / growth_factor);
gpa, try mf.ensureCapacityForSetLocation(gpa);
node.parent, const next_ni = node.next;
new_parent_size +| new_parent_size / 2,
);
continue;
}
const next_ni = next_ni_ptr.*;
next_ni.get(mf).prev = node.prev; next_ni.get(mf).prev = node.prev;
switch (node.prev) { switch (node.prev) {
.none => parent.first = next_ni, .none => parent.first = next_ni,
@ -696,7 +725,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
} }
last.next = ni; last.next = ni;
node.prev = parent.last; node.prev = parent.last;
next_ni_ptr.* = .none; node.next = .none;
parent.last = ni; parent.last = ni;
if (node.flags.has_content) { if (node.flags.has_content) {
const parent_file_offset = node.parent.fileLocation(mf, false).offset; const parent_file_offset = node.parent.fileLocation(mf, false).offset;
@ -706,50 +735,120 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
old_size, old_size,
); );
} }
old_offset = new_offset; ni.setLocationAssumeCapacity(mf, new_offset, new_size);
}, return;
true => { }
// Move the next floating node to make space for this fixed node // Search for the first floating node following this fixed node
const next_ni = next_ni_ptr.*; var last_fixed_ni = ni;
const next = next_ni.get(mf); var first_floating_ni = node.next;
assert(!next.flags.fixed); var shift = new_size - old_size;
const next_offset, const next_size = next.location().resolve(mf); var direction: enum { forward, reverse } = .forward;
while (true) {
assert(last_fixed_ni != .none);
const last_fixed = last_fixed_ni.get(mf);
assert(last_fixed.flags.fixed);
const old_last_fixed_offset, const last_fixed_size = last_fixed.location().resolve(mf);
const new_last_fixed_offset = old_last_fixed_offset + shift;
make_space: switch (first_floating_ni) {
else => {
const first_floating = first_floating_ni.get(mf);
const old_first_floating_offset, const first_floating_size =
first_floating.location().resolve(mf);
assert(old_last_fixed_offset + last_fixed_size <= old_first_floating_offset);
if (new_last_fixed_offset + last_fixed_size <= old_first_floating_offset)
break :make_space;
assert(direction == .forward);
if (first_floating.flags.fixed) {
shift = first_floating.flags.alignment.forward(@intCast(
@max(shift, first_floating_size),
));
// Not enough space, try the next node
last_fixed_ni = first_floating_ni;
first_floating_ni = first_floating.next;
continue;
}
// Move the found floating node to make space for preceding fixed nodes
const last = parent.last.get(mf); const last = parent.last.get(mf);
const last_offset, const last_size = last.location().resolve(mf); const last_offset, const last_size = last.location().resolve(mf);
const new_offset = next.flags.alignment.forward(@intCast( const new_first_floating_offset = first_floating.flags.alignment.forward(
@max(old_offset + new_size, last_offset + last_size), @intCast(@max(new_last_fixed_offset + last_fixed_size, last_offset + last_size)),
)); );
const new_parent_size = new_offset + next_size; const new_parent_size = new_first_floating_offset + first_floating_size;
if (new_parent_size > old_parent_size) { if (new_parent_size > old_parent_size) {
try mf.resizeNode( try mf.resizeNode(
gpa, gpa,
node.parent, node.parent,
new_parent_size +| new_parent_size / 2, new_parent_size +| new_parent_size / growth_factor,
); );
continue; _, old_parent_size = parent.location().resolve(mf);
} }
try mf.ensureCapacityForSetLocation(gpa); try mf.ensureCapacityForSetLocation(gpa);
next.prev = parent.last; if (parent.last != first_floating_ni) {
parent.last = next_ni; first_floating.prev = parent.last;
last.next = next_ni; parent.last = first_floating_ni;
next_ni_ptr.* = next.next; last.next = first_floating_ni;
switch (next.next) { last_fixed.next = first_floating.next;
switch (first_floating.next) {
.none => {}, .none => {},
else => |next_next_ni| next_next_ni.get(mf).prev = ni, else => |next_ni| next_ni.get(mf).prev = last_fixed_ni,
} }
next.next = .none; first_floating.next = .none;
if (node.flags.has_content) { }
const parent_file_offset = node.parent.fileLocation(mf, false).offset; if (first_floating.flags.has_content) {
const parent_file_offset =
node.parent.fileLocation(mf, false).offset;
try mf.moveRange( try mf.moveRange(
parent_file_offset + next_offset, parent_file_offset + old_first_floating_offset,
parent_file_offset + new_offset, parent_file_offset + new_first_floating_offset,
next_size, first_floating_size,
); );
} }
next_ni.setLocationAssumeCapacity(mf, new_offset, next_size); first_floating_ni.setLocationAssumeCapacity(
mf,
new_first_floating_offset,
first_floating_size,
);
// Continue the search after the just-moved floating node
first_floating_ni = last_fixed.next;
continue;
}, },
.none => {
assert(direction == .forward);
const new_parent_size = new_last_fixed_offset + last_fixed_size;
if (new_parent_size > old_parent_size) {
try mf.resizeNode(
gpa,
node.parent,
new_parent_size +| new_parent_size / growth_factor,
);
_, old_parent_size = parent.location().resolve(mf);
}
}, },
} }
try mf.ensureCapacityForSetLocation(gpa);
if (last_fixed_ni == ni) {
// The original fixed node now has enough space
last_fixed_ni.setLocationAssumeCapacity(
mf,
old_last_fixed_offset,
last_fixed_size + shift,
);
return;
}
// Move a fixed node into trailing free space
if (last_fixed.flags.has_content) {
const parent_file_offset = node.parent.fileLocation(mf, false).offset;
try mf.moveRange(
parent_file_offset + old_last_fixed_offset,
parent_file_offset + new_last_fixed_offset,
last_fixed_size,
);
}
last_fixed_ni.setLocationAssumeCapacity(mf, new_last_fixed_offset, last_fixed_size);
// Retry the previous nodes now that there is enough space
first_floating_ni = last_fixed_ni;
last_fixed_ni = last_fixed.prev;
direction = .reverse;
} }
} }
@ -843,7 +942,7 @@ fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) !void {
pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void { pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void {
if (mf.contents.len >= new_capacity) return; if (mf.contents.len >= new_capacity) return;
try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / 2); try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / growth_factor);
} }
pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void { pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {

View file

@ -389,10 +389,7 @@ pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only,
} }
return switch (zigBackend(target, false)) { return switch (zigBackend(target, false)) {
.stage2_wasm => .llvm_lld_only, .stage2_wasm => .llvm_lld_only,
.stage2_x86_64 => switch (target.ofmt) { .stage2_x86_64 => .yes,
.elf, .macho => .yes,
else => .llvm_only,
},
else => .llvm_only, else => .llvm_only,
}; };
} }
@ -776,10 +773,9 @@ pub fn supportsTailCall(target: *const std.Target, backend: std.builtin.Compiler
} }
pub fn supportsThreads(target: *const std.Target, backend: std.builtin.CompilerBackend) bool { pub fn supportsThreads(target: *const std.Target, backend: std.builtin.CompilerBackend) bool {
_ = target;
return switch (backend) { return switch (backend) {
.stage2_aarch64 => false, .stage2_aarch64 => false,
.stage2_powerpc => true,
.stage2_x86_64 => target.ofmt == .macho or target.ofmt == .elf,
else => true, else => true,
}; };
} }

View file

@ -7,7 +7,6 @@ test "thread local variable" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) { if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) {
@ -27,7 +26,6 @@ test "pointer to thread local array" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest; // TODO
const s = "Hello world"; const s = "Hello world";
@memcpy(buffer[0..s.len], s); @memcpy(buffer[0..s.len], s);
@ -41,9 +39,8 @@ test "reference a global threadlocal variable" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest; // TODO
_ = nrfx_uart_rx(&g_uart0); try nrfx_uart_rx(&g_uart0);
} }
const nrfx_uart_t = extern struct { const nrfx_uart_t = extern struct {
@ -51,11 +48,12 @@ const nrfx_uart_t = extern struct {
drv_inst_idx: u8, drv_inst_idx: u8,
}; };
pub fn nrfx_uart_rx(p_instance: [*c]const nrfx_uart_t) void { pub fn nrfx_uart_rx(p_instance: [*c]const nrfx_uart_t) !void {
_ = p_instance; try expect(p_instance.*.p_reg == 0);
try expect(p_instance.*.drv_inst_idx == 0xab);
} }
threadlocal var g_uart0 = nrfx_uart_t{ threadlocal var g_uart0 = nrfx_uart_t{
.p_reg = 0, .p_reg = 0,
.drv_inst_idx = 0, .drv_inst_idx = 0xab,
}; };

View file

@ -2291,24 +2291,12 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
if (options.skip_single_threaded and test_target.single_threaded == true) if (options.skip_single_threaded and test_target.single_threaded == true)
continue; continue;
// TODO get compiler-rt tests passing for self-hosted backends. if (!would_use_llvm and target.cpu.arch == .aarch64) {
if (((target.cpu.arch != .x86_64 and target.cpu.arch != .aarch64) or target.ofmt == .coff) and // TODO get std tests passing for the aarch64 self-hosted backend.
test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) if (mem.eql(u8, options.name, "std")) continue;
continue; // TODO get zigc tests passing for the aarch64 self-hosted backend.
if (mem.eql(u8, options.name, "zigc")) continue;
// TODO get zigc tests passing for other self-hosted backends. }
if (target.cpu.arch != .x86_64 and
test_target.use_llvm == false and mem.eql(u8, options.name, "zigc"))
continue;
// TODO get std lib tests passing for other self-hosted backends.
if ((target.cpu.arch != .x86_64 or target.os.tag != .linux) and
test_target.use_llvm == false and mem.eql(u8, options.name, "std"))
continue;
if (target.cpu.arch != .x86_64 and
test_target.use_llvm == false and mem.eql(u8, options.name, "c-import"))
continue;
const want_this_mode = for (options.optimize_modes) |m| { const want_this_mode = for (options.optimize_modes) |m| {
if (m == test_target.optimize_mode) break true; if (m == test_target.optimize_mode) break true;
@ -2362,7 +2350,7 @@ fn addOneModuleTest(
const single_threaded_suffix = if (test_target.single_threaded == true) "-single" else ""; const single_threaded_suffix = if (test_target.single_threaded == true) "-single" else "";
const backend_suffix = if (test_target.use_llvm == true) const backend_suffix = if (test_target.use_llvm == true)
"-llvm" "-llvm"
else if (target.ofmt == std.Target.ObjectFormat.c) else if (target.ofmt == .c)
"-cbe" "-cbe"
else if (test_target.use_llvm == false) else if (test_target.use_llvm == false)
"-selfhosted" "-selfhosted"
@ -2389,7 +2377,7 @@ fn addOneModuleTest(
use_pic, use_pic,
}); });
if (target.ofmt == std.Target.ObjectFormat.c) { if (target.ofmt == .c) {
var altered_query = test_target.target; var altered_query = test_target.target;
altered_query.ofmt = null; altered_query.ofmt = null;