authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-09-25 19:41:13-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-09-25 19:41:13-07:00
log0e876a6378b5380900cf8755f624a670e8716f80
tree79a0dbc66f377721ed37206fe803bd831fc71675
parent44422886561f5c0029f3d61898609220bd35deea
parent054dbb6798bd81cbea809999b2fdffc1b59e5ca3
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21514 from ziglang/elf-incr

elf: get incremental tests passing via `incr-check` tool

14 files changed, 330 insertions(+), 249 deletions(-)

src/link/Elf.zig+163-130
......@@ -147,6 +147,7 @@ thunks: std.ArrayListUnmanaged(Thunk) = .empty,
147147
148148/// List of output merge sections with deduped contents.
149149merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty,
150comment_merge_section_index: ?MergeSection.Index = null,
150151
151152first_eflags: ?elf.Elf64_Word = null,
152153
......@@ -568,19 +569,6 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64, min_align
568569 if (maybe_phdr) |phdr| phdr.p_filesz = needed_size;
569570 }
570571 shdr.sh_size = needed_size;
571
572 if (maybe_phdr) |phdr| {
573 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
574 if (needed_size > mem_capacity) {
575 var err = try self.base.addErrorWithNotes(2);
576 try err.addMsg("fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{phndx.?});
577 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
578 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
579 }
580
581 phdr.p_memsz = needed_size;
582 }
583
584572 self.markDirty(shdr_index);
585573}
586574
......@@ -1047,6 +1035,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
10471035 try self.updateMergeSectionSizes();
10481036 try self.updateSectionSizes();
10491037
1038 try self.addLoadPhdrs();
10501039 try self.allocatePhdrTable();
10511040 try self.allocateAllocSections();
10521041 try self.sortPhdrs();
......@@ -2790,6 +2779,7 @@ fn checkDuplicates(self: *Elf) !void {
27902779
27912780pub fn addCommentString(self: *Elf) !void {
27922781 const gpa = self.base.comp.gpa;
2782 if (self.comment_merge_section_index != null) return;
27932783 const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS);
27942784 const msec = self.mergeSection(msec_index);
27952785 const res = try msec.insertZ(gpa, "zig " ++ builtin.zig_version_string);
......@@ -2803,6 +2793,7 @@ pub fn addCommentString(self: *Elf) !void {
28032793 msub.entsize = 1;
28042794 msub.alive = true;
28052795 res.sub.* = msub_index;
2796 self.comment_merge_section_index = msec_index;
28062797}
28072798
28082799pub fn resolveMergeSections(self: *Elf) !void {
......@@ -2920,7 +2911,7 @@ fn initSyntheticSections(self: *Elf) !void {
29202911 .offset = std.math.maxInt(u64),
29212912 });
29222913 }
2923 if (comp.link_eh_frame_hdr) {
2914 if (comp.link_eh_frame_hdr and self.eh_frame_hdr_section_index == null) {
29242915 self.eh_frame_hdr_section_index = try self.addSection(.{
29252916 .name = try self.insertShString(".eh_frame_hdr"),
29262917 .type = elf.SHT_PROGBITS,
......@@ -2931,7 +2922,7 @@ fn initSyntheticSections(self: *Elf) !void {
29312922 }
29322923 }
29332924
2934 if (self.got.entries.items.len > 0) {
2925 if (self.got.entries.items.len > 0 and self.got_section_index == null) {
29352926 self.got_section_index = try self.addSection(.{
29362927 .name = try self.insertShString(".got"),
29372928 .type = elf.SHT_PROGBITS,
......@@ -2941,13 +2932,15 @@ fn initSyntheticSections(self: *Elf) !void {
29412932 });
29422933 }
29432934
2944 self.got_plt_section_index = try self.addSection(.{
2945 .name = try self.insertShString(".got.plt"),
2946 .type = elf.SHT_PROGBITS,
2947 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
2948 .addralign = @alignOf(u64),
2949 .offset = std.math.maxInt(u64),
2950 });
2935 if (self.got_plt_section_index == null) {
2936 self.got_plt_section_index = try self.addSection(.{
2937 .name = try self.insertShString(".got.plt"),
2938 .type = elf.SHT_PROGBITS,
2939 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
2940 .addralign = @alignOf(u64),
2941 .offset = std.math.maxInt(u64),
2942 });
2943 }
29512944
29522945 const needs_rela_dyn = blk: {
29532946 if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0)
......@@ -2960,7 +2953,7 @@ fn initSyntheticSections(self: *Elf) !void {
29602953 }
29612954 break :blk false;
29622955 };
2963 if (needs_rela_dyn) {
2956 if (needs_rela_dyn and self.rela_dyn_section_index == null) {
29642957 self.rela_dyn_section_index = try self.addSection(.{
29652958 .name = try self.insertShString(".rela.dyn"),
29662959 .type = elf.SHT_RELA,
......@@ -2972,24 +2965,28 @@ fn initSyntheticSections(self: *Elf) !void {
29722965 }
29732966
29742967 if (self.plt.symbols.items.len > 0) {
2975 self.plt_section_index = try self.addSection(.{
2976 .name = try self.insertShString(".plt"),
2977 .type = elf.SHT_PROGBITS,
2978 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
2979 .addralign = 16,
2980 .offset = std.math.maxInt(u64),
2981 });
2982 self.rela_plt_section_index = try self.addSection(.{
2983 .name = try self.insertShString(".rela.plt"),
2984 .type = elf.SHT_RELA,
2985 .flags = elf.SHF_ALLOC,
2986 .addralign = @alignOf(elf.Elf64_Rela),
2987 .entsize = @sizeOf(elf.Elf64_Rela),
2988 .offset = std.math.maxInt(u64),
2989 });
2968 if (self.plt_section_index == null) {
2969 self.plt_section_index = try self.addSection(.{
2970 .name = try self.insertShString(".plt"),
2971 .type = elf.SHT_PROGBITS,
2972 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
2973 .addralign = 16,
2974 .offset = std.math.maxInt(u64),
2975 });
2976 }
2977 if (self.rela_plt_section_index == null) {
2978 self.rela_plt_section_index = try self.addSection(.{
2979 .name = try self.insertShString(".rela.plt"),
2980 .type = elf.SHT_RELA,
2981 .flags = elf.SHF_ALLOC,
2982 .addralign = @alignOf(elf.Elf64_Rela),
2983 .entsize = @sizeOf(elf.Elf64_Rela),
2984 .offset = std.math.maxInt(u64),
2985 });
2986 }
29902987 }
29912988
2992 if (self.plt_got.symbols.items.len > 0) {
2989 if (self.plt_got.symbols.items.len > 0 and self.plt_got_section_index == null) {
29932990 self.plt_got_section_index = try self.addSection(.{
29942991 .name = try self.insertShString(".plt.got"),
29952992 .type = elf.SHT_PROGBITS,
......@@ -2999,7 +2996,7 @@ fn initSyntheticSections(self: *Elf) !void {
29992996 });
30002997 }
30012998
3002 if (self.copy_rel.symbols.items.len > 0) {
2999 if (self.copy_rel.symbols.items.len > 0 and self.copy_rel_section_index == null) {
30033000 self.copy_rel_section_index = try self.addSection(.{
30043001 .name = try self.insertShString(".copyrel"),
30053002 .type = elf.SHT_NOBITS,
......@@ -3017,7 +3014,7 @@ fn initSyntheticSections(self: *Elf) !void {
30173014 if (self.base.isStatic() and !comp.config.pie) break :blk false;
30183015 break :blk target.dynamic_linker.get() != null;
30193016 };
3020 if (needs_interp) {
3017 if (needs_interp and self.interp_section_index == null) {
30213018 self.interp_section_index = try self.addSection(.{
30223019 .name = try self.insertShString(".interp"),
30233020 .type = elf.SHT_PROGBITS,
......@@ -3028,68 +3025,82 @@ fn initSyntheticSections(self: *Elf) !void {
30283025 }
30293026
30303027 if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) {
3031 self.dynstrtab_section_index = try self.addSection(.{
3032 .name = try self.insertShString(".dynstr"),
3033 .flags = elf.SHF_ALLOC,
3034 .type = elf.SHT_STRTAB,
3035 .entsize = 1,
3036 .addralign = 1,
3037 .offset = std.math.maxInt(u64),
3038 });
3039 self.dynamic_section_index = try self.addSection(.{
3040 .name = try self.insertShString(".dynamic"),
3041 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
3042 .type = elf.SHT_DYNAMIC,
3043 .entsize = @sizeOf(elf.Elf64_Dyn),
3044 .addralign = @alignOf(elf.Elf64_Dyn),
3045 .offset = std.math.maxInt(u64),
3046 });
3047 self.dynsymtab_section_index = try self.addSection(.{
3048 .name = try self.insertShString(".dynsym"),
3049 .flags = elf.SHF_ALLOC,
3050 .type = elf.SHT_DYNSYM,
3051 .addralign = @alignOf(elf.Elf64_Sym),
3052 .entsize = @sizeOf(elf.Elf64_Sym),
3053 .info = 1,
3054 .offset = std.math.maxInt(u64),
3055 });
3056 self.hash_section_index = try self.addSection(.{
3057 .name = try self.insertShString(".hash"),
3058 .flags = elf.SHF_ALLOC,
3059 .type = elf.SHT_HASH,
3060 .addralign = 4,
3061 .entsize = 4,
3062 .offset = std.math.maxInt(u64),
3063 });
3064 self.gnu_hash_section_index = try self.addSection(.{
3065 .name = try self.insertShString(".gnu.hash"),
3066 .flags = elf.SHF_ALLOC,
3067 .type = elf.SHT_GNU_HASH,
3068 .addralign = 8,
3069 .offset = std.math.maxInt(u64),
3070 });
3071
3072 const needs_versions = for (self.dynsym.entries.items) |entry| {
3073 const sym = self.symbol(entry.ref).?;
3074 if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true;
3075 } else false;
3076 if (needs_versions) {
3077 self.versym_section_index = try self.addSection(.{
3078 .name = try self.insertShString(".gnu.version"),
3028 if (self.dynstrtab_section_index == null) {
3029 self.dynstrtab_section_index = try self.addSection(.{
3030 .name = try self.insertShString(".dynstr"),
30793031 .flags = elf.SHF_ALLOC,
3080 .type = elf.SHT_GNU_VERSYM,
3081 .addralign = @alignOf(elf.Elf64_Versym),
3082 .entsize = @sizeOf(elf.Elf64_Versym),
3032 .type = elf.SHT_STRTAB,
3033 .entsize = 1,
3034 .addralign = 1,
30833035 .offset = std.math.maxInt(u64),
30843036 });
3085 self.verneed_section_index = try self.addSection(.{
3086 .name = try self.insertShString(".gnu.version_r"),
3037 }
3038 if (self.dynamic_section_index == null) {
3039 self.dynamic_section_index = try self.addSection(.{
3040 .name = try self.insertShString(".dynamic"),
3041 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
3042 .type = elf.SHT_DYNAMIC,
3043 .entsize = @sizeOf(elf.Elf64_Dyn),
3044 .addralign = @alignOf(elf.Elf64_Dyn),
3045 .offset = std.math.maxInt(u64),
3046 });
3047 }
3048 if (self.dynsymtab_section_index == null) {
3049 self.dynsymtab_section_index = try self.addSection(.{
3050 .name = try self.insertShString(".dynsym"),
30873051 .flags = elf.SHF_ALLOC,
3088 .type = elf.SHT_GNU_VERNEED,
3089 .addralign = @alignOf(elf.Elf64_Verneed),
3052 .type = elf.SHT_DYNSYM,
3053 .addralign = @alignOf(elf.Elf64_Sym),
3054 .entsize = @sizeOf(elf.Elf64_Sym),
3055 .info = 1,
30903056 .offset = std.math.maxInt(u64),
30913057 });
30923058 }
3059 if (self.hash_section_index == null) {
3060 self.hash_section_index = try self.addSection(.{
3061 .name = try self.insertShString(".hash"),
3062 .flags = elf.SHF_ALLOC,
3063 .type = elf.SHT_HASH,
3064 .addralign = 4,
3065 .entsize = 4,
3066 .offset = std.math.maxInt(u64),
3067 });
3068 }
3069 if (self.gnu_hash_section_index == null) {
3070 self.gnu_hash_section_index = try self.addSection(.{
3071 .name = try self.insertShString(".gnu.hash"),
3072 .flags = elf.SHF_ALLOC,
3073 .type = elf.SHT_GNU_HASH,
3074 .addralign = 8,
3075 .offset = std.math.maxInt(u64),
3076 });
3077 }
3078
3079 const needs_versions = for (self.dynsym.entries.items) |entry| {
3080 const sym = self.symbol(entry.ref).?;
3081 if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true;
3082 } else false;
3083 if (needs_versions) {
3084 if (self.versym_section_index == null) {
3085 self.versym_section_index = try self.addSection(.{
3086 .name = try self.insertShString(".gnu.version"),
3087 .flags = elf.SHF_ALLOC,
3088 .type = elf.SHT_GNU_VERSYM,
3089 .addralign = @alignOf(elf.Elf64_Versym),
3090 .entsize = @sizeOf(elf.Elf64_Versym),
3091 .offset = std.math.maxInt(u64),
3092 });
3093 }
3094 if (self.verneed_section_index == null) {
3095 self.verneed_section_index = try self.addSection(.{
3096 .name = try self.insertShString(".gnu.version_r"),
3097 .flags = elf.SHF_ALLOC,
3098 .type = elf.SHT_GNU_VERNEED,
3099 .addralign = @alignOf(elf.Elf64_Verneed),
3100 .offset = std.math.maxInt(u64),
3101 });
3102 }
3103 }
30933104 }
30943105
30953106 try self.initSymtab();
......@@ -3136,36 +3147,38 @@ pub fn initShStrtab(self: *Elf) !void {
31363147fn initSpecialPhdrs(self: *Elf) !void {
31373148 comptime assert(max_number_of_special_phdrs == 5);
31383149
3139 if (self.interp_section_index != null) {
3150 if (self.interp_section_index != null and self.phdr_interp_index == null) {
31403151 self.phdr_interp_index = try self.addPhdr(.{
31413152 .type = elf.PT_INTERP,
31423153 .flags = elf.PF_R,
31433154 .@"align" = 1,
31443155 });
31453156 }
3146 if (self.dynamic_section_index != null) {
3157 if (self.dynamic_section_index != null and self.phdr_dynamic_index == null) {
31473158 self.phdr_dynamic_index = try self.addPhdr(.{
31483159 .type = elf.PT_DYNAMIC,
31493160 .flags = elf.PF_R | elf.PF_W,
31503161 });
31513162 }
3152 if (self.eh_frame_hdr_section_index != null) {
3163 if (self.eh_frame_hdr_section_index != null and self.phdr_gnu_eh_frame_index == null) {
31533164 self.phdr_gnu_eh_frame_index = try self.addPhdr(.{
31543165 .type = elf.PT_GNU_EH_FRAME,
31553166 .flags = elf.PF_R,
31563167 });
31573168 }
3158 self.phdr_gnu_stack_index = try self.addPhdr(.{
3159 .type = elf.PT_GNU_STACK,
3160 .flags = elf.PF_W | elf.PF_R,
3161 .memsz = self.base.stack_size,
3162 .@"align" = 1,
3163 });
3169 if (self.phdr_gnu_stack_index == null) {
3170 self.phdr_gnu_stack_index = try self.addPhdr(.{
3171 .type = elf.PT_GNU_STACK,
3172 .flags = elf.PF_W | elf.PF_R,
3173 .memsz = self.base.stack_size,
3174 .@"align" = 1,
3175 });
3176 }
31643177
31653178 const has_tls = for (self.sections.items(.shdr)) |shdr| {
31663179 if (shdr.sh_flags & elf.SHF_TLS != 0) break true;
31673180 } else false;
3168 if (has_tls) {
3181 if (has_tls and self.phdr_tls_index == null) {
31693182 self.phdr_tls_index = try self.addPhdr(.{
31703183 .type = elf.PT_TLS,
31713184 .flags = elf.PF_R,
......@@ -3704,27 +3717,19 @@ fn getMaxNumberOfPhdrs() u64 {
37043717 return num;
37053718}
37063719
3707/// Calculates how many segments (PT_LOAD progam headers) are required
3708/// to cover the set of sections.
3709/// We permit a maximum of 3**2 number of segments.
3710fn calcNumberOfSegments(self: *Elf) usize {
3711 var covers: [9]bool = [_]bool{false} ** 9;
3720fn addLoadPhdrs(self: *Elf) error{OutOfMemory}!void {
37123721 for (self.sections.items(.shdr)) |shdr| {
37133722 if (shdr.sh_type == elf.SHT_NULL) continue;
37143723 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
37153724 const flags = shdrToPhdrFlags(shdr.sh_flags);
3716 covers[flags - 1] = true;
3717 }
3718 var count: usize = 0;
3719 for (covers) |cover| {
3720 if (cover) count += 1;
3725 if (self.getPhdr(.{ .flags = flags, .type = elf.PT_LOAD }) == null) {
3726 _ = try self.addPhdr(.{ .flags = flags, .type = elf.PT_LOAD });
3727 }
37213728 }
3722 return count;
37233729}
37243730
37253731/// Allocates PHDR table in virtual memory and in file.
37263732fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
3727 const new_load_segments = self.calcNumberOfSegments();
37283733 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
37293734 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
37303735
......@@ -3736,7 +3741,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
37363741 .p32 => @sizeOf(elf.Elf32_Phdr),
37373742 .p64 => @sizeOf(elf.Elf64_Phdr),
37383743 };
3739 const needed_size = (self.phdrs.items.len + new_load_segments) * phsize;
3744 const needed_size = self.phdrs.items.len * phsize;
37403745 const available_space = self.allocatedSize(phdr_table.p_offset);
37413746
37423747 if (needed_size > available_space) {
......@@ -3879,15 +3884,14 @@ pub fn allocateAllocSections(self: *Elf) !void {
38793884
38803885 const first = slice.items(.shdr)[cover.items[0]];
38813886 var new_offset = try self.findFreeSpace(filesz, @"align");
3882 const phndx = try self.addPhdr(.{
3883 .type = elf.PT_LOAD,
3884 .offset = new_offset,
3885 .addr = first.sh_addr,
3886 .memsz = memsz,
3887 .filesz = filesz,
3888 .@"align" = @"align",
3889 .flags = shdrToPhdrFlags(first.sh_flags),
3890 });
3887 const phndx = self.getPhdr(.{ .type = elf.PT_LOAD, .flags = shdrToPhdrFlags(first.sh_flags) }).?;
3888 const phdr = &self.phdrs.items[phndx];
3889 phdr.p_offset = new_offset;
3890 phdr.p_vaddr = first.sh_addr;
3891 phdr.p_paddr = first.sh_addr;
3892 phdr.p_memsz = memsz;
3893 phdr.p_filesz = filesz;
3894 phdr.p_align = @"align";
38913895
38923896 for (cover.items) |shndx| {
38933897 const shdr = &slice.items(.shdr)[shndx];
......@@ -4102,6 +4106,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
41024106 nlocals += @intCast(self.sections.slice().len);
41034107
41044108 if (self.requiresThunks()) for (self.thunks.items) |*th| {
4109 th.output_symtab_ctx.reset();
41054110 th.output_symtab_ctx.ilocal = nlocals;
41064111 th.calcSymtabSize(self);
41074112 nlocals += th.output_symtab_ctx.nlocals;
......@@ -4113,6 +4118,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
41134118 const ctx = switch (file_ptr) {
41144119 inline else => |x| &x.output_symtab_ctx,
41154120 };
4121 ctx.reset();
41164122 ctx.ilocal = nlocals;
41174123 ctx.iglobal = nglobals;
41184124 try file_ptr.updateSymtabSize(self);
......@@ -4122,6 +4128,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
41224128 }
41234129
41244130 if (self.got_section_index) |_| {
4131 self.got.output_symtab_ctx.reset();
41254132 self.got.output_symtab_ctx.ilocal = nlocals;
41264133 self.got.updateSymtabSize(self);
41274134 nlocals += self.got.output_symtab_ctx.nlocals;
......@@ -4129,6 +4136,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
41294136 }
41304137
41314138 if (self.plt_section_index) |_| {
4139 self.plt.output_symtab_ctx.reset();
41324140 self.plt.output_symtab_ctx.ilocal = nlocals;
41334141 self.plt.updateSymtabSize(self);
41344142 nlocals += self.plt.output_symtab_ctx.nlocals;
......@@ -4136,6 +4144,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
41364144 }
41374145
41384146 if (self.plt_got_section_index) |_| {
4147 self.plt_got.output_symtab_ctx.reset();
41394148 self.plt_got.output_symtab_ctx.ilocal = nlocals;
41404149 self.plt_got.updateSymtabSize(self);
41414150 nlocals += self.plt_got.output_symtab_ctx.nlocals;
......@@ -4334,6 +4343,9 @@ pub fn writeSymtab(self: *Elf) !void {
43344343
43354344 try self.symtab.resize(gpa, nsyms);
43364345 const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow;
4346 // TODO we could resize instead and in ZigObject/Object always access as slice
4347 self.strtab.clearRetainingCapacity();
4348 self.strtab.appendAssumeCapacity(0);
43374349 try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size);
43384350
43394351 for (slice.items(.shdr), 0..) |shdr, shndx| {
......@@ -4714,7 +4726,20 @@ pub fn isEffectivelyDynLib(self: Elf) bool {
47144726 };
47154727}
47164728
4717pub fn addPhdr(self: *Elf, opts: struct {
4729fn getPhdr(self: *Elf, opts: struct {
4730 type: u32 = 0,
4731 flags: u32 = 0,
4732}) ?u16 {
4733 for (self.phdrs.items, 0..) |phdr, phndx| {
4734 if (self.phdr_table_load_index) |index| {
4735 if (phndx == index) continue;
4736 }
4737 if (phdr.p_type == opts.type and phdr.p_flags == opts.flags) return @intCast(phndx);
4738 }
4739 return null;
4740}
4741
4742fn addPhdr(self: *Elf, opts: struct {
47184743 type: u32 = 0,
47194744 flags: u32 = 0,
47204745 @"align": u64 = 0,
......@@ -5305,7 +5330,7 @@ fn fmtDumpState(
53055330 {
53065331 try writer.writeAll("atom lists\n");
53075332 for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| {
5308 try writer.print("shdr({d}) : {s} : {}", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) });
5333 try writer.print("shdr({d}) : {s} : {}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) });
53095334 }
53105335 }
53115336
......@@ -5437,6 +5462,14 @@ pub const SymtabCtx = struct {
54375462 nlocals: u32 = 0,
54385463 nglobals: u32 = 0,
54395464 strsize: u32 = 0,
5465
5466 pub fn reset(ctx: *SymtabCtx) void {
5467 ctx.ilocal = 0;
5468 ctx.iglobal = 0;
5469 ctx.nlocals = 0;
5470 ctx.nglobals = 0;
5471 ctx.strsize = 0;
5472 }
54405473};
54415474
54425475pub const null_sym = elf.Elf64_Sym{
src/link/Elf/AtomList.zig+3
......@@ -19,6 +19,9 @@ pub fn offset(list: AtomList, elf_file: *Elf) u64 {
1919}
2020
2121pub fn updateSize(list: *AtomList, elf_file: *Elf) void {
22 // TODO perhaps a 'stale' flag would be better here?
23 list.size = 0;
24 list.alignment = .@"1";
2225 for (list.atoms.items) |ref| {
2326 const atom_ptr = elf_file.atom(ref).?;
2427 assert(atom_ptr.alive);
src/link/Elf/LinkerDefined.zig+1
......@@ -145,6 +145,7 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
145145 try self.symbols_resolver.ensureUnusedCapacity(gpa, nsyms);
146146
147147 for (slice.items(.shdr)) |shdr| {
148 // TODO use getOrPut for incremental so that we don't create duplicates
148149 if (elf_file.getStartStopBasename(shdr)) |name| {
149150 const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
150151 defer gpa.free(start_name);
src/link/Elf/merge_section.zig+4
......@@ -95,6 +95,10 @@ pub const MergeSection = struct {
9595 }
9696
9797 pub fn updateSize(msec: *MergeSection) void {
98 // TODO a 'stale' flag would be better here perhaps?
99 msec.size = 0;
100 msec.alignment = .@"1";
101 msec.entsize = 0;
98102 for (msec.finalized_subsections.items) |msub_index| {
99103 const msub = msec.mergeSubsection(msub_index);
100104 assert(msub.alive);
test/incremental/add_decl+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/add_decl_namespaced+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/delete_comptime_decls+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45pub fn main() void {}
test/incremental/hello+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/modify_inline_fn+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/move_src+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const std = @import("std");
test/incremental/remove_enum_field+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const MyEnum = enum(u8) {
test/incremental/type_becomes_comptime_only+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const SomeType = u32;
test/incremental/unreferenced_error+2-1
......@@ -1,4 +1,5 @@
1#target=x86_64-linux
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
23#update=initial version
34#file=main.zig
45const std = @import("std");
tools/incr-check.zig+141-110
......@@ -3,13 +3,7 @@ const fatal = std.process.fatal;
33const Allocator = std.mem.Allocator;
44const Cache = std.Build.Cache;
55
6const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--emit none|bin|c] [--zig-cc-binary /path/to/zig]";
7
8const EmitMode = enum {
9 none,
10 bin,
11 c,
12};
6const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--debug-link] [--zig-cc-binary /path/to/zig]";
137
148pub fn main() !void {
159 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
......@@ -20,21 +14,19 @@ pub fn main() !void {
2014 var opt_input_file_name: ?[]const u8 = null;
2115 var opt_lib_dir: ?[]const u8 = null;
2216 var opt_cc_zig: ?[]const u8 = null;
23 var emit: EmitMode = .bin;
2417 var debug_zcu = false;
18 var debug_link = false;
2519
2620 var arg_it = try std.process.argsWithAllocator(arena);
2721 _ = arg_it.skip();
2822 while (arg_it.next()) |arg| {
2923 if (arg.len > 0 and arg[0] == '-') {
30 if (std.mem.eql(u8, arg, "--emit")) {
31 const emit_str = arg_it.next() orelse fatal("expected arg after '--emit'\n{s}", .{usage});
32 emit = std.meta.stringToEnum(EmitMode, emit_str) orelse
33 fatal("invalid emit mode '{s}'\n{s}", .{ emit_str, usage });
34 } else if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
24 if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
3525 opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage});
3626 } else if (std.mem.eql(u8, arg, "--debug-zcu")) {
3727 debug_zcu = true;
28 } else if (std.mem.eql(u8, arg, "--debug-link")) {
29 debug_link = true;
3830 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {
3931 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});
4032 } else {
......@@ -73,104 +65,114 @@ pub fn main() !void {
7365 else
7466 null;
7567
76 var child_args: std.ArrayListUnmanaged([]const u8) = .empty;
77 try child_args.appendSlice(arena, &.{
78 resolved_zig_exe,
79 "build-exe",
80 case.root_source_file,
81 "-fincremental",
82 "-target",
83 case.target_query,
84 "--cache-dir",
85 ".local-cache",
86 "--global-cache-dir",
87 ".global_cache",
88 "--listen=-",
89 });
90 if (opt_resolved_lib_dir) |resolved_lib_dir| {
91 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });
92 }
93 switch (emit) {
94 .bin => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),
95 .none => try child_args.append(arena, "-fno-emit-bin"),
96 .c => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
97 }
98 if (debug_zcu) {
99 try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });
100 }
68 const debug_log_verbose = debug_zcu or debug_link;
69
70 for (case.targets) |target| {
71 std.log.scoped(.status).info("target: '{s}-{s}'", .{ target.query, @tagName(target.backend) });
10172
102 var child = std.process.Child.init(child_args.items, arena);
103 child.stdin_behavior = .Pipe;
104 child.stdout_behavior = .Pipe;
105 child.stderr_behavior = .Pipe;
106 child.progress_node = child_prog_node;
107 child.cwd_dir = tmp_dir;
108 child.cwd = tmp_dir_path;
109
110 var cc_child_args: std.ArrayListUnmanaged([]const u8) = .empty;
111 if (emit == .c) {
112 const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
113 try std.fs.path.relative(arena, tmp_dir_path, cc_zig_exe)
114 else
115 resolved_zig_exe;
116
117 try cc_child_args.appendSlice(arena, &.{
118 resolved_cc_zig_exe,
119 "cc",
73 var child_args: std.ArrayListUnmanaged([]const u8) = .empty;
74 try child_args.appendSlice(arena, &.{
75 resolved_zig_exe,
76 "build-exe",
77 case.root_source_file,
78 "-fincremental",
12079 "-target",
121 case.target_query,
122 "-I",
123 opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using '--emit c'", .{}),
124 "-o",
80 target.query,
81 "--cache-dir",
82 ".local-cache",
83 "--global-cache-dir",
84 ".global_cache",
85 "--listen=-",
12586 });
126 }
87 if (opt_resolved_lib_dir) |resolved_lib_dir| {
88 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });
89 }
90 switch (target.backend) {
91 .sema => try child_args.append(arena, "-fno-emit-bin"),
92 .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),
93 .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }),
94 .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
95 }
96 if (debug_zcu) {
97 try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });
98 }
99 if (debug_link) {
100 try child_args.appendSlice(arena, &.{ "--debug-log", "link", "--debug-log", "link_state", "--debug-log", "link_relocs" });
101 }
127102
128 var eval: Eval = .{
129 .arena = arena,
130 .case = case,
131 .tmp_dir = tmp_dir,
132 .tmp_dir_path = tmp_dir_path,
133 .child = &child,
134 .allow_stderr = debug_zcu,
135 .emit = emit,
136 .cc_child_args = &cc_child_args,
137 };
103 var child = std.process.Child.init(child_args.items, arena);
104 child.stdin_behavior = .Pipe;
105 child.stdout_behavior = .Pipe;
106 child.stderr_behavior = .Pipe;
107 child.progress_node = child_prog_node;
108 child.cwd_dir = tmp_dir;
109 child.cwd = tmp_dir_path;
110
111 var cc_child_args: std.ArrayListUnmanaged([]const u8) = .empty;
112 if (target.backend == .cbe) {
113 const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
114 try std.fs.path.relative(arena, tmp_dir_path, cc_zig_exe)
115 else
116 resolved_zig_exe;
117
118 try cc_child_args.appendSlice(arena, &.{
119 resolved_cc_zig_exe,
120 "cc",
121 "-target",
122 target.query,
123 "-I",
124 opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using backend 'cbe'", .{}),
125 "-o",
126 });
127 }
138128
139 try child.spawn();
129 var eval: Eval = .{
130 .arena = arena,
131 .case = case,
132 .target = target,
133 .tmp_dir = tmp_dir,
134 .tmp_dir_path = tmp_dir_path,
135 .child = &child,
136 .allow_stderr = debug_log_verbose,
137 .cc_child_args = &cc_child_args,
138 };
140139
141 var poller = std.io.poll(arena, Eval.StreamEnum, .{
142 .stdout = child.stdout.?,
143 .stderr = child.stderr.?,
144 });
145 defer poller.deinit();
140 try child.spawn();
146141
147 for (case.updates) |update| {
148 var update_node = prog_node.start(update.name, 0);
149 defer update_node.end();
142 var poller = std.io.poll(arena, Eval.StreamEnum, .{
143 .stdout = child.stdout.?,
144 .stderr = child.stderr.?,
145 });
146 defer poller.deinit();
150147
151 if (debug_zcu) {
152 std.log.info("=== START UPDATE '{s}' ===", .{update.name});
153 }
148 for (case.updates) |update| {
149 var update_node = prog_node.start(update.name, 0);
150 defer update_node.end();
154151
155 eval.write(update);
156 try eval.requestUpdate();
157 try eval.check(&poller, update, update_node);
158 }
152 if (debug_log_verbose) {
153 std.log.scoped(.status).info("update: '{s}'", .{update.name});
154 }
155
156 eval.write(update);
157 try eval.requestUpdate();
158 try eval.check(&poller, update, update_node);
159 }
159160
160 try eval.end(&poller);
161 try eval.end(&poller);
161162
162 waitChild(&child);
163 waitChild(&child);
164 }
163165}
164166
165167const Eval = struct {
166168 arena: Allocator,
167169 case: Case,
170 target: Case.Target,
168171 tmp_dir: std.fs.Dir,
169172 tmp_dir_path: []const u8,
170173 child: *std.process.Child,
171174 allow_stderr: bool,
172 emit: EmitMode,
173 /// When `emit == .c`, this contains the first few arguments to `zig cc` to build the generated binary.
175 /// When `target.backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary.
174176 /// The arguments `out.c in.c` must be appended before spawning the subprocess.
175177 cc_child_args: *std.ArrayListUnmanaged([]const u8),
176178
......@@ -254,11 +256,10 @@ const Eval = struct {
254256 }
255257 }
256258
257 if (eval.emit == .none) {
259 if (eval.target.backend == .sema) {
258260 try eval.checkSuccessOutcome(update, null, prog_node);
259261 // This message indicates the end of the update.
260262 stdout.discard(body.len);
261 return;
262263 }
263264
264265 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
......@@ -268,11 +269,11 @@ const Eval = struct {
268269 const bin_name = try std.zig.binNameAlloc(arena, .{
269270 .root_name = name,
270271 .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{
271 .arch_os_abi = eval.case.target_query,
272 .object_format = switch (eval.emit) {
273 .none => unreachable,
274 .bin => null,
275 .c => "c",
272 .arch_os_abi = eval.target.query,
273 .object_format = switch (eval.target.backend) {
274 .sema => unreachable,
275 .selfhosted, .llvm => null,
276 .cbe => "c",
276277 },
277278 })),
278279 .output_mode = .Exe,
......@@ -282,7 +283,6 @@ const Eval = struct {
282283 try eval.checkSuccessOutcome(update, bin_path, prog_node);
283284 // This message indicates the end of the update.
284285 stdout.discard(body.len);
285 return;
286286 },
287287 else => {
288288 // Ignore other messages.
......@@ -329,14 +329,14 @@ const Eval = struct {
329329 .stdout, .exit_code => {},
330330 }
331331 const emitted_path = opt_emitted_path orelse {
332 std.debug.assert(eval.emit == .none);
332 std.debug.assert(eval.target.backend == .sema);
333333 return;
334334 };
335335
336 const binary_path = switch (eval.emit) {
337 .none => unreachable,
338 .bin => emitted_path,
339 .c => bin: {
336 const binary_path = switch (eval.target.backend) {
337 .sema => unreachable,
338 .selfhosted, .llvm => emitted_path,
339 .cbe => bin: {
340340 const rand_int = std.crypto.random.int(u64);
341341 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
342342 try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node);
......@@ -462,7 +462,26 @@ const Eval = struct {
462462const Case = struct {
463463 updates: []Update,
464464 root_source_file: []const u8,
465 target_query: []const u8,
465 targets: []const Target,
466
467 const Target = struct {
468 query: []const u8,
469 backend: Backend,
470 const Backend = enum {
471 /// Run semantic analysis only. Runtime output will not be tested, but we still verify
472 /// that compilation succeeds. Corresponds to `-fno-emit-bin`.
473 sema,
474 /// Use the self-hosted code generation backend for this target.
475 /// Corresponds to `-fno-llvm -fno-lld`.
476 selfhosted,
477 /// Use the LLVM backend.
478 /// Corresponds to `-fllvm -flld`.
479 llvm,
480 /// Use the C backend. The output is compiled with `zig cc`.
481 /// Corresponds to `-ofmt=c`.
482 cbe,
483 };
484 };
466485
467486 const Update = struct {
468487 name: []const u8,
......@@ -492,9 +511,9 @@ const Case = struct {
492511 };
493512
494513 fn parse(arena: Allocator, bytes: []const u8) !Case {
514 var targets: std.ArrayListUnmanaged(Target) = .empty;
495515 var updates: std.ArrayListUnmanaged(Update) = .empty;
496516 var changes: std.ArrayListUnmanaged(FullContents) = .empty;
497 var target_query: ?[]const u8 = null;
498517 var it = std.mem.splitScalar(u8, bytes, '\n');
499518 var line_n: usize = 1;
500519 var root_source_file: ?[]const u8 = null;
......@@ -506,8 +525,16 @@ const Case = struct {
506525 if (val.len == 0) {
507526 fatal("line {d}: missing value", .{line_n});
508527 } else if (std.mem.eql(u8, key, "target")) {
509 if (target_query != null) fatal("line {d}: duplicate target", .{line_n});
510 target_query = val;
528 const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse
529 fatal("line {d}: target does not include backend", .{line_n});
530 const query = val[0..split_idx];
531 const backend_str = val[split_idx + 1 ..];
532 const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse
533 fatal("line {d}: invalid backend '{s}'", .{ line_n, backend_str });
534 try targets.append(arena, .{
535 .query = query,
536 .backend = backend,
537 });
511538 } else if (std.mem.eql(u8, key, "update")) {
512539 if (updates.items.len > 0) {
513540 const last_update = &updates.items[updates.items.len - 1];
......@@ -559,15 +586,19 @@ const Case = struct {
559586 }
560587 }
561588
589 if (targets.items.len == 0) {
590 fatal("missing target", .{});
591 }
592
562593 if (changes.items.len > 0) {
563594 const last_update = &updates.items[updates.items.len - 1];
564 last_update.changes = try changes.toOwnedSlice(arena);
595 last_update.changes = changes.items; // arena so no need for toOwnedSlice
565596 }
566597
567598 return .{
568599 .updates = updates.items,
569600 .root_source_file = root_source_file orelse fatal("missing root source file", .{}),
570 .target_query = target_query orelse fatal("missing target", .{}),
601 .targets = targets.items, // arena so no need for toOwnedSlice
571602 };
572603 }
573604};