link_image_id.c (1797B)
1 /* link_image_id_compute: format-agnostic 16-byte identity hash for a 2 * resolved LinkImage. 3 * 4 * Two FNV-1a 64-bit streams with different seeds produce 128 bits. The 5 * mix covers each segment's vaddr, file_size, and post-shift bytes, so 6 * the digest changes if either content or layout shifts. Determinism 7 * (no time / random component) is intentional — reproducible builds. 8 * 9 * Wrapped per format: 10 * - ELF .note.gnu.build-id (link_emit_elf) 11 * - Mach-O LC_UUID payload (link_emit_macho, Phase 3) 12 * - COFF/PE debug directory (deferred) 13 * 14 * Lived in link_elf.c through Phase 0; lifted out so the Mach-O writer 15 * sees the same bytes. */ 16 17 #include "core/core.h" 18 #include "link/link_internal.h" 19 20 static u64 fnv1a64(const u8* data, size_t n, u64 seed) { 21 const u64 PRIME = 0x100000001b3ull; 22 u64 h = seed; 23 size_t i; 24 for (i = 0; i < n; ++i) { 25 h ^= (u64)data[i]; 26 h *= PRIME; 27 } 28 return h; 29 } 30 31 void link_image_id_compute(const LinkImage* img, u8 out[LINK_IMAGE_ID_BYTES]) { 32 const u64 SEED_LO = 0xcbf29ce484222325ull; 33 const u64 SEED_HI = 0x14650fb0739d0383ull; 34 u64 lo = SEED_LO, hi = SEED_HI; 35 u32 i; 36 for (i = 0; i < img->nsegments; ++i) { 37 const LinkSegment* seg = &img->segments[i]; 38 u64 vaddr = seg->vaddr; 39 u64 fsz = seg->file_size; 40 lo = fnv1a64((const u8*)&vaddr, sizeof vaddr, lo); 41 lo = fnv1a64((const u8*)&fsz, sizeof fsz, lo); 42 hi = fnv1a64((const u8*)&vaddr, sizeof vaddr, hi); 43 hi = fnv1a64((const u8*)&fsz, sizeof fsz, hi); 44 if (img->segment_bytes[i] && fsz) { 45 lo = fnv1a64(img->segment_bytes[i], (size_t)fsz, lo); 46 hi = fnv1a64(img->segment_bytes[i], (size_t)fsz, hi); 47 } 48 } 49 for (i = 0; i < 8; ++i) out[i] = (u8)(lo >> (i * 8)); 50 for (i = 0; i < 8; ++i) out[8 + i] = (u8)(hi >> (i * 8)); 51 }