mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-28 03:17:08 +03:00
8cba6b1df8
This is f5fb720a5399ee98e45f36337b2f68a4d23a783c plus ehaas's nonnull attribute pull request currently at 4b26cb3ac610a0a070fc43e43da8b4cdf0e9101b with zig patches intact.
377 lines
13 KiB
Zig
Vendored
377 lines
13 KiB
Zig
Vendored
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const Target = std.Target;
|
|
const Object = @import("../Object.zig");
|
|
|
|
const Section = struct {
|
|
data: std.ArrayList(u8) = .empty,
|
|
relocations: std.ArrayList(Relocation) = .empty,
|
|
flags: u64,
|
|
type: u32,
|
|
index: u16 = undefined,
|
|
};
|
|
|
|
const Symbol = struct {
|
|
section: ?*Section,
|
|
size: u64,
|
|
offset: u64,
|
|
index: u16 = undefined,
|
|
info: u8,
|
|
};
|
|
|
|
const Relocation = struct {
|
|
symbol: *Symbol,
|
|
addend: i64,
|
|
offset: u48,
|
|
type: u8,
|
|
};
|
|
|
|
const additional_sections = 3; // null section, strtab, symtab
|
|
const strtab_index = 1;
|
|
const symtab_index = 2;
|
|
const strtab_default = "\x00.strtab\x00.symtab\x00";
|
|
const strtab_name = 1;
|
|
const symtab_name = "\x00.strtab\x00".len;
|
|
|
|
const Elf = @This();
|
|
|
|
obj: Object,
|
|
/// The keys are owned by the Codegen.tree
|
|
sections: std.StringHashMapUnmanaged(*Section) = .empty,
|
|
local_symbols: std.StringHashMapUnmanaged(*Symbol) = .empty,
|
|
global_symbols: std.StringHashMapUnmanaged(*Symbol) = .empty,
|
|
unnamed_symbol_mangle: u32 = 0,
|
|
strtab_len: u64 = strtab_default.len,
|
|
arena: std.heap.ArenaAllocator,
|
|
|
|
pub fn create(gpa: Allocator, target: Target) !*Object {
|
|
const elf = try gpa.create(Elf);
|
|
elf.* = .{
|
|
.obj = .{ .format = .elf, .target = target },
|
|
.arena = std.heap.ArenaAllocator.init(gpa),
|
|
};
|
|
return &elf.obj;
|
|
}
|
|
|
|
pub fn deinit(elf: *Elf) void {
|
|
const gpa = elf.arena.child_allocator;
|
|
{
|
|
var it = elf.sections.valueIterator();
|
|
while (it.next()) |sect| {
|
|
sect.*.data.deinit(gpa);
|
|
sect.*.relocations.deinit(gpa);
|
|
}
|
|
}
|
|
elf.sections.deinit(gpa);
|
|
elf.local_symbols.deinit(gpa);
|
|
elf.global_symbols.deinit(gpa);
|
|
elf.arena.deinit();
|
|
gpa.destroy(elf);
|
|
}
|
|
|
|
fn sectionString(sec: Object.Section) []const u8 {
|
|
return switch (sec) {
|
|
.undefined => unreachable,
|
|
.data => "data",
|
|
.read_only_data => "rodata",
|
|
.func => "text",
|
|
.strings => "rodata.str",
|
|
.custom => |name| name,
|
|
};
|
|
}
|
|
|
|
pub fn getSection(elf: *Elf, section_kind: Object.Section) !*std.ArrayList(u8) {
|
|
const section_name = sectionString(section_kind);
|
|
const section = elf.sections.get(section_name) orelse blk: {
|
|
const section = try elf.arena.allocator().create(Section);
|
|
section.* = .{
|
|
.data = std.ArrayList(u8).init(elf.arena.child_allocator),
|
|
.type = std.elf.SHT_PROGBITS,
|
|
.flags = switch (section_kind) {
|
|
.func, .custom => std.elf.SHF_ALLOC + std.elf.SHF_EXECINSTR,
|
|
.strings => std.elf.SHF_ALLOC + std.elf.SHF_MERGE + std.elf.SHF_STRINGS,
|
|
.read_only_data => std.elf.SHF_ALLOC,
|
|
.data => std.elf.SHF_ALLOC + std.elf.SHF_WRITE,
|
|
.undefined => unreachable,
|
|
},
|
|
};
|
|
try elf.sections.putNoClobber(elf.arena.child_allocator, section_name, section);
|
|
elf.strtab_len += section_name.len + ".\x00".len;
|
|
break :blk section;
|
|
};
|
|
return §ion.data;
|
|
}
|
|
|
|
pub fn declareSymbol(
|
|
elf: *Elf,
|
|
section_kind: Object.Section,
|
|
maybe_name: ?[]const u8,
|
|
linkage: std.builtin.GlobalLinkage,
|
|
@"type": Object.SymbolType,
|
|
offset: u64,
|
|
size: u64,
|
|
) ![]const u8 {
|
|
const section = blk: {
|
|
if (section_kind == .undefined) break :blk null;
|
|
const section_name = sectionString(section_kind);
|
|
break :blk elf.sections.get(section_name);
|
|
};
|
|
const binding: u8 = switch (linkage) {
|
|
.Internal => std.elf.STB_LOCAL,
|
|
.Strong => std.elf.STB_GLOBAL,
|
|
.Weak => std.elf.STB_WEAK,
|
|
.LinkOnce => unreachable,
|
|
};
|
|
const sym_type: u8 = switch (@"type") {
|
|
.func => std.elf.STT_FUNC,
|
|
.variable => std.elf.STT_OBJECT,
|
|
.external => std.elf.STT_NOTYPE,
|
|
};
|
|
const name = if (maybe_name) |some| some else blk: {
|
|
defer elf.unnamed_symbol_mangle += 1;
|
|
break :blk try std.fmt.allocPrint(elf.arena.allocator(), ".L.{d}", .{elf.unnamed_symbol_mangle});
|
|
};
|
|
|
|
const gop = if (linkage == .Internal)
|
|
try elf.local_symbols.getOrPut(elf.arena.child_allocator, name)
|
|
else
|
|
try elf.global_symbols.getOrPut(elf.arena.child_allocator, name);
|
|
|
|
if (!gop.found_existing) {
|
|
gop.value_ptr.* = try elf.arena.allocator().create(Symbol);
|
|
elf.strtab_len += name.len + 1; // +1 for null byte
|
|
}
|
|
gop.value_ptr.*.* = .{
|
|
.section = section,
|
|
.size = size,
|
|
.offset = offset,
|
|
.info = (binding << 4) + sym_type,
|
|
};
|
|
return name;
|
|
}
|
|
|
|
pub fn addRelocation(elf: *Elf, name: []const u8, section_kind: Object.Section, address: u64, addend: i64) !void {
|
|
const section_name = sectionString(section_kind);
|
|
const symbol = elf.local_symbols.get(name) orelse elf.global_symbols.get(name).?; // reference to undeclared symbol
|
|
const section = elf.sections.get(section_name).?;
|
|
if (section.relocations.items.len == 0) elf.strtab_len += ".rela".len;
|
|
|
|
try section.relocations.append(elf.arena.child_allocator, .{
|
|
.symbol = symbol,
|
|
.offset = @intCast(address),
|
|
.addend = addend,
|
|
.type = if (symbol.section == null) 4 else 2, // TODO
|
|
});
|
|
}
|
|
|
|
/// elf header
|
|
/// sections contents
|
|
/// symbols
|
|
/// relocations
|
|
/// strtab
|
|
/// section headers
|
|
pub fn finish(elf: *Elf, w: *std.Io.Writer) !void {
|
|
var num_sections: std.elf.Half = additional_sections;
|
|
var relocations_len: std.elf.Elf64_Off = 0;
|
|
var sections_len: std.elf.Elf64_Off = 0;
|
|
{
|
|
var it = elf.sections.valueIterator();
|
|
while (it.next()) |sect| {
|
|
sections_len += sect.*.data.items.len;
|
|
relocations_len += sect.*.relocations.items.len * @sizeOf(std.elf.Elf64_Rela);
|
|
sect.*.index = num_sections;
|
|
num_sections += 1;
|
|
num_sections += @intFromBool(sect.*.relocations.items.len != 0);
|
|
}
|
|
}
|
|
const symtab_len = (elf.local_symbols.count() + elf.global_symbols.count() + 1) * @sizeOf(std.elf.Elf64_Sym);
|
|
|
|
const symtab_offset = @sizeOf(std.elf.Elf64_Ehdr) + sections_len;
|
|
const symtab_offset_aligned = std.mem.alignForward(u64, symtab_offset, 8);
|
|
const rela_offset = symtab_offset_aligned + symtab_len;
|
|
const strtab_offset = rela_offset + relocations_len;
|
|
const sh_offset = strtab_offset + elf.strtab_len;
|
|
const sh_offset_aligned = std.mem.alignForward(u64, sh_offset, 16);
|
|
const endian = elf.obj.target.cpu.arch.endian();
|
|
|
|
const elf_header: std.elf.Elf64_Ehdr = .{
|
|
.e_ident = .{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
|
.e_type = std.elf.ET.REL, // we only produce relocatables
|
|
.e_machine = elf.obj.target.toElfMachine(),
|
|
.e_version = 1,
|
|
.e_entry = 0, // linker will handle this
|
|
.e_phoff = 0, // no program header
|
|
.e_shoff = sh_offset_aligned, // section headers offset
|
|
.e_flags = 0, // no flags
|
|
.e_ehsize = @sizeOf(std.elf.Elf64_Ehdr),
|
|
.e_phentsize = 0, // no program header
|
|
.e_phnum = 0, // no program header
|
|
.e_shentsize = @sizeOf(std.elf.Elf64_Shdr),
|
|
.e_shnum = num_sections,
|
|
.e_shstrndx = strtab_index,
|
|
};
|
|
try w.writeStruct(elf_header, endian);
|
|
|
|
// write contents of sections
|
|
{
|
|
var it = elf.sections.valueIterator();
|
|
while (it.next()) |sect| try w.writeAll(sect.*.data.items);
|
|
}
|
|
|
|
// pad to 8 bytes
|
|
try w.splatByteAll(0, @intCast(symtab_offset_aligned - symtab_offset));
|
|
|
|
var name_offset: u32 = strtab_default.len;
|
|
// write symbols
|
|
{
|
|
// first symbol must be null
|
|
try w.writeStruct(std.mem.zeroes(std.elf.Elf64_Sym), endian);
|
|
|
|
var sym_index: u16 = 1;
|
|
var it = elf.local_symbols.iterator();
|
|
while (it.next()) |entry| {
|
|
const sym = entry.value_ptr.*;
|
|
try w.writeStruct(std.elf.Elf64_Sym{
|
|
.st_name = name_offset,
|
|
.st_info = sym.info,
|
|
.st_other = 0,
|
|
.st_shndx = if (sym.section) |some| some.index else 0,
|
|
.st_value = sym.offset,
|
|
.st_size = sym.size,
|
|
}, endian);
|
|
sym.index = sym_index;
|
|
sym_index += 1;
|
|
name_offset += @intCast(entry.key_ptr.len + 1); // +1 for null byte
|
|
}
|
|
it = elf.global_symbols.iterator();
|
|
while (it.next()) |entry| {
|
|
const sym = entry.value_ptr.*;
|
|
try w.writeStruct(std.elf.Elf64_Sym{
|
|
.st_name = name_offset,
|
|
.st_info = sym.info,
|
|
.st_other = 0,
|
|
.st_shndx = if (sym.section) |some| some.index else 0,
|
|
.st_value = sym.offset,
|
|
.st_size = sym.size,
|
|
}, endian);
|
|
sym.index = sym_index;
|
|
sym_index += 1;
|
|
name_offset += @intCast(entry.key_ptr.len + 1); // +1 for null byte
|
|
}
|
|
}
|
|
|
|
// write relocations
|
|
{
|
|
var it = elf.sections.valueIterator();
|
|
while (it.next()) |sect| {
|
|
for (sect.*.relocations.items) |rela| {
|
|
try w.writeStruct(std.elf.Elf64_Rela{
|
|
.r_offset = rela.offset,
|
|
.r_addend = rela.addend,
|
|
.r_info = (@as(u64, rela.symbol.index) << 32) | rela.type,
|
|
}, endian);
|
|
}
|
|
}
|
|
}
|
|
|
|
// write strtab
|
|
try w.writeAll(strtab_default);
|
|
{
|
|
var it = elf.local_symbols.keyIterator();
|
|
while (it.next()) |key| try w.print("{s}\x00", .{key.*});
|
|
it = elf.global_symbols.keyIterator();
|
|
while (it.next()) |key| try w.print("{s}\x00", .{key.*});
|
|
}
|
|
{
|
|
var it = elf.sections.iterator();
|
|
while (it.next()) |entry| {
|
|
if (entry.value_ptr.*.relocations.items.len != 0) try w.writeAll(".rela");
|
|
try w.print(".{s}\x00", .{entry.key_ptr.*});
|
|
}
|
|
}
|
|
|
|
// pad to 16 bytes
|
|
try w.splatByteAll(0, @intCast(sh_offset_aligned - sh_offset));
|
|
// mandatory null header
|
|
try w.writeStruct(std.mem.zeroes(std.elf.Elf64_Shdr), endian);
|
|
|
|
// write strtab section header
|
|
{
|
|
const sect_header: std.elf.Elf64_Shdr = .{
|
|
.sh_name = strtab_name,
|
|
.sh_type = std.elf.SHT_STRTAB,
|
|
.sh_flags = 0,
|
|
.sh_addr = 0,
|
|
.sh_offset = strtab_offset,
|
|
.sh_size = elf.strtab_len,
|
|
.sh_link = 0,
|
|
.sh_info = 0,
|
|
.sh_addralign = 1,
|
|
.sh_entsize = 0,
|
|
};
|
|
try w.writeStruct(sect_header, endian);
|
|
}
|
|
|
|
// write symtab section header
|
|
{
|
|
const sect_header: std.elf.Elf64_Shdr = .{
|
|
.sh_name = symtab_name,
|
|
.sh_type = std.elf.SHT_SYMTAB,
|
|
.sh_flags = 0,
|
|
.sh_addr = 0,
|
|
.sh_offset = symtab_offset_aligned,
|
|
.sh_size = symtab_len,
|
|
.sh_link = strtab_index,
|
|
.sh_info = elf.local_symbols.size + 1,
|
|
.sh_addralign = 8,
|
|
.sh_entsize = @sizeOf(std.elf.Elf64_Sym),
|
|
};
|
|
try w.writeStruct(sect_header, endian);
|
|
}
|
|
|
|
// remaining section headers
|
|
{
|
|
var sect_offset: u64 = @sizeOf(std.elf.Elf64_Ehdr);
|
|
var rela_sect_offset: u64 = rela_offset;
|
|
var it = elf.sections.iterator();
|
|
while (it.next()) |entry| {
|
|
const sect = entry.value_ptr.*;
|
|
const rela_count = sect.relocations.items.len;
|
|
const rela_name_offset: u32 = if (rela_count != 0) @truncate(".rela".len) else 0;
|
|
try w.writeStruct(std.elf.Elf64_Shdr{
|
|
.sh_name = rela_name_offset + name_offset,
|
|
.sh_type = sect.type,
|
|
.sh_flags = sect.flags,
|
|
.sh_addr = 0,
|
|
.sh_offset = sect_offset,
|
|
.sh_size = sect.data.items.len,
|
|
.sh_link = 0,
|
|
.sh_info = 0,
|
|
.sh_addralign = if (sect.flags & std.elf.SHF_EXECINSTR != 0) 16 else 1,
|
|
.sh_entsize = 0,
|
|
}, endian);
|
|
|
|
if (rela_count != 0) {
|
|
const size = rela_count * @sizeOf(std.elf.Elf64_Rela);
|
|
try w.writeStruct(std.elf.Elf64_Shdr{
|
|
.sh_name = name_offset,
|
|
.sh_type = std.elf.SHT_RELA,
|
|
.sh_flags = 0,
|
|
.sh_addr = 0,
|
|
.sh_offset = rela_sect_offset,
|
|
.sh_size = rela_count * @sizeOf(std.elf.Elf64_Rela),
|
|
.sh_link = symtab_index,
|
|
.sh_info = sect.index,
|
|
.sh_addralign = 8,
|
|
.sh_entsize = @sizeOf(std.elf.Elf64_Rela),
|
|
}, endian);
|
|
rela_sect_offset += size;
|
|
}
|
|
|
|
sect_offset += sect.data.items.len;
|
|
name_offset += @as(u32, @intCast(entry.key_ptr.len + ".\x00".len)) + rela_name_offset;
|
|
}
|
|
}
|
|
try w.flush();
|
|
}
|