authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-03 04:55:15-04:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-03 17:45:17+02:00
logf0d13489f819ecd1c0d1c23914127c225f507241
treea14472bbe681872457f85811ebecd7bdf671b945
parentcd24ab7f6e56c8365896a9b2dfc506a787346ecb

Elf: add program headers for the program header table


6 files changed, 184 insertions(+), 128 deletions(-)

lib/std/start.zig-1
......@@ -19,7 +19,6 @@ const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start";
1919// self-hosted is capable enough to handle all of the real start.zig logic.
2020pub const simplified_logic =
2121 builtin.zig_backend == .stage2_wasm or
22 builtin.zig_backend == .stage2_x86_64 or
2322 builtin.zig_backend == .stage2_x86 or
2423 builtin.zig_backend == .stage2_aarch64 or
2524 builtin.zig_backend == .stage2_arm or
src/link/Elf.zig+180-123
......@@ -106,7 +106,12 @@ shdr_table_offset: ?u64 = null,
106106/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
107107/// Same order as in the file.
108108program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
109phdr_table_offset: ?u64 = null,
109/// The index into the program headers of the PT_PHDR program header
110phdr_table_index: ?u16 = null,
111/// The index into the program headers of the PT_LOAD program header containing the phdr
112/// Most linkers would merge this with phdr_load_ro_index,
113/// but hot swap means we can't ensure they are consecutive.
114phdr_table_load_index: ?u16 = null,
110115/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
111116phdr_load_re_index: ?u16 = null,
112117/// The index into the program headers of the global offset table.
......@@ -386,9 +391,10 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
386391
387392 const end = start + padToIdeal(size);
388393
389 if (self.shdr_table_offset) |off| {
390 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
391 const tight_size = self.sections.slice().len * shdr_size;
394 if (self.phdr_table_index) |index| {
395 const off = self.program_headers.items[index].p_offset;
396 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
397 const tight_size = self.program_headers.items.len * phdr_size;
392398 const increased_size = padToIdeal(tight_size);
393399 const test_end = off + increased_size;
394400 if (end > off and start < test_end) {
......@@ -396,9 +402,9 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
396402 }
397403 }
398404
399 if (self.phdr_table_offset) |off| {
400 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
401 const tight_size = self.sections.slice().len * phdr_size;
405 if (self.shdr_table_offset) |off| {
406 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
407 const tight_size = self.sections.slice().len * shdr_size;
402408 const increased_size = padToIdeal(tight_size);
403409 const test_end = off + increased_size;
404410 if (end > off and start < test_end) {
......@@ -427,10 +433,11 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
427433 if (start == 0)
428434 return 0;
429435 var min_pos: u64 = std.math.maxInt(u64);
430 if (self.shdr_table_offset) |off| {
436 if (self.phdr_table_index) |index| {
437 const off = self.program_headers.items[index].p_offset;
431438 if (off > start and off < min_pos) min_pos = off;
432439 }
433 if (self.phdr_table_offset) |off| {
440 if (self.shdr_table_offset) |off| {
434441 if (off > start and off < min_pos) min_pos = off;
435442 }
436443 for (self.sections.items(.shdr)) |section| {
......@@ -462,96 +469,146 @@ pub fn populateMissingMetadata(self: *Elf) !void {
462469 };
463470 const ptr_size: u8 = self.ptrWidthBytes();
464471
465 if (self.phdr_load_re_index == null) {
466 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
467 const file_size = self.base.options.program_code_size_hint;
468 const p_align = self.page_size;
469 const off = self.findFreeSpace(file_size, p_align);
470 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
471 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
472 try self.program_headers.append(gpa, .{
473 .p_type = elf.PT_LOAD,
474 .p_offset = off,
475 .p_filesz = file_size,
476 .p_vaddr = entry_addr,
477 .p_paddr = entry_addr,
478 .p_memsz = file_size,
479 .p_align = p_align,
480 .p_flags = elf.PF_X | elf.PF_R | elf.PF_W,
481 });
482 self.entry_addr = null;
483 self.phdr_table_dirty = true;
484 }
485
486 if (self.phdr_got_index == null) {
487 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
488 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
489 // We really only need ptr alignment but since we are using PROGBITS, linux requires
490 // page align.
491 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
492 const off = self.findFreeSpace(file_size, p_align);
493 log.debug("found PT_LOAD GOT free space 0x{x} to 0x{x}", .{ off, off + file_size });
494 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
495 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
496 // else in virtual memory.
497 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
498 try self.program_headers.append(gpa, .{
499 .p_type = elf.PT_LOAD,
500 .p_offset = off,
501 .p_filesz = file_size,
502 .p_vaddr = got_addr,
503 .p_paddr = got_addr,
504 .p_memsz = file_size,
505 .p_align = p_align,
506 .p_flags = elf.PF_R | elf.PF_W,
507 });
508 self.phdr_table_dirty = true;
509 }
510
511 if (self.phdr_load_ro_index == null) {
512 self.phdr_load_ro_index = @intCast(u16, self.program_headers.items.len);
513 // TODO Find a hint about how much data need to be in rodata ?
514 const file_size = 1024;
515 // Same reason as for GOT
516 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
517 const off = self.findFreeSpace(file_size, p_align);
518 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
519 // TODO Same as for GOT
520 const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000;
521 try self.program_headers.append(gpa, .{
522 .p_type = elf.PT_LOAD,
523 .p_offset = off,
524 .p_filesz = file_size,
525 .p_vaddr = rodata_addr,
526 .p_paddr = rodata_addr,
527 .p_memsz = file_size,
528 .p_align = p_align,
529 .p_flags = elf.PF_R | elf.PF_W,
530 });
531 self.phdr_table_dirty = true;
532 }
533
534 if (self.phdr_load_rw_index == null) {
535 self.phdr_load_rw_index = @intCast(u16, self.program_headers.items.len);
536 // TODO Find a hint about how much data need to be in data ?
537 const file_size = 1024;
538 // Same reason as for GOT
539 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
540 const off = self.findFreeSpace(file_size, p_align);
541 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
542 // TODO Same as for GOT
543 const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000;
544 try self.program_headers.append(gpa, .{
545 .p_type = elf.PT_LOAD,
546 .p_offset = off,
547 .p_filesz = file_size,
548 .p_vaddr = rwdata_addr,
549 .p_paddr = rwdata_addr,
550 .p_memsz = file_size,
551 .p_align = p_align,
552 .p_flags = elf.PF_R | elf.PF_W,
553 });
554 self.phdr_table_dirty = true;
472 { // Program Headers
473 var new_phdr_table_index: ?u16 = null;
474 if (self.phdr_table_index == null) {
475 new_phdr_table_index = @intCast(u16, self.program_headers.items.len);
476 const phalign: u16 = switch (self.ptr_width) {
477 .p32 => @alignOf(elf.Elf32_Phdr),
478 .p64 => @alignOf(elf.Elf64_Phdr),
479 };
480 try self.program_headers.append(gpa, .{
481 .p_type = elf.PT_PHDR,
482 .p_offset = undefined,
483 .p_filesz = undefined,
484 .p_vaddr = undefined,
485 .p_paddr = undefined,
486 .p_memsz = undefined,
487 .p_align = phalign,
488 .p_flags = elf.PF_R,
489 });
490 self.phdr_table_dirty = true;
491 }
492
493 if (self.phdr_table_load_index == null) {
494 self.phdr_table_load_index = @intCast(u16, self.program_headers.items.len);
495 // TODO Same as for GOT
496 const phdr_addr: u64 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x1000000 else 0x1000;
497 try self.program_headers.append(gpa, .{
498 .p_type = elf.PT_LOAD,
499 .p_offset = undefined,
500 .p_filesz = undefined,
501 .p_vaddr = phdr_addr,
502 .p_paddr = phdr_addr,
503 .p_memsz = undefined,
504 .p_align = self.page_size,
505 .p_flags = elf.PF_R,
506 });
507 self.phdr_table_dirty = true;
508 }
509
510 if (self.phdr_load_re_index == null) {
511 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
512 const file_size = self.base.options.program_code_size_hint;
513 const p_align = self.page_size;
514 const off = self.findFreeSpace(file_size, p_align);
515 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
516 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
517 try self.program_headers.append(gpa, .{
518 .p_type = elf.PT_LOAD,
519 .p_offset = off,
520 .p_filesz = file_size,
521 .p_vaddr = entry_addr,
522 .p_paddr = entry_addr,
523 .p_memsz = file_size,
524 .p_align = p_align,
525 .p_flags = elf.PF_X | elf.PF_R | elf.PF_W,
526 });
527 self.entry_addr = null;
528 self.phdr_table_dirty = true;
529 }
530
531 if (self.phdr_got_index == null) {
532 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
533 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
534 // We really only need ptr alignment but since we are using PROGBITS, linux requires
535 // page align.
536 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
537 const off = self.findFreeSpace(file_size, p_align);
538 log.debug("found PT_LOAD GOT free space 0x{x} to 0x{x}", .{ off, off + file_size });
539 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
540 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
541 // else in virtual memory.
542 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
543 try self.program_headers.append(gpa, .{
544 .p_type = elf.PT_LOAD,
545 .p_offset = off,
546 .p_filesz = file_size,
547 .p_vaddr = got_addr,
548 .p_paddr = got_addr,
549 .p_memsz = file_size,
550 .p_align = p_align,
551 .p_flags = elf.PF_R | elf.PF_W,
552 });
553 self.phdr_table_dirty = true;
554 }
555
556 if (self.phdr_load_ro_index == null) {
557 self.phdr_load_ro_index = @intCast(u16, self.program_headers.items.len);
558 // TODO Find a hint about how much data need to be in rodata ?
559 const file_size = 1024;
560 // Same reason as for GOT
561 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
562 const off = self.findFreeSpace(file_size, p_align);
563 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
564 // TODO Same as for GOT
565 const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000;
566 try self.program_headers.append(gpa, .{
567 .p_type = elf.PT_LOAD,
568 .p_offset = off,
569 .p_filesz = file_size,
570 .p_vaddr = rodata_addr,
571 .p_paddr = rodata_addr,
572 .p_memsz = file_size,
573 .p_align = p_align,
574 .p_flags = elf.PF_R | elf.PF_W,
575 });
576 self.phdr_table_dirty = true;
577 }
578
579 if (self.phdr_load_rw_index == null) {
580 self.phdr_load_rw_index = @intCast(u16, self.program_headers.items.len);
581 // TODO Find a hint about how much data need to be in data ?
582 const file_size = 1024;
583 // Same reason as for GOT
584 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
585 const off = self.findFreeSpace(file_size, p_align);
586 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
587 // TODO Same as for GOT
588 const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000;
589 try self.program_headers.append(gpa, .{
590 .p_type = elf.PT_LOAD,
591 .p_offset = off,
592 .p_filesz = file_size,
593 .p_vaddr = rwdata_addr,
594 .p_paddr = rwdata_addr,
595 .p_memsz = file_size,
596 .p_align = p_align,
597 .p_flags = elf.PF_R | elf.PF_W,
598 });
599 self.phdr_table_dirty = true;
600 }
601
602 if (new_phdr_table_index) |index| {
603 const phsize: u64 = switch (self.ptr_width) {
604 .p32 => @sizeOf(elf.Elf32_Phdr),
605 .p64 => @sizeOf(elf.Elf64_Phdr),
606 };
607 const phdr_table = &self.program_headers.items[index];
608 phdr_table.p_offset = self.findFreeSpace(self.program_headers.items.len * phsize, @intCast(u32, phdr_table.p_align));
609 self.phdr_table_index = index;
610 self.phdr_table_dirty = true;
611 }
555612 }
556613
557614 if (self.shstrtab_index == null) {
......@@ -849,19 +906,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
849906 self.shdr_table_dirty = true;
850907 }
851908
852 const phsize: u64 = switch (self.ptr_width) {
853 .p32 => @sizeOf(elf.Elf32_Phdr),
854 .p64 => @sizeOf(elf.Elf64_Phdr),
855 };
856 const phalign: u16 = switch (self.ptr_width) {
857 .p32 => @alignOf(elf.Elf32_Phdr),
858 .p64 => @alignOf(elf.Elf64_Phdr),
859 };
860 if (self.phdr_table_offset == null) {
861 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
862 self.phdr_table_dirty = true;
863 }
864
865909 {
866910 // Iterate over symbols, populating free_list and last_text_block.
867911 if (self.local_symbols.items.len != 1) {
......@@ -1132,18 +1176,30 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11321176 .p32 => @sizeOf(elf.Elf32_Phdr),
11331177 .p64 => @sizeOf(elf.Elf64_Phdr),
11341178 };
1135 const phalign: u16 = switch (self.ptr_width) {
1136 .p32 => @alignOf(elf.Elf32_Phdr),
1137 .p64 => @alignOf(elf.Elf64_Phdr),
1138 };
1139 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
1179
1180 const phdr_table_index = self.phdr_table_index.?;
1181 const phdr_table = &self.program_headers.items[phdr_table_index];
1182 const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?];
1183
1184 const allocated_size = self.allocatedSize(phdr_table.p_offset);
11401185 const needed_size = self.program_headers.items.len * phsize;
11411186
11421187 if (needed_size > allocated_size) {
1143 self.phdr_table_offset = null; // free the space
1144 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
1188 self.phdr_table_index = null; // free the space
1189 defer self.phdr_table_index = phdr_table_index;
1190 phdr_table.p_offset = self.findFreeSpace(needed_size, @intCast(u32, phdr_table.p_align));
11451191 }
11461192
1193 phdr_table_load.p_offset = mem.alignBackwardGeneric(u64, phdr_table.p_offset, phdr_table_load.p_align);
1194 const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
1195 phdr_table_load.p_filesz = load_align_offset + needed_size;
1196 phdr_table_load.p_memsz = load_align_offset + needed_size;
1197
1198 phdr_table.p_filesz = needed_size;
1199 phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
1200 phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
1201 phdr_table.p_memsz = needed_size;
1202
11471203 switch (self.ptr_width) {
11481204 .p32 => {
11491205 const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
......@@ -1155,7 +1211,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11551211 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
11561212 }
11571213 }
1158 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1214 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
11591215 },
11601216 .p64 => {
11611217 const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
......@@ -1167,7 +1223,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11671223 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
11681224 }
11691225 }
1170 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1226 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
11711227 },
11721228 }
11731229 self.phdr_table_dirty = false;
......@@ -1992,13 +2048,14 @@ fn writeElfHeader(self: *Elf) !void {
19922048
19932049 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
19942050
2051 const phdr_table_offset = self.program_headers.items[self.phdr_table_index.?].p_offset;
19952052 switch (self.ptr_width) {
19962053 .p32 => {
19972054 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
19982055 index += 4;
19992056
20002057 // e_phoff
2001 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
2058 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, phdr_table_offset), endian);
20022059 index += 4;
20032060
20042061 // e_shoff
......@@ -2011,7 +2068,7 @@ fn writeElfHeader(self: *Elf) !void {
20112068 index += 8;
20122069
20132070 // e_phoff
2014 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
2071 mem.writeInt(u64, hdr_buf[index..][0..8], phdr_table_offset, endian);
20152072 index += 8;
20162073
20172074 // e_shoff
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,4 +2,4 @@
22// output_mode=Exe
33// target=aarch64-macos
44//
5// :110:9: error: root struct of file 'tmp' has no member named 'main'
5// :109:9: error: root struct of file 'tmp' has no member named 'main'
test/cases/x86_64-linux/hello_world_with_updates.0.zig+1-1
......@@ -2,4 +2,4 @@
22// output_mode=Exe
33// target=x86_64-linux
44//
5// :110:9: error: root struct of file 'tmp' has no member named 'main'
5// :109:9: error: root struct of file 'tmp' has no member named 'main'
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,4 +2,4 @@
22// output_mode=Exe
33// target=x86_64-macos
44//
5// :110:9: error: root struct of file 'tmp' has no member named 'main'
5// :109:9: error: root struct of file 'tmp' has no member named 'main'
test/cases/x86_64-windows/hello_world_with_updates.0.zig+1-1
......@@ -2,4 +2,4 @@
22// output_mode=Exe
33// target=x86_64-windows
44//
5// :131:9: error: root struct of file 'tmp' has no member named 'main'
5// :130:9: error: root struct of file 'tmp' has no member named 'main'