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 }