| ... | ... | @@ -60,6 +60,8 @@ string_bytes: std.ArrayList(u8), |
| 60 | 60 | section_table: std.AutoArrayHashMapUnmanaged(String, Section), |
| 61 | 61 | pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index), |
| 62 | 62 | object_section_table: std.array_hash_map.Auto(String, Symbol.Index), |
| 63 | section_merges: std.AutoArrayHashMapUnmanaged(String, String), |
| 64 | section_merge_pending_index: u32, |
| 63 | 65 | symbols: std.ArrayList(Symbol), |
| 64 | 66 | globals: std.array_hash_map.Auto(GlobalName, Symbol.Index), |
| 65 | 67 | global_pending_index: u32, |
| ... | ... | @@ -93,6 +95,8 @@ pub const archive_end_of_header = "`\n"; |
| 93 | 95 | |
| 94 | 96 | pub const imp_prefix = "__imp_"; |
| 95 | 97 | |
| 98 | const header_name_max_len = @typeInfo(@FieldType(std.coff.SectionHeader, "name")).array.len; |
| 99 | |
| 96 | 100 | /// This is the start of a Portable Executable (PE) file. |
| 97 | 101 | /// It starts with a MS-DOS header followed by a MS-DOS stub program. |
| 98 | 102 | /// This data does not change so we include it as follows in all binaries. |
| ... | ... | @@ -797,6 +801,7 @@ pub const String = enum(u32) { |
| 797 | 801 | @".dtors" = 57, |
| 798 | 802 | @".dtors$ZZZ" = 64, |
| 799 | 803 | @".bss" = 75, |
| 804 | @".fptable" = 80, |
| 800 | 805 | _, |
| 801 | 806 | |
| 802 | 807 | pub const Optional = enum(u32) { |
| ... | ... | @@ -811,6 +816,7 @@ pub const String = enum(u32) { |
| 811 | 816 | @".dtors" = @intFromEnum(String.@".dtors"), |
| 812 | 817 | @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"), |
| 813 | 818 | @".bss" = @intFromEnum(String.@".bss"), |
| 819 | @".fptable" = @intFromEnum(String.@".fptable"), |
| 814 | 820 | none = std.math.maxInt(u32), |
| 815 | 821 | _, |
| 816 | 822 | |
| ... | ... | @@ -1242,11 +1248,6 @@ pub const Reloc = extern struct { |
| 1242 | 1248 | .ADDR32, |
| 1243 | 1249 | .ADDR32NB, |
| 1244 | 1250 | .SECREL, |
| 1245 | | => std.mem.readInt( |
| 1246 | | u32, |
| 1247 | | loc_slice[0..4], |
| 1248 | | target_endian, |
| 1249 | | ), |
| 1250 | 1251 | .REL32, |
| 1251 | 1252 | .REL32_1, |
| 1252 | 1253 | .REL32_2, |
| ... | ... | @@ -1263,11 +1264,6 @@ pub const Reloc = extern struct { |
| 1263 | 1264 | else => |kind| @panic(@tagName(kind)), |
| 1264 | 1265 | .ABSOLUTE => 0, |
| 1265 | 1266 | .DIR16, |
| 1266 | | => std.mem.readInt( |
| 1267 | | u16, |
| 1268 | | loc_slice[0..2], |
| 1269 | | target_endian, |
| 1270 | | ), |
| 1271 | 1267 | .REL16, |
| 1272 | 1268 | => std.mem.readInt( |
| 1273 | 1269 | i16, |
| ... | ... | @@ -1277,11 +1273,6 @@ pub const Reloc = extern struct { |
| 1277 | 1273 | .DIR32, |
| 1278 | 1274 | .DIR32NB, |
| 1279 | 1275 | .SECREL, |
| 1280 | | => std.mem.readInt( |
| 1281 | | u32, |
| 1282 | | loc_slice[0..4], |
| 1283 | | target_endian, |
| 1284 | | ), |
| 1285 | 1276 | .REL32, |
| 1286 | 1277 | => std.mem.readInt( |
| 1287 | 1278 | i32, |
| ... | ... | @@ -1553,6 +1544,8 @@ fn create( |
| 1553 | 1544 | .section_table = .empty, |
| 1554 | 1545 | .pseudo_section_table = .empty, |
| 1555 | 1546 | .object_section_table = .empty, |
| 1547 | .section_merges = .empty, |
| 1548 | .section_merge_pending_index = 0, |
| 1556 | 1549 | .symbols = .empty, |
| 1557 | 1550 | .globals = .empty, |
| 1558 | 1551 | .global_pending_index = 0, |
| ... | ... | @@ -1698,7 +1691,7 @@ fn initHeaders( |
| 1698 | 1691 | const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment); |
| 1699 | 1692 | const is_image = coff.isImage(); |
| 1700 | 1693 | const is_archive = coff.isArchive(); |
| 1701 | | |
| 1694 | const target = &comp.root_mod.resolved_target.result; |
| 1702 | 1695 | const optional_header_size: u16 = if (is_image) switch (magic) { |
| 1703 | 1696 | _ => unreachable, |
| 1704 | 1697 | inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))), |
| ... | ... | @@ -1713,12 +1706,14 @@ fn initHeaders( |
| 1713 | 1706 | // Sections |
| 1714 | 1707 | expected_nodes_len += 4; |
| 1715 | 1708 | |
| 1716 | | if (is_image) |
| 1709 | if (is_image) { |
| 1717 | 1710 | // Pseudo-sections and import / export table |
| 1718 | | expected_nodes_len += 9 |
| 1719 | | else |
| 1720 | | // Symbol table |
| 1721 | | expected_nodes_len += 2; |
| 1711 | expected_nodes_len += 9; |
| 1712 | if (comp.config.link_libc and target.abi == .msvc) |
| 1713 | expected_nodes_len += 1; |
| 1714 | } else |
| 1715 | // Symbol table |
| 1716 | expected_nodes_len += 2; |
| 1722 | 1717 | |
| 1723 | 1718 | // TLS section |
| 1724 | 1719 | if (comp.config.any_non_single_threaded) { |
| ... | ... | @@ -2004,9 +1999,10 @@ fn initHeaders( |
| 2004 | 1999 | |
| 2005 | 2000 | try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count); |
| 2006 | 2001 | assert(coff.addSymbolAssumeCapacity() == .null); |
| 2002 | |
| 2007 | 2003 | // TODO: How do we tell MappedFile not to allocate physical space for these? |
| 2008 | 2004 | // TODO: Could have a node flag 'virtual' that can never have slice* called on it or fileLocation |
| 2009 | | |
| 2005 | // TODO: Instead of it's own section, we can place .bss as a pseudo-section at the end of .text in the extra space |
| 2010 | 2006 | assert(try coff.addSection(.@".bss", .{ |
| 2011 | 2007 | .CNT_UNINITIALIZED_DATA = true, |
| 2012 | 2008 | .MEM_READ = true, |
| ... | ... | @@ -2028,6 +2024,18 @@ fn initHeaders( |
| 2028 | 2024 | }) == .text); |
| 2029 | 2025 | |
| 2030 | 2026 | if (is_image) { |
| 2027 | if (comp.config.link_libc and target.abi == .msvc) { |
| 2028 | // This section contains a function pointer table used by control flow guard: |
| 2029 | // https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard |
| 2030 | // The page containing it is set to PAGE_READONLY during startup, so this can't |
| 2031 | // be merged into .data this protection would overlap writable memory. |
| 2032 | _ = try coff.addSection(.@".fptable", .{ |
| 2033 | .CNT_INITIALIZED_DATA = true, |
| 2034 | .MEM_READ = true, |
| 2035 | .MEM_WRITE = true, |
| 2036 | }); |
| 2037 | } |
| 2038 | |
| 2031 | 2039 | coff.import_table.ni = try coff.mf.addLastChildNode( |
| 2032 | 2040 | gpa, |
| 2033 | 2041 | (try coff.objectSectionMapIndex( |
| ... | ... | @@ -2199,7 +2207,8 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void { |
| 2199 | 2207 | coff.synth_prog_node = prog_node.start("Synthetics", count: { |
| 2200 | 2208 | var count = |
| 2201 | 2209 | coff.globals.count() - coff.global_pending_index + |
| 2202 | | coff.late_globals.items.len - coff.late_globals_pending_index; |
| 2210 | coff.late_globals.items.len - coff.late_globals_pending_index + |
| 2211 | coff.section_merges.count() - coff.section_merge_pending_index; |
| 2203 | 2212 | |
| 2204 | 2213 | for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index; |
| 2205 | 2214 | break :count count; |
| ... | ... | @@ -2562,7 +2571,8 @@ fn getString(coff: *Coff, string: []const u8) String.Optional { |
| 2562 | 2571 | fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName { |
| 2563 | 2572 | assert(!coff.isImage()); |
| 2564 | 2573 | const gpa = coff.base.comp.gpa; |
| 2565 | | return if (name.len > 8) name: { |
| 2574 | |
| 2575 | return if (name.len > header_name_max_len) name: { |
| 2566 | 2576 | const string = opt_string orelse try coff.getOrPutString(name); |
| 2567 | 2577 | const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string); |
| 2568 | 2578 | if (!string_gop.found_existing) { |
| ... | ... | @@ -3202,8 +3212,6 @@ const ObjectSectionAttributes = packed struct { |
| 3202 | 3212 | initialized: bool = false, |
| 3203 | 3213 | uninitialized: bool = false, |
| 3204 | 3214 | |
| 3205 | | // TODO: Include init / not init flags? |
| 3206 | | |
| 3207 | 3215 | pub fn fromFlags(flags: std.coff.SectionHeader.Flags) ObjectSectionAttributes { |
| 3208 | 3216 | return .{ |
| 3209 | 3217 | .read = flags.MEM_READ, |
| ... | ... | @@ -3244,26 +3252,22 @@ fn pseudoSectionMapIndex( |
| 3244 | 3252 | const gpa = coff.base.comp.gpa; |
| 3245 | 3253 | const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name); |
| 3246 | 3254 | const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index); |
| 3247 | | const sn = if (!pseudo_section_gop.found_existing) sn: { |
| 3248 | | const default_parent: Symbol.Index = if (attributes.uninitialized) |
| 3249 | | .bss |
| 3250 | | else if (attributes.execute) |
| 3251 | | .text |
| 3252 | | else if (attributes.write) |
| 3253 | | .data |
| 3254 | | else |
| 3255 | | .rdata; |
| 3255 | const parent_sn = if (!pseudo_section_gop.found_existing) sn: { |
| 3256 | const effective_name = coff.section_merges.get(name) orelse name; |
| 3257 | const parent = if (coff.section_table.get(effective_name)) |existing_sec| |
| 3258 | existing_sec.si |
| 3259 | else if (coff.isImage()) parent: { |
| 3260 | const parent: Symbol.Index = if (attributes.uninitialized) |
| 3261 | .bss |
| 3262 | else if (attributes.execute) |
| 3263 | .text |
| 3264 | else if (attributes.write) |
| 3265 | .data |
| 3266 | else |
| 3267 | .rdata; |
| 3256 | 3268 | |
| 3257 | | const parent = if (coff.isImage() or std.mem.eql( |
| 3258 | | u8, |
| 3259 | | name.toSlice(coff), |
| 3260 | | default_parent.knownString().toSlice(coff).?, |
| 3261 | | )) |
| 3262 | | default_parent |
| 3263 | | else if (coff.section_table.get(name)) |section| |
| 3264 | | section.si |
| 3265 | | else |
| 3266 | | try coff.addSection(name, attributes.asFlags()); |
| 3269 | break :parent parent; |
| 3270 | } else try coff.addSection(effective_name, attributes.asFlags()); |
| 3267 | 3271 | |
| 3268 | 3272 | try coff.nodes.ensureUnusedCapacity(gpa, 1); |
| 3269 | 3273 | try coff.symbols.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -3282,9 +3286,9 @@ fn pseudoSectionMapIndex( |
| 3282 | 3286 | |
| 3283 | 3287 | try coff.verifyParentSectionAttributes( |
| 3284 | 3288 | .pseudo, |
| 3285 | | sn.name(coff), |
| 3289 | parent_sn.name(coff), |
| 3286 | 3290 | name, |
| 3287 | | .fromFlags(sn.header(coff).flags), |
| 3291 | .fromFlags(parent_sn.header(coff).flags), |
| 3288 | 3292 | attributes, |
| 3289 | 3293 | ); |
| 3290 | 3294 | |
| ... | ... | @@ -3614,6 +3618,8 @@ fn loadObject( |
| 3614 | 3618 | const target_endian = coff.targetEndian(); |
| 3615 | 3619 | const is_archive = coff.isArchive(); |
| 3616 | 3620 | assert(!coff.isObj()); |
| 3621 | // We want to evaluate new merges as we see them in .drectve sections to avoid redundant work |
| 3622 | assert(coff.section_merge_pending_index == coff.section_merges.count()); |
| 3617 | 3623 | |
| 3618 | 3624 | log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberNameString(member_name) }); |
| 3619 | 3625 | |
| ... | ... | @@ -3826,10 +3832,15 @@ fn loadObject( |
| 3826 | 3832 | var num_global_symbols: u32 = 0; |
| 3827 | 3833 | var pending_symbols: std.AutoArrayHashMapUnmanaged(u32, PendingSymbol) = .empty; |
| 3828 | 3834 | defer pending_symbols.deinit(gpa); |
| 3829 | | |
| 3830 | 3835 | if (!is_archive) |
| 3831 | 3836 | try pending_symbols.ensureUnusedCapacity(gpa, header.number_of_symbols); |
| 3832 | 3837 | |
| 3838 | var section_merges: std.ArrayList(struct { |
| 3839 | from: String, |
| 3840 | to: String, |
| 3841 | }) = .empty; |
| 3842 | defer section_merges.deinit(gpa); |
| 3843 | |
| 3833 | 3844 | // Discover symbol names and COMDAT symbol mappings |
| 3834 | 3845 | var symbol_i: u32 = 0; |
| 3835 | 3846 | while (symbol_i < header.number_of_symbols) { |
| ... | ... | @@ -4081,15 +4092,48 @@ fn loadObject( |
| 4081 | 4092 | ); |
| 4082 | 4093 | } else if (std.ascii.startsWithIgnoreCase(arg, "/guardsym:")) { |
| 4083 | 4094 | // TODO: https://learn.microsoft.com/en-us/windows/win32/secbp/pe-metadata |
| 4084 | | } else if (std.ascii.startsWithIgnoreCase(arg, "/merge:")) { |
| 4095 | } else if (std.ascii.startsWithIgnoreCase(arg, "/merge:")) merge: { |
| 4085 | 4096 | var split = std.mem.splitScalar(u8, arg["/merge:".len..], '='); |
| 4086 | 4097 | const from = split.first(); |
| 4087 | 4098 | const to = split.next() orelse |
| 4088 | 4099 | return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg}); |
| 4100 | if (to.len > header_name_max_len) |
| 4101 | return diags.failParse( |
| 4102 | path, |
| 4103 | "/merge .drectve target exceeds max length of {d}: '{s}'", |
| 4104 | .{ header_name_max_len, arg }, |
| 4105 | ); |
| 4106 | if (std.mem.eql(u8, from, to)) break :merge; |
| 4107 | |
| 4108 | try coff.ensureManyUnusedStringCapacity(2, from.len + to.len + 2); |
| 4109 | const from_str = coff.getOrPutStringAssumeCapacity(from); |
| 4110 | const to_str = coff.getOrPutStringAssumeCapacity(to); |
| 4111 | |
| 4112 | { |
| 4113 | var iter = to_str; |
| 4114 | while (coff.section_merges.get(iter)) |next_to| { |
| 4115 | if (next_to == from_str) |
| 4116 | return diags.failParse( |
| 4117 | path, |
| 4118 | "/merge .drectve argument would create a cycle: {s}={s} leads to {s}={s}", |
| 4119 | .{ from, to, iter.toSlice(coff), to }, |
| 4120 | ); |
| 4121 | |
| 4122 | iter = next_to; |
| 4123 | } |
| 4124 | } |
| 4089 | 4125 | |
| 4090 | | // TODO: Override the parent selection for generated sections below |
| 4091 | | _ = from; |
| 4092 | | _ = to; |
| 4126 | try coff.section_merges.ensureUnusedCapacity(gpa, 1); |
| 4127 | const gop = coff.section_merges.getOrPutAssumeCapacity(from_str); |
| 4128 | if (!gop.found_existing) { |
| 4129 | coff.synth_prog_node.increaseEstimatedTotalItems(1); |
| 4130 | gop.value_ptr.* = to_str; |
| 4131 | } else if (gop.value_ptr.* != to_str) |
| 4132 | return diags.failParse( |
| 4133 | path, |
| 4134 | "conflicting /merge .drectve arguments: first seen as {s}={s}, now seen as {s}={s}", |
| 4135 | .{ from, gop.value_ptr.toSlice(coff), from, to }, |
| 4136 | ); |
| 4093 | 4137 | } else if (std.ascii.startsWithIgnoreCase(arg, "/disallowlib:")) { |
| 4094 | 4138 | const lib_name = arg["/disallowlib:".len..]; |
| 4095 | 4139 | // TODO: Track these and issue error in prelink if any match |
| ... | ... | @@ -4250,6 +4294,9 @@ fn loadObject( |
| 4250 | 4294 | }; |
| 4251 | 4295 | } |
| 4252 | 4296 | |
| 4297 | while (coff.section_merge_pending_index < coff.section_merges.count()) : (coff.section_merge_pending_index += 1) |
| 4298 | try coff.flushSectionMerge(coff.section_merge_pending_index); |
| 4299 | |
| 4253 | 4300 | // Resolve pending associations, create parent sections |
| 4254 | 4301 | var num_included_sections: u16 = 0; |
| 4255 | 4302 | var num_included_symbols: u32 = 0; |
| ... | ... | @@ -4276,6 +4323,11 @@ fn loadObject( |
| 4276 | 4323 | .pending => unreachable, |
| 4277 | 4324 | } |
| 4278 | 4325 | |
| 4326 | // TODO: Until we support sorting .pdata, we shouldn't merge these in, the result would be invalid |
| 4327 | const section_name = section.name.toSlice(coff); |
| 4328 | if (std.mem.startsWith(u8, section_name, ".pdata")) |
| 4329 | continue; |
| 4330 | |
| 4279 | 4331 | num_included_sections += 1; |
| 4280 | 4332 | num_included_symbols += section.num_symbols; |
| 4281 | 4333 | num_included_relocs += section.header.number_of_relocations; |
| ... | ... | @@ -4293,7 +4345,7 @@ fn loadObject( |
| 4293 | 4345 | try coff.input_sections.ensureUnusedCapacity(gpa, num_included_sections); |
| 4294 | 4346 | |
| 4295 | 4347 | for (sections) |*section| { |
| 4296 | | if (section.comdat_result != .include) continue; |
| 4348 | if (section.parent_si == .null) continue; |
| 4297 | 4349 | |
| 4298 | 4350 | const ni = try coff.mf.addLastChildNode(gpa, section.parent_si.node(coff), .{ |
| 4299 | 4351 | .size = section.header.size_of_raw_data, |
| ... | ... | @@ -4457,7 +4509,7 @@ fn loadObject( |
| 4457 | 4509 | |
| 4458 | 4510 | const relocation_size = std.coff.Relocation.sizeOf(); |
| 4459 | 4511 | for (sections) |section| { |
| 4460 | | if (section.comdat_result != .include) continue; |
| 4512 | if (section.si == .null) continue; |
| 4461 | 4513 | |
| 4462 | 4514 | const loc_sym = section.si.get(coff); |
| 4463 | 4515 | assert(loc_sym.loc_relocs == .none); |
| ... | ... | @@ -5481,6 +5533,26 @@ pub fn flush( |
| 5481 | 5533 | pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { |
| 5482 | 5534 | const comp = coff.base.comp; |
| 5483 | 5535 | task: { |
| 5536 | while (coff.section_merge_pending_index < coff.section_merges.count()) { |
| 5537 | defer coff.section_merge_pending_index += 1; |
| 5538 | const sub_prog_node = coff.synth_prog_node.start( |
| 5539 | coff.section_merges.keys()[coff.section_merge_pending_index].toSlice(coff), |
| 5540 | 0, |
| 5541 | ); |
| 5542 | defer sub_prog_node.end(); |
| 5543 | coff.flushSectionMerge(coff.section_merge_pending_index) catch |err| switch (err) { |
| 5544 | //error.OutOfMemory => |e| return e, |
| 5545 | else => |e| return comp.link_diags.fail( |
| 5546 | "linker failed to merge section {s} into {s}: {t}", |
| 5547 | .{ |
| 5548 | coff.section_merges.keys()[coff.section_merge_pending_index].toSlice(coff), |
| 5549 | coff.section_merges.values()[coff.section_merge_pending_index].toSlice(coff), |
| 5550 | e, |
| 5551 | }, |
| 5552 | ), |
| 5553 | }; |
| 5554 | break :task; |
| 5555 | } |
| 5484 | 5556 | while (coff.pending_uavs.pop()) |pending_uav| { |
| 5485 | 5557 | const sub_prog_node = coff.idleProgNode(tid, coff.const_prog_node, .{ .uav = pending_uav.key }); |
| 5486 | 5558 | defer sub_prog_node.end(); |
| ... | ... | @@ -5655,7 +5727,8 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { |
| 5655 | 5727 | try coff.flushMember(pending_mi.key); |
| 5656 | 5728 | break :task; |
| 5657 | 5729 | } |
| 5658 | | // TODO: This and the next task ideally only run once, as it's wasteful otherwise |
| 5730 | // TODO: All the sort / shrink tasks ideally run only once - otherwise it's wasteful |
| 5731 | // Defer until exports_complete? |
| 5659 | 5732 | if (coff.export_table.pending_sort) { |
| 5660 | 5733 | defer coff.export_table.pending_sort = false; |
| 5661 | 5734 | const sub_prog_node = coff.idleProgNode( |
| ... | ... | @@ -5694,6 +5767,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { |
| 5694 | 5767 | break :task; |
| 5695 | 5768 | } |
| 5696 | 5769 | } |
| 5770 | if (coff.section_merge_pending_index < coff.section_merges.count()) return true; |
| 5697 | 5771 | if (coff.pending_uavs.count() > 0) return true; |
| 5698 | 5772 | if (coff.pending_input != null) return true; |
| 5699 | 5773 | if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true; |
| ... | ... | @@ -6805,6 +6879,70 @@ fn flushExportsSort(coff: *Coff) void { |
| 6805 | 6879 | }); |
| 6806 | 6880 | } |
| 6807 | 6881 | |
| 6882 | fn flushSectionMerge(coff: *Coff, index: u32) !void { |
| 6883 | assert(coff.isImage()); |
| 6884 | const from = coff.section_merges.keys()[index]; |
| 6885 | const to = coff.section_merges.values()[index]; |
| 6886 | assert(from != to); |
| 6887 | |
| 6888 | log.debug("flushSectionMerge({s}->{s})", .{ from.toSlice(coff), to.toSlice(coff) }); |
| 6889 | |
| 6890 | const opt_to_sec = coff.section_table.getPtr(to); |
| 6891 | if (coff.section_table.getPtr(from)) |from_sec| { |
| 6892 | const from_sym = from_sec.si.get(coff); |
| 6893 | if (opt_to_sec) |to_sec| { |
| 6894 | const to_sym = to_sec.si.get(coff); |
| 6895 | |
| 6896 | // TODO: Create a pseudo-section named `from` in `to`, copy `from_sec` ni into that pseudo section |
| 6897 | // TODO: Update .section_number for all contained syms |
| 6898 | // TODO: Remove `from_sec` from section table (set size = 0 and can do it in flushResized?). |
| 6899 | // This is non-trivial as we can't leave holes in the section table. |
| 6900 | // TODO: Merge section flags |
| 6901 | _ = to_sym; |
| 6902 | |
| 6903 | return coff.base.comp.link_diags.fail("TODO implement section to section merge", .{}); |
| 6904 | } else if (coff.pseudo_section_table.get(to)) |to_ps_si| { |
| 6905 | const to_sym = to_ps_si.get(coff); |
| 6906 | if (from_sym.section_number == to_sym.section_number) |
| 6907 | return; |
| 6908 | |
| 6909 | // TODO: Same as above, except place `from` into a node in `to_psmi`'s parent |
| 6910 | return coff.base.comp.link_diags.fail("TODO implement section to pseudosection merge", .{}); |
| 6911 | } |
| 6912 | |
| 6913 | // If `to` doesn't exist, /MERGE is defined as renaming `from` to `to`. |
| 6914 | // No other path will create image-level sections, so we can safely rename this now |
| 6915 | const from_name = &from_sec.si.get(coff).section_number.header(coff).name; |
| 6916 | const to_slice = to.toSlice(coff); |
| 6917 | @memcpy(from_name[0..to_slice.len], to_slice); |
| 6918 | @memset(from_name[to_slice.len..], 0); |
| 6919 | } else if (coff.pseudo_section_table.getIndex(from)) |from_index| { |
| 6920 | const from_psmi: Node.PseudoSectionMapIndex = @enumFromInt(from_index); |
| 6921 | const from_sym = from_psmi.symbol(coff).get(coff); |
| 6922 | if (opt_to_sec) |to_sec| { |
| 6923 | const to_sym = to_sec.si.get(coff); |
| 6924 | if (from_sym.section_number == to_sym.section_number) |
| 6925 | return; |
| 6926 | |
| 6927 | // TODO: Move from_psmi's node into to_sec |
| 6928 | // TODO: Update .section_number for all contained syms |
| 6929 | // TODO: Merge section flags |
| 6930 | |
| 6931 | return coff.base.comp.link_diags.fail("TODO implement pseudosection to section merge", .{}); |
| 6932 | } else if (coff.pseudo_section_table.get(to)) |to_ps_si| { |
| 6933 | const to_sym = to_ps_si.get(coff); |
| 6934 | if (from_sym.section_number == to_sym.section_number) |
| 6935 | return; |
| 6936 | |
| 6937 | // TODO: Same as above, but move from_psmi's node after to_psmi's node in its parent |
| 6938 | |
| 6939 | return coff.base.comp.link_diags.fail("TODO implement pseudosection to pseudosection merge", .{}); |
| 6940 | } |
| 6941 | |
| 6942 | // Renaming pseudo-sections have no effect on the output, so this is a no-op. |
| 6943 | } |
| 6944 | } |
| 6945 | |
| 6808 | 6946 | fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { |
| 6809 | 6947 | var rva = start_rva; |
| 6810 | 6948 | for ( |