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 }