authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-23 18:32:43-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-23 18:32:43-07:00
logc9413a880be0e5817d31a35c95d4c8f7d1f81eff
treeab1a7b755226b6ce163f069f303397d539202f81
parent8b78df403fe71ffdf8c39361dd30a3c1171a1f1c
parentab8a5bfe83848c0d0ceb7ac08fe7f535783e1605
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17244 from ziglang/elf-vm-mgmt

elf: misc improvements, plus let's actually link against a parsed archive!

6 files changed, 373 insertions(+), 360 deletions(-)

src/link/Elf.zig+330-341
...@@ -373,9 +373,8 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -373,9 +373,8 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
373 return null;373 return null;
374}374}
375375
376pub fn allocatedSize(self: *Elf, start: u64) u64 {376fn allocatedSize(self: *Elf, start: u64) u64 {
377 if (start == 0)377 if (start == 0) return 0;
378 return 0;
379 var min_pos: u64 = std.math.maxInt(u64);378 var min_pos: u64 = std.math.maxInt(u64);
380 if (self.shdr_table_offset) |off| {379 if (self.shdr_table_offset) |off| {
381 if (off > start and off < min_pos) min_pos = off;380 if (off > start and off < min_pos) min_pos = off;
...@@ -391,7 +390,17 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {...@@ -391,7 +390,17 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
391 return min_pos - start;390 return min_pos - start;
392}391}
393392
394pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {393fn allocatedVirtualSize(self: *Elf, start: u64) u64 {
394 if (start == 0) return 0;
395 var min_pos: u64 = std.math.maxInt(u64);
396 for (self.phdrs.items) |phdr| {
397 if (phdr.p_vaddr <= start) continue;
398 if (phdr.p_vaddr < min_pos) min_pos = phdr.p_vaddr;
399 }
400 return min_pos - start;
401}
402
403fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
395 var start: u64 = 0;404 var start: u64 = 0;
396 while (self.detectAllocCollision(start, object_size)) |item_end| {405 while (self.detectAllocCollision(start, object_size)) |item_end| {
397 start = mem.alignForward(u64, item_end, min_alignment);406 start = mem.alignForward(u64, item_end, min_alignment);
...@@ -399,6 +408,113 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {...@@ -399,6 +408,113 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
399 return start;408 return start;
400}409}
401410
411const AllocateSegmentOpts = struct {
412 addr: u64, // TODO find free VM space
413 size: u64,
414 alignment: u64,
415 flags: u32 = elf.PF_R,
416};
417
418fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {
419 const index = @as(u16, @intCast(self.phdrs.items.len));
420 try self.phdrs.ensureUnusedCapacity(self.base.allocator, 1);
421 const off = self.findFreeSpace(opts.size, opts.alignment);
422 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
423 index,
424 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',
425 if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',
426 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
427 off,
428 off + opts.size,
429 opts.addr,
430 opts.addr + opts.size,
431 });
432 self.phdrs.appendAssumeCapacity(.{
433 .p_type = elf.PT_LOAD,
434 .p_offset = off,
435 .p_filesz = opts.size,
436 .p_vaddr = opts.addr,
437 .p_paddr = opts.addr,
438 .p_memsz = opts.size,
439 .p_align = opts.alignment,
440 .p_flags = opts.flags,
441 });
442 self.phdr_table_dirty = true;
443 return index;
444}
445
446const AllocateAllocSectionOpts = struct {
447 name: [:0]const u8,
448 phdr_index: u16,
449 alignment: u16 = 1,
450 flags: u16 = elf.SHF_ALLOC,
451 type: u32 = elf.SHT_PROGBITS,
452};
453
454fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{OutOfMemory}!u16 {
455 const gpa = self.base.allocator;
456 const phdr = &self.phdrs.items[opts.phdr_index];
457 const index = @as(u16, @intCast(self.shdrs.items.len));
458 try self.shdrs.ensureUnusedCapacity(gpa, 1);
459 const sh_name = try self.shstrtab.insert(gpa, opts.name);
460 try self.phdr_to_shdr_table.putNoClobber(gpa, index, opts.phdr_index);
461 log.debug("allocating '{s}' in phdr({d}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
462 opts.name,
463 opts.phdr_index,
464 phdr.p_offset,
465 phdr.p_offset + phdr.p_filesz,
466 phdr.p_vaddr,
467 phdr.p_vaddr + phdr.p_memsz,
468 });
469 self.shdrs.appendAssumeCapacity(.{
470 .sh_name = sh_name,
471 .sh_type = opts.type,
472 .sh_flags = opts.flags,
473 .sh_addr = phdr.p_vaddr,
474 .sh_offset = phdr.p_offset,
475 .sh_size = phdr.p_filesz,
476 .sh_link = 0,
477 .sh_info = 0,
478 .sh_addralign = opts.alignment,
479 .sh_entsize = 0,
480 });
481 self.shdr_table_dirty = true;
482 return index;
483}
484
485const AllocateNonAllocSectionOpts = struct {
486 name: [:0]const u8,
487 size: u64,
488 alignment: u16 = 1,
489 flags: u32 = 0,
490 type: u32 = elf.SHT_PROGBITS,
491 link: u32 = 0,
492 info: u32 = 0,
493 entsize: u64 = 0,
494};
495
496fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{OutOfMemory}!u16 {
497 const index = @as(u16, @intCast(self.shdrs.items.len));
498 try self.shdrs.ensureUnusedCapacity(self.base.allocator, 1);
499 const sh_name = try self.shstrtab.insert(self.base.allocator, opts.name);
500 const off = self.findFreeSpace(opts.size, opts.alignment);
501 log.debug("allocating '{s}' from 0x{x} to 0x{x} ", .{ opts.name, off, off + opts.size });
502 self.shdrs.appendAssumeCapacity(.{
503 .sh_name = sh_name,
504 .sh_type = opts.type,
505 .sh_flags = opts.flags,
506 .sh_addr = 0,
507 .sh_offset = off,
508 .sh_size = opts.size,
509 .sh_link = opts.link,
510 .sh_info = opts.info,
511 .sh_addralign = opts.alignment,
512 .sh_entsize = opts.entsize,
513 });
514 self.shdr_table_dirty = true;
515 return index;
516}
517
402pub fn populateMissingMetadata(self: *Elf) !void {518pub fn populateMissingMetadata(self: *Elf) !void {
403 const gpa = self.base.allocator;519 const gpa = self.base.allocator;
404 const small_ptr = switch (self.ptr_width) {520 const small_ptr = switch (self.ptr_width) {
...@@ -429,7 +545,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -429,7 +545,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
429545
430 if (self.phdr_table_load_index == null) {546 if (self.phdr_table_load_index == null) {
431 self.phdr_table_load_index = @intCast(self.phdrs.items.len);547 self.phdr_table_load_index = @intCast(self.phdrs.items.len);
432 // TODO Same as for GOT
433 try self.phdrs.append(gpa, .{548 try self.phdrs.append(gpa, .{
434 .p_type = elf.PT_LOAD,549 .p_type = elf.PT_LOAD,
435 .p_offset = 0,550 .p_offset = 0,
...@@ -444,401 +559,200 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -444,401 +559,200 @@ pub fn populateMissingMetadata(self: *Elf) !void {
444 }559 }
445560
446 if (self.phdr_load_re_index == null) {561 if (self.phdr_load_re_index == null) {
447 self.phdr_load_re_index = @intCast(self.phdrs.items.len);562 self.phdr_load_re_index = try self.allocateSegment(.{
448 const file_size = self.base.options.program_code_size_hint;563 .addr = self.defaultEntryAddress(),
449 const p_align = self.page_size;564 .size = self.base.options.program_code_size_hint,
450 const off = self.findFreeSpace(file_size, p_align);565 .alignment = self.page_size,
451 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });566 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
452 const entry_addr = self.defaultEntryAddress();
453 try self.phdrs.append(gpa, .{
454 .p_type = elf.PT_LOAD,
455 .p_offset = off,
456 .p_filesz = file_size,
457 .p_vaddr = entry_addr,
458 .p_paddr = entry_addr,
459 .p_memsz = file_size,
460 .p_align = p_align,
461 .p_flags = elf.PF_X | elf.PF_R | elf.PF_W,
462 });567 });
463 self.entry_addr = null;568 self.entry_addr = null;
464 self.phdr_table_dirty = true;
465 }569 }
466570
467 if (self.phdr_got_index == null) {571 if (self.phdr_got_index == null) {
468 self.phdr_got_index = @intCast(self.phdrs.items.len);
469 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
470 // We really only need ptr alignment but since we are using PROGBITS, linux requires
471 // page align.
472 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
473 const off = self.findFreeSpace(file_size, p_align);
474 log.debug("found PT_LOAD GOT free space 0x{x} to 0x{x}", .{ off, off + file_size });
475 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.572 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
476 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something573 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
477 // else in virtual memory.574 // else in virtual memory.
478 const got_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x4000000 else 0x8000;575 const addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
479 try self.phdrs.append(gpa, .{576 // We really only need ptr alignment but since we are using PROGBITS, linux requires
480 .p_type = elf.PT_LOAD,577 // page align.
481 .p_offset = off,578 const alignment = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
482 .p_filesz = file_size,579 self.phdr_got_index = try self.allocateSegment(.{
483 .p_vaddr = got_addr,580 .addr = addr,
484 .p_paddr = got_addr,581 .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
485 .p_memsz = file_size,582 .alignment = alignment,
486 .p_align = p_align,583 .flags = elf.PF_R | elf.PF_W,
487 .p_flags = elf.PF_R | elf.PF_W,
488 });584 });
489 self.phdr_table_dirty = true;
490 }585 }
491586
492 if (self.phdr_load_ro_index == null) {587 if (self.phdr_load_ro_index == null) {
493 self.phdr_load_ro_index = @intCast(self.phdrs.items.len);
494 // TODO Find a hint about how much data need to be in rodata ?
495 const file_size = 1024;
496 // Same reason as for GOT
497 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
498 const off = self.findFreeSpace(file_size, p_align);
499 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
500 // TODO Same as for GOT588 // TODO Same as for GOT
501 const rodata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0xc000000 else 0xa000;589 const addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0xc000000 else 0xa000;
502 try self.phdrs.append(gpa, .{590 // Same reason as for GOT
503 .p_type = elf.PT_LOAD,591 const alignment = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
504 .p_offset = off,592 self.phdr_load_ro_index = try self.allocateSegment(.{
505 .p_filesz = file_size,593 .addr = addr,
506 .p_vaddr = rodata_addr,594 .size = 1024,
507 .p_paddr = rodata_addr,595 .alignment = alignment,
508 .p_memsz = file_size,596 .flags = elf.PF_R | elf.PF_W,
509 .p_align = p_align,
510 .p_flags = elf.PF_R | elf.PF_W,
511 });597 });
512 self.phdr_table_dirty = true;
513 }598 }
514599
515 if (self.phdr_load_rw_index == null) {600 if (self.phdr_load_rw_index == null) {
516 self.phdr_load_rw_index = @intCast(self.phdrs.items.len);
517 // TODO Find a hint about how much data need to be in data ?
518 const file_size = 1024;
519 // Same reason as for GOT
520 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
521 const off = self.findFreeSpace(file_size, p_align);
522 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
523 // TODO Same as for GOT601 // TODO Same as for GOT
524 const rwdata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x10000000 else 0xc000;602 const addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x10000000 else 0xc000;
525 try self.phdrs.append(gpa, .{603 // Same reason as for GOT
526 .p_type = elf.PT_LOAD,604 const alignment = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
527 .p_offset = off,605 self.phdr_load_rw_index = try self.allocateSegment(.{
528 .p_filesz = file_size,606 .addr = addr,
529 .p_vaddr = rwdata_addr,607 .size = 1024,
530 .p_paddr = rwdata_addr,608 .alignment = alignment,
531 .p_memsz = file_size,609 .flags = elf.PF_R | elf.PF_W,
532 .p_align = p_align,
533 .p_flags = elf.PF_R | elf.PF_W,
534 });610 });
535 self.phdr_table_dirty = true;
536 }611 }
537612
538 if (self.phdr_load_zerofill_index == null) {613 if (self.phdr_load_zerofill_index == null) {
539 self.phdr_load_zerofill_index = @intCast(self.phdrs.items.len);
540 const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
541 const off = self.phdrs.items[self.phdr_load_rw_index.?].p_offset;
542 log.debug("found PT_LOAD zerofill free space 0x{x} to 0x{x}", .{ off, off });
543 // TODO Same as for GOT614 // TODO Same as for GOT
544 const addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x14000000 else 0xf000;615 const addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x14000000 else 0xf000;
545 try self.phdrs.append(gpa, .{616 const alignment = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size);
546 .p_type = elf.PT_LOAD,617 self.phdr_load_zerofill_index = try self.allocateSegment(.{
547 .p_offset = off,618 .addr = addr,
548 .p_filesz = 0,619 .size = 0,
549 .p_vaddr = addr,620 .alignment = alignment,
550 .p_paddr = addr,621 .flags = elf.PF_R | elf.PF_W,
551 .p_memsz = 0,
552 .p_align = p_align,
553 .p_flags = elf.PF_R | elf.PF_W,
554 });622 });
555 self.phdr_table_dirty = true;623 const phdr = &self.phdrs.items[self.phdr_load_zerofill_index.?];
624 phdr.p_offset = self.phdrs.items[self.phdr_load_rw_index.?].p_offset; // .bss overlaps .data
556 }625 }
557626
558 if (self.shstrtab_section_index == null) {627 if (self.shstrtab_section_index == null) {
559 self.shstrtab_section_index = @intCast(self.shdrs.items.len);
560 assert(self.shstrtab.buffer.items.len == 0);628 assert(self.shstrtab.buffer.items.len == 0);
561 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0629 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0
562 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1);630 self.shstrtab_section_index = try self.allocateNonAllocSection(.{
563 log.debug("found .shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len });631 .name = ".shstrtab",
564 try self.shdrs.append(gpa, .{632 .size = @intCast(self.shstrtab.buffer.items.len),
565 .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"),633 .type = elf.SHT_STRTAB,
566 .sh_type = elf.SHT_STRTAB,
567 .sh_flags = 0,
568 .sh_addr = 0,
569 .sh_offset = off,
570 .sh_size = self.shstrtab.buffer.items.len,
571 .sh_link = 0,
572 .sh_info = 0,
573 .sh_addralign = 1,
574 .sh_entsize = 0,
575 });634 });
576 self.shstrtab_dirty = true;635 self.shstrtab_dirty = true;
577 self.shdr_table_dirty = true;
578 }636 }
579637
580 if (self.strtab_section_index == null) {638 if (self.strtab_section_index == null) {
581 self.strtab_section_index = @intCast(self.shdrs.items.len);
582 assert(self.strtab.buffer.items.len == 0);639 assert(self.strtab.buffer.items.len == 0);
583 try self.strtab.buffer.append(gpa, 0); // need a 0 at position 0640 try self.strtab.buffer.append(gpa, 0); // need a 0 at position 0
584 const off = self.findFreeSpace(self.strtab.buffer.items.len, 1);641 self.strtab_section_index = try self.allocateNonAllocSection(.{
585 log.debug("found .strtab free space 0x{x} to 0x{x}", .{ off, off + self.strtab.buffer.items.len });642 .name = ".strtab",
586 try self.shdrs.append(gpa, .{643 .size = @intCast(self.strtab.buffer.items.len),
587 .sh_name = try self.shstrtab.insert(gpa, ".strtab"),644 .type = elf.SHT_STRTAB,
588 .sh_type = elf.SHT_STRTAB,
589 .sh_flags = 0,
590 .sh_addr = 0,
591 .sh_offset = off,
592 .sh_size = self.strtab.buffer.items.len,
593 .sh_link = 0,
594 .sh_info = 0,
595 .sh_addralign = 1,
596 .sh_entsize = 0,
597 });645 });
598 self.strtab_dirty = true;646 self.strtab_dirty = true;
599 self.shdr_table_dirty = true;
600 }647 }
601648
602 if (self.text_section_index == null) {649 if (self.text_section_index == null) {
603 self.text_section_index = @intCast(self.shdrs.items.len);650 self.text_section_index = try self.allocateAllocSection(.{
604 const phdr = &self.phdrs.items[self.phdr_load_re_index.?];651 .name = ".text",
605 try self.shdrs.append(gpa, .{652 .phdr_index = self.phdr_load_re_index.?,
606 .sh_name = try self.shstrtab.insert(gpa, ".text"),653 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
607 .sh_type = elf.SHT_PROGBITS,
608 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
609 .sh_addr = phdr.p_vaddr,
610 .sh_offset = phdr.p_offset,
611 .sh_size = phdr.p_filesz,
612 .sh_link = 0,
613 .sh_info = 0,
614 .sh_addralign = 1,
615 .sh_entsize = 0,
616 });654 });
617 try self.phdr_to_shdr_table.putNoClobber(gpa, self.text_section_index.?, self.phdr_load_re_index.?);
618 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.text_section_index.?, .{});655 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.text_section_index.?, .{});
619 self.shdr_table_dirty = true;
620 }656 }
621657
622 if (self.got_section_index == null) {658 if (self.got_section_index == null) {
623 self.got_section_index = @intCast(self.shdrs.items.len);659 self.got_section_index = try self.allocateAllocSection(.{
624 const phdr = &self.phdrs.items[self.phdr_got_index.?];660 .name = ".got",
625 try self.shdrs.append(gpa, .{661 .phdr_index = self.phdr_got_index.?,
626 .sh_name = try self.shstrtab.insert(gpa, ".got"),662 .alignment = ptr_size,
627 .sh_type = elf.SHT_PROGBITS,
628 .sh_flags = elf.SHF_ALLOC,
629 .sh_addr = phdr.p_vaddr,
630 .sh_offset = phdr.p_offset,
631 .sh_size = phdr.p_filesz,
632 .sh_link = 0,
633 .sh_info = 0,
634 .sh_addralign = @as(u16, ptr_size),
635 .sh_entsize = 0,
636 });663 });
637 try self.phdr_to_shdr_table.putNoClobber(gpa, self.got_section_index.?, self.phdr_got_index.?);
638 self.shdr_table_dirty = true;
639 }664 }
640665
641 if (self.rodata_section_index == null) {666 if (self.rodata_section_index == null) {
642 self.rodata_section_index = @intCast(self.shdrs.items.len);667 self.rodata_section_index = try self.allocateAllocSection(.{
643 const phdr = &self.phdrs.items[self.phdr_load_ro_index.?];668 .name = ".rodata",
644 try self.shdrs.append(gpa, .{669 .phdr_index = self.phdr_load_ro_index.?,
645 .sh_name = try self.shstrtab.insert(gpa, ".rodata"),
646 .sh_type = elf.SHT_PROGBITS,
647 .sh_flags = elf.SHF_ALLOC,
648 .sh_addr = phdr.p_vaddr,
649 .sh_offset = phdr.p_offset,
650 .sh_size = phdr.p_filesz,
651 .sh_link = 0,
652 .sh_info = 0,
653 .sh_addralign = 1,
654 .sh_entsize = 0,
655 });670 });
656 try self.phdr_to_shdr_table.putNoClobber(gpa, self.rodata_section_index.?, self.phdr_load_ro_index.?);
657 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.rodata_section_index.?, .{});671 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.rodata_section_index.?, .{});
658 self.shdr_table_dirty = true;
659 }672 }
660673
661 if (self.data_section_index == null) {674 if (self.data_section_index == null) {
662 self.data_section_index = @intCast(self.shdrs.items.len);675 self.data_section_index = try self.allocateAllocSection(.{
663 const phdr = &self.phdrs.items[self.phdr_load_rw_index.?];676 .name = ".data",
664 try self.shdrs.append(gpa, .{677 .phdr_index = self.phdr_load_rw_index.?,
665 .sh_name = try self.shstrtab.insert(gpa, ".data"),678 .alignment = ptr_size,
666 .sh_type = elf.SHT_PROGBITS,679 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
667 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,
668 .sh_addr = phdr.p_vaddr,
669 .sh_offset = phdr.p_offset,
670 .sh_size = phdr.p_filesz,
671 .sh_link = 0,
672 .sh_info = 0,
673 .sh_addralign = @as(u16, ptr_size),
674 .sh_entsize = 0,
675 });680 });
676 try self.phdr_to_shdr_table.putNoClobber(gpa, self.data_section_index.?, self.phdr_load_rw_index.?);
677 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.data_section_index.?, .{});681 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.data_section_index.?, .{});
678 self.shdr_table_dirty = true;
679 }682 }
680683
681 if (self.bss_section_index == null) {684 if (self.bss_section_index == null) {
682 self.bss_section_index = @intCast(self.shdrs.items.len);685 self.bss_section_index = try self.allocateAllocSection(.{
683 const phdr = &self.phdrs.items[self.phdr_load_zerofill_index.?];686 .name = ".bss",
684 try self.shdrs.append(gpa, .{687 .phdr_index = self.phdr_load_zerofill_index.?,
685 .sh_name = try self.shstrtab.insert(gpa, ".bss"),688 .alignment = ptr_size,
686 .sh_type = elf.SHT_NOBITS,689 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
687 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,690 .type = elf.SHT_NOBITS,
688 .sh_addr = phdr.p_vaddr,
689 .sh_offset = phdr.p_offset,
690 .sh_size = phdr.p_filesz,
691 .sh_link = 0,
692 .sh_info = 0,
693 .sh_addralign = @as(u16, ptr_size),
694 .sh_entsize = 0,
695 });691 });
696 try self.phdr_to_shdr_table.putNoClobber(gpa, self.bss_section_index.?, self.phdr_load_zerofill_index.?);
697 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.bss_section_index.?, .{});692 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.bss_section_index.?, .{});
698 self.shdr_table_dirty = true;
699 }693 }
700694
701 if (self.symtab_section_index == null) {695 if (self.symtab_section_index == null) {
702 self.symtab_section_index = @intCast(self.shdrs.items.len);
703 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);696 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
704 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);697 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
705 const file_size = self.base.options.symbol_count_hint * each_size;698 self.symtab_section_index = try self.allocateNonAllocSection(.{
706 const off = self.findFreeSpace(file_size, min_align);699 .name = ".symtab",
707 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });700 .size = self.base.options.symbol_count_hint * each_size,
708 try self.shdrs.append(gpa, .{701 .alignment = min_align,
709 .sh_name = try self.shstrtab.insert(gpa, ".symtab"),702 .type = elf.SHT_SYMTAB,
710 .sh_type = elf.SHT_SYMTAB,703 .link = self.strtab_section_index.?, // Index of associated string table
711 .sh_flags = 0,704 .info = @intCast(self.symbols.items.len),
712 .sh_addr = 0,705 .entsize = each_size,
713 .sh_offset = off,
714 .sh_size = file_size,
715 // The section header index of the associated string table.
716 .sh_link = self.strtab_section_index.?,
717 .sh_info = @intCast(self.symbols.items.len),
718 .sh_addralign = min_align,
719 .sh_entsize = each_size,
720 });706 });
721 self.shdr_table_dirty = true;707 self.shdr_table_dirty = true;
722 }708 }
723709
724 if (self.dwarf) |*dw| {710 if (self.dwarf) |*dw| {
725 if (self.debug_str_section_index == null) {711 if (self.debug_str_section_index == null) {
726 self.debug_str_section_index = @intCast(self.shdrs.items.len);
727 assert(dw.strtab.buffer.items.len == 0);712 assert(dw.strtab.buffer.items.len == 0);
728 try dw.strtab.buffer.append(gpa, 0);713 try dw.strtab.buffer.append(gpa, 0);
729 try self.shdrs.append(gpa, .{714 self.debug_str_section_index = try self.allocateNonAllocSection(.{
730 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),715 .name = ".debug_str",
731 .sh_type = elf.SHT_PROGBITS,716 .size = @intCast(dw.strtab.buffer.items.len),
732 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,717 .flags = elf.SHF_MERGE | elf.SHF_STRINGS,
733 .sh_addr = 0,718 .entsize = 1,
734 .sh_offset = 0,
735 .sh_size = 0,
736 .sh_link = 0,
737 .sh_info = 0,
738 .sh_addralign = 1,
739 .sh_entsize = 1,
740 });719 });
741 self.debug_strtab_dirty = true;720 self.debug_strtab_dirty = true;
742 self.shdr_table_dirty = true;
743 }721 }
744722
745 if (self.debug_info_section_index == null) {723 if (self.debug_info_section_index == null) {
746 self.debug_info_section_index = @intCast(self.shdrs.items.len);724 self.debug_info_section_index = try self.allocateNonAllocSection(.{
747 const file_size_hint = 200;725 .name = ".debug_info",
748 const p_align = 1;726 .size = 200,
749 const off = self.findFreeSpace(file_size_hint, p_align);727 .alignment = 1,
750 log.debug("found .debug_info free space 0x{x} to 0x{x}", .{
751 off,
752 off + file_size_hint,
753 });
754 try self.shdrs.append(gpa, .{
755 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
756 .sh_type = elf.SHT_PROGBITS,
757 .sh_flags = 0,
758 .sh_addr = 0,
759 .sh_offset = off,
760 .sh_size = file_size_hint,
761 .sh_link = 0,
762 .sh_info = 0,
763 .sh_addralign = p_align,
764 .sh_entsize = 0,
765 });728 });
766 self.shdr_table_dirty = true;
767 self.debug_info_header_dirty = true;729 self.debug_info_header_dirty = true;
768 }730 }
769731
770 if (self.debug_abbrev_section_index == null) {732 if (self.debug_abbrev_section_index == null) {
771 self.debug_abbrev_section_index = @intCast(self.shdrs.items.len);733 self.debug_abbrev_section_index = try self.allocateNonAllocSection(.{
772 const file_size_hint = 128;734 .name = ".debug_abbrev",
773 const p_align = 1;735 .size = 128,
774 const off = self.findFreeSpace(file_size_hint, p_align);736 .alignment = 1,
775 log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{
776 off,
777 off + file_size_hint,
778 });737 });
779 try self.shdrs.append(gpa, .{
780 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
781 .sh_type = elf.SHT_PROGBITS,
782 .sh_flags = 0,
783 .sh_addr = 0,
784 .sh_offset = off,
785 .sh_size = file_size_hint,
786 .sh_link = 0,
787 .sh_info = 0,
788 .sh_addralign = p_align,
789 .sh_entsize = 0,
790 });
791 self.shdr_table_dirty = true;
792 self.debug_abbrev_section_dirty = true;738 self.debug_abbrev_section_dirty = true;
793 }739 }
794740
795 if (self.debug_aranges_section_index == null) {741 if (self.debug_aranges_section_index == null) {
796 self.debug_aranges_section_index = @intCast(self.shdrs.items.len);742 self.debug_aranges_section_index = try self.allocateNonAllocSection(.{
797 const file_size_hint = 160;743 .name = ".debug_aranges",
798 const p_align = 16;744 .size = 160,
799 const off = self.findFreeSpace(file_size_hint, p_align);745 .alignment = 16,
800 log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{
801 off,
802 off + file_size_hint,
803 });
804 try self.shdrs.append(gpa, .{
805 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
806 .sh_type = elf.SHT_PROGBITS,
807 .sh_flags = 0,
808 .sh_addr = 0,
809 .sh_offset = off,
810 .sh_size = file_size_hint,
811 .sh_link = 0,
812 .sh_info = 0,
813 .sh_addralign = p_align,
814 .sh_entsize = 0,
815 });746 });
816 self.shdr_table_dirty = true;
817 self.debug_aranges_section_dirty = true;747 self.debug_aranges_section_dirty = true;
818 }748 }
819749
820 if (self.debug_line_section_index == null) {750 if (self.debug_line_section_index == null) {
821 self.debug_line_section_index = @intCast(self.shdrs.items.len);751 self.debug_line_section_index = try self.allocateNonAllocSection(.{
822 const file_size_hint = 250;752 .name = ".debug_line",
823 const p_align = 1;753 .size = 250,
824 const off = self.findFreeSpace(file_size_hint, p_align);754 .alignment = 1,
825 log.debug("found .debug_line free space 0x{x} to 0x{x}", .{
826 off,
827 off + file_size_hint,
828 });755 });
829 try self.shdrs.append(gpa, .{
830 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
831 .sh_type = elf.SHT_PROGBITS,
832 .sh_flags = 0,
833 .sh_addr = 0,
834 .sh_offset = off,
835 .sh_size = file_size_hint,
836 .sh_link = 0,
837 .sh_info = 0,
838 .sh_addralign = p_align,
839 .sh_entsize = 0,
840 });
841 self.shdr_table_dirty = true;
842 self.debug_line_header_dirty = true;756 self.debug_line_header_dirty = true;
843 }757 }
844 }758 }
...@@ -907,7 +821,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -907,7 +821,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
907}821}
908822
909pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {823pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
910 // TODO Also detect virtual address collisions.
911 const shdr = &self.shdrs.items[shdr_index];824 const shdr = &self.shdrs.items[shdr_index];
912 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;825 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
913 const phdr = &self.phdrs.items[phdr_index];826 const phdr = &self.phdrs.items[phdr_index];
...@@ -922,8 +835,8 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {...@@ -922,8 +835,8 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
922 } else shdr.sh_size;835 } else shdr.sh_size;
923 shdr.sh_size = 0;836 shdr.sh_size = 0;
924837
925 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{838 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
926 self.shstrtab.get(shdr.sh_name),839 self.shstrtab.getAssumeExists(shdr.sh_name),
927 new_offset,840 new_offset,
928 new_offset + existing_size,841 new_offset + existing_size,
929 });842 });
...@@ -935,6 +848,9 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {...@@ -935,6 +848,9 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
935 phdr.p_offset = new_offset;848 phdr.p_offset = new_offset;
936 }849 }
937850
851 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
852 assert(needed_size <= mem_capacity); // TODO grow section in virtual memory
853
938 shdr.sh_size = needed_size;854 shdr.sh_size = needed_size;
939 phdr.p_memsz = needed_size;855 phdr.p_memsz = needed_size;
940856
...@@ -1160,7 +1076,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1160,7 +1076,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1160 for (self.file(index).?.zig_module.atoms.keys()) |atom_index| {1076 for (self.file(index).?.zig_module.atoms.keys()) |atom_index| {
1161 const atom_ptr = self.atom(atom_index).?;1077 const atom_ptr = self.atom(atom_index).?;
1162 if (!atom_ptr.alive) continue;1078 if (!atom_ptr.alive) continue;
1163 const shdr = &self.shdrs.items[atom_ptr.output_section_index];1079 const shdr = &self.shdrs.items[atom_ptr.outputShndx().?];
1164 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;1080 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1165 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;1081 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
1166 const code = try gpa.alloc(u8, size);1082 const code = try gpa.alloc(u8, size);
...@@ -1190,6 +1106,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1190,6 +1106,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1190 try self.updateSymtabSize();1106 try self.updateSymtabSize();
1191 try self.writeSymtab();1107 try self.writeSymtab();
11921108
1109 // .bss always overlaps .data in file offset, but is zero-sized in file so it doesn't
1110 // get mapped by the loader
1111 if (self.data_section_index) |data_shndx| blk: {
1112 const bss_shndx = self.bss_section_index orelse break :blk;
1113 const data_phndx = self.phdr_to_shdr_table.get(data_shndx).?;
1114 const bss_phndx = self.phdr_to_shdr_table.get(bss_shndx).?;
1115 self.shdrs.items[bss_shndx].sh_offset = self.shdrs.items[data_shndx].sh_offset;
1116 self.phdrs.items[bss_phndx].p_offset = self.phdrs.items[data_phndx].p_offset;
1117 }
1118
1193 // Dump the state for easy debugging.1119 // Dump the state for easy debugging.
1194 // State can be dumped via `--debug-log link_state`.1120 // State can be dumped via `--debug-log link_state`.
1195 if (build_options.enable_logging) {1121 if (build_options.enable_logging) {
...@@ -1567,6 +1493,7 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {...@@ -1567,6 +1493,7 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {
1567/// This routine will prune unneeded objects extracted from archives and1493/// This routine will prune unneeded objects extracted from archives and
1568/// unneeded shared objects.1494/// unneeded shared objects.
1569fn markLive(self: *Elf) void {1495fn markLive(self: *Elf) void {
1496 if (self.zig_module_index) |index| self.file(index).?.markLive(self);
1570 for (self.objects.items) |index| {1497 for (self.objects.items) |index| {
1571 const file_ptr = self.file(index).?;1498 const file_ptr = self.file(index).?;
1572 if (file_ptr.isAlive()) file_ptr.markLive(self);1499 if (file_ptr.isAlive()) file_ptr.markLive(self);
...@@ -1689,7 +1616,7 @@ fn writeObjects(self: *Elf) !void {...@@ -1689,7 +1616,7 @@ fn writeObjects(self: *Elf) !void {
1689 const atom_ptr = self.atom(atom_index) orelse continue;1616 const atom_ptr = self.atom(atom_index) orelse continue;
1690 if (!atom_ptr.alive) continue;1617 if (!atom_ptr.alive) continue;
16911618
1692 const shdr = &self.shdrs.items[atom_ptr.output_section_index];1619 const shdr = &self.shdrs.items[atom_ptr.outputShndx().?];
1693 if (shdr.sh_type == elf.SHT_NOBITS) continue;1620 if (shdr.sh_type == elf.SHT_NOBITS) continue;
1694 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; // TODO we don't yet know how to handle non-alloc sections1621 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; // TODO we don't yet know how to handle non-alloc sections
16951622
...@@ -2563,15 +2490,15 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {...@@ -2563,15 +2490,15 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
2563 }2490 }
2564}2491}
25652492
2566pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Symbol.Index {2493pub fn getOrCreateMetadataForLazySymbol(self: *Elf, lazy_sym: link.File.LazySymbol) !Symbol.Index {
2567 const mod = self.base.options.module.?;2494 const mod = self.base.options.module.?;
2568 const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));2495 const gop = try self.lazy_syms.getOrPut(self.base.allocator, lazy_sym.getDecl(mod));
2569 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();2496 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
2570 if (!gop.found_existing) gop.value_ptr.* = .{};2497 if (!gop.found_existing) gop.value_ptr.* = .{};
2571 const metadata: struct {2498 const metadata: struct {
2572 symbol_index: *Symbol.Index,2499 symbol_index: *Symbol.Index,
2573 state: *LazySymbolMetadata.State,2500 state: *LazySymbolMetadata.State,
2574 } = switch (sym.kind) {2501 } = switch (lazy_sym.kind) {
2575 .code => .{2502 .code => .{
2576 .symbol_index = &gop.value_ptr.text_symbol_index,2503 .symbol_index = &gop.value_ptr.text_symbol_index,
2577 .state = &gop.value_ptr.text_state,2504 .state = &gop.value_ptr.text_state,
...@@ -2583,17 +2510,14 @@ pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !...@@ -2583,17 +2510,14 @@ pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !
2583 };2510 };
2584 const zig_module = self.file(self.zig_module_index.?).?.zig_module;2511 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2585 switch (metadata.state.*) {2512 switch (metadata.state.*) {
2586 .unused => metadata.symbol_index.* = try zig_module.addAtom(switch (sym.kind) {2513 .unused => metadata.symbol_index.* = try zig_module.addAtom(self),
2587 .code => self.text_section_index.?,
2588 .const_data => self.rodata_section_index.?,
2589 }, self),
2590 .pending_flush => return metadata.symbol_index.*,2514 .pending_flush => return metadata.symbol_index.*,
2591 .flushed => {},2515 .flushed => {},
2592 }2516 }
2593 metadata.state.* = .pending_flush;2517 metadata.state.* = .pending_flush;
2594 const symbol_index = metadata.symbol_index.*;2518 const symbol_index = metadata.symbol_index.*;
2595 // anyerror needs to be deferred until flushModule2519 // anyerror needs to be deferred until flushModule
2596 if (sym.getDecl(mod) != .none) try self.updateLazySymbol(sym, symbol_index);2520 if (lazy_sym.getDecl(mod) != .none) try self.updateLazySymbol(lazy_sym, symbol_index);
2597 return symbol_index;2521 return symbol_index;
2598}2522}
25992523
...@@ -2602,35 +2526,37 @@ pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Sy...@@ -2602,35 +2526,37 @@ pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Sy
2602 if (!gop.found_existing) {2526 if (!gop.found_existing) {
2603 const zig_module = self.file(self.zig_module_index.?).?.zig_module;2527 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2604 gop.value_ptr.* = .{2528 gop.value_ptr.* = .{
2605 .symbol_index = try zig_module.addAtom(self.getDeclShdrIndex(decl_index), self),2529 .symbol_index = try zig_module.addAtom(self),
2606 .exports = .{},2530 .exports = .{},
2607 };2531 };
2608 }2532 }
2609 return gop.value_ptr.symbol_index;2533 return gop.value_ptr.symbol_index;
2610}2534}
26112535
2612fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {2536fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index, code: []const u8) u16 {
2613 const mod = self.base.options.module.?;2537 const mod = self.base.options.module.?;
2614 const decl = mod.declPtr(decl_index);2538 const decl = mod.declPtr(decl_index);
2615 const ty = decl.ty;2539 const shdr_index = switch (decl.ty.zigTypeTag(mod)) {
2616 const zig_ty = ty.zigTypeTag(mod);2540 // TODO: what if this is a function pointer?
2617 const val = decl.val;2541 .Fn => self.text_section_index.?,
2618 const shdr_index: u16 = blk: {2542 else => blk: {
2619 if (val.isUndefDeep(mod)) {2543 if (decl.getOwnedVariable(mod)) |variable| {
2620 // TODO in release-fast and release-small, we should put undef in .bss2544 if (variable.is_const) break :blk self.rodata_section_index.?;
2621 break :blk self.data_section_index.?;2545 if (variable.init.toValue().isUndefDeep(mod)) {
2622 }2546 const mode = self.base.options.optimize_mode;
26232547 if (mode == .Debug or mode == .ReleaseSafe) break :blk self.data_section_index.?;
2624 switch (zig_ty) {2548 break :blk self.bss_section_index.?;
2625 // TODO: what if this is a function pointer?
2626 .Fn => break :blk self.text_section_index.?,
2627 else => {
2628 if (val.getVariable(mod)) |_| {
2629 break :blk self.data_section_index.?;
2630 }2549 }
2631 break :blk self.rodata_section_index.?;2550 // TODO I blatantly copied the logic from the Wasm linker, but is there a less
2632 },2551 // intrusive check for all zeroes than this?
2633 }2552 const is_all_zeroes = for (code) |byte| {
2553 if (byte != 0) break false;
2554 } else true;
2555 if (is_all_zeroes) break :blk self.bss_section_index.?;
2556 break :blk self.data_section_index.?;
2557 }
2558 break :blk self.rodata_section_index.?;
2559 },
2634 };2560 };
2635 return shdr_index;2561 return shdr_index;
2636}2562}
...@@ -2655,7 +2581,10 @@ fn updateDeclCode(...@@ -2655,7 +2581,10 @@ fn updateDeclCode(
2655 const sym = self.symbol(sym_index);2581 const sym = self.symbol(sym_index);
2656 const esym = &zig_module.local_esyms.items[sym.esym_index];2582 const esym = &zig_module.local_esyms.items[sym.esym_index];
2657 const atom_ptr = sym.atom(self).?;2583 const atom_ptr = sym.atom(self).?;
2658 const shdr_index = sym.output_section_index;2584
2585 const shdr_index = self.getDeclShdrIndex(decl_index, code);
2586 sym.output_section_index = shdr_index;
2587 atom_ptr.output_section_index = shdr_index;
26592588
2660 sym.name_offset = try self.strtab.insert(gpa, decl_name);2589 sym.name_offset = try self.strtab.insert(gpa, decl_name);
2661 atom_ptr.alive = true;2590 atom_ptr.alive = true;
...@@ -2908,9 +2837,14 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol....@@ -2908,9 +2837,14 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
2908 },2837 },
2909 };2838 };
29102839
2840 const output_section_index = switch (sym.kind) {
2841 .code => self.text_section_index.?,
2842 .const_data => self.rodata_section_index.?,
2843 };
2911 const local_sym = self.symbol(symbol_index);2844 const local_sym = self.symbol(symbol_index);
2912 const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?;2845 const phdr_index = self.phdr_to_shdr_table.get(output_section_index).?;
2913 local_sym.name_offset = name_str_index;2846 local_sym.name_offset = name_str_index;
2847 local_sym.output_section_index = output_section_index;
2914 const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];2848 const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];
2915 local_esym.st_name = name_str_index;2849 local_esym.st_name = name_str_index;
2916 local_esym.st_info |= elf.STT_OBJECT;2850 local_esym.st_info |= elf.STT_OBJECT;
...@@ -2920,6 +2854,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol....@@ -2920,6 +2854,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
2920 atom_ptr.name_offset = name_str_index;2854 atom_ptr.name_offset = name_str_index;
2921 atom_ptr.alignment = required_alignment;2855 atom_ptr.alignment = required_alignment;
2922 atom_ptr.size = code.len;2856 atom_ptr.size = code.len;
2857 atom_ptr.output_section_index = output_section_index;
29232858
2924 try atom_ptr.allocate(self);2859 try atom_ptr.allocate(self);
2925 errdefer self.freeDeclMetadata(symbol_index);2860 errdefer self.freeDeclMetadata(symbol_index);
...@@ -2938,7 +2873,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol....@@ -2938,7 +2873,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
2938 try self.got.writeEntry(self, gop.index);2873 try self.got.writeEntry(self, gop.index);
29392874
2940 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;2875 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
2941 const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset;2876 const file_offset = self.shdrs.items[output_section_index].sh_offset + section_offset;
2942 try self.base.file.?.pwriteAll(code, file_offset);2877 try self.base.file.?.pwriteAll(code, file_offset);
2943}2878}
29442879
...@@ -2966,7 +2901,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2966,7 +2901,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2966 const name = self.strtab.get(name_str_index).?;2901 const name = self.strtab.get(name_str_index).?;
29672902
2968 const zig_module = self.file(self.zig_module_index.?).?.zig_module;2903 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2969 const sym_index = try zig_module.addAtom(self.rodata_section_index.?, self);2904 const sym_index = try zig_module.addAtom(self);
29702905
2971 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{2906 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{
2972 .none = {},2907 .none = {},
...@@ -2988,6 +2923,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2988,6 +2923,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2988 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;2923 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
2989 const local_sym = self.symbol(sym_index);2924 const local_sym = self.symbol(sym_index);
2990 local_sym.name_offset = name_str_index;2925 local_sym.name_offset = name_str_index;
2926 local_sym.output_section_index = self.rodata_section_index.?;
2991 const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];2927 const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];
2992 local_esym.st_name = name_str_index;2928 local_esym.st_name = name_str_index;
2993 local_esym.st_info |= elf.STT_OBJECT;2929 local_esym.st_info |= elf.STT_OBJECT;
...@@ -2997,6 +2933,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2997,6 +2933,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2997 atom_ptr.name_offset = name_str_index;2933 atom_ptr.name_offset = name_str_index;
2998 atom_ptr.alignment = required_alignment;2934 atom_ptr.alignment = required_alignment;
2999 atom_ptr.size = code.len;2935 atom_ptr.size = code.len;
2936 atom_ptr.output_section_index = self.rodata_section_index.?;
30002937
3001 try atom_ptr.allocate(self);2938 try atom_ptr.allocate(self);
3002 errdefer self.freeDeclMetadata(sym_index);2939 errdefer self.freeDeclMetadata(sym_index);
...@@ -4014,6 +3951,54 @@ fn reportParseError(...@@ -4014,6 +3951,54 @@ fn reportParseError(
4014 });3951 });
4015}3952}
40163953
3954fn fmtShdrs(self: *Elf) std.fmt.Formatter(formatShdrs) {
3955 return .{ .data = self };
3956}
3957
3958fn formatShdrs(
3959 self: *Elf,
3960 comptime unused_fmt_string: []const u8,
3961 options: std.fmt.FormatOptions,
3962 writer: anytype,
3963) !void {
3964 _ = options;
3965 _ = unused_fmt_string;
3966 for (self.shdrs.items, 0..) |shdr, i| {
3967 try writer.print("shdr({d}) : phdr({?d}) : {s} : @{x} ({x}) : align({x}) : size({x})\n", .{
3968 i, self.phdr_to_shdr_table.get(@intCast(i)),
3969 self.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,
3970 shdr.sh_addr, shdr.sh_addralign,
3971 shdr.sh_size,
3972 });
3973 }
3974}
3975
3976fn fmtPhdrs(self: *Elf) std.fmt.Formatter(formatPhdrs) {
3977 return .{ .data = self };
3978}
3979
3980fn formatPhdrs(
3981 self: *Elf,
3982 comptime unused_fmt_string: []const u8,
3983 options: std.fmt.FormatOptions,
3984 writer: anytype,
3985) !void {
3986 _ = options;
3987 _ = unused_fmt_string;
3988 for (self.phdrs.items, 0..) |phdr, i| {
3989 const write = phdr.p_flags & elf.PF_W != 0;
3990 const read = phdr.p_flags & elf.PF_R != 0;
3991 const exec = phdr.p_flags & elf.PF_X != 0;
3992 var flags: [3]u8 = [_]u8{'_'} ** 3;
3993 if (exec) flags[0] = 'X';
3994 if (write) flags[1] = 'W';
3995 if (read) flags[2] = 'R';
3996 try writer.print("phdr({d}) : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})\n", .{
3997 i, flags, phdr.p_offset, phdr.p_vaddr, phdr.p_align, phdr.p_filesz, phdr.p_memsz,
3998 });
3999 }
4000}
4001
4017fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {4002fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
4018 return .{ .data = self };4003 return .{ .data = self };
4019}4004}
...@@ -4053,6 +4038,10 @@ fn fmtDumpState(...@@ -4053,6 +4038,10 @@ fn fmtDumpState(
4053 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});4038 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
4054 }4039 }
4055 try writer.print("{}\n", .{self.got.fmt(self)});4040 try writer.print("{}\n", .{self.got.fmt(self)});
4041 try writer.writeAll("Output shdrs\n");
4042 try writer.print("{}\n", .{self.fmtShdrs()});
4043 try writer.writeAll("Output phdrs\n");
4044 try writer.print("{}\n", .{self.fmtPhdrs()});
4056}4045}
40574046
4058/// Binary search4047/// Binary search
src/link/Elf/Atom.zig+10-5
...@@ -17,7 +17,7 @@ alignment: Alignment = .@"1",...@@ -17,7 +17,7 @@ alignment: Alignment = .@"1",
17input_section_index: Index = 0,17input_section_index: Index = 0,
1818
19/// Index of the output section.19/// Index of the output section.
20output_section_index: Index = 0,20output_section_index: u16 = 0,
2121
22/// Index of the input section containing this atom's relocs.22/// Index of the input section containing this atom's relocs.
23relocs_section_index: Index = 0,23relocs_section_index: Index = 0,
...@@ -53,6 +53,11 @@ pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {...@@ -53,6 +53,11 @@ pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
53 return object.shdrs.items[self.input_section_index];53 return object.shdrs.items[self.input_section_index];
54}54}
5555
56pub fn outputShndx(self: Atom) ?u16 {
57 if (self.output_section_index == 0) return null;
58 return self.output_section_index;
59}
60
56pub fn codeInObject(self: Atom, elf_file: *Elf) error{Overflow}![]const u8 {61pub fn codeInObject(self: Atom, elf_file: *Elf) error{Overflow}![]const u8 {
57 const object = elf_file.file(self.file_index).?.object;62 const object = elf_file.file(self.file_index).?.object;
58 return object.shdrContents(self.input_section_index);63 return object.shdrContents(self.input_section_index);
...@@ -109,8 +114,8 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {...@@ -109,8 +114,8 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
109}114}
110115
111pub fn allocate(self: *Atom, elf_file: *Elf) !void {116pub fn allocate(self: *Atom, elf_file: *Elf) !void {
112 const shdr = &elf_file.shdrs.items[self.output_section_index];117 const shdr = &elf_file.shdrs.items[self.outputShndx().?];
113 const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?;118 const meta = elf_file.last_atom_and_free_list_table.getPtr(self.outputShndx().?).?;
114 const free_list = &meta.free_list;119 const free_list = &meta.free_list;
115 const last_atom_index = &meta.last_atom_index;120 const last_atom_index = &meta.last_atom_index;
116 const new_atom_ideal_capacity = Elf.padToIdeal(self.size);121 const new_atom_ideal_capacity = Elf.padToIdeal(self.size);
...@@ -179,7 +184,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {...@@ -179,7 +184,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
179 true;184 true;
180 if (expand_section) {185 if (expand_section) {
181 const needed_size = (self.value + self.size) - shdr.sh_addr;186 const needed_size = (self.value + self.size) - shdr.sh_addr;
182 try elf_file.growAllocSection(self.output_section_index, needed_size);187 try elf_file.growAllocSection(self.outputShndx().?, needed_size);
183 last_atom_index.* = self.atom_index;188 last_atom_index.* = self.atom_index;
184189
185 if (elf_file.dwarf) |_| {190 if (elf_file.dwarf) |_| {
...@@ -234,7 +239,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {...@@ -234,7 +239,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
234239
235 const gpa = elf_file.base.allocator;240 const gpa = elf_file.base.allocator;
236 const zig_module = elf_file.file(self.file_index).?.zig_module;241 const zig_module = elf_file.file(self.file_index).?.zig_module;
237 const shndx = self.output_section_index;242 const shndx = self.outputShndx().?;
238 const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;243 const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;
239 const free_list = &meta.free_list;244 const free_list = &meta.free_list;
240 const last_atom_index = &meta.last_atom_index;245 const last_atom_index = &meta.last_atom_index;
src/link/Elf/Object.zig+4-4
...@@ -272,9 +272,9 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {...@@ -272,9 +272,9 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {
272 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];272 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
273 sym_ptr.file_index = self.index;273 sym_ptr.file_index = self.index;
274 sym_ptr.output_section_index = if (sym_ptr.atom(elf_file)) |atom_ptr|274 sym_ptr.output_section_index = if (sym_ptr.atom(elf_file)) |atom_ptr|
275 atom_ptr.output_section_index275 atom_ptr.outputShndx().?
276 else276 else
277 0;277 elf.SHN_UNDEF;
278 }278 }
279279
280 for (self.symtab[first_global..]) |sym| {280 for (self.symtab[first_global..]) |sym| {
...@@ -440,9 +440,9 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {...@@ -440,9 +440,9 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
440 else => self.atoms.items[esym.st_shndx],440 else => self.atoms.items[esym.st_shndx],
441 };441 };
442 const output_section_index = if (elf_file.atom(atom_index)) |atom|442 const output_section_index = if (elf_file.atom(atom_index)) |atom|
443 atom.output_section_index443 atom.outputShndx().?
444 else444 else
445 0;445 elf.SHN_UNDEF;
446 global.value = esym.st_value;446 global.value = esym.st_value;
447 global.atom_index = atom_index;447 global.atom_index = atom_index;
448 global.esym_index = esym_index;448 global.esym_index = esym_index;
src/link/Elf/Symbol.zig+9-4
...@@ -33,10 +33,15 @@ extra_index: u32 = 0,...@@ -33,10 +33,15 @@ extra_index: u32 = 0,
33pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {33pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
34 const file_ptr = symbol.file(elf_file).?;34 const file_ptr = symbol.file(elf_file).?;
35 // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS;35 // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS;
36 return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and36 return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.outputShndx() == null and
37 file_ptr != .linker_defined;37 file_ptr != .linker_defined;
38}38}
3939
40pub fn outputShndx(symbol: Symbol) ?u16 {
41 if (symbol.output_section_index == 0) return null;
42 return symbol.output_section_index;
43}
44
40pub fn isLocal(symbol: Symbol) bool {45pub fn isLocal(symbol: Symbol) bool {
41 return !(symbol.flags.import or symbol.flags.@"export");46 return !(symbol.flags.import or symbol.flags.@"export");
42}47}
...@@ -183,7 +188,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {...@@ -183,7 +188,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
183 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;188 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
184 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)189 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)
185 break :blk elf.SHN_ABS;190 break :blk elf.SHN_ABS;
186 break :blk symbol.output_section_index;191 break :blk symbol.outputShndx() orelse elf.SHN_UNDEF;
187 };192 };
188 const st_value = blk: {193 const st_value = blk: {
189 // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file);194 // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file);
...@@ -276,8 +281,8 @@ fn format2(...@@ -276,8 +281,8 @@ fn format2(
276 } else {281 } else {
277 try writer.writeAll(" : absolute");282 try writer.writeAll(" : absolute");
278 }283 }
279 } else if (symbol.output_section_index != 0) {284 } else if (symbol.outputShndx()) |shndx| {
280 try writer.print(" : sect({d})", .{symbol.output_section_index});285 try writer.print(" : sect({d})", .{shndx});
281 }286 }
282 if (symbol.atom(ctx.elf_file)) |atom_ptr| {287 if (symbol.atom(ctx.elf_file)) |atom_ptr| {
283 try writer.print(" : atom({d})", .{atom_ptr.atom_index});288 try writer.print(" : atom({d})", .{atom_ptr.atom_index});
src/link/Elf/ZigModule.zig+19-5
...@@ -49,21 +49,19 @@ pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {...@@ -49,21 +49,19 @@ pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
49 return index | 0x10000000;49 return index | 0x10000000;
50}50}
5151
52pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index {52pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
53 const gpa = elf_file.base.allocator;53 const gpa = elf_file.base.allocator;
5454
55 const atom_index = try elf_file.addAtom();55 const atom_index = try elf_file.addAtom();
56 try self.atoms.putNoClobber(gpa, atom_index, {});56 try self.atoms.putNoClobber(gpa, atom_index, {});
57 const atom_ptr = elf_file.atom(atom_index).?;57 const atom_ptr = elf_file.atom(atom_index).?;
58 atom_ptr.file_index = self.index;58 atom_ptr.file_index = self.index;
59 atom_ptr.output_section_index = output_section_index;
6059
61 const symbol_index = try elf_file.addSymbol();60 const symbol_index = try elf_file.addSymbol();
62 try self.local_symbols.append(gpa, symbol_index);61 try self.local_symbols.append(gpa, symbol_index);
63 const symbol_ptr = elf_file.symbol(symbol_index);62 const symbol_ptr = elf_file.symbol(symbol_index);
64 symbol_ptr.file_index = self.index;63 symbol_ptr.file_index = self.index;
65 symbol_ptr.atom_index = atom_index;64 symbol_ptr.atom_index = atom_index;
66 symbol_ptr.output_section_index = output_section_index;
6765
68 const esym_index = try self.addLocalEsym(gpa);66 const esym_index = try self.addLocalEsym(gpa);
69 const esym = &self.local_esyms.items[esym_index];67 const esym = &self.local_esyms.items[esym_index];
...@@ -98,9 +96,9 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {...@@ -98,9 +96,9 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
98 else => esym.st_shndx,96 else => esym.st_shndx,
99 };97 };
100 const output_section_index = if (elf_file.atom(atom_index)) |atom|98 const output_section_index = if (elf_file.atom(atom_index)) |atom|
101 atom.output_section_index99 atom.outputShndx().?
102 else100 else
103 0;101 elf.SHN_UNDEF;
104 global.value = esym.st_value;102 global.value = esym.st_value;
105 global.atom_index = atom_index;103 global.atom_index = atom_index;
106 global.esym_index = esym_index;104 global.esym_index = esym_index;
...@@ -157,6 +155,22 @@ pub fn resetGlobals(self: *ZigModule, elf_file: *Elf) void {...@@ -157,6 +155,22 @@ pub fn resetGlobals(self: *ZigModule, elf_file: *Elf) void {
157 }155 }
158}156}
159157
158pub fn markLive(self: *ZigModule, elf_file: *Elf) void {
159 for (self.globals(), 0..) |index, i| {
160 const esym = self.global_esyms.items[i];
161 if (esym.st_bind() == elf.STB_WEAK) continue;
162
163 const global = elf_file.symbol(index);
164 const file = global.file(elf_file) orelse continue;
165 const should_keep = esym.st_shndx == elf.SHN_UNDEF or
166 (esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
167 if (should_keep and !file.isAlive()) {
168 file.setAlive();
169 file.markLive(elf_file);
170 }
171 }
172}
173
160pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {174pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
161 for (self.locals()) |local_index| {175 for (self.locals()) |local_index| {
162 const local = elf_file.symbol(local_index);176 const local = elf_file.symbol(local_index);
src/link/Elf/file.zig+1-1
...@@ -84,7 +84,7 @@ pub const File = union(enum) {...@@ -84,7 +84,7 @@ pub const File = union(enum) {
8484
85 pub fn markLive(file: File, elf_file: *Elf) void {85 pub fn markLive(file: File, elf_file: *Elf) void {
86 switch (file) {86 switch (file) {
87 .zig_module, .linker_defined => unreachable,87 .linker_defined => unreachable,
88 inline else => |x| x.markLive(elf_file),88 inline else => |x| x.markLive(elf_file),
89 }89 }
90 }90 }