| ... | ... | @@ -163,14 +163,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 163 | 163 | try file.writeAll(&(wasm.magic ++ wasm.version)); |
| 164 | 164 | |
| 165 | 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 | 166 | const symbol = try wasm_bin.symbols.addOne(allocator); |
| 175 | 167 | symbol.* = .{ |
| 176 | 168 | .name = "__stack_pointer", |
| ... | ... | @@ -178,6 +170,28 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 178 | 170 | .flags = 0, |
| 179 | 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 | 195 | return wasm_bin; |
| 182 | 196 | } |
| 183 | 197 | |
| ... | ... | @@ -651,36 +665,50 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 651 | 665 | break :result self.code_section_index.?; |
| 652 | 666 | }, |
| 653 | 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 | 668 | // TODO: Add mutables global decls to .bss section instead |
| 670 | 669 | const segment_name = try std.mem.concat(self.base.allocator, u8, &.{ |
| 671 | 670 | ".rodata.", |
| 672 | 671 | std.mem.span(symbol.name), |
| 673 | 672 | }); |
| 674 | 673 | errdefer self.base.allocator.free(segment_name); |
| 675 | | try self.segment_info.append(self.base.allocator, .{ |
| 674 | const segment_info: types.Segment = .{ |
| 676 | 675 | .name = segment_name, |
| 677 | 676 | .alignment = atom.alignment, |
| 678 | 677 | .flags = 0, |
| 679 | | }); |
| 678 | }; |
| 680 | 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 | 960 | break :blk base; |
| 933 | 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 | 966 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); |
| 937 | 967 | memory_ptr += stack_size; |
| 938 | 968 | // We always put the stack pointer global at index 0 |
| ... | ... | @@ -951,7 +981,7 @@ fn setupMemory(self: *Wasm) !void { |
| 951 | 981 | offset += segment.size; |
| 952 | 982 | } |
| 953 | 983 | |
| 954 | | if (!place_stack_first) { |
| 984 | if (!place_stack_first and !is_obj) { |
| 955 | 985 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); |
| 956 | 986 | memory_ptr += stack_size; |
| 957 | 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 | 1041 | switch (relocatable_data.type) { |
| 1012 | 1042 | .data => { |
| 1013 | 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 | 1046 | if (!result.found_existing) { |
| 1016 | 1047 | result.value_ptr.* = index; |
| 1017 | 1048 | try self.segments.append(self.base.allocator, .{ |
| ... | ... | @@ -1368,7 +1399,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1368 | 1399 | const writer = file.writer(); |
| 1369 | 1400 | var atom: *Atom = self.atoms.get(code_index).?.getFirst(); |
| 1370 | 1401 | while (true) { |
| 1371 | | try atom.resolveRelocs(self); |
| 1402 | if (!is_obj) { |
| 1403 | try atom.resolveRelocs(self); |
| 1404 | } |
| 1372 | 1405 | try leb.writeULEB128(writer, atom.size); |
| 1373 | 1406 | try writer.writeAll(atom.code.items); |
| 1374 | 1407 | atom = atom.next orelse break; |
| ... | ... | @@ -1390,8 +1423,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1390 | 1423 | var it = self.data_segments.iterator(); |
| 1391 | 1424 | var segment_count: u32 = 0; |
| 1392 | 1425 | while (it.next()) |entry| { |
| 1393 | | // do not output 'bss' section |
| 1394 | | if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; |
| 1426 | // do not output 'bss' section unless we import memory and therefore |
| 1427 | // want to guarantee the data is zero initialized |
| 1428 | if (std.mem.eql(u8, entry.key_ptr.*, ".bss") and !import_memory) continue; |
| 1395 | 1429 | segment_count += 1; |
| 1396 | 1430 | const atom_index = entry.value_ptr.*; |
| 1397 | 1431 | var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); |
| ... | ... | @@ -1406,7 +1440,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 1406 | 1440 | // fill in the offset table and the data segments |
| 1407 | 1441 | var current_offset: u32 = 0; |
| 1408 | 1442 | while (true) { |
| 1409 | | try atom.resolveRelocs(self); |
| 1443 | if (!is_obj) { |
| 1444 | try atom.resolveRelocs(self); |
| 1445 | } |
| 1410 | 1446 | |
| 1411 | 1447 | // Pad with zeroes to ensure all segments are aligned |
| 1412 | 1448 | if (current_offset != atom.offset) { |
| ... | ... | @@ -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 |
| 1447 | | if (!is_obj) { |
| 1448 | | const Name = struct { |
| 1449 | | index: u32, |
| 1450 | | name: []const u8, |
| 1451 | | |
| 1452 | | fn lessThan(context: void, lhs: @This(), rhs: @This()) bool { |
| 1453 | | _ = context; |
| 1454 | | return lhs.index < rhs.index; |
| 1455 | | } |
| 1456 | | }; |
| 1482 | if (is_obj) { |
| 1483 | try self.emitLinkSection(file, arena); |
| 1484 | } else { |
| 1485 | try self.emitNameSection(file, arena); |
| 1486 | } |
| 1487 | } |
| 1457 | 1488 | |
| 1458 | | var funcs = try std.ArrayList(Name).initCapacity(self.base.allocator, self.functions.items.len + self.imported_functions_count); |
| 1459 | | defer funcs.deinit(); |
| 1460 | | var globals = try std.ArrayList(Name).initCapacity(self.base.allocator, self.wasm_globals.items.len); |
| 1461 | | defer globals.deinit(); |
| 1462 | | var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count()); |
| 1463 | | defer segments.deinit(); |
| 1489 | fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 1490 | const Name = struct { |
| 1491 | index: u32, |
| 1492 | name: []const u8, |
| 1464 | 1493 | |
| 1465 | | for (self.resolved_symbols.keys()) |sym_loc| { |
| 1466 | | const symbol = sym_loc.getSymbol(self).*; |
| 1467 | | switch (symbol.tag) { |
| 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 }); |
| 1494 | fn lessThan(context: void, lhs: @This(), rhs: @This()) bool { |
| 1495 | _ = context; |
| 1496 | return lhs.index < rhs.index; |
| 1476 | 1497 | } |
| 1498 | }; |
| 1477 | 1499 | |
| 1478 | | std.sort.sort(Name, funcs.items, {}, Name.lessThan); |
| 1479 | | std.sort.sort(Name, globals.items, {}, Name.lessThan); |
| 1500 | var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.items.len + self.imported_functions_count); |
| 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); |
| 1482 | | const writer = file.writer(); |
| 1483 | | try leb.writeULEB128(writer, @intCast(u32, "name".len)); |
| 1484 | | try writer.writeAll("name"); |
| 1504 | for (self.resolved_symbols.keys()) |sym_loc| { |
| 1505 | const symbol = sym_loc.getSymbol(self).*; |
| 1506 | switch (symbol.tag) { |
| 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); |
| 1487 | | try self.emitNameSubsection(.global, globals.items, writer); |
| 1488 | | try self.emitNameSubsection(.data_segment, segments.items, writer); |
| 1517 | std.sort.sort(Name, funcs.items, {}, Name.lessThan); |
| 1518 | std.sort.sort(Name, globals.items, {}, Name.lessThan); |
| 1489 | 1519 | |
| 1490 | | try writeCustomSectionHeader( |
| 1491 | | file, |
| 1492 | | header_offset, |
| 1493 | | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 1494 | | ); |
| 1495 | | } |
| 1520 | const header_offset = try reserveCustomSectionHeader(file); |
| 1521 | const writer = file.writer(); |
| 1522 | try leb.writeULEB128(writer, @intCast(u32, "name".len)); |
| 1523 | try writer.writeAll("name"); |
| 1496 | 1524 | |
| 1497 | | if (is_obj) { |
| 1498 | | try self.emitLinkSection(file, arena); |
| 1499 | | } |
| 1525 | try self.emitNameSubsection(.function, funcs.items, writer); |
| 1526 | try self.emitNameSubsection(.global, globals.items, writer); |
| 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 | 1536 | fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { |