| author | |
| committer | |
| log | 8856ba75059f74a326d1f8d3af40a30c5a3ac1ed |
| tree | 35ceb0bd2015c4bf89dda04c8ffc2bc49633c3bf |
| parent | c986c6c90a5746cb671177e486a77bd78a5946b0 |
| signature | Commit is signed but in an unrecognized format. |
Rather than parsing every symbol into an atom, we now only parse them
into an atom when such atom is marked. This means garbage-collected
symbols will also not be parsed into atoms, and neither are discarded
symbols which have been resolved by other symbols. (Such as multiple
weak symbols).
This also introduces a binary search for finding the start index into
the list of relocations. This speeds up finding the corresponding
relocations tremendously as they're ordered ascended by address.
Lastly, we re-use the memory of atom's data as well as relocations
instead of duplicating it. This means we half the memory usage of
atom's data and relocations for linked object files. As we are
aware of decls and synthetic atoms, we free the memory of those
atoms indepedently of the atoms of object files to prevent double-frees.3 files changed, 208 insertions(+), 200 deletions(-)
src/link/Wasm.zig+75-51| ... | @@ -1309,6 +1309,31 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -1309,6 +1309,31 @@ pub fn deinit(wasm: *Wasm) void { |
| 1309 | archive.deinit(gpa); | 1309 | archive.deinit(gpa); |
| 1310 | } | 1310 | } |
| 1311 | 1311 | ||
| 1312 | // For decls and anon decls we free the memory of its atoms. | ||
| 1313 | // The memory of atoms parsed from object files is managed by | ||
| 1314 | // the object file itself, and therefore we can skip those. | ||
| 1315 | { | ||
| 1316 | var it = wasm.decls.valueIterator(); | ||
| 1317 | while (it.next()) |atom_index_ptr| { | ||
| 1318 | const atom = wasm.getAtomPtr(atom_index_ptr.*); | ||
| 1319 | for (atom.locals.items) |local_index| { | ||
| 1320 | const local_atom = wasm.getAtomPtr(local_index); | ||
| 1321 | local_atom.deinit(gpa); | ||
| 1322 | } | ||
| 1323 | atom.deinit(gpa); | ||
| 1324 | } | ||
| 1325 | } | ||
| 1326 | { | ||
| 1327 | for (wasm.anon_decls.values()) |atom_index| { | ||
| 1328 | const atom = wasm.getAtomPtr(atom_index); | ||
| 1329 | for (atom.locals.items) |local_index| { | ||
| 1330 | const local_atom = wasm.getAtomPtr(local_index); | ||
| 1331 | local_atom.deinit(gpa); | ||
| 1332 | } | ||
| 1333 | atom.deinit(gpa); | ||
| 1334 | } | ||
| 1335 | } | ||
| 1336 | |||
| 1312 | wasm.decls.deinit(gpa); | 1337 | wasm.decls.deinit(gpa); |
| 1313 | wasm.anon_decls.deinit(gpa); | 1338 | wasm.anon_decls.deinit(gpa); |
| 1314 | wasm.atom_types.deinit(gpa); | 1339 | wasm.atom_types.deinit(gpa); |
| ... | @@ -1321,9 +1346,6 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -1321,9 +1346,6 @@ pub fn deinit(wasm: *Wasm) void { |
| 1321 | wasm.symbol_atom.deinit(gpa); | 1346 | wasm.symbol_atom.deinit(gpa); |
| 1322 | wasm.export_names.deinit(gpa); | 1347 | wasm.export_names.deinit(gpa); |
| 1323 | wasm.atoms.deinit(gpa); | 1348 | wasm.atoms.deinit(gpa); |
| 1324 | for (wasm.managed_atoms.items) |*managed_atom| { | ||
| 1325 | managed_atom.deinit(wasm); | ||
| 1326 | } | ||
| 1327 | wasm.managed_atoms.deinit(gpa); | 1349 | wasm.managed_atoms.deinit(gpa); |
| 1328 | wasm.segments.deinit(gpa); | 1350 | wasm.segments.deinit(gpa); |
| 1329 | wasm.data_segments.deinit(gpa); | 1351 | wasm.data_segments.deinit(gpa); |
| ... | @@ -1342,6 +1364,10 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -1342,6 +1364,10 @@ pub fn deinit(wasm: *Wasm) void { |
| 1342 | wasm.exports.deinit(gpa); | 1364 | wasm.exports.deinit(gpa); |
| 1343 | 1365 | ||
| 1344 | wasm.string_table.deinit(gpa); | 1366 | wasm.string_table.deinit(gpa); |
| 1367 | for (wasm.synthetic_functions.items) |atom_index| { | ||
| 1368 | const atom = wasm.getAtomPtr(atom_index); | ||
| 1369 | atom.deinit(gpa); | ||
| 1370 | } | ||
| 1345 | wasm.synthetic_functions.deinit(gpa); | 1371 | wasm.synthetic_functions.deinit(gpa); |
| 1346 | 1372 | ||
| 1347 | if (wasm.dwarf) |*dwarf| { | 1373 | if (wasm.dwarf) |*dwarf| { |
| ... | @@ -2406,7 +2432,7 @@ fn setupErrorsLen(wasm: *Wasm) !void { | ... | @@ -2406,7 +2432,7 @@ fn setupErrorsLen(wasm: *Wasm) !void { |
| 2406 | prev_atom.next = atom.next; | 2432 | prev_atom.next = atom.next; |
| 2407 | atom.prev = null; | 2433 | atom.prev = null; |
| 2408 | } | 2434 | } |
| 2409 | atom.deinit(wasm); | 2435 | atom.deinit(wasm.base.allocator); |
| 2410 | break :blk index; | 2436 | break :blk index; |
| 2411 | } else new_atom: { | 2437 | } else new_atom: { |
| 2412 | const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); | 2438 | const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); |
| ... | @@ -2509,6 +2535,7 @@ fn createSyntheticFunction( | ... | @@ -2509,6 +2535,7 @@ fn createSyntheticFunction( |
| 2509 | .next = null, | 2535 | .next = null, |
| 2510 | .prev = null, | 2536 | .prev = null, |
| 2511 | .code = function_body.moveToUnmanaged(), | 2537 | .code = function_body.moveToUnmanaged(), |
| 2538 | .original_offset = 0, | ||
| 2512 | }; | 2539 | }; |
| 2513 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); | 2540 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); |
| 2514 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); | 2541 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); |
| ... | @@ -2545,6 +2572,7 @@ pub fn createFunction( | ... | @@ -2545,6 +2572,7 @@ pub fn createFunction( |
| 2545 | .prev = null, | 2572 | .prev = null, |
| 2546 | .code = function_body.moveToUnmanaged(), | 2573 | .code = function_body.moveToUnmanaged(), |
| 2547 | .relocs = relocations.moveToUnmanaged(), | 2574 | .relocs = relocations.moveToUnmanaged(), |
| 2575 | .original_offset = 0, | ||
| 2548 | }; | 2576 | }; |
| 2549 | const symbol = loc.getSymbol(wasm); | 2577 | const symbol = loc.getSymbol(wasm); |
| 2550 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported | 2578 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported |
| ... | @@ -3016,14 +3044,14 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -3016,14 +3044,14 @@ fn setupMemory(wasm: *Wasm) !void { |
| 3016 | /// From a given object's index and the index of the segment, returns the corresponding | 3044 | /// From a given object's index and the index of the segment, returns the corresponding |
| 3017 | /// index of the segment within the final data section. When the segment does not yet | 3045 | /// index of the segment within the final data section. When the segment does not yet |
| 3018 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. | 3046 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. |
| 3019 | pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32) !?u32 { | 3047 | pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 { |
| 3020 | const object: Object = wasm.objects.items[object_index]; | 3048 | const object: Object = wasm.objects.items[object_index]; |
| 3021 | const relocatable_data = object.relocatable_data[relocatable_index]; | 3049 | const symbol = object.symtable[symbol_index]; |
| 3022 | const index = @as(u32, @intCast(wasm.segments.items.len)); | 3050 | const index = @as(u32, @intCast(wasm.segments.items.len)); |
| 3023 | 3051 | ||
| 3024 | switch (relocatable_data.type) { | 3052 | switch (symbol.tag) { |
| 3025 | .data => { | 3053 | .data => { |
| 3026 | const segment_info = object.segment_info[relocatable_data.index]; | 3054 | const segment_info = object.segment_info[symbol.index]; |
| 3027 | const merge_segment = wasm.base.options.output_mode != .Obj; | 3055 | const merge_segment = wasm.base.options.output_mode != .Obj; |
| 3028 | const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); | 3056 | const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); |
| 3029 | if (!result.found_existing) { | 3057 | if (!result.found_existing) { |
| ... | @@ -3041,67 +3069,67 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32 | ... | @@ -3041,67 +3069,67 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32 |
| 3041 | return index; | 3069 | return index; |
| 3042 | } else return result.value_ptr.*; | 3070 | } else return result.value_ptr.*; |
| 3043 | }, | 3071 | }, |
| 3044 | .code => return wasm.code_section_index orelse blk: { | 3072 | .function => return wasm.code_section_index orelse blk: { |
| 3045 | wasm.code_section_index = index; | 3073 | wasm.code_section_index = index; |
| 3046 | try wasm.appendDummySegment(); | 3074 | try wasm.appendDummySegment(); |
| 3047 | break :blk index; | 3075 | break :blk index; |
| 3048 | }, | 3076 | }, |
| 3049 | .debug => { | 3077 | .section => { |
| 3050 | const debug_name = object.getDebugName(relocatable_data); | 3078 | const section_name = object.string_table.get(symbol.name); |
| 3051 | if (mem.eql(u8, debug_name, ".debug_info")) { | 3079 | if (mem.eql(u8, section_name, ".debug_info")) { |
| 3052 | return wasm.debug_info_index orelse blk: { | 3080 | return wasm.debug_info_index orelse blk: { |
| 3053 | wasm.debug_info_index = index; | 3081 | wasm.debug_info_index = index; |
| 3054 | try wasm.appendDummySegment(); | 3082 | try wasm.appendDummySegment(); |
| 3055 | break :blk index; | 3083 | break :blk index; |
| 3056 | }; | 3084 | }; |
| 3057 | } else if (mem.eql(u8, debug_name, ".debug_line")) { | 3085 | } else if (mem.eql(u8, section_name, ".debug_line")) { |
| 3058 | return wasm.debug_line_index orelse blk: { | 3086 | return wasm.debug_line_index orelse blk: { |
| 3059 | wasm.debug_line_index = index; | 3087 | wasm.debug_line_index = index; |
| 3060 | try wasm.appendDummySegment(); | 3088 | try wasm.appendDummySegment(); |
| 3061 | break :blk index; | 3089 | break :blk index; |
| 3062 | }; | 3090 | }; |
| 3063 | } else if (mem.eql(u8, debug_name, ".debug_loc")) { | 3091 | } else if (mem.eql(u8, section_name, ".debug_loc")) { |
| 3064 | return wasm.debug_loc_index orelse blk: { | 3092 | return wasm.debug_loc_index orelse blk: { |
| 3065 | wasm.debug_loc_index = index; | 3093 | wasm.debug_loc_index = index; |
| 3066 | try wasm.appendDummySegment(); | 3094 | try wasm.appendDummySegment(); |
| 3067 | break :blk index; | 3095 | break :blk index; |
| 3068 | }; | 3096 | }; |
| 3069 | } else if (mem.eql(u8, debug_name, ".debug_ranges")) { | 3097 | } else if (mem.eql(u8, section_name, ".debug_ranges")) { |
| 3070 | return wasm.debug_line_index orelse blk: { | 3098 | return wasm.debug_line_index orelse blk: { |
| 3071 | wasm.debug_ranges_index = index; | 3099 | wasm.debug_ranges_index = index; |
| 3072 | try wasm.appendDummySegment(); | 3100 | try wasm.appendDummySegment(); |
| 3073 | break :blk index; | 3101 | break :blk index; |
| 3074 | }; | 3102 | }; |
| 3075 | } else if (mem.eql(u8, debug_name, ".debug_pubnames")) { | 3103 | } else if (mem.eql(u8, section_name, ".debug_pubnames")) { |
| 3076 | return wasm.debug_pubnames_index orelse blk: { | 3104 | return wasm.debug_pubnames_index orelse blk: { |
| 3077 | wasm.debug_pubnames_index = index; | 3105 | wasm.debug_pubnames_index = index; |
| 3078 | try wasm.appendDummySegment(); | 3106 | try wasm.appendDummySegment(); |
| 3079 | break :blk index; | 3107 | break :blk index; |
| 3080 | }; | 3108 | }; |
| 3081 | } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) { | 3109 | } else if (mem.eql(u8, section_name, ".debug_pubtypes")) { |
| 3082 | return wasm.debug_pubtypes_index orelse blk: { | 3110 | return wasm.debug_pubtypes_index orelse blk: { |
| 3083 | wasm.debug_pubtypes_index = index; | 3111 | wasm.debug_pubtypes_index = index; |
| 3084 | try wasm.appendDummySegment(); | 3112 | try wasm.appendDummySegment(); |
| 3085 | break :blk index; | 3113 | break :blk index; |
| 3086 | }; | 3114 | }; |
| 3087 | } else if (mem.eql(u8, debug_name, ".debug_abbrev")) { | 3115 | } else if (mem.eql(u8, section_name, ".debug_abbrev")) { |
| 3088 | return wasm.debug_abbrev_index orelse blk: { | 3116 | return wasm.debug_abbrev_index orelse blk: { |
| 3089 | wasm.debug_abbrev_index = index; | 3117 | wasm.debug_abbrev_index = index; |
| 3090 | try wasm.appendDummySegment(); | 3118 | try wasm.appendDummySegment(); |
| 3091 | break :blk index; | 3119 | break :blk index; |
| 3092 | }; | 3120 | }; |
| 3093 | } else if (mem.eql(u8, debug_name, ".debug_str")) { | 3121 | } else if (mem.eql(u8, section_name, ".debug_str")) { |
| 3094 | return wasm.debug_str_index orelse blk: { | 3122 | return wasm.debug_str_index orelse blk: { |
| 3095 | wasm.debug_str_index = index; | 3123 | wasm.debug_str_index = index; |
| 3096 | try wasm.appendDummySegment(); | 3124 | try wasm.appendDummySegment(); |
| 3097 | break :blk index; | 3125 | break :blk index; |
| 3098 | }; | 3126 | }; |
| 3099 | } else { | 3127 | } else { |
| 3100 | log.warn("found unknown debug section '{s}'", .{debug_name}); | 3128 | log.warn("found unknown section '{s}'", .{section_name}); |
| 3101 | log.warn(" debug section will be skipped", .{}); | 3129 | return error.UnexpectedValue; |
| 3102 | return null; | ||
| 3103 | } | 3130 | } |
| 3104 | }, | 3131 | }, |
| 3132 | else => unreachable, | ||
| 3105 | } | 3133 | } |
| 3106 | } | 3134 | } |
| 3107 | 3135 | ||
| ... | @@ -3468,11 +3496,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -3468,11 +3496,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 3468 | try wasm.setupInitFunctions(); | 3496 | try wasm.setupInitFunctions(); |
| 3469 | try wasm.setupStart(); | 3497 | try wasm.setupStart(); |
| 3470 | 3498 | ||
| 3471 | for (wasm.objects.items, 0..) |*object, object_index| { | 3499 | try wasm.markReferences(); |
| 3472 | try object.parseIntoAtoms(gpa, @as(u16, @intCast(object_index)), wasm); | ||
| 3473 | } | ||
| 3474 | |||
| 3475 | wasm.markReferences(); | ||
| 3476 | try wasm.setupImports(); | 3500 | try wasm.setupImports(); |
| 3477 | try wasm.allocateAtoms(); | 3501 | try wasm.allocateAtoms(); |
| 3478 | try wasm.setupMemory(); | 3502 | try wasm.setupMemory(); |
| ... | @@ -3558,7 +3582,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -3558,7 +3582,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 3558 | try wasm.setupInitFunctions(); | 3582 | try wasm.setupInitFunctions(); |
| 3559 | try wasm.setupErrorsLen(); | 3583 | try wasm.setupErrorsLen(); |
| 3560 | try wasm.setupStart(); | 3584 | try wasm.setupStart(); |
| 3561 | wasm.markReferences(); | 3585 | try wasm.markReferences(); |
| 3562 | try wasm.setupImports(); | 3586 | try wasm.setupImports(); |
| 3563 | if (wasm.base.options.module) |mod| { | 3587 | if (wasm.base.options.module) |mod| { |
| 3564 | var decl_it = wasm.decls.iterator(); | 3588 | var decl_it = wasm.decls.iterator(); |
| ... | @@ -3615,10 +3639,6 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -3615,10 +3639,6 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 3615 | } | 3639 | } |
| 3616 | } | 3640 | } |
| 3617 | 3641 | ||
| 3618 | for (wasm.objects.items, 0..) |*object, object_index| { | ||
| 3619 | try object.parseIntoAtoms(wasm.base.allocator, @as(u16, @intCast(object_index)), wasm); | ||
| 3620 | } | ||
| 3621 | |||
| 3622 | try wasm.allocateAtoms(); | 3642 | try wasm.allocateAtoms(); |
| 3623 | try wasm.setupMemory(); | 3643 | try wasm.setupMemory(); |
| 3624 | wasm.allocateVirtualAddresses(); | 3644 | wasm.allocateVirtualAddresses(); |
| ... | @@ -3885,18 +3905,15 @@ fn writeToFile( | ... | @@ -3885,18 +3905,15 @@ fn writeToFile( |
| 3885 | var atom_index = wasm.atoms.get(code_index).?; | 3905 | var atom_index = wasm.atoms.get(code_index).?; |
| 3886 | 3906 | ||
| 3887 | // The code section must be sorted in line with the function order. | 3907 | // The code section must be sorted in line with the function order. |
| 3888 | var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); | 3908 | var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); |
| 3889 | defer sorted_atoms.deinit(); | 3909 | defer sorted_atoms.deinit(); |
| 3890 | 3910 | ||
| 3891 | while (true) { | 3911 | while (true) { |
| 3892 | var atom = wasm.getAtomPtr(atom_index); | 3912 | const atom = wasm.getAtomPtr(atom_index); |
| 3893 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { | 3913 | if (!is_obj) { |
| 3894 | if (!is_obj) { | 3914 | atom.resolveRelocs(wasm); |
| 3895 | atom.resolveRelocs(wasm); | ||
| 3896 | } | ||
| 3897 | sorted_atoms.appendAssumeCapacity(atom); | ||
| 3898 | } | 3915 | } |
| 3899 | // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; | 3916 | sorted_atoms.appendAssumeCapacity(atom); // found more code atoms than functions |
| 3900 | atom_index = atom.prev orelse break; | 3917 | atom_index = atom.prev orelse break; |
| 3901 | } | 3918 | } |
| 3902 | 3919 | ||
| ... | @@ -3908,7 +3925,7 @@ fn writeToFile( | ... | @@ -3908,7 +3925,7 @@ fn writeToFile( |
| 3908 | } | 3925 | } |
| 3909 | }.sort; | 3926 | }.sort; |
| 3910 | 3927 | ||
| 3911 | mem.sort(*Atom, sorted_atoms.items, wasm, atom_sort_fn); | 3928 | mem.sort(*const Atom, sorted_atoms.items, wasm, atom_sort_fn); |
| 3912 | 3929 | ||
| 3913 | for (sorted_atoms.items) |sorted_atom| { | 3930 | for (sorted_atoms.items) |sorted_atom| { |
| 3914 | try leb.writeULEB128(binary_writer, sorted_atom.size); | 3931 | try leb.writeULEB128(binary_writer, sorted_atom.size); |
| ... | @@ -5060,20 +5077,20 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s | ... | @@ -5060,20 +5077,20 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s |
| 5060 | 5077 | ||
| 5061 | /// Verifies all resolved symbols and checks whether itself needs to be marked alive, | 5078 | /// Verifies all resolved symbols and checks whether itself needs to be marked alive, |
| 5062 | /// as well as any of its references. | 5079 | /// as well as any of its references. |
| 5063 | fn markReferences(wasm: *Wasm) void { | 5080 | fn markReferences(wasm: *Wasm) !void { |
| 5064 | const tracy = trace(@src()); | 5081 | const tracy = trace(@src()); |
| 5065 | defer tracy.end(); | 5082 | defer tracy.end(); |
| 5066 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 5083 | for (wasm.resolved_symbols.keys()) |sym_loc| { |
| 5067 | const sym = sym_loc.getSymbol(wasm); | 5084 | const sym = sym_loc.getSymbol(wasm); |
| 5068 | if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip()) { | 5085 | if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip()) { |
| 5069 | wasm.mark(sym_loc); | 5086 | try wasm.mark(sym_loc); |
| 5070 | } | 5087 | } |
| 5071 | } | 5088 | } |
| 5072 | } | 5089 | } |
| 5073 | 5090 | ||
| 5074 | /// Marks a symbol as 'alive' recursively so itself and any references it contains to | 5091 | /// Marks a symbol as 'alive' recursively so itself and any references it contains to |
| 5075 | /// other symbols will not be omit from the binary. | 5092 | /// other symbols will not be omit from the binary. |
| 5076 | fn mark(wasm: *Wasm, loc: SymbolLoc) void { | 5093 | fn mark(wasm: *Wasm, loc: SymbolLoc) !void { |
| 5077 | const symbol = loc.getSymbol(wasm); | 5094 | const symbol = loc.getSymbol(wasm); |
| 5078 | if (symbol.isAlive()) { | 5095 | if (symbol.isAlive()) { |
| 5079 | // Symbol is already marked alive, including its references. | 5096 | // Symbol is already marked alive, including its references. |
| ... | @@ -5082,13 +5099,20 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) void { | ... | @@ -5082,13 +5099,20 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) void { |
| 5082 | return; | 5099 | return; |
| 5083 | } | 5100 | } |
| 5084 | symbol.mark(); | 5101 | symbol.mark(); |
| 5102 | if (symbol.isUndefined()) { | ||
| 5103 | // undefined symbols do not have an associated `Atom` and therefore also | ||
| 5104 | // do not contain relocations. | ||
| 5105 | return; | ||
| 5106 | } | ||
| 5085 | 5107 | ||
| 5086 | if (wasm.symbol_atom.get(loc)) |atom_index| { | 5108 | const file = loc.file orelse return; // Marking synthetic and Zig symbols is done seperately |
| 5087 | const atom = wasm.getAtom(atom_index); | 5109 | const object = &wasm.objects.items[file]; |
| 5088 | const relocations: []const types.Relocation = atom.relocs.items; | 5110 | const atom_index = try Object.parseSymbolIntoAtom(object, file, loc.index, wasm); |
| 5089 | for (relocations) |reloc| { | 5111 | |
| 5090 | const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file }; | 5112 | const atom = wasm.getAtom(atom_index); |
| 5091 | wasm.mark(target_loc.finalLoc(wasm)); | 5113 | const relocations: []const types.Relocation = atom.relocs.items; |
| 5092 | } | 5114 | for (relocations) |reloc| { |
| 5115 | const target_loc: SymbolLoc = .{ .index = reloc.index, .file = file }; | ||
| 5116 | try wasm.mark(target_loc.finalLoc(wasm)); | ||
| 5093 | } | 5117 | } |
| 5094 | } | 5118 | } |
src/link/Wasm/Atom.zig+11-7| ... | @@ -23,6 +23,10 @@ alignment: Wasm.Alignment, | ... | @@ -23,6 +23,10 @@ alignment: Wasm.Alignment, |
| 23 | /// Offset into the section where the atom lives, this already accounts | 23 | /// Offset into the section where the atom lives, this already accounts |
| 24 | /// for alignment. | 24 | /// for alignment. |
| 25 | offset: u32, | 25 | offset: u32, |
| 26 | /// The original offset within the object file. This value is substracted from | ||
| 27 | /// relocation offsets to determine where in the `data` to rewrite the value | ||
| 28 | original_offset: u32, | ||
| 29 | |||
| 26 | /// Represents the index of the file this atom was generated from. | 30 | /// Represents the index of the file this atom was generated from. |
| 27 | /// This is 'null' when the atom was generated by a Decl from Zig code. | 31 | /// This is 'null' when the atom was generated by a Decl from Zig code. |
| 28 | file: ?u16, | 32 | file: ?u16, |
| ... | @@ -50,11 +54,11 @@ pub const empty: Atom = .{ | ... | @@ -50,11 +54,11 @@ pub const empty: Atom = .{ |
| 50 | .prev = null, | 54 | .prev = null, |
| 51 | .size = 0, | 55 | .size = 0, |
| 52 | .sym_index = 0, | 56 | .sym_index = 0, |
| 57 | .original_offset = 0, | ||
| 53 | }; | 58 | }; |
| 54 | 59 | ||
| 55 | /// Frees all resources owned by this `Atom`. | 60 | /// Frees all resources owned by this `Atom`. |
| 56 | pub fn deinit(atom: *Atom, wasm: *Wasm) void { | 61 | pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void { |
| 57 | const gpa = wasm.base.allocator; | ||
| 58 | atom.relocs.deinit(gpa); | 62 | atom.relocs.deinit(gpa); |
| 59 | atom.code.deinit(gpa); | 63 | atom.code.deinit(gpa); |
| 60 | atom.locals.deinit(gpa); | 64 | atom.locals.deinit(gpa); |
| ... | @@ -114,10 +118,10 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { | ... | @@ -114,10 +118,10 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { |
| 114 | .R_WASM_GLOBAL_INDEX_I32, | 118 | .R_WASM_GLOBAL_INDEX_I32, |
| 115 | .R_WASM_MEMORY_ADDR_I32, | 119 | .R_WASM_MEMORY_ADDR_I32, |
| 116 | .R_WASM_SECTION_OFFSET_I32, | 120 | .R_WASM_SECTION_OFFSET_I32, |
| 117 | => std.mem.writeInt(u32, atom.code.items[reloc.offset..][0..4], @as(u32, @intCast(value)), .little), | 121 | => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @intCast(value)), .little), |
| 118 | .R_WASM_TABLE_INDEX_I64, | 122 | .R_WASM_TABLE_INDEX_I64, |
| 119 | .R_WASM_MEMORY_ADDR_I64, | 123 | .R_WASM_MEMORY_ADDR_I64, |
| 120 | => std.mem.writeInt(u64, atom.code.items[reloc.offset..][0..8], value, .little), | 124 | => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little), |
| 121 | .R_WASM_GLOBAL_INDEX_LEB, | 125 | .R_WASM_GLOBAL_INDEX_LEB, |
| 122 | .R_WASM_EVENT_INDEX_LEB, | 126 | .R_WASM_EVENT_INDEX_LEB, |
| 123 | .R_WASM_FUNCTION_INDEX_LEB, | 127 | .R_WASM_FUNCTION_INDEX_LEB, |
| ... | @@ -127,12 +131,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { | ... | @@ -127,12 +131,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { |
| 127 | .R_WASM_TABLE_NUMBER_LEB, | 131 | .R_WASM_TABLE_NUMBER_LEB, |
| 128 | .R_WASM_TYPE_INDEX_LEB, | 132 | .R_WASM_TYPE_INDEX_LEB, |
| 129 | .R_WASM_MEMORY_ADDR_TLS_SLEB, | 133 | .R_WASM_MEMORY_ADDR_TLS_SLEB, |
| 130 | => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @as(u32, @intCast(value))), | 134 | => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @intCast(value))), |
| 131 | .R_WASM_MEMORY_ADDR_LEB64, | 135 | .R_WASM_MEMORY_ADDR_LEB64, |
| 132 | .R_WASM_MEMORY_ADDR_SLEB64, | 136 | .R_WASM_MEMORY_ADDR_SLEB64, |
| 133 | .R_WASM_TABLE_INDEX_SLEB64, | 137 | .R_WASM_TABLE_INDEX_SLEB64, |
| 134 | .R_WASM_MEMORY_ADDR_TLS_SLEB64, | 138 | .R_WASM_MEMORY_ADDR_TLS_SLEB64, |
| 135 | => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value), | 139 | => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value), |
| 136 | } | 140 | } |
| 137 | } | 141 | } |
| 138 | } | 142 | } |
| ... | @@ -150,7 +154,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa | ... | @@ -150,7 +154,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa |
| 150 | .R_WASM_TABLE_INDEX_I64, | 154 | .R_WASM_TABLE_INDEX_I64, |
| 151 | .R_WASM_TABLE_INDEX_SLEB, | 155 | .R_WASM_TABLE_INDEX_SLEB, |
| 152 | .R_WASM_TABLE_INDEX_SLEB64, | 156 | .R_WASM_TABLE_INDEX_SLEB64, |
| 153 | => return wasm_bin.function_table.get(target_loc) orelse 0, | 157 | => return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0, |
| 154 | .R_WASM_TYPE_INDEX_LEB => { | 158 | .R_WASM_TYPE_INDEX_LEB => { |
| 155 | const file_index = atom.file orelse { | 159 | const file_index = atom.file orelse { |
| 156 | return relocation.index; | 160 | return relocation.index; |
src/link/Wasm/Object.zig+122-142| ... | @@ -59,20 +59,16 @@ init_funcs: []const types.InitFunc = &.{}, | ... | @@ -59,20 +59,16 @@ init_funcs: []const types.InitFunc = &.{}, |
| 59 | comdat_info: []const types.Comdat = &.{}, | 59 | comdat_info: []const types.Comdat = &.{}, |
| 60 | /// Represents non-synthetic sections that can essentially be mem-cpy'd into place | 60 | /// Represents non-synthetic sections that can essentially be mem-cpy'd into place |
| 61 | /// after performing relocations. | 61 | /// after performing relocations. |
| 62 | relocatable_data: []const RelocatableData = &.{}, | 62 | relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .{}, |
| 63 | /// String table for all strings required by the object file, such as symbol names, | 63 | /// String table for all strings required by the object file, such as symbol names, |
| 64 | /// import name, module name and export names. Each string will be deduplicated | 64 | /// import name, module name and export names. Each string will be deduplicated |
| 65 | /// and returns an offset into the table. | 65 | /// and returns an offset into the table. |
| 66 | string_table: Wasm.StringTable = .{}, | 66 | string_table: Wasm.StringTable = .{}, |
| 67 | /// All the names of each debug section found in the current object file. | ||
| 68 | /// Each name is terminated by a null-terminator. The name can be found, | ||
| 69 | /// from the `index` offset within the `RelocatableData`. | ||
| 70 | debug_names: [:0]const u8, | ||
| 71 | 67 | ||
| 72 | /// Represents a single item within a section (depending on its `type`) | 68 | /// Represents a single item within a section (depending on its `type`) |
| 73 | const RelocatableData = struct { | 69 | const RelocatableData = struct { |
| 74 | /// The type of the relocatable data | 70 | /// The type of the relocatable data |
| 75 | type: enum { data, code, debug }, | 71 | type: Tag, |
| 76 | /// Pointer to the data of the segment, where its length is written to `size` | 72 | /// Pointer to the data of the segment, where its length is written to `size` |
| 77 | data: [*]u8, | 73 | data: [*]u8, |
| 78 | /// The size in bytes of the data representing the segment within the section | 74 | /// The size in bytes of the data representing the segment within the section |
| ... | @@ -85,6 +81,8 @@ const RelocatableData = struct { | ... | @@ -85,6 +81,8 @@ const RelocatableData = struct { |
| 85 | /// Represents the index of the section it belongs to | 81 | /// Represents the index of the section it belongs to |
| 86 | section_index: u32, | 82 | section_index: u32, |
| 87 | 83 | ||
| 84 | const Tag = enum { data, code, custom }; | ||
| 85 | |||
| 88 | /// Returns the alignment of the segment, by retrieving it from the segment | 86 | /// Returns the alignment of the segment, by retrieving it from the segment |
| 89 | /// meta data of the given object file. | 87 | /// meta data of the given object file. |
| 90 | /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's | 88 | /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's |
| ... | @@ -99,14 +97,14 @@ const RelocatableData = struct { | ... | @@ -99,14 +97,14 @@ const RelocatableData = struct { |
| 99 | return switch (relocatable_data.type) { | 97 | return switch (relocatable_data.type) { |
| 100 | .data => .data, | 98 | .data => .data, |
| 101 | .code => .function, | 99 | .code => .function, |
| 102 | .debug => .section, | 100 | .custom => .section, |
| 103 | }; | 101 | }; |
| 104 | } | 102 | } |
| 105 | 103 | ||
| 106 | /// Returns the index within a section itrelocatable_data, or in case of a debug section, | 104 | /// Returns the index within a section, or in case of a custom section, |
| 107 | /// returns the section index within the object file. | 105 | /// returns the section index within the object file. |
| 108 | pub fn getIndex(relocatable_data: RelocatableData) u32 { | 106 | pub fn getIndex(relocatable_data: RelocatableData) u32 { |
| 109 | if (relocatable_data.type == .debug) return relocatable_data.section_index; | 107 | if (relocatable_data.type == .custom) return relocatable_data.section_index; |
| 110 | return relocatable_data.index; | 108 | return relocatable_data.index; |
| 111 | } | 109 | } |
| 112 | }; | 110 | }; |
| ... | @@ -121,7 +119,6 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz | ... | @@ -121,7 +119,6 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz |
| 121 | var object: Object = .{ | 119 | var object: Object = .{ |
| 122 | .file = file, | 120 | .file = file, |
| 123 | .name = try gpa.dupe(u8, name), | 121 | .name = try gpa.dupe(u8, name), |
| 124 | .debug_names = &.{}, | ||
| 125 | }; | 122 | }; |
| 126 | 123 | ||
| 127 | var is_object_file: bool = false; | 124 | var is_object_file: bool = false; |
| ... | @@ -182,10 +179,16 @@ pub fn deinit(object: *Object, gpa: Allocator) void { | ... | @@ -182,10 +179,16 @@ pub fn deinit(object: *Object, gpa: Allocator) void { |
| 182 | gpa.free(info.name); | 179 | gpa.free(info.name); |
| 183 | } | 180 | } |
| 184 | gpa.free(object.segment_info); | 181 | gpa.free(object.segment_info); |
| 185 | for (object.relocatable_data) |rel_data| { | 182 | { |
| 186 | gpa.free(rel_data.data[0..rel_data.size]); | 183 | var it = object.relocatable_data.valueIterator(); |
| 184 | while (it.next()) |relocatable_data| { | ||
| 185 | for (relocatable_data.*) |rel_data| { | ||
| 186 | gpa.free(rel_data.data[0..rel_data.size]); | ||
| 187 | } | ||
| 188 | gpa.free(relocatable_data.*); | ||
| 189 | } | ||
| 187 | } | 190 | } |
| 188 | gpa.free(object.relocatable_data); | 191 | object.relocatable_data.deinit(gpa); |
| 189 | object.string_table.deinit(gpa); | 192 | object.string_table.deinit(gpa); |
| 190 | gpa.free(object.name); | 193 | gpa.free(object.name); |
| 191 | object.* = undefined; | 194 | object.* = undefined; |
| ... | @@ -345,23 +348,7 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -345,23 +348,7 @@ fn Parser(comptime ReaderType: type) type { |
| 345 | errdefer parser.object.deinit(gpa); | 348 | errdefer parser.object.deinit(gpa); |
| 346 | try parser.verifyMagicBytes(); | 349 | try parser.verifyMagicBytes(); |
| 347 | const version = try parser.reader.reader().readInt(u32, .little); | 350 | const version = try parser.reader.reader().readInt(u32, .little); |
| 348 | |||
| 349 | parser.object.version = version; | 351 | parser.object.version = version; |
| 350 | var relocatable_data = std.ArrayList(RelocatableData).init(gpa); | ||
| 351 | var debug_names = std.ArrayList(u8).init(gpa); | ||
| 352 | |||
| 353 | errdefer { | ||
| 354 | // only free the inner contents of relocatable_data if we didn't | ||
| 355 | // assign it to the object yet. | ||
| 356 | if (parser.object.relocatable_data.len == 0) { | ||
| 357 | for (relocatable_data.items) |rel_data| { | ||
| 358 | gpa.free(rel_data.data[0..rel_data.size]); | ||
| 359 | } | ||
| 360 | relocatable_data.deinit(); | ||
| 361 | } | ||
| 362 | gpa.free(debug_names.items); | ||
| 363 | debug_names.deinit(); | ||
| 364 | } | ||
| 365 | 352 | ||
| 366 | var section_index: u32 = 0; | 353 | var section_index: u32 = 0; |
| 367 | while (parser.reader.reader().readByte()) |byte| : (section_index += 1) { | 354 | while (parser.reader.reader().readByte()) |byte| : (section_index += 1) { |
| ... | @@ -377,26 +364,34 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -377,26 +364,34 @@ fn Parser(comptime ReaderType: type) type { |
| 377 | 364 | ||
| 378 | if (std.mem.eql(u8, name, "linking")) { | 365 | if (std.mem.eql(u8, name, "linking")) { |
| 379 | is_object_file.* = true; | 366 | is_object_file.* = true; |
| 380 | parser.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them. | ||
| 381 | try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left))); | 367 | try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left))); |
| 382 | } else if (std.mem.startsWith(u8, name, "reloc")) { | 368 | } else if (std.mem.startsWith(u8, name, "reloc")) { |
| 383 | try parser.parseRelocations(gpa); | 369 | try parser.parseRelocations(gpa); |
| 384 | } else if (std.mem.eql(u8, name, "target_features")) { | 370 | } else if (std.mem.eql(u8, name, "target_features")) { |
| 385 | try parser.parseFeatures(gpa); | 371 | try parser.parseFeatures(gpa); |
| 386 | } else if (std.mem.startsWith(u8, name, ".debug")) { | 372 | } else if (std.mem.startsWith(u8, name, ".debug")) { |
| 373 | const gop = try parser.object.relocatable_data.getOrPut(gpa, .custom); | ||
| 374 | var relocatable_data: std.ArrayListUnmanaged(RelocatableData) = .{}; | ||
| 375 | defer relocatable_data.deinit(gpa); | ||
| 376 | if (!gop.found_existing) { | ||
| 377 | gop.value_ptr.* = &.{}; | ||
| 378 | } else { | ||
| 379 | relocatable_data = std.ArrayListUnmanaged(RelocatableData).fromOwnedSlice(gop.value_ptr.*); | ||
| 380 | } | ||
| 387 | const debug_size = @as(u32, @intCast(reader.context.bytes_left)); | 381 | const debug_size = @as(u32, @intCast(reader.context.bytes_left)); |
| 388 | const debug_content = try gpa.alloc(u8, debug_size); | 382 | const debug_content = try gpa.alloc(u8, debug_size); |
| 389 | errdefer gpa.free(debug_content); | 383 | errdefer gpa.free(debug_content); |
| 390 | try reader.readNoEof(debug_content); | 384 | try reader.readNoEof(debug_content); |
| 391 | 385 | ||
| 392 | try relocatable_data.append(.{ | 386 | try relocatable_data.append(gpa, .{ |
| 393 | .type = .debug, | 387 | .type = .custom, |
| 394 | .data = debug_content.ptr, | 388 | .data = debug_content.ptr, |
| 395 | .size = debug_size, | 389 | .size = debug_size, |
| 396 | .index = try parser.object.string_table.put(gpa, name), | 390 | .index = try parser.object.string_table.put(gpa, name), |
| 397 | .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset | 391 | .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset |
| 398 | .section_index = section_index, | 392 | .section_index = section_index, |
| 399 | }); | 393 | }); |
| 394 | gop.value_ptr.* = try relocatable_data.toOwnedSlice(gpa); | ||
| 400 | } else { | 395 | } else { |
| 401 | try reader.skipBytes(reader.context.bytes_left, .{}); | 396 | try reader.skipBytes(reader.context.bytes_left, .{}); |
| 402 | } | 397 | } |
| ... | @@ -515,26 +510,32 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -515,26 +510,32 @@ fn Parser(comptime ReaderType: type) type { |
| 515 | const start = reader.context.bytes_left; | 510 | const start = reader.context.bytes_left; |
| 516 | var index: u32 = 0; | 511 | var index: u32 = 0; |
| 517 | const count = try readLeb(u32, reader); | 512 | const count = try readLeb(u32, reader); |
| 513 | const imported_function_count = parser.object.importedCountByKind(.function); | ||
| 514 | var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); | ||
| 515 | defer relocatable_data.deinit(); | ||
| 518 | while (index < count) : (index += 1) { | 516 | while (index < count) : (index += 1) { |
| 519 | const code_len = try readLeb(u32, reader); | 517 | const code_len = try readLeb(u32, reader); |
| 520 | const offset = @as(u32, @intCast(start - reader.context.bytes_left)); | 518 | const offset = @as(u32, @intCast(start - reader.context.bytes_left)); |
| 521 | const data = try gpa.alloc(u8, code_len); | 519 | const data = try gpa.alloc(u8, code_len); |
| 522 | errdefer gpa.free(data); | 520 | errdefer gpa.free(data); |
| 523 | try reader.readNoEof(data); | 521 | try reader.readNoEof(data); |
| 524 | try relocatable_data.append(.{ | 522 | relocatable_data.appendAssumeCapacity(.{ |
| 525 | .type = .code, | 523 | .type = .code, |
| 526 | .data = data.ptr, | 524 | .data = data.ptr, |
| 527 | .size = code_len, | 525 | .size = code_len, |
| 528 | .index = parser.object.importedCountByKind(.function) + index, | 526 | .index = imported_function_count + index, |
| 529 | .offset = offset, | 527 | .offset = offset, |
| 530 | .section_index = section_index, | 528 | .section_index = section_index, |
| 531 | }); | 529 | }); |
| 532 | } | 530 | } |
| 531 | try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice()); | ||
| 533 | }, | 532 | }, |
| 534 | .data => { | 533 | .data => { |
| 535 | const start = reader.context.bytes_left; | 534 | const start = reader.context.bytes_left; |
| 536 | var index: u32 = 0; | 535 | var index: u32 = 0; |
| 537 | const count = try readLeb(u32, reader); | 536 | const count = try readLeb(u32, reader); |
| 537 | var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); | ||
| 538 | defer relocatable_data.deinit(); | ||
| 538 | while (index < count) : (index += 1) { | 539 | while (index < count) : (index += 1) { |
| 539 | const flags = try readLeb(u32, reader); | 540 | const flags = try readLeb(u32, reader); |
| 540 | const data_offset = try readInit(reader); | 541 | const data_offset = try readInit(reader); |
| ... | @@ -545,7 +546,7 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -545,7 +546,7 @@ fn Parser(comptime ReaderType: type) type { |
| 545 | const data = try gpa.alloc(u8, data_len); | 546 | const data = try gpa.alloc(u8, data_len); |
| 546 | errdefer gpa.free(data); | 547 | errdefer gpa.free(data); |
| 547 | try reader.readNoEof(data); | 548 | try reader.readNoEof(data); |
| 548 | try relocatable_data.append(.{ | 549 | relocatable_data.appendAssumeCapacity(.{ |
| 549 | .type = .data, | 550 | .type = .data, |
| 550 | .data = data.ptr, | 551 | .data = data.ptr, |
| 551 | .size = data_len, | 552 | .size = data_len, |
| ... | @@ -554,6 +555,7 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -554,6 +555,7 @@ fn Parser(comptime ReaderType: type) type { |
| 554 | .section_index = section_index, | 555 | .section_index = section_index, |
| 555 | }); | 556 | }); |
| 556 | } | 557 | } |
| 558 | try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice()); | ||
| 557 | }, | 559 | }, |
| 558 | else => try parser.reader.reader().skipBytes(len, .{}), | 560 | else => try parser.reader.reader().skipBytes(len, .{}), |
| 559 | } | 561 | } |
| ... | @@ -561,7 +563,6 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -561,7 +563,6 @@ fn Parser(comptime ReaderType: type) type { |
| 561 | error.EndOfStream => {}, // finished parsing the file | 563 | error.EndOfStream => {}, // finished parsing the file |
| 562 | else => |e| return e, | 564 | else => |e| return e, |
| 563 | } | 565 | } |
| 564 | parser.object.relocatable_data = try relocatable_data.toOwnedSlice(); | ||
| 565 | } | 566 | } |
| 566 | 567 | ||
| 567 | /// Based on the "features" custom section, parses it into a list of | 568 | /// Based on the "features" custom section, parses it into a list of |
| ... | @@ -789,7 +790,8 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -789,7 +790,8 @@ fn Parser(comptime ReaderType: type) type { |
| 789 | }, | 790 | }, |
| 790 | .section => { | 791 | .section => { |
| 791 | symbol.index = try leb.readULEB128(u32, reader); | 792 | symbol.index = try leb.readULEB128(u32, reader); |
| 792 | for (parser.object.relocatable_data) |data| { | 793 | const section_data = parser.object.relocatable_data.get(.custom).?; |
| 794 | for (section_data) |data| { | ||
| 793 | if (data.section_index == symbol.index) { | 795 | if (data.section_index == symbol.index) { |
| 794 | symbol.name = data.index; | 796 | symbol.name = data.index; |
| 795 | break; | 797 | break; |
| ... | @@ -798,22 +800,15 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -798,22 +800,15 @@ fn Parser(comptime ReaderType: type) type { |
| 798 | }, | 800 | }, |
| 799 | else => { | 801 | else => { |
| 800 | symbol.index = try leb.readULEB128(u32, reader); | 802 | symbol.index = try leb.readULEB128(u32, reader); |
| 801 | var maybe_import: ?types.Import = null; | ||
| 802 | |||
| 803 | const is_undefined = symbol.isUndefined(); | 803 | const is_undefined = symbol.isUndefined(); |
| 804 | if (is_undefined) { | ||
| 805 | maybe_import = parser.object.findImport(symbol.tag.externalType(), symbol.index); | ||
| 806 | } | ||
| 807 | const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME); | 804 | const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME); |
| 808 | if (!(is_undefined and !explicit_name)) { | 805 | symbol.name = if (!is_undefined or (is_undefined and explicit_name)) name: { |
| 809 | const name_len = try leb.readULEB128(u32, reader); | 806 | const name_len = try leb.readULEB128(u32, reader); |
| 810 | const name = try gpa.alloc(u8, name_len); | 807 | const name = try gpa.alloc(u8, name_len); |
| 811 | defer gpa.free(name); | 808 | defer gpa.free(name); |
| 812 | try reader.readNoEof(name); | 809 | try reader.readNoEof(name); |
| 813 | symbol.name = try parser.object.string_table.put(gpa, name); | 810 | break :name try parser.object.string_table.put(gpa, name); |
| 814 | } else { | 811 | } else parser.object.findImport(symbol.tag.externalType(), symbol.index).name; |
| 815 | symbol.name = maybe_import.?.name; | ||
| 816 | } | ||
| 817 | }, | 812 | }, |
| 818 | } | 813 | } |
| 819 | return symbol; | 814 | return symbol; |
| ... | @@ -887,110 +882,95 @@ fn assertEnd(reader: anytype) !void { | ... | @@ -887,110 +882,95 @@ fn assertEnd(reader: anytype) !void { |
| 887 | } | 882 | } |
| 888 | 883 | ||
| 889 | /// Parses an object file into atoms, for code and data sections | 884 | /// Parses an object file into atoms, for code and data sections |
| 890 | pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { | 885 | pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index { |
| 891 | const Key = struct { | 886 | const symbol = &object.symtable[symbol_index]; |
| 892 | kind: Symbol.Tag, | 887 | const relocatable_data: RelocatableData = switch (symbol.tag) { |
| 893 | index: u32, | 888 | .function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)], |
| 894 | }; | 889 | .data => object.relocatable_data.get(.data).?[symbol.index], |
| 895 | var symbol_for_segment = std.AutoArrayHashMap(Key, std.ArrayList(u32)).init(gpa); | 890 | .section => blk: { |
| 896 | defer for (symbol_for_segment.values()) |*list| { | 891 | const data = object.relocatable_data.get(.custom).?; |
| 897 | list.deinit(); | 892 | for (data) |dat| { |
| 898 | } else symbol_for_segment.deinit(); | 893 | if (dat.section_index == symbol.index) { |
| 899 | 894 | break :blk dat; | |
| 900 | for (object.symtable, 0..) |symbol, symbol_index| { | ||
| 901 | switch (symbol.tag) { | ||
| 902 | .function, .data, .section => if (!symbol.isUndefined()) { | ||
| 903 | const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); | ||
| 904 | const sym_idx = @as(u32, @intCast(symbol_index)); | ||
| 905 | if (!gop.found_existing) { | ||
| 906 | gop.value_ptr.* = std.ArrayList(u32).init(gpa); | ||
| 907 | } | 895 | } |
| 908 | try gop.value_ptr.*.append(sym_idx); | 896 | } |
| 909 | }, | 897 | unreachable; |
| 910 | else => continue, | 898 | }, |
| 911 | } | 899 | else => unreachable, |
| 900 | }; | ||
| 901 | const final_index = try wasm.getMatchingSegment(object_index, symbol_index); | ||
| 902 | const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); | ||
| 903 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | ||
| 904 | atom.* = Atom.empty; | ||
| 905 | try wasm.appendAtomAtIndex(final_index, atom_index); | ||
| 906 | |||
| 907 | atom.sym_index = symbol_index; | ||
| 908 | atom.file = object_index; | ||
| 909 | atom.size = relocatable_data.size; | ||
| 910 | atom.alignment = relocatable_data.getAlignment(object); | ||
| 911 | atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]); | ||
| 912 | atom.original_offset = relocatable_data.offset; | ||
| 913 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom_index); | ||
| 914 | const segment: *Wasm.Segment = &wasm.segments.items[final_index]; | ||
| 915 | if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned | ||
| 916 | segment.alignment = segment.alignment.max(atom.alignment); | ||
| 912 | } | 917 | } |
| 913 | 918 | ||
| 914 | for (object.relocatable_data, 0..) |relocatable_data, index| { | 919 | if (object.relocations.get(relocatable_data.section_index)) |relocations| { |
| 915 | const final_index = (try wasm_bin.getMatchingSegment(object_index, @as(u32, @intCast(index)))) orelse { | 920 | const start = searchRelocStart(relocations, relocatable_data.offset); |
| 916 | continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. | 921 | const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size); |
| 917 | }; | 922 | atom.relocs = std.ArrayListUnmanaged(types.Relocation).fromOwnedSlice(relocations[start..][0..len]); |
| 918 | 923 | for (atom.relocs.items) |*reloc| { | |
| 919 | const atom_index: Atom.Index = @intCast(wasm_bin.managed_atoms.items.len); | 924 | switch (reloc.relocation_type) { |
| 920 | const atom = try wasm_bin.managed_atoms.addOne(gpa); | 925 | .R_WASM_TABLE_INDEX_I32, |
| 921 | atom.* = Atom.empty; | 926 | .R_WASM_TABLE_INDEX_I64, |
| 922 | atom.file = object_index; | 927 | .R_WASM_TABLE_INDEX_SLEB, |
| 923 | atom.size = relocatable_data.size; | 928 | .R_WASM_TABLE_INDEX_SLEB64, |
| 924 | atom.alignment = relocatable_data.getAlignment(object); | 929 | => { |
| 925 | 930 | try wasm.function_table.put(wasm.base.allocator, .{ | |
| 926 | const relocations: []types.Relocation = object.relocations.get(relocatable_data.section_index) orelse &.{}; | 931 | .file = object_index, |
| 927 | for (relocations) |relocation| { | 932 | .index = reloc.index, |
| 928 | if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) { | 933 | }, 0); |
| 929 | // set the offset relative to the offset of the segment itobject, | 934 | }, |
| 930 | // rather than within the entire section. | 935 | .R_WASM_GLOBAL_INDEX_I32, |
| 931 | var reloc = relocation; | 936 | .R_WASM_GLOBAL_INDEX_LEB, |
| 932 | reloc.offset -= relocatable_data.offset; | 937 | => { |
| 933 | try atom.relocs.append(gpa, reloc); | 938 | const sym = object.symtable[reloc.index]; |
| 934 | 939 | if (sym.tag != .global) { | |
| 935 | switch (relocation.relocation_type) { | 940 | try wasm.got_symbols.append( |
| 936 | .R_WASM_TABLE_INDEX_I32, | 941 | wasm.base.allocator, |
| 937 | .R_WASM_TABLE_INDEX_I64, | 942 | .{ .file = object_index, .index = reloc.index }, |
| 938 | .R_WASM_TABLE_INDEX_SLEB, | 943 | ); |
| 939 | .R_WASM_TABLE_INDEX_SLEB64, | 944 | } |
| 940 | => { | 945 | }, |
| 941 | try wasm_bin.function_table.put(gpa, .{ | 946 | else => {}, |
| 942 | .file = object_index, | ||
| 943 | .index = relocation.index, | ||
| 944 | }, 0); | ||
| 945 | }, | ||
| 946 | .R_WASM_GLOBAL_INDEX_I32, | ||
| 947 | .R_WASM_GLOBAL_INDEX_LEB, | ||
| 948 | => { | ||
| 949 | const sym = object.symtable[relocation.index]; | ||
| 950 | if (sym.tag != .global) { | ||
| 951 | try wasm_bin.got_symbols.append( | ||
| 952 | wasm_bin.base.allocator, | ||
| 953 | .{ .file = object_index, .index = relocation.index }, | ||
| 954 | ); | ||
| 955 | } | ||
| 956 | }, | ||
| 957 | else => {}, | ||
| 958 | } | ||
| 959 | } | 947 | } |
| 960 | } | 948 | } |
| 949 | } | ||
| 961 | 950 | ||
| 962 | try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); | 951 | return atom_index; |
| 963 | 952 | } | |
| 964 | if (symbol_for_segment.getPtr(.{ | ||
| 965 | .kind = relocatable_data.getSymbolKind(), | ||
| 966 | .index = relocatable_data.getIndex(), | ||
| 967 | })) |symbols| { | ||
| 968 | atom.sym_index = symbols.pop(); | ||
| 969 | try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index); | ||
| 970 | |||
| 971 | // symbols referencing the same atom will be added as alias | ||
| 972 | // or as 'parent' when they are global. | ||
| 973 | while (symbols.popOrNull()) |idx| { | ||
| 974 | try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom_index); | ||
| 975 | const alias_symbol = object.symtable[idx]; | ||
| 976 | if (alias_symbol.isGlobal()) { | ||
| 977 | atom.sym_index = idx; | ||
| 978 | } | ||
| 979 | } | ||
| 980 | } | ||
| 981 | 953 | ||
| 982 | const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; | 954 | fn searchRelocStart(relocs: []const types.Relocation, address: u32) usize { |
| 983 | if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned | 955 | var min: usize = 0; |
| 984 | segment.alignment = segment.alignment.max(atom.alignment); | 956 | var max: usize = relocs.len; |
| 957 | while (min < max) { | ||
| 958 | const index = (min + max) / 2; | ||
| 959 | const curr = relocs[index]; | ||
| 960 | if (curr.offset < address) { | ||
| 961 | min = index + 1; | ||
| 962 | } else { | ||
| 963 | max = index; | ||
| 985 | } | 964 | } |
| 986 | |||
| 987 | try wasm_bin.appendAtomAtIndex(final_index, atom_index); | ||
| 988 | log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index }); | ||
| 989 | } | 965 | } |
| 966 | return min; | ||
| 990 | } | 967 | } |
| 991 | 968 | ||
| 992 | /// Verifies if a given value is in between a minimum -and maximum value. | 969 | fn searchRelocEnd(relocs: []const types.Relocation, address: u32) usize { |
| 993 | /// The maxmimum value is calculated using the length, both start and end are inclusive. | 970 | for (relocs, 0..relocs.len) |reloc, index| { |
| 994 | inline fn isInbetween(min: u32, length: u32, value: u32) bool { | 971 | if (reloc.offset > address) { |
| 995 | return value >= min and value <= min + length; | 972 | return index; |
| 973 | } | ||
| 974 | } | ||
| 975 | return relocs.len; | ||
| 996 | } | 976 | } |