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,...@@ -147,6 +147,7 @@ thunks: std.ArrayListUnmanaged(Thunk) = .empty,
147147
148/// List of output merge sections with deduped contents.148/// List of output merge sections with deduped contents.
149merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty,149merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty,
150comment_merge_section_index: ?MergeSection.Index = null,
150151
151first_eflags: ?elf.Elf64_Word = null,152first_eflags: ?elf.Elf64_Word = null,
152153
...@@ -568,19 +569,6 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64, min_align...@@ -568,19 +569,6 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64, min_align
568 if (maybe_phdr) |phdr| phdr.p_filesz = needed_size;569 if (maybe_phdr) |phdr| phdr.p_filesz = needed_size;
569 }570 }
570 shdr.sh_size = needed_size;571 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
584 self.markDirty(shdr_index);572 self.markDirty(shdr_index);
585}573}
586574
...@@ -1047,6 +1035,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -1047,6 +1035,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1047 try self.updateMergeSectionSizes();1035 try self.updateMergeSectionSizes();
1048 try self.updateSectionSizes();1036 try self.updateSectionSizes();
10491037
1038 try self.addLoadPhdrs();
1050 try self.allocatePhdrTable();1039 try self.allocatePhdrTable();
1051 try self.allocateAllocSections();1040 try self.allocateAllocSections();
1052 try self.sortPhdrs();1041 try self.sortPhdrs();
...@@ -2790,6 +2779,7 @@ fn checkDuplicates(self: *Elf) !void {...@@ -2790,6 +2779,7 @@ fn checkDuplicates(self: *Elf) !void {
27902779
2791pub fn addCommentString(self: *Elf) !void {2780pub fn addCommentString(self: *Elf) !void {
2792 const gpa = self.base.comp.gpa;2781 const gpa = self.base.comp.gpa;
2782 if (self.comment_merge_section_index != null) return;
2793 const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS);2783 const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS);
2794 const msec = self.mergeSection(msec_index);2784 const msec = self.mergeSection(msec_index);
2795 const res = try msec.insertZ(gpa, "zig " ++ builtin.zig_version_string);2785 const res = try msec.insertZ(gpa, "zig " ++ builtin.zig_version_string);
...@@ -2803,6 +2793,7 @@ pub fn addCommentString(self: *Elf) !void {...@@ -2803,6 +2793,7 @@ pub fn addCommentString(self: *Elf) !void {
2803 msub.entsize = 1;2793 msub.entsize = 1;
2804 msub.alive = true;2794 msub.alive = true;
2805 res.sub.* = msub_index;2795 res.sub.* = msub_index;
2796 self.comment_merge_section_index = msec_index;
2806}2797}
28072798
2808pub fn resolveMergeSections(self: *Elf) !void {2799pub fn resolveMergeSections(self: *Elf) !void {
...@@ -2920,7 +2911,7 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -2920,7 +2911,7 @@ fn initSyntheticSections(self: *Elf) !void {
2920 .offset = std.math.maxInt(u64),2911 .offset = std.math.maxInt(u64),
2921 });2912 });
2922 }2913 }
2923 if (comp.link_eh_frame_hdr) {2914 if (comp.link_eh_frame_hdr and self.eh_frame_hdr_section_index == null) {
2924 self.eh_frame_hdr_section_index = try self.addSection(.{2915 self.eh_frame_hdr_section_index = try self.addSection(.{
2925 .name = try self.insertShString(".eh_frame_hdr"),2916 .name = try self.insertShString(".eh_frame_hdr"),
2926 .type = elf.SHT_PROGBITS,2917 .type = elf.SHT_PROGBITS,
...@@ -2931,7 +2922,7 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -2931,7 +2922,7 @@ fn initSyntheticSections(self: *Elf) !void {
2931 }2922 }
2932 }2923 }
29332924
2934 if (self.got.entries.items.len > 0) {2925 if (self.got.entries.items.len > 0 and self.got_section_index == null) {
2935 self.got_section_index = try self.addSection(.{2926 self.got_section_index = try self.addSection(.{
2936 .name = try self.insertShString(".got"),2927 .name = try self.insertShString(".got"),
2937 .type = elf.SHT_PROGBITS,2928 .type = elf.SHT_PROGBITS,
...@@ -2941,13 +2932,15 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -2941,13 +2932,15 @@ fn initSyntheticSections(self: *Elf) !void {
2941 });2932 });
2942 }2933 }
29432934
2944 self.got_plt_section_index = try self.addSection(.{2935 if (self.got_plt_section_index == null) {
2945 .name = try self.insertShString(".got.plt"),2936 self.got_plt_section_index = try self.addSection(.{
2946 .type = elf.SHT_PROGBITS,2937 .name = try self.insertShString(".got.plt"),
2947 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,2938 .type = elf.SHT_PROGBITS,
2948 .addralign = @alignOf(u64),2939 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
2949 .offset = std.math.maxInt(u64),2940 .addralign = @alignOf(u64),
2950 });2941 .offset = std.math.maxInt(u64),
2942 });
2943 }
29512944
2952 const needs_rela_dyn = blk: {2945 const needs_rela_dyn = blk: {
2953 if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0)2946 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 {...@@ -2960,7 +2953,7 @@ fn initSyntheticSections(self: *Elf) !void {
2960 }2953 }
2961 break :blk false;2954 break :blk false;
2962 };2955 };
2963 if (needs_rela_dyn) {2956 if (needs_rela_dyn and self.rela_dyn_section_index == null) {
2964 self.rela_dyn_section_index = try self.addSection(.{2957 self.rela_dyn_section_index = try self.addSection(.{
2965 .name = try self.insertShString(".rela.dyn"),2958 .name = try self.insertShString(".rela.dyn"),
2966 .type = elf.SHT_RELA,2959 .type = elf.SHT_RELA,
...@@ -2972,24 +2965,28 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -2972,24 +2965,28 @@ fn initSyntheticSections(self: *Elf) !void {
2972 }2965 }
29732966
2974 if (self.plt.symbols.items.len > 0) {2967 if (self.plt.symbols.items.len > 0) {
2975 self.plt_section_index = try self.addSection(.{2968 if (self.plt_section_index == null) {
2976 .name = try self.insertShString(".plt"),2969 self.plt_section_index = try self.addSection(.{
2977 .type = elf.SHT_PROGBITS,2970 .name = try self.insertShString(".plt"),
2978 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,2971 .type = elf.SHT_PROGBITS,
2979 .addralign = 16,2972 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
2980 .offset = std.math.maxInt(u64),2973 .addralign = 16,
2981 });2974 .offset = std.math.maxInt(u64),
2982 self.rela_plt_section_index = try self.addSection(.{2975 });
2983 .name = try self.insertShString(".rela.plt"),2976 }
2984 .type = elf.SHT_RELA,2977 if (self.rela_plt_section_index == null) {
2985 .flags = elf.SHF_ALLOC,2978 self.rela_plt_section_index = try self.addSection(.{
2986 .addralign = @alignOf(elf.Elf64_Rela),2979 .name = try self.insertShString(".rela.plt"),
2987 .entsize = @sizeOf(elf.Elf64_Rela),2980 .type = elf.SHT_RELA,
2988 .offset = std.math.maxInt(u64),2981 .flags = elf.SHF_ALLOC,
2989 });2982 .addralign = @alignOf(elf.Elf64_Rela),
2983 .entsize = @sizeOf(elf.Elf64_Rela),
2984 .offset = std.math.maxInt(u64),
2985 });
2986 }
2990 }2987 }
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) {
2993 self.plt_got_section_index = try self.addSection(.{2990 self.plt_got_section_index = try self.addSection(.{
2994 .name = try self.insertShString(".plt.got"),2991 .name = try self.insertShString(".plt.got"),
2995 .type = elf.SHT_PROGBITS,2992 .type = elf.SHT_PROGBITS,
...@@ -2999,7 +2996,7 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -2999,7 +2996,7 @@ fn initSyntheticSections(self: *Elf) !void {
2999 });2996 });
3000 }2997 }
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) {
3003 self.copy_rel_section_index = try self.addSection(.{3000 self.copy_rel_section_index = try self.addSection(.{
3004 .name = try self.insertShString(".copyrel"),3001 .name = try self.insertShString(".copyrel"),
3005 .type = elf.SHT_NOBITS,3002 .type = elf.SHT_NOBITS,
...@@ -3017,7 +3014,7 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -3017,7 +3014,7 @@ fn initSyntheticSections(self: *Elf) !void {
3017 if (self.base.isStatic() and !comp.config.pie) break :blk false;3014 if (self.base.isStatic() and !comp.config.pie) break :blk false;
3018 break :blk target.dynamic_linker.get() != null;3015 break :blk target.dynamic_linker.get() != null;
3019 };3016 };
3020 if (needs_interp) {3017 if (needs_interp and self.interp_section_index == null) {
3021 self.interp_section_index = try self.addSection(.{3018 self.interp_section_index = try self.addSection(.{
3022 .name = try self.insertShString(".interp"),3019 .name = try self.insertShString(".interp"),
3023 .type = elf.SHT_PROGBITS,3020 .type = elf.SHT_PROGBITS,
...@@ -3028,68 +3025,82 @@ fn initSyntheticSections(self: *Elf) !void {...@@ -3028,68 +3025,82 @@ fn initSyntheticSections(self: *Elf) !void {
3028 }3025 }
30293026
3030 if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) {3027 if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) {
3031 self.dynstrtab_section_index = try self.addSection(.{3028 if (self.dynstrtab_section_index == null) {
3032 .name = try self.insertShString(".dynstr"),3029 self.dynstrtab_section_index = try self.addSection(.{
3033 .flags = elf.SHF_ALLOC,3030 .name = try self.insertShString(".dynstr"),
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"),
3079 .flags = elf.SHF_ALLOC,3031 .flags = elf.SHF_ALLOC,
3080 .type = elf.SHT_GNU_VERSYM,3032 .type = elf.SHT_STRTAB,
3081 .addralign = @alignOf(elf.Elf64_Versym),3033 .entsize = 1,
3082 .entsize = @sizeOf(elf.Elf64_Versym),3034 .addralign = 1,
3083 .offset = std.math.maxInt(u64),3035 .offset = std.math.maxInt(u64),
3084 });3036 });
3085 self.verneed_section_index = try self.addSection(.{3037 }
3086 .name = try self.insertShString(".gnu.version_r"),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"),
3087 .flags = elf.SHF_ALLOC,3051 .flags = elf.SHF_ALLOC,
3088 .type = elf.SHT_GNU_VERNEED,3052 .type = elf.SHT_DYNSYM,
3089 .addralign = @alignOf(elf.Elf64_Verneed),3053 .addralign = @alignOf(elf.Elf64_Sym),
3054 .entsize = @sizeOf(elf.Elf64_Sym),
3055 .info = 1,
3090 .offset = std.math.maxInt(u64),3056 .offset = std.math.maxInt(u64),
3091 });3057 });
3092 }3058 }
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 }
3093 }3104 }
30943105
3095 try self.initSymtab();3106 try self.initSymtab();
...@@ -3136,36 +3147,38 @@ pub fn initShStrtab(self: *Elf) !void {...@@ -3136,36 +3147,38 @@ pub fn initShStrtab(self: *Elf) !void {
3136fn initSpecialPhdrs(self: *Elf) !void {3147fn initSpecialPhdrs(self: *Elf) !void {
3137 comptime assert(max_number_of_special_phdrs == 5);3148 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) {
3140 self.phdr_interp_index = try self.addPhdr(.{3151 self.phdr_interp_index = try self.addPhdr(.{
3141 .type = elf.PT_INTERP,3152 .type = elf.PT_INTERP,
3142 .flags = elf.PF_R,3153 .flags = elf.PF_R,
3143 .@"align" = 1,3154 .@"align" = 1,
3144 });3155 });
3145 }3156 }
3146 if (self.dynamic_section_index != null) {3157 if (self.dynamic_section_index != null and self.phdr_dynamic_index == null) {
3147 self.phdr_dynamic_index = try self.addPhdr(.{3158 self.phdr_dynamic_index = try self.addPhdr(.{
3148 .type = elf.PT_DYNAMIC,3159 .type = elf.PT_DYNAMIC,
3149 .flags = elf.PF_R | elf.PF_W,3160 .flags = elf.PF_R | elf.PF_W,
3150 });3161 });
3151 }3162 }
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) {
3153 self.phdr_gnu_eh_frame_index = try self.addPhdr(.{3164 self.phdr_gnu_eh_frame_index = try self.addPhdr(.{
3154 .type = elf.PT_GNU_EH_FRAME,3165 .type = elf.PT_GNU_EH_FRAME,
3155 .flags = elf.PF_R,3166 .flags = elf.PF_R,
3156 });3167 });
3157 }3168 }
3158 self.phdr_gnu_stack_index = try self.addPhdr(.{3169 if (self.phdr_gnu_stack_index == null) {
3159 .type = elf.PT_GNU_STACK,3170 self.phdr_gnu_stack_index = try self.addPhdr(.{
3160 .flags = elf.PF_W | elf.PF_R,3171 .type = elf.PT_GNU_STACK,
3161 .memsz = self.base.stack_size,3172 .flags = elf.PF_W | elf.PF_R,
3162 .@"align" = 1,3173 .memsz = self.base.stack_size,
3163 });3174 .@"align" = 1,
3175 });
3176 }
31643177
3165 const has_tls = for (self.sections.items(.shdr)) |shdr| {3178 const has_tls = for (self.sections.items(.shdr)) |shdr| {
3166 if (shdr.sh_flags & elf.SHF_TLS != 0) break true;3179 if (shdr.sh_flags & elf.SHF_TLS != 0) break true;
3167 } else false;3180 } else false;
3168 if (has_tls) {3181 if (has_tls and self.phdr_tls_index == null) {
3169 self.phdr_tls_index = try self.addPhdr(.{3182 self.phdr_tls_index = try self.addPhdr(.{
3170 .type = elf.PT_TLS,3183 .type = elf.PT_TLS,
3171 .flags = elf.PF_R,3184 .flags = elf.PF_R,
...@@ -3704,27 +3717,19 @@ fn getMaxNumberOfPhdrs() u64 {...@@ -3704,27 +3717,19 @@ fn getMaxNumberOfPhdrs() u64 {
3704 return num;3717 return num;
3705}3718}
37063719
3707/// Calculates how many segments (PT_LOAD progam headers) are required3720fn addLoadPhdrs(self: *Elf) error{OutOfMemory}!void {
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;
3712 for (self.sections.items(.shdr)) |shdr| {3721 for (self.sections.items(.shdr)) |shdr| {
3713 if (shdr.sh_type == elf.SHT_NULL) continue;3722 if (shdr.sh_type == elf.SHT_NULL) continue;
3714 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;3723 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
3715 const flags = shdrToPhdrFlags(shdr.sh_flags);3724 const flags = shdrToPhdrFlags(shdr.sh_flags);
3716 covers[flags - 1] = true;3725 if (self.getPhdr(.{ .flags = flags, .type = elf.PT_LOAD }) == null) {
3717 }3726 _ = try self.addPhdr(.{ .flags = flags, .type = elf.PT_LOAD });
3718 var count: usize = 0;3727 }
3719 for (covers) |cover| {
3720 if (cover) count += 1;
3721 }3728 }
3722 return count;
3723}3729}
37243730
3725/// Allocates PHDR table in virtual memory and in file.3731/// Allocates PHDR table in virtual memory and in file.
3726fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {3732fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
3727 const new_load_segments = self.calcNumberOfSegments();
3728 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];3733 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
3729 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];3734 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
37303735
...@@ -3736,7 +3741,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {...@@ -3736,7 +3741,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
3736 .p32 => @sizeOf(elf.Elf32_Phdr),3741 .p32 => @sizeOf(elf.Elf32_Phdr),
3737 .p64 => @sizeOf(elf.Elf64_Phdr),3742 .p64 => @sizeOf(elf.Elf64_Phdr),
3738 };3743 };
3739 const needed_size = (self.phdrs.items.len + new_load_segments) * phsize;3744 const needed_size = self.phdrs.items.len * phsize;
3740 const available_space = self.allocatedSize(phdr_table.p_offset);3745 const available_space = self.allocatedSize(phdr_table.p_offset);
37413746
3742 if (needed_size > available_space) {3747 if (needed_size > available_space) {
...@@ -3879,15 +3884,14 @@ pub fn allocateAllocSections(self: *Elf) !void {...@@ -3879,15 +3884,14 @@ pub fn allocateAllocSections(self: *Elf) !void {
38793884
3880 const first = slice.items(.shdr)[cover.items[0]];3885 const first = slice.items(.shdr)[cover.items[0]];
3881 var new_offset = try self.findFreeSpace(filesz, @"align");3886 var new_offset = try self.findFreeSpace(filesz, @"align");
3882 const phndx = try self.addPhdr(.{3887 const phndx = self.getPhdr(.{ .type = elf.PT_LOAD, .flags = shdrToPhdrFlags(first.sh_flags) }).?;
3883 .type = elf.PT_LOAD,3888 const phdr = &self.phdrs.items[phndx];
3884 .offset = new_offset,3889 phdr.p_offset = new_offset;
3885 .addr = first.sh_addr,3890 phdr.p_vaddr = first.sh_addr;
3886 .memsz = memsz,3891 phdr.p_paddr = first.sh_addr;
3887 .filesz = filesz,3892 phdr.p_memsz = memsz;
3888 .@"align" = @"align",3893 phdr.p_filesz = filesz;
3889 .flags = shdrToPhdrFlags(first.sh_flags),3894 phdr.p_align = @"align";
3890 });
38913895
3892 for (cover.items) |shndx| {3896 for (cover.items) |shndx| {
3893 const shdr = &slice.items(.shdr)[shndx];3897 const shdr = &slice.items(.shdr)[shndx];
...@@ -4102,6 +4106,7 @@ pub fn updateSymtabSize(self: *Elf) !void {...@@ -4102,6 +4106,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
4102 nlocals += @intCast(self.sections.slice().len);4106 nlocals += @intCast(self.sections.slice().len);
41034107
4104 if (self.requiresThunks()) for (self.thunks.items) |*th| {4108 if (self.requiresThunks()) for (self.thunks.items) |*th| {
4109 th.output_symtab_ctx.reset();
4105 th.output_symtab_ctx.ilocal = nlocals;4110 th.output_symtab_ctx.ilocal = nlocals;
4106 th.calcSymtabSize(self);4111 th.calcSymtabSize(self);
4107 nlocals += th.output_symtab_ctx.nlocals;4112 nlocals += th.output_symtab_ctx.nlocals;
...@@ -4113,6 +4118,7 @@ pub fn updateSymtabSize(self: *Elf) !void {...@@ -4113,6 +4118,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
4113 const ctx = switch (file_ptr) {4118 const ctx = switch (file_ptr) {
4114 inline else => |x| &x.output_symtab_ctx,4119 inline else => |x| &x.output_symtab_ctx,
4115 };4120 };
4121 ctx.reset();
4116 ctx.ilocal = nlocals;4122 ctx.ilocal = nlocals;
4117 ctx.iglobal = nglobals;4123 ctx.iglobal = nglobals;
4118 try file_ptr.updateSymtabSize(self);4124 try file_ptr.updateSymtabSize(self);
...@@ -4122,6 +4128,7 @@ pub fn updateSymtabSize(self: *Elf) !void {...@@ -4122,6 +4128,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
4122 }4128 }
41234129
4124 if (self.got_section_index) |_| {4130 if (self.got_section_index) |_| {
4131 self.got.output_symtab_ctx.reset();
4125 self.got.output_symtab_ctx.ilocal = nlocals;4132 self.got.output_symtab_ctx.ilocal = nlocals;
4126 self.got.updateSymtabSize(self);4133 self.got.updateSymtabSize(self);
4127 nlocals += self.got.output_symtab_ctx.nlocals;4134 nlocals += self.got.output_symtab_ctx.nlocals;
...@@ -4129,6 +4136,7 @@ pub fn updateSymtabSize(self: *Elf) !void {...@@ -4129,6 +4136,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
4129 }4136 }
41304137
4131 if (self.plt_section_index) |_| {4138 if (self.plt_section_index) |_| {
4139 self.plt.output_symtab_ctx.reset();
4132 self.plt.output_symtab_ctx.ilocal = nlocals;4140 self.plt.output_symtab_ctx.ilocal = nlocals;
4133 self.plt.updateSymtabSize(self);4141 self.plt.updateSymtabSize(self);
4134 nlocals += self.plt.output_symtab_ctx.nlocals;4142 nlocals += self.plt.output_symtab_ctx.nlocals;
...@@ -4136,6 +4144,7 @@ pub fn updateSymtabSize(self: *Elf) !void {...@@ -4136,6 +4144,7 @@ pub fn updateSymtabSize(self: *Elf) !void {
4136 }4144 }
41374145
4138 if (self.plt_got_section_index) |_| {4146 if (self.plt_got_section_index) |_| {
4147 self.plt_got.output_symtab_ctx.reset();
4139 self.plt_got.output_symtab_ctx.ilocal = nlocals;4148 self.plt_got.output_symtab_ctx.ilocal = nlocals;
4140 self.plt_got.updateSymtabSize(self);4149 self.plt_got.updateSymtabSize(self);
4141 nlocals += self.plt_got.output_symtab_ctx.nlocals;4150 nlocals += self.plt_got.output_symtab_ctx.nlocals;
...@@ -4334,6 +4343,9 @@ pub fn writeSymtab(self: *Elf) !void {...@@ -4334,6 +4343,9 @@ pub fn writeSymtab(self: *Elf) !void {
43344343
4335 try self.symtab.resize(gpa, nsyms);4344 try self.symtab.resize(gpa, nsyms);
4336 const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow;4345 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);
4337 try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size);4349 try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size);
43384350
4339 for (slice.items(.shdr), 0..) |shdr, shndx| {4351 for (slice.items(.shdr), 0..) |shdr, shndx| {
...@@ -4714,7 +4726,20 @@ pub fn isEffectivelyDynLib(self: Elf) bool {...@@ -4714,7 +4726,20 @@ pub fn isEffectivelyDynLib(self: Elf) bool {
4714 };4726 };
4715}4727}
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 {
4718 type: u32 = 0,4743 type: u32 = 0,
4719 flags: u32 = 0,4744 flags: u32 = 0,
4720 @"align": u64 = 0,4745 @"align": u64 = 0,
...@@ -5305,7 +5330,7 @@ fn fmtDumpState(...@@ -5305,7 +5330,7 @@ fn fmtDumpState(
5305 {5330 {
5306 try writer.writeAll("atom lists\n");5331 try writer.writeAll("atom lists\n");
5307 for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| {5332 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) });
5309 }5334 }
5310 }5335 }
53115336
...@@ -5437,6 +5462,14 @@ pub const SymtabCtx = struct {...@@ -5437,6 +5462,14 @@ pub const SymtabCtx = struct {
5437 nlocals: u32 = 0,5462 nlocals: u32 = 0,
5438 nglobals: u32 = 0,5463 nglobals: u32 = 0,
5439 strsize: u32 = 0,5464 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 }
5440};5473};
54415474
5442pub const null_sym = elf.Elf64_Sym{5475pub 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 {...@@ -19,6 +19,9 @@ pub fn offset(list: AtomList, elf_file: *Elf) u64 {
19}19}
2020
21pub fn updateSize(list: *AtomList, elf_file: *Elf) void {21pub 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";
22 for (list.atoms.items) |ref| {25 for (list.atoms.items) |ref| {
23 const atom_ptr = elf_file.atom(ref).?;26 const atom_ptr = elf_file.atom(ref).?;
24 assert(atom_ptr.alive);27 assert(atom_ptr.alive);
src/link/Elf/LinkerDefined.zig+1
...@@ -145,6 +145,7 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {...@@ -145,6 +145,7 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
145 try self.symbols_resolver.ensureUnusedCapacity(gpa, nsyms);145 try self.symbols_resolver.ensureUnusedCapacity(gpa, nsyms);
146146
147 for (slice.items(.shdr)) |shdr| {147 for (slice.items(.shdr)) |shdr| {
148 // TODO use getOrPut for incremental so that we don't create duplicates
148 if (elf_file.getStartStopBasename(shdr)) |name| {149 if (elf_file.getStartStopBasename(shdr)) |name| {
149 const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});150 const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
150 defer gpa.free(start_name);151 defer gpa.free(start_name);
src/link/Elf/merge_section.zig+4
...@@ -95,6 +95,10 @@ pub const MergeSection = struct {...@@ -95,6 +95,10 @@ pub const MergeSection = struct {
95 }95 }
9696
97 pub fn updateSize(msec: *MergeSection) void {97 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;
98 for (msec.finalized_subsections.items) |msub_index| {102 for (msec.finalized_subsections.items) |msub_index| {
99 const msub = msec.mergeSubsection(msub_index);103 const msub = msec.mergeSubsection(msub_index);
100 assert(msub.alive);104 assert(msub.alive);
test/incremental/add_decl+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/add_decl_namespaced+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/delete_comptime_decls+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4pub fn main() void {}5pub fn main() void {}
test/incremental/hello+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/modify_inline_fn+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/move_src+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
test/incremental/remove_enum_field+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const MyEnum = enum(u8) {5const MyEnum = enum(u8) {
test/incremental/type_becomes_comptime_only+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const SomeType = u32;5const SomeType = u32;
test/incremental/unreferenced_error+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1#target=x86_64-linux1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
2#update=initial version3#update=initial version
3#file=main.zig4#file=main.zig
4const std = @import("std");5const std = @import("std");
tools/incr-check.zig+141-110
...@@ -3,13 +3,7 @@ const fatal = std.process.fatal;...@@ -3,13 +3,7 @@ const fatal = std.process.fatal;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const Cache = std.Build.Cache;4const 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]";6const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--debug-link] [--zig-cc-binary /path/to/zig]";
7
8const EmitMode = enum {
9 none,
10 bin,
11 c,
12};
137
14pub fn main() !void {8pub fn main() !void {
15 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);9 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
...@@ -20,21 +14,19 @@ pub fn main() !void {...@@ -20,21 +14,19 @@ pub fn main() !void {
20 var opt_input_file_name: ?[]const u8 = null;14 var opt_input_file_name: ?[]const u8 = null;
21 var opt_lib_dir: ?[]const u8 = null;15 var opt_lib_dir: ?[]const u8 = null;
22 var opt_cc_zig: ?[]const u8 = null;16 var opt_cc_zig: ?[]const u8 = null;
23 var emit: EmitMode = .bin;
24 var debug_zcu = false;17 var debug_zcu = false;
18 var debug_link = false;
2519
26 var arg_it = try std.process.argsWithAllocator(arena);20 var arg_it = try std.process.argsWithAllocator(arena);
27 _ = arg_it.skip();21 _ = arg_it.skip();
28 while (arg_it.next()) |arg| {22 while (arg_it.next()) |arg| {
29 if (arg.len > 0 and arg[0] == '-') {23 if (arg.len > 0 and arg[0] == '-') {
30 if (std.mem.eql(u8, arg, "--emit")) {24 if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
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")) {
35 opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage});25 opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage});
36 } else if (std.mem.eql(u8, arg, "--debug-zcu")) {26 } else if (std.mem.eql(u8, arg, "--debug-zcu")) {
37 debug_zcu = true;27 debug_zcu = true;
28 } else if (std.mem.eql(u8, arg, "--debug-link")) {
29 debug_link = true;
38 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {30 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {
39 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});31 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});
40 } else {32 } else {
...@@ -73,104 +65,114 @@ pub fn main() !void {...@@ -73,104 +65,114 @@ pub fn main() !void {
73 else65 else
74 null;66 null;
7567
76 var child_args: std.ArrayListUnmanaged([]const u8) = .empty;68 const debug_log_verbose = debug_zcu or debug_link;
77 try child_args.appendSlice(arena, &.{69
78 resolved_zig_exe,70 for (case.targets) |target| {
79 "build-exe",71 std.log.scoped(.status).info("target: '{s}-{s}'", .{ target.query, @tagName(target.backend) });
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 }
10172
102 var child = std.process.Child.init(child_args.items, arena);73 var child_args: std.ArrayListUnmanaged([]const u8) = .empty;
103 child.stdin_behavior = .Pipe;74 try child_args.appendSlice(arena, &.{
104 child.stdout_behavior = .Pipe;75 resolved_zig_exe,
105 child.stderr_behavior = .Pipe;76 "build-exe",
106 child.progress_node = child_prog_node;77 case.root_source_file,
107 child.cwd_dir = tmp_dir;78 "-fincremental",
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",
120 "-target",79 "-target",
121 case.target_query,80 target.query,
122 "-I",81 "--cache-dir",
123 opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using '--emit c'", .{}),82 ".local-cache",
124 "-o",83 "--global-cache-dir",
84 ".global_cache",
85 "--listen=-",
125 });86 });
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 = .{103 var child = std.process.Child.init(child_args.items, arena);
129 .arena = arena,104 child.stdin_behavior = .Pipe;
130 .case = case,105 child.stdout_behavior = .Pipe;
131 .tmp_dir = tmp_dir,106 child.stderr_behavior = .Pipe;
132 .tmp_dir_path = tmp_dir_path,107 child.progress_node = child_prog_node;
133 .child = &child,108 child.cwd_dir = tmp_dir;
134 .allow_stderr = debug_zcu,109 child.cwd = tmp_dir_path;
135 .emit = emit,110
136 .cc_child_args = &cc_child_args,111 var cc_child_args: std.ArrayListUnmanaged([]const u8) = .empty;
137 };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, .{140 try child.spawn();
142 .stdout = child.stdout.?,
143 .stderr = child.stderr.?,
144 });
145 defer poller.deinit();
146141
147 for (case.updates) |update| {142 var poller = std.io.poll(arena, Eval.StreamEnum, .{
148 var update_node = prog_node.start(update.name, 0);143 .stdout = child.stdout.?,
149 defer update_node.end();144 .stderr = child.stderr.?,
145 });
146 defer poller.deinit();
150147
151 if (debug_zcu) {148 for (case.updates) |update| {
152 std.log.info("=== START UPDATE '{s}' ===", .{update.name});149 var update_node = prog_node.start(update.name, 0);
153 }150 defer update_node.end();
154151
155 eval.write(update);152 if (debug_log_verbose) {
156 try eval.requestUpdate();153 std.log.scoped(.status).info("update: '{s}'", .{update.name});
157 try eval.check(&poller, update, update_node);154 }
158 }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 }
163}165}
164166
165const Eval = struct {167const Eval = struct {
166 arena: Allocator,168 arena: Allocator,
167 case: Case,169 case: Case,
170 target: Case.Target,
168 tmp_dir: std.fs.Dir,171 tmp_dir: std.fs.Dir,
169 tmp_dir_path: []const u8,172 tmp_dir_path: []const u8,
170 child: *std.process.Child,173 child: *std.process.Child,
171 allow_stderr: bool,174 allow_stderr: bool,
172 emit: EmitMode,175 /// When `target.backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary.
173 /// When `emit == .c`, this contains the first few arguments to `zig cc` to build the generated binary.
174 /// The arguments `out.c in.c` must be appended before spawning the subprocess.176 /// The arguments `out.c in.c` must be appended before spawning the subprocess.
175 cc_child_args: *std.ArrayListUnmanaged([]const u8),177 cc_child_args: *std.ArrayListUnmanaged([]const u8),
176178
...@@ -254,11 +256,10 @@ const Eval = struct {...@@ -254,11 +256,10 @@ const Eval = struct {
254 }256 }
255 }257 }
256258
257 if (eval.emit == .none) {259 if (eval.target.backend == .sema) {
258 try eval.checkSuccessOutcome(update, null, prog_node);260 try eval.checkSuccessOutcome(update, null, prog_node);
259 // This message indicates the end of the update.261 // This message indicates the end of the update.
260 stdout.discard(body.len);262 stdout.discard(body.len);
261 return;
262 }263 }
263264
264 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];265 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
...@@ -268,11 +269,11 @@ const Eval = struct {...@@ -268,11 +269,11 @@ const Eval = struct {
268 const bin_name = try std.zig.binNameAlloc(arena, .{269 const bin_name = try std.zig.binNameAlloc(arena, .{
269 .root_name = name,270 .root_name = name,
270 .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{271 .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{
271 .arch_os_abi = eval.case.target_query,272 .arch_os_abi = eval.target.query,
272 .object_format = switch (eval.emit) {273 .object_format = switch (eval.target.backend) {
273 .none => unreachable,274 .sema => unreachable,
274 .bin => null,275 .selfhosted, .llvm => null,
275 .c => "c",276 .cbe => "c",
276 },277 },
277 })),278 })),
278 .output_mode = .Exe,279 .output_mode = .Exe,
...@@ -282,7 +283,6 @@ const Eval = struct {...@@ -282,7 +283,6 @@ const Eval = struct {
282 try eval.checkSuccessOutcome(update, bin_path, prog_node);283 try eval.checkSuccessOutcome(update, bin_path, prog_node);
283 // This message indicates the end of the update.284 // This message indicates the end of the update.
284 stdout.discard(body.len);285 stdout.discard(body.len);
285 return;
286 },286 },
287 else => {287 else => {
288 // Ignore other messages.288 // Ignore other messages.
...@@ -329,14 +329,14 @@ const Eval = struct {...@@ -329,14 +329,14 @@ const Eval = struct {
329 .stdout, .exit_code => {},329 .stdout, .exit_code => {},
330 }330 }
331 const emitted_path = opt_emitted_path orelse {331 const emitted_path = opt_emitted_path orelse {
332 std.debug.assert(eval.emit == .none);332 std.debug.assert(eval.target.backend == .sema);
333 return;333 return;
334 };334 };
335335
336 const binary_path = switch (eval.emit) {336 const binary_path = switch (eval.target.backend) {
337 .none => unreachable,337 .sema => unreachable,
338 .bin => emitted_path,338 .selfhosted, .llvm => emitted_path,
339 .c => bin: {339 .cbe => bin: {
340 const rand_int = std.crypto.random.int(u64);340 const rand_int = std.crypto.random.int(u64);
341 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);341 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
342 try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node);342 try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node);
...@@ -462,7 +462,26 @@ const Eval = struct {...@@ -462,7 +462,26 @@ const Eval = struct {
462const Case = struct {462const Case = struct {
463 updates: []Update,463 updates: []Update,
464 root_source_file: []const u8,464 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
467 const Update = struct {486 const Update = struct {
468 name: []const u8,487 name: []const u8,
...@@ -492,9 +511,9 @@ const Case = struct {...@@ -492,9 +511,9 @@ const Case = struct {
492 };511 };
493512
494 fn parse(arena: Allocator, bytes: []const u8) !Case {513 fn parse(arena: Allocator, bytes: []const u8) !Case {
514 var targets: std.ArrayListUnmanaged(Target) = .empty;
495 var updates: std.ArrayListUnmanaged(Update) = .empty;515 var updates: std.ArrayListUnmanaged(Update) = .empty;
496 var changes: std.ArrayListUnmanaged(FullContents) = .empty;516 var changes: std.ArrayListUnmanaged(FullContents) = .empty;
497 var target_query: ?[]const u8 = null;
498 var it = std.mem.splitScalar(u8, bytes, '\n');517 var it = std.mem.splitScalar(u8, bytes, '\n');
499 var line_n: usize = 1;518 var line_n: usize = 1;
500 var root_source_file: ?[]const u8 = null;519 var root_source_file: ?[]const u8 = null;
...@@ -506,8 +525,16 @@ const Case = struct {...@@ -506,8 +525,16 @@ const Case = struct {
506 if (val.len == 0) {525 if (val.len == 0) {
507 fatal("line {d}: missing value", .{line_n});526 fatal("line {d}: missing value", .{line_n});
508 } else if (std.mem.eql(u8, key, "target")) {527 } else if (std.mem.eql(u8, key, "target")) {
509 if (target_query != null) fatal("line {d}: duplicate target", .{line_n});528 const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse
510 target_query = val;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 });
511 } else if (std.mem.eql(u8, key, "update")) {538 } else if (std.mem.eql(u8, key, "update")) {
512 if (updates.items.len > 0) {539 if (updates.items.len > 0) {
513 const last_update = &updates.items[updates.items.len - 1];540 const last_update = &updates.items[updates.items.len - 1];
...@@ -559,15 +586,19 @@ const Case = struct {...@@ -559,15 +586,19 @@ const Case = struct {
559 }586 }
560 }587 }
561588
589 if (targets.items.len == 0) {
590 fatal("missing target", .{});
591 }
592
562 if (changes.items.len > 0) {593 if (changes.items.len > 0) {
563 const last_update = &updates.items[updates.items.len - 1];594 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
565 }596 }
566597
567 return .{598 return .{
568 .updates = updates.items,599 .updates = updates.items,
569 .root_source_file = root_source_file orelse fatal("missing root source file", .{}),600 .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
571 };602 };
572 }603 }
573};604};