Skip to content

Commit

Permalink
svm: PQR support
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexicon226 committed Feb 5, 2025
1 parent 0b6117c commit c98b31b
Show file tree
Hide file tree
Showing 5 changed files with 625 additions and 60 deletions.
53 changes: 38 additions & 15 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(),

Check warning on line 503 in src/svm/elf.zig

View check run for this annotation

Codecov / codecov/patch

src/svm/elf.zig#L503

Added line #L503 was not covered by tests
&.{},
@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(),

Check warning on line 657 in src/svm/elf.zig

View check run for this annotation

Codecov / codecov/patch

src/svm/elf.zig#L657

Added line #L657 was not covered by tests
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);

Check warning on line 662 in src/svm/elf.zig

View check run for this annotation

Codecov / codecov/patch

src/svm/elf.zig#L662

Added line #L662 was not covered by tests
} else {
const hash = sbpf.hashSymbolName(symbol_name);
if (config.reject_broken_elfs and
Expand Down
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

Check warning on line 69 in src/svm/executable.zig

View check run for this annotation

Codecov / codecov/patch

src/svm/executable.zig#L69

Added line #L69 was not covered by tests
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

0 comments on commit c98b31b

Please sign in to comment.