authorgravatar for julien.philippon@epitech.euErsikan <julien.philippon@epitech.eu> 2021-03-12 23:46:51+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-21 11:33:12-08:00
loge15a267668bae168f2bba06cc3706c54bc062522
treeb84e6f17eda1e677dbb7a9adcf630741ae94c05b
parent44061cd760cba603ddc2afa10d882ce71c5d389b

elf: Put constant data in the .rodata section

Allocate a new program header and a new section to accomodate the read-only data section ".rodata". Separate TextBlock into multiple TextBlockList, to separate decl in different sections. If a Decl is not a function, it is added to the .rodata section.

1 files changed, 106 insertions(+), 40 deletions(-)

src/link/Elf.zig+106-40
......@@ -54,13 +54,14 @@ phdr_load_re_index: ?u16 = null,
5454/// The index into the program headers of the global offset table.
5555/// It needs PT_LOAD and Read flags.
5656phdr_got_index: ?u16 = null,
57/// The index into the program headers of a PT_LOAD program header with Read flag
58phdr_load_ro_index: ?u16 = null,
5759entry_addr: ?u64 = null,
5860
5961debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
6062shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
6163shstrtab_index: ?u16 = null,
6264
63text_section_index: ?u16 = null,
6465symtab_section_index: ?u16 = null,
6566got_section_index: ?u16 = null,
6667debug_info_section_index: ?u16 = null,
......@@ -115,8 +116,8 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},
115116/// overcapacity can be negative. A simple way to have negative overcapacity is to
116117/// allocate a fresh text block, which will have ideal capacity, and then grow it
117118/// by 1 byte. It will then have -1 overcapacity.
118text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
119last_text_block: ?*TextBlock = null,
119text_block_list: TextBlockList = .{},
120rodata_block_list: TextBlockList = .{},
120121
121122/// A list of `SrcFn` whose Line Number Programs have surplus capacity.
122123/// This is the same concept as `text_block_free_list`; see those doc comments.
......@@ -204,6 +205,14 @@ pub const TextBlock = struct {
204205 }
205206};
206207
208/// A list of text blocks in a specific section
209const TextBlockList = struct {
210 free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
211 last_block: ?*TextBlock = null,
212 phdr_index: ?u16 = null,
213 section_index: ?u16 = null,
214};
215
207216pub const Export = struct {
208217 sym_index: ?u32 = null,
209218};
......@@ -314,7 +323,8 @@ pub fn deinit(self: *Elf) void {
314323 self.global_symbol_free_list.deinit(self.base.allocator);
315324 self.local_symbol_free_list.deinit(self.base.allocator);
316325 self.offset_table_free_list.deinit(self.base.allocator);
317 self.text_block_free_list.deinit(self.base.allocator);
326 self.text_block_list.free_list.deinit(self.base.allocator);
327 self.rodata_block_list.free_list.deinit(self.base.allocator);
318328 self.dbg_line_fn_free_list.deinit(self.base.allocator);
319329 self.dbg_info_decl_free_list.deinit(self.base.allocator);
320330 self.offset_table.deinit(self.base.allocator);
......@@ -450,6 +460,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
450460 const ptr_size: u8 = self.ptrWidthBytes();
451461 if (self.phdr_load_re_index == null) {
452462 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
463 self.text_block_list.phdr_index = self.phdr_load_re_index;
453464 const file_size = self.base.options.program_code_size_hint;
454465 const p_align = 0x1000;
455466 const off = self.findFreeSpace(file_size, p_align);
......@@ -492,6 +503,29 @@ pub fn populateMissingMetadata(self: *Elf) !void {
492503 });
493504 self.phdr_table_dirty = true;
494505 }
506 if (self.phdr_load_ro_index == null) {
507 self.phdr_load_ro_index = @intCast(u16, self.program_headers.items.len);
508 self.rodata_block_list.phdr_index = self.phdr_load_ro_index;
509 // TODO Find a hint about how much data need to be in rodata ?
510 const file_size = 1024;
511 // Same reason as for GOT
512 const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
513 const off = self.findFreeSpace(file_size, p_align);
514 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
515 // TODO Same as for GOT
516 const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x6000000 else 0xD000;
517 try self.program_headers.append(self.base.allocator, .{
518 .p_type = elf.PT_LOAD,
519 .p_offset = off,
520 .p_filesz = file_size,
521 .p_vaddr = rodata_addr,
522 .p_paddr = rodata_addr,
523 .p_memsz = file_size,
524 .p_align = p_align,
525 .p_flags = elf.PF_R,
526 });
527 self.phdr_table_dirty = true;
528 }
495529 if (self.shstrtab_index == null) {
496530 self.shstrtab_index = @intCast(u16, self.sections.items.len);
497531 assert(self.shstrtab.items.len == 0);
......@@ -513,8 +547,8 @@ pub fn populateMissingMetadata(self: *Elf) !void {
513547 self.shstrtab_dirty = true;
514548 self.shdr_table_dirty = true;
515549 }
516 if (self.text_section_index == null) {
517 self.text_section_index = @intCast(u16, self.sections.items.len);
550 if (self.text_block_list.section_index == null) {
551 self.text_block_list.section_index = @intCast(u16, self.sections.items.len);
518552 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
519553
520554 try self.sections.append(self.base.allocator, .{
......@@ -549,6 +583,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {
549583 });
550584 self.shdr_table_dirty = true;
551585 }
586 if (self.rodata_block_list.section_index == null) {
587 self.rodata_block_list.section_index = @intCast(u16, self.sections.items.len);
588 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];
589
590 try self.sections.append(self.base.allocator, .{
591 .sh_name = try self.makeString(".rodata"),
592 .sh_type = elf.SHT_PROGBITS,
593 .sh_flags = elf.SHF_ALLOC,
594 .sh_addr = phdr.p_vaddr,
595 .sh_offset = phdr.p_offset,
596 .sh_size = phdr.p_filesz,
597 .sh_link = 0,
598 .sh_info = 0,
599 .sh_addralign = phdr.p_align,
600 .sh_entsize = 0,
601 });
602 self.shdr_table_dirty = true;
603 }
552604 if (self.symtab_section_index == null) {
553605 self.symtab_section_index = @intCast(u16, self.sections.items.len);
554606 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
......@@ -1927,17 +1979,17 @@ fn writeElfHeader(self: *Elf) !void {
19271979 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
19281980}
19291981
1930fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
1982fn freeTextBlock(self: *Elf, block_list: *TextBlockList, text_block: *TextBlock) void {
19311983 var already_have_free_list_node = false;
19321984 {
19331985 var i: usize = 0;
19341986 // TODO turn text_block_free_list into a hash map
1935 while (i < self.text_block_free_list.items.len) {
1936 if (self.text_block_free_list.items[i] == text_block) {
1937 _ = self.text_block_free_list.swapRemove(i);
1987 while (i < block_list.free_list.items.len) {
1988 if (block_list.free_list.items[i] == text_block) {
1989 _ = block_list.free_list.swapRemove(i);
19381990 continue;
19391991 }
1940 if (self.text_block_free_list.items[i] == text_block.prev) {
1992 if (block_list.free_list.items[i] == text_block.prev) {
19411993 already_have_free_list_node = true;
19421994 }
19431995 i += 1;
......@@ -1945,9 +1997,9 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
19451997 }
19461998 // TODO process free list for dbg info just like we do above for vaddrs
19471999
1948 if (self.last_text_block == text_block) {
2000 if (block_list.last_block == text_block) {
19492001 // TODO shrink the .text section size here
1950 self.last_text_block = text_block.prev;
2002 block_list.last_block = text_block.prev;
19512003 }
19522004 if (self.dbg_info_decl_first == text_block) {
19532005 self.dbg_info_decl_first = text_block.dbg_info_next;
......@@ -1963,7 +2015,7 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
19632015 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
19642016 // The free list is heuristics, it doesn't have to be perfect, so we can
19652017 // ignore the OOM here.
1966 self.text_block_free_list.append(self.base.allocator, prev) catch {};
2018 block_list.free_list.append(self.base.allocator, prev) catch {};
19672019 }
19682020 } else {
19692021 text_block.prev = null;
......@@ -1990,25 +2042,24 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
19902042 }
19912043}
19922044
1993fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
2045fn shrinkTextBlock(self: *Elf, block_list: *TextBlockList, text_block: *TextBlock, new_block_size: u64) void {
19942046 _ = self;
2047 _ = block_list;
19952048 _ = text_block;
19962049 _ = new_block_size;
1997 // TODO check the new capacity, and if it crosses the size threshold into a big enough
1998 // capacity, insert a free list node for it.
19992050}
20002051
2001fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
2052fn growTextBlock(self: *Elf, block_list: *TextBlockList, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
20022053 const sym = self.local_symbols.items[text_block.local_sym_index];
20032054 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
20042055 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
20052056 if (!need_realloc) return sym.st_value;
2006 return self.allocateTextBlock(text_block, new_block_size, alignment);
2057 return self.allocateTextBlock(block_list, text_block, new_block_size, alignment);
20072058}
20082059
2009fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
2010 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
2011 const shdr = &self.sections.items[self.text_section_index.?];
2060fn allocateTextBlock(self: *Elf, block_list: *TextBlockList, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
2061 const phdr = &self.program_headers.items[block_list.phdr_index.?];
2062 const shdr = &self.sections.items[block_list.section_index.?];
20122063 const new_block_ideal_capacity = padToIdeal(new_block_size);
20132064
20142065 // We use these to indicate our intention to update metadata, placing the new block,
......@@ -2023,8 +2074,8 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
20232074 // The list is unordered. We'll just take the first thing that works.
20242075 const vaddr = blk: {
20252076 var i: usize = 0;
2026 while (i < self.text_block_free_list.items.len) {
2027 const big_block = self.text_block_free_list.items[i];
2077 while (i < block_list.free_list.items.len) {
2078 const big_block = block_list.free_list.items[i];
20282079 // We now have a pointer to a live text block that has too much capacity.
20292080 // Is it enough that we could fit this new text block?
20302081 const sym = self.local_symbols.items[big_block.local_sym_index];
......@@ -2039,7 +2090,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
20392090 // should be deleted because the block that it points to has grown to take up
20402091 // more of the extra capacity.
20412092 if (!big_block.freeListEligible(self.*)) {
2042 _ = self.text_block_free_list.swapRemove(i);
2093 _ = block_list.free_list.swapRemove(i);
20432094 } else {
20442095 i += 1;
20452096 }
......@@ -2057,7 +2108,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
20572108 free_list_removal = i;
20582109 }
20592110 break :blk new_start_vaddr;
2060 } else if (self.last_text_block) |last| {
2111 } else if (block_list.last_block) |last| {
20612112 const sym = self.local_symbols.items[last.local_sym_index];
20622113 const ideal_capacity = padToIdeal(sym.st_size);
20632114 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
......@@ -2077,7 +2128,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
20772128 if (needed_size > text_capacity) {
20782129 // Must move the entire text section.
20792130 const new_offset = self.findFreeSpace(needed_size, 0x1000);
2080 const text_size = if (self.last_text_block) |last| blk: {
2131 const text_size = if (block_list.last_block) |last| blk: {
20812132 const sym = self.local_symbols.items[last.local_sym_index];
20822133 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
20832134 } else 0;
......@@ -2086,7 +2137,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
20862137 shdr.sh_offset = new_offset;
20872138 phdr.p_offset = new_offset;
20882139 }
2089 self.last_text_block = text_block;
2140 block_list.last_block = text_block;
20902141
20912142 shdr.sh_size = needed_size;
20922143 phdr.p_memsz = needed_size;
......@@ -2124,11 +2175,19 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
21242175 text_block.next = null;
21252176 }
21262177 if (free_list_removal) |i| {
2127 _ = self.text_block_free_list.swapRemove(i);
2178 _ = block_list.free_list.swapRemove(i);
21282179 }
21292180 return vaddr;
21302181}
21312182
2183/// Get the block list corresponding to a specific decl
2184/// For example, if the decl is a function, it returns the list of the section .text
2185fn getDeclBlockList(self: *Elf, decl: *const Module.Decl) *TextBlockList {
2186 // const is_fn = decl.val.tag() == .function;
2187 const is_fn = decl.ty.zigTypeTag() == .Fn;
2188 return if (is_fn) &self.text_block_list else &self.rodata_block_list;
2189}
2190
21322191pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
21332192 if (self.llvm_object) |_| return;
21342193
......@@ -2172,8 +2231,10 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
21722231 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
21732232 }
21742233
2234 const block_list = self.getDeclBlockList(decl);
2235
21752236 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2176 self.freeTextBlock(&decl.link.elf);
2237 self.freeTextBlock(block_list, &decl.link.elf);
21772238 if (decl.link.elf.local_sym_index != 0) {
21782239 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
21792240 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
......@@ -2216,6 +2277,8 @@ fn deinitRelocs(gpa: Allocator, table: *File.DbgInfoTypeRelocsTable) void {
22162277fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
22172278 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
22182279
2280 const block_list = self.getDeclBlockList(decl);
2281
22192282 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
22202283 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
22212284 if (local_sym.st_size != 0) {
......@@ -2223,7 +2286,7 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8
22232286 const need_realloc = code.len > capacity or
22242287 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
22252288 if (need_realloc) {
2226 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
2289 const vaddr = try self.growTextBlock(block_list, &decl.link.elf, code.len, required_alignment);
22272290 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, local_sym.st_value, vaddr });
22282291 if (vaddr != local_sym.st_value) {
22292292 local_sym.st_value = vaddr;
......@@ -2233,27 +2296,28 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8
22332296 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
22342297 }
22352298 } else if (code.len < local_sym.st_size) {
2236 self.shrinkTextBlock(&decl.link.elf, code.len);
2299 self.shrinkTextBlock(block_list, &decl.link.elf, code.len);
22372300 }
22382301 local_sym.st_size = code.len;
22392302 local_sym.st_name = try self.updateString(local_sym.st_name, mem.sliceTo(decl.name, 0));
22402303 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
22412304 local_sym.st_other = 0;
2242 local_sym.st_shndx = self.text_section_index.?;
2305 local_sym.st_shndx = block_list.section_index.?;
22432306 // TODO this write could be avoided if no fields of the symbol were changed.
22442307 try self.writeSymbol(decl.link.elf.local_sym_index);
22452308 } else {
22462309 const decl_name = mem.sliceTo(decl.name, 0);
22472310 const name_str_index = try self.makeString(decl_name);
2248 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
2311 const vaddr = try self.allocateTextBlock(block_list, &decl.link.elf, code.len, required_alignment);
2312 errdefer self.freeTextBlock(block_list, &decl.link.elf);
22492313 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
2250 errdefer self.freeTextBlock(&decl.link.elf);
2314 errdefer self.freeTextBlock(block_list, &decl.link.elf);
22512315
22522316 local_sym.* = .{
22532317 .st_name = name_str_index,
22542318 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
22552319 .st_other = 0,
2256 .st_shndx = self.text_section_index.?,
2320 .st_shndx = block_list.section_index.?,
22572321 .st_value = vaddr,
22582322 .st_size = code.len,
22592323 };
......@@ -2263,8 +2327,8 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8
22632327 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
22642328 }
22652329
2266 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
2267 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
2330 const section_offset = local_sym.st_value - self.program_headers.items[block_list.phdr_index.?].p_vaddr;
2331 const file_offset = self.sections.items[block_list.section_index.?].sh_offset + section_offset;
22682332 try self.base.file.?.pwriteAll(code, file_offset);
22692333
22702334 return local_sym;
......@@ -2772,6 +2836,8 @@ pub fn updateDeclExports(
27722836 if (decl.link.elf.local_sym_index == 0) return;
27732837 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
27742838
2839 const block_list = self.getDeclBlockList(decl);
2840
27752841 for (exports) |exp| {
27762842 if (exp.options.section) |section_name| {
27772843 if (!mem.eql(u8, section_name, ".text")) {
......@@ -2808,7 +2874,7 @@ pub fn updateDeclExports(
28082874 .st_name = try self.updateString(sym.st_name, exp.options.name),
28092875 .st_info = (stb_bits << 4) | stt_bits,
28102876 .st_other = 0,
2811 .st_shndx = self.text_section_index.?,
2877 .st_shndx = block_list.section_index.?,
28122878 .st_value = decl_sym.st_value,
28132879 .st_size = decl_sym.st_size,
28142880 };
......@@ -2822,7 +2888,7 @@ pub fn updateDeclExports(
28222888 .st_name = name,
28232889 .st_info = (stb_bits << 4) | stt_bits,
28242890 .st_other = 0,
2825 .st_shndx = self.text_section_index.?,
2891 .st_shndx = block_list.section_index.?,
28262892 .st_value = decl_sym.st_value,
28272893 .st_size = decl_sym.st_size,
28282894 };