| ... | @@ -163,14 +163,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -163,14 +163,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 163 | try file.writeAll(&(wasm.magic ++ wasm.version)); | 163 | try file.writeAll(&(wasm.magic ++ wasm.version)); |
| 164 | | 164 | |
| 165 | // As sym_index '0' is reserved, we use it for our stack pointer symbol | 165 | // As sym_index '0' is reserved, we use it for our stack pointer symbol |
| 166 | const global = try wasm_bin.wasm_globals.addOne(allocator); | | |
| 167 | global.* = .{ | | |
| 168 | .global_type = .{ | | |
| 169 | .valtype = .i32, | | |
| 170 | .mutable = true, | | |
| 171 | }, | | |
| 172 | .init = .{ .i32_const = 0 }, | | |
| 173 | }; | | |
| 174 | const symbol = try wasm_bin.symbols.addOne(allocator); | 166 | const symbol = try wasm_bin.symbols.addOne(allocator); |
| 175 | symbol.* = .{ | 167 | symbol.* = .{ |
| 176 | .name = "__stack_pointer", | 168 | .name = "__stack_pointer", |
| ... | @@ -178,6 +170,28 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -178,6 +170,28 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 178 | .flags = 0, | 170 | .flags = 0, |
| 179 | .index = 0, | 171 | .index = 0, |
| 180 | }; | 172 | }; |
| | 173 | // For object files we will import the stack pointer symbol |
| | 174 | if (options.output_mode == .Obj) { |
| | 175 | symbol.setUndefined(true); |
| | 176 | try wasm_bin.imports.putNoClobber( |
| | 177 | allocator, |
| | 178 | .{ .file = null, .index = 0 }, |
| | 179 | .{ |
| | 180 | .module_name = wasm_bin.host_name, |
| | 181 | .name = "__stack_pointer", |
| | 182 | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, |
| | 183 | }, |
| | 184 | ); |
| | 185 | } else { |
| | 186 | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| | 187 | global.* = .{ |
| | 188 | .global_type = .{ |
| | 189 | .valtype = .i32, |
| | 190 | .mutable = true, |
| | 191 | }, |
| | 192 | .init = .{ .i32_const = 0 }, |
| | 193 | }; |
| | 194 | } |
| 181 | return wasm_bin; | 195 | return wasm_bin; |
| 182 | } | 196 | } |
| 183 | | 197 | |
| ... | @@ -651,36 +665,50 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -651,36 +665,50 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 651 | break :result self.code_section_index.?; | 665 | break :result self.code_section_index.?; |
| 652 | }, | 666 | }, |
| 653 | .data => result: { | 667 | .data => result: { |
| 654 | const gop = try self.data_segments.getOrPut(self.base.allocator, ".rodata"); | | |
| 655 | const atom_index = if (gop.found_existing) blk: { | | |
| 656 | self.segments.items[gop.value_ptr.*].size += atom.size; | | |
| 657 | break :blk gop.value_ptr.*; | | |
| 658 | } else blk: { | | |
| 659 | const index = @intCast(u32, self.segments.items.len); | | |
| 660 | try self.segments.append(self.base.allocator, .{ | | |
| 661 | .alignment = atom.alignment, | | |
| 662 | .size = 0, | | |
| 663 | .offset = 0, | | |
| 664 | }); | | |
| 665 | gop.value_ptr.* = index; | | |
| 666 | break :blk index; | | |
| 667 | }; | | |
| 668 | const info_index = @intCast(u32, self.segment_info.items.len); | | |
| 669 | // TODO: Add mutables global decls to .bss section instead | 668 | // TODO: Add mutables global decls to .bss section instead |
| 670 | const segment_name = try std.mem.concat(self.base.allocator, u8, &.{ | 669 | const segment_name = try std.mem.concat(self.base.allocator, u8, &.{ |
| 671 | ".rodata.", | 670 | ".rodata.", |
| 672 | std.mem.span(symbol.name), | 671 | std.mem.span(symbol.name), |
| 673 | }); | 672 | }); |
| 674 | errdefer self.base.allocator.free(segment_name); | 673 | errdefer self.base.allocator.free(segment_name); |
| 675 | try self.segment_info.append(self.base.allocator, .{ | 674 | const segment_info: types.Segment = .{ |
| 676 | .name = segment_name, | 675 | .name = segment_name, |
| 677 | .alignment = atom.alignment, | 676 | .alignment = atom.alignment, |
| 678 | .flags = 0, | 677 | .flags = 0, |
| 679 | }); | 678 | }; |
| 680 | symbol.tag = .data; | 679 | symbol.tag = .data; |
| 681 | symbol.index = info_index; | | |
| 682 | | 680 | |
| 683 | break :result atom_index; | 681 | const should_merge = self.base.options.output_mode != .Obj; |
| | 682 | const gop = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(should_merge)); |
| | 683 | if (gop.found_existing) { |
| | 684 | const index = gop.value_ptr.*; |
| | 685 | self.segments.items[index].size += atom.size; |
| | 686 | |
| | 687 | // segment indexes can be off by 1 due to also containing a segment |
| | 688 | // for the code section, so we must check if the existing segment |
| | 689 | // is larger than that of the code section, and substract the index by 1 in such case. |
| | 690 | const info_add = if (self.code_section_index) |idx| blk: { |
| | 691 | if (idx < index) break :blk @as(u32, 1); |
| | 692 | break :blk 0; |
| | 693 | } else @as(u32, 0); |
| | 694 | symbol.index = index - info_add; |
| | 695 | // segment info already exists, so free its memory |
| | 696 | self.base.allocator.free(segment_name); |
| | 697 | break :result index; |
| | 698 | } else { |
| | 699 | const index = @intCast(u32, self.segments.items.len); |
| | 700 | try self.segments.append(self.base.allocator, .{ |
| | 701 | .alignment = atom.alignment, |
| | 702 | .size = 0, |
| | 703 | .offset = 0, |
| | 704 | }); |
| | 705 | gop.value_ptr.* = index; |
| | 706 | |
| | 707 | const info_index = @intCast(u32, self.segment_info.items.len); |
| | 708 | try self.segment_info.append(self.base.allocator, segment_info); |
| | 709 | symbol.index = info_index; |
| | 710 | break :result index; |
| | 711 | } |
| 684 | }, | 712 | }, |
| 685 | }; | 713 | }; |
| 686 | | 714 | |
| ... | @@ -932,7 +960,9 @@ fn setupMemory(self: *Wasm) !void { | ... | @@ -932,7 +960,9 @@ fn setupMemory(self: *Wasm) !void { |
| 932 | break :blk base; | 960 | break :blk base; |
| 933 | } else 0; | 961 | } else 0; |
| 934 | | 962 | |
| 935 | if (place_stack_first) { | 963 | const is_obj = self.base.options.output_mode == .Obj; |
| | 964 | |
| | 965 | if (place_stack_first and !is_obj) { |
| 936 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); | 966 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); |
| 937 | memory_ptr += stack_size; | 967 | memory_ptr += stack_size; |
| 938 | // We always put the stack pointer global at index 0 | 968 | // We always put the stack pointer global at index 0 |
| ... | @@ -951,7 +981,7 @@ fn setupMemory(self: *Wasm) !void { | ... | @@ -951,7 +981,7 @@ fn setupMemory(self: *Wasm) !void { |
| 951 | offset += segment.size; | 981 | offset += segment.size; |
| 952 | } | 982 | } |
| 953 | | 983 | |
| 954 | if (!place_stack_first) { | 984 | if (!place_stack_first and !is_obj) { |
| 955 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); | 985 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); |
| 956 | memory_ptr += stack_size; | 986 | memory_ptr += stack_size; |
| 957 | self.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); | 987 | self.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); |
| ... | @@ -1011,7 +1041,8 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 | ... | @@ -1011,7 +1041,8 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 |
| 1011 | switch (relocatable_data.type) { | 1041 | switch (relocatable_data.type) { |
| 1012 | .data => { | 1042 | .data => { |
| 1013 | const segment_info = object.segment_info[relocatable_data.index]; | 1043 | const segment_info = object.segment_info[relocatable_data.index]; |
| 1014 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName()); | 1044 | const merge_segment = self.base.options.output_mode != .Obj; |
| | 1045 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); |
| 1015 | if (!result.found_existing) { | 1046 | if (!result.found_existing) { |
| 1016 | result.value_ptr.* = index; | 1047 | result.value_ptr.* = index; |
| 1017 | try self.segments.append(self.base.allocator, .{ | 1048 | try self.segments.append(self.base.allocator, .{ |
| ... | @@ -1368,7 +1399,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1368,7 +1399,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1368 | const writer = file.writer(); | 1399 | const writer = file.writer(); |
| 1369 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); | 1400 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); |
| 1370 | while (true) { | 1401 | while (true) { |
| 1371 | try atom.resolveRelocs(self); | 1402 | if (!is_obj) { |
| | 1403 | try atom.resolveRelocs(self); |
| | 1404 | } |
| 1372 | try leb.writeULEB128(writer, atom.size); | 1405 | try leb.writeULEB128(writer, atom.size); |
| 1373 | try writer.writeAll(atom.code.items); | 1406 | try writer.writeAll(atom.code.items); |
| 1374 | atom = atom.next orelse break; | 1407 | atom = atom.next orelse break; |
| ... | @@ -1390,8 +1423,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1390,8 +1423,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1390 | var it = self.data_segments.iterator(); | 1423 | var it = self.data_segments.iterator(); |
| 1391 | var segment_count: u32 = 0; | 1424 | var segment_count: u32 = 0; |
| 1392 | while (it.next()) |entry| { | 1425 | while (it.next()) |entry| { |
| 1393 | // do not output 'bss' section | 1426 | // do not output 'bss' section unless we import memory and therefore |
| 1394 | if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; | 1427 | // want to guarantee the data is zero initialized |
| | 1428 | if (std.mem.eql(u8, entry.key_ptr.*, ".bss") and !import_memory) continue; |
| 1395 | segment_count += 1; | 1429 | segment_count += 1; |
| 1396 | const atom_index = entry.value_ptr.*; | 1430 | const atom_index = entry.value_ptr.*; |
| 1397 | var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); | 1431 | var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); |
| ... | @@ -1406,7 +1440,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1406,7 +1440,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1406 | // fill in the offset table and the data segments | 1440 | // fill in the offset table and the data segments |
| 1407 | var current_offset: u32 = 0; | 1441 | var current_offset: u32 = 0; |
| 1408 | while (true) { | 1442 | while (true) { |
| 1409 | try atom.resolveRelocs(self); | 1443 | if (!is_obj) { |
| | 1444 | try atom.resolveRelocs(self); |
| | 1445 | } |
| 1410 | | 1446 | |
| 1411 | // Pad with zeroes to ensure all segments are aligned | 1447 | // Pad with zeroes to ensure all segments are aligned |
| 1412 | if (current_offset != atom.offset) { | 1448 | if (current_offset != atom.offset) { |
| ... | @@ -1443,60 +1479,58 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { | ... | @@ -1443,60 +1479,58 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1443 | ); | 1479 | ); |
| 1444 | } | 1480 | } |
| 1445 | | 1481 | |
| 1446 | // Custom section "name" which contains symbol names | 1482 | if (is_obj) { |
| 1447 | if (!is_obj) { | 1483 | try self.emitLinkSection(file, arena); |
| 1448 | const Name = struct { | 1484 | } else { |
| 1449 | index: u32, | 1485 | try self.emitNameSection(file, arena); |
| 1450 | name: []const u8, | 1486 | } |
| 1451 | | 1487 | } |
| 1452 | fn lessThan(context: void, lhs: @This(), rhs: @This()) bool { | | |
| 1453 | _ = context; | | |
| 1454 | return lhs.index < rhs.index; | | |
| 1455 | } | | |
| 1456 | }; | | |
| 1457 | | 1488 | |
| 1458 | var funcs = try std.ArrayList(Name).initCapacity(self.base.allocator, self.functions.items.len + self.imported_functions_count); | 1489 | fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 1459 | defer funcs.deinit(); | 1490 | const Name = struct { |
| 1460 | var globals = try std.ArrayList(Name).initCapacity(self.base.allocator, self.wasm_globals.items.len); | 1491 | index: u32, |
| 1461 | defer globals.deinit(); | 1492 | name: []const u8, |
| 1462 | var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count()); | | |
| 1463 | defer segments.deinit(); | | |
| 1464 | | 1493 | |
| 1465 | for (self.resolved_symbols.keys()) |sym_loc| { | 1494 | fn lessThan(context: void, lhs: @This(), rhs: @This()) bool { |
| 1466 | const symbol = sym_loc.getSymbol(self).*; | 1495 | _ = context; |
| 1467 | switch (symbol.tag) { | 1496 | return lhs.index < rhs.index; |
| 1468 | .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }), | | |
| 1469 | .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }), | | |
| 1470 | else => {}, | | |
| 1471 | } | | |
| 1472 | } | | |
| 1473 | // data segments are already 'ordered' | | |
| 1474 | for (self.data_segments.keys()) |key, index| { | | |
| 1475 | segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key }); | | |
| 1476 | } | 1497 | } |
| | 1498 | }; |
| 1477 | | 1499 | |
| 1478 | std.sort.sort(Name, funcs.items, {}, Name.lessThan); | 1500 | var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.items.len + self.imported_functions_count); |
| 1479 | std.sort.sort(Name, globals.items, {}, Name.lessThan); | 1501 | var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len); |
| | 1502 | var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count()); |
| 1480 | | 1503 | |
| 1481 | const header_offset = try reserveCustomSectionHeader(file); | 1504 | for (self.resolved_symbols.keys()) |sym_loc| { |
| 1482 | const writer = file.writer(); | 1505 | const symbol = sym_loc.getSymbol(self).*; |
| 1483 | try leb.writeULEB128(writer, @intCast(u32, "name".len)); | 1506 | switch (symbol.tag) { |
| 1484 | try writer.writeAll("name"); | 1507 | .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }), |
| | 1508 | .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }), |
| | 1509 | else => {}, |
| | 1510 | } |
| | 1511 | } |
| | 1512 | // data segments are already 'ordered' |
| | 1513 | for (self.data_segments.keys()) |key, index| { |
| | 1514 | segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key }); |
| | 1515 | } |
| 1485 | | 1516 | |
| 1486 | try self.emitNameSubsection(.function, funcs.items, writer); | 1517 | std.sort.sort(Name, funcs.items, {}, Name.lessThan); |
| 1487 | try self.emitNameSubsection(.global, globals.items, writer); | 1518 | std.sort.sort(Name, globals.items, {}, Name.lessThan); |
| 1488 | try self.emitNameSubsection(.data_segment, segments.items, writer); | | |
| 1489 | | 1519 | |
| 1490 | try writeCustomSectionHeader( | 1520 | const header_offset = try reserveCustomSectionHeader(file); |
| 1491 | file, | 1521 | const writer = file.writer(); |
| 1492 | header_offset, | 1522 | try leb.writeULEB128(writer, @intCast(u32, "name".len)); |
| 1493 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | 1523 | try writer.writeAll("name"); |
| 1494 | ); | | |
| 1495 | } | | |
| 1496 | | 1524 | |
| 1497 | if (is_obj) { | 1525 | try self.emitNameSubsection(.function, funcs.items, writer); |
| 1498 | try self.emitLinkSection(file, arena); | 1526 | try self.emitNameSubsection(.global, globals.items, writer); |
| 1499 | } | 1527 | try self.emitNameSubsection(.data_segment, segments.items, writer); |
| | 1528 | |
| | 1529 | try writeCustomSectionHeader( |
| | 1530 | file, |
| | 1531 | header_offset, |
| | 1532 | @intCast(u32, (try file.getPos()) - header_offset - 6), |
| | 1533 | ); |
| 1500 | } | 1534 | } |
| 1501 | | 1535 | |
| 1502 | fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { | 1536 | fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { |