summaryrefslogtreecommitdiff
path: root/elf/src/elf.c
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2026-07-09 23:51:55 -0400
committerLLLL Colonq <llll@colonq>2026-07-09 23:51:55 -0400
commit2bdcaf319b1d74ffbaccf08a58336f804761beab (patch)
treec21de6df74ec79b5574ad2b89fcc275d10847802 /elf/src/elf.c
Refactor into monorepo
Diffstat (limited to 'elf/src/elf.c')
-rw-r--r--elf/src/elf.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/elf/src/elf.c b/elf/src/elf.c
new file mode 100644
index 0000000..ed0d145
--- /dev/null
+++ b/elf/src/elf.c
@@ -0,0 +1,490 @@
+#include <lcq/elf.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+i64 elf_read_header_ident(elf_header_ident *ret, u8 *buf, i64 len) {
+ if (len < ELF_HEADER_IDENT_SIZE) return -1;
+ if (buf[0] != 0x7f || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') return -1;
+ ret->file_class = buf[4];
+ ret->endianness = buf[5];
+ ret->version = buf[6];
+ ret->abi = buf[7];
+ ret->abi_version = buf[8];
+ return 0;
+}
+
+i64 elf_write_header_ident(elf_header_ident *i, u8 *buf, i64 len) {
+ if (len < ELF_HEADER_IDENT_SIZE) return -1;
+ buf[0] = 0x7f; buf[1] = 'E'; buf[2] = 'L'; buf[3] = 'F';
+ buf[4] = i->file_class;
+ buf[5] = i->endianness;
+ buf[6] = i->version;
+ buf[7] = i->abi;
+ buf[8] = i->abi_version;
+ return 0;
+}
+
+elf_ctx elf_load_from_path(char *p) {
+ FILE *f = NULL;
+ i64 len = 0;
+ u8 *buf = NULL;
+ elf_ctx ret;
+ ret.buf = NULL; ret.len = -1;
+ if (!p) return ret;
+ f = fopen(p, "r");
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buf = calloc((size_t) len, sizeof(u8));
+ if ((size_t) len != fread(buf, sizeof(u8), (size_t) len, f)
+ || elf_read_header_ident(&ret.ident, buf, len) < 0) {
+ free(buf);
+ } else {
+ ret.buf = buf; ret.len = len;
+ }
+ fclose(f);
+ return ret;
+}
+
+elf_ctx elf_ctx_new(u8 *buf, i64 len, elf_class cl, elf_endianness end) {
+ elf_ctx ret;
+ ret.buf = buf; ret.len = len;
+ ret.ident.file_class = cl;
+ ret.ident.endianness = end;
+ ret.ident.version = 1;
+ ret.ident.abi = 0;
+ ret.ident.abi_version = 0;
+ elf_write_header_ident(&ret.ident, ret.buf, ret.len);
+ return ret;
+}
+
+static u8 read_u8(elf_ctx *ctx, u64 *off) {
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ return ctx->buf[(*off)++];
+}
+static void write_u8(elf_ctx *ctx, u64 *off, u8 x) {
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ ctx->buf[(*off)++] = x;
+}
+static u16 read_u16(elf_ctx *ctx, u64 *off) {
+ u16 b0, b1;
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ assert(ctx->ident.endianness == ELF_ENDIANNESS_LITTLE
+ || ctx->ident.endianness == ELF_ENDIANNESS_BIG);
+ b0 = ctx->buf[(*off)++]; b1 = ctx->buf[(*off)++];
+ if (ctx->ident.endianness == ELF_ENDIANNESS_LITTLE) {
+ return b0 | b1 << 8;
+ } else {
+ return b0 << 8 | b1;
+ }
+}
+static void write_u16(elf_ctx *ctx, u64 *off, u16 x) {
+ u16 b0, b1;
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ assert(ctx->ident.endianness == ELF_ENDIANNESS_LITTLE
+ || ctx->ident.endianness == ELF_ENDIANNESS_BIG);
+ b0 = x; b1 = x >> 8;
+ if (ctx->ident.endianness == ELF_ENDIANNESS_LITTLE) {
+ ctx->buf[(*off)++] = (u8) b0; ctx->buf[(*off)++] = (u8) b1;
+ } else {
+ ctx->buf[(*off)++] = (u8) b1; ctx->buf[(*off)++] = (u8) b0;
+ }
+}
+static u32 read_u32(elf_ctx *ctx, u64 *off) {
+ u32 b0, b1, b2, b3;
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ assert(ctx->ident.endianness == ELF_ENDIANNESS_LITTLE
+ || ctx->ident.endianness == ELF_ENDIANNESS_BIG);
+ b0 = ctx->buf[(*off)++]; b1 = ctx->buf[(*off)++];
+ b2 = ctx->buf[(*off)++]; b3 = ctx->buf[(*off)++];
+ if (ctx->ident.endianness == ELF_ENDIANNESS_LITTLE) {
+ return b0 | b1 << 8 | b2 << 16 | b3 << 24;
+ } else {
+ return b0 << 24 | b1 << 16 | b2 << 8 | b3;
+ }
+}
+static void write_u32(elf_ctx *ctx, u64 *off, u32 x) {
+ u32 b0, b1, b2, b3;
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ assert(ctx->ident.endianness == ELF_ENDIANNESS_LITTLE
+ || ctx->ident.endianness == ELF_ENDIANNESS_BIG);
+ b0 = x; b1 = x >> 8; b2 = x >> 16; b3 = x >> 24;
+ if (ctx->ident.endianness == ELF_ENDIANNESS_LITTLE) {
+ ctx->buf[(*off)++] = (u8) b0; ctx->buf[(*off)++] = (u8) b1;
+ ctx->buf[(*off)++] = (u8) b2; ctx->buf[(*off)++] = (u8) b3;
+ } else {
+ ctx->buf[(*off)++] = (u8) b3; ctx->buf[(*off)++] = (u8) b2;
+ ctx->buf[(*off)++] = (u8) b1; ctx->buf[(*off)++] = (u8) b0;
+ }
+}
+static u64 read_u64(elf_ctx *ctx, u64 *off) {
+ u64 b0, b1, b2, b3, b4, b5, b6, b7;
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ assert(ctx->ident.endianness == ELF_ENDIANNESS_LITTLE
+ || ctx->ident.endianness == ELF_ENDIANNESS_BIG);
+ b0 = ctx->buf[(*off)++]; b1 = ctx->buf[(*off)++];
+ b2 = ctx->buf[(*off)++]; b3 = ctx->buf[(*off)++];
+ b4 = ctx->buf[(*off)++]; b5 = ctx->buf[(*off)++];
+ b6 = ctx->buf[(*off)++]; b7 = ctx->buf[(*off)++];
+ if (ctx->ident.endianness == ELF_ENDIANNESS_LITTLE) {
+ return b0 | b1 << 8 | b2 << 16 | b3 << 24
+ | b4 << 32 | b5 << 40 | b6 << 48 | b7 << 56;
+ } else {
+ return b0 << 56 | b1 << 48 | b2 << 40 | b3 << 32
+ | b4 << 24 | b5 << 16 | b6 << 8 | b7;
+ }
+}
+static void write_u64(elf_ctx *ctx, u64 *off, u64 x) {
+ u64 b0, b1, b2, b3, b4, b5, b6, b7;
+ assert(off != NULL);
+ assert((i64) *off < ctx->len);
+ assert(ctx->ident.endianness == ELF_ENDIANNESS_LITTLE
+ || ctx->ident.endianness == ELF_ENDIANNESS_BIG);
+ b0 = x; b1 = x >> 8; b2 = x >> 16; b3 = x >> 24;
+ b4 = x >> 32; b5 = x >> 40; b6 = x >> 48; b7 = x >> 56;
+ if (ctx->ident.endianness == ELF_ENDIANNESS_LITTLE) {
+ ctx->buf[(*off)++] = (u8) b0; ctx->buf[(*off)++] = (u8) b1;
+ ctx->buf[(*off)++] = (u8) b2; ctx->buf[(*off)++] = (u8) b3;
+ ctx->buf[(*off)++] = (u8) b4; ctx->buf[(*off)++] = (u8) b5;
+ ctx->buf[(*off)++] = (u8) b6; ctx->buf[(*off)++] = (u8) b7;
+ } else {
+ ctx->buf[(*off)++] = (u8) b7; ctx->buf[(*off)++] = (u8) b6;
+ ctx->buf[(*off)++] = (u8) b5; ctx->buf[(*off)++] = (u8) b4;
+ ctx->buf[(*off)++] = (u8) b3; ctx->buf[(*off)++] = (u8) b2;
+ ctx->buf[(*off)++] = (u8) b1; ctx->buf[(*off)++] = (u8) b0;
+ }
+}
+static u64 read_file_class_word(elf_ctx *ctx, u64 *off) {
+ assert(ctx);
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ return (u64) read_u32(ctx, off);
+ } else {
+ return (u64) read_u64(ctx, off);
+ }
+}
+static void write_file_class_word(elf_ctx *ctx, u64 *off, u64 x) {
+ assert(ctx);
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ write_u32(ctx, off, (u32) x);
+ } else {
+ write_u64(ctx, off, x);
+ }
+}
+static i64 read_file_class_signed_word(elf_ctx *ctx, u64 *off) {
+ assert(ctx);
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ return (i64) (i32) read_u32(ctx, off);
+ } else {
+ return (i64) read_u64(ctx, off);
+ }
+}
+static void write_file_class_signed_word(elf_ctx *ctx, u64 *off, i64 x) {
+ assert(ctx);
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ assert(x < UINT32_MAX);
+ write_u32(ctx, off, (u32) (u64) x);
+ } else {
+ write_u64(ctx, off, (u64) x);
+ }
+}
+
+i64 elf_header_size(elf_ctx *ctx) {
+ return ctx->ident.file_class == ELF_CLASS_32
+ ? ELF32_HEADER_SIZE : ELF64_HEADER_SIZE;
+}
+i64 elf_read_header(elf_header *ret, elf_ctx *ctx) {
+ u64 off = ELF_HEADER_IDENT_SIZE;
+ i64 sz = elf_header_size(ctx);
+ if (!ret || !ctx || ctx->len < sz) return -1;
+ ret->type = read_u16(ctx, &off);
+ ret->machine = read_u16(ctx, &off);
+ ret->version = read_u32(ctx, &off);
+ ret->entry = read_file_class_word(ctx, &off);
+ ret->program_header_offset = read_file_class_word(ctx, &off);
+ ret->section_header_offset = read_file_class_word(ctx, &off);
+ ret->flags = read_u32(ctx, &off);
+ ret->elf_header_size = read_u16(ctx, &off);
+ ret->program_header_entry_size = read_u16(ctx, &off);
+ ret->program_header_entries = read_u16(ctx, &off);
+ ret->section_header_entry_size = read_u16(ctx, &off);
+ ret->section_header_entries = read_u16(ctx, &off);
+ ret->section_name_table_index = read_u16(ctx, &off);
+ return 0;
+}
+i64 elf_write_header(elf_header *h, elf_ctx *ctx) {
+ u64 off = ELF_HEADER_IDENT_SIZE;
+ i64 sz = elf_header_size(ctx);
+ if (!h || !ctx || ctx->len < sz) return -1;
+ write_u16(ctx, &off, h->type);
+ write_u16(ctx, &off, h->machine);
+ write_u32(ctx, &off, h->version);
+ printf("entry off: %lu\n", off);
+ write_file_class_word(ctx, &off, h->entry);
+ write_file_class_word(ctx, &off, h->program_header_offset);
+ write_file_class_word(ctx, &off, h->section_header_offset);
+ write_u32(ctx, &off, h->flags);
+ write_u16(ctx, &off, h->elf_header_size);
+ write_u16(ctx, &off, h->program_header_entry_size);
+ write_u16(ctx, &off, h->program_header_entries);
+ write_u16(ctx, &off, h->section_header_entry_size);
+ write_u16(ctx, &off, h->section_header_entries);
+ write_u16(ctx, &off, h->section_name_table_index);
+ return 0;
+}
+
+/* given the index of a program/section header, return its file offset */
+u64 elf_program_header_offset(elf_header *h, u64 idx) {
+ return h->program_header_offset + idx * h->program_header_entry_size;
+}
+u64 elf_section_header_offset(elf_header *h, u64 idx) {
+ return h->section_header_offset + idx * h->section_header_entry_size;
+}
+
+i64 elf_section_header_size(elf_ctx *ctx) {
+ return ctx->ident.file_class == ELF_CLASS_32
+ ? ELF32_SECTION_HEADER_SIZE : ELF64_SECTION_HEADER_SIZE;
+}
+i64 elf_read_section_header(elf_section_header *ret, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_section_header_size(ctx);
+ if (!ret || !ctx || ctx->len < (i64) off + sz) return -1;
+ ret->name_index = read_u32(ctx, &off);
+ ret->type = read_u32(ctx, &off);
+ ret->flags = read_file_class_word(ctx, &off);
+ ret->addr = read_file_class_word(ctx, &off);
+ ret->offset = read_file_class_word(ctx, &off);
+ ret->size = read_file_class_word(ctx, &off);
+ ret->link = read_u32(ctx, &off);
+ ret->info = read_u32(ctx, &off);
+ ret->addr_alignment = read_file_class_word(ctx, &off);
+ ret->entry_size = read_file_class_word(ctx, &off);
+ return 0;
+}
+i64 elf_write_section_header(elf_section_header *sh, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_section_header_size(ctx);
+ if (!sh || !ctx || ctx->len < (i64) off + sz) return -1;
+ printf("section off: %lu\n", off);
+ write_u32(ctx, &off, sh->name_index);
+ write_u32(ctx, &off, sh->type);
+ write_file_class_word(ctx, &off, sh->flags);
+ write_file_class_word(ctx, &off, sh->addr);
+ write_file_class_word(ctx, &off, sh->offset);
+ write_file_class_word(ctx, &off, sh->size);
+ write_u32(ctx, &off, sh->link);
+ write_u32(ctx, &off, sh->info);
+ write_file_class_word(ctx, &off, sh->addr_alignment);
+ write_file_class_word(ctx, &off, sh->entry_size);
+ return 0;
+}
+
+char *elf_read_section_name(elf_ctx *ctx, elf_header *h, elf_section_header *sh) {
+ elf_section_header name_section;
+ if (!ctx || !h || !sh) return NULL;
+ if (h->section_name_table_index >= h->section_header_entries) return NULL;
+ if (elf_read_section_header(&name_section, ctx,
+ elf_section_header_offset(h, h->section_name_table_index)) < 0)
+ return NULL;
+ return (char *) &ctx->buf[name_section.offset + sh->name_index];
+}
+
+i64 elf_symbol_size(elf_ctx *ctx) {
+ return ctx->ident.file_class == ELF_CLASS_32
+ ? ELF32_SYMBOL_SIZE : ELF64_SYMBOL_SIZE;
+}
+i64 elf_read_symbol(elf_symbol *ret, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_symbol_size(ctx);
+ u8 info = 0, other = 0;
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (!ret || !ctx || ctx->len < (i64) off + sz) return -1;
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ ret->name_index = read_u32(ctx, &off);
+ ret->value = read_u32(ctx, &off);
+ ret->size = read_u32(ctx, &off);
+ info = read_u8(ctx, &off);
+ ret->bind = info >> 4;
+ ret->type = info & 0xf;
+ other = read_u8(ctx, &off);
+ ret->visibility = other & 0x3;
+ ret->section_header_index = read_u16(ctx, &off);
+ } else {
+ ret->name_index = read_u32(ctx, &off);
+ info = read_u8(ctx, &off);
+ ret->bind = info >> 4;
+ ret->type = info & 0xf;
+ other = read_u8(ctx, &off);
+ ret->visibility = other & 0x3;
+ ret->section_header_index = read_u16(ctx, &off);
+ ret->value = read_u64(ctx, &off);
+ ret->size = read_u64(ctx, &off);
+ }
+ return 0;
+}
+i64 elf_write_symbol(elf_symbol *sym, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_symbol_size(ctx);
+ u8 info, other;
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (!sym || !ctx || ctx->len < (i64) off + sz) return -1;
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ write_u32(ctx, &off, sym->name_index);
+ write_u32(ctx, &off, (u32) sym->value);
+ write_u32(ctx, &off, (u32) sym->size);
+ info = (u8) sym->bind << 4 | sym->type;
+ write_u8(ctx, &off, info);
+ other = sym->visibility;
+ write_u8(ctx, &off, other);
+ write_u16(ctx, &off, sym->section_header_index);
+ } else {
+ write_u32(ctx, &off, sym->name_index);
+ info = (u8) sym->bind << 4 | sym->type;
+ write_u8(ctx, &off, info);
+ other = sym->visibility;
+ write_u8(ctx, &off, other);
+ write_u16(ctx, &off, sym->section_header_index);
+ write_u64(ctx, &off, sym->value);
+ write_u64(ctx, &off, sym->size);
+ }
+ return 0;
+}
+
+static void parse_relocation_info(
+ u64 *symtab_index, u64 *type, elf_ctx *ctx, u64 info
+) {
+ assert(symtab_index != NULL && type != NULL);
+ assert(ctx->ident.file_class == ELF_CLASS_32
+ || ctx->ident.file_class == ELF_CLASS_64);
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ info &= 0xffffffff;
+ *symtab_index = info >> 8;
+ *type = (u8) info;
+ } else {
+ *symtab_index = info > 32;
+ *type = info & 0xffffffff;
+ }
+}
+char *elf_read_symbol_name(
+ elf_ctx *ctx, elf_header *h, elf_section_header *sh, elf_symbol *sym
+) {
+ elf_section_header name_section;
+ if (!ctx || !sh || !sh) return NULL;
+ if (!(sh->type == ELF_SECTION_TYPE_SYMTAB
+ || sh->type == ELF_SECTION_TYPE_DYNSYM))
+ return NULL;
+ if (sh->link >= h->section_header_entries) return NULL;
+ if (elf_read_section_header(&name_section, ctx,
+ elf_section_header_offset(h, sh->link)) < 0)
+ return NULL;
+ return (char *) &ctx->buf[name_section.offset + sym->name_index];
+}
+
+i64 elf_rel_size(elf_ctx *ctx) {
+ return ctx->ident.file_class == ELF_CLASS_32
+ ? ELF32_REL_SIZE : ELF64_REL_SIZE;
+}
+i64 elf_read_rel(elf_rel *ret, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_rel_size(ctx);
+ u64 info;
+ if (!ret || !ctx || ctx->len < (i64) off + sz) return -1;
+ ret->offset = read_file_class_word(ctx, &off);
+ info = read_file_class_word(ctx, &off);
+ parse_relocation_info(&ret->symtab_index, &ret->type, ctx, info);
+ return 0;
+}
+
+i64 elf_rela_size(elf_ctx *ctx) {
+ return ctx->ident.file_class == ELF_CLASS_32
+ ? ELF32_RELA_SIZE : ELF64_RELA_SIZE;
+}
+i64 elf_read_rela(elf_rela *ret, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_rela_size(ctx);
+ u64 info;
+ if (!ret || !ctx || ctx->len < (i64) off + sz) return -1;
+ ret->offset = read_file_class_word(ctx, &off);
+ info = read_file_class_word(ctx, &off);
+ parse_relocation_info(&ret->symtab_index, &ret->type, ctx, info);
+ ret->addend = read_file_class_signed_word(ctx, &off);
+ return 0;
+}
+
+i64 elf_program_header_size(elf_ctx *ctx) {
+ return ctx->ident.file_class == ELF_CLASS_32
+ ? ELF32_PROGRAM_HEADER_SIZE : ELF64_PROGRAM_HEADER_SIZE;
+}
+i64 elf_read_program_header(elf_program_header *ret, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_program_header_size(ctx);
+ if (!ret || !ctx || ctx->len < (i64) off + sz) return -1;
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ ret->type = read_u32(ctx, &off);
+ ret->offset = read_file_class_word(ctx, &off);
+ ret->virtual_addr = read_file_class_word(ctx, &off);
+ ret->physical_addr = read_file_class_word(ctx, &off);
+ ret->file_size = read_u32(ctx, &off);
+ ret->mem_size = read_u32(ctx, &off);
+ ret->flags = read_u32(ctx, &off);
+ ret->align = read_u32(ctx, &off);
+ } else {
+ ret->type = read_u32(ctx, &off);
+ ret->flags = read_u32(ctx, &off);
+ ret->offset = read_file_class_word(ctx, &off);
+ ret->virtual_addr = read_file_class_word(ctx, &off);
+ ret->physical_addr = read_file_class_word(ctx, &off);
+ ret->file_size = read_u64(ctx, &off);
+ ret->mem_size = read_u64(ctx, &off);
+ ret->align = read_u64(ctx, &off);
+ }
+ return 0;
+}
+i64 elf_write_program_header(elf_program_header *ph, elf_ctx *ctx, u64 off) {
+ i64 sz = elf_program_header_size(ctx);
+ if (!ph || !ctx || ctx->len < (i64) off + sz) return -1;
+ if (ctx->ident.file_class == ELF_CLASS_32) {
+ write_u32(ctx, &off, ph->type);
+ write_file_class_word(ctx, &off, ph->offset);
+ write_file_class_word(ctx, &off, ph->virtual_addr);
+ write_file_class_word(ctx, &off, ph->physical_addr);
+ write_u32(ctx, &off, (u32) ph->file_size);
+ write_u32(ctx, &off, (u32) ph->mem_size);
+ write_u32(ctx, &off, ph->flags);
+ write_u32(ctx, &off, (u32) ph->align);
+ } else {
+ write_u32(ctx, &off, ph->type);
+ write_u32(ctx, &off, ph->flags);
+ write_file_class_word(ctx, &off, ph->offset);
+ write_file_class_word(ctx, &off, ph->virtual_addr);
+ write_file_class_word(ctx, &off, ph->physical_addr);
+ write_u64(ctx, &off, ph->file_size);
+ write_u64(ctx, &off, ph->mem_size);
+ write_u64(ctx, &off, ph->align);
+ }
+ return 0;
+}
+
+i64 elf_write_bytes(elf_ctx *ctx, u64 *off, u8 *buf, i64 len) {
+ i64 i = 0;
+ if (!ctx || !buf || ctx->len < (i64) *off + len) return -1;
+ for (; i < len; ++i) {
+ printf("byte off: %ld\n", (i64) *off + i);
+ ctx->buf[(i64) *off + i] = buf[i];
+ }
+ *off += (u64) len;
+ return 0;
+}