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 }