lib/tldr/src/formats/elf/program.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const root = @import("../../root.zig");
3 const format = @import("format.zig");
4 const layout = @import("layout/root.zig");
5 const output_section = @import("section.zig");
6
7 const model = root.model;
8 const ehdr_size = format.ehdr_size;
9 const phdr_size = format.phdr_size;
10 const Header = format.Header;
11 const OutputSection = layout.OutputSection;
12 const OutputSectionKind = output_section.OutputSectionKind;
13 const writeU32 = format.writeU32;
14 const writeU64 = format.writeU64;
15
16 pub const HeaderFields = struct {
17 entry_address: u64,
18 program_header_count: u16,
19 section_header_offset: u64,
20 section_count: u16,
21 section_string_table_index: u16,
22 };
23
24 pub const TlsSegment = struct {
25 offset: u64,
26 address: u64,
27 file_size: u64,
28 memory_size: u64,
29 alignment: u64,
30 };
31
32 pub fn countHeaders(output_sections: []const OutputSection) model.Error!usize {
33 const count = 1 +
34 @as(usize, @intFromBool(noteSegment(output_sections) != null)) +
35 @as(usize, @intFromBool(ehFrameHeaderSegment(output_sections) != null)) +
36 countLoadSegments(output_sections) +
37 @as(usize, @intFromBool(tlsSegment(output_sections) != null));
38 if (count > std.math.maxInt(u16)) return error.TooManyProgramHeaders;
39 return count;
40 }
41
42 /// Writes the executable's ELF file header at the start of `image`: type
43 /// `EXEC`, the entry address, a program header table right after the file
44 /// header, and the section table fields from `fields`. The image writer calls
45 /// it once per link with the program header count returned by `countHeaders`.
46 /// `countHeaders` always counts an entry for the program header table itself,
47 /// so `program_header_count` is at least one and the header records a program
48 /// header entry size.
49 pub fn writeElfHeader(image: []u8, fields: HeaderFields) void {
50 const header = Header{
51 .type = .EXEC,
52 .entry = fields.entry_address,
53 .phoff = ehdr_size,
54 .phnum = fields.program_header_count,
55 .shoff = fields.section_header_offset,
56 .shnum = fields.section_count,
57 .shstrndx = fields.section_string_table_index,
58 };
59 header.write(image);
60 }
61
62 pub fn writeProgramHeaders(image: []u8, output_sections: []const OutputSection, options: model.LinkOptions, program_header_count: usize) void {
63 const first_load = firstLoadSection(output_sections) orelse return;
64 const first_load_base = first_load.address - first_load.file_offset;
65
66 writeU32(image, ehdr_size + 0, std.elf.PT_PHDR);
67 writeU32(image, ehdr_size + 4, std.elf.PF_R);
68 writeU64(image, ehdr_size + 8, ehdr_size);
69 writeU64(image, ehdr_size + 16, first_load_base + ehdr_size);
70 writeU64(image, ehdr_size + 24, first_load_base + ehdr_size);
71 writeU64(image, ehdr_size + 32, program_header_count * phdr_size);
72 writeU64(image, ehdr_size + 40, program_header_count * phdr_size);
73 writeU64(image, ehdr_size + 48, 8);
74
75 var index: usize = 1;
76 if (noteSegment(output_sections)) |note| {
77 const offset = ehdr_size + index * phdr_size;
78 writeU32(image, offset + 0, std.elf.PT_NOTE);
79 writeU32(image, offset + 4, std.elf.PF_R);
80 writeU64(image, offset + 8, note.file_offset);
81 writeU64(image, offset + 16, note.address);
82 writeU64(image, offset + 24, note.address);
83 writeU64(image, offset + 32, note.logicalSize());
84 writeU64(image, offset + 40, note.logicalSize());
85 writeU64(image, offset + 48, note.alignment);
86 index += 1;
87 }
88 if (ehFrameHeaderSegment(output_sections)) |header| {
89 const offset = ehdr_size + index * phdr_size;
90 writeU32(image, offset + 0, std.elf.PT_GNU_EH_FRAME);
91 writeU32(image, offset + 4, std.elf.PF_R);
92 writeU64(image, offset + 8, header.file_offset);
93 writeU64(image, offset + 16, header.address);
94 writeU64(image, offset + 24, header.address);
95 writeU64(image, offset + 32, header.logicalSize());
96 writeU64(image, offset + 40, header.logicalSize());
97 writeU64(image, offset + 48, header.alignment);
98 index += 1;
99 }
100 var first_load_written = false;
101 var run: ?LoadRun = null;
102 for (output_sections) |section| {
103 if (section.logicalSize() == 0) continue;
104 if (!section.kind.isAllocated()) continue;
105 if (run) |*current| {
106 if (sectionContinuesLoad(current.last_kind, section.kind)) {
107 if (!section.kind.isNoBits()) {
108 current.file_end = @max(current.file_end, section.file_offset + section.fileLoadSize());
109 }
110 current.memory_end = section.address + section.reserved_size;
111 current.last_kind = section.kind;
112 continue;
113 }
114 writeLoadHeader(image, ehdr_size + index * phdr_size, current.*, options.page_size);
115 index += 1;
116 run = null;
117 }
118 const load_file_offset = if (!first_load_written) 0 else section.file_offset;
119 const load_address = if (!first_load_written) first_load_base else section.address;
120 run = .{
121 .file_offset = load_file_offset,
122 .address = load_address,
123 .file_end = if (section.kind.isNoBits()) load_file_offset else section.file_offset + section.fileLoadSize(),
124 .memory_end = section.address + section.reserved_size,
125 .flags = section.kind.programFlags(),
126 .last_kind = section.kind,
127 };
128 first_load_written = true;
129 }
130 if (run) |current| {
131 writeLoadHeader(image, ehdr_size + index * phdr_size, current, options.page_size);
132 index += 1;
133 }
134 if (tlsSegment(output_sections)) |segment| {
135 const offset = ehdr_size + index * phdr_size;
136 writeU32(image, offset + 0, std.elf.PT_TLS);
137 writeU32(image, offset + 4, std.elf.PF_R);
138 writeU64(image, offset + 8, segment.offset);
139 writeU64(image, offset + 16, segment.address);
140 writeU64(image, offset + 24, segment.address);
141 writeU64(image, offset + 32, segment.file_size);
142 writeU64(image, offset + 40, segment.memory_size);
143 writeU64(image, offset + 48, segment.alignment);
144 }
145 }
146
147 pub fn firstLoadSection(output_sections: []const OutputSection) ?OutputSection {
148 for (output_sections) |section| {
149 if (section.logicalSize() == 0) continue;
150 if (section.kind.isAllocated()) return section;
151 }
152 return null;
153 }
154
155 pub fn tlsSegment(output_sections: []const OutputSection) ?TlsSegment {
156 var found = false;
157 var start_offset: u64 = 0;
158 var start_address: u64 = 0;
159 var file_end: u64 = 0;
160 var memory_end: u64 = 0;
161 var alignment: u64 = 1;
162
163 for (output_sections) |section| {
164 if (!section.kind.isTls()) continue;
165 if (section.logicalSize() == 0) continue;
166 if (!found) {
167 start_offset = section.file_offset;
168 start_address = section.address;
169 file_end = section.file_offset;
170 memory_end = section.address;
171 found = true;
172 }
173 alignment = @max(alignment, @max(section.alignment, 1));
174 if (!section.kind.isNoBits()) file_end = @max(file_end, section.file_offset + section.fileLoadSize());
175 memory_end = @max(memory_end, section.address + section.reserved_size);
176 }
177
178 if (!found) return null;
179 return .{
180 .offset = start_offset,
181 .address = start_address,
182 .file_size = file_end - start_offset,
183 .memory_size = memory_end - start_address,
184 .alignment = alignment,
185 };
186 }
187
188 pub fn tlsMemorySize(output_sections: []const OutputSection) u64 {
189 const segment = tlsSegment(output_sections) orelse return 0;
190 return segment.memory_size;
191 }
192
193 const LoadRun = struct {
194 file_offset: u64,
195 address: u64,
196 file_end: u64,
197 memory_end: u64,
198 flags: u32,
199 last_kind: OutputSectionKind,
200 };
201
202 pub fn sectionContinuesLoad(previous: OutputSectionKind, current: OutputSectionKind) bool {
203 return current.isTls() and previous.isTls();
204 }
205
206 fn writeLoadHeader(image: []u8, offset: usize, run: LoadRun, page_size: u64) void {
207 writeU32(image, offset + 0, std.elf.PT_LOAD);
208 writeU32(image, offset + 4, run.flags);
209 writeU64(image, offset + 8, run.file_offset);
210 writeU64(image, offset + 16, run.address);
211 writeU64(image, offset + 24, run.address);
212 writeU64(image, offset + 32, run.file_end - run.file_offset);
213 writeU64(image, offset + 40, run.memory_end - run.address);
214 writeU64(image, offset + 48, page_size);
215 }
216
217 fn countLoadSegments(output_sections: []const OutputSection) usize {
218 var count: usize = 0;
219 var previous_kind: ?OutputSectionKind = null;
220 for (output_sections) |section| {
221 if (section.logicalSize() == 0 or !section.kind.isAllocated()) continue;
222 if (previous_kind == null or !sectionContinuesLoad(previous_kind.?, section.kind)) count += 1;
223 previous_kind = section.kind;
224 }
225 return count;
226 }
227
228 fn noteSegment(output_sections: []const OutputSection) ?OutputSection {
229 const section = output_sections[@backingInt(OutputSectionKind.build_id_note)];
230 if (section.logicalSize() == 0) return null;
231 return section;
232 }
233
234 fn ehFrameHeaderSegment(output_sections: []const OutputSection) ?OutputSection {
235 const section = output_sections[@backingInt(OutputSectionKind.eh_frame_hdr)];
236 if (section.logicalSize() == 0) return null;
237 return section;
238 }