tiny.tldr.formats.elf.merge
Defined in formats.elf.
API (19)
Actions
Public operations.
Fixed.collectFixed.deinitStartIndex.deinitStartIndex.forSectionString.deinitString.finishString.registeraddresscollectStartscontributionensurefixedpieceshouldIndexStartsstring
Types and contracts
Public types and contracts.
Source
Source: lib/tldr/src/formats/elf/merge/fixed.zig:60
pub const Fixed = struct { pieces: Map = .empty, pub fn deinit(self: *Fixed, allocator: Allocator) void { self.pieces.deinit(allocator); } pub fn collect( self: *Fixed, allocator: Allocator, object: ObjectFile, section: SectionHeader, section_index: usize, output_sections: *[output_section_count]OutputSection, merge_sections: []MergeSectionLayout, options: model.LinkOptions, ) model.Error!void { if (section.size % section.entry_size != 0) return error.InvalidObject; if (object.relocationsForSection(section_index).len != 0) return error.UnsupportedRelocation; try validateAlignment(section.alignment); const entry_size: usize = @intCast(section.entry_size); const entry_count: usize = @intCast(section.size / section.entry_size); const layouts = try allocator.alloc(MergePieceLayout, entry_count); errdefer allocator.free(layouts); const output_index = outputIndexForAlloc(section); const output_index_u8: u8 = @intCast(output_index); var output = &output_sections[output_index]; const alignment = @max(section.alignment, 1); output.alignment = @max(output.alignment, alignment); const bytes = try sectionBytes(object.bytes, section); for (layouts, 0..) |*piece_layout, index| { const start = index * entry_size; const piece_bytes = bytes[start..][0..entry_size]; piece_layout.* = .{ .input_offset = @intCast(start), .size = section.entry_size, }; const key: Key = .{ .section_type = section.section_type, .output_index = output_index_u8, .entry_size = section.entry_size, .alignment = alignment, .bytes = piece_bytes, }; if (self.pieces.getContext(key, context)) |existing| { piece_layout.contribution = existing.contribution; piece_layout.output_intra_offset = existing.intra_offset; continue; } const aligned_offset = alignForwardU64(output.file_size, alignment); const reserved_size = contributionReserveSize(section.entry_size, alignment, options); const contribution = SectionContribution.init( output_index, aligned_offset, section.entry_size, reserved_size, alignment, ); try self.pieces.putContext(allocator, key, .{ .contribution = contribution }, context); piece_layout.contribution = contribution; output.file_size = aligned_offset + reserved_size; output.memory_size = output.file_size; } merge_sections[section_index] = .{ .pieces = layouts, .fixed_piece_size = section.entry_size, }; }};Source: lib/tldr/src/formats/elf/merge/lookup.zig:10
pub const Piece = struct { contribution: SectionContribution, intra_offset: u64,};Source: lib/tldr/src/formats/elf/merge/start.zig:16
pub const Index = struct { built: bool = false, sections: []SectionStarts = &.{}, pub fn deinit(self: *Index, allocator: Allocator) void { for (self.sections) |section| allocator.free(section.starts); allocator.free(self.sections); self.* = .{}; } pub fn forSection( self: *Index, allocator: Allocator, object: ObjectFile, section_index: usize, ) Allocator.Error![]const u64 { if (!self.built) try self.build(allocator, object); for (self.sections) |section| { if (section.section_index == section_index) return section.starts; } return &.{}; } fn build(self: *Index, allocator: Allocator, object: ObjectFile) Allocator.Error!void { var builders = std.ArrayListUnmanaged(SectionBuilder).empty; defer builders.deinit(allocator); errdefer deinitBuilders(allocator, builders.items); for (object.symbols) |symbol| { if (symbol.section_index >= object.sections.len) continue; const section_index: usize = symbol.section_index; const section = object.sections[section_index]; if (!mergeStringSectionWithoutRelocations(object, section, section_index)) continue; if (!symbol.isGlobalDefinition()) continue; const starts = try builderForSection(allocator, &builders, section_index); try starts.append(allocator, symbol.value); } const sections = try allocator.alloc(SectionStarts, builders.items.len); var initialized_sections: usize = 0; errdefer { for (sections[0..initialized_sections]) |section| allocator.free(section.starts); allocator.free(sections); } for (builders.items, sections) |*builder, *section| { std.mem.sort(u64, builder.starts.items, {}, std.sort.asc(u64)); section.* = .{ .section_index = builder.section_index, .starts = try builder.starts.toOwnedSlice(allocator), }; initialized_sections += 1; } self.sections = sections; self.built = true; }};Source: lib/tldr/src/formats/elf/merge/string.zig:24
pub const String = struct { records: std.ArrayListUnmanaged(Record) = .empty, pending: std.ArrayListUnmanaged(Pending) = .empty, const Pending = struct { bytes: []const u8, external_starts: []const u64, layout_slot: *MergeSectionLayout, alignment: u64, section_type: u32, output_index: u8, base: usize = 0, count: usize = 0, pieces: []MergePieceLayout = &.{}, }; const ScanContext = struct { pending: []Pending, records: []Record, }; pub fn deinit(self: *String, allocator: Allocator) void { self.records.deinit(allocator); self.pending.deinit(allocator); } pub fn register( self: *String, allocator: Allocator, object: ObjectFile, section: SectionHeader, section_index: usize, external_starts: []const u64, output_sections: *[output_section_count]OutputSection, layout_slot: *MergeSectionLayout, ) model.Error!void { if (object.relocationsForSection(section_index).len != 0) return error.UnsupportedRelocation; const bytes = try sectionBytes(object.bytes, section); if (bytes.len == 0) return; if (bytes[bytes.len - 1] != 0) return error.InvalidObject; const output_index = outputIndexForAlloc(section); var output = &output_sections[output_index]; const alignment = @max(section.alignment, 1); output.alignment = @max(output.alignment, alignment); const starts = if (external_starts.len != 0) try allocator.dupe(u64, external_starts) else external_starts; try self.pending.append(allocator, .{ .bytes = bytes, .external_starts = starts, .layout_slot = layout_slot, .alignment = alignment, .section_type = section.section_type, .output_index = @intCast(output_index), }); } pub fn finish( self: *String, allocator: Allocator, output_sections: *[output_section_count]OutputSection, options: model.LinkOptions, ) model.Error!void { try self.materialize(allocator, options); try target.assign(allocator, output_sections, self.records.items, options); } fn materialize(self: *String, allocator: Allocator, options: model.LinkOptions) model.Error!void { if (self.pending.items.len == 0) return; const pending = self.pending.items; var total_bytes: usize = 0; for (pending) |entry| total_bytes += entry.bytes.len; const requested_workers = if (options.max_link_jobs != 0) options.max_link_jobs else total_bytes / scan_bytes_per_worker; const workers = if (total_bytes >= parallel_scan_threshold) parallel.chooseWorkers(pending.len, requested_workers) else 1; var count_context = ScanContext{ .pending = pending, .records = &.{} }; if (workers <= 1) { for (pending, 0..) |_, index| countPending(&count_context, 0, index); } else { parallel.forItems(pending.len, workers, &count_context, countPending); } var total: usize = 0; for (pending) |*entry| { entry.base = total; total += entry.count; entry.pieces = try allocator.alloc(MergePieceLayout, entry.count); entry.layout_slot.* = .{ .pieces = entry.pieces }; } if (std.math.cast(u32, total) == null) return error.InvalidObject; try self.records.resize(allocator, total); var fill_context = ScanContext{ .pending = pending, .records = self.records.items }; if (workers <= 1) { for (pending, 0..) |_, index| fillPending(&fill_context, 0, index); } else { parallel.forItems(pending.len, workers, &fill_context, fillPending); } } fn countPending(context: *ScanContext, worker: usize, index: usize) void { _ = worker; context.pending[index].count = start.count(context.pending[index].bytes); } fn fillPending(context: *ScanContext, worker: usize, index: usize) void { _ = worker; const entry = context.pending[index]; const records = context.records[entry.base..][0..entry.count]; const bytes = entry.bytes; var input_offset: usize = 0; var piece_index: usize = 0; var external_start_index: usize = 0; for (bytes, 0..) |byte, byte_index| { if (byte != 0) continue; const end = byte_index + 1; const string_bytes = bytes[input_offset..end]; entry.pieces[piece_index] = .{ .input_offset = @intCast(input_offset), .size = @intCast(string_bytes.len), }; records[piece_index] = .{ .ordinal = @intCast(entry.base + piece_index), .section_type = entry.section_type, .output_index = entry.output_index, .alignment = entry.alignment, .reverse_prefix = record.reversePrefix(string_bytes), .bytes = string_bytes, .piece = &entry.pieces[piece_index], .external_start = start.has(entry.external_starts, &external_start_index, input_offset), }; piece_index += 1; input_offset = end; } }};Source: lib/tldr/src/formats/elf/merge/lookup.zig:15
pub fn address( object_layout: ObjectLayout, object: ObjectFile, output_sections: []const OutputSection, section_index: usize, offset: u64,) ?u64 { const entry = piece(object_layout, object, section_index, offset) orelse return null; const output = output_sections[entry.contribution.outputIndex()]; return output.address + entry.contribution.offset + entry.intra_offset;}Source: lib/tldr/src/formats/elf/merge/lookup.zig:27
pub fn contribution( object_layout: ObjectLayout, object: ObjectFile, section_index: usize, offset: u64,) ?SectionContribution { const entry = piece(object_layout, object, section_index, offset) orelse return null; return entry.contribution;}Source: lib/tldr/src/formats/elf/merge/lookup.zig:37
pub fn piece( object_layout: ObjectLayout, object: ObjectFile, section_index: usize, offset: u64,) ?Piece { const merge_section = mergeSectionLayout(object_layout, section_index) orelse return null; const section = object.sections[section_index]; if (offset >= section.size) return null; if (merge_section.fixed_piece_size != 0) { const piece_index: usize = @intCast(offset / merge_section.fixed_piece_size); if (piece_index >= merge_section.pieces.len) return null; const fixed_piece = merge_section.pieces[piece_index]; return .{ .contribution = fixed_piece.contribution, .intra_offset = fixed_piece.output_intra_offset + offset % merge_section.fixed_piece_size, }; } const string_piece = stringPiece(merge_section.pieces, offset) orelse return null; return .{ .contribution = string_piece.contribution, .intra_offset = string_piece.output_intra_offset + offset - string_piece.input_offset, };}Source: lib/tldr/src/formats/elf/merge/section.zig:9
pub fn ensure( allocator: Allocator, object: ObjectFile, merge_sections: *[]MergeSectionLayout,) Allocator.Error![]MergeSectionLayout { if (merge_sections.*.len != 0) return merge_sections.*; const sections = try allocator.alloc(MergeSectionLayout, object.sections.len); @memset(sections, .{}); merge_sections.* = sections; return sections;}Source: lib/tldr/src/formats/elf/merge/section.zig:21
pub fn fixed(section: SectionHeader) bool { if ((section.flags & std.elf.SHF_ALLOC) == 0) return false; if ((section.flags & std.elf.SHF_MERGE) == 0) return false; if ((section.flags & std.elf.SHF_STRINGS) != 0) return false; if (section.section_type != std.elf.SHT_PROGBITS) return false; if (section.entry_size == 0) return false; return true;}Source: lib/tldr/src/formats/elf/merge/section.zig:30
pub fn string(section: SectionHeader) bool { if ((section.flags & std.elf.SHF_ALLOC) == 0) return false; if ((section.flags & std.elf.SHF_MERGE) == 0) return false; if ((section.flags & std.elf.SHF_STRINGS) == 0) return false; if (section.section_type != std.elf.SHT_PROGBITS) return false; if (section.entry_size > 1) return false; if (section.alignment > 1) return false; return true;}Source: lib/tldr/src/formats/elf/merge/start.zig:83
pub fn collect( allocator: Allocator, object: ObjectFile, section_index: usize,) Allocator.Error![]u64 { var starts = std.ArrayListUnmanaged(u64).empty; errdefer starts.deinit(allocator); for (object.symbols) |symbol| { if (symbol.section_index != section_index) continue; if (!symbol.isGlobalDefinition()) continue; try starts.append(allocator, symbol.value); } if (starts.items.len == 0) return &.{}; std.mem.sort(u64, starts.items, {}, std.sort.asc(u64)); return try starts.toOwnedSlice(allocator);}Source: lib/tldr/src/formats/elf/merge/start.zig:73
pub fn shouldIndex(object: ObjectFile) bool { var section_count: usize = 0; for (object.sections, 0..) |section, section_index| { if (!mergeStringSectionWithoutRelocations(object, section, section_index)) continue; section_count += 1; if (section_count > 1) return true; } return false;}Source: lib/tldr/src/formats/elf/merge/root.zig
const fixed_module = @import("fixed.zig");const lookup = @import("lookup.zig");const section = @import("section.zig");const start = @import("start.zig");const string_module = @import("string.zig");pub const Piece = lookup.Piece;pub const Fixed = fixed_module.Fixed;pub const StartIndex = start.Index;pub const String = string_module.String;pub const address = lookup.address;pub const collectStarts = start.collect;pub const contribution = lookup.contribution;pub const ensure = section.ensure;pub const fixed = section.fixed;pub const shouldIndexStarts = start.shouldIndex;pub const piece = lookup.piece;pub const string = section.string;Source: lib/tldr/src/formats/elf/root.zig:15
pub const merge = @import("merge/root.zig");Audit
| Definitions | 20 |
|---|---|
| Public names | 20 |
| Members | 7 |
| Version | 26.7.0 |
| Revision | daab053ee433 |