| author | |
| committer | |
| log | 58344e0017a1e866ee9744b4862c57dbf39284b6 |
| tree | 8d86d17bd12371a6369dee44ee867616a4e4ef5d |
| parent | 8098b3f84cf24878e3388e056f60aba69033e0f6 |
| parent | a26ab9afeeeca405118fb3411dce0810ca723b5f |
| signature |
stage2 macho: make int casts fallible where necessary6 files changed, 208 insertions(+), 285 deletions(-)
src/link/MachO.zig+78-47| ... | ... | @@ -120,6 +120,7 @@ stub_helper_stubs_start_off: ?u64 = null, |
| 120 | 120 | |
| 121 | 121 | /// Table of symbol names aka the string table. |
| 122 | 122 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 123 | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, | |
| 123 | 124 | |
| 124 | 125 | /// Table of trampolines to the actual symbols in __text section. |
| 125 | 126 | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| ... | ... | @@ -142,11 +143,11 @@ string_table_needs_relocation: bool = false, |
| 142 | 143 | /// or removed from the freelist. |
| 143 | 144 | /// |
| 144 | 145 | /// 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 so | |
| 146 | /// padToIdeal(minimum_text_block_size). That is, when it has so | |
| 146 | 147 | /// much extra capacity, that we could fit a small new symbol in it, itself with |
| 147 | 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 | 152 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| 152 | 153 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| ... | ... | @@ -191,9 +192,9 @@ pub const StubFixup = struct { |
| 191 | 192 | len: usize, |
| 192 | 193 | }; |
| 193 | 194 | |
| 194 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | |
| 195 | pub const alloc_num = 4; | |
| 196 | pub const alloc_den = 3; | |
| 195 | /// When allocating, the ideal_capacity is calculated by | |
| 196 | /// actual_capacity + (actual_capacity / ideal_factor) | |
| 197 | const ideal_factor = 2; | |
| 197 | 198 | |
| 198 | 199 | /// Default path to dyld |
| 199 | 200 | /// 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 | 214 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| 214 | 215 | /// (plus extra for reserved capacity). |
| 215 | 216 | const minimum_text_block_size = 64; |
| 216 | const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; | |
| 217 | const min_text_capacity = padToIdeal(minimum_text_block_size); | |
| 217 | 218 | |
| 218 | 219 | pub const TextBlock = struct { |
| 219 | 220 | /// Each decl always gets a local symbol with the fully qualified name. |
| ... | ... | @@ -276,7 +277,7 @@ pub const TextBlock = struct { |
| 276 | 277 | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; |
| 277 | 278 | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; |
| 278 | 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 | 281 | if (cap <= ideal_cap) return false; |
| 281 | 282 | const surplus = cap - ideal_cap; |
| 282 | 283 | return surplus >= min_text_capacity; |
| ... | ... | @@ -872,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 872 | 873 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 873 | 874 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 874 | 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)); | |
| 876 | 877 | |
| 877 | 878 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 878 | 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 | 943 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 943 | 944 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 944 | 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)); | |
| 946 | 947 | |
| 947 | 948 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 948 | 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 | 1024 | self.text_block_free_list.deinit(self.base.allocator); |
| 1024 | 1025 | self.offset_table.deinit(self.base.allocator); |
| 1025 | 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 | 1034 | self.string_table.deinit(self.base.allocator); |
| 1027 | 1035 | self.global_symbols.deinit(self.base.allocator); |
| 1028 | 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 | 1237 | const this_addr = symbol.n_value + fixup.start; |
| 1230 | 1238 | switch (self.base.options.target.cpu.arch) { |
| 1231 | 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 | 1242 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| 1234 | 1243 | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| 1235 | 1244 | }, |
| 1236 | 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 | 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 | 1251 | else => unreachable, // unsupported target architecture |
| 1242 | 1252 | } |
| ... | ... | @@ -1249,14 +1259,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1249 | 1259 | const text_addr = symbol.n_value + fixup.start; |
| 1250 | 1260 | switch (self.base.options.target.cpu.arch) { |
| 1251 | 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 | 1264 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| 1254 | 1265 | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| 1255 | 1266 | }, |
| 1256 | 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 | 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 | 1273 | else => unreachable, // unsupported target architecture |
| 1262 | 1274 | } |
| ... | ... | @@ -1479,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1479 | 1491 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1480 | 1492 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1481 | 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); | |
| 1483 | 1495 | |
| 1484 | 1496 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| 1485 | 1497 | |
| ... | ... | @@ -1644,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1644 | 1656 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1645 | 1657 | |
| 1646 | 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); | |
| 1648 | 1660 | |
| 1649 | 1661 | log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); |
| 1650 | 1662 | |
| ... | ... | @@ -1701,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1701 | 1713 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1702 | 1714 | |
| 1703 | 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); | |
| 1705 | 1717 | |
| 1706 | 1718 | log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); |
| 1707 | 1719 | |
| ... | ... | @@ -2074,7 +2086,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2074 | 2086 | code[1] = 0x8d; |
| 2075 | 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 | 2090 | mem.writeIntLittle(u32, code[3..7], displacement); |
| 2079 | 2091 | } |
| 2080 | 2092 | // push %r11 |
| ... | ... | @@ -2084,7 +2096,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2084 | 2096 | code[9] = 0xff; |
| 2085 | 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 | 2100 | mem.writeIntLittle(u32, code[11..], displacement); |
| 2089 | 2101 | } |
| 2090 | 2102 | self.stub_helper_stubs_start_off = stub_helper.offset + code_size; |
| ... | ... | @@ -2093,8 +2105,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2093 | 2105 | .aarch64 => { |
| 2094 | 2106 | var code: [4 * @sizeOf(u32)]u8 = undefined; |
| 2095 | 2107 | { |
| 2096 | const displacement = data.addr - stub_helper.addr; | |
| 2097 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32()); | |
| 2108 | const displacement = try math.cast(i21, data.addr - stub_helper.addr); | |
| 2109 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 2098 | 2110 | } |
| 2099 | 2111 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( |
| 2100 | 2112 | .x16, |
| ... | ... | @@ -2103,9 +2115,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2103 | 2115 | aarch64.Instruction.LoadStorePairOffset.pre_index(-16), |
| 2104 | 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 | 2120 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ |
| 2108 | .literal = @intCast(u19, displacement / 4), | |
| 2121 | .literal = literal, | |
| 2109 | 2122 | }).toU32()); |
| 2110 | 2123 | } |
| 2111 | 2124 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); |
| ... | ... | @@ -2120,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2120 | 2133 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 2121 | 2134 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2122 | 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); | |
| 2124 | 2137 | |
| 2125 | 2138 | // We use these to indicate our intention to update metadata, placing the new block, |
| 2126 | 2139 | // and possibly removing a free list node. |
| ... | ... | @@ -2140,7 +2153,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2140 | 2153 | // Is it enough that we could fit this new text block? |
| 2141 | 2154 | const sym = self.local_symbols.items[big_block.local_sym_index]; |
| 2142 | 2155 | const capacity = big_block.capacity(self.*); |
| 2143 | const ideal_capacity = capacity * alloc_num / alloc_den; | |
| 2156 | const ideal_capacity = padToIdeal(capacity); | |
| 2144 | 2157 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; |
| 2145 | 2158 | const capacity_end_vaddr = sym.n_value + capacity; |
| 2146 | 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 | 2185 | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 2173 | 2186 | // TODO We should pad out the excess capacity with NOPs. For executables, |
| 2174 | 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 | 2189 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; |
| 2177 | 2190 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 2178 | 2191 | block_placement = last; |
| ... | ... | @@ -2230,14 +2243,26 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 2230 | 2243 | } |
| 2231 | 2244 | |
| 2232 | 2245 | fn 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 | 2251 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); |
| 2234 | 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 | 2254 | self.string_table.appendSliceAssumeCapacity(bytes); |
| 2237 | 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 | 2262 | self.string_table_dirty = true; |
| 2239 | 2263 | if (self.d_sym) |*ds| |
| 2240 | 2264 | ds.string_table_dirty = true; |
| 2265 | ||
| 2241 | 2266 | return offset; |
| 2242 | 2267 | } |
| 2243 | 2268 | |
| ... | ... | @@ -2335,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { |
| 2335 | 2360 | } |
| 2336 | 2361 | |
| 2337 | 2362 | inline 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 | 2364 | const test_end = off + increased_size; |
| 2340 | 2365 | if (end > off and start < test_end) { |
| 2341 | 2366 | return test_end; |
| ... | ... | @@ -2344,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { |
| 2344 | 2369 | } |
| 2345 | 2370 | |
| 2346 | 2371 | fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { |
| 2347 | const end = start + satMul(size, alloc_num) / alloc_den; | |
| 2372 | const end = start + padToIdeal(size); | |
| 2348 | 2373 | |
| 2349 | 2374 | // __LINKEDIT is a weird segment where sections get their own load commands so we |
| 2350 | 2375 | // special-case it. |
| ... | ... | @@ -2425,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2425 | 2450 | return st; |
| 2426 | 2451 | } |
| 2427 | 2452 | |
| 2428 | /// Saturating multiplication | |
| 2429 | pub 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 | ||
| 2434 | 2453 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2435 | 2454 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2436 | 2455 | const sect = &text_segment.sections.items[self.got_section_index.?]; |
| ... | ... | @@ -2445,8 +2464,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2445 | 2464 | var code: [8]u8 = undefined; |
| 2446 | 2465 | switch (self.base.options.target.cpu.arch) { |
| 2447 | 2466 | .x86_64 => { |
| 2448 | const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7); | |
| 2449 | const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); | |
| 2467 | const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7); | |
| 2468 | const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1); | |
| 2450 | 2469 | // lea %rax, [rip - disp] |
| 2451 | 2470 | code[0] = 0x48; |
| 2452 | 2471 | code[1] = 0x8D; |
| ... | ... | @@ -2456,8 +2475,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2456 | 2475 | code[7] = 0xC3; |
| 2457 | 2476 | }, |
| 2458 | 2477 | .aarch64 => { |
| 2459 | const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); | |
| 2460 | const symbol_off = @intCast(i21, pos_symbol_off) * -1; | |
| 2478 | const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]); | |
| 2479 | const symbol_off = @as(i21, pos_symbol_off) * -1; | |
| 2461 | 2480 | // adr x0, #-disp |
| 2462 | 2481 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); |
| 2463 | 2482 | // ret x28 |
| ... | ... | @@ -2503,16 +2522,19 @@ fn writeStub(self: *MachO, index: u32) !void { |
| 2503 | 2522 | defer self.base.allocator.free(code); |
| 2504 | 2523 | switch (self.base.options.target.cpu.arch) { |
| 2505 | 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 | 2527 | // jmp |
| 2508 | 2528 | code[0] = 0xff; |
| 2509 | 2529 | code[1] = 0x25; |
| 2510 | 2530 | mem.writeIntLittle(u32, code[2..][0..4], displacement); |
| 2511 | 2531 | }, |
| 2512 | 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 | 2536 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ |
| 2515 | .literal = @intCast(u19, displacement / 4), | |
| 2537 | .literal = literal, | |
| 2516 | 2538 | }).toU32()); |
| 2517 | 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 | 2557 | defer self.base.allocator.free(code); |
| 2536 | 2558 | switch (self.base.options.target.cpu.arch) { |
| 2537 | 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 | 2564 | // pushq |
| 2540 | 2565 | code[0] = 0x68; |
| 2541 | 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 | 2569 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); |
| 2545 | 2570 | }, |
| 2546 | 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 | 2573 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ |
| 2549 | .literal = 0x2, | |
| 2574 | .literal = @divExact(stub_size - @sizeOf(u32), 4), | |
| 2550 | 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 | 2577 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 2553 | 2578 | }, |
| 2554 | 2579 | else => unreachable, |
| ... | ... | @@ -3239,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { |
| 3239 | 3264 | } |
| 3240 | 3265 | } |
| 3241 | 3266 | } |
| 3267 | ||
| 3268 | pub 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 | 18 | const MachO = @import("../MachO.zig"); |
| 19 | 19 | const SrcFn = MachO.SrcFn; |
| 20 | 20 | const TextBlock = MachO.TextBlock; |
| 21 | const satMul = MachO.satMul; | |
| 22 | const alloc_num = MachO.alloc_num; | |
| 23 | const alloc_den = MachO.alloc_den; | |
| 21 | const padToIdeal = MachO.padToIdeal; | |
| 24 | 22 | const makeStaticString = MachO.makeStaticString; |
| 25 | 23 | |
| 26 | 24 | usingnamespace @import("commands.zig"); |
| ... | ... | @@ -207,7 +205,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 207 | 205 | |
| 208 | 206 | const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 209 | 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 | 209 | const off = linkedit.inner.fileoff + linkedit.inner.filesize; |
| 212 | 210 | const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize; |
| 213 | 211 | |
| ... | ... | @@ -804,7 +802,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 { |
| 804 | 802 | } |
| 805 | 803 | |
| 806 | 804 | fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 { |
| 807 | const end = start + satMul(size, alloc_num) / alloc_den; | |
| 805 | const end = start + padToIdeal(size); | |
| 808 | 806 | |
| 809 | 807 | if (self.symtab_cmd_index) |idx| outer: { |
| 810 | 808 | if (self.load_commands.items.len == idx) break :outer; |
| ... | ... | @@ -812,7 +810,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 |
| 812 | 810 | { |
| 813 | 811 | // Symbol table |
| 814 | 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 | 814 | const test_end = symtab.symoff + increased_size; |
| 817 | 815 | if (end > symtab.symoff and start < test_end) { |
| 818 | 816 | return test_end; |
| ... | ... | @@ -820,7 +818,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 |
| 820 | 818 | } |
| 821 | 819 | { |
| 822 | 820 | // String table |
| 823 | const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den; | |
| 821 | const increased_size = padToIdeal(symtab.strsize); | |
| 824 | 822 | const test_end = symtab.stroff + increased_size; |
| 825 | 823 | if (end > symtab.stroff and start < test_end) { |
| 826 | 824 | return test_end; |
| ... | ... | @@ -1099,7 +1097,7 @@ pub fn commitDeclDebugInfo( |
| 1099 | 1097 | last.next = src_fn; |
| 1100 | 1098 | self.dbg_line_fn_last = src_fn; |
| 1101 | 1099 | |
| 1102 | src_fn.off = last.off + (last.len * alloc_num / alloc_den); | |
| 1100 | src_fn.off = last.off + padToIdeal(last.len); | |
| 1103 | 1101 | } |
| 1104 | 1102 | } else if (src_fn.prev == null) { |
| 1105 | 1103 | // Append new function. |
| ... | ... | @@ -1108,14 +1106,14 @@ pub fn commitDeclDebugInfo( |
| 1108 | 1106 | last.next = src_fn; |
| 1109 | 1107 | self.dbg_line_fn_last = src_fn; |
| 1110 | 1108 | |
| 1111 | src_fn.off = last.off + (last.len * alloc_num / alloc_den); | |
| 1109 | src_fn.off = last.off + padToIdeal(last.len); | |
| 1112 | 1110 | } |
| 1113 | 1111 | } else { |
| 1114 | 1112 | // This is the first function of the Line Number Program. |
| 1115 | 1113 | self.dbg_line_fn_first = src_fn; |
| 1116 | 1114 | self.dbg_line_fn_last = src_fn; |
| 1117 | 1115 | |
| 1118 | src_fn.off = self.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den; | |
| 1116 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module)); | |
| 1119 | 1117 | } |
| 1120 | 1118 | |
| 1121 | 1119 | const last_src_fn = self.dbg_line_fn_last.?; |
| ... | ... | @@ -1259,7 +1257,7 @@ fn updateDeclDebugInfoAllocation( |
| 1259 | 1257 | last.dbg_info_next = text_block; |
| 1260 | 1258 | self.dbg_info_decl_last = text_block; |
| 1261 | 1259 | |
| 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 | 1262 | } else if (text_block.dbg_info_prev == null) { |
| 1265 | 1263 | // Append new Decl. |
| ... | ... | @@ -1268,14 +1266,14 @@ fn updateDeclDebugInfoAllocation( |
| 1268 | 1266 | last.dbg_info_next = text_block; |
| 1269 | 1267 | self.dbg_info_decl_last = text_block; |
| 1270 | 1268 | |
| 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 | 1271 | } else { |
| 1274 | 1272 | // This is the first Decl of the .debug_info |
| 1275 | 1273 | self.dbg_info_decl_first = text_block; |
| 1276 | 1274 | self.dbg_info_decl_last = text_block; |
| 1277 | 1275 | |
| 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 | } |
| 1281 | 1279 |
src/link/MachO/commands.zig+3-5| ... | ... | @@ -10,9 +10,7 @@ const assert = std.debug.assert; |
| 10 | 10 | const Allocator = std.mem.Allocator; |
| 11 | 11 | const MachO = @import("../MachO.zig"); |
| 12 | 12 | const makeStaticString = MachO.makeStaticString; |
| 13 | const satMul = MachO.satMul; | |
| 14 | const alloc_num = MachO.alloc_num; | |
| 15 | const alloc_den = MachO.alloc_den; | |
| 13 | const padToIdeal = MachO.padToIdeal; | |
| 16 | 14 | |
| 17 | 15 | pub const LoadCommand = union(enum) { |
| 18 | 16 | Segment: SegmentCommand, |
| ... | ... | @@ -214,9 +212,9 @@ pub const SegmentCommand = struct { |
| 214 | 212 | } |
| 215 | 213 | |
| 216 | 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 | 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 | 218 | const test_end = section.offset + increased_size; |
| 221 | 219 | if (end > section.offset and start < test_end) { |
| 222 | 220 | return test_end; |
test/stage2/aarch64.zig-110| ... | ... | @@ -1,84 +1,12 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const TestContext = @import("../../src/test.zig").TestContext; |
| 3 | 3 | |
| 4 | const macos_aarch64 = std.zig.CrossTarget{ | |
| 5 | .cpu_arch = .aarch64, | |
| 6 | .os_tag = .macos, | |
| 7 | }; | |
| 8 | ||
| 9 | 4 | const linux_aarch64 = std.zig.CrossTarget{ |
| 10 | 5 | .cpu_arch = .aarch64, |
| 11 | 6 | .os_tag = .linux, |
| 12 | 7 | }; |
| 13 | 8 | |
| 14 | 9 | pub 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 | 11 | var case = ctx.exe("linux_aarch64 hello world", linux_aarch64); |
| 84 | 12 | // Regular old hello world |
| ... | ... | @@ -119,28 +47,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 119 | 47 | ); |
| 120 | 48 | } |
| 121 | 49 | |
| 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 | 51 | var case = ctx.exe("exit fn taking argument", linux_aarch64); |
| 146 | 52 | |
| ... | ... | @@ -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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | |
| 3 | ||
| 4 | const archs = [2]std.Target.Cpu.Arch{ | |
| 5 | .aarch64, .x86_64, | |
| 6 | }; | |
| 7 | ||
| 8 | pub 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 | .os_tag = .linux, |
| 12 | 12 | }; |
| 13 | 13 | |
| 14 | const macos_x64 = std.zig.CrossTarget{ | |
| 15 | .cpu_arch = .x86_64, | |
| 16 | .os_tag = .macos, | |
| 17 | }; | |
| 18 | ||
| 19 | 14 | const linux_riscv64 = std.zig.CrossTarget{ |
| 20 | 15 | .cpu_arch = .riscv64, |
| 21 | 16 | .os_tag = .linux, |
| ... | ... | @@ -28,6 +23,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 28 | 23 | try @import("aarch64.zig").addCases(ctx); |
| 29 | 24 | try @import("llvm.zig").addCases(ctx); |
| 30 | 25 | try @import("wasm.zig").addCases(ctx); |
| 26 | try @import("darwin.zig").addCases(ctx); | |
| 31 | 27 | |
| 32 | 28 | { |
| 33 | 29 | var case = ctx.exe("hello world with updates", linux_x64); |
| ... | ... | @@ -141,95 +137,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 141 | 137 | ); |
| 142 | 138 | } |
| 143 | 139 | |
| 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 | 141 | var case = ctx.exe("riscv64 hello world", linux_riscv64); |
| 235 | 142 | // Regular old hello world |
| ... | ... | @@ -1446,22 +1353,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1446 | 1353 | \\} |
| 1447 | 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 | 1357 | var case = ctx.exe("orelse at comptime", linux_x64); |
| 1467 | 1358 | case.addCompareOutput( |