Skip to content

Commit

Permalink
elf: draft out incomplete symbol resolution in objects
Browse files Browse the repository at this point in the history
  • Loading branch information
kubkon committed Sep 22, 2021
1 parent 7ea7d77 commit 122280e
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 7 deletions.
114 changes: 113 additions & 1 deletion src/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const log = std.log.scoped(.elf);
const mem = std.mem;

const Allocator = mem.Allocator;
const Atom = @import("Elf/Atom.zig");
const Object = @import("Elf/Object.zig");
const Zld = @import("Zld.zig");

Expand All @@ -28,7 +29,12 @@ shdrs_offset: ?u64 = null,

entry_address: ?u64 = null,

globals: std.StringArrayHashMapUnmanaged(elf.Elf64_Sym) = .{},
globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},

pub const SymbolWithLoc = struct {
sym_index: u32,
file: u16,
};

pub fn openPath(allocator: *Allocator, options: Zld.Options) !*Elf {
const file = try options.emit.directory.createFile(options.emit.sub_path, .{
Expand Down Expand Up @@ -80,6 +86,12 @@ pub fn closeFiles(self: Elf) void {

pub fn flush(self: *Elf) !void {
try self.parsePositionals(self.base.options.positionals);

for (self.objects.items) |_, object_id| {
try self.resolveSymbolsInObject(@intCast(u16, object_id));
}

try self.logSymtab();
try self.writeHeader();
}

Expand Down Expand Up @@ -127,6 +139,88 @@ fn parseObject(self: *Elf, path: []const u8) !bool {
return true;
}

fn resolveSymbolsInObject(self: *Elf, object_id: u16) !void {
const object = self.objects.items[object_id];

log.debug("resolving symbols in {s}", .{object.name});

for (object.symtab.items) |sym, i| {
const sym_id = @intCast(u32, i);
const sym_name = object.getString(sym.st_name);
const st_bind = sym.st_info >> 4;

switch (st_bind) {
elf.STB_LOCAL => {
log.debug(" (symbol '{s}' local to object; skipping...)", .{sym_name});
continue;
},
elf.STB_WEAK => {
const name = try self.base.allocator.dupe(u8, sym_name);
const res = try self.globals.getOrPut(self.base.allocator, name);
defer if (res.found_existing) self.base.allocator.free(name);

if (!res.found_existing) {
res.value_ptr.* = .{
.sym_index = sym_id,
.file = object_id,
};
continue;
}

const global = res.value_ptr.*;
const linked_obj = self.objects.items[global.file];
const linked_sym = linked_obj.symtab.items[global.sym_index];

if (linked_sym.st_shndx != elf.SHN_UNDEF) {
log.debug(" (symbol '{s}' already defined; skipping...)", .{sym_name});
continue;
}

res.value_ptr.* = .{
.sym_index = sym_id,
.file = object_id,
};
},
elf.STB_GLOBAL => {
const name = try self.base.allocator.dupe(u8, sym_name);
const res = try self.globals.getOrPut(self.base.allocator, name);
defer if (res.found_existing) self.base.allocator.free(name);

if (!res.found_existing) {
res.value_ptr.* = .{
.sym_index = sym_id,
.file = object_id,
};
continue;
}

const global = res.value_ptr.*;
const linked_obj = self.objects.items[global.file];
const linked_sym = linked_obj.symtab.items[global.sym_index];
const linked_sym_bind = linked_sym.st_info >> 4;

if (linked_sym_bind == elf.STB_GLOBAL and linked_sym.st_shndx != elf.SHN_UNDEF) {
log.err("symbol '{s}' defined multiple times", .{sym_name});
log.err(" first definition in '{s}'", .{linked_obj.name});
log.err(" next definition in '{s}'", .{object.name});
return error.MultipleSymbolDefinitions;
}

res.value_ptr.* = .{
.sym_index = sym_id,
.file = object_id,
};
},
else => {
log.err("unhandled symbol binding type: {}", .{st_bind});
log.err(" symbol '{s}'", .{sym_name});
log.err(" first definition in '{s}'", .{object.name});
return error.UnhandledSymbolBindType;
},
}
}
}

fn writeHeader(self: *Elf) !void {
var buffer: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;

Expand Down Expand Up @@ -216,3 +310,21 @@ fn writeHeader(self: *Elf) !void {

try self.base.file.pwriteAll(buffer[0..index], 0);
}

fn logSymtab(self: Elf) !void {
for (self.objects.items) |object| {
log.debug("locals in {s}", .{object.name});
for (object.symtab.items) |sym, i| {
const st_bind = sym.st_info >> 4;
if (st_bind != elf.STB_LOCAL) continue;
log.debug(" {d}: {s}: {}", .{ i, object.getString(sym.st_name), sym });
}
}

log.debug("globals:", .{});
for (self.globals.values()) |global| {
const object = self.objects.items[global.file];
const sym = object.symtab.items[global.sym_index];
log.debug(" {s}: {} => {}", .{ object.getString(sym.st_name), global, sym });
}
}
48 changes: 48 additions & 0 deletions src/Elf/Atom.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const Atom = @This();

const std = @import("std");
const elf = std.elf;

/// Each decl always gets a local symbol with the fully qualified name.
/// The vaddr and size are found here directly.
/// The file offset is found by computing the vaddr offset from the section vaddr
/// the symbol references, and adding that to the file offset of the section.
/// If this field is 0, it means the codegen size = 0 and there is no symbol or
/// offset table entry.
local_sym_index: u32,

/// List of symbol aliases pointing to the same atom via different entries
aliases: std.ArrayListUnmanaged(u32) = .{},

/// List of symbols contained within this atom
contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},

/// Code (may be non-relocated) this atom represents
code: std.ArrayListUnmanaged(u8) = .{},

/// Alignment of this atom as a power of 2.
/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
alignment: u32,

/// List of relocations belonging to this atom.
relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},

/// Points to the previous and next neighbours
next: ?*Atom,
prev: ?*Atom,

pub const SymbolAtOffset = struct {
local_sym_index: u32,
offset: u64,

pub fn format(
self: SymbolAtOffset,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;
try std.fmt.format(writer, "{{ {d}: .offset = {d} }}", .{ self.local_sym_index, self.offset });
}
};
15 changes: 9 additions & 6 deletions src/Elf/Object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const log = std.log.scoped(.elf);
const mem = std.mem;

const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
const Elf = @import("../Elf.zig");

file: fs.File,
name: []const u8,
Expand Down Expand Up @@ -72,11 +74,6 @@ pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void {

try self.parseShdrs(allocator, reader);
try self.parseSymtab(allocator);

log.debug("symtab:", .{});
for (self.symtab.items) |sym, i| {
log.debug(" {d}: {s}: {}", .{ i, self.getString(sym.st_name), sym });
}
}

fn parseShdrs(self: *Object, allocator: *Allocator, reader: anytype) !void {
Expand Down Expand Up @@ -135,7 +132,13 @@ fn parseSymtab(self: *Object, allocator: *Allocator) !void {
}
}

fn getString(self: Object, off: u32) []const u8 {
pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, elf_file: *Elf) !void {
_ = self;
_ = allocator;
_ = elf_file;
}

pub fn getString(self: Object, off: u32) []const u8 {
assert(off < self.strtab.items.len);
return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
}
Expand Down

0 comments on commit 122280e

Please sign in to comment.