int pe_load_res_file(TCCState *s1, int fd) { struct pe_rsrc_header hdr; Section *rsrc_section; int i, ret = -1; BYTE *ptr; lseek(fd, 0, SEEK_SET); if (!read_n(fd, &hdr, sizeof hdr)) goto quit; if (!pe_test_res_file(&hdr, sizeof hdr)) goto quit; rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC); ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData); lseek(fd, hdr.sectionhdr.PointerToRawData, SEEK_SET); if (!read_n(fd, ptr, hdr.sectionhdr.SizeOfRawData)) goto quit; lseek(fd, hdr.sectionhdr.PointerToRelocations, SEEK_SET); for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i) { struct pe_rsrc_reloc rel; if (!read_n(fd, &rel, sizeof rel)) goto quit; if (rel.type != 7) goto quit; // DIR32NB put_elf_reloc(symtab_section, rsrc_section, rel.offset, R_386_RELATIVE, 0); } ret = 0; quit: if (ret) error_noabort("unrecognized resource file format"); return ret; }
/* 'is_jmp' is '1' if it is a jump */ static void gcall_or_jmp(int is_jmp) { int r; if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { /* constant case */ if (vtop->r & VT_SYM) { /* relocation case */ greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32); } else { /* put an empty PC32 relocation */ put_elf_reloc(symtab_section, cur_text_section, ind + 1, R_386_PC32, 0); } oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ } else { /* otherwise, indirect call */ r = gv(RC_INT); o(0xff); /* call/jmp *r */ o(0xd0 + r + (is_jmp << 4)); } }
static int pe_check_symbols(struct pe_info *pe) { Elf32_Sym *sym; int sym_index, sym_end; int ret = 0; pe_align_section(text_section, 8); sym_end = symtab_section->data_offset / sizeof(Elf32_Sym); for (sym_index = 1; sym_index < sym_end; ++sym_index) { sym = (Elf32_Sym *) symtab_section->data + sym_index; if (sym->st_shndx == SHN_UNDEF) { const char *name = symtab_section->link->data + sym->st_name; unsigned type = ELF32_ST_TYPE(sym->st_info); int imp_sym = pe_find_import(pe->s1, name); struct import_symbol *is; if (imp_sym == 0) goto not_found; is = pe_add_import(pe, imp_sym); if (!is) goto not_found; if (type == STT_FUNC) { unsigned long offset = is->thk_offset; if (offset) { // Got aliased symbol, like stricmp and _stricmp } else { char buffer[100]; offset = text_section->data_offset; // Add the 'jmp IAT[x]' instruction *(WORD *) section_ptr_add(text_section, 8) = 0x25FF; // Add a helper symbol, will be patched later in pe_build_imports sprintf(buffer, "IAT.%s", name); is->iat_index = put_elf_sym(symtab_section, 0, sizeof(DWORD), ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, SHN_UNDEF, buffer); put_elf_reloc(symtab_section, text_section, offset + 2, R_386_32, is->iat_index); is->thk_offset = offset; } // tcc_realloc might have altered sym's address sym = (Elf32_Sym *) symtab_section->data + sym_index; // Patch the original symbol sym->st_value = offset; sym->st_shndx = text_section->sh_num; sym->st_other &= ~1; // Do not export continue; } if (type == STT_OBJECT) { if (is->iat_index == 0) { // Original symbol will be patched later in pe_build_imports is->iat_index = sym_index; continue; } } not_found: error_noabort("undefined symbol '%s'", name); ret = 1; } else if (pe->s1->rdynamic && ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { // If -rdynamic option, then export all non local symbols sym->st_other |= 1; } } return ret; }
static void pe_build_exports(struct pe_info *pe) { Elf32_Sym *sym; int sym_index, sym_end; DWORD rva_base, func_o, name_o, ord_o, str_o; struct pe_export_header *hdr; int sym_count, n, ord, *sorted, *sp; FILE *op; char buf[260]; const char *dllname; const char *name; rva_base = pe->thunk->sh_addr - pe->imagebase; sym_count = 0; n = 1; sorted = NULL; op = NULL; sym_end = symtab_section->data_offset / sizeof(Elf32_Sym); for (sym_index = 1; sym_index < sym_end; ++sym_index) { sym = (Elf32_Sym *) symtab_section->data + sym_index; name = symtab_section->link->data + sym->st_name; // Only export symbols from actually written sections if ((sym->st_other & 1) && pe->s1->sections[sym->st_shndx]->sh_addr) { dynarray_add((void ***) &sorted, &sym_count, (void *) n); dynarray_add((void ***) &sorted, &sym_count, (void *) name); } ++n; } if (sym_count == 0) return; sym_count /= 2; qsort(sorted, sym_count, 2 * sizeof(sorted[0]), sym_cmp); pe_align_section(pe->thunk, 16); dllname = tcc_basename(pe->filename); pe->exp_offs = pe->thunk->data_offset; func_o = pe->exp_offs + sizeof(struct pe_export_header); name_o = func_o + sym_count * sizeof(DWORD); ord_o = name_o + sym_count * sizeof(DWORD); str_o = ord_o + sym_count * sizeof(WORD); hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs); hdr->Characteristics = 0; hdr->Base = 1; hdr->NumberOfFunctions = sym_count; hdr->NumberOfNames = sym_count; hdr->AddressOfFunctions = func_o + rva_base; hdr->AddressOfNames = name_o + rva_base; hdr->AddressOfNameOrdinals = ord_o + rva_base; hdr->Name = str_o + rva_base; put_elf_str(pe->thunk, dllname); if (pe->def != NULL) { // Write exports to .def file op = fopen(pe->def, "w"); if (op == NULL) { error_noabort("could not create '%s': %s", pe->def, strerror(errno)); } else { fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname); if (verbose) { printf("<- %s (%d symbols)\n", buf, sym_count); } } } for (sp = sorted, ord = 0; ord < sym_count; ++ord, sp += 2) { sym_index = sp[0]; name = (const char *) sp[1]; // Insert actual address later in pe_relocate_rva put_elf_reloc(symtab_section, pe->thunk, func_o, R_386_RELATIVE, sym_index); *(DWORD *)(pe->thunk->data + name_o) = pe->thunk->data_offset + rva_base; *(WORD *)(pe->thunk->data + ord_o) = ord; put_elf_str(pe->thunk, name); func_o += sizeof(DWORD); name_o += sizeof(DWORD); ord_o += sizeof(WORD); if (op) fprintf(op, "%s@%d\n", name, ord); } pe->exp_size = pe->thunk->data_offset - pe->exp_offs; tcc_free(sorted); if (op) fclose(op); }
void gcode(void) { int i, n, t, r, stacksize, addr, pc, disp, rel, errs, more, func_start; Branch *b, *bn; // Generate function prolog func_start = cur_text_section->data_offset; if (!func_naked) { // Align local size to word stacksize = (-loc + 3) & -4; if (stacksize >= 4096) { // Generate stack guard since parameters can cross page boundary Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); gen(0xb8); // mov stacksize, %eax genword(stacksize); gen(0xe8); // call __chkstk, (does the stackframe too) put_reloc(cur_text_section, sym, cur_text_section->data_offset, R_386_PC32); genword(-4); } else { if (do_debug || loc || !func_noargs) { gen(0x55); // push %ebp gen(0x89); // mov %esp, %ebp gen(0xe5); } if (stacksize > 0) { if (stacksize == (char) stacksize) { gen(0x83); // sub esp, stacksize gen(0xec); gen(stacksize); } else { gen(0x81); // sub esp, stacksize gen(0xec); genword(stacksize); } } } // Save callee-saved registers used by function. for (r = 0; r < NB_REGS; ++r) { if ((reg_classes[r] & RC_SAVE) && (regs_used & (1 << r))) { gen(0x50 + r); // push r } } } // Optimize jumps more = 1; while (more) { more = 0; for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeLabel) b->target = 0; if (b->type != CodeJump) continue; t = skip_nops(b->target, 1); if (branch[t].type == CodeJump && !branch[t].param && b->target != branch[t].target) { // Eliminate jump to jump b->target = branch[t].target; more = 1; continue; } // Find next non-nop n = i + 1; while (branch[n].type == CodeNop || branch[n].type == CodeLine) n++; bn = branch + n; if (b->ind != bn->ind) continue; if (!b->param && bn->type == CodeJump) { // Eliminate dead jump instruction bn->type = CodeNop; more = 1; continue; } if (b->target > i && b->target <= n) { // Eliminate jump to next instruction b->type = CodeNop; more = 1; continue; } t = skip_nops(n + 1, 0); if (bn->type == CodeJump && !bn->param && b->target == t && bn->ind == branch[t].ind) { // Optimize inverted jump if (b->param) b->param ^= 1; b->target = bn->target; bn->type = CodeNop; more = 1; continue; } } // Eliminate unused labels for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeJump) branch[b->target].target++; } for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeLabel && !b->target && !b->sym) { // Remove label with no references b->type = CodeNop; more = 1; } } } // Assign addresses to branch points, assuming only long jumps addr = cur_text_section->data_offset; pc = 0; for (i = 0; i < br; ++i) { b = branch + i; addr += b->ind - pc; b->addr = addr; switch (b->type) { case CodeJump: addr += 5; if (b->param != 0) addr++; break; case CodeAlign: // Use convervative estimate for short/long jump estimation addr += b->param - 1; break; } pc = b->ind; } // Find jumps which can be encoded as short jumps for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeJump) { disp = branch[b->target].addr - b->addr - 2; if (b->param) disp--; if (disp == (char) disp) b->type = CodeShortJump; } } // Assign final addresses to branch points addr = cur_text_section->data_offset; pc = 0; for (i = 0; i < br; ++i) { b = branch + i; addr += b->ind - pc; b->addr = addr; switch (b->type) { case CodeJump: addr += 5; if (b->param) addr++; break; case CodeShortJump: addr += 2; break; case CodeAlign: addr = (addr + b->param - 1) & -b->param; break; } pc = b->ind; } // Generate code blocks pc = 0; errs = 0; for (i = 0; i < br; ++i) { b = branch + i; // Output code block before branch point if (b->ind != pc) { genblk(code + pc, b->ind - pc); pc = b->ind; } switch (b->type) { case CodeLabel: // Export label if symbol defined if (b->sym) { put_extern_sym_ex(b->sym, cur_text_section, b->addr, 0, 0); sym_free(b->sym); } break; case CodeJump: // Generate long jump instruction if (branch[b->target].type != CodeLabel) { printf("internal error: jump %d to non-label %d\n", i, b->target); errs++; } if (b->param > 0xff) error("invalid displacement"); if (b->param == 0) { gen(0xe9); genword(branch[b->target].addr - (b->addr + 5)); } else { gen(0x0f); gen(b->param - 0x10); genword(branch[b->target].addr - (b->addr + 6)); } break; case CodeShortJump: // Generate short jump instruction if (branch[b->target].type != CodeLabel) { printf("internal error: jump %d to non-label %d\n", i, b->target); errs++; } if (b->param == 0) { gen(0xeb); } else { gen(b->param - 0x20); } gen(branch[b->target].addr - (b->addr + 2)); break; case CodeReloc: if (b->param) { rel = R_386_PC32; } else { rel = R_386_32; } put_elf_reloc(symtab_section, cur_text_section, b->addr, rel, b->target); break; case CodeAlign: i = addr; while (i & (b->param - 1)) { gen(b->target); i++; } break; case CodeLine: put_stabn(N_SLINE, 0, b->target, b->addr - func_start); break; } } // Generate function epilog if (!func_naked) { // Restore callee-saved registers used by function. for (r = NB_REGS; r >= 0; --r) { if ((reg_classes[r] & RC_SAVE) && (regs_used & (1 << r))) { gen(0x58 + r); // pop r } } if (do_debug || loc || !func_noargs) gen(0xc9); // leave // Generate return if (func_ret_sub == 0) { gen(0xc3); // ret } else { gen(0xc2); // ret n gen(func_ret_sub); gen(func_ret_sub >> 8); } } #ifdef DEBUG_BRANCH printf("\nbranch table for %s\n", func_name); printf(" # t targ parm ind addr\n"); printf("---- - ---- ----- -------- --------\n"); for (i = 0; i < br; ++i) { b = branch + i; printf("%04d %c %04d %04x %08x %08x", i, "SLJjRANlE"[b->type], b->target, b->param, b->ind, b->addr); if (branch[i].sym) { printf(" sym=%s", get_tok_str(b->sym->v, NULL)); } printf("\n"); } printf("\n"); #endif if (errs) error("internal error: code generation"); }
ST_FN void pe_build_exports(struct pe_info *pe) { Elf32_Sym *sym; int sym_index, sym_end; DWORD rva_base, func_o, name_o, ord_o, str_o; struct pe_export_header *hdr; int sym_count, n, ord, *sorted, *sp; FILE *op; char buf[MAX_PATH]; const char *dllname; const char *name; rva_base = pe->thunk->sh_addr - pe->imagebase; sym_count = 0, n = 1, sorted = NULL, op = NULL; sym_end = symtab_section->data_offset / sizeof(Elf32_Sym); for (sym_index = 1; sym_index < sym_end; ++sym_index) { sym = (Elf32_Sym*)symtab_section->data + sym_index; name = symtab_section->link->data + sym->st_name; if ((sym->st_other & 1) /* export only symbols from actually written sections */ && pe->s1->sections[sym->st_shndx]->sh_addr) { dynarray_add((void***)&sorted, &sym_count, (void*)n); dynarray_add((void***)&sorted, &sym_count, (void*)name); } ++n; #if 0 if (sym->st_other & 1) printf("export: %s\n", name); if (sym->st_other & 2) printf("stdcall: %s\n", name); #endif } if (0 == sym_count) return; sym_count /= 2; qsort (sorted, sym_count, 2 * sizeof sorted[0], sym_cmp); pe_align_section(pe->thunk, 16); dllname = tcc_basename(pe->filename); pe->exp_offs = pe->thunk->data_offset; func_o = pe->exp_offs + sizeof(struct pe_export_header); name_o = func_o + sym_count * sizeof (DWORD); ord_o = name_o + sym_count * sizeof (DWORD); str_o = ord_o + sym_count * sizeof(WORD); hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs); hdr->Characteristics = 0; hdr->Base = 1; hdr->NumberOfFunctions = sym_count; hdr->NumberOfNames = sym_count; hdr->AddressOfFunctions = func_o + rva_base; hdr->AddressOfNames = name_o + rva_base; hdr->AddressOfNameOrdinals = ord_o + rva_base; hdr->Name = str_o + rva_base; put_elf_str(pe->thunk, dllname); #if 1 /* automatically write exports to <output-filename>.def */ strcpy(buf, pe->filename); strcpy(tcc_fileextension(buf), ".def"); op = fopen(buf, "w"); if (NULL == op) { error_noabort("could not create '%s': %s", buf, strerror(errno)); } else { fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname); if (pe->s1->verbose) printf("<- %s (%d symbols)\n", buf, sym_count); } #endif for (sp = sorted, ord = 0; ord < sym_count; ++ord, sp += 2) { sym_index = sp[0], name = (const char *)sp[1]; /* insert actual address later in pe_relocate_rva */ put_elf_reloc(symtab_section, pe->thunk, func_o, R_386_RELATIVE, sym_index); *(DWORD*)(pe->thunk->data + name_o) = pe->thunk->data_offset + rva_base; *(WORD*)(pe->thunk->data + ord_o) = ord; put_elf_str(pe->thunk, name); func_o += sizeof (DWORD); name_o += sizeof (DWORD); ord_o += sizeof (WORD); if (op) fprintf(op, "%s\n", name); } pe->exp_size = pe->thunk->data_offset - pe->exp_offs; tcc_free(sorted); }