Commit a4622501bd
Changed files (4)
src
link
src/link/Wasm/Atom.zig
@@ -23,6 +23,9 @@ alignment: u32,
/// Offset into the section where the atom lives, this already accounts
/// for alignment.
offset: u32,
+/// Represents the index of the file this atom was generated from.
+/// This is 'null' when the atom was generated by a Decl from Zig code.
+file: ?u16,
/// Next atom in relation to this atom.
/// When null, this atom is the last atom
@@ -38,6 +41,7 @@ locals: std.ArrayListUnmanaged(Atom) = .{},
/// Represents a default empty wasm `Atom`
pub const empty: Atom = .{
.alignment = 0,
+ .file = null,
.next = null,
.offset = 0,
.prev = null,
@@ -93,16 +97,17 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
/// Resolves the relocations within the atom, writing the new value
/// at the calculated offset.
pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
- const symbol: Symbol = wasm_bin.managed_symbols.items[self.sym_index];
+ const loc: Wasm.SymbolLoc = .{ .file = self.file, .index = self.sym_index };
+ const symbol = loc.getSymbol(wasm_bin).*;
log.debug("Resolving relocs in atom '{s}' count({d})", .{
symbol.name,
self.relocs.items.len,
});
for (self.relocs.items) |reloc| {
- const value = try relocationValue(reloc, wasm_bin);
+ const value = try self.relocationValue(reloc, wasm_bin);
log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
- wasm_bin.managed_symbols.items[reloc.index].name,
+ (Wasm.SymbolLoc{ .file = self.file, .index = reloc.index }).getSymbol(wasm_bin).name,
symbol.name,
reloc.offset,
value,
@@ -138,8 +143,9 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
/// From a given `relocation` will return the new value to be written.
/// All values will be represented as a `u64` as all values can fit within it.
/// The final value must be casted to the correct size.
-fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
- const symbol: Symbol = wasm_bin.managed_symbols.items[relocation.index];
+fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
+ const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };
+ const symbol = target_loc.getSymbol(wasm_bin).*;
return switch (relocation.relocation_type) {
.R_WASM_FUNCTION_INDEX_LEB => symbol.index,
.R_WASM_TABLE_NUMBER_LEB => symbol.index,
src/link/Wasm/Object.zig
@@ -131,9 +131,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
/// Finds the import within the list of imports from a given kind and index of that kind.
/// Asserts the import exists
-pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) *std.wasm.Import {
+pub fn findImport(self: *const Object, import_kind: std.wasm.ExternalKind, index: u32) std.wasm.Import {
var i: u32 = 0;
- return for (self.imports) |*import| {
+ return for (self.imports) |import| {
if (std.meta.activeTag(import.kind) == import_kind) {
if (i == index) return import;
i += 1;
@@ -681,7 +681,7 @@ fn Parser(comptime ReaderType: type) type {
},
else => {
symbol.index = try leb.readULEB128(u32, reader);
- var maybe_import: ?*std.wasm.Import = null;
+ var maybe_import: ?std.wasm.Import = null;
const is_undefined = symbol.isUndefined();
if (is_undefined) {
@@ -791,10 +791,14 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
.kind = relocatable_data.getSymbolKind(),
.index = @intCast(u32, relocatable_data.index),
}) orelse continue; // encountered a segment we do not create an atom for
- const final_index = try wasm_bin.getMatchingSegment(gpa, object_index, @intCast(u32, index));
+ const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index));
- const atom = try Atom.create(gpa);
- errdefer atom.deinit(gpa);
+ const atom = try gpa.create(Atom);
+ atom.* = Atom.empty;
+ errdefer {
+ atom.deinit(gpa);
+ gpa.destroy(atom);
+ }
try wasm_bin.managed_atoms.append(gpa, atom);
atom.file = object_index;
@@ -803,19 +807,23 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
atom.sym_index = sym_index;
const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{};
- for (relocations) |*relocation| {
+ for (relocations) |relocation| {
if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) {
// set the offset relative to the offset of the segment itself,
// rather than within the entire section.
- relocation.offset -= relocatable_data.offset;
- try atom.relocs.append(gpa, relocation.*);
-
- if (relocation.isTableIndex()) {
- try wasm_bin.elements.appendSymbol(gpa, .{
- .file = object_index,
- .sym_index = relocation.index,
- });
- }
+ var reloc = relocation;
+ reloc.offset -= relocatable_data.offset;
+ try atom.relocs.append(gpa, reloc);
+
+ // TODO: Automatically append the target symbol to the indirect
+ // function table when the relocation is a table index.
+ //
+ // if (relocation.isTableIndex()) {
+ // try wasm_bin.elements.appendSymbol(gpa, .{
+ // .file = object_index,
+ // .sym_index = relocation.index,
+ // });
+ // }
}
}
src/link/Wasm/Symbol.zig
@@ -78,7 +78,7 @@ pub const Flag = enum(u32) {
pub fn requiresImport(self: Symbol) bool {
if (!self.isUndefined()) return false;
if (self.isWeak()) return false;
- if (self.kind == .data) return false;
+ if (self.tag == .data) return false;
// if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib
return true;
src/link/Wasm.zig
@@ -34,6 +34,8 @@ pub const base_tag = link.File.Tag.wasm;
pub const DeclBlock = Atom;
base: link.File,
+/// Output name of the file
+name: []const u8,
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
llvm_object: ?*LlvmObject = null,
/// When importing objects from the host environment, a name must be supplied.
@@ -156,6 +158,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
// TODO: read the file and keep valid parts instead of truncating
const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
wasm_bin.base.file = file;
+ wasm_bin.name = sub_path;
try file.writeAll(&(wasm.magic ++ wasm.version));
@@ -170,7 +173,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
};
const symbol = try wasm_bin.symbols.addOne(allocator);
symbol.* = .{
- .name = "__stack_pointer",
+ .name = try allocator.dupeZ(u8, "__stack_pointer"),
.tag = .global,
.flags = 0,
.index = 0,
@@ -188,6 +191,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
.file = null,
.allocator = gpa,
},
+ .name = undefined,
};
const use_llvm = build_options.have_llvm and options.use_llvm;
const use_stage1 = build_options.is_stage1 and options.use_stage1;
@@ -233,6 +237,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
.file = object_index,
.index = sym_index,
};
+ const sym_name = std.mem.sliceTo(symbol.name, 0);
if (symbol.isLocal()) {
if (symbol.isUndefined()) {
@@ -247,21 +252,23 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
// TODO: locals are allowed to have duplicate symbol names
// TODO: Store undefined symbols so we can verify at the end if they've all been found
// if not, emit an error (unless --allow-undefined is enabled).
- const maybe_existing = try self.globals.getOrPut(self.base.allocator, std.mem.sliceTo(symbol.name, 0));
+ const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name);
if (!maybe_existing.found_existing) {
maybe_existing.value_ptr.* = location;
-
- try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
continue;
}
const existing_loc = maybe_existing.value_ptr.*;
const existing_sym: *Symbol = existing_loc.getSymbol(self);
+ const existing_file_path = if (existing_loc.file) |file| blk: {
+ break :blk self.objects.items[file].name;
+ } else self.name;
+
if (!existing_sym.isUndefined()) {
if (!symbol.isUndefined()) {
log.err("symbol '{s}' defined multiple times", .{existing_sym.name});
- log.err(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});
+ log.err(" first definition in '{s}'", .{existing_file_path});
log.err(" next definition in '{s}'", .{object.name});
return error.SymbolCollision;
}
@@ -271,11 +278,11 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
// simply overwrite with the new symbol
log.info("Overwriting symbol '{s}'", .{symbol.name});
- log.info(" first definition in '{s}'", .{self.objects.items[existing_loc.file.?].name});
- log.info(" next definition in '{s}'", .{object.name});
+ log.info(" old definition in '{s}'", .{existing_file_path});
+ log.info(" new definition in '{s}'", .{object.name});
try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
maybe_existing.value_ptr.* = location;
- try self.globals.putNoClobber(self.base.allocator, std.mem.sliceTo(symbol.name, 0), location);
+ try self.globals.put(self.base.allocator, sym_name, location);
}
}
@@ -302,6 +309,12 @@ pub fn deinit(self: *Wasm) void {
object.deinit(gpa);
}
+ for (self.symbols.items) |symbol| {
+ if (symbol.tag != .dead) {
+ gpa.free(mem.sliceTo(symbol.name, 0));
+ }
+ }
+
self.decls.deinit(gpa);
self.symbols.deinit(gpa);
self.symbols_free_list.deinit(gpa);
@@ -441,7 +454,7 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
const atom: *Atom = &decl.link.wasm;
atom.size = @intCast(u32, code.len);
atom.alignment = decl.ty.abiAlignment(self.base.options.target);
- self.symbols.items[atom.sym_index].name = decl.name;
+ self.symbols.items[atom.sym_index].name = try self.base.allocator.dupeZ(u8, std.mem.sliceTo(decl.name, 0));
try atom.code.appendSlice(self.base.allocator, code);
}
@@ -449,8 +462,10 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
/// and then append it as a 'contained' atom onto the Decl.
pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {
assert(ty.zigTypeTag() != .Fn); // cannot create local symbols for functions
+ const local_index = decl.link.wasm.locals.items.len;
+ const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, local_index });
var symbol: Symbol = .{
- .name = "unnamed_local",
+ .name = name,
.flags = 0,
.tag = .data,
.index = undefined,
@@ -494,7 +509,7 @@ pub fn getDeclVAddr(
const atom = decl.link.wasm.symbolAtom(symbol_index);
const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
if (ty.zigTypeTag() == .Fn) {
- std.debug.assert(addend == 0); // addend not allowed for function relocations
+ assert(addend == 0); // addend not allowed for function relocations
// We found a function pointer, so add it to our table,
// as function pointers are not allowed to be stored inside the data section.
// They are instead stored in a function table which are called by index.
@@ -543,15 +558,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section
for (atom.locals.items) |local_atom| {
self.symbols.items[local_atom.sym_index].tag = .dead; // also for any local symbol
+ // self.base.allocator.free(mem.sliceTo(self.symbols.items[local_atom.sym_index].name, 0));
self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};
}
+ // self.base.allocator.free(mem.sliceTo(self.symbols.items[atom.sym_index].name, 0));
if (decl.isExtern()) {
- const import = self.imports.fetchRemove(.{ .file = null, .index = atom.sym_index }).?.value;
- switch (import.kind) {
- .function => self.imported_functions_count -= 1,
- else => unreachable,
- }
+ assert(self.imports.remove(.{ .file = null, .index = atom.sym_index }));
}
atom.deinit(self.base.allocator);
@@ -577,16 +590,18 @@ fn mapFunctionTable(self: *Wasm) void {
fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
const symbol_index = decl.link.wasm.sym_index;
const symbol: *Symbol = &self.symbols.items[symbol_index];
- symbol.name = decl.name;
+ const decl_name = mem.sliceTo(decl.name, 0);
+ symbol.name = try self.base.allocator.dupeZ(u8, decl_name);
symbol.setUndefined(true);
+ // also add it as a global so it can be resolved
+ try self.globals.put(self.base.allocator, decl_name, .{ .file = null, .index = symbol_index });
switch (decl.ty.zigTypeTag()) {
.Fn => {
const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {
- break :blk std.mem.sliceTo(lib_name, 0);
+ break :blk mem.sliceTo(lib_name, 0);
} else self.host_name;
if (!gop.found_existing) {
- self.imported_functions_count += 1;
gop.value_ptr.* = .{
.module_name = module_name,
.name = std.mem.span(symbol.name),
@@ -608,9 +623,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
const symbol: *Symbol = &self.symbols.items[atom.sym_index];
const final_index: u32 = switch (kind) {
.function => |fn_data| result: {
- const type_index = fn_data.type_index;
const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);
- try self.functions.append(self.base.allocator, .{ .type_index = type_index });
+ try self.functions.append(self.base.allocator, .{ .type_index = fn_data.type_index });
symbol.tag = .function;
symbol.index = index;
@@ -641,6 +655,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
break :blk index;
};
const info_index = @intCast(u32, self.segment_info.items.len);
+ // TODO: Add mutables global decls to .bss section instead
const segment_name = try std.mem.concat(self.base.allocator, u8, &.{
".rodata.",
std.mem.span(symbol.name),
@@ -684,7 +699,7 @@ fn allocateAtoms(self: *Wasm) !void {
offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);
atom.offset = offset;
log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
- self.symbols.items[atom.sym_index].name,
+ (SymbolLoc{ .file = atom.file, .index = atom.sym_index }).getSymbol(self).name,
offset,
offset + atom.size,
atom.size,
@@ -695,7 +710,7 @@ fn allocateAtoms(self: *Wasm) !void {
}
}
-fn setupImports(self: *Wasm) void {
+fn setupImports(self: *Wasm) !void {
for (self.resolved_symbols.items) |symbol_loc| {
if (symbol_loc.file == null) {
// imports generated by Zig code are already in the `import` section
@@ -708,7 +723,7 @@ fn setupImports(self: *Wasm) void {
}
log.debug("Symbol '{s}' will be imported from the host", .{symbol.name});
- const import = self.objects.items[symbol_loc.file.?].findImport(symbol.externalType(), symbol.index);
+ const import = self.objects.items[symbol_loc.file.?].findImport(symbol.tag.externalType(), symbol.index);
// TODO: De-duplicate imports
try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);
}
@@ -737,6 +752,9 @@ fn setupImports(self: *Wasm) void {
else => unreachable,
}
}
+ self.imported_functions_count = function_index;
+ self.imported_globals_count = global_index;
+ self.imported_tables_count = table_index;
}
/// Takes the global, function and table section from each linked object file
@@ -761,12 +779,13 @@ fn mergeSections(self: *Wasm) !void {
const object = self.objects.items[sym_loc.file.?];
const symbol = &object.symtable[sym_loc.index];
- if (symbol.isUndefined()) {
+ if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) {
// Skip undefined symbols as they go in the `import` section
+ // Also skip symbols that do not need to have a section merged.
continue;
}
- const offset = object.importedCountByKind(symbol.externalType());
+ const offset = object.importedCountByKind(symbol.tag.externalType());
const index = symbol.index - offset;
switch (symbol.tag) {
.function => {
@@ -776,7 +795,7 @@ fn mergeSections(self: *Wasm) !void {
},
.global => {
const original_global = object.globals[index];
- symbol.index = @intCast(u32, self.globals.items.len) + self.imported_globals_count;
+ symbol.index = @intCast(u32, self.wasm_globals.items.len) + self.imported_globals_count;
try self.wasm_globals.append(self.base.allocator, original_global);
},
.table => {
@@ -790,7 +809,7 @@ fn mergeSections(self: *Wasm) !void {
log.debug("Merged ({d}) functions", .{self.functions.items.len});
log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len});
- log.debug("Merged ({d}) tables", .{self.tables.tems.len});
+ log.debug("Merged ({d}) tables", .{self.tables.items.len});
}
/// Merges function types of all object files into the final
@@ -811,13 +830,13 @@ fn mergeTypes(self: *Wasm) !void {
if (symbol.isUndefined()) {
log.debug("Adding type from extern function '{s}'", .{symbol.name});
- const import: *wasm.Import = self.imports.getPtr(sym_loc);
- const original_type = object.types[import.kind.function];
+ const import: *wasm.Import = self.imports.getPtr(sym_loc).?;
+ const original_type = object.func_types[import.kind.function];
import.kind.function = try self.putOrGetFuncType(original_type);
} else {
log.debug("Adding type from function '{s}'", .{symbol.name});
const func = &self.functions.items[symbol.index - self.imported_functions_count];
- func.type_index = try self.putOrGetFuncType(object.types[func.type_index]);
+ func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]);
}
}
log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len});
@@ -835,7 +854,11 @@ fn setupExports(self: *Wasm) !void {
const symbol = sym_loc.getSymbol(self);
if (!symbol.isExported()) continue;
- const exp: wasm.Export = .{ .name = symbol.name, .kind = symbol.externalType(), .index = symbol.index };
+ const exp: wasm.Export = .{
+ .name = mem.sliceTo(symbol.name, 0),
+ .kind = symbol.tag.externalType(),
+ .index = symbol.index,
+ };
log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index });
try self.exports.append(self.base.allocator, exp);
}
@@ -948,6 +971,41 @@ fn setupMemory(self: *Wasm) !void {
}
}
+/// From a given object's index and the index of the segment, returns the corresponding
+/// index of the segment within the final data section. When the segment does not yet
+/// exist, a new one will be initialized and appended. The new index will be returned in that case.
+pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !u32 {
+ const object: Object = self.objects.items[object_index];
+ const relocatable_data = object.relocatable_data[relocatable_index];
+ const index = @intCast(u32, self.segments.items.len);
+
+ switch (relocatable_data.type) {
+ .data => {
+ const segment_info = object.segment_info[relocatable_data.index];
+ const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName());
+ if (!result.found_existing) {
+ result.value_ptr.* = index;
+ try self.segments.append(self.base.allocator, .{
+ .alignment = 1,
+ .size = 0,
+ .offset = 0,
+ });
+ return index;
+ } else return result.value_ptr.*;
+ },
+ .code => return self.code_section_index orelse blk: {
+ self.code_section_index = index;
+ try self.segments.append(self.base.allocator, .{
+ .alignment = 1,
+ .size = 0,
+ .offset = 0,
+ });
+ break :blk index;
+ },
+ .custom => return error.@"TODO: Custom section relocations for wasm",
+ }
+}
+
fn resetState(self: *Wasm) void {
for (self.segment_info.items) |*segment_info| {
self.base.allocator.free(segment_info.name);
@@ -1015,7 +1073,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// When we finish/error we reset the state of the linker
// So we can rebuild the binary file on each incremental update
defer self.resetState();
- self.setupImports();
+ try self.setupImports();
var decl_it = self.decls.keyIterator();
while (decl_it.next()) |decl| {
if (decl.*.isExtern()) continue;
@@ -1050,7 +1108,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
{
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();
-
+ log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len});
for (self.func_types.items) |func_type| {
try leb.writeULEB128(writer, wasm.function_type);
try leb.writeULEB128(writer, @intCast(u32, func_type.params.len));
@@ -1095,8 +1153,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
var it = self.imports.iterator();
while (it.next()) |entry| {
- const import_symbol = self.symbols.items[entry.key_ptr.*];
- std.debug.assert(import_symbol.isUndefined());
+ assert(entry.key_ptr.*.getSymbol(self).isUndefined());
const import = entry.value_ptr.*;
try emitImport(writer, import);
}
@@ -1207,7 +1264,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
.Fn => {
const target = exprt.exported_decl.link.wasm.sym_index;
const target_symbol = self.symbols.items[target];
- std.debug.assert(target_symbol.tag == .function);
+ assert(target_symbol.tag == .function);
// Type of the export
try writer.writeByte(wasm.externalKind(.function));
// Exported function index
@@ -1323,8 +1380,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
try writer.writeByteNTimes(0, diff);
current_offset += diff;
}
- std.debug.assert(current_offset == atom.offset);
- std.debug.assert(atom.code.items.len == atom.size);
+ assert(current_offset == atom.offset);
+ assert(atom.code.items.len == atom.size);
try writer.writeAll(atom.code.items);
current_offset += atom.size;
@@ -1335,10 +1392,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
// segments are aligned.
if (current_offset != segment.size) {
try writer.writeByteNTimes(0, segment.size - current_offset);
+ current_offset += segment.size - current_offset;
}
break;
}
}
+ assert(current_offset == segment.size);
}
try writeVecSectionHeader(
@@ -1371,8 +1430,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
for (self.symbols.items) |symbol| {
switch (symbol.tag) {
- .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),
- .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),
+ .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
+ .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
else => {},
}
}