authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-20 17:28:31+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-20 17:28:31+01:00
log58344e0017a1e866ee9744b4862c57dbf39284b6
tree8d86d17bd12371a6369dee44ee867616a4e4ef5d
parent8098b3f84cf24878e3388e056f60aba69033e0f6
parenta26ab9afeeeca405118fb3411dce0810ca723b5f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7829 from kubkon/macho-safer

stage2 macho: make int casts fallible where necessary

6 files changed, 208 insertions(+), 285 deletions(-)

src/link/MachO.zig+78-47
...@@ -120,6 +120,7 @@ stub_helper_stubs_start_off: ?u64 = null,...@@ -120,6 +120,7 @@ stub_helper_stubs_start_off: ?u64 = null,
120120
121/// Table of symbol names aka the string table.121/// Table of symbol names aka the string table.
122string_table: std.ArrayListUnmanaged(u8) = .{},122string_table: std.ArrayListUnmanaged(u8) = .{},
123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
123124
124/// Table of trampolines to the actual symbols in __text section.125/// Table of trampolines to the actual symbols in __text section.
125offset_table: std.ArrayListUnmanaged(u64) = .{},126offset_table: std.ArrayListUnmanaged(u64) = .{},
...@@ -142,11 +143,11 @@ string_table_needs_relocation: bool = false,...@@ -142,11 +143,11 @@ string_table_needs_relocation: bool = false,
142/// or removed from the freelist.143/// or removed from the freelist.
143///144///
144/// A text block has surplus capacity when its overcapacity value is greater than145/// A text block has surplus capacity when its overcapacity value is greater than
145/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so146/// padToIdeal(minimum_text_block_size). That is, when it has so
146/// much extra capacity, that we could fit a small new symbol in it, itself with147/// much extra capacity, that we could fit a small new symbol in it, itself with
147/// ideal_capacity or more.148/// ideal_capacity or more.
148///149///
149/// Ideal capacity is defined by size * alloc_num / alloc_den.150/// Ideal capacity is defined by size + (size / ideal_factor).
150///151///
151/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that152/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
152/// overcapacity can be negative. A simple way to have negative overcapacity is to153/// overcapacity can be negative. A simple way to have negative overcapacity is to
...@@ -191,9 +192,9 @@ pub const StubFixup = struct {...@@ -191,9 +192,9 @@ pub const StubFixup = struct {
191 len: usize,192 len: usize,
192};193};
193194
194/// `alloc_num / alloc_den` is the factor of padding when allocating.195/// When allocating, the ideal_capacity is calculated by
195pub const alloc_num = 4;196/// actual_capacity + (actual_capacity / ideal_factor)
196pub const alloc_den = 3;197const ideal_factor = 2;
197198
198/// Default path to dyld199/// Default path to dyld
199/// TODO instead of hardcoding it, we should probably look through some env vars and search paths200/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
...@@ -213,7 +214,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B....@@ -213,7 +214,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.
213/// it as a possible place to put new symbols, it must have enough room for this many bytes214/// it as a possible place to put new symbols, it must have enough room for this many bytes
214/// (plus extra for reserved capacity).215/// (plus extra for reserved capacity).
215const minimum_text_block_size = 64;216const minimum_text_block_size = 64;
216const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;217const min_text_capacity = padToIdeal(minimum_text_block_size);
217218
218pub const TextBlock = struct {219pub const TextBlock = struct {
219 /// Each decl always gets a local symbol with the fully qualified name.220 /// Each decl always gets a local symbol with the fully qualified name.
...@@ -276,7 +277,7 @@ pub const TextBlock = struct {...@@ -276,7 +277,7 @@ pub const TextBlock = struct {
276 const self_sym = macho_file.local_symbols.items[self.local_sym_index];277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
277 const next_sym = macho_file.local_symbols.items[next.local_sym_index];278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
278 const cap = next_sym.n_value - self_sym.n_value;279 const cap = next_sym.n_value - self_sym.n_value;
279 const ideal_cap = self.size * alloc_num / alloc_den;280 const ideal_cap = padToIdeal(self.size);
280 if (cap <= ideal_cap) return false;281 if (cap <= ideal_cap) return false;
281 const surplus = cap - ideal_cap;282 const surplus = cap - ideal_cap;
282 return surplus >= min_text_capacity;283 return surplus >= min_text_capacity;
...@@ -872,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -872,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
872 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;873 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
873 const text_section = text_segment.sections.items[self.text_section_index.?];874 const text_section = text_segment.sections.items[self.text_section_index.?];
874 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);875 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
875 const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;876 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
876877
877 if (needed_size + after_last_cmd_offset > text_section.offset) {878 if (needed_size + after_last_cmd_offset > text_section.offset) {
878 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});879 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
...@@ -942,7 +943,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -942,7 +943,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
942 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;943 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
943 const text_section = text_segment.sections.items[self.text_section_index.?];944 const text_section = text_segment.sections.items[self.text_section_index.?];
944 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);945 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
945 const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;946 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
946947
947 if (needed_size + after_last_cmd_offset > text_section.offset) {948 if (needed_size + after_last_cmd_offset > text_section.offset) {
948 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});949 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
...@@ -1023,6 +1024,13 @@ pub fn deinit(self: *MachO) void {...@@ -1023,6 +1024,13 @@ pub fn deinit(self: *MachO) void {
1023 self.text_block_free_list.deinit(self.base.allocator);1024 self.text_block_free_list.deinit(self.base.allocator);
1024 self.offset_table.deinit(self.base.allocator);1025 self.offset_table.deinit(self.base.allocator);
1025 self.offset_table_free_list.deinit(self.base.allocator);1026 self.offset_table_free_list.deinit(self.base.allocator);
1027 {
1028 var it = self.string_table_directory.iterator();
1029 while (it.next()) |entry| {
1030 self.base.allocator.free(entry.key);
1031 }
1032 }
1033 self.string_table_directory.deinit(self.base.allocator);
1026 self.string_table.deinit(self.base.allocator);1034 self.string_table.deinit(self.base.allocator);
1027 self.global_symbols.deinit(self.base.allocator);1035 self.global_symbols.deinit(self.base.allocator);
1028 self.global_symbol_free_list.deinit(self.base.allocator);1036 self.global_symbol_free_list.deinit(self.base.allocator);
...@@ -1229,14 +1237,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1229,14 +1237,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1229 const this_addr = symbol.n_value + fixup.start;1237 const this_addr = symbol.n_value + fixup.start;
1230 switch (self.base.options.target.cpu.arch) {1238 switch (self.base.options.target.cpu.arch) {
1231 .x86_64 => {1239 .x86_64 => {
1232 const displacement = @intCast(u32, target_addr - this_addr - fixup.len);1240 assert(target_addr >= this_addr + fixup.len);
1241 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);
1233 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];1242 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1234 mem.writeIntSliceLittle(u32, placeholder, displacement);1243 mem.writeIntSliceLittle(u32, placeholder, displacement);
1235 },1244 },
1236 .aarch64 => {1245 .aarch64 => {
1237 const displacement = @intCast(u27, target_addr - this_addr);1246 assert(target_addr >= this_addr);
1247 const displacement = try math.cast(u27, target_addr - this_addr);
1238 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1248 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1239 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32());1249 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());
1240 },1250 },
1241 else => unreachable, // unsupported target architecture1251 else => unreachable, // unsupported target architecture
1242 }1252 }
...@@ -1249,14 +1259,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1249,14 +1259,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1249 const text_addr = symbol.n_value + fixup.start;1259 const text_addr = symbol.n_value + fixup.start;
1250 switch (self.base.options.target.cpu.arch) {1260 switch (self.base.options.target.cpu.arch) {
1251 .x86_64 => {1261 .x86_64 => {
1252 const displacement = @intCast(u32, stub_addr - text_addr - fixup.len);1262 assert(stub_addr >= text_addr + fixup.len);
1263 const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len);
1253 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];1264 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1254 mem.writeIntSliceLittle(u32, placeholder, displacement);1265 mem.writeIntSliceLittle(u32, placeholder, displacement);
1255 },1266 },
1256 .aarch64 => {1267 .aarch64 => {
1257 const displacement = @intCast(u32, stub_addr - text_addr);1268 assert(stub_addr >= text_addr);
1269 const displacement = try math.cast(i28, stub_addr - text_addr);
1258 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1270 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1259 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());1271 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32());
1260 },1272 },
1261 else => unreachable, // unsupported target architecture1273 else => unreachable, // unsupported target architecture
1262 }1274 }
...@@ -1479,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1479,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1479 const program_code_size_hint = self.base.options.program_code_size_hint;1491 const program_code_size_hint = self.base.options.program_code_size_hint;
1480 const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;1492 const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
1481 const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint;1493 const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint;
1482 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);1494 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
14831495
1484 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });1496 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
14851497
...@@ -1644,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1644,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1644 const address_and_offset = self.nextSegmentAddressAndOffset();1656 const address_and_offset = self.nextSegmentAddressAndOffset();
16451657
1646 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1658 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1647 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);1659 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
16481660
1649 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });1661 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
16501662
...@@ -1701,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1701,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1701 const address_and_offset = self.nextSegmentAddressAndOffset();1713 const address_and_offset = self.nextSegmentAddressAndOffset();
17021714
1703 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;1715 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
1704 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);1716 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
17051717
1706 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });1718 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
17071719
...@@ -2074,7 +2086,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2074,7 +2086,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2074 code[1] = 0x8d;2086 code[1] = 0x8d;
2075 code[2] = 0x1d;2087 code[2] = 0x1d;
2076 {2088 {
2077 const displacement = @intCast(u32, data.addr - stub_helper.addr - 7);2089 const displacement = try math.cast(u32, data.addr - stub_helper.addr - 7);
2078 mem.writeIntLittle(u32, code[3..7], displacement);2090 mem.writeIntLittle(u32, code[3..7], displacement);
2079 }2091 }
2080 // push %r112092 // push %r11
...@@ -2084,7 +2096,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2084,7 +2096,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2084 code[9] = 0xff;2096 code[9] = 0xff;
2085 code[10] = 0x25;2097 code[10] = 0x25;
2086 {2098 {
2087 const displacement = @intCast(u32, got.addr - stub_helper.addr - code_size);2099 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2088 mem.writeIntLittle(u32, code[11..], displacement);2100 mem.writeIntLittle(u32, code[11..], displacement);
2089 }2101 }
2090 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;2102 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
...@@ -2093,8 +2105,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2093,8 +2105,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2093 .aarch64 => {2105 .aarch64 => {
2094 var code: [4 * @sizeOf(u32)]u8 = undefined;2106 var code: [4 * @sizeOf(u32)]u8 = undefined;
2095 {2107 {
2096 const displacement = data.addr - stub_helper.addr;2108 const displacement = try math.cast(i21, data.addr - stub_helper.addr);
2097 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());2109 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2098 }2110 }
2099 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(2111 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2100 .x16,2112 .x16,
...@@ -2103,9 +2115,10 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2103,9 +2115,10 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2103 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),2115 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2104 ).toU32());2116 ).toU32());
2105 {2117 {
2106 const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32);2118 const displacement = try math.divExact(u64, got.addr - stub_helper.addr - 2 * @sizeOf(u32), 4);
2119 const literal = try math.cast(u19, displacement);
2107 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{2120 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2108 .literal = @intCast(u19, displacement / 4),2121 .literal = literal,
2109 }).toU32());2122 }).toU32());
2110 }2123 }
2111 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());2124 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
...@@ -2120,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2120,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2120fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {2133fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
2121 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2134 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2122 const text_section = &text_segment.sections.items[self.text_section_index.?];2135 const text_section = &text_segment.sections.items[self.text_section_index.?];
2123 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;2136 const new_block_ideal_capacity = padToIdeal(new_block_size);
21242137
2125 // We use these to indicate our intention to update metadata, placing the new block,2138 // We use these to indicate our intention to update metadata, placing the new block,
2126 // and possibly removing a free list node.2139 // and possibly removing a free list node.
...@@ -2140,7 +2153,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2140,7 +2153,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2140 // Is it enough that we could fit this new text block?2153 // Is it enough that we could fit this new text block?
2141 const sym = self.local_symbols.items[big_block.local_sym_index];2154 const sym = self.local_symbols.items[big_block.local_sym_index];
2142 const capacity = big_block.capacity(self.*);2155 const capacity = big_block.capacity(self.*);
2143 const ideal_capacity = capacity * alloc_num / alloc_den;2156 const ideal_capacity = padToIdeal(capacity);
2144 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;2157 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
2145 const capacity_end_vaddr = sym.n_value + capacity;2158 const capacity_end_vaddr = sym.n_value + capacity;
2146 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;2159 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
...@@ -2172,7 +2185,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2172,7 +2185,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2172 const last_symbol = self.local_symbols.items[last.local_sym_index];2185 const last_symbol = self.local_symbols.items[last.local_sym_index];
2173 // TODO We should pad out the excess capacity with NOPs. For executables,2186 // TODO We should pad out the excess capacity with NOPs. For executables,
2174 // no padding seems to be OK, but it will probably not be for objects.2187 // no padding seems to be OK, but it will probably not be for objects.
2175 const ideal_capacity = last.size * alloc_num / alloc_den;2188 const ideal_capacity = padToIdeal(last.size);
2176 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;2189 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
2177 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);2190 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
2178 block_placement = last;2191 block_placement = last;
...@@ -2230,14 +2243,26 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {...@@ -2230,14 +2243,26 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {
2230}2243}
22312244
2232fn makeString(self: *MachO, bytes: []const u8) !u32 {2245fn makeString(self: *MachO, bytes: []const u8) !u32 {
2246 if (self.string_table_directory.get(bytes)) |offset| {
2247 log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset });
2248 return offset;
2249 }
2250
2233 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);2251 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
2234 const offset = @intCast(u32, self.string_table.items.len);2252 const offset = @intCast(u32, self.string_table.items.len);
2235 log.debug("writing '{s}' into the string table at offset 0x{x}", .{ bytes, offset });2253 log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
2236 self.string_table.appendSliceAssumeCapacity(bytes);2254 self.string_table.appendSliceAssumeCapacity(bytes);
2237 self.string_table.appendAssumeCapacity(0);2255 self.string_table.appendAssumeCapacity(0);
2256 try self.string_table_directory.putNoClobber(
2257 self.base.allocator,
2258 try self.base.allocator.dupe(u8, bytes),
2259 offset,
2260 );
2261
2238 self.string_table_dirty = true;2262 self.string_table_dirty = true;
2239 if (self.d_sym) |*ds|2263 if (self.d_sym) |*ds|
2240 ds.string_table_dirty = true;2264 ds.string_table_dirty = true;
2265
2241 return offset;2266 return offset;
2242}2267}
22432268
...@@ -2335,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 {...@@ -2335,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 {
2335}2360}
23362361
2337inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 {2362inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 {
2338 const increased_size = satMul(size, alloc_num) / alloc_den;2363 const increased_size = padToIdeal(size);
2339 const test_end = off + increased_size;2364 const test_end = off + increased_size;
2340 if (end > off and start < test_end) {2365 if (end > off and start < test_end) {
2341 return test_end;2366 return test_end;
...@@ -2344,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 {...@@ -2344,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 {
2344}2369}
23452370
2346fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 {2371fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 {
2347 const end = start + satMul(size, alloc_num) / alloc_den;2372 const end = start + padToIdeal(size);
23482373
2349 // __LINKEDIT is a weird segment where sections get their own load commands so we2374 // __LINKEDIT is a weird segment where sections get their own load commands so we
2350 // special-case it.2375 // special-case it.
...@@ -2425,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta...@@ -2425,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
2425 return st;2450 return st;
2426}2451}
24272452
2428/// Saturating multiplication
2429pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2430 const T = @TypeOf(a, b);
2431 return std.math.mul(T, a, b) catch std.math.maxInt(T);
2432}
2433
2434fn writeOffsetTableEntry(self: *MachO, index: usize) !void {2453fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2435 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2454 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2436 const sect = &text_segment.sections.items[self.got_section_index.?];2455 const sect = &text_segment.sections.items[self.got_section_index.?];
...@@ -2445,8 +2464,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {...@@ -2445,8 +2464,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2445 var code: [8]u8 = undefined;2464 var code: [8]u8 = undefined;
2446 switch (self.base.options.target.cpu.arch) {2465 switch (self.base.options.target.cpu.arch) {
2447 .x86_64 => {2466 .x86_64 => {
2448 const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7);2467 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);
2449 const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1);2468 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);
2450 // lea %rax, [rip - disp]2469 // lea %rax, [rip - disp]
2451 code[0] = 0x48;2470 code[0] = 0x48;
2452 code[1] = 0x8D;2471 code[1] = 0x8D;
...@@ -2456,8 +2475,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {...@@ -2456,8 +2475,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2456 code[7] = 0xC3;2475 code[7] = 0xC3;
2457 },2476 },
2458 .aarch64 => {2477 .aarch64 => {
2459 const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]);2478 const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]);
2460 const symbol_off = @intCast(i21, pos_symbol_off) * -1;2479 const symbol_off = @as(i21, pos_symbol_off) * -1;
2461 // adr x0, #-disp2480 // adr x0, #-disp
2462 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());2481 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
2463 // ret x282482 // ret x28
...@@ -2503,16 +2522,19 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2503,16 +2522,19 @@ fn writeStub(self: *MachO, index: u32) !void {
2503 defer self.base.allocator.free(code);2522 defer self.base.allocator.free(code);
2504 switch (self.base.options.target.cpu.arch) {2523 switch (self.base.options.target.cpu.arch) {
2505 .x86_64 => {2524 .x86_64 => {
2506 const displacement = @intCast(u32, la_ptr_addr - stub_addr - stubs.reserved2);2525 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
2526 const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
2507 // jmp2527 // jmp
2508 code[0] = 0xff;2528 code[0] = 0xff;
2509 code[1] = 0x25;2529 code[1] = 0x25;
2510 mem.writeIntLittle(u32, code[2..][0..4], displacement);2530 mem.writeIntLittle(u32, code[2..][0..4], displacement);
2511 },2531 },
2512 .aarch64 => {2532 .aarch64 => {
2513 const displacement = la_ptr_addr - stub_addr;2533 assert(la_ptr_addr >= stub_addr);
2534 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);
2535 const literal = try math.cast(u19, displacement);
2514 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{2536 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2515 .literal = @intCast(u19, displacement / 4),2537 .literal = literal,
2516 }).toU32());2538 }).toU32());
2517 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());2539 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());
2518 },2540 },
...@@ -2535,7 +2557,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2535,7 +2557,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2535 defer self.base.allocator.free(code);2557 defer self.base.allocator.free(code);
2536 switch (self.base.options.target.cpu.arch) {2558 switch (self.base.options.target.cpu.arch) {
2537 .x86_64 => {2559 .x86_64 => {
2538 const displacement = @intCast(i32, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size);2560 const displacement = try math.cast(
2561 i32,
2562 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
2563 );
2539 // pushq2564 // pushq
2540 code[0] = 0x68;2565 code[0] = 0x68;
2541 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.2566 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
...@@ -2544,11 +2569,11 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2544,11 +2569,11 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2544 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));2569 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
2545 },2570 },
2546 .aarch64 => {2571 .aarch64 => {
2547 const displacement = @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4;2572 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2548 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{2573 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2549 .literal = 0x2,2574 .literal = @divExact(stub_size - @sizeOf(u32), 4),
2550 }).toU32());2575 }).toU32());
2551 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32());2576 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
2552 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.2577 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2553 },2578 },
2554 else => unreachable,2579 else => unreachable,
...@@ -3239,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {...@@ -3239,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {
3239 }3264 }
3240 }3265 }
3241}3266}
3267
3268pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3269 // TODO https://github.com/ziglang/zig/issues/1284
3270 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
3271 std.math.maxInt(@TypeOf(actual_size));
3272}
src/link/MachO/DebugSymbols.zig+11-13
...@@ -18,9 +18,7 @@ const link = @import("../../link.zig");...@@ -18,9 +18,7 @@ const link = @import("../../link.zig");
18const MachO = @import("../MachO.zig");18const MachO = @import("../MachO.zig");
19const SrcFn = MachO.SrcFn;19const SrcFn = MachO.SrcFn;
20const TextBlock = MachO.TextBlock;20const TextBlock = MachO.TextBlock;
21const satMul = MachO.satMul;21const padToIdeal = MachO.padToIdeal;
22const alloc_num = MachO.alloc_num;
23const alloc_den = MachO.alloc_den;
24const makeStaticString = MachO.makeStaticString;22const makeStaticString = MachO.makeStaticString;
2523
26usingnamespace @import("commands.zig");24usingnamespace @import("commands.zig");
...@@ -207,7 +205,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void...@@ -207,7 +205,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
207205
208 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;206 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
209 const ideal_size: u16 = 200 + 128 + 160 + 250;207 const ideal_size: u16 = 200 + 128 + 160 + 250;
210 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, page_size);208 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size);
211 const off = linkedit.inner.fileoff + linkedit.inner.filesize;209 const off = linkedit.inner.fileoff + linkedit.inner.filesize;
212 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;210 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
213211
...@@ -804,7 +802,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {...@@ -804,7 +802,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
804}802}
805803
806fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {804fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
807 const end = start + satMul(size, alloc_num) / alloc_den;805 const end = start + padToIdeal(size);
808806
809 if (self.symtab_cmd_index) |idx| outer: {807 if (self.symtab_cmd_index) |idx| outer: {
810 if (self.load_commands.items.len == idx) break :outer;808 if (self.load_commands.items.len == idx) break :outer;
...@@ -812,7 +810,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64...@@ -812,7 +810,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64
812 {810 {
813 // Symbol table811 // Symbol table
814 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);812 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
815 const increased_size = satMul(symsize, alloc_num) / alloc_den;813 const increased_size = padToIdeal(symsize);
816 const test_end = symtab.symoff + increased_size;814 const test_end = symtab.symoff + increased_size;
817 if (end > symtab.symoff and start < test_end) {815 if (end > symtab.symoff and start < test_end) {
818 return test_end;816 return test_end;
...@@ -820,7 +818,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64...@@ -820,7 +818,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64
820 }818 }
821 {819 {
822 // String table820 // String table
823 const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;821 const increased_size = padToIdeal(symtab.strsize);
824 const test_end = symtab.stroff + increased_size;822 const test_end = symtab.stroff + increased_size;
825 if (end > symtab.stroff and start < test_end) {823 if (end > symtab.stroff and start < test_end) {
826 return test_end;824 return test_end;
...@@ -1099,7 +1097,7 @@ pub fn commitDeclDebugInfo(...@@ -1099,7 +1097,7 @@ pub fn commitDeclDebugInfo(
1099 last.next = src_fn;1097 last.next = src_fn;
1100 self.dbg_line_fn_last = src_fn;1098 self.dbg_line_fn_last = src_fn;
11011099
1102 src_fn.off = last.off + (last.len * alloc_num / alloc_den);1100 src_fn.off = last.off + padToIdeal(last.len);
1103 }1101 }
1104 } else if (src_fn.prev == null) {1102 } else if (src_fn.prev == null) {
1105 // Append new function.1103 // Append new function.
...@@ -1108,14 +1106,14 @@ pub fn commitDeclDebugInfo(...@@ -1108,14 +1106,14 @@ pub fn commitDeclDebugInfo(
1108 last.next = src_fn;1106 last.next = src_fn;
1109 self.dbg_line_fn_last = src_fn;1107 self.dbg_line_fn_last = src_fn;
11101108
1111 src_fn.off = last.off + (last.len * alloc_num / alloc_den);1109 src_fn.off = last.off + padToIdeal(last.len);
1112 }1110 }
1113 } else {1111 } else {
1114 // This is the first function of the Line Number Program.1112 // This is the first function of the Line Number Program.
1115 self.dbg_line_fn_first = src_fn;1113 self.dbg_line_fn_first = src_fn;
1116 self.dbg_line_fn_last = src_fn;1114 self.dbg_line_fn_last = src_fn;
11171115
1118 src_fn.off = self.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;1116 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module));
1119 }1117 }
11201118
1121 const last_src_fn = self.dbg_line_fn_last.?;1119 const last_src_fn = self.dbg_line_fn_last.?;
...@@ -1259,7 +1257,7 @@ fn updateDeclDebugInfoAllocation(...@@ -1259,7 +1257,7 @@ fn updateDeclDebugInfoAllocation(
1259 last.dbg_info_next = text_block;1257 last.dbg_info_next = text_block;
1260 self.dbg_info_decl_last = text_block;1258 self.dbg_info_decl_last = text_block;
12611259
1262 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);1260 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len);
1263 }1261 }
1264 } else if (text_block.dbg_info_prev == null) {1262 } else if (text_block.dbg_info_prev == null) {
1265 // Append new Decl.1263 // Append new Decl.
...@@ -1268,14 +1266,14 @@ fn updateDeclDebugInfoAllocation(...@@ -1268,14 +1266,14 @@ fn updateDeclDebugInfoAllocation(
1268 last.dbg_info_next = text_block;1266 last.dbg_info_next = text_block;
1269 self.dbg_info_decl_last = text_block;1267 self.dbg_info_decl_last = text_block;
12701268
1271 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);1269 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len);
1272 }1270 }
1273 } else {1271 } else {
1274 // This is the first Decl of the .debug_info1272 // This is the first Decl of the .debug_info
1275 self.dbg_info_decl_first = text_block;1273 self.dbg_info_decl_first = text_block;
1276 self.dbg_info_decl_last = text_block;1274 self.dbg_info_decl_last = text_block;
12771275
1278 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;1276 text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes());
1279 }1277 }
1280}1278}
12811279
src/link/MachO/commands.zig+3-5
...@@ -10,9 +10,7 @@ const assert = std.debug.assert;...@@ -10,9 +10,7 @@ const assert = std.debug.assert;
10const Allocator = std.mem.Allocator;10const Allocator = std.mem.Allocator;
11const MachO = @import("../MachO.zig");11const MachO = @import("../MachO.zig");
12const makeStaticString = MachO.makeStaticString;12const makeStaticString = MachO.makeStaticString;
13const satMul = MachO.satMul;13const padToIdeal = MachO.padToIdeal;
14const alloc_num = MachO.alloc_num;
15const alloc_den = MachO.alloc_den;
1614
17pub const LoadCommand = union(enum) {15pub const LoadCommand = union(enum) {
18 Segment: SegmentCommand,16 Segment: SegmentCommand,
...@@ -214,9 +212,9 @@ pub const SegmentCommand = struct {...@@ -214,9 +212,9 @@ pub const SegmentCommand = struct {
214 }212 }
215213
216 fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {214 fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
217 const end = start + satMul(size, alloc_num) / alloc_den;215 const end = start + padToIdeal(size);
218 for (self.sections.items) |section| {216 for (self.sections.items) |section| {
219 const increased_size = satMul(section.size, alloc_num) / alloc_den;217 const increased_size = padToIdeal(section.size);
220 const test_end = section.offset + increased_size;218 const test_end = section.offset + increased_size;
221 if (end > section.offset and start < test_end) {219 if (end > section.offset and start < test_end) {
222 return test_end;220 return test_end;
test/stage2/aarch64.zig-110
...@@ -1,84 +1,12 @@...@@ -1,84 +1,12 @@
1const std = @import("std");1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;2const TestContext = @import("../../src/test.zig").TestContext;
33
4const macos_aarch64 = std.zig.CrossTarget{
5 .cpu_arch = .aarch64,
6 .os_tag = .macos,
7};
8
9const linux_aarch64 = std.zig.CrossTarget{4const linux_aarch64 = std.zig.CrossTarget{
10 .cpu_arch = .aarch64,5 .cpu_arch = .aarch64,
11 .os_tag = .linux,6 .os_tag = .linux,
12};7};
138
14pub fn addCases(ctx: *TestContext) !void {9pub fn addCases(ctx: *TestContext) !void {
15 {
16 var case = ctx.exe("hello world with updates", macos_aarch64);
17
18 // Regular old hello world
19 case.addCompareOutput(
20 \\extern "c" fn write(usize, usize, usize) void;
21 \\extern "c" fn exit(usize) noreturn;
22 \\
23 \\export fn _start() noreturn {
24 \\ print();
25 \\
26 \\ exit(0);
27 \\}
28 \\
29 \\fn print() void {
30 \\ const msg = @ptrToInt("Hello, World!\n");
31 \\ const len = 14;
32 \\ write(1, msg, len);
33 \\}
34 ,
35 "Hello, World!\n",
36 );
37
38 // Now change the message only
39 case.addCompareOutput(
40 \\extern "c" fn write(usize, usize, usize) void;
41 \\extern "c" fn exit(usize) noreturn;
42 \\
43 \\export fn _start() noreturn {
44 \\ print();
45 \\
46 \\ exit(0);
47 \\}
48 \\
49 \\fn print() void {
50 \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
51 \\ const len = 104;
52 \\ write(1, msg, len);
53 \\}
54 ,
55 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
56 );
57
58 // Now we print it twice.
59 case.addCompareOutput(
60 \\extern "c" fn write(usize, usize, usize) void;
61 \\extern "c" fn exit(usize) noreturn;
62 \\
63 \\export fn _start() noreturn {
64 \\ print();
65 \\ print();
66 \\
67 \\ exit(0);
68 \\}
69 \\
70 \\fn print() void {
71 \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
72 \\ const len = 104;
73 \\ write(1, msg, len);
74 \\}
75 ,
76 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
77 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
78 \\
79 );
80 }
81
82 {10 {
83 var case = ctx.exe("linux_aarch64 hello world", linux_aarch64);11 var case = ctx.exe("linux_aarch64 hello world", linux_aarch64);
84 // Regular old hello world12 // Regular old hello world
...@@ -119,28 +47,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -119,28 +47,6 @@ pub fn addCases(ctx: *TestContext) !void {
119 );47 );
120 }48 }
12149
122 {
123 var case = ctx.exe("exit fn taking argument", macos_aarch64);
124
125 case.addCompareOutput(
126 \\export fn _start() noreturn {
127 \\ exit(0);
128 \\}
129 \\
130 \\fn exit(ret: usize) noreturn {
131 \\ asm volatile ("svc #0x80"
132 \\ :
133 \\ : [number] "{x16}" (1),
134 \\ [arg1] "{x0}" (ret)
135 \\ : "memory"
136 \\ );
137 \\ unreachable;
138 \\}
139 ,
140 "",
141 );
142 }
143
144 {50 {
145 var case = ctx.exe("exit fn taking argument", linux_aarch64);51 var case = ctx.exe("exit fn taking argument", linux_aarch64);
14652
...@@ -162,20 +68,4 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -162,20 +68,4 @@ pub fn addCases(ctx: *TestContext) !void {
162 "",68 "",
163 );69 );
164 }70 }
165
166 {
167 var case = ctx.exe("only libc exit", macos_aarch64);
168
169 // This test case covers an infrequent scenarion where the string table *may* be relocated
170 // into the position preceeding the symbol table which results in a dyld error.
171 case.addCompareOutput(
172 \\extern "c" fn exit(usize) noreturn;
173 \\
174 \\export fn _start() noreturn {
175 \\ exit(0);
176 \\}
177 ,
178 "",
179 );
180 }
181}71}
test/stage2/darwin.zig created+115
...@@ -0,0 +1,115 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const archs = [2]std.Target.Cpu.Arch{
5 .aarch64, .x86_64,
6};
7
8pub fn addCases(ctx: *TestContext) !void {
9 for (archs) |arch| {
10 const target: std.zig.CrossTarget = .{
11 .cpu_arch = arch,
12 .os_tag = .macos,
13 };
14 {
15 var case = ctx.exe("hello world with updates", target);
16 case.addError("", &[_][]const u8{"error: no entry point found"});
17
18 // Incorrect return type
19 case.addError(
20 \\export fn _start() noreturn {
21 \\}
22 , &[_][]const u8{":2:1: error: expected noreturn, found void"});
23
24 // Regular old hello world
25 case.addCompareOutput(
26 \\extern "c" fn write(usize, usize, usize) usize;
27 \\extern "c" fn exit(usize) noreturn;
28 \\
29 \\export fn _start() noreturn {
30 \\ print();
31 \\
32 \\ exit(0);
33 \\}
34 \\
35 \\fn print() void {
36 \\ const msg = @ptrToInt("Hello, World!\n");
37 \\ const len = 14;
38 \\ _ = write(1, msg, len);
39 \\}
40 ,
41 "Hello, World!\n",
42 );
43
44 // Now change the message only
45 case.addCompareOutput(
46 \\extern "c" fn write(usize, usize, usize) usize;
47 \\extern "c" fn exit(usize) noreturn;
48 \\
49 \\export fn _start() noreturn {
50 \\ print();
51 \\
52 \\ exit(0);
53 \\}
54 \\
55 \\fn print() void {
56 \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
57 \\ const len = 104;
58 \\ _ = write(1, msg, len);
59 \\}
60 ,
61 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
62 );
63
64 // Now we print it twice.
65 case.addCompareOutput(
66 \\extern "c" fn write(usize, usize, usize) usize;
67 \\extern "c" fn exit(usize) noreturn;
68 \\
69 \\export fn _start() noreturn {
70 \\ print();
71 \\ print();
72 \\
73 \\ exit(0);
74 \\}
75 \\
76 \\fn print() void {
77 \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
78 \\ const len = 104;
79 \\ _ = write(1, msg, len);
80 \\}
81 ,
82 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
83 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
84 \\
85 );
86 }
87 {
88 var case = ctx.exe("corner case - update existing, singular TextBlock", target);
89
90 // This test case also covers an infrequent scenarion where the string table *may* be relocated
91 // into the position preceeding the symbol table which results in a dyld error.
92 case.addCompareOutput(
93 \\extern "c" fn exit(usize) noreturn;
94 \\
95 \\export fn _start() noreturn {
96 \\ exit(0);
97 \\}
98 ,
99 "",
100 );
101
102 case.addCompareOutput(
103 \\extern "c" fn exit(usize) noreturn;
104 \\extern "c" fn write(usize, usize, usize) usize;
105 \\
106 \\export fn _start() noreturn {
107 \\ _ = write(1, @ptrToInt("Hey!\n"), 5);
108 \\ exit(0);
109 \\}
110 ,
111 "Hey!\n",
112 );
113 }
114 }
115}
test/stage2/test.zig+1-110
...@@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{...@@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{
11 .os_tag = .linux,11 .os_tag = .linux,
12};12};
1313
14const macos_x64 = std.zig.CrossTarget{
15 .cpu_arch = .x86_64,
16 .os_tag = .macos,
17};
18
19const linux_riscv64 = std.zig.CrossTarget{14const linux_riscv64 = std.zig.CrossTarget{
20 .cpu_arch = .riscv64,15 .cpu_arch = .riscv64,
21 .os_tag = .linux,16 .os_tag = .linux,
...@@ -28,6 +23,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -28,6 +23,7 @@ pub fn addCases(ctx: *TestContext) !void {
28 try @import("aarch64.zig").addCases(ctx);23 try @import("aarch64.zig").addCases(ctx);
29 try @import("llvm.zig").addCases(ctx);24 try @import("llvm.zig").addCases(ctx);
30 try @import("wasm.zig").addCases(ctx);25 try @import("wasm.zig").addCases(ctx);
26 try @import("darwin.zig").addCases(ctx);
3127
32 {28 {
33 var case = ctx.exe("hello world with updates", linux_x64);29 var case = ctx.exe("hello world with updates", linux_x64);
...@@ -141,95 +137,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -141,95 +137,6 @@ pub fn addCases(ctx: *TestContext) !void {
141 );137 );
142 }138 }
143139
144 {
145 var case = ctx.exe("hello world with updates", macos_x64);
146 case.addError("", &[_][]const u8{"error: no entry point found"});
147
148 // Incorrect return type
149 case.addError(
150 \\export fn _start() noreturn {
151 \\}
152 , &[_][]const u8{":2:1: error: expected noreturn, found void"});
153
154 // Regular old hello world
155 case.addCompareOutput(
156 \\extern "c" fn write(usize, usize, usize) usize;
157 \\extern "c" fn exit(usize) noreturn;
158 \\
159 \\export fn _start() noreturn {
160 \\ print();
161 \\
162 \\ exit(0);
163 \\}
164 \\
165 \\fn print() void {
166 \\ const msg = @ptrToInt("Hello, World!\n");
167 \\ const len = 14;
168 \\ const nwritten = write(1, msg, len);
169 \\ assert(nwritten == len);
170 \\}
171 \\
172 \\fn assert(ok: bool) void {
173 \\ if (!ok) unreachable; // assertion failure
174 \\}
175 ,
176 "Hello, World!\n",
177 );
178
179 // Now change the message only
180 case.addCompareOutput(
181 \\extern "c" fn write(usize, usize, usize) usize;
182 \\extern "c" fn exit(usize) noreturn;
183 \\
184 \\export fn _start() noreturn {
185 \\ print();
186 \\
187 \\ exit(0);
188 \\}
189 \\
190 \\fn print() void {
191 \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
192 \\ const len = 104;
193 \\ const nwritten = write(1, msg, len);
194 \\ assert(nwritten == len);
195 \\}
196 \\
197 \\fn assert(ok: bool) void {
198 \\ if (!ok) unreachable; // assertion failure
199 \\}
200 ,
201 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
202 );
203
204 // Now we print it twice.
205 case.addCompareOutput(
206 \\extern "c" fn write(usize, usize, usize) usize;
207 \\extern "c" fn exit(usize) noreturn;
208 \\
209 \\export fn _start() noreturn {
210 \\ print();
211 \\ print();
212 \\
213 \\ exit(0);
214 \\}
215 \\
216 \\fn print() void {
217 \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
218 \\ const len = 104;
219 \\ const nwritten = write(1, msg, len);
220 \\ assert(nwritten == len);
221 \\}
222 \\
223 \\fn assert(ok: bool) void {
224 \\ if (!ok) unreachable; // assertion failure
225 \\}
226 ,
227 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
228 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
229 \\
230 );
231 }
232
233 {140 {
234 var case = ctx.exe("riscv64 hello world", linux_riscv64);141 var case = ctx.exe("riscv64 hello world", linux_riscv64);
235 // Regular old hello world142 // Regular old hello world
...@@ -1446,22 +1353,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1446,22 +1353,6 @@ pub fn addCases(ctx: *TestContext) !void {
1446 \\}1353 \\}
1447 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});1354 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});
1448 }1355 }
1449
1450 {
1451 var case = ctx.exe("only libc exit", macos_x64);
1452
1453 // This test case covers an infrequent scenarion where the string table *may* be relocated
1454 // into the position preceeding the symbol table which results in a dyld error.
1455 case.addCompareOutput(
1456 \\extern "c" fn exit(usize) noreturn;
1457 \\
1458 \\export fn _start() noreturn {
1459 \\ exit(0);
1460 \\}
1461 ,
1462 "",
1463 );
1464 }
1465 {1356 {
1466 var case = ctx.exe("orelse at comptime", linux_x64);1357 var case = ctx.exe("orelse at comptime", linux_x64);
1467 case.addCompareOutput(1358 case.addCompareOutput(