xref: /qemu/linux-user/gen-vdso-elfn.c.inc (revision e34136d9)
12fa536d1SRichard Henderson/*
22fa536d1SRichard Henderson * Post-process a vdso elf image for inclusion into qemu.
32fa536d1SRichard Henderson * Elf size specialization.
42fa536d1SRichard Henderson *
52fa536d1SRichard Henderson * Copyright 2023 Linaro, Ltd.
62fa536d1SRichard Henderson *
72fa536d1SRichard Henderson * SPDX-License-Identifier: GPL-2.0-or-later
82fa536d1SRichard Henderson */
92fa536d1SRichard Henderson
102fa536d1SRichard Hendersonstatic void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr)
112fa536d1SRichard Henderson{
122fa536d1SRichard Henderson    bswaps(&ehdr->e_type);            /* Object file type */
132fa536d1SRichard Henderson    bswaps(&ehdr->e_machine);         /* Architecture */
142fa536d1SRichard Henderson    bswaps(&ehdr->e_version);         /* Object file version */
152fa536d1SRichard Henderson    bswaps(&ehdr->e_entry);           /* Entry point virtual address */
162fa536d1SRichard Henderson    bswaps(&ehdr->e_phoff);           /* Program header table file offset */
172fa536d1SRichard Henderson    bswaps(&ehdr->e_shoff);           /* Section header table file offset */
182fa536d1SRichard Henderson    bswaps(&ehdr->e_flags);           /* Processor-specific flags */
192fa536d1SRichard Henderson    bswaps(&ehdr->e_ehsize);          /* ELF header size in bytes */
202fa536d1SRichard Henderson    bswaps(&ehdr->e_phentsize);       /* Program header table entry size */
212fa536d1SRichard Henderson    bswaps(&ehdr->e_phnum);           /* Program header table entry count */
222fa536d1SRichard Henderson    bswaps(&ehdr->e_shentsize);       /* Section header table entry size */
232fa536d1SRichard Henderson    bswaps(&ehdr->e_shnum);           /* Section header table entry count */
242fa536d1SRichard Henderson    bswaps(&ehdr->e_shstrndx);        /* Section header string table index */
252fa536d1SRichard Henderson}
262fa536d1SRichard Henderson
272fa536d1SRichard Hendersonstatic void elfN(bswap_phdr)(ElfN(Phdr) *phdr)
282fa536d1SRichard Henderson{
292fa536d1SRichard Henderson    bswaps(&phdr->p_type);            /* Segment type */
302fa536d1SRichard Henderson    bswaps(&phdr->p_flags);           /* Segment flags */
312fa536d1SRichard Henderson    bswaps(&phdr->p_offset);          /* Segment file offset */
322fa536d1SRichard Henderson    bswaps(&phdr->p_vaddr);           /* Segment virtual address */
332fa536d1SRichard Henderson    bswaps(&phdr->p_paddr);           /* Segment physical address */
342fa536d1SRichard Henderson    bswaps(&phdr->p_filesz);          /* Segment size in file */
352fa536d1SRichard Henderson    bswaps(&phdr->p_memsz);           /* Segment size in memory */
362fa536d1SRichard Henderson    bswaps(&phdr->p_align);           /* Segment alignment */
372fa536d1SRichard Henderson}
382fa536d1SRichard Henderson
392fa536d1SRichard Hendersonstatic void elfN(bswap_shdr)(ElfN(Shdr) *shdr)
402fa536d1SRichard Henderson{
412fa536d1SRichard Henderson    bswaps(&shdr->sh_name);
422fa536d1SRichard Henderson    bswaps(&shdr->sh_type);
432fa536d1SRichard Henderson    bswaps(&shdr->sh_flags);
442fa536d1SRichard Henderson    bswaps(&shdr->sh_addr);
452fa536d1SRichard Henderson    bswaps(&shdr->sh_offset);
462fa536d1SRichard Henderson    bswaps(&shdr->sh_size);
472fa536d1SRichard Henderson    bswaps(&shdr->sh_link);
482fa536d1SRichard Henderson    bswaps(&shdr->sh_info);
492fa536d1SRichard Henderson    bswaps(&shdr->sh_addralign);
502fa536d1SRichard Henderson    bswaps(&shdr->sh_entsize);
512fa536d1SRichard Henderson}
522fa536d1SRichard Henderson
532fa536d1SRichard Hendersonstatic void elfN(bswap_sym)(ElfN(Sym) *sym)
542fa536d1SRichard Henderson{
552fa536d1SRichard Henderson    bswaps(&sym->st_name);
562fa536d1SRichard Henderson    bswaps(&sym->st_value);
572fa536d1SRichard Henderson    bswaps(&sym->st_size);
582fa536d1SRichard Henderson    bswaps(&sym->st_shndx);
592fa536d1SRichard Henderson}
602fa536d1SRichard Henderson
612fa536d1SRichard Hendersonstatic void elfN(bswap_dyn)(ElfN(Dyn) *dyn)
622fa536d1SRichard Henderson{
632fa536d1SRichard Henderson    bswaps(&dyn->d_tag);              /* Dynamic type tag */
642fa536d1SRichard Henderson    bswaps(&dyn->d_un.d_ptr);         /* Dynamic ptr or val, in union */
652fa536d1SRichard Henderson}
662fa536d1SRichard Henderson
672fa536d1SRichard Hendersonstatic void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx,
682fa536d1SRichard Henderson                                void *buf, bool need_bswap)
692fa536d1SRichard Henderson{
702fa536d1SRichard Henderson    unsigned str_idx = shdr[sym_idx].sh_link;
712fa536d1SRichard Henderson    ElfN(Sym) *sym = buf + shdr[sym_idx].sh_offset;
722fa536d1SRichard Henderson    unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym);
732fa536d1SRichard Henderson    const char *str = buf + shdr[str_idx].sh_offset;
742fa536d1SRichard Henderson
752fa536d1SRichard Henderson    for (unsigned i = 0; i < sym_n; ++i) {
762fa536d1SRichard Henderson        const char *name;
772fa536d1SRichard Henderson
782fa536d1SRichard Henderson        if (need_bswap) {
792fa536d1SRichard Henderson            elfN(bswap_sym)(sym + i);
802fa536d1SRichard Henderson        }
812fa536d1SRichard Henderson        name = str + sym[i].st_name;
822fa536d1SRichard Henderson
832fa536d1SRichard Henderson        if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
842fa536d1SRichard Henderson            sigreturn_addr = sym[i].st_value;
852fa536d1SRichard Henderson        }
862fa536d1SRichard Henderson        if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
872fa536d1SRichard Henderson            rt_sigreturn_addr = sym[i].st_value;
882fa536d1SRichard Henderson        }
892fa536d1SRichard Henderson    }
902fa536d1SRichard Henderson}
912fa536d1SRichard Henderson
922fa536d1SRichard Hendersonstatic void elfN(process)(FILE *outf, void *buf, bool need_bswap)
932fa536d1SRichard Henderson{
942fa536d1SRichard Henderson    ElfN(Ehdr) *ehdr = buf;
952fa536d1SRichard Henderson    ElfN(Phdr) *phdr;
962fa536d1SRichard Henderson    ElfN(Shdr) *shdr;
972fa536d1SRichard Henderson    unsigned phnum, shnum;
982fa536d1SRichard Henderson    unsigned dynamic_ofs = 0;
992fa536d1SRichard Henderson    unsigned dynamic_addr = 0;
1002fa536d1SRichard Henderson    unsigned symtab_idx = 0;
1012fa536d1SRichard Henderson    unsigned dynsym_idx = 0;
1022fa536d1SRichard Henderson    unsigned first_segsz = 0;
1032fa536d1SRichard Henderson    int errors = 0;
1042fa536d1SRichard Henderson
1052fa536d1SRichard Henderson    if (need_bswap) {
1062fa536d1SRichard Henderson        elfN(bswap_ehdr)(ehdr);
1072fa536d1SRichard Henderson    }
1082fa536d1SRichard Henderson
1092fa536d1SRichard Henderson    phnum = ehdr->e_phnum;
1102fa536d1SRichard Henderson    phdr = buf + ehdr->e_phoff;
1112fa536d1SRichard Henderson    if (need_bswap) {
1122fa536d1SRichard Henderson        for (unsigned i = 0; i < phnum; ++i) {
1132fa536d1SRichard Henderson            elfN(bswap_phdr)(phdr + i);
1142fa536d1SRichard Henderson        }
1152fa536d1SRichard Henderson    }
1162fa536d1SRichard Henderson
1172fa536d1SRichard Henderson    shnum = ehdr->e_shnum;
1182fa536d1SRichard Henderson    shdr = buf + ehdr->e_shoff;
1192fa536d1SRichard Henderson    if (need_bswap) {
1202fa536d1SRichard Henderson        for (unsigned i = 0; i < shnum; ++i) {
1212fa536d1SRichard Henderson            elfN(bswap_shdr)(shdr + i);
1222fa536d1SRichard Henderson        }
1232fa536d1SRichard Henderson    }
1242fa536d1SRichard Henderson    for (unsigned i = 0; i < shnum; ++i) {
1252fa536d1SRichard Henderson        switch (shdr[i].sh_type) {
1262fa536d1SRichard Henderson        case SHT_SYMTAB:
1272fa536d1SRichard Henderson            symtab_idx = i;
1282fa536d1SRichard Henderson            break;
1292fa536d1SRichard Henderson        case SHT_DYNSYM:
1302fa536d1SRichard Henderson            dynsym_idx = i;
1312fa536d1SRichard Henderson            break;
1322fa536d1SRichard Henderson        }
1332fa536d1SRichard Henderson    }
1342fa536d1SRichard Henderson
1352fa536d1SRichard Henderson    /*
1362fa536d1SRichard Henderson     * Validate the VDSO is created as we expect: that PT_PHDR,
1372fa536d1SRichard Henderson     * PT_DYNAMIC, and PT_NOTE located in a writable data segment.
1382fa536d1SRichard Henderson     * PHDR and DYNAMIC require relocation, and NOTE will get the
1392fa536d1SRichard Henderson     * linux version number.
1402fa536d1SRichard Henderson     */
1412fa536d1SRichard Henderson    for (unsigned i = 0; i < phnum; ++i) {
1422fa536d1SRichard Henderson        if (phdr[i].p_type != PT_LOAD) {
1432fa536d1SRichard Henderson            continue;
1442fa536d1SRichard Henderson        }
1452fa536d1SRichard Henderson        if (first_segsz != 0) {
1462fa536d1SRichard Henderson            fprintf(stderr, "Multiple LOAD segments\n");
1472fa536d1SRichard Henderson            errors++;
1482fa536d1SRichard Henderson        }
1492fa536d1SRichard Henderson        if (phdr[i].p_offset != 0) {
1502fa536d1SRichard Henderson            fprintf(stderr, "LOAD segment does not cover EHDR\n");
1512fa536d1SRichard Henderson            errors++;
1522fa536d1SRichard Henderson        }
1532fa536d1SRichard Henderson        if (phdr[i].p_vaddr != 0) {
1542fa536d1SRichard Henderson            fprintf(stderr, "LOAD segment not loaded at address 0\n");
1552fa536d1SRichard Henderson            errors++;
1562fa536d1SRichard Henderson        }
1572fa536d1SRichard Henderson        first_segsz = phdr[i].p_filesz;
1582fa536d1SRichard Henderson        if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) {
1592fa536d1SRichard Henderson            fprintf(stderr, "LOAD segment does not cover PHDRs\n");
1602fa536d1SRichard Henderson            errors++;
1612fa536d1SRichard Henderson        }
1622fa536d1SRichard Henderson        if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) {
1632fa536d1SRichard Henderson            fprintf(stderr, "LOAD segment is not read-write\n");
1642fa536d1SRichard Henderson            errors++;
1652fa536d1SRichard Henderson        }
1662fa536d1SRichard Henderson    }
1672fa536d1SRichard Henderson    for (unsigned i = 0; i < phnum; ++i) {
1682fa536d1SRichard Henderson        const char *which;
1692fa536d1SRichard Henderson
1702fa536d1SRichard Henderson        switch (phdr[i].p_type) {
1712fa536d1SRichard Henderson        case PT_PHDR:
1722fa536d1SRichard Henderson            which = "PT_PHDR";
1732fa536d1SRichard Henderson            break;
1742fa536d1SRichard Henderson        case PT_NOTE:
1752fa536d1SRichard Henderson            which = "PT_NOTE";
1762fa536d1SRichard Henderson            break;
1772fa536d1SRichard Henderson        case PT_DYNAMIC:
1782fa536d1SRichard Henderson            dynamic_ofs = phdr[i].p_offset;
1792fa536d1SRichard Henderson            dynamic_addr = phdr[i].p_vaddr;
1802fa536d1SRichard Henderson            which = "PT_DYNAMIC";
1812fa536d1SRichard Henderson            break;
1822fa536d1SRichard Henderson        default:
1832fa536d1SRichard Henderson            continue;
1842fa536d1SRichard Henderson        }
1852fa536d1SRichard Henderson        if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) {
1862fa536d1SRichard Henderson            fprintf(stderr, "LOAD segment does not cover %s\n", which);
1872fa536d1SRichard Henderson            errors++;
1882fa536d1SRichard Henderson        }
1892fa536d1SRichard Henderson    }
1902fa536d1SRichard Henderson    if (errors) {
1912fa536d1SRichard Henderson        exit(EXIT_FAILURE);
1922fa536d1SRichard Henderson    }
1932fa536d1SRichard Henderson
1942fa536d1SRichard Henderson    /* Relocate the program headers. */
1952fa536d1SRichard Henderson    for (unsigned i = 0; i < phnum; ++i) {
1962fa536d1SRichard Henderson        output_reloc(outf, buf, &phdr[i].p_vaddr);
1972fa536d1SRichard Henderson        output_reloc(outf, buf, &phdr[i].p_paddr);
1982fa536d1SRichard Henderson    }
1992fa536d1SRichard Henderson
2002fa536d1SRichard Henderson    /* Relocate the DYNAMIC entries. */
2012fa536d1SRichard Henderson    if (dynamic_addr) {
2022fa536d1SRichard Henderson        ElfN(Dyn) *dyn = buf + dynamic_ofs;
2032fa536d1SRichard Henderson        __typeof(dyn->d_tag) tag;
2042fa536d1SRichard Henderson
2052fa536d1SRichard Henderson        do {
2062fa536d1SRichard Henderson
2072fa536d1SRichard Henderson            if (need_bswap) {
2082fa536d1SRichard Henderson                elfN(bswap_dyn)(dyn);
2092fa536d1SRichard Henderson            }
2102fa536d1SRichard Henderson            tag = dyn->d_tag;
2112fa536d1SRichard Henderson
2122fa536d1SRichard Henderson            switch (tag) {
2132fa536d1SRichard Henderson            case DT_HASH:
2142fa536d1SRichard Henderson            case DT_SYMTAB:
2152fa536d1SRichard Henderson            case DT_STRTAB:
2162fa536d1SRichard Henderson            case DT_VERDEF:
2172fa536d1SRichard Henderson            case DT_VERSYM:
2182fa536d1SRichard Henderson            case DT_PLTGOT:
2192fa536d1SRichard Henderson            case DT_ADDRRNGLO ... DT_ADDRRNGHI:
2202fa536d1SRichard Henderson                /* These entries store an address in the entry. */
2212fa536d1SRichard Henderson                output_reloc(outf, buf, &dyn->d_un.d_val);
2222fa536d1SRichard Henderson                break;
2232fa536d1SRichard Henderson
2242fa536d1SRichard Henderson            case DT_NULL:
2252fa536d1SRichard Henderson            case DT_STRSZ:
2262fa536d1SRichard Henderson            case DT_SONAME:
2272fa536d1SRichard Henderson            case DT_DEBUG:
2282fa536d1SRichard Henderson            case DT_FLAGS:
2292fa536d1SRichard Henderson            case DT_FLAGS_1:
2302fa536d1SRichard Henderson            case DT_SYMBOLIC:
2312fa536d1SRichard Henderson            case DT_BIND_NOW:
2322fa536d1SRichard Henderson            case DT_VERDEFNUM:
2332fa536d1SRichard Henderson            case DT_VALRNGLO ... DT_VALRNGHI:
2342fa536d1SRichard Henderson                /* These entries store an integer in the entry. */
2352fa536d1SRichard Henderson                break;
2362fa536d1SRichard Henderson
2372fa536d1SRichard Henderson            case DT_SYMENT:
2382fa536d1SRichard Henderson                if (dyn->d_un.d_val != sizeof(ElfN(Sym))) {
2392fa536d1SRichard Henderson                    fprintf(stderr, "VDSO has incorrect dynamic symbol size\n");
2402fa536d1SRichard Henderson                    errors++;
2412fa536d1SRichard Henderson                }
2422fa536d1SRichard Henderson                break;
2432fa536d1SRichard Henderson
2442fa536d1SRichard Henderson            case DT_REL:
2452fa536d1SRichard Henderson            case DT_RELSZ:
2462fa536d1SRichard Henderson            case DT_RELA:
2472fa536d1SRichard Henderson            case DT_RELASZ:
2482fa536d1SRichard Henderson                /*
2492fa536d1SRichard Henderson                 * These entries indicate that the VDSO was built incorrectly.
2502fa536d1SRichard Henderson                 * It should not have any real relocations.
2512fa536d1SRichard Henderson                 * ??? The RISC-V toolchain will emit these even when there
2522fa536d1SRichard Henderson                 * are no relocations.  Validate zeros.
2532fa536d1SRichard Henderson                 */
2542fa536d1SRichard Henderson                if (dyn->d_un.d_val != 0) {
2552fa536d1SRichard Henderson                    fprintf(stderr, "VDSO has dynamic relocations\n");
2562fa536d1SRichard Henderson                    errors++;
2572fa536d1SRichard Henderson                }
2582fa536d1SRichard Henderson                break;
2592fa536d1SRichard Henderson            case DT_RELENT:
2602fa536d1SRichard Henderson            case DT_RELAENT:
2612fa536d1SRichard Henderson            case DT_TEXTREL:
2622fa536d1SRichard Henderson                /* These entries store an integer in the entry. */
2632fa536d1SRichard Henderson                /* Should not be required; see above. */
2642fa536d1SRichard Henderson                break;
2652fa536d1SRichard Henderson
2662fa536d1SRichard Henderson            case DT_NEEDED:
2672fa536d1SRichard Henderson            case DT_VERNEED:
2682fa536d1SRichard Henderson            case DT_PLTREL:
2692fa536d1SRichard Henderson            case DT_JMPREL:
2702fa536d1SRichard Henderson            case DT_RPATH:
2712fa536d1SRichard Henderson            case DT_RUNPATH:
2722fa536d1SRichard Henderson                fprintf(stderr, "VDSO has external dependencies\n");
2732fa536d1SRichard Henderson                errors++;
2742fa536d1SRichard Henderson                break;
2752fa536d1SRichard Henderson
276*e34136d9SRichard Henderson            case PT_LOPROC + 3:
277*e34136d9SRichard Henderson                if (ehdr->e_machine == EM_PPC64) {
278*e34136d9SRichard Henderson                    break;  /* DT_PPC64_OPT: integer bitmask */
279*e34136d9SRichard Henderson                }
280*e34136d9SRichard Henderson                goto do_default;
281*e34136d9SRichard Henderson
2822fa536d1SRichard Henderson            default:
283*e34136d9SRichard Henderson            do_default:
2842fa536d1SRichard Henderson                /* This is probably something target specific. */
2852fa536d1SRichard Henderson                fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n",
2862fa536d1SRichard Henderson                        (unsigned long)tag);
2872fa536d1SRichard Henderson                errors++;
2882fa536d1SRichard Henderson                break;
2892fa536d1SRichard Henderson            }
2902fa536d1SRichard Henderson            dyn++;
2912fa536d1SRichard Henderson        } while (tag != DT_NULL);
2922fa536d1SRichard Henderson        if (errors) {
2932fa536d1SRichard Henderson            exit(EXIT_FAILURE);
2942fa536d1SRichard Henderson        }
2952fa536d1SRichard Henderson    }
2962fa536d1SRichard Henderson
2972fa536d1SRichard Henderson    /* Relocate the dynamic symbol table. */
2982fa536d1SRichard Henderson    if (dynsym_idx) {
2992fa536d1SRichard Henderson        ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset;
3002fa536d1SRichard Henderson        unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym);
3012fa536d1SRichard Henderson
3022fa536d1SRichard Henderson        for (unsigned i = 0; i < sym_n; ++i) {
3032fa536d1SRichard Henderson            output_reloc(outf, buf, &sym[i].st_value);
3042fa536d1SRichard Henderson        }
3052fa536d1SRichard Henderson    }
3062fa536d1SRichard Henderson
3072fa536d1SRichard Henderson    /* Search both dynsym and symtab for the signal return symbols. */
3082fa536d1SRichard Henderson    if (dynsym_idx) {
3092fa536d1SRichard Henderson        elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap);
3102fa536d1SRichard Henderson    }
3112fa536d1SRichard Henderson    if (symtab_idx) {
3122fa536d1SRichard Henderson        elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap);
3132fa536d1SRichard Henderson    }
3142fa536d1SRichard Henderson}
315