kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

rewrite.c (9735B)


      1 #include "obj/rewrite.h"
      2 
      3 #include <limits.h>
      4 #include <string.h>
      5 
      6 #include "obj/elf/elf.h"
      7 
      8 #define SHT_SYMTAB_SHNDX 18u
      9 #define SHN_XINDEX 0xffffu
     10 
     11 typedef struct ElfRewriteView {
     12   ObjRewrite rw;
     13   int is32;
     14   int be;
     15   u64 shoff;
     16   u32 shentsize;
     17   u32 shnum;
     18   u32 shstrndx;
     19   u64 shstr_off;
     20   u64 shstr_size;
     21 } ElfRewriteView;
     22 
     23 typedef struct ElfRewriteSec {
     24   u32 name;
     25   u32 type;
     26   u64 flags;
     27   u64 off;
     28   u64 size;
     29   u32 link;
     30   u32 info;
     31   u64 entsize;
     32   u8* hdr;
     33 } ElfRewriteSec;
     34 
     35 static KitStatus elf_bad(ElfRewriteView* v, const char* detail) {
     36   kit_ctx_diagf(v->rw.ctx, "%.*s: malformed linked ELF image (%s)",
     37                 KIT_SLICE_ARG(v->rw.name), detail);
     38   return KIT_MALFORMED;
     39 }
     40 
     41 static int elf_sec(ElfRewriteView* v, u32 index, ElfRewriteSec* out) {
     42   u64 pos;
     43   u8* p;
     44   if (index >= v->shnum ||
     45       (u64)index > (UINT64_MAX - v->shoff) / v->shentsize)
     46     return 0;
     47   pos = v->shoff + (u64)index * v->shentsize;
     48   if (!obj_rw_range(v->rw.len, pos, v->shentsize)) return 0;
     49   p = v->rw.bytes + (size_t)pos;
     50   memset(out, 0, sizeof *out);
     51   out->name = obj_rw_u32(p, v->be);
     52   out->type = obj_rw_u32(p + 4, v->be);
     53   if (v->is32) {
     54     out->flags = obj_rw_u32(p + 8, v->be);
     55     out->off = obj_rw_u32(p + 16, v->be);
     56     out->size = obj_rw_u32(p + 20, v->be);
     57     out->link = obj_rw_u32(p + 24, v->be);
     58     out->info = obj_rw_u32(p + 28, v->be);
     59     out->entsize = obj_rw_u32(p + 36, v->be);
     60   } else {
     61     out->flags = obj_rw_u64(p + 8, v->be);
     62     out->off = obj_rw_u64(p + 24, v->be);
     63     out->size = obj_rw_u64(p + 32, v->be);
     64     out->link = obj_rw_u32(p + 40, v->be);
     65     out->info = obj_rw_u32(p + 44, v->be);
     66     out->entsize = obj_rw_u64(p + 56, v->be);
     67   }
     68   out->hdr = p;
     69   return 1;
     70 }
     71 
     72 static int elf_name(ElfRewriteView* v, const ElfRewriteSec* s,
     73                     const char** name, size_t* len) {
     74   u64 remain;
     75   const char* p;
     76   size_t n = 0;
     77   if ((u64)s->name >= v->shstr_size) return 0;
     78   remain = v->shstr_size - s->name;
     79   p = (const char*)v->rw.bytes + (size_t)(v->shstr_off + s->name);
     80   while ((u64)n < remain && p[n]) ++n;
     81   if ((u64)n == remain) return 0;
     82   *name = p;
     83   *len = n;
     84   return 1;
     85 }
     86 
     87 static int name_eq(const char* name, size_t len, const char* want) {
     88   size_t n = strlen(want);
     89   return len == n && memcmp(name, want, n) == 0;
     90 }
     91 
     92 static int name_prefix(const char* name, size_t len, const char* prefix) {
     93   size_t n = strlen(prefix);
     94   return len >= n && memcmp(name, prefix, n) == 0;
     95 }
     96 
     97 static int elf_debug_name(const char* name, size_t len) {
     98   return name_prefix(name, len, ".debug_") ||
     99          name_prefix(name, len, ".zdebug_") ||
    100          name_prefix(name, len, ".stab") ||
    101          name_eq(name, len, ".gdb_index") ||
    102          name_eq(name, len, ".gnu_debuglink") ||
    103          name_eq(name, len, ".gnu_debugaltlink") ||
    104          name_eq(name, len, ".ctf") || name_prefix(name, len, ".BTF");
    105 }
    106 
    107 static int elf_signature_name(const char* name, size_t len) {
    108   return name_eq(name, len, ".signature") || name_eq(name, len, ".sig") ||
    109          name_eq(name, len, ".ima") ||
    110          name_eq(name, len, ".note.signature") ||
    111          name_eq(name, len, ".note.sigstore") ||
    112          name_eq(name, len, ".module_sig");
    113 }
    114 
    115 static KitStatus elf_view_init(ElfRewriteView* v, const KitContext* ctx,
    116                                KitSlice name, const KitSlice* input) {
    117   KitStatus st;
    118   u8* e;
    119   u16 type, raw_shnum, raw_shstr;
    120   ElfRewriteSec zero, str;
    121   memset(v, 0, sizeof *v);
    122   st = obj_rewrite_init(&v->rw, ctx, name, input);
    123   if (st != KIT_OK) return st;
    124   e = v->rw.bytes;
    125   if (v->rw.len < ELF32_EHDR_SIZE || e[0] != 0x7f || e[1] != 'E' ||
    126       e[2] != 'L' || e[3] != 'F')
    127     return elf_bad(v, "header");
    128   if (e[EI_CLASS] == ELFCLASS32)
    129     v->is32 = 1;
    130   else if (e[EI_CLASS] != ELFCLASS64)
    131     return elf_bad(v, "class");
    132   if (e[EI_DATA] == ELFDATA2LSB)
    133     v->be = 0;
    134   else if (e[EI_DATA] == 2)
    135     v->be = 1;
    136   else
    137     return elf_bad(v, "byte order");
    138   if ((!v->is32 && v->rw.len < ELF64_EHDR_SIZE))
    139     return elf_bad(v, "truncated header");
    140   type = obj_rw_u16(e + 16, v->be);
    141   if (type == ET_REL) {
    142     kit_ctx_diagf(ctx, "%.*s: relocatable object is not a linked image",
    143                   KIT_SLICE_ARG(name));
    144     return KIT_INVALID;
    145   }
    146   if (type != ET_EXEC && type != ET_DYN)
    147     return elf_bad(v, "unsupported e_type");
    148   if (v->is32) {
    149     v->shoff = obj_rw_u32(e + 32, v->be);
    150     v->shentsize = obj_rw_u16(e + 46, v->be);
    151     raw_shnum = obj_rw_u16(e + 48, v->be);
    152     raw_shstr = obj_rw_u16(e + 50, v->be);
    153     if (v->shentsize < ELF32_SHDR_SIZE) return elf_bad(v, "section size");
    154   } else {
    155     v->shoff = obj_rw_u64(e + 40, v->be);
    156     v->shentsize = obj_rw_u16(e + 58, v->be);
    157     raw_shnum = obj_rw_u16(e + 60, v->be);
    158     raw_shstr = obj_rw_u16(e + 62, v->be);
    159     if (v->shentsize < ELF64_SHDR_SIZE) return elf_bad(v, "section size");
    160   }
    161   v->shnum = raw_shnum;
    162   v->shstrndx = raw_shstr;
    163   if (v->shoff == 0 || !obj_rw_range(v->rw.len, v->shoff, v->shentsize))
    164     return elf_bad(v, "section table");
    165   /* Read section zero with a temporary one-entry bound to recover the ELF
    166    * extended section-count/string-index encodings. */
    167   if (v->shnum == 0 || v->shstrndx == SHN_XINDEX) {
    168     v->shnum = 1;
    169     if (!elf_sec(v, 0, &zero)) return elf_bad(v, "section zero");
    170     if (raw_shnum == 0) {
    171       if (zero.size == 0 || zero.size > UINT32_MAX)
    172         return elf_bad(v, "extended section count");
    173       v->shnum = (u32)zero.size;
    174     } else
    175       v->shnum = raw_shnum;
    176     if (raw_shstr == SHN_XINDEX) v->shstrndx = zero.link;
    177   }
    178   if (v->shnum == 0 || v->shstrndx >= v->shnum ||
    179       !obj_rw_range(v->rw.len, v->shoff,
    180                     (u64)v->shnum * v->shentsize))
    181     return elf_bad(v, "section bounds");
    182   if (!elf_sec(v, v->shstrndx, &str) || str.type != SHT_STRTAB ||
    183       !obj_rw_range(v->rw.len, str.off, str.size))
    184     return elf_bad(v, "section-name table");
    185   v->shstr_off = str.off;
    186   v->shstr_size = str.size;
    187   return KIT_OK;
    188 }
    189 
    190 KitStatus obj_rewrite_elf(const KitContext* ctx, KitSlice name,
    191                           const KitSlice* input,
    192                           const KitLinkedRewriteOptions* opts, KitWriter* out,
    193                           KitLinkedRewriteReport* report_out) {
    194   ElfRewriteView v;
    195   u8* remove = NULL;
    196   size_t remove_size;
    197   u32 i;
    198   int changed;
    199   KitStatus st = elf_view_init(&v, ctx, name, input);
    200   if (st != KIT_OK) {
    201     obj_rewrite_discard(&v.rw);
    202     return st;
    203   }
    204   remove_size = v.shnum ? (size_t)v.shnum : 1u;
    205   remove = (u8*)ctx->heap->alloc(ctx->heap, remove_size, 1u);
    206   if (!remove) {
    207     obj_rewrite_discard(&v.rw);
    208     return KIT_NOMEM;
    209   }
    210   memset(remove, 0, remove_size);
    211 
    212   for (i = 1; i < v.shnum; ++i) {
    213     ElfRewriteSec s;
    214     const char* secname = "<unnamed>";
    215     size_t secname_len = sizeof("<unnamed>") - 1u;
    216     if (!elf_sec(&v, i, &s) || !elf_name(&v, &s, &secname, &secname_len)) {
    217       st = elf_bad(&v, "section record");
    218       goto done;
    219     }
    220     if (elf_signature_name(secname, secname_len)) {
    221       v.rw.report.had_signature = true;
    222       if (opts->remove_signature) remove[i] = 1;
    223     }
    224     if (elf_debug_name(secname, secname_len)) remove[i] = 1;
    225     if (opts->strip_level == KIT_LINKED_STRIP_ALL && s.type == SHT_SYMTAB) {
    226       remove[i] = 1;
    227       if (s.link < v.shnum && s.link != v.shstrndx) remove[s.link] = 1;
    228       if (s.entsize)
    229         v.rw.report.removed_symbols += (u32)(s.size / s.entsize);
    230     }
    231   }
    232   if (v.rw.report.had_signature && !opts->remove_signature) {
    233     kit_ctx_diagf(ctx,
    234                   "%.*s: signed ELF image is unchanged; pass "
    235                   "--remove-signature and sign the result again",
    236                   KIT_SLICE_ARG(name));
    237     st = KIT_UNSUPPORTED;
    238     goto done;
    239   }
    240   v.rw.report.removed_signature =
    241       v.rw.report.had_signature && opts->remove_signature;
    242 
    243   /* Remove relocation/index companions whose target or symbol table is being
    244    * removed. Iterate to a fixed point because a companion can precede its
    245    * target in the section table. */
    246   do {
    247     changed = 0;
    248     for (i = 1; i < v.shnum; ++i) {
    249       ElfRewriteSec s;
    250       if (remove[i] || !elf_sec(&v, i, &s)) continue;
    251       if ((s.type == SHT_RELA || s.type == SHT_REL) &&
    252           ((s.info < v.shnum && remove[s.info]) ||
    253            (s.link < v.shnum && remove[s.link]))) {
    254         remove[i] = 1;
    255         changed = 1;
    256       } else if (s.type == SHT_SYMTAB_SHNDX && s.link < v.shnum &&
    257                  remove[s.link]) {
    258         remove[i] = 1;
    259         changed = 1;
    260       }
    261     }
    262   } while (changed);
    263 
    264   for (i = 1; i < v.shnum; ++i) {
    265     ElfRewriteSec s;
    266     const char* secname = "<unnamed>";
    267     size_t secname_len = sizeof("<unnamed>") - 1u;
    268     int was_debug = 0;
    269     if (!remove[i]) continue;
    270     if (!elf_sec(&v, i, &s)) {
    271       st = elf_bad(&v, "removed section");
    272       goto done;
    273     }
    274     if (elf_name(&v, &s, &secname, &secname_len))
    275       was_debug = elf_debug_name(secname, secname_len) ||
    276                   s.type == SHT_RELA || s.type == SHT_REL;
    277     if (s.flags & SHF_ALLOC) {
    278       kit_ctx_diagf(ctx,
    279                     "%.*s: refusing to remove mapped ELF section %.*s",
    280                     KIT_SLICE_ARG(name), (int)secname_len, secname);
    281       st = KIT_UNSUPPORTED;
    282       goto done;
    283     }
    284     if (s.type != SHT_NOBITS && s.size) {
    285       if (!obj_rw_range(v.rw.len, s.off, s.size)) {
    286         st = elf_bad(&v, "section contents");
    287         goto done;
    288       }
    289       memset(v.rw.bytes + (size_t)s.off, 0, (size_t)s.size);
    290     }
    291     memset(s.hdr, 0, v.shentsize);
    292     if (was_debug) ++v.rw.report.removed_debug_sections;
    293   }
    294 
    295   st = obj_rewrite_commit(&v.rw, out, report_out);
    296 done:
    297   ctx->heap->free(ctx->heap, remove, remove_size);
    298   if (st != KIT_OK) obj_rewrite_discard(&v.rw);
    299   return st;
    300 }