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 }