Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(svm): PQR support #501

Open
wants to merge 1 commit into
base: Rexicon226/sbpfv2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 39 additions & 16 deletions src/svm/elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,24 @@ pub const Elf = struct {
shdrs: []align(1) const elf.Elf64_Shdr,
phdrs: []align(1) const elf.Elf64_Phdr,

fn parse(bytes: []const u8) Headers {
fn parse(bytes: []const u8) !Headers {
const header: elf.Elf64_Ehdr = @bitCast(bytes[0..@sizeOf(elf.Elf64_Ehdr)].*);

const shoff = header.e_shoff;
const shnum = header.e_shnum;
const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
const shdrs = std.mem.bytesAsSlice(elf.Elf64_Shdr, bytes[shoff..][0..shsize]);
const shdrs = std.mem.bytesAsSlice(
elf.Elf64_Shdr,
try safeSlice(bytes, shoff, shsize),
);

const phoff = header.e_phoff;
const phnum = header.e_phnum;
const phsize = phnum * @sizeOf(elf.Elf64_Phdr);
const phdrs = std.mem.bytesAsSlice(elf.Elf64_Phdr, bytes[phoff..][0..phsize]);
const phdrs = std.mem.bytesAsSlice(
elf.Elf64_Phdr,
try safeSlice(bytes, phoff, phsize),
);

return .{
.bytes = bytes,
Expand Down Expand Up @@ -342,7 +348,7 @@ pub const Elf = struct {
loader: *BuiltinProgram,
config: Config,
) !Elf {
const headers = Headers.parse(bytes);
const headers = try Headers.parse(bytes);
const data = try Data.parse(headers);

const text_section = data.getShdrByName(headers, ".text") orelse
Expand Down Expand Up @@ -372,6 +378,7 @@ pub const Elf = struct {

_ = try self.function_registry.registerHashedLegacy(
allocator,
!sbpf_version.enableStaticSyscalls(),
"entrypoint",
entry_pc,
);
Expand Down Expand Up @@ -477,7 +484,11 @@ pub const Elf = struct {
const text_section = self.headers.shdrs[text_section_index];

// fixup PC-relative call instructions
const text_bytes: []u8 = self.bytes[text_section.sh_offset..][0..text_section.sh_size];
const text_bytes: []u8 = try safeSlice(
self.bytes,
text_section.sh_offset,
text_section.sh_size,
);
const instructions = self.getInstructions();
for (instructions, 0..) |inst, i| {
if (inst.opcode == .call_imm and
Expand All @@ -489,13 +500,14 @@ pub const Elf = struct {
return error.RelativeJumpOutOfBounds;
const key = try self.function_registry.registerHashedLegacy(
allocator,
!version.enableStaticSyscalls(),
&.{},
@intCast(target_pc),
);
// offset into the instruction where the immediate is stored
const offset = (i *| 8) +| 4;
const slice = text_bytes[offset..][0..4];
std.mem.writeInt(u32, slice, key, .little);
std.mem.writeInt(u32, slice, @intCast(key), .little);
}
}

Expand Down Expand Up @@ -564,14 +576,14 @@ pub const Elf = struct {
// the target is a lddw instruction which takes up two instruction slots

const va_low = val: {
const imm_slice = self.bytes[imm_offset..][0..4];
break :val std.mem.readInt(u32, imm_slice, .little);
const imm_slice = try safeSlice(self.bytes, imm_offset, 4);
break :val std.mem.readInt(u32, imm_slice[0..4], .little);
};

const va_high = val: {
const imm_high_offset = r_offset +| 12;
const imm_slice = self.bytes[imm_high_offset..][0..4];
break :val std.mem.readInt(u32, imm_slice, .little);
const imm_slice = try safeSlice(self.bytes, imm_high_offset, 4);
break :val std.mem.readInt(u32, imm_slice[0..4], .little);
};

var ref_addr = (@as(u64, va_high) << 32) | va_low;
Expand All @@ -582,14 +594,24 @@ pub const Elf = struct {
}

{
const imm_slice = self.bytes[imm_offset..][0..4];
std.mem.writeInt(u32, imm_slice, @truncate(ref_addr), .little);
const imm_slice = try safeSlice(self.bytes, imm_offset, 4);
std.mem.writeInt(
u32,
imm_slice[0..4],
@truncate(ref_addr),
.little,
);
}

{
const imm_high_offset = r_offset +| 12;
const imm_slice = self.bytes[imm_high_offset..][0..4];
std.mem.writeInt(u32, imm_slice, @intCast(ref_addr >> 32), .little);
const imm_slice = try safeSlice(self.bytes, imm_high_offset, 4);
std.mem.writeInt(
u32,
imm_slice[0..4],
@intCast(ref_addr >> 32),
.little,
);
}
} else {
const address: u64 = switch (version) {
Expand Down Expand Up @@ -632,11 +654,12 @@ pub const Elf = struct {
const target_pc = (symbol.st_value -| text_section.sh_addr) / 8;
const key = try self.function_registry.registerHashedLegacy(
allocator,
!version.enableStaticSyscalls(),
symbol_name,
@intCast(target_pc),
);
const slice = try safeSlice(self.bytes, imm_offset, 4);
std.mem.writeInt(u32, slice[0..4], key, .little);
std.mem.writeInt(u32, slice[0..4], @intCast(key), .little);
} else {
const hash = sbpf.hashSymbolName(symbol_name);
if (config.reject_broken_elfs and
Expand Down Expand Up @@ -697,7 +720,7 @@ pub const Elf = struct {
fn safeSlice(base: anytype, start: usize, len: usize) error{OutOfBounds}!@TypeOf(base) {
if (start >= base.len) return error.OutOfBounds;
const end = std.math.add(usize, start, len) catch return error.OutOfBounds;
if (end >= base.len) return error.OutOfBounds;
if (end > base.len) return error.OutOfBounds;
return base[start..][0..len];
}
};
55 changes: 46 additions & 9 deletions src/svm/executable.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,36 @@ pub const Executable = struct {
return Assembler.parse(allocator, source, config);
}

pub fn fromTextBytes(
allocator: std.mem.Allocator,
source: []const u8,
version: sbpf.SBPFVersion,
registry: *Registry(u64),
config: Config,
) !Executable {
const entry_pc = if (registry.lookupName("entrypoint")) |entry_pc|
entry_pc.value
else
try registry.registerHashedLegacy(
allocator,
!version.enableStaticSyscalls(),
"entrypoint",
0,
);

return .{
.instructions = std.mem.bytesAsSlice(sbpf.Instruction, source),
.bytes = source,
.version = version,
.config = config,
.function_registry = registry.*,
.entry_pc = entry_pc,
.ro_section = .{ .borrowed = .{ .offset = 0, .start = 0, .end = 0 } },
.from_elf = false,
.text_vaddr = memory.PROGRAM_START,
};
}

/// When the executable comes from the assembler, we need to guarantee that the
/// instructions are aligned to `sbpf.Instruction` rather than 1 like they would be
/// if we created the executable from the Elf file. The GPA requires allocations and
Expand Down Expand Up @@ -173,7 +203,7 @@ pub const Assembler = struct {
.dst = operands[0].register,
.src = .r0,
.off = 0,
.imm = @bitCast(@as(i32, @intCast(operands[1].integer))),
.imm = @truncate(@as(u64, @bitCast(operands[1].integer))),
} else .{
.opcode = @enumFromInt(bind.opc | sbpf.Instruction.x),
.dst = operands[0].register,
Expand Down Expand Up @@ -332,7 +362,12 @@ pub const Assembler = struct {
const entry_pc = if (function_registry.lookupName("entrypoint")) |entry|
entry.value
else pc: {
_ = try function_registry.registerHashedLegacy(allocator, "entrypoint", 0);
_ = try function_registry.registerHashedLegacy(
allocator,
!config.minimum_version.enableStaticSyscalls(),
"entrypoint",
0,
);
break :pc 0;
};

Expand Down Expand Up @@ -424,7 +459,7 @@ pub const Assembler = struct {

pub fn Registry(T: type) type {
return struct {
map: std.AutoHashMapUnmanaged(u32, Entry) = .{},
map: std.AutoHashMapUnmanaged(u64, Entry) = .{},

const Entry = struct {
name: []const u8,
Expand All @@ -436,7 +471,7 @@ pub fn Registry(T: type) type {
fn register(
self: *Self,
allocator: std.mem.Allocator,
key: u32,
key: u64,
name: []const u8,
value: T,
) !void {
Expand All @@ -455,7 +490,7 @@ pub fn Registry(T: type) type {
allocator: std.mem.Allocator,
name: []const u8,
value: T,
) !u32 {
) !u64 {
const key = sbpf.hashSymbolName(name);
try self.register(allocator, key, name, value);
return key;
Expand All @@ -464,18 +499,20 @@ pub fn Registry(T: type) type {
pub fn registerHashedLegacy(
self: *Self,
allocator: std.mem.Allocator,
hash_symbol_name: bool,
name: []const u8,
value: T,
) !u32 {
) !u64 {
const hash = if (std.mem.eql(u8, name, "entrypoint"))
sbpf.hashSymbolName(name)
else
sbpf.hashSymbolName(&std.mem.toBytes(value));
try self.register(allocator, hash, &.{}, value);
return hash;
const key: u64 = if (hash_symbol_name) hash else value;
try self.register(allocator, key, &.{}, value);
return key;
}

pub fn lookupKey(self: *const Self, key: u32) ?Entry {
pub fn lookupKey(self: *const Self, key: u64) ?Entry {
return self.map.get(key);
}

Expand Down
Loading