Good evening. For several month i was trying to salvage tccboot JiT and AoT compilation of linux-2.4 kernel. This covered a notable amount of test-cases, and didn't yield a stable known-good baseline with any, to emit a bootable kernel. Recently i could identify a somewhat severe regression in tcc introduced may 2022, commit-id 8e860702e44ca1279098e0fc2142ad579774b1d2
Reverting this commit shows linux-2.4.37.11 can be compiled,linked and booted without problems. Too rebasing this backout/revert up until october 2024 (commit c21576f8a32715ab439690d18184b0e02022bbbd) was possible and linux-2.4 compiled/linked with tcc then booted flawlessly again. Linux-2.4 kernel compilation seems a reasonable test-case, hence it was introduced by Fabrice 20 years ago with tccboot too. And the regression that broke compilation/linking of kernel went unnoticed for more than 2 years on mob branch. Instead of reverting/backing-out the mentioned change on mob branch maybe it's possible to address the issue by fixing the regression. The revert/backout diff that i could confirm that tcc succeeds compiling/linking kernel with is attached. --
reverted and rebased commit 8e860702e44ca1279098e0fc2142ad579774b1d2 onto c21576f8a32715ab439690d18184b0e02022bbbd any later version yields further merge conflicts otherwise linux-2.4 compiled/linked with tcc causes a kernel crash if the commit is not reverted diff --git a/tccelf.c b/tccelf.c index c01854ad..ef38662f 100644 --- a/tccelf.c +++ b/tccelf.c @@ -49,10 +49,10 @@ struct sym_version { #ifdef TCC_TARGET_PE #define shf_RELRO SHF_ALLOC -static const char rdata[] = ".rdata"; +//static const char rdata[] = ".rdata"; #else #define shf_RELRO s1->shf_RELRO -static const char rdata[] = ".data.ro"; +//static const char rdata[] = ".data.ro"; #endif /* ------------------------------------------------------------------------- */ @@ -73,8 +73,12 @@ ST_FUNC void tccelf_new(TCCState *s) /* create standard sections */ text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); +#ifdef TCC_TARGET_PE + rodata_section = new_section(s, ".rdata", SHT_PROGBITS, SHF_ALLOC); +#else /* create ro data section (make ro after relocation done with GNU_RELRO) */ - rodata_section = new_section(s, rdata, SHT_PROGBITS, shf_RELRO); + rodata_section = new_section(s, ".data.ro", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); +#endif bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE); common_section->sh_num = SHN_COMMON; @@ -99,8 +103,10 @@ ST_FUNC void tccelf_new(TCCState *s) if (s->do_bounds_check) { /* if bound checking, then add corresponding sections */ /* (make ro after relocation done with GNU_RELRO) */ - bounds_section = new_section(s, ".bounds", SHT_PROGBITS, shf_RELRO); - lbounds_section = new_section(s, ".lbounds", SHT_PROGBITS, shf_RELRO); + bounds_section = new_section(s, ".bounds", + SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + lbounds_section = new_section(s, ".lbounds", + SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); } #endif } @@ -1502,8 +1508,10 @@ ST_FUNC void add_array (TCCState *s1, const char *sec, int c) { Section *s; s = find_section(s1, sec); - s->sh_flags = shf_RELRO; + s->sh_flags |= SHF_WRITE; +#ifndef TCC_TARGET_PE s->sh_type = sec[1] == 'i' ? SHT_INIT_ARRAY : SHT_FINI_ARRAY; +#endif put_elf_reloc (s1->symtab, s, s->data_offset, R_DATA_PTR, c); section_ptr_add(s, PTR_SIZE); } @@ -1815,7 +1823,9 @@ static void tcc_add_linker_symbols(TCCState *s1) set_global_sym(s1, "__global_pointer$", data_section, 0x800); #endif /* horrible new standard ldscript defines */ +#ifndef TCC_TARGET_PE add_init_array_defines(s1, ".preinit_array"); +#endif add_init_array_defines(s1, ".init_array"); add_init_array_defines(s1, ".fini_array"); /* add start and stop symbols for sections whose name can be @@ -2110,179 +2120,61 @@ static int set_sec_sizes(TCCState *s1) return textrel; } -/* various data used under elf_output_file() */ + +/* Info to be copied in dynamic section */ struct dyn_inf { Section *dynamic; Section *dynstr; - struct { - /* Info to be copied in dynamic section */ - unsigned long data_offset; - addr_t rel_addr; - addr_t rel_size; - }; - ElfW(Phdr) *phdr; - int phnum; - Section *interp; - Section *note; Section *gnu_hash; - /* read only segment mapping for GNU_RELRO */ - Section _roinf, *roinf; + unsigned long data_offset; + addr_t rel_addr; + addr_t rel_size; }; -/* Decide the layout of sections loaded in memory. This must be done before - program headers are filled since they contain info about the layout. - We do the following ordering: interp, symbol tables, relocations, progbits, - nobits */ -static int sort_sections(TCCState *s1, int *sec_order, Section *interp) -{ - Section *s; - int i, j, k, f, f0, n; - int nb_sections = s1->nb_sections; - int *sec_cls = sec_order + nb_sections; - - for (i = 1; i < nb_sections; i++) { - s = s1->sections[i]; - if (s->sh_flags & SHF_ALLOC) { - j = 0x100; - if (s->sh_flags & SHF_WRITE) - j = 0x200; - if (s->sh_flags & SHF_TLS) - j += 0x200; - } else if (s->sh_name) { - j = 0x700; - } else { - j = 0x900; /* no sh_name: won't go to file */ - } - if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_DYNSYM) { - k = 0x10; - } else if (s->sh_type == SHT_STRTAB && strcmp(s->name, ".stabstr")) { - k = 0x11; - if (i == nb_sections - 1) /* ".shstrtab" assumed to remain last */ - k = 0xff; - } else if (s->sh_type == SHT_HASH || s->sh_type == SHT_GNU_HASH) { - k = 0x12; - } else if (s->sh_type == SHT_RELX) { - k = 0x20; - if (s1->plt && s == s1->plt->reloc) - k = 0x21; - } else if (s->sh_type == SHT_PREINIT_ARRAY) { - k = 0x41; - } else if (s->sh_type == SHT_INIT_ARRAY) { - k = 0x42; - } else if (s->sh_type == SHT_FINI_ARRAY) { - k = 0x43; -#ifdef CONFIG_TCC_BCHECK - } else if (s == bounds_section || s == lbounds_section) { - k = 0x44; -#endif - } else if (s == rodata_section || 0 == strcmp(s->name, ".data.rel.ro")) { - k = 0x45; - } else if (s->sh_type == SHT_DYNAMIC) { - k = 0x46; - } else if (s == s1->got) { - k = 0x47; /* .got as RELRO needs BIND_NOW in DT_FLAGS */ - } else { - k = 0x50; - if (s->sh_type == SHT_NOTE) - k = 0x60; - if (s->sh_flags & SHF_EXECINSTR) - k = 0x70; - if (s->sh_type == SHT_NOBITS) - k = 0x80; - if (s == interp) - k = 0x00; - } - k += j; - - for (n = i; n > 1 && k < (f = sec_cls[n - 1]); --n) - sec_cls[n] = f, sec_order[n] = sec_order[n - 1]; - sec_cls[n] = k, sec_order[n] = i; - } - sec_order[0] = 0; +/* Info for GNU_RELRO */ +struct ro_inf { + addr_t sh_offset; + addr_t sh_addr; + addr_t sh_size; +}; - /* count PT_LOAD headers needed */ - n = f0 = 0; - for (i = 1; i < nb_sections; i++) { - s = s1->sections[sec_order[i]]; - k = sec_cls[i]; - f = 0; - if (k < 0x700) { - f = s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR|SHF_TLS); -#if TARGETOS_NetBSD - /* NetBSD only supports 2 PT_LOAD sections. - See: https://blog.netbsd.org/tnf/entry/the_first_report_on_lld */ - if ((f & SHF_WRITE) == 0) f |= SHF_EXECINSTR; -#else - if ((k & 0xfff0) == 0x240) /* RELRO sections */ - f |= 1<<4; -#endif - if (f != f0) /* start new header when flags changed or relro */ - f0 = f, ++n, f |= 1<<8; - } - sec_cls[i] = f; - //printf("ph %d sec %02d : %3X %3X %8.2X %04X %s\n", !!f * n, i, f, k, s->sh_type, s->sh_size, s->name); - } - return n; -} +static void alloc_sec_names( + TCCState *s1, int is_obj + ); -static ElfW(Phdr) *fill_phdr(ElfW(Phdr) *ph, int type, Section *s) -{ - if (s) { - ph->p_offset = s->sh_offset; - ph->p_vaddr = s->sh_addr; - ph->p_filesz = s->sh_size; - ph->p_align = s->sh_addralign; - } - ph->p_type = type; - ph->p_flags = PF_R; - ph->p_paddr = ph->p_vaddr; - ph->p_memsz = ph->p_filesz; - return ph; -} +static int layout_any_sections( + TCCState *s1, int file_offset, int *sec_order, int is_obj + ); /* Assign sections to segments and decide how are sections laid out when loaded in memory. This function also fills corresponding program headers. */ -static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) +static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, + int phnum, int phfill, + Section *interp, + struct ro_inf *roinf, int *sec_order) { + int i, file_offset; Section *s; - addr_t addr, tmp, align, s_align, base; - ElfW(Phdr) *ph = NULL; - int i, f, n, phnum, phfill; - int file_offset; - - /* compute number of program headers */ - phnum = sort_sections(s1, sec_order, d->interp); - phfill = 0; /* set to 1 to have dll's with a PT_PHDR */ - if (d->interp) - phfill = 2; - phnum += phfill; - if (d->note) - ++phnum; - if (d->dynamic) - ++phnum; - if (d->roinf) - ++phnum; - d->phnum = phnum; - d->phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); file_offset = 0; if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); + { + unsigned long s_align; + long long tmp; + addr_t addr; + ElfW(Phdr) *ph; + int j, k, f, file_type = s1->output_type; - s_align = ELF_PAGE_SIZE; - if (s1->section_align) - s_align = s1->section_align; - - addr = ELF_START_ADDR; - if (s1->output_type & TCC_OUTPUT_DYN) - addr = 0; + s_align = ELF_PAGE_SIZE; + if (s1->section_align) + s_align = s1->section_align; - if (s1->has_text_addr) { - addr = s1->text_addr; - if (0) { + if (s1->has_text_addr) { int a_offset, p_offset; + addr = s1->text_addr; /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset % ELF_PAGE_SIZE */ a_offset = (int) (addr & (s_align - 1)); @@ -2290,109 +2182,163 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) if (a_offset < p_offset) a_offset += s_align; file_offset += (a_offset - p_offset); + } else { + if (file_type & TCC_OUTPUT_DYN) + addr = 0; + else + addr = ELF_START_ADDR; + /* compute address after headers */ + addr += (file_offset & (s_align - 1)); } - } - base = addr; - /* compute address after headers */ - addr = addr + (file_offset & (s_align - 1)); - n = 0; - for(i = 1; i < s1->nb_sections; i++) { - s = s1->sections[sec_order[i]]; - f = sec_order[i + s1->nb_sections]; - align = s->sh_addralign - 1; - - if (f == 0) { /* no alloc */ - file_offset = (file_offset + align) & ~align; - s->sh_offset = file_offset; - if (s->sh_type != SHT_NOBITS) - file_offset += s->sh_size; - continue; - } + ph = &phdr[0]; + /* Leave one program headers for the program interpreter and one for + the program header table itself if needed. These are done later as + they require section layout to be done first. */ + if (interp) + ph += 2; - if ((f & 1<<8) && n) { - /* different rwx section flags */ - if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { - /* if in the middle of a page, w e duplicate the page in - memory so that one copy is RX and the other is RW */ - if ((addr & (s_align - 1)) != 0) - addr += s_align; - } else { - align = s_align - 1; - } - } + /* read only segment mapping for GNU_RELRO */ + roinf->sh_offset = roinf->sh_addr = roinf->sh_size = 0; - tmp = addr; - addr = (addr + align) & ~align; - file_offset += (int)(addr - tmp); - s->sh_offset = file_offset; - s->sh_addr = addr; - - if (f & 1<<8) { - /* set new program header */ - ph = &d->phdr[phfill + n]; - ph->p_type = PT_LOAD; - ph->p_align = s_align; - ph->p_flags = PF_R; - if (f & SHF_WRITE) - ph->p_flags |= PF_W; - if (f & SHF_EXECINSTR) - ph->p_flags |= PF_X; - if (f & SHF_TLS) { - ph->p_type = PT_TLS; - ph->p_align = align + 1; - } + for(j = 0; j < phfill; j++) { + ph->p_type = j == 2 ? PT_TLS : PT_LOAD; + if (j == 0) + ph->p_flags = PF_R | PF_X; + else + ph->p_flags = PF_R | PF_W; + ph->p_align = j == 2 ? 4 : s_align; + + /* Decide the layout of sections loaded in memory. This must + be done before program headers are filled since they contain + info about the layout. We do the following ordering: interp, + symbol tables, relocations, progbits, nobits */ + /* XXX: do faster and simpler sorting */ + f = -1; + for(k = 0; k < 10; k++) { + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + /* compute if section should be included */ + if (j == 0) { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_TLS)) != + SHF_ALLOC) + continue; + } else if (j == 1) { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_TLS)) != + (SHF_ALLOC | SHF_WRITE)) + continue; + } else { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_TLS)) != + (SHF_ALLOC | SHF_WRITE | SHF_TLS)) + continue; + } + if (s == interp) { + if (k != 0) + continue; + } else if ((s->sh_type == SHT_DYNSYM || + s->sh_type == SHT_STRTAB || + s->sh_type == SHT_HASH) + && !strstr(s->name, ".stab")) { + if (k != 1) + continue; + } else if (s->sh_type == SHT_RELX) { + if (s1->plt && s == s1->plt->reloc) { + if (k != 3) + continue; + } else { + if (k != 2) + continue; + } + } else if ((s == rodata_section +#ifdef CONFIG_TCC_BCHECK + || s == bounds_section + || s == lbounds_section +#endif + ) && (s->sh_flags & SHF_WRITE)) { + if (k != 4) + continue; + /* Align next section on page size. + This is needed to remap roinf section ro. */ + f = 1; + + } else if (s->sh_type == SHT_PREINIT_ARRAY) { + if (k != 6) + continue; + } else if (s->sh_type == SHT_INIT_ARRAY) { + if (k != 7) + continue; + } else if (s->sh_type == SHT_FINI_ARRAY) { + if (k != 8) + continue; + } else if (s->sh_type == SHT_NOBITS) { + if (k != 9) + continue; + } else { + if (k != 5) + continue; + } + *sec_order++ = i; + + /* section matches: we align it and add its size */ + tmp = addr; + if (f-- == 0) + s->sh_addralign = PAGESIZE; + addr = (addr + s->sh_addralign - 1) & + ~(s->sh_addralign - 1); + file_offset += (int) ( addr - tmp ); + s->sh_offset = file_offset; + s->sh_addr = addr; + + /* update program header infos */ + if (ph->p_offset == 0) { + ph->p_offset = file_offset; + ph->p_vaddr = addr; + ph->p_paddr = ph->p_vaddr; + } + + if (k == 4) { + if (roinf->sh_size == 0) { + roinf->sh_offset = s->sh_offset; + roinf->sh_addr = s->sh_addr; + } + roinf->sh_size = (addr - roinf->sh_addr) + s->sh_size; + } + + addr += s->sh_size; + if (s->sh_type != SHT_NOBITS) + file_offset += s->sh_size; + } + } // - ph->p_offset = file_offset; - ph->p_vaddr = addr; - if (n == 0) { + if (j == 0) { /* Make the first PT_LOAD segment include the program headers itself (and the ELF header as well), it'll come out with same memory use but will make various tools like binutils strip work better. */ - ph->p_offset = 0; - ph->p_vaddr = base; - } - ph->p_paddr = ph->p_vaddr; - ++n; - } - if (f & 1<<4) { - Section *roinf = &d->_roinf; - if (roinf->sh_size == 0) { - roinf->sh_offset = s->sh_offset; - roinf->sh_addr = s->sh_addr; - roinf->sh_addralign = 1; + ph->p_offset &= ~(ph->p_align - 1); + ph->p_vaddr &= ~(ph->p_align - 1); + ph->p_paddr &= ~(ph->p_align - 1); } - roinf->sh_size = (addr - roinf->sh_addr) + s->sh_size; + ph->p_filesz = file_offset - ph->p_offset; + ph->p_memsz = addr - ph->p_vaddr; + ph++; + if (j == 0) { + if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { + /* if in the middle of a page, we duplicate the page in + memory so that one copy is RX and the other is RW */ + if ((addr & (s_align - 1)) != 0) + addr += s_align; + } else { + addr = (addr + s_align - 1) & ~(s_align - 1); + file_offset = (file_offset + s_align - 1) & ~(s_align - 1); + } + } } - - addr += s->sh_size; - if (s->sh_type != SHT_NOBITS) - file_offset += s->sh_size; - - ph->p_filesz = file_offset - ph->p_offset; - ph->p_memsz = addr - ph->p_vaddr; - } - - /* Fill other headers */ - if (d->note) - fill_phdr(++ph, PT_NOTE, d->note); - if (d->dynamic) - fill_phdr(++ph, PT_DYNAMIC, d->dynamic)->p_flags |= PF_W; - if (d->roinf) - fill_phdr(++ph, PT_GNU_RELRO, d->roinf)->p_flags |= PF_W; - if (d->interp) - fill_phdr(&d->phdr[1], PT_INTERP, d->interp); - if (phfill) { - ph = &d->phdr[0]; - ph->p_offset = sizeof(ElfW(Ehdr)); - ph->p_vaddr = base + ph->p_offset; - ph->p_filesz = phnum * sizeof(ElfW(Phdr)); - ph->p_align = 4; - fill_phdr(ph, PT_PHDR, NULL); } - return file_offset; + + /* all other sections come after */ + return layout_any_sections(s1, file_offset, sec_order, 0); } /* put dynamic tag */ @@ -2404,6 +2350,75 @@ static void put_dt(Section *dynamic, int dt, addr_t val) dyn->d_un.d_val = val; } +static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp, + Section *dynamic, Section *note, struct ro_inf *roinf) +{ + ElfW(Phdr) *ph; + + /* if interpreter, then add corresponding program header */ + if (interp) { + ph = &phdr[0]; + + ph->p_type = PT_PHDR; + ph->p_offset = sizeof(ElfW(Ehdr)); + ph->p_filesz = ph->p_memsz = phnum * sizeof(ElfW(Phdr)); + ph->p_vaddr = interp->sh_addr - ph->p_filesz; + ph->p_paddr = ph->p_vaddr; + ph->p_flags = PF_R | PF_X; + ph->p_align = 4; /* interp->sh_addralign; */ + ph++; + + ph->p_type = PT_INTERP; + ph->p_offset = interp->sh_offset; + ph->p_vaddr = interp->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = interp->sh_size; + ph->p_memsz = interp->sh_size; + ph->p_flags = PF_R; + ph->p_align = interp->sh_addralign; + } + + if (note) { + ph = &phdr[phnum - 2 - (roinf != NULL)]; + + ph->p_type = PT_NOTE; + ph->p_offset = note->sh_offset; + ph->p_vaddr = note->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = note->sh_size; + ph->p_memsz = note->sh_size; + ph->p_flags = PF_R; + ph->p_align = note->sh_addralign; + } + + /* if dynamic section, then add corresponding program header */ + if (dynamic) { + ph = &phdr[phnum - 1 - (roinf != NULL)]; + + ph->p_type = PT_DYNAMIC; + ph->p_offset = dynamic->sh_offset; + ph->p_vaddr = dynamic->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = dynamic->sh_size; + ph->p_memsz = dynamic->sh_size; + ph->p_flags = PF_R | PF_W; + ph->p_align = dynamic->sh_addralign; + } + + if (roinf) { + ph = &phdr[phnum - 1]; + + ph->p_type = PT_GNU_RELRO; + ph->p_offset = roinf->sh_offset; + ph->p_vaddr = roinf->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = roinf->sh_size; + ph->p_memsz = roinf->sh_size; + ph->p_flags = PF_R; + ph->p_align = 1; + } +} + /* Fill the dynamic section with tags describing the address and size of sections */ static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf) @@ -2506,7 +2521,6 @@ static void update_reloc_sections(TCCState *s1, struct dyn_inf *dyninf) } } -static int tidy_section_headers(TCCState *s1, int *sec_order); #endif /* ndef ELF_OBJ_ONLY */ /* Create an ELF file on disk. @@ -2528,9 +2542,6 @@ static int tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, ehdr.e_phentsize = sizeof(ElfW(Phdr)); ehdr.e_phnum = phnum; ehdr.e_phoff = sizeof(ElfW(Ehdr)); -#ifndef ELF_OBJ_ONLY - shnum = tidy_section_headers(s1, sec_order); -#endif } /* align to 4 */ @@ -2590,8 +2601,8 @@ static int tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, sort_syms(s1, symtab_section); - for(i = 1; i < shnum; i++) { - s = s1->sections[sec_order ? sec_order[i] : i]; + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[sec_order[i]]; if (s->sh_type != SHT_NOBITS) { while (offset < s->sh_offset) { fputc(0, f); @@ -2610,7 +2621,7 @@ static int tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, offset++; } - for(i = 0; i < shnum; i++) { + for(i = 0; i < s1->nb_sections; i++) { sh = &shdr; memset(sh, 0, sizeof(ElfW(Shdr))); s = s1->sections[i]; @@ -2690,7 +2701,7 @@ static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum, #ifndef ELF_OBJ_ONLY /* Sort section headers by assigned sh_addr, remove sections that we aren't going to output. */ -static int tidy_section_headers(TCCState *s1, int *sec_order) +static void tidy_section_headers(TCCState *s1, int *sec_order) { int i, nnew, l, *backmap; Section **snew, *s; @@ -2730,8 +2741,8 @@ static int tidy_section_headers(TCCState *s1, int *sec_order) sec_order[i] = i; tcc_free(s1->sections); s1->sections = snew; + s1->nb_sections = nnew; tcc_free(backmap); - return nnew; } #ifdef TCC_TARGET_ARM @@ -2797,33 +2808,34 @@ static void alloc_sec_names(TCCState *s1, int is_obj); /* XXX: suppress unneeded sections */ static int elf_output_file(TCCState *s1, const char *filename) { - int i, ret, file_type, file_offset, *sec_order; + int i, ret, phnum, phfill, shnum, file_type, file_offset, *sec_order; struct dyn_inf dyninf = {0}; - Section *interp, *dynstr, *dynamic; + struct ro_inf roinf; + ElfW(Phdr) *phdr; + Section *interp, *dynamic, *dynstr, *note; + struct ro_inf *roinf_use = NULL; int textrel, got_sym, dt_flags_1; file_type = s1->output_type; s1->nb_errors = 0; ret = -1; - interp = dynstr = dynamic = NULL; + phdr = NULL; sec_order = NULL; - dyninf.roinf = &dyninf._roinf; + interp = dynamic = dynstr = note = NULL; #ifdef TCC_TARGET_ARM create_arm_attribute_section (s1); #endif #if TARGETOS_OpenBSD - dyninf.note = create_bsd_note_section (s1, ".note.openbsd.ident", "OpenBSD"); + note = create_bsd_note_section (s1, ".note.openbsd.ident", "OpenBSD"); #endif #if TARGETOS_NetBSD - dyninf.note = create_bsd_note_section (s1, ".note.netbsd.ident", "NetBSD"); + note = create_bsd_note_section (s1, ".note.netbsd.ident", "NetBSD"); #endif -#if TARGETOS_FreeBSD || TARGETOS_NetBSD - dyninf.roinf = NULL; -#endif + { /* if linking, also link in runtime libraries (libc, libgcc, etc.) */ tcc_add_runtime(s1); resolve_common_syms(s1); @@ -2840,7 +2852,6 @@ static int elf_output_file(TCCState *s1, const char *filename) interp->sh_addralign = 1; ptr = section_ptr_add(interp, 1 + strlen(elfint)); strcpy(ptr, elfint); - dyninf.interp = interp; } /* add dynamic symbol table */ @@ -2874,6 +2885,7 @@ static int elf_output_file(TCCState *s1, const char *filename) build_got_entries(s1, 0); } version_add (s1); + } textrel = set_sec_sizes(s1); alloc_sec_names(s1, 0); @@ -2915,11 +2927,36 @@ static int elf_output_file(TCCState *s1, const char *filename) dynstr->sh_size = dynstr->data_offset; } + /* compute number of program headers */ + phfill = 2; + for (i = 1; i < s1->nb_sections; i++) + if (s1->sections[i]->sh_flags & SHF_TLS) + phfill = 3; + phnum = 3; + if (interp) + phnum += phfill; + if (note) + phnum++; +#if !TARGETOS_FreeBSD && !TARGETOS_NetBSD + /* GNU_RELRO */ + phnum++, roinf_use = &roinf; +#endif + + /* allocate program segment headers */ + phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); + /* compute number of sections */ + shnum = s1->nb_sections; /* this array is used to reorder sections in the output file */ - sec_order = tcc_malloc(sizeof(int) * 2 * s1->nb_sections); + sec_order = tcc_malloc(sizeof(int) * shnum); + sec_order[0] = 0; + /* compute section to program header mapping */ - file_offset = layout_sections(s1, sec_order, &dyninf); + file_offset = layout_sections(s1, phdr, phnum, phfill, interp, &roinf, sec_order + 1); + /* Fill remaining program header and finalize relocation related to dynamic + linking. */ + { + fill_unloadable_phdr(phdr, phnum, interp, dynamic, note, roinf_use); if (dynamic) { /* put in GOT the dynamic section address and relocate PLT */ write32le(s1->got->data, dynamic->sh_addr); @@ -2933,6 +2970,7 @@ static int elf_output_file(TCCState *s1, const char *filename) /* if building executable or DLL, then relocate each section except the GOT which is already relocated */ relocate_syms(s1, s1->symtab, 0); + ret = -1; if (s1->nb_errors != 0) goto the_end; relocate_sections(s1); @@ -2941,20 +2979,25 @@ static int elf_output_file(TCCState *s1, const char *filename) dynamic->data_offset = dyninf.data_offset; fill_dynamic(s1, &dyninf); } + tidy_section_headers(s1, sec_order); + /* Perform relocation to GOT or PLT entries */ if (file_type == TCC_OUTPUT_EXE && s1->static_link) fill_got(s1); else if (s1->got) fill_local_got_entries(s1); + } + if (dyninf.gnu_hash) update_gnu_hash(s1, dyninf.gnu_hash); /* Create the ELF file with name 'filename' */ - ret = tcc_write_elf_file(s1, filename, dyninf.phnum, dyninf.phdr, file_offset, sec_order); + ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order ); + s1->nb_sections = shnum; the_end: tcc_free(sec_order); - tcc_free(dyninf.phdr); + tcc_free(phdr); return ret; } #endif /* ndef ELF_OBJ_ONLY */ @@ -2977,24 +3020,42 @@ static void alloc_sec_names(TCCState *s1, int is_obj) strsec->sh_size = strsec->data_offset; } -/* Output an elf .o file */ -static int elf_output_obj(TCCState *s1, const char *filename) +static int layout_any_sections(TCCState *s1, int file_offset, int *sec_order, int is_obj) { + int i; Section *s; - int i, ret, file_offset; - s1->nb_errors = 0; - /* Allocate strings for section names */ - alloc_sec_names(s1, 1); - file_offset = sizeof (ElfW(Ehdr)); for(i = 1; i < s1->nb_sections; i++) { s = s1->sections[i]; - file_offset = (file_offset + 15) & -16; + if (!is_obj && (s->sh_flags & SHF_ALLOC)) + continue; + *sec_order++ = i; + file_offset = (file_offset + s->sh_addralign - 1) & + ~(s->sh_addralign - 1); s->sh_offset = file_offset; if (s->sh_type != SHT_NOBITS) file_offset += s->sh_size; } + return file_offset; +} + +/* Output an elf .o file */ +static int elf_output_obj(TCCState *s1, const char *filename) +{ + int ret, file_offset; + int *sec_order; + s1->nb_errors = 0; + + /* Allocate strings for section names */ + alloc_sec_names(s1, 1); + + /* this array is used to reorder sections in the output file */ + sec_order = tcc_malloc(sizeof(int) * s1->nb_sections); + sec_order[0] = 0; + file_offset = layout_any_sections(s1, sizeof (ElfW(Ehdr)), sec_order + 1, 1); + /* Create the ELF file with name 'filename' */ - ret = tcc_write_elf_file(s1, filename, 0, NULL, file_offset, NULL); + ret = tcc_write_elf_file(s1, filename, 0, NULL, file_offset, sec_order); + tcc_free(sec_order); return ret; } diff --git a/tccpe.c b/tccpe.c index c87814d3..1bcb7435 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1137,9 +1137,7 @@ static int pe_section_class(Section *s) return sec_debug; } else if (flags & SHF_ALLOC) { - if (type == SHT_PROGBITS - || type == SHT_INIT_ARRAY - || type == SHT_FINI_ARRAY) { + if (type == SHT_PROGBITS) { if (flags & SHF_EXECINSTR) return sec_text; if (flags & SHF_WRITE) @@ -1152,44 +1150,52 @@ static int pe_section_class(Section *s) return sec_pdata; return sec_rdata; } else if (type == SHT_NOBITS) { - return sec_bss; + if (flags & SHF_WRITE) + return sec_bss; } - return sec_other; } else { if (0 == strcmp(name, ".reloc")) return sec_reloc; } - return sec_last; + if (0 == memcmp(name, ".stab", 5)) + return name[5] ? sec_stabstr : sec_stab; + if (flags & SHF_ALLOC) + return sec_other; + return -1; } static int pe_assign_addresses (struct pe_info *pe) { - int i, k, n, c, nbs; + int i, k, o, c; ADDR3264 addr; - int *sec_order, *sec_cls; + int *section_order; struct section_info *si; Section *s; TCCState *s1 = pe->s1; if (PE_DLL == pe->type) pe->reloc = new_section(pe->s1, ".reloc", SHT_PROGBITS, 0); - //pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); - - nbs = s1->nb_sections; - sec_order = tcc_mallocz(2 * sizeof (int) * nbs); - sec_cls = sec_order + nbs; - for (i = 1; i < nbs; ++i) { - s = s1->sections[i]; - k = pe_section_class(s); - for (n = i; n > 1 && k < (c = sec_cls[n - 1]); --n) - sec_cls[n] = c, sec_order[n] = sec_order[n - 1]; - sec_cls[n] = k, sec_order[n] = i; + // pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); + + section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int)); + for (o = k = 0 ; k < sec_last; ++k) { + for (i = 1; i < s1->nb_sections; ++i) { + s = s1->sections[i]; + if (k == pe_section_class(s)) + section_order[o++] = i; + } } + si = NULL; addr = pe->imagebase + 1; - for (i = 1; (c = sec_cls[i]) < sec_last; ++i) { - s = s1->sections[sec_order[i]]; + for (i = 0; i < o; ++i) { + k = section_order[i]; + s = s1->sections[k]; + c = pe_section_class(s); + + if ((c == sec_stab || c == sec_stabstr) && 0 == s1->do_debug) + continue; if (PE_MERGE_DATA && c == sec_bss) c = sec_data; @@ -1250,31 +1256,28 @@ add_section: } //printf("%08x %05x %08x %s\n", si->sh_addr, si->sh_size, si->pe_flags, s->name); } + tcc_free(section_order); #if 0 - for (i = 1; i < nbs; ++i) { - Section *s = s1->sections[sec_order[i]]; + for (i = 1; i < s1->nb_sections; ++i) { + Section *s = s1->sections[i]; int type = s->sh_type; int flags = s->sh_flags; printf("section %-16s %-10s %p %04x %s,%s,%s\n", s->name, type == SHT_PROGBITS ? "progbits" : - type == SHT_INIT_ARRAY ? "initarr" : - type == SHT_FINI_ARRAY ? "finiarr" : type == SHT_NOBITS ? "nobits" : type == SHT_SYMTAB ? "symtab" : type == SHT_STRTAB ? "strtab" : type == SHT_RELX ? "rel" : "???", s->sh_addr, - (unsigned)s->data_offset, + s->data_offset, flags & SHF_ALLOC ? "alloc" : "", flags & SHF_WRITE ? "write" : "", flags & SHF_EXECINSTR ? "exec" : "" ); - fflush(stdout); } s1->verbose = 2; #endif - tcc_free(sec_order); return 0; }
diff --git a/tccelf.c b/tccelf.c index e6d5bc0..ed51d68 100644 --- a/tccelf.c +++ b/tccelf.c @@ -47,14 +47,6 @@ struct sym_version { /* section is dynsymtab_section */ #define SHF_DYNSYM 0x40000000 -#ifdef TCC_TARGET_PE -static const int shf_RELRO = SHF_ALLOC; -static const char rdata[] = ".rdata"; -#else -static const int shf_RELRO = SHF_ALLOC | SHF_WRITE; -static const char rdata[] = ".data.ro"; -#endif - /* ------------------------------------------------------------------------- */ ST_FUNC void tccelf_new(TCCState *s) @@ -66,8 +58,12 @@ ST_FUNC void tccelf_new(TCCState *s) /* create standard sections */ text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); +#ifdef TCC_TARGET_PE + rodata_section = new_section(s, ".rdata", SHT_PROGBITS, SHF_ALLOC); +#else /* create ro data section (make ro after relocation done with GNU_RELRO) */ - rodata_section = new_section(s, rdata, SHT_PROGBITS, shf_RELRO); + rodata_section = new_section(s, ".data.ro", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); +#endif bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE); common_section->sh_num = SHN_COMMON; @@ -90,8 +86,10 @@ ST_FUNC void tccelf_bounds_new(TCCState *s) { TCCState *s1 = s; /* create bounds sections (make ro after relocation done with GNU_RELRO) */ - bounds_section = new_section(s, ".bounds", SHT_PROGBITS, shf_RELRO); - lbounds_section = new_section(s, ".lbounds", SHT_PROGBITS, shf_RELRO); + bounds_section = new_section(s, ".bounds", + SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + lbounds_section = new_section(s, ".lbounds", + SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); } #endif @@ -1311,8 +1309,10 @@ ST_FUNC void add_array (TCCState *s1, const char *sec, int c) { Section *s; s = find_section(s1, sec); - s->sh_flags = shf_RELRO; + s->sh_flags |= SHF_WRITE; +#ifndef TCC_TARGET_PE s->sh_type = sec[1] == 'i' ? SHT_INIT_ARRAY : SHT_FINI_ARRAY; +#endif put_elf_reloc (s1->symtab, s, s->data_offset, R_DATA_PTR, c); section_ptr_add(s, PTR_SIZE); } @@ -1554,7 +1554,9 @@ static void tcc_add_linker_symbols(TCCState *s1) set_global_sym(s1, "__global_pointer$", data_section, 0x800); #endif /* horrible new standard ldscript defines */ +#ifndef TCC_TARGET_PE add_init_array_defines(s1, ".preinit_array"); +#endif add_init_array_defines(s1, ".init_array"); add_init_array_defines(s1, ".fini_array"); /* add start and stop symbols for sections whose name can be @@ -1847,172 +1849,59 @@ static int set_sec_sizes(TCCState *s1) return textrel; } -/* various data used under elf_output_file() */ + +/* Info to be copied in dynamic section */ struct dyn_inf { Section *dynamic; Section *dynstr; - struct { - /* Info to be copied in dynamic section */ - unsigned long data_offset; - addr_t rel_addr; - addr_t rel_size; - }; - - ElfW(Phdr) *phdr; - int phnum; - Section *interp; - Section *note; - - /* read only segment mapping for GNU_RELRO */ - Section _roinf, *roinf; + unsigned long data_offset; + addr_t rel_addr; + addr_t rel_size; }; -/* Decide the layout of sections loaded in memory. This must be done before - program headers are filled since they contain info about the layout. - We do the following ordering: interp, symbol tables, relocations, progbits, - nobits */ -static int sort_sections(TCCState *s1, int *sec_order, Section *interp) -{ - Section *s; - int i, j, k, f, f0, n; - int nb_sections = s1->nb_sections; - int *sec_cls = sec_order + nb_sections; - - for (i = 1; i < nb_sections; i++) { - s = s1->sections[i]; - if (s->sh_flags & SHF_ALLOC) { - j = 0x100; - if (s->sh_flags & SHF_WRITE) - j = 0x200; - if (s->sh_flags & SHF_TLS) - j += 0x200; - } else if (s->sh_name) { - j = 0x700; - } else { - j = 0x900; /* no sh_name: won't go to file */ - } - if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_DYNSYM) { - k = 0x10; - } else if (s->sh_type == SHT_STRTAB && strcmp(s->name, ".stabstr")) { - k = 0x11; - if (i == nb_sections - 1) /* ".shstrtab" assumed to remain last */ - k = 0xff; - } else if (s->sh_type == SHT_HASH) { - k = 0x12; - } else if (s->sh_type == SHT_RELX) { - k = 0x20; - if (s1->plt && s == s1->plt->reloc) - k = 0x21; - } else if (s->sh_type == SHT_PREINIT_ARRAY) { - k = 0x41; - } else if (s->sh_type == SHT_INIT_ARRAY) { - k = 0x42; - } else if (s->sh_type == SHT_FINI_ARRAY) { - k = 0x43; -#ifdef CONFIG_TCC_BCHECK - } else if (s == bounds_section || s == lbounds_section) { - k = 0x44; -#endif - } else if (s == rodata_section || 0 == strcmp(s->name, ".data.rel.ro")) { - k = 0x45; - } else if (s->sh_type == SHT_DYNAMIC) { - k = 0x46; - } else if (s == s1->got) { - k = 0x47; /* .got as RELRO needs BIND_NOW in DT_FLAGS */ - } else { - k = 0x50; - if (s->sh_type == SHT_NOTE) - k = 0x60; - if (s->sh_flags & SHF_EXECINSTR) - k = 0x70; - if (s->sh_type == SHT_NOBITS) - k = 0x80; - if (s == interp) - k = 0x00; - } - k += j; - - for (n = i; n > 1 && k < (f = sec_cls[n - 1]); --n) - sec_cls[n] = f, sec_order[n] = sec_order[n - 1]; - sec_cls[n] = k, sec_order[n] = i; - } - sec_order[0] = 0; +/* Info for GNU_RELRO */ +struct ro_inf { + addr_t sh_offset; + addr_t sh_addr; + addr_t sh_size; +}; - /* count PT_LOAD headers needed */ - n = f0 = 0; - for (i = 1; i < nb_sections; i++) { - s = s1->sections[sec_order[i]]; - k = sec_cls[i]; - f = 0; - if (k < 0x700) { - f = s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR|SHF_TLS); - if ((k & 0xfff0) == 0x240) /* RELRO sections */ - f |= 1<<4; - if (f != f0) /* start new header when flags changed or relro */ - f0 = f, ++n, f |= 1<<8; - } - sec_cls[i] = f; - //printf("ph %d sec %02d : %3X %3X %8.2X %04X %s\n", !!f * n, i, f, k, s->sh_type, s->sh_size, s->name); - } - return n; -} +static void alloc_sec_names( + TCCState *s1, int is_obj + ); -static ElfW(Phdr) *fill_phdr(ElfW(Phdr) *ph, int type, Section *s) -{ - if (s) { - ph->p_offset = s->sh_offset; - ph->p_vaddr = s->sh_addr; - ph->p_filesz = s->sh_size; - ph->p_align = s->sh_addralign; - } - ph->p_type = type; - ph->p_flags = PF_R; - ph->p_paddr = ph->p_vaddr; - ph->p_memsz = ph->p_filesz; - return ph; -} +static int layout_any_sections( + TCCState *s1, int file_offset, int *sec_order, int is_obj + ); /* Assign sections to segments and decide how are sections laid out when loaded in memory. This function also fills corresponding program headers. */ -static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) +static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, + int phnum, int phfill, + Section *interp, + struct ro_inf *roinf, int *sec_order) { + int i, file_offset; Section *s; - addr_t addr, tmp, align, s_align, base; - ElfW(Phdr) *ph = NULL; - int i, f, n, phnum, phfill; - int file_offset; - - /* compute number of program headers */ - phnum = sort_sections(s1, sec_order, d->interp); - phfill = 0; /* set to 1 to have dll's with a PT_PHDR */ - if (d->interp) - phfill = 2; - phnum += phfill; - if (d->note) - ++phnum; - if (d->dynamic) - ++phnum; - if (d->roinf) - ++phnum; - d->phnum = phnum; - d->phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); file_offset = 0; if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); - s_align = ELF_PAGE_SIZE; - if (s1->section_align) - s_align = s1->section_align; + { + unsigned long s_align; + long long tmp; + addr_t addr; + ElfW(Phdr) *ph; + int j, k, f, file_type = s1->output_type; - addr = ELF_START_ADDR; - if (s1->output_type & TCC_OUTPUT_DYN) - addr = 0; + s_align = ELF_PAGE_SIZE; + if (s1->section_align) + s_align = s1->section_align; - if (s1->has_text_addr) { - addr = s1->text_addr; - if (0) { + if (s1->has_text_addr) { int a_offset, p_offset; + addr = s1->text_addr; /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset % ELF_PAGE_SIZE */ a_offset = (int) (addr & (s_align - 1)); @@ -2020,108 +1909,161 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d) if (a_offset < p_offset) a_offset += s_align; file_offset += (a_offset - p_offset); + } else { + if (file_type & TCC_OUTPUT_DYN) + addr = 0; + else + addr = ELF_START_ADDR; + /* compute address after headers */ + addr += (file_offset & (s_align - 1)); } - } - base = addr; - /* compute address after headers */ - addr = addr + (file_offset & (s_align - 1)); - n = 0; - for(i = 1; i < s1->nb_sections; i++) { - s = s1->sections[sec_order[i]]; - f = sec_order[i + s1->nb_sections]; - align = s->sh_addralign - 1; - - if (f == 0) { /* no alloc */ - file_offset = (file_offset + align) & ~align; - s->sh_offset = file_offset; - if (s->sh_type != SHT_NOBITS) - file_offset += s->sh_size; - continue; - } + ph = &phdr[0]; + /* Leave one program headers for the program interpreter and one for + the program header table itself if needed. These are done later as + they require section layout to be done first. */ + if (interp) + ph += 2; - if ((f & 1<<8) && n) { - /* different rwx section flags */ - if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { - /* if in the middle of a page, w e duplicate the page in - memory so that one copy is RX and the other is RW */ - if ((addr & (s_align - 1)) != 0) - addr += s_align; - } else { - align = s_align - 1; - } - } + /* read only segment mapping for GNU_RELRO */ + roinf->sh_offset = roinf->sh_addr = roinf->sh_size = 0; - tmp = addr; - addr = (addr + align) & ~align; - file_offset += (int)(addr - tmp); - s->sh_offset = file_offset; - s->sh_addr = addr; - - if (f & 1<<8) { - /* set new program header */ - ph = &d->phdr[phfill + n]; - ph->p_type = PT_LOAD; - ph->p_align = s_align; - ph->p_flags = PF_R; - if (f & SHF_WRITE) - ph->p_flags |= PF_W; - if (f & SHF_EXECINSTR) - ph->p_flags |= PF_X; - if (f & SHF_TLS) { - ph->p_type = PT_TLS; - ph->p_align = 4; + for(j = 0; j < phfill; j++) { + ph->p_type = j == 2 ? PT_TLS : PT_LOAD; + if (j == 0) + ph->p_flags = PF_R | PF_X; + else + ph->p_flags = PF_R | PF_W; + ph->p_align = j == 2 ? 4 : s_align; + + /* Decide the layout of sections loaded in memory. This must + be done before program headers are filled since they contain + info about the layout. We do the following ordering: interp, + symbol tables, relocations, progbits, nobits */ + /* XXX: do faster and simpler sorting */ + f = -1; + for(k = 0; k < 10; k++) { + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + /* compute if section should be included */ + if (j == 0) { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_TLS)) != + SHF_ALLOC) + continue; + } else if (j == 1) { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_TLS)) != + (SHF_ALLOC | SHF_WRITE)) + continue; + } else { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_TLS)) != + (SHF_ALLOC | SHF_WRITE | SHF_TLS)) + continue; + } + if (s == interp) { + if (k != 0) + continue; + } else if ((s->sh_type == SHT_DYNSYM || + s->sh_type == SHT_STRTAB || + s->sh_type == SHT_HASH) + && !strstr(s->name, ".stab")) { + if (k != 1) + continue; + } else if (s->sh_type == SHT_RELX) { + if (s1->plt && s == s1->plt->reloc) { + if (k != 3) + continue; + } else { + if (k != 2) + continue; + } + } else if ((s == rodata_section +#ifdef CONFIG_TCC_BCHECK + || s == bounds_section + || s == lbounds_section +#endif + ) && (s->sh_flags & SHF_WRITE)) { + if (k != 4) + continue; + /* Align next section on page size. + This is needed to remap roinf section ro. */ + f = 1; + + } else if (s->sh_type == SHT_PREINIT_ARRAY) { + if (k != 6) + continue; + } else if (s->sh_type == SHT_INIT_ARRAY) { + if (k != 7) + continue; + } else if (s->sh_type == SHT_FINI_ARRAY) { + if (k != 8) + continue; + } else if (s->sh_type == SHT_NOBITS) { + if (k != 9) + continue; + } else { + if (k != 5) + continue; + } + *sec_order++ = i; + + /* section matches: we align it and add its size */ + tmp = addr; + if (f-- == 0) + s->sh_addralign = PAGESIZE; + addr = (addr + s->sh_addralign - 1) & + ~(s->sh_addralign - 1); + file_offset += (int) ( addr - tmp ); + s->sh_offset = file_offset; + s->sh_addr = addr; + + /* update program header infos */ + if (ph->p_offset == 0) { + ph->p_offset = file_offset; + ph->p_vaddr = addr; + ph->p_paddr = ph->p_vaddr; + } + + if (k == 4) { + if (roinf->sh_size == 0) { + roinf->sh_offset = s->sh_offset; + roinf->sh_addr = s->sh_addr; + } + roinf->sh_size = (addr - roinf->sh_addr) + s->sh_size; + } + + addr += s->sh_size; + if (s->sh_type != SHT_NOBITS) + file_offset += s->sh_size; + } } - ph->p_offset = file_offset; - ph->p_vaddr = addr; - if (n == 0) { + if (j == 0) { /* Make the first PT_LOAD segment include the program headers itself (and the ELF header as well), it'll come out with same memory use but will make various tools like binutils strip work better. */ - ph->p_offset = 0; - ph->p_vaddr = base; - } - ph->p_paddr = ph->p_vaddr; - ++n; - } - - if (f & 1<<4) { - Section *roinf = &d->_roinf; - if (roinf->sh_size == 0) { - roinf->sh_offset = s->sh_offset; - roinf->sh_addr = s->sh_addr; - roinf->sh_addralign = 1; + ph->p_offset &= ~(ph->p_align - 1); + ph->p_vaddr &= ~(ph->p_align - 1); + ph->p_paddr &= ~(ph->p_align - 1); } - roinf->sh_size = (addr - roinf->sh_addr) + s->sh_size; + ph->p_filesz = file_offset - ph->p_offset; + ph->p_memsz = addr - ph->p_vaddr; + ph++; + if (j == 0) { + if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { + /* if in the middle of a page, we duplicate the page in + memory so that one copy is RX and the other is RW */ + if ((addr & (s_align - 1)) != 0) + addr += s_align; + } else { + addr = (addr + s_align - 1) & ~(s_align - 1); + file_offset = (file_offset + s_align - 1) & ~(s_align - 1); + } + } } - - addr += s->sh_size; - if (s->sh_type != SHT_NOBITS) - file_offset += s->sh_size; - - ph->p_filesz = file_offset - ph->p_offset; - ph->p_memsz = addr - ph->p_vaddr; - } - - /* Fill other headers */ - if (d->note) - fill_phdr(++ph, PT_NOTE, d->note); - if (d->dynamic) - fill_phdr(++ph, PT_DYNAMIC, d->dynamic)->p_flags |= PF_W; - if (d->roinf) - fill_phdr(++ph, PT_GNU_RELRO, d->roinf)->p_flags |= PF_W; - if (d->interp) - fill_phdr(&d->phdr[1], PT_INTERP, d->interp); - if (phfill) { - ph = &d->phdr[0]; - ph->p_offset = sizeof(ElfW(Ehdr)); - ph->p_vaddr = base + ph->p_offset; - ph->p_filesz = phnum * sizeof(ElfW(Phdr)); - ph->p_align = 4; - fill_phdr(ph, PT_PHDR, NULL); } - return file_offset; + + /* all other sections come after */ + return layout_any_sections(s1, file_offset, sec_order, 0); } /* put dynamic tag */ @@ -2133,6 +2075,75 @@ static void put_dt(Section *dynamic, int dt, addr_t val) dyn->d_un.d_val = val; } +static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp, + Section *dynamic, Section *note, struct ro_inf *roinf) +{ + ElfW(Phdr) *ph; + + /* if interpreter, then add corresponding program header */ + if (interp) { + ph = &phdr[0]; + + ph->p_type = PT_PHDR; + ph->p_offset = sizeof(ElfW(Ehdr)); + ph->p_filesz = ph->p_memsz = phnum * sizeof(ElfW(Phdr)); + ph->p_vaddr = interp->sh_addr - ph->p_filesz; + ph->p_paddr = ph->p_vaddr; + ph->p_flags = PF_R | PF_X; + ph->p_align = 4; /* interp->sh_addralign; */ + ph++; + + ph->p_type = PT_INTERP; + ph->p_offset = interp->sh_offset; + ph->p_vaddr = interp->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = interp->sh_size; + ph->p_memsz = interp->sh_size; + ph->p_flags = PF_R; + ph->p_align = interp->sh_addralign; + } + + if (note) { + ph = &phdr[phnum - 2 - (roinf != NULL)]; + + ph->p_type = PT_NOTE; + ph->p_offset = note->sh_offset; + ph->p_vaddr = note->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = note->sh_size; + ph->p_memsz = note->sh_size; + ph->p_flags = PF_R; + ph->p_align = note->sh_addralign; + } + + /* if dynamic section, then add corresponding program header */ + if (dynamic) { + ph = &phdr[phnum - 1 - (roinf != NULL)]; + + ph->p_type = PT_DYNAMIC; + ph->p_offset = dynamic->sh_offset; + ph->p_vaddr = dynamic->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = dynamic->sh_size; + ph->p_memsz = dynamic->sh_size; + ph->p_flags = PF_R | PF_W; + ph->p_align = dynamic->sh_addralign; + } + + if (roinf) { + ph = &phdr[phnum - 1]; + + ph->p_type = PT_GNU_RELRO; + ph->p_offset = roinf->sh_offset; + ph->p_vaddr = roinf->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = roinf->sh_size; + ph->p_memsz = roinf->sh_size; + ph->p_flags = PF_R; + ph->p_align = 1; + } +} + /* Fill the dynamic section with tags describing the address and size of sections */ static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf) @@ -2234,7 +2245,6 @@ static void update_reloc_sections(TCCState *s1, struct dyn_inf *dyninf) } } -static int tidy_section_headers(TCCState *s1, int *sec_order); #endif /* ndef ELF_OBJ_ONLY */ /* Create an ELF file on disk. @@ -2256,9 +2266,6 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, ehdr.e_phentsize = sizeof(ElfW(Phdr)); ehdr.e_phnum = phnum; ehdr.e_phoff = sizeof(ElfW(Ehdr)); -#ifndef ELF_OBJ_ONLY - shnum = tidy_section_headers(s1, sec_order); -#endif } /* align to 4 */ @@ -2321,8 +2328,8 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); sort_syms(s1, symtab_section); - for(i = 1; i < shnum; i++) { - s = s1->sections[sec_order ? sec_order[i] : i]; + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[sec_order[i]]; if (s->sh_type != SHT_NOBITS) { while (offset < s->sh_offset) { fputc(0, f); @@ -2341,7 +2348,7 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, offset++; } - for(i = 0; i < shnum; i++) { + for(i = 0; i < s1->nb_sections; i++) { sh = &shdr; memset(sh, 0, sizeof(ElfW(Shdr))); s = s1->sections[i]; @@ -2422,7 +2429,7 @@ static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum, #ifndef ELF_OBJ_ONLY /* Sort section headers by assigned sh_addr, remove sections that we aren't going to output. */ -static int tidy_section_headers(TCCState *s1, int *sec_order) +static void tidy_section_headers(TCCState *s1, int *sec_order) { int i, nnew, l, *backmap; Section **snew, *s; @@ -2462,8 +2469,8 @@ static int tidy_section_headers(TCCState *s1, int *sec_order) sec_order[i] = i; tcc_free(s1->sections); s1->sections = snew; + s1->nb_sections = nnew; tcc_free(backmap); - return nnew; } #ifdef TCC_TARGET_ARM @@ -2529,33 +2536,34 @@ static void alloc_sec_names(TCCState *s1, int is_obj); /* XXX: suppress unneeded sections */ static int elf_output_file(TCCState *s1, const char *filename) { - int i, ret, file_type, file_offset, *sec_order; + int i, ret, phnum, phfill, shnum, file_type, file_offset, *sec_order; struct dyn_inf dyninf = {0}; - Section *interp, *dynstr, *dynamic; + struct ro_inf roinf; + ElfW(Phdr) *phdr; + Section *interp, *dynamic, *dynstr, *note; + struct ro_inf *roinf_use = NULL; int textrel, got_sym, dt_flags_1; file_type = s1->output_type; s1->nb_errors = 0; ret = -1; - interp = dynstr = dynamic = NULL; + phdr = NULL; sec_order = NULL; - dyninf.roinf = &dyninf._roinf; + interp = dynamic = dynstr = note = NULL; #ifdef TCC_TARGET_ARM create_arm_attribute_section (s1); #endif #if TARGETOS_OpenBSD - dyninf.note = create_bsd_note_section (s1, ".note.openbsd.ident", "OpenBSD"); + note = create_bsd_note_section (s1, ".note.openbsd.ident", "OpenBSD"); #endif #if TARGETOS_NetBSD - dyninf.note = create_bsd_note_section (s1, ".note.netbsd.ident", "NetBSD"); + note = create_bsd_note_section (s1, ".note.netbsd.ident", "NetBSD"); #endif -#if TARGETOS_FreeBSD || TARGETOS_NetBSD - dyninf.roinf = NULL; -#endif + { /* if linking, also link in runtime libraries (libc, libgcc, etc.) */ tcc_add_runtime(s1); resolve_common_syms(s1); @@ -2572,7 +2580,6 @@ static int elf_output_file(TCCState *s1, const char *filename) interp->sh_addralign = 1; ptr = section_ptr_add(interp, 1 + strlen(elfint)); strcpy(ptr, elfint); - dyninf.interp = interp; } /* add dynamic symbol table */ @@ -2605,6 +2612,7 @@ static int elf_output_file(TCCState *s1, const char *filename) build_got_entries(s1, 0); } version_add (s1); + } textrel = set_sec_sizes(s1); alloc_sec_names(s1, 0); @@ -2646,11 +2654,36 @@ static int elf_output_file(TCCState *s1, const char *filename) dynstr->sh_size = dynstr->data_offset; } + /* compute number of program headers */ + phfill = 2; + for (i = 1; i < s1->nb_sections; i++) + if (s1->sections[i]->sh_flags & SHF_TLS) + phfill = 3; + phnum = 3; + if (interp) + phnum += phfill; + if (note) + phnum++; +#if !TARGETOS_FreeBSD && !TARGETOS_NetBSD + /* GNU_RELRO */ + phnum++, roinf_use = &roinf; +#endif + + /* allocate program segment headers */ + phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); + /* compute number of sections */ + shnum = s1->nb_sections; /* this array is used to reorder sections in the output file */ - sec_order = tcc_malloc(sizeof(int) * 2 * s1->nb_sections); + sec_order = tcc_malloc(sizeof(int) * shnum); + sec_order[0] = 0; + /* compute section to program header mapping */ - file_offset = layout_sections(s1, sec_order, &dyninf); + file_offset = layout_sections(s1, phdr, phnum, phfill, interp, &roinf, sec_order + 1); + /* Fill remaining program header and finalize relocation related to dynamic + linking. */ + { + fill_unloadable_phdr(phdr, phnum, interp, dynamic, note, roinf_use); if (dynamic) { ElfW(Sym) *sym; @@ -2672,6 +2705,7 @@ static int elf_output_file(TCCState *s1, const char *filename) /* if building executable or DLL, then relocate each section except the GOT which is already relocated */ relocate_syms(s1, s1->symtab, 0); + ret = -1; if (s1->nb_errors != 0) goto the_end; relocate_sections(s1); @@ -2680,17 +2714,21 @@ static int elf_output_file(TCCState *s1, const char *filename) dynamic->data_offset = dyninf.data_offset; fill_dynamic(s1, &dyninf); } + tidy_section_headers(s1, sec_order); + /* Perform relocation to GOT or PLT entries */ if (file_type == TCC_OUTPUT_EXE && s1->static_link) fill_got(s1); else if (s1->got) fill_local_got_entries(s1); - + } /* Create the ELF file with name 'filename' */ - ret = tcc_write_elf_file(s1, filename, dyninf.phnum, dyninf.phdr, file_offset, sec_order); + ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order); + s1->nb_sections = shnum; + the_end: tcc_free(sec_order); - tcc_free(dyninf.phdr); + tcc_free(phdr); return ret; } #endif /* ndef ELF_OBJ_ONLY */ @@ -2713,24 +2751,42 @@ static void alloc_sec_names(TCCState *s1, int is_obj) strsec->sh_size = strsec->data_offset; } -/* Output an elf .o file */ -static int elf_output_obj(TCCState *s1, const char *filename) +static int layout_any_sections(TCCState *s1, int file_offset, int *sec_order, int is_obj) { + int i; Section *s; - int i, ret, file_offset; - s1->nb_errors = 0; - /* Allocate strings for section names */ - alloc_sec_names(s1, 1); - file_offset = sizeof (ElfW(Ehdr)); for(i = 1; i < s1->nb_sections; i++) { s = s1->sections[i]; - file_offset = (file_offset + 15) & -16; + if (!is_obj && (s->sh_flags & SHF_ALLOC)) + continue; + *sec_order++ = i; + file_offset = (file_offset + s->sh_addralign - 1) & + ~(s->sh_addralign - 1); s->sh_offset = file_offset; if (s->sh_type != SHT_NOBITS) file_offset += s->sh_size; } + return file_offset; +} + +/* Output an elf .o file */ +static int elf_output_obj(TCCState *s1, const char *filename) +{ + int ret, file_offset; + int *sec_order; + s1->nb_errors = 0; + + /* Allocate strings for section names */ + alloc_sec_names(s1, 1); + + /* this array is used to reorder sections in the output file */ + sec_order = tcc_malloc(sizeof(int) * s1->nb_sections); + sec_order[0] = 0; + file_offset = layout_any_sections(s1, sizeof (ElfW(Ehdr)), sec_order + 1, 1); + /* Create the ELF file with name 'filename' */ - ret = tcc_write_elf_file(s1, filename, 0, NULL, file_offset, NULL); + ret = tcc_write_elf_file(s1, filename, 0, NULL, file_offset, sec_order); + tcc_free(sec_order); return ret; } diff --git a/tccpe.c b/tccpe.c index 0efe649..6677331 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1071,18 +1071,12 @@ static int pe_section_class(Section *s) { int type, flags; const char *name; + type = s->sh_type; flags = s->sh_flags; name = s->name; - if (0 == memcmp(name, ".stab", 5)) { - if (0 == s->s1->do_debug) - return sec_last; - return name[5] ? sec_stabstr : sec_stab; - } if (flags & SHF_ALLOC) { - if (type == SHT_PROGBITS - || type == SHT_INIT_ARRAY - || type == SHT_FINI_ARRAY) { + if (type == SHT_PROGBITS) { if (flags & SHF_EXECINSTR) return sec_text; if (flags & SHF_WRITE) @@ -1095,44 +1089,52 @@ static int pe_section_class(Section *s) return sec_pdata; return sec_rdata; } else if (type == SHT_NOBITS) { - return sec_bss; + if (flags & SHF_WRITE) + return sec_bss; } - return sec_other; } else { if (0 == strcmp(name, ".reloc")) return sec_reloc; } - return sec_last; + if (0 == memcmp(name, ".stab", 5)) + return name[5] ? sec_stabstr : sec_stab; + if (flags & SHF_ALLOC) + return sec_other; + return -1; } static int pe_assign_addresses (struct pe_info *pe) { - int i, k, n, c, nbs; + int i, k, o, c; DWORD addr; - int *sec_order, *sec_cls; + int *section_order; struct section_info *si; Section *s; TCCState *s1 = pe->s1; if (PE_DLL == pe->type) pe->reloc = new_section(pe->s1, ".reloc", SHT_PROGBITS, 0); - //pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); - - nbs = s1->nb_sections; - sec_order = tcc_mallocz(2 * sizeof (int) * nbs); - sec_cls = sec_order + nbs; - for (i = 1; i < nbs; ++i) { - s = s1->sections[i]; - k = pe_section_class(s); - for (n = i; n > 1 && k < (c = sec_cls[n - 1]); --n) - sec_cls[n] = c, sec_order[n] = sec_order[n - 1]; - sec_cls[n] = k, sec_order[n] = i; + // pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); + + section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int)); + for (o = k = 0 ; k < sec_last; ++k) { + for (i = 1; i < s1->nb_sections; ++i) { + s = s1->sections[i]; + if (k == pe_section_class(s)) + section_order[o++] = i; + } } + si = NULL; addr = pe->imagebase + 1; - for (i = 1; (c = sec_cls[i]) < sec_last; ++i) { - s = s1->sections[sec_order[i]]; + for (i = 0; i < o; ++i) { + k = section_order[i]; + s = s1->sections[k]; + c = pe_section_class(s); + + if ((c == sec_stab || c == sec_stabstr) && 0 == s1->do_debug) + continue; if (PE_MERGE_DATA && c == sec_bss) c = sec_data; @@ -1193,31 +1195,28 @@ add_section: } //printf("%08x %05x %08x %s\n", si->sh_addr, si->sh_size, si->pe_flags, s->name); } + tcc_free(section_order); #if 0 - for (i = 1; i < nbs; ++i) { - Section *s = s1->sections[sec_order[i]]; + for (i = 1; i < s1->nb_sections; ++i) { + Section *s = s1->sections[i]; int type = s->sh_type; int flags = s->sh_flags; printf("section %-16s %-10s %08x %04x %s,%s,%s\n", s->name, type == SHT_PROGBITS ? "progbits" : - type == SHT_INIT_ARRAY ? "initarr" : - type == SHT_FINI_ARRAY ? "finiarr" : type == SHT_NOBITS ? "nobits" : type == SHT_SYMTAB ? "symtab" : type == SHT_STRTAB ? "strtab" : type == SHT_RELX ? "rel" : "???", - (unsigned)s->sh_addr, - (unsigned)s->data_offset, + s->sh_addr, + s->data_offset, flags & SHF_ALLOC ? "alloc" : "", flags & SHF_WRITE ? "write" : "", flags & SHF_EXECINSTR ? "exec" : "" ); - fflush(stdout); } s1->verbose = 2; #endif - tcc_free(sec_order); return 0; }
signature.asc
Description: Digital signature
_______________________________________________ Tinycc-devel mailing list Tinycc-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/tinycc-devel