lib/tldr/src/formats/elf/image/writer.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const root = @import("../../../root.zig");
  3 const elf = @import("../root.zig");
  4 const symbols = @import("symbols.zig");
  5 const zero = @import("zero.zig");
  6 
  7 const Allocator = std.mem.Allocator;
  8 const model = root.model;
  9 const trace = root.trace;
 10 const StringTable = elf.string.Table;
 11 const ObjectFile = elf.parser.ObjectFile;
 12 const ObjectLayout = elf.layout.ObjectLayout;
 13 const OutputSection = elf.layout.OutputSection;
 14 const GlobalSymbol = elf.layout.GlobalSymbol;
 15 const SymbolRef = elf.layout.SymbolRef;
 16 const GotLayout = elf.layout.GotLayout;
 17 const SymbolAddressCache = elf.addressing.Cache;
 18 const OutputSectionKind = elf.output_section.OutputSectionKind;
 19 const output_section_count = elf.output_section.output_section_count;
 20 const SectionHeader = elf.format.SectionHeader;
 21 const SymbolRecord = elf.format.SymbolRecord;
 22 const ehdr_size = elf.format.ehdr_size;
 23 const phdr_size = elf.format.phdr_size;
 24 const shdr_size = elf.format.shdr_size;
 25 const sym_size = elf.format.sym_size;
 26 const alignForwardU64 = elf.format.alignForwardU64;
 27 const copyInto = elf.format.copyInto;
 28 const elfSymbolInfo = elf.format.elfSymbolInfo;
 29 
 30 pub fn write(
 31     scratch: Allocator,
 32     image_allocator: Allocator,
 33     objects: []const ObjectFile,
 34     layouts: []const ObjectLayout,
 35     output_sections: *[output_section_count]OutputSection,
 36     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
 37     global_symbol_refs: []const SymbolRef,
 38     symbol_addresses: *SymbolAddressCache,
 39     got_layout: GotLayout,
 40     options: model.LinkOptions,
 41     entry_address: u64,
 42     live_output_count: usize,
 43     program_header_count: usize,
 44 ) model.Error!root.LinkedImage.Storage.Allocation {
 45     var shstrtab = try StringTable.init(scratch);
 46     defer shstrtab.deinit(scratch);
 47     var strtab = try StringTable.init(scratch);
 48     defer strtab.deinit(scratch);
 49 
 50     var sh_names: [output_section_count]u32 = @splat(0);
 51     for (output_sections, 0..) |section, index| {
 52         if (section.logicalSize() != 0) sh_names[index] = try shstrtab.add(scratch, section.kind.name());
 53     }
 54     const sh_name_symtab = try shstrtab.add(scratch, ".symtab");
 55     const sh_name_strtab = try shstrtab.add(scratch, ".strtab");
 56     const sh_name_shstrtab = try shstrtab.add(scratch, ".shstrtab");
 57 
 58     var executable_symbols = std.ArrayListUnmanaged(SymbolRecord).empty;
 59     defer executable_symbols.deinit(scratch);
 60     const local_symbol_capacity = std.math.add(usize, 1, live_output_count) catch return error.InvalidObject;
 61     const executable_symbol_capacity = std.math.add(usize, local_symbol_capacity, global_symbol_refs.len) catch return error.InvalidObject;
 62     try executable_symbols.ensureTotalCapacity(scratch, executable_symbol_capacity);
 63     try executable_symbols.append(scratch, .{});
 64     for (output_sections) |section| {
 65         if (section.logicalSize() == 0) continue;
 66         try executable_symbols.append(scratch, .{
 67             .info = elfSymbolInfo(std.elf.STB_LOCAL, std.elf.STT_SECTION),
 68             .section_index = section.section_index,
 69         });
 70     }
 71     const first_global_symbol: u32 = @intCast(executable_symbols.items.len);
 72     {
 73         const symbol_phase = trace.product(.write_symbol_table);
 74         defer symbol_phase.end();
 75         try symbols.appendExecutable(scratch, &executable_symbols, &strtab, objects, layouts, output_sections, globals, global_symbol_refs, symbol_addresses, options.max_link_jobs);
 76     }
 77 
 78     const symtab_size = executable_symbols.items.len * sym_size;
 79 
 80     var last_load_end: u64 = 0;
 81     for (output_sections) |section| {
 82         if (section.logicalSize() == 0) continue;
 83         const file_end = if (section.kind.isNoBits())
 84             section.file_offset
 85         else
 86             section.file_offset + section.fileLoadSize();
 87         last_load_end = @max(last_load_end, file_end);
 88     }
 89 
 90     var metadata_offset = alignForwardU64(last_load_end, 8);
 91     const symtab_offset = metadata_offset;
 92     metadata_offset += symtab_size;
 93     const strtab_offset = metadata_offset;
 94     metadata_offset += strtab.bytes().len;
 95     const shstrtab_offset = metadata_offset;
 96     metadata_offset += shstrtab.bytes().len;
 97     const shoff = alignForwardU64(metadata_offset, 8);
 98 
 99     const shnum = 1 + live_output_count + 3;
100     if (shnum > std.math.maxInt(u16)) return error.TooManySections;
101     const symtab_section_index: u16 = @intCast(1 + live_output_count);
102     const strtab_section_index: u16 = symtab_section_index + 1;
103     const shstrtab_section_index: u16 = strtab_section_index + 1;
104     const total_size = shoff + shnum * shdr_size;
105 
106     var allocation = try root.LinkedImage.Storage.allocate(image_allocator, @intCast(total_size), options);
107     errdefer allocation.deinit(image_allocator);
108     const image = allocation.bytes;
109     if (allocation.needsZeroFill()) {
110         const zero_phase = trace.product(.write_zero_fill);
111         defer zero_phase.end();
112         try zero.unwrittenFileRanges(image, objects, layouts, output_sections, symtab_offset, metadata_offset, shoff);
113     }
114     try writeGeneratedPayloads(scratch, image, objects, layouts, output_sections, globals, symbol_addresses, options);
115     try elf.got_table.writePayload(image, objects, layouts, output_sections, globals, symbol_addresses, got_layout);
116 
117     elf.program.writeElfHeader(image, .{
118         .entry_address = entry_address,
119         .program_header_count = @intCast(program_header_count),
120         .section_header_offset = shoff,
121         .section_count = @intCast(shnum),
122         .section_string_table_index = shstrtab_section_index,
123     });
124     elf.program.writeProgramHeaders(image, output_sections, options, program_header_count);
125     const metadata_phase = trace.product(.write_metadata);
126     const symbol_table = image[@as(usize, @intCast(symtab_offset))..];
127     for (executable_symbols.items, 0..) |entry, index| {
128         entry.write(symbol_table, index * sym_size);
129     }
130     copyInto(image, @intCast(strtab_offset), strtab.bytes());
131     copyInto(image, @intCast(shstrtab_offset), shstrtab.bytes());
132     metadata_phase.end();
133 
134     const section_table = image[@as(usize, @intCast(shoff))..];
135     var next_section_index: u16 = 1;
136     (SectionHeader{}).write(section_table, 0);
137     for (output_sections, 0..) |section, output_index| {
138         if (section.logicalSize() == 0) continue;
139         (SectionHeader{
140             .name_offset = sh_names[output_index],
141             .section_type = section.kind.sectionType(),
142             .flags = section.kind.flags(),
143             .address = section.address,
144             .offset = section.file_offset,
145             .size = section.logicalSize(),
146             .alignment = section.alignment,
147             .entry_size = section.kind.entrySize(),
148         }).write(section_table, @as(usize, next_section_index) * shdr_size);
149         next_section_index += 1;
150     }
151     (SectionHeader{
152         .name_offset = sh_name_symtab,
153         .section_type = std.elf.SHT_SYMTAB,
154         .offset = symtab_offset,
155         .size = symtab_size,
156         .link = strtab_section_index,
157         .info = first_global_symbol,
158         .alignment = 8,
159         .entry_size = sym_size,
160     }).write(section_table, @as(usize, symtab_section_index) * shdr_size);
161     (SectionHeader{
162         .name_offset = sh_name_strtab,
163         .section_type = std.elf.SHT_STRTAB,
164         .offset = strtab_offset,
165         .size = strtab.bytes().len,
166         .alignment = 1,
167     }).write(section_table, @as(usize, strtab_section_index) * shdr_size);
168     (SectionHeader{
169         .name_offset = sh_name_shstrtab,
170         .section_type = std.elf.SHT_STRTAB,
171         .offset = shstrtab_offset,
172         .size = shstrtab.bytes().len,
173         .alignment = 1,
174     }).write(section_table, @as(usize, shstrtab_section_index) * shdr_size);
175 
176     return allocation;
177 }
178 
179 fn writeGeneratedPayloads(
180     scratch: Allocator,
181     image: []u8,
182     objects: []const ObjectFile,
183     layouts: []const ObjectLayout,
184     output_sections: []const OutputSection,
185     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
186     symbol_addresses: *SymbolAddressCache,
187     options: model.LinkOptions,
188 ) model.Error!void {
189     if (options.build_id != .none) {
190         const section = output_sections[@backingInt(OutputSectionKind.build_id_note)];
191         try elf.note.writeBuildIdHeader(image, section.file_offset);
192     }
193     try elf.ehframe.writeHeader(scratch, image, objects, layouts, output_sections, globals, symbol_addresses, options);
194 }