lib/tldr/src/formats/elf/section.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const OutputSectionKind = enum {
4 build_id_note,
5 init,
6 text,
7 fini,
8 iplt,
9 eh_frame_hdr,
10 eh_frame,
11 rodata,
12 rela_iplt,
13 preinit_array,
14 init_array,
15 fini_array,
16 data,
17 got,
18 igot,
19 tdata,
20 tbss,
21 bss,
22 debug_abbrev,
23 debug_addr,
24 debug_aranges,
25 debug_cu_index,
26 debug_frame,
27 debug_info,
28 debug_line,
29 debug_line_str,
30 debug_loc,
31 debug_loclists,
32 debug_macinfo,
33 debug_macro,
34 debug_names,
35 debug_pubnames,
36 debug_pubtypes,
37 debug_ranges,
38 debug_rnglists,
39 debug_str,
40 debug_str_offsets,
41 debug_tu_index,
42 debug_types,
43
44 pub fn name(self: OutputSectionKind) []const u8 {
45 return switch (self) {
46 .build_id_note => ".note.gnu.build-id",
47 .init => ".init",
48 .text => ".text",
49 .fini => ".fini",
50 .iplt => ".iplt",
51 .eh_frame_hdr => ".eh_frame_hdr",
52 .eh_frame => ".eh_frame",
53 .rodata => ".rodata",
54 .rela_iplt => ".rela.iplt",
55 .preinit_array => ".preinit_array",
56 .init_array => ".init_array",
57 .fini_array => ".fini_array",
58 .data => ".data",
59 .got => ".got",
60 .igot => ".igot",
61 .tdata => ".tdata",
62 .tbss => ".tbss",
63 .bss => ".bss",
64 .debug_abbrev => ".debug_abbrev",
65 .debug_addr => ".debug_addr",
66 .debug_aranges => ".debug_aranges",
67 .debug_cu_index => ".debug_cu_index",
68 .debug_frame => ".debug_frame",
69 .debug_info => ".debug_info",
70 .debug_line => ".debug_line",
71 .debug_line_str => ".debug_line_str",
72 .debug_loc => ".debug_loc",
73 .debug_loclists => ".debug_loclists",
74 .debug_macinfo => ".debug_macinfo",
75 .debug_macro => ".debug_macro",
76 .debug_names => ".debug_names",
77 .debug_pubnames => ".debug_pubnames",
78 .debug_pubtypes => ".debug_pubtypes",
79 .debug_ranges => ".debug_ranges",
80 .debug_rnglists => ".debug_rnglists",
81 .debug_str => ".debug_str",
82 .debug_str_offsets => ".debug_str_offsets",
83 .debug_tu_index => ".debug_tu_index",
84 .debug_types => ".debug_types",
85 };
86 }
87
88 pub fn flags(self: OutputSectionKind) u64 {
89 return switch (self) {
90 .build_id_note => std.elf.SHF_ALLOC,
91 .init, .text, .fini, .iplt => std.elf.SHF_ALLOC | std.elf.SHF_EXECINSTR,
92 .eh_frame_hdr, .eh_frame, .rodata, .rela_iplt => std.elf.SHF_ALLOC,
93 .preinit_array, .init_array, .fini_array, .data, .got, .igot => std.elf.SHF_ALLOC | std.elf.SHF_WRITE,
94 .tdata, .tbss => std.elf.SHF_ALLOC | std.elf.SHF_WRITE | std.elf.SHF_TLS,
95 .bss => std.elf.SHF_ALLOC | std.elf.SHF_WRITE,
96 .debug_abbrev,
97 .debug_addr,
98 .debug_aranges,
99 .debug_cu_index,
100 .debug_frame,
101 .debug_info,
102 .debug_line,
103 .debug_line_str,
104 .debug_loc,
105 .debug_loclists,
106 .debug_macinfo,
107 .debug_macro,
108 .debug_names,
109 .debug_pubnames,
110 .debug_pubtypes,
111 .debug_ranges,
112 .debug_rnglists,
113 .debug_str,
114 .debug_str_offsets,
115 .debug_tu_index,
116 .debug_types,
117 => 0,
118 };
119 }
120
121 pub fn sectionType(self: OutputSectionKind) u32 {
122 return switch (self) {
123 .build_id_note => std.elf.SHT_NOTE,
124 .rela_iplt => std.elf.SHT_RELA,
125 .preinit_array => std.elf.SHT_PREINIT_ARRAY,
126 .init_array => std.elf.SHT_INIT_ARRAY,
127 .fini_array => std.elf.SHT_FINI_ARRAY,
128 .init,
129 .text,
130 .fini,
131 .iplt,
132 .eh_frame_hdr,
133 .eh_frame,
134 .rodata,
135 .data,
136 .got,
137 .igot,
138 .tdata,
139 .debug_abbrev,
140 .debug_addr,
141 .debug_aranges,
142 .debug_cu_index,
143 .debug_frame,
144 .debug_info,
145 .debug_line,
146 .debug_line_str,
147 .debug_loc,
148 .debug_loclists,
149 .debug_macinfo,
150 .debug_macro,
151 .debug_names,
152 .debug_pubnames,
153 .debug_pubtypes,
154 .debug_ranges,
155 .debug_rnglists,
156 .debug_str,
157 .debug_str_offsets,
158 .debug_tu_index,
159 .debug_types,
160 => std.elf.SHT_PROGBITS,
161 .tbss, .bss => std.elf.SHT_NOBITS,
162 };
163 }
164
165 pub fn programFlags(self: OutputSectionKind) u32 {
166 return switch (self) {
167 .build_id_note => std.elf.PF_R,
168 .init, .text, .fini, .iplt => std.elf.PF_R | std.elf.PF_X,
169 .eh_frame_hdr, .eh_frame, .rodata, .rela_iplt => std.elf.PF_R,
170 .preinit_array, .init_array, .fini_array, .data, .got, .igot, .tdata, .tbss, .bss => std.elf.PF_R | std.elf.PF_W,
171 .debug_abbrev,
172 .debug_addr,
173 .debug_aranges,
174 .debug_cu_index,
175 .debug_frame,
176 .debug_info,
177 .debug_line,
178 .debug_line_str,
179 .debug_loc,
180 .debug_loclists,
181 .debug_macinfo,
182 .debug_macro,
183 .debug_names,
184 .debug_pubnames,
185 .debug_pubtypes,
186 .debug_ranges,
187 .debug_rnglists,
188 .debug_str,
189 .debug_str_offsets,
190 .debug_tu_index,
191 .debug_types,
192 => 0,
193 };
194 }
195
196 pub fn isAllocated(self: OutputSectionKind) bool {
197 return (self.flags() & std.elf.SHF_ALLOC) != 0;
198 }
199
200 pub fn isNoBits(self: OutputSectionKind) bool {
201 return self.sectionType() == std.elf.SHT_NOBITS;
202 }
203
204 pub fn isTls(self: OutputSectionKind) bool {
205 return self == .tdata or self == .tbss;
206 }
207
208 pub fn entrySize(self: OutputSectionKind) u64 {
209 return switch (self) {
210 .rela_iplt => 24,
211 else => 0,
212 };
213 }
214 };
215
216 pub const output_section_count = @typeInfo(OutputSectionKind).@"enum".field_names.len;
217
218 pub fn indexForAllocatedNamed(section: anytype, name: []const u8) usize {
219 const named_alloc = (section.flags & std.elf.SHF_TLS) == 0 and section.section_type != std.elf.SHT_NOBITS;
220 if (named_alloc) {
221 if (name.len > 1 and name[0] == '.') {
222 switch (name[1]) {
223 'e' => if (std.mem.eql(u8, name, ".eh_frame")) return @backingInt(OutputSectionKind.eh_frame),
224 'f' => {
225 if ((section.flags & std.elf.SHF_EXECINSTR) != 0 and std.mem.eql(u8, name, ".fini")) return @backingInt(OutputSectionKind.fini);
226 if (std.mem.eql(u8, name, ".fini_array") or std.mem.startsWith(u8, name, ".fini_array.")) return @backingInt(OutputSectionKind.fini_array);
227 },
228 'i' => {
229 if ((section.flags & std.elf.SHF_EXECINSTR) != 0 and std.mem.eql(u8, name, ".init")) return @backingInt(OutputSectionKind.init);
230 if (std.mem.eql(u8, name, ".init_array") or std.mem.startsWith(u8, name, ".init_array.")) return @backingInt(OutputSectionKind.init_array);
231 },
232 'p' => if (std.mem.eql(u8, name, ".preinit_array")) return @backingInt(OutputSectionKind.preinit_array),
233 else => {},
234 }
235 }
236 if (section.section_type == std.elf.SHT_X86_64_UNWIND) return @backingInt(OutputSectionKind.eh_frame);
237 }
238 return indexForAllocated(section);
239 }
240
241 pub fn indexForAllocated(section: anytype) usize {
242 if ((section.flags & std.elf.SHF_TLS) != 0) {
243 if (section.section_type == std.elf.SHT_NOBITS) return @backingInt(OutputSectionKind.tbss);
244 return @backingInt(OutputSectionKind.tdata);
245 }
246 if (section.section_type == std.elf.SHT_NOBITS) return @backingInt(OutputSectionKind.bss);
247 if ((section.flags & std.elf.SHF_EXECINSTR) != 0) return @backingInt(OutputSectionKind.text);
248 if ((section.flags & std.elf.SHF_WRITE) != 0) return @backingInt(OutputSectionKind.data);
249 return @backingInt(OutputSectionKind.rodata);
250 }
251
252 pub fn isAllocatedPayloadType(section_type: u32) bool {
253 return switch (section_type) {
254 std.elf.SHT_PROGBITS,
255 std.elf.SHT_NOBITS,
256 std.elf.SHT_NOTE,
257 std.elf.SHT_PREINIT_ARRAY,
258 std.elf.SHT_INIT_ARRAY,
259 std.elf.SHT_FINI_ARRAY,
260 std.elf.SHT_X86_64_UNWIND,
261 => true,
262 else => false,
263 };
264 }
265
266 pub fn debugOutputIndexForName(name: []const u8) ?usize {
267 const debug_prefix = ".debug_";
268 if (name.len <= debug_prefix.len) return null;
269 if (name[0] != '.' or name[1] != 'd') return null;
270 if (!std.mem.startsWith(u8, name, debug_prefix)) return null;
271 const suffix = name[debug_prefix.len..];
272 switch (suffix[0]) {
273 'a' => {
274 if (std.mem.eql(u8, suffix, "abbrev")) return @backingInt(OutputSectionKind.debug_abbrev);
275 if (std.mem.eql(u8, suffix, "addr")) return @backingInt(OutputSectionKind.debug_addr);
276 if (std.mem.eql(u8, suffix, "aranges")) return @backingInt(OutputSectionKind.debug_aranges);
277 },
278 'c' => if (std.mem.eql(u8, suffix, "cu_index")) return @backingInt(OutputSectionKind.debug_cu_index),
279 'f' => if (std.mem.eql(u8, suffix, "frame")) return @backingInt(OutputSectionKind.debug_frame),
280 'i' => if (std.mem.eql(u8, suffix, "info")) return @backingInt(OutputSectionKind.debug_info),
281 'l' => {
282 if (std.mem.eql(u8, suffix, "line")) return @backingInt(OutputSectionKind.debug_line);
283 if (std.mem.eql(u8, suffix, "line_str")) return @backingInt(OutputSectionKind.debug_line_str);
284 if (std.mem.eql(u8, suffix, "loc")) return @backingInt(OutputSectionKind.debug_loc);
285 if (std.mem.eql(u8, suffix, "loclists")) return @backingInt(OutputSectionKind.debug_loclists);
286 },
287 'm' => {
288 if (std.mem.eql(u8, suffix, "macinfo")) return @backingInt(OutputSectionKind.debug_macinfo);
289 if (std.mem.eql(u8, suffix, "macro")) return @backingInt(OutputSectionKind.debug_macro);
290 },
291 'n' => if (std.mem.eql(u8, suffix, "names")) return @backingInt(OutputSectionKind.debug_names),
292 'p' => {
293 if (std.mem.eql(u8, suffix, "pubnames")) return @backingInt(OutputSectionKind.debug_pubnames);
294 if (std.mem.eql(u8, suffix, "pubtypes")) return @backingInt(OutputSectionKind.debug_pubtypes);
295 },
296 'r' => {
297 if (std.mem.eql(u8, suffix, "ranges")) return @backingInt(OutputSectionKind.debug_ranges);
298 if (std.mem.eql(u8, suffix, "rnglists")) return @backingInt(OutputSectionKind.debug_rnglists);
299 },
300 's' => {
301 if (std.mem.eql(u8, suffix, "str")) return @backingInt(OutputSectionKind.debug_str);
302 if (std.mem.eql(u8, suffix, "str_offsets")) return @backingInt(OutputSectionKind.debug_str_offsets);
303 },
304 't' => {
305 if (std.mem.eql(u8, suffix, "tu_index")) return @backingInt(OutputSectionKind.debug_tu_index);
306 if (std.mem.eql(u8, suffix, "types")) return @backingInt(OutputSectionKind.debug_types);
307 },
308 else => {},
309 }
310 return null;
311 }
312
313 const AllocatedSectionFixture = struct {
314 flags: u64,
315 section_type: u32,
316 };
317
318 test "ELF output section classifies allocated special names" {
319 const alloc = std.elf.SHF_ALLOC;
320 const exec = std.elf.SHF_ALLOC | std.elf.SHF_EXECINSTR;
321 const writable = std.elf.SHF_ALLOC | std.elf.SHF_WRITE;
322 const tls = std.elf.SHF_ALLOC | std.elf.SHF_TLS;
323 try std.testing.expectEqual(@backingInt(OutputSectionKind.init), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = exec, .section_type = std.elf.SHT_PROGBITS }, ".init"));
324 try std.testing.expectEqual(@backingInt(OutputSectionKind.fini), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = exec, .section_type = std.elf.SHT_PROGBITS }, ".fini"));
325 try std.testing.expectEqual(@backingInt(OutputSectionKind.preinit_array), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = writable, .section_type = std.elf.SHT_PREINIT_ARRAY }, ".preinit_array"));
326 try std.testing.expectEqual(@backingInt(OutputSectionKind.init_array), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = writable, .section_type = std.elf.SHT_INIT_ARRAY }, ".init_array.100"));
327 try std.testing.expectEqual(@backingInt(OutputSectionKind.fini_array), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = writable, .section_type = std.elf.SHT_FINI_ARRAY }, ".fini_array"));
328 try std.testing.expectEqual(@backingInt(OutputSectionKind.eh_frame), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = alloc, .section_type = std.elf.SHT_PROGBITS }, ".eh_frame"));
329 try std.testing.expectEqual(@backingInt(OutputSectionKind.eh_frame), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = alloc, .section_type = std.elf.SHT_X86_64_UNWIND }, ".gcc_except_table"));
330 try std.testing.expectEqual(@backingInt(OutputSectionKind.tdata), indexForAllocatedNamed(AllocatedSectionFixture{ .flags = tls, .section_type = std.elf.SHT_PROGBITS }, ".init_array"));
331 }