mirror of
https://github.com/mirror/tinycc.git
synced 2025-02-26 08:00:09 +08:00
tccelf: some linker cleanup
- generate and use SYM@PLT for plt addresses - get rid of patch_dynsym_undef hack (no idea what it did on FreeBSD) - use sym_attrs instead of symtab_to_dynsym - special case for function pointers into .so on i386 - libtcc_test: test tcc_add_symbol with data object - move target specicic code to *-link.c files - add R_XXX_RELATIVE (needed for PE)
This commit is contained in:
parent
fe6453f8f0
commit
ca92bfc3c6
65
arm-link.c
65
arm-link.c
@ -14,8 +14,8 @@
|
|||||||
#define ELF_START_ADDR 0x00008000
|
#define ELF_START_ADDR 0x00008000
|
||||||
#define ELF_PAGE_SIZE 0x1000
|
#define ELF_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
#define HAVE_SECTION_RELOC
|
|
||||||
#define PCRELATIVE_DLLPLT 1
|
#define PCRELATIVE_DLLPLT 1
|
||||||
|
#define RELOCATE_DLLPLT 0
|
||||||
|
|
||||||
enum float_abi {
|
enum float_abi {
|
||||||
ARM_SOFTFP_FLOAT,
|
ARM_SOFTFP_FLOAT,
|
||||||
@ -101,6 +101,69 @@ int gotplt_entry_type (int reloc_type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
|
||||||
|
{
|
||||||
|
Section *plt = s1->plt;
|
||||||
|
uint8_t *p;
|
||||||
|
unsigned plt_offset;
|
||||||
|
|
||||||
|
/* when building a DLL, GOT entry accesses must be done relative to
|
||||||
|
start of GOT (see x86_64 examble above) */
|
||||||
|
if (s1->output_type == TCC_OUTPUT_DLL)
|
||||||
|
tcc_error("DLLs unimplemented!");
|
||||||
|
|
||||||
|
/* empty PLT: create PLT0 entry that push address of call site and
|
||||||
|
jump to ld.so resolution routine (GOT + 8) */
|
||||||
|
if (plt->data_offset == 0) {
|
||||||
|
p = section_ptr_add(plt, 20);
|
||||||
|
write32le(p, 0xe52de004); /* push {lr} */
|
||||||
|
write32le(p+4, 0xe59fe004); /* ldr lr, [pc, #4] */
|
||||||
|
write32le(p+8, 0xe08fe00e); /* add lr, pc, lr */
|
||||||
|
write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */
|
||||||
|
/* p+16 is set in relocate_plt */
|
||||||
|
}
|
||||||
|
plt_offset = plt->data_offset;
|
||||||
|
|
||||||
|
if (attr->plt_thumb_stub) {
|
||||||
|
p = section_ptr_add(plt, 4);
|
||||||
|
write32le(p, 0x4778); /* bx pc */
|
||||||
|
write32le(p+2, 0x46c0); /* nop */
|
||||||
|
}
|
||||||
|
p = section_ptr_add(plt, 16);
|
||||||
|
/* Jump to GOT entry where ld.so initially put address of PLT0 */
|
||||||
|
write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] */
|
||||||
|
write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */
|
||||||
|
write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */
|
||||||
|
/* p + 12 contains offset to GOT entry once patched by relocate_plt */
|
||||||
|
write32le(p+12, got_offset);
|
||||||
|
return plt_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relocate the PLT: compute addresses and offsets in the PLT now that final
|
||||||
|
address for PLT and GOT are known (see fill_program_header) */
|
||||||
|
ST_FUNC void relocate_plt(TCCState *s1)
|
||||||
|
{
|
||||||
|
uint8_t *p, *p_end;
|
||||||
|
|
||||||
|
if (!s1->plt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = s1->plt->data;
|
||||||
|
p_end = p + s1->plt->data_offset;
|
||||||
|
|
||||||
|
if (p < p_end) {
|
||||||
|
int x = s1->got->sh_addr - s1->plt->sh_addr - 12;
|
||||||
|
write32le(s1->plt->data + 16, x - 16);
|
||||||
|
p += 20;
|
||||||
|
while (p < p_end) {
|
||||||
|
if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */
|
||||||
|
p += 4;
|
||||||
|
add32le(p + 12, x + s1->plt->data - p);
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void relocate_init(Section *sr) {}
|
void relocate_init(Section *sr) {}
|
||||||
|
|
||||||
void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
|
void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
|
||||||
|
70
arm64-link.c
70
arm64-link.c
@ -13,8 +13,8 @@
|
|||||||
#define ELF_START_ADDR 0x00400000
|
#define ELF_START_ADDR 0x00400000
|
||||||
#define ELF_PAGE_SIZE 0x1000
|
#define ELF_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
#define HAVE_SECTION_RELOC
|
|
||||||
#define PCRELATIVE_DLLPLT 1
|
#define PCRELATIVE_DLLPLT 1
|
||||||
|
#define RELOCATE_DLLPLT 1
|
||||||
|
|
||||||
#else /* !TARGET_DEFS_ONLY */
|
#else /* !TARGET_DEFS_ONLY */
|
||||||
|
|
||||||
@ -81,6 +81,74 @@ int gotplt_entry_type (int reloc_type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
|
||||||
|
{
|
||||||
|
Section *plt = s1->plt;
|
||||||
|
uint8_t *p;
|
||||||
|
unsigned plt_offset;
|
||||||
|
|
||||||
|
if (s1->output_type == TCC_OUTPUT_DLL)
|
||||||
|
tcc_error("DLLs unimplemented!");
|
||||||
|
|
||||||
|
if (plt->data_offset == 0) {
|
||||||
|
section_ptr_add(plt, 32);
|
||||||
|
}
|
||||||
|
plt_offset = plt->data_offset;
|
||||||
|
|
||||||
|
p = section_ptr_add(plt, 16);
|
||||||
|
write32le(p, got_offset);
|
||||||
|
write32le(p + 4, (uint64_t) got_offset >> 32);
|
||||||
|
return plt_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relocate the PLT: compute addresses and offsets in the PLT now that final
|
||||||
|
address for PLT and GOT are known (see fill_program_header) */
|
||||||
|
ST_FUNC void relocate_plt(TCCState *s1)
|
||||||
|
{
|
||||||
|
uint8_t *p, *p_end;
|
||||||
|
|
||||||
|
if (!s1->plt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = s1->plt->data;
|
||||||
|
p_end = p + s1->plt->data_offset;
|
||||||
|
|
||||||
|
if (p < p_end) {
|
||||||
|
uint64_t plt = s1->plt->sh_addr;
|
||||||
|
uint64_t got = s1->got->sh_addr;
|
||||||
|
uint64_t off = (got >> 12) - (plt >> 12);
|
||||||
|
if ((off + ((uint32_t)1 << 20)) >> 21)
|
||||||
|
tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", off, got, plt);
|
||||||
|
write32le(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]!
|
||||||
|
write32le(p + 4, (0x90000010 | // adrp x16,...
|
||||||
|
(off & 0x1ffffc) << 3 | (off & 3) << 29));
|
||||||
|
write32le(p + 8, (0xf9400211 | // ldr x17,[x16,#...]
|
||||||
|
(got & 0xff8) << 7));
|
||||||
|
write32le(p + 12, (0x91000210 | // add x16,x16,#...
|
||||||
|
(got & 0xfff) << 10));
|
||||||
|
write32le(p + 16, 0xd61f0220); // br x17
|
||||||
|
write32le(p + 20, 0xd503201f); // nop
|
||||||
|
write32le(p + 24, 0xd503201f); // nop
|
||||||
|
write32le(p + 28, 0xd503201f); // nop
|
||||||
|
p += 32;
|
||||||
|
while (p < p_end) {
|
||||||
|
uint64_t pc = plt + (p - s1->plt->data);
|
||||||
|
uint64_t addr = got + read64le(p);
|
||||||
|
uint64_t off = (addr >> 12) - (pc >> 12);
|
||||||
|
if ((off + ((uint32_t)1 << 20)) >> 21)
|
||||||
|
tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc);
|
||||||
|
write32le(p, (0x90000010 | // adrp x16,...
|
||||||
|
(off & 0x1ffffc) << 3 | (off & 3) << 29));
|
||||||
|
write32le(p + 4, (0xf9400211 | // ldr x17,[x16,#...]
|
||||||
|
(addr & 0xff8) << 7));
|
||||||
|
write32le(p + 8, (0x91000210 | // add x16,x16,#...
|
||||||
|
(addr & 0xfff) << 10));
|
||||||
|
write32le(p + 12, 0xd61f0220); // br x17
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void relocate_init(Section *sr) {}
|
void relocate_init(Section *sr) {}
|
||||||
|
|
||||||
void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
|
void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
|
||||||
|
28
c67-link.c
28
c67-link.c
@ -14,8 +14,8 @@
|
|||||||
#define ELF_START_ADDR 0x00000400
|
#define ELF_START_ADDR 0x00000400
|
||||||
#define ELF_PAGE_SIZE 0x1000
|
#define ELF_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
#define HAVE_SECTION_RELOC
|
|
||||||
#define PCRELATIVE_DLLPLT 0
|
#define PCRELATIVE_DLLPLT 0
|
||||||
|
#define RELOCATE_DLLPLT 0
|
||||||
|
|
||||||
#else /* !TARGET_DEFS_ONLY */
|
#else /* !TARGET_DEFS_ONLY */
|
||||||
|
|
||||||
@ -68,6 +68,32 @@ int gotplt_entry_type (int reloc_type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
|
||||||
|
{
|
||||||
|
tcc_error("C67 got not implemented");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relocate the PLT: compute addresses and offsets in the PLT now that final
|
||||||
|
address for PLT and GOT are known (see fill_program_header) */
|
||||||
|
ST_FUNC void relocate_plt(TCCState *s1)
|
||||||
|
{
|
||||||
|
uint8_t *p, *p_end;
|
||||||
|
|
||||||
|
if (!s1->plt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = s1->plt->data;
|
||||||
|
p_end = p + s1->plt->data_offset;
|
||||||
|
|
||||||
|
if (p < p_end) {
|
||||||
|
/* XXX: TODO */
|
||||||
|
while (p < p_end) {
|
||||||
|
/* XXX: TODO */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void relocate_init(Section *sr) {}
|
void relocate_init(Section *sr) {}
|
||||||
|
|
||||||
void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
|
void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
|
||||||
|
75
i386-link.c
75
i386-link.c
@ -14,8 +14,8 @@
|
|||||||
#define ELF_START_ADDR 0x08048000
|
#define ELF_START_ADDR 0x08048000
|
||||||
#define ELF_PAGE_SIZE 0x1000
|
#define ELF_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
#define HAVE_SECTION_RELOC
|
|
||||||
#define PCRELATIVE_DLLPLT 0
|
#define PCRELATIVE_DLLPLT 0
|
||||||
|
#define RELOCATE_DLLPLT 0
|
||||||
|
|
||||||
#else /* !TARGET_DEFS_ONLY */
|
#else /* !TARGET_DEFS_ONLY */
|
||||||
|
|
||||||
@ -26,6 +26,7 @@
|
|||||||
int code_reloc (int reloc_type)
|
int code_reloc (int reloc_type)
|
||||||
{
|
{
|
||||||
switch (reloc_type) {
|
switch (reloc_type) {
|
||||||
|
case R_386_RELATIVE:
|
||||||
case R_386_16:
|
case R_386_16:
|
||||||
case R_386_32:
|
case R_386_32:
|
||||||
case R_386_GOTPC:
|
case R_386_GOTPC:
|
||||||
@ -53,6 +54,7 @@ int code_reloc (int reloc_type)
|
|||||||
int gotplt_entry_type (int reloc_type)
|
int gotplt_entry_type (int reloc_type)
|
||||||
{
|
{
|
||||||
switch (reloc_type) {
|
switch (reloc_type) {
|
||||||
|
case R_386_RELATIVE:
|
||||||
case R_386_16:
|
case R_386_16:
|
||||||
case R_386_32:
|
case R_386_32:
|
||||||
case R_386_GLOB_DAT:
|
case R_386_GLOB_DAT:
|
||||||
@ -78,6 +80,73 @@ int gotplt_entry_type (int reloc_type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
|
||||||
|
{
|
||||||
|
Section *plt = s1->plt;
|
||||||
|
uint8_t *p;
|
||||||
|
int modrm;
|
||||||
|
unsigned plt_offset, relofs;
|
||||||
|
|
||||||
|
/* on i386 if we build a DLL, we add a %ebx offset */
|
||||||
|
if (s1->output_type == TCC_OUTPUT_DLL)
|
||||||
|
modrm = 0xa3;
|
||||||
|
else
|
||||||
|
modrm = 0x25;
|
||||||
|
|
||||||
|
/* empty PLT: create PLT0 entry that pushes the library indentifier
|
||||||
|
(GOT + PTR_SIZE) and jumps to ld.so resolution routine
|
||||||
|
(GOT + 2 * PTR_SIZE) */
|
||||||
|
if (plt->data_offset == 0) {
|
||||||
|
p = section_ptr_add(plt, 16);
|
||||||
|
p[0] = 0xff; /* pushl got + PTR_SIZE */
|
||||||
|
p[1] = modrm + 0x10;
|
||||||
|
write32le(p + 2, PTR_SIZE);
|
||||||
|
p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
|
||||||
|
p[7] = modrm;
|
||||||
|
write32le(p + 8, PTR_SIZE * 2);
|
||||||
|
}
|
||||||
|
plt_offset = plt->data_offset;
|
||||||
|
|
||||||
|
/* The PLT slot refers to the relocation entry it needs via offset.
|
||||||
|
The reloc entry is created below, so its offset is the current
|
||||||
|
data_offset */
|
||||||
|
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
|
||||||
|
|
||||||
|
/* Jump to GOT entry where ld.so initially put the address of ip + 4 */
|
||||||
|
p = section_ptr_add(plt, 16);
|
||||||
|
p[0] = 0xff; /* jmp *(got + x) */
|
||||||
|
p[1] = modrm;
|
||||||
|
write32le(p + 2, got_offset);
|
||||||
|
p[6] = 0x68; /* push $xxx */
|
||||||
|
write32le(p + 7, relofs);
|
||||||
|
p[11] = 0xe9; /* jmp plt_start */
|
||||||
|
write32le(p + 12, -(plt->data_offset));
|
||||||
|
return plt_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relocate the PLT: compute addresses and offsets in the PLT now that final
|
||||||
|
address for PLT and GOT are known (see fill_program_header) */
|
||||||
|
ST_FUNC void relocate_plt(TCCState *s1)
|
||||||
|
{
|
||||||
|
uint8_t *p, *p_end;
|
||||||
|
|
||||||
|
if (!s1->plt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = s1->plt->data;
|
||||||
|
p_end = p + s1->plt->data_offset;
|
||||||
|
|
||||||
|
if (p < p_end) {
|
||||||
|
add32le(p + 2, s1->got->sh_addr);
|
||||||
|
add32le(p + 8, s1->got->sh_addr);
|
||||||
|
p += 16;
|
||||||
|
while (p < p_end) {
|
||||||
|
add32le(p + 2, s1->got->sh_addr);
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
|
static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
|
||||||
|
|
||||||
void relocate_init(Section *sr)
|
void relocate_init(Section *sr)
|
||||||
@ -94,7 +163,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case R_386_32:
|
case R_386_32:
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
if (s1->output_type == TCC_OUTPUT_DLL) {
|
||||||
esym_index = s1->symtab_to_dynsym[sym_index];
|
esym_index = s1->sym_attrs[sym_index].dyn_index;
|
||||||
qrel->r_offset = rel->r_offset;
|
qrel->r_offset = rel->r_offset;
|
||||||
if (esym_index) {
|
if (esym_index) {
|
||||||
qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
|
qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
|
||||||
@ -110,7 +179,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add
|
|||||||
case R_386_PC32:
|
case R_386_PC32:
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
if (s1->output_type == TCC_OUTPUT_DLL) {
|
||||||
/* DLL relocation */
|
/* DLL relocation */
|
||||||
esym_index = s1->symtab_to_dynsym[sym_index];
|
esym_index = s1->sym_attrs[sym_index].dyn_index;
|
||||||
if (esym_index) {
|
if (esym_index) {
|
||||||
qrel->r_offset = rel->r_offset;
|
qrel->r_offset = rel->r_offset;
|
||||||
qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
|
qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
|
||||||
|
1
libtcc.c
1
libtcc.c
@ -941,7 +941,6 @@ LIBTCCAPI void tcc_delete(TCCState *s1)
|
|||||||
tcc_run_free(s1);
|
tcc_run_free(s1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tcc_free(s1->sym_attrs);
|
|
||||||
tcc_free(s1);
|
tcc_free(s1);
|
||||||
tcc_memstats(bench);
|
tcc_memstats(bench);
|
||||||
}
|
}
|
||||||
|
29
tcc.h
29
tcc.h
@ -271,8 +271,6 @@
|
|||||||
# define ElfW_Rel ElfW(Rela)
|
# define ElfW_Rel ElfW(Rela)
|
||||||
# define SHT_RELX SHT_RELA
|
# define SHT_RELX SHT_RELA
|
||||||
# define REL_SECTION_FMT ".rela%s"
|
# define REL_SECTION_FMT ".rela%s"
|
||||||
/* XXX: DLL with PLT would only work with x86-64 for now */
|
|
||||||
# define TCC_OUTPUT_DLL_WITH_PLT
|
|
||||||
#else
|
#else
|
||||||
# define ELFCLASSW ELFCLASS32
|
# define ELFCLASSW ELFCLASS32
|
||||||
# define ElfW(type) Elf##32##_##type
|
# define ElfW(type) Elf##32##_##type
|
||||||
@ -577,8 +575,10 @@ typedef struct ASMOperand {
|
|||||||
|
|
||||||
/* extra symbol attributes (not in symbol table) */
|
/* extra symbol attributes (not in symbol table) */
|
||||||
struct sym_attr {
|
struct sym_attr {
|
||||||
unsigned long got_offset;
|
unsigned got_offset;
|
||||||
unsigned long plt_offset;
|
unsigned plt_offset;
|
||||||
|
int plt_sym;
|
||||||
|
int dyn_index;
|
||||||
#ifdef TCC_TARGET_ARM
|
#ifdef TCC_TARGET_ARM
|
||||||
unsigned char plt_thumb_stub:1;
|
unsigned char plt_thumb_stub:1;
|
||||||
#endif
|
#endif
|
||||||
@ -633,7 +633,7 @@ struct TCCState {
|
|||||||
addr_t text_addr; /* address of text section */
|
addr_t text_addr; /* address of text section */
|
||||||
int has_text_addr;
|
int has_text_addr;
|
||||||
|
|
||||||
unsigned long section_align; /* section alignment */
|
unsigned section_align; /* section alignment */
|
||||||
|
|
||||||
char *init_symbol; /* symbols to call at load-time (not used currently) */
|
char *init_symbol; /* symbols to call at load-time (not used currently) */
|
||||||
char *fini_symbol; /* symbols to call at unload-time (not used currently) */
|
char *fini_symbol; /* symbols to call at unload-time (not used currently) */
|
||||||
@ -714,8 +714,6 @@ struct TCCState {
|
|||||||
/* got & plt handling */
|
/* got & plt handling */
|
||||||
Section *got;
|
Section *got;
|
||||||
Section *plt;
|
Section *plt;
|
||||||
/* give the correspondance from symtab indexes to dynsym indexes */
|
|
||||||
int *symtab_to_dynsym;
|
|
||||||
|
|
||||||
/* temporary dynamic symbol sections (for dll loading) */
|
/* temporary dynamic symbol sections (for dll loading) */
|
||||||
Section *dynsymtab_section;
|
Section *dynsymtab_section;
|
||||||
@ -1332,9 +1330,6 @@ ST_FUNC void tccelf_new(TCCState *s);
|
|||||||
ST_FUNC void tccelf_delete(TCCState *s);
|
ST_FUNC void tccelf_delete(TCCState *s);
|
||||||
ST_FUNC void tccelf_stab_new(TCCState *s);
|
ST_FUNC void tccelf_stab_new(TCCState *s);
|
||||||
|
|
||||||
/* return offset of 'ptr' from start of section 'sec' */
|
|
||||||
#define OFFSET_FROM_SECTION_START(sec, ptr) ((size_t)ptr - (size_t)sec->data)
|
|
||||||
|
|
||||||
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
|
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
|
||||||
ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
|
ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
|
||||||
ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
|
ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
|
||||||
@ -1362,17 +1357,16 @@ ST_FUNC void put_stabd(int type, int other, int desc);
|
|||||||
ST_FUNC void relocate_common_syms(void);
|
ST_FUNC void relocate_common_syms(void);
|
||||||
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
|
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
|
||||||
ST_FUNC void relocate_section(TCCState *s1, Section *s);
|
ST_FUNC void relocate_section(TCCState *s1, Section *s);
|
||||||
ST_FUNC void relocate_plt(TCCState *s1);
|
|
||||||
|
|
||||||
ST_FUNC void tcc_add_linker_symbols(TCCState *s1);
|
ST_FUNC void tcc_add_linker_symbols(TCCState *s1);
|
||||||
ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
|
ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
|
||||||
ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
|
ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
|
||||||
ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
|
ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
|
||||||
ST_FUNC void tcc_add_bcheck(TCCState *s1);
|
ST_FUNC void tcc_add_bcheck(TCCState *s1);
|
||||||
|
|
||||||
ST_FUNC void build_got_entries(TCCState *s1);
|
|
||||||
ST_FUNC void tcc_add_runtime(TCCState *s1);
|
ST_FUNC void tcc_add_runtime(TCCState *s1);
|
||||||
|
|
||||||
|
ST_FUNC void build_got_entries(TCCState *s1);
|
||||||
|
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc);
|
||||||
ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
|
ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
|
||||||
#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
|
#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
|
||||||
ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
|
ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
|
||||||
@ -1400,6 +1394,10 @@ enum gotplt_entry {
|
|||||||
|
|
||||||
ST_FUNC int code_reloc (int reloc_type);
|
ST_FUNC int code_reloc (int reloc_type);
|
||||||
ST_FUNC int gotplt_entry_type (int reloc_type);
|
ST_FUNC int gotplt_entry_type (int reloc_type);
|
||||||
|
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr);
|
||||||
|
ST_FUNC void relocate_init(Section *sr);
|
||||||
|
ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val);
|
||||||
|
ST_FUNC void relocate_plt(TCCState *s1);
|
||||||
|
|
||||||
/* ------------ xxx-gen.c ------------ */
|
/* ------------ xxx-gen.c ------------ */
|
||||||
|
|
||||||
@ -1506,11 +1504,6 @@ ST_FUNC void gen_clear_cache(void);
|
|||||||
#ifdef TCC_TARGET_C67
|
#ifdef TCC_TARGET_C67
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ------------ xxx-link.c ------------ */
|
|
||||||
|
|
||||||
ST_FUNC void relocate_init(Section *sr);
|
|
||||||
ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val);
|
|
||||||
|
|
||||||
/* ------------ tcccoff.c ------------ */
|
/* ------------ tcccoff.c ------------ */
|
||||||
|
|
||||||
#ifdef TCC_TARGET_COFF
|
#ifdef TCC_TARGET_COFF
|
||||||
|
555
tccelf.c
555
tccelf.c
@ -67,6 +67,7 @@ ST_FUNC void tccelf_new(TCCState *s)
|
|||||||
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
|
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
|
||||||
".dynstrtab",
|
".dynstrtab",
|
||||||
".dynhashtab", SHF_PRIVATE);
|
".dynhashtab", SHF_PRIVATE);
|
||||||
|
get_sym_attr(s, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TCC_BCHECK
|
#ifdef CONFIG_TCC_BCHECK
|
||||||
@ -123,6 +124,7 @@ ST_FUNC void tccelf_delete(TCCState *s1)
|
|||||||
#endif
|
#endif
|
||||||
/* free loaded dlls array */
|
/* free loaded dlls array */
|
||||||
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
|
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
|
||||||
|
tcc_free(s1->sym_attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
|
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
|
||||||
@ -160,6 +162,35 @@ ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh
|
|||||||
return sec;
|
return sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ST_FUNC Section *new_symtab(TCCState *s1,
|
||||||
|
const char *symtab_name, int sh_type, int sh_flags,
|
||||||
|
const char *strtab_name,
|
||||||
|
const char *hash_name, int hash_sh_flags)
|
||||||
|
{
|
||||||
|
Section *symtab, *strtab, *hash;
|
||||||
|
int *ptr, nb_buckets;
|
||||||
|
|
||||||
|
symtab = new_section(s1, symtab_name, sh_type, sh_flags);
|
||||||
|
symtab->sh_entsize = sizeof(ElfW(Sym));
|
||||||
|
strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
|
||||||
|
put_elf_str(strtab, "");
|
||||||
|
symtab->link = strtab;
|
||||||
|
put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
|
||||||
|
|
||||||
|
nb_buckets = 1;
|
||||||
|
|
||||||
|
hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
|
||||||
|
hash->sh_entsize = sizeof(int);
|
||||||
|
symtab->hash = hash;
|
||||||
|
hash->link = symtab;
|
||||||
|
|
||||||
|
ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
|
||||||
|
ptr[0] = nb_buckets;
|
||||||
|
ptr[1] = 1;
|
||||||
|
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
|
||||||
|
return symtab;
|
||||||
|
}
|
||||||
|
|
||||||
/* realloc section and set its content to zero */
|
/* realloc section and set its content to zero */
|
||||||
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
|
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
|
||||||
{
|
{
|
||||||
@ -490,7 +521,7 @@ ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
|
|||||||
rel = section_ptr_add(sr, sizeof(ElfW_Rel));
|
rel = section_ptr_add(sr, sizeof(ElfW_Rel));
|
||||||
rel->r_offset = offset;
|
rel->r_offset = offset;
|
||||||
rel->r_info = ELFW(R_INFO)(symbol, type);
|
rel->r_info = ELFW(R_INFO)(symbol, type);
|
||||||
#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
|
#if SHT_RELX == SHT_RELA
|
||||||
rel->r_addend = addend;
|
rel->r_addend = addend;
|
||||||
#else
|
#else
|
||||||
if (addend)
|
if (addend)
|
||||||
@ -542,14 +573,14 @@ ST_FUNC void put_stabd(int type, int other, int desc)
|
|||||||
put_stabs(NULL, type, other, desc, 0);
|
put_stabs(NULL, type, other, desc, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc)
|
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
struct sym_attr *tab;
|
struct sym_attr *tab;
|
||||||
|
|
||||||
if (index >= s1->nb_sym_attrs) {
|
if (index >= s1->nb_sym_attrs) {
|
||||||
if (!alloc)
|
if (!alloc)
|
||||||
return NULL;
|
return s1->sym_attrs;
|
||||||
/* find immediately bigger power of 2 and reallocate array */
|
/* find immediately bigger power of 2 and reallocate array */
|
||||||
n = 1;
|
n = 1;
|
||||||
while (index >= n)
|
while (index >= n)
|
||||||
@ -706,29 +737,19 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
|
|||||||
int type, sym_index;
|
int type, sym_index;
|
||||||
unsigned char *ptr;
|
unsigned char *ptr;
|
||||||
addr_t tgt, addr;
|
addr_t tgt, addr;
|
||||||
struct sym_attr *symattr;
|
|
||||||
|
|
||||||
relocate_init(sr);
|
relocate_init(sr);
|
||||||
|
|
||||||
for_each_elem(sr, 0, rel, ElfW_Rel) {
|
for_each_elem(sr, 0, rel, ElfW_Rel) {
|
||||||
ptr = s->data + rel->r_offset;
|
ptr = s->data + rel->r_offset;
|
||||||
|
|
||||||
sym_index = ELFW(R_SYM)(rel->r_info);
|
sym_index = ELFW(R_SYM)(rel->r_info);
|
||||||
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
|
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
|
||||||
type = ELFW(R_TYPE)(rel->r_info);
|
type = ELFW(R_TYPE)(rel->r_info);
|
||||||
symattr = get_sym_attr(s1, sym_index, 0);
|
|
||||||
tgt = sym->st_value;
|
tgt = sym->st_value;
|
||||||
/* If static relocation to a dynamic symbol, relocate to PLT entry.
|
#if SHT_RELX == SHT_RELA
|
||||||
Note 1: in tcc -run mode we go through PLT to avoid range issues
|
|
||||||
Note 2: symbols compiled with libtcc and later added with
|
|
||||||
tcc_add_symbol are not dynamic and thus have symattr NULL */
|
|
||||||
if (gotplt_entry_type(type) != NO_GOTPLT_ENTRY &&
|
|
||||||
code_reloc(type) && symattr && symattr->plt_offset)
|
|
||||||
tgt = s1->plt->sh_addr + symattr->plt_offset;
|
|
||||||
#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
|
|
||||||
tgt += rel->r_addend;
|
tgt += rel->r_addend;
|
||||||
#endif
|
#endif
|
||||||
addr = s->sh_addr + rel->r_offset;
|
addr = s->sh_addr + rel->r_offset;
|
||||||
|
|
||||||
relocate(s1, rel, type, ptr, addr, tgt);
|
relocate(s1, rel, type, ptr, addr, tgt);
|
||||||
}
|
}
|
||||||
/* if the relocation is allocated, we change its symbol table */
|
/* if the relocation is allocated, we change its symbol table */
|
||||||
@ -752,7 +773,7 @@ static void relocate_rel(TCCState *s1, Section *sr)
|
|||||||
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
||||||
{
|
{
|
||||||
ElfW_Rel *rel;
|
ElfW_Rel *rel;
|
||||||
int sym_index, esym_index, type, count;
|
int sym_index, type, count;
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
for_each_elem(sr, 0, rel, ElfW_Rel) {
|
for_each_elem(sr, 0, rel, ElfW_Rel) {
|
||||||
@ -773,8 +794,7 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
|||||||
#elif defined(TCC_TARGET_X86_64)
|
#elif defined(TCC_TARGET_X86_64)
|
||||||
case R_X86_64_PC32:
|
case R_X86_64_PC32:
|
||||||
#endif
|
#endif
|
||||||
esym_index = s1->symtab_to_dynsym[sym_index];
|
if (get_sym_attr(s1, sym_index, 0)->dyn_index)
|
||||||
if (esym_index)
|
|
||||||
count++;
|
count++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -791,206 +811,43 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
|||||||
|
|
||||||
static void build_got(TCCState *s1)
|
static void build_got(TCCState *s1)
|
||||||
{
|
{
|
||||||
unsigned char *ptr;
|
|
||||||
|
|
||||||
/* if no got, then create it */
|
/* if no got, then create it */
|
||||||
s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||||||
s1->got->sh_entsize = 4;
|
s1->got->sh_entsize = 4;
|
||||||
set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
|
set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
|
||||||
0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
|
0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
|
||||||
ptr = section_ptr_add(s1->got, 3 * PTR_SIZE);
|
/* keep space for _DYNAMIC pointer and two dummy got entries */
|
||||||
#if PTR_SIZE == 4
|
section_ptr_add(s1->got, 3 * PTR_SIZE);
|
||||||
/* keep space for _DYNAMIC pointer, if present */
|
|
||||||
write32le(ptr, 0);
|
|
||||||
/* two dummy got entries */
|
|
||||||
write32le(ptr + 4, 0);
|
|
||||||
write32le(ptr + 8, 0);
|
|
||||||
#else
|
|
||||||
/* keep space for _DYNAMIC pointer, if present */
|
|
||||||
write32le(ptr, 0);
|
|
||||||
write32le(ptr + 4, 0);
|
|
||||||
/* two dummy got entries */
|
|
||||||
write32le(ptr + 8, 0);
|
|
||||||
write32le(ptr + 12, 0);
|
|
||||||
write32le(ptr + 16, 0);
|
|
||||||
write32le(ptr + 20, 0);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
|
/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
|
||||||
in s1->symtab. When creating the dynamic symbol table entry for the GOT
|
in s1->symtab. When creating the dynamic symbol table entry for the GOT
|
||||||
relocation, use 'size' and 'info' for the corresponding symbol metadata.
|
relocation, use 'size' and 'info' for the corresponding symbol metadata.
|
||||||
Returns the offset of the GOT or (if any) PLT entry. */
|
Returns the offset of the GOT or (if any) PLT entry. */
|
||||||
static unsigned long put_got_entry(TCCState *s1, int dyn_reloc_type,
|
static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type,
|
||||||
int reloc_type, unsigned long size,
|
int reloc_type, unsigned long size,
|
||||||
int info, int sym_index)
|
int info, int sym_index)
|
||||||
{
|
{
|
||||||
int index, need_plt_entry = 0;
|
int need_plt_entry;
|
||||||
const char *name;
|
const char *name;
|
||||||
ElfW(Sym) *sym, *esym;
|
ElfW(Sym) *sym;
|
||||||
unsigned long offset;
|
struct sym_attr *attr;
|
||||||
int *ptr;
|
unsigned got_offset;
|
||||||
size_t got_offset;
|
char plt_name[100];
|
||||||
struct sym_attr *symattr;
|
int len;
|
||||||
|
|
||||||
need_plt_entry = (dyn_reloc_type == R_JMP_SLOT);
|
need_plt_entry = (dyn_reloc_type == R_JMP_SLOT);
|
||||||
|
attr = get_sym_attr(s1, sym_index, 1);
|
||||||
if (!s1->got)
|
|
||||||
build_got(s1);
|
|
||||||
|
|
||||||
/* create PLT if needed */
|
|
||||||
if (need_plt_entry && !s1->plt) {
|
|
||||||
s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
|
|
||||||
SHF_ALLOC | SHF_EXECINSTR);
|
|
||||||
s1->plt->sh_entsize = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* already a GOT and/or PLT entry, no need to add one */
|
|
||||||
if (sym_index < s1->nb_sym_attrs) {
|
|
||||||
if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset)
|
|
||||||
return s1->sym_attrs[sym_index].plt_offset;
|
|
||||||
else if (!need_plt_entry && s1->sym_attrs[sym_index].got_offset)
|
|
||||||
return s1->sym_attrs[sym_index].got_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
symattr = get_sym_attr(s1, sym_index, 1);
|
|
||||||
|
|
||||||
/* create the GOT entry */
|
|
||||||
ptr = section_ptr_add(s1->got, PTR_SIZE);
|
|
||||||
*ptr = 0;
|
|
||||||
got_offset = OFFSET_FROM_SECTION_START (s1->got, ptr);
|
|
||||||
|
|
||||||
/* In case a function is both called and its address taken 2 GOT entries
|
/* In case a function is both called and its address taken 2 GOT entries
|
||||||
are created, one for taking the address (GOT) and the other for the PLT
|
are created, one for taking the address (GOT) and the other for the PLT
|
||||||
entry (PLTGOT). We don't record the offset of the PLTGOT entry in the
|
entry (PLTGOT). */
|
||||||
got_offset field since it might overwrite the offset of a GOT entry.
|
if (need_plt_entry ? attr->plt_offset : attr->got_offset)
|
||||||
Besides, for PLT entry the static relocation is against the PLT entry
|
return attr;
|
||||||
and the dynamic relocation for PLTGOT is created in this function. */
|
|
||||||
if (!need_plt_entry)
|
|
||||||
symattr->got_offset = got_offset;
|
|
||||||
|
|
||||||
sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
|
/* create the GOT entry */
|
||||||
name = (char *) symtab_section->link->data + sym->st_name;
|
got_offset = s1->got->data_offset;
|
||||||
offset = sym->st_value;
|
section_ptr_add(s1->got, PTR_SIZE);
|
||||||
|
|
||||||
/* create PLT entry */
|
|
||||||
if (need_plt_entry) {
|
|
||||||
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
|
|
||||||
Section *plt;
|
|
||||||
uint8_t *p;
|
|
||||||
int modrm;
|
|
||||||
unsigned long relofs;
|
|
||||||
|
|
||||||
#if defined(TCC_OUTPUT_DLL_WITH_PLT)
|
|
||||||
modrm = 0x25;
|
|
||||||
#else
|
|
||||||
/* if we build a DLL, we add a %ebx offset */
|
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL)
|
|
||||||
modrm = 0xa3;
|
|
||||||
else
|
|
||||||
modrm = 0x25;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
plt = s1->plt;
|
|
||||||
/* empty PLT: create PLT0 entry that pushes the library indentifier
|
|
||||||
(GOT + PTR_SIZE) and jumps to ld.so resolution routine
|
|
||||||
(GOT + 2 * PTR_SIZE) */
|
|
||||||
if (plt->data_offset == 0) {
|
|
||||||
p = section_ptr_add(plt, 16);
|
|
||||||
p[0] = 0xff; /* pushl got + PTR_SIZE */
|
|
||||||
p[1] = modrm + 0x10;
|
|
||||||
write32le(p + 2, PTR_SIZE);
|
|
||||||
p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
|
|
||||||
p[7] = modrm;
|
|
||||||
write32le(p + 8, PTR_SIZE * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The PLT slot refers to the relocation entry it needs via offset.
|
|
||||||
The reloc entry is created below, so its offset is the current
|
|
||||||
data_offset */
|
|
||||||
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
|
|
||||||
symattr->plt_offset = plt->data_offset;
|
|
||||||
|
|
||||||
/* Jump to GOT entry where ld.so initially put the address of ip + 4 */
|
|
||||||
p = section_ptr_add(plt, 16);
|
|
||||||
p[0] = 0xff; /* jmp *(got + x) */
|
|
||||||
p[1] = modrm;
|
|
||||||
write32le(p + 2, got_offset);
|
|
||||||
p[6] = 0x68; /* push $xxx */
|
|
||||||
#ifdef TCC_TARGET_X86_64
|
|
||||||
/* On x86-64, the relocation is referred to by _index_ */
|
|
||||||
write32le(p + 7, relofs / sizeof (ElfW_Rel));
|
|
||||||
#else
|
|
||||||
write32le(p + 7, relofs);
|
|
||||||
#endif
|
|
||||||
p[11] = 0xe9; /* jmp plt_start */
|
|
||||||
write32le(p + 12, -(plt->data_offset));
|
|
||||||
|
|
||||||
/* If this was an UNDEF symbol set the offset in the dynsymtab to the
|
|
||||||
PLT slot, so that PC32 relocs to it can be resolved */
|
|
||||||
if (sym->st_shndx == SHN_UNDEF)
|
|
||||||
offset = plt->data_offset - 16;
|
|
||||||
#elif defined(TCC_TARGET_ARM)
|
|
||||||
Section *plt;
|
|
||||||
uint8_t *p;
|
|
||||||
|
|
||||||
/* when building a DLL, GOT entry accesses must be done relative to
|
|
||||||
start of GOT (see x86_64 examble above) */
|
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL)
|
|
||||||
tcc_error("DLLs unimplemented!");
|
|
||||||
|
|
||||||
plt = s1->plt;
|
|
||||||
/* empty PLT: create PLT0 entry that push address of call site and
|
|
||||||
jump to ld.so resolution routine (GOT + 8) */
|
|
||||||
if (plt->data_offset == 0) {
|
|
||||||
p = section_ptr_add(plt, 20);
|
|
||||||
write32le(p, 0xe52de004); /* push {lr} */
|
|
||||||
write32le(p+4, 0xe59fe004); /* ldr lr, [pc, #4] */
|
|
||||||
write32le(p+8, 0xe08fe00e); /* add lr, pc, lr */
|
|
||||||
write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */
|
|
||||||
/* p+16 is set in relocate_plt */
|
|
||||||
}
|
|
||||||
|
|
||||||
symattr->plt_offset = plt->data_offset;
|
|
||||||
if (symattr->plt_thumb_stub) {
|
|
||||||
p = section_ptr_add(plt, 4);
|
|
||||||
write32le(p, 0x4778); /* bx pc */
|
|
||||||
write32le(p+2, 0x46c0); /* nop */
|
|
||||||
}
|
|
||||||
p = section_ptr_add(plt, 16);
|
|
||||||
/* Jump to GOT entry where ld.so initially put address of PLT0 */
|
|
||||||
write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] */
|
|
||||||
write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */
|
|
||||||
write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */
|
|
||||||
/* p + 12 contains offset to GOT entry once patched by relocate_plt */
|
|
||||||
write32le(p+12, got_offset);
|
|
||||||
|
|
||||||
/* the symbol is modified so that it will be relocated to the PLT */
|
|
||||||
if (sym->st_shndx == SHN_UNDEF)
|
|
||||||
offset = plt->data_offset - 16;
|
|
||||||
#elif defined(TCC_TARGET_ARM64)
|
|
||||||
Section *plt;
|
|
||||||
uint8_t *p;
|
|
||||||
|
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL)
|
|
||||||
tcc_error("DLLs unimplemented!");
|
|
||||||
|
|
||||||
plt = s1->plt;
|
|
||||||
if (plt->data_offset == 0)
|
|
||||||
section_ptr_add(plt, 32);
|
|
||||||
symattr->plt_offset = plt->data_offset;
|
|
||||||
p = section_ptr_add(plt, 16);
|
|
||||||
write32le(p, got_offset);
|
|
||||||
write32le(p + 4, (uint64_t) got_offset >> 32);
|
|
||||||
|
|
||||||
if (sym->st_shndx == SHN_UNDEF)
|
|
||||||
offset = plt->data_offset - 16;
|
|
||||||
#elif defined(TCC_TARGET_C67)
|
|
||||||
tcc_error("C67 got not implemented");
|
|
||||||
#else
|
|
||||||
#error unsupported CPU
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the GOT relocation that will insert the address of the object or
|
/* Create the GOT relocation that will insert the address of the object or
|
||||||
function of interest in the GOT entry. This is a static relocation for
|
function of interest in the GOT entry. This is a static relocation for
|
||||||
@ -999,31 +856,44 @@ static unsigned long put_got_entry(TCCState *s1, int dyn_reloc_type,
|
|||||||
done lazily for GOT entry with *_JUMP_SLOT relocation type (the one
|
done lazily for GOT entry with *_JUMP_SLOT relocation type (the one
|
||||||
associated to a PLT entry) but is currently done at load time for an
|
associated to a PLT entry) but is currently done at load time for an
|
||||||
unknown reason. */
|
unknown reason. */
|
||||||
|
|
||||||
|
sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
|
||||||
|
name = (char *) symtab_section->link->data + sym->st_name;
|
||||||
|
|
||||||
if (s1->dynsym) {
|
if (s1->dynsym) {
|
||||||
/* create the dynamic symbol table entry that the relocation refers to
|
if (0 == attr->dyn_index)
|
||||||
in its r_info field to identify the symbol */
|
attr->dyn_index = set_elf_sym(s1->dynsym, sym->st_value, size,
|
||||||
/* XXX This might generate multiple syms for name. */
|
info, 0, sym->st_shndx, name);
|
||||||
index = find_elf_sym (s1->dynsym, name);
|
put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type,
|
||||||
if (index) {
|
attr->dyn_index);
|
||||||
esym = (ElfW(Sym) *) s1->dynsym->data + index;
|
} else {
|
||||||
esym->st_value = offset;
|
put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type,
|
||||||
|
|
||||||
} else if (s1->output_type == TCC_OUTPUT_MEMORY ||
|
|
||||||
ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
|
|
||||||
gotplt_entry_type(reloc_type) == ALWAYS_GOTPLT_ENTRY)
|
|
||||||
index = put_elf_sym(s1->dynsym, offset, size, info, 0,
|
|
||||||
sym->st_shndx, name);
|
|
||||||
else
|
|
||||||
tcc_error("Runtime relocation without dynamic symbol: %s", name);
|
|
||||||
put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type, index);
|
|
||||||
} else
|
|
||||||
put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type,
|
|
||||||
sym_index);
|
sym_index);
|
||||||
|
}
|
||||||
|
|
||||||
if (need_plt_entry)
|
if (need_plt_entry) {
|
||||||
return symattr->plt_offset;
|
if (!s1->plt) {
|
||||||
else
|
s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
|
||||||
return symattr->got_offset;
|
SHF_ALLOC | SHF_EXECINSTR);
|
||||||
|
s1->plt->sh_entsize = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
attr->plt_offset = create_plt_entry(s1, got_offset, attr);
|
||||||
|
|
||||||
|
/* create a symbol 'sym@plt' for the PLT jump vector */
|
||||||
|
len = strlen(name);
|
||||||
|
if (len > sizeof plt_name - 5)
|
||||||
|
len = sizeof plt_name - 5;
|
||||||
|
memcpy(plt_name, name, len);
|
||||||
|
strcpy(plt_name + len, "@plt");
|
||||||
|
attr->plt_sym = put_elf_sym(s1->symtab, attr->plt_offset, sym->st_size,
|
||||||
|
ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, s1->plt->sh_num, plt_name);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
attr->got_offset = got_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build GOT and PLT entries */
|
/* build GOT and PLT entries */
|
||||||
@ -1033,6 +903,7 @@ ST_FUNC void build_got_entries(TCCState *s1)
|
|||||||
ElfW_Rel *rel;
|
ElfW_Rel *rel;
|
||||||
ElfW(Sym) *sym;
|
ElfW(Sym) *sym;
|
||||||
int i, type, gotplt_entry, reloc_type, sym_index;
|
int i, type, gotplt_entry, reloc_type, sym_index;
|
||||||
|
struct sym_attr *attr;
|
||||||
|
|
||||||
for(i = 1; i < s1->nb_sections; i++) {
|
for(i = 1; i < s1->nb_sections; i++) {
|
||||||
s = s1->sections[i];
|
s = s1->sections[i];
|
||||||
@ -1047,29 +918,36 @@ ST_FUNC void build_got_entries(TCCState *s1)
|
|||||||
sym_index = ELFW(R_SYM)(rel->r_info);
|
sym_index = ELFW(R_SYM)(rel->r_info);
|
||||||
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
|
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
|
||||||
|
|
||||||
if (gotplt_entry == NO_GOTPLT_ENTRY)
|
if (gotplt_entry == NO_GOTPLT_ENTRY) {
|
||||||
|
#ifdef TCC_TARGET_I386
|
||||||
|
if (type == R_386_32 && sym->st_shndx == SHN_UNDEF) {
|
||||||
|
/* the i386 generator uses the plt address for function
|
||||||
|
pointers into .so. This may break pointer equality
|
||||||
|
but helps to keep it simple */
|
||||||
|
char *name = (char *)symtab_section->link->data + sym->st_name;
|
||||||
|
int index = find_elf_sym(s1->dynsymtab_section, name);
|
||||||
|
ElfW(Sym) *esym = (ElfW(Sym) *)s1->dynsymtab_section->data + index;
|
||||||
|
if (index
|
||||||
|
&& (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC
|
||||||
|
|| (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE
|
||||||
|
&& ELFW(ST_TYPE)(sym->st_info) == STT_FUNC)))
|
||||||
|
goto jmp_slot;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Proceed with PLT/GOT [entry] creation if any of the following
|
/* Automatically create PLT/GOT [entry] it is an undefined reference
|
||||||
condition is met:
|
(resolved at runtime), or the symbol is absolute, probably created
|
||||||
- it is an undefined reference (dynamic relocation needed)
|
by tcc_add_symbol, and thus on 64-bit targets might be too far
|
||||||
- symbol is absolute (probably created by tcc_add_symbol and
|
from application code */
|
||||||
thus might be too far from application code)
|
if (gotplt_entry == AUTO_GOTPLT_ENTRY) {
|
||||||
- relocation requires a PLT/GOT (BUILD_GOTPLT_ENTRY or
|
if (sym->st_shndx == SHN_UNDEF) {
|
||||||
ALWAYS_GOTPLT_ENTRY). */
|
if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT)
|
||||||
if (sym->st_shndx != SHN_UNDEF &&
|
continue;
|
||||||
sym->st_shndx != SHN_ABS &&
|
} else if (!(sym->st_shndx == SHN_ABS && PTR_SIZE == 8))
|
||||||
gotplt_entry == AUTO_GOTPLT_ENTRY)
|
continue;
|
||||||
continue;
|
}
|
||||||
|
|
||||||
/* Building a dynamic library but target is not capable of PC
|
|
||||||
relative PLT entries. It can thus only use PLT entries if
|
|
||||||
it expects one to be used (ALWAYS_GOTPLT_ENTRY). */
|
|
||||||
if (sym->st_shndx == SHN_UNDEF &&
|
|
||||||
s1->output_type == TCC_OUTPUT_DLL &&
|
|
||||||
!PCRELATIVE_DLLPLT &&
|
|
||||||
gotplt_entry == AUTO_GOTPLT_ENTRY)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
if (type == R_X86_64_PLT32 &&
|
if (type == R_X86_64_PLT32 &&
|
||||||
@ -1078,6 +956,13 @@ ST_FUNC void build_got_entries(TCCState *s1)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (code_reloc(type)) {
|
||||||
|
#ifdef TCC_TARGET_I386
|
||||||
|
jmp_slot:
|
||||||
|
#endif
|
||||||
|
reloc_type = R_JMP_SLOT;
|
||||||
|
} else
|
||||||
|
reloc_type = R_GLOB_DAT;
|
||||||
|
|
||||||
if (!s1->got)
|
if (!s1->got)
|
||||||
build_got(s1);
|
build_got(s1);
|
||||||
@ -1085,45 +970,15 @@ ST_FUNC void build_got_entries(TCCState *s1)
|
|||||||
if (gotplt_entry == BUILD_GOT_ONLY)
|
if (gotplt_entry == BUILD_GOT_ONLY)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (code_reloc(type))
|
attr = put_got_entry(s1, reloc_type, type, sym->st_size, sym->st_info,
|
||||||
reloc_type = R_JMP_SLOT;
|
sym_index);
|
||||||
else
|
|
||||||
reloc_type = R_GLOB_DAT;
|
if (reloc_type == R_JMP_SLOT)
|
||||||
put_got_entry(s1, reloc_type, type, sym->st_size, sym->st_info,
|
rel->r_info = ELFW(R_INFO)(attr->plt_sym, type);
|
||||||
sym_index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ST_FUNC Section *new_symtab(TCCState *s1,
|
|
||||||
const char *symtab_name, int sh_type, int sh_flags,
|
|
||||||
const char *strtab_name,
|
|
||||||
const char *hash_name, int hash_sh_flags)
|
|
||||||
{
|
|
||||||
Section *symtab, *strtab, *hash;
|
|
||||||
int *ptr, nb_buckets;
|
|
||||||
|
|
||||||
symtab = new_section(s1, symtab_name, sh_type, sh_flags);
|
|
||||||
symtab->sh_entsize = sizeof(ElfW(Sym));
|
|
||||||
strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
|
|
||||||
put_elf_str(strtab, "");
|
|
||||||
symtab->link = strtab;
|
|
||||||
put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
|
|
||||||
|
|
||||||
nb_buckets = 1;
|
|
||||||
|
|
||||||
hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
|
|
||||||
hash->sh_entsize = sizeof(int);
|
|
||||||
symtab->hash = hash;
|
|
||||||
hash->link = symtab;
|
|
||||||
|
|
||||||
ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
|
|
||||||
ptr[0] = nb_buckets;
|
|
||||||
ptr[1] = 1;
|
|
||||||
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
|
|
||||||
return symtab;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* put dynamic tag */
|
/* put dynamic tag */
|
||||||
static void put_dt(Section *dynamic, int dt, addr_t val)
|
static void put_dt(Section *dynamic, int dt, addr_t val)
|
||||||
{
|
{
|
||||||
@ -1308,52 +1163,26 @@ static void tcc_output_binary(TCCState *s1, FILE *f,
|
|||||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||||
#define HAVE_PHDR 1
|
#define HAVE_PHDR 1
|
||||||
#define EXTRA_RELITEMS 14
|
#define EXTRA_RELITEMS 14
|
||||||
|
|
||||||
/* move the relocation value from .dynsym to .got */
|
|
||||||
static void patch_dynsym_undef(TCCState *s1, Section *s)
|
|
||||||
{
|
|
||||||
uint32_t *gotd = (void *)s1->got->data;
|
|
||||||
ElfW(Sym) *sym;
|
|
||||||
|
|
||||||
gotd += 3; /* dummy entries in .got */
|
|
||||||
/* relocate symbols in .dynsym */
|
|
||||||
for_each_elem(s, 1, sym, ElfW(Sym)) {
|
|
||||||
if (sym->st_shndx == SHN_UNDEF) {
|
|
||||||
*gotd++ = sym->st_value + 6; /* XXX 6 is magic ? */
|
|
||||||
sym->st_value = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
#define HAVE_PHDR 1
|
#define HAVE_PHDR 1
|
||||||
#define EXTRA_RELITEMS 9
|
#define EXTRA_RELITEMS 9
|
||||||
|
|
||||||
/* zero plt offsets of weak symbols in .dynsym */
|
|
||||||
static void patch_dynsym_undef(TCCState *s1, Section *s)
|
|
||||||
{
|
|
||||||
ElfW(Sym) *sym;
|
|
||||||
|
|
||||||
for_each_elem(s, 1, sym, ElfW(Sym))
|
|
||||||
if (sym->st_shndx == SHN_UNDEF && ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
|
|
||||||
sym->st_value = 0;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
|
ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
|
||||||
{
|
{
|
||||||
int sym_index = ELFW(R_SYM) (rel->r_info);
|
int sym_index = ELFW(R_SYM) (rel->r_info);
|
||||||
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
|
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
|
||||||
unsigned long offset;
|
struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
|
||||||
|
unsigned offset = attr->got_offset;
|
||||||
|
|
||||||
if (sym_index >= s1->nb_sym_attrs)
|
if (0 == offset)
|
||||||
return;
|
return;
|
||||||
offset = s1->sym_attrs[sym_index].got_offset;
|
|
||||||
section_reserve(s1->got, offset + PTR_SIZE);
|
section_reserve(s1->got, offset + PTR_SIZE);
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
/* only works for x86-64 */
|
write64le(s1->got->data + offset, sym->st_value);
|
||||||
write32le(s1->got->data + offset + 4, sym->st_value >> 32);
|
#else
|
||||||
|
write32le(s1->got->data + offset, sym->st_value);
|
||||||
#endif
|
#endif
|
||||||
write32le(s1->got->data + offset, sym->st_value & 0xffffffff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform relocation to GOT or PLT entries */
|
/* Perform relocation to GOT or PLT entries */
|
||||||
@ -1426,6 +1255,7 @@ static void bind_exe_dynsyms(TCCState *s1)
|
|||||||
index = put_elf_sym(s1->dynsym, offset, esym->st_size,
|
index = put_elf_sym(s1->dynsym, offset, esym->st_size,
|
||||||
esym->st_info, 0, bss_section->sh_num,
|
esym->st_info, 0, bss_section->sh_num,
|
||||||
name);
|
name);
|
||||||
|
|
||||||
/* Ensure R_COPY works for weak symbol aliases */
|
/* Ensure R_COPY works for weak symbol aliases */
|
||||||
if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
|
if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
|
||||||
for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
|
for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
|
||||||
@ -1440,6 +1270,7 @@ static void bind_exe_dynsyms(TCCState *s1)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
put_elf_reloc(s1->dynsym, bss_section,
|
put_elf_reloc(s1->dynsym, bss_section,
|
||||||
offset, R_COPY, index);
|
offset, R_COPY, index);
|
||||||
offset += esym->st_size;
|
offset += esym->st_size;
|
||||||
@ -1497,106 +1328,23 @@ static void bind_libs_dynsyms(TCCState *s1)
|
|||||||
symbols to be resolved by other shared libraries or by the executable. */
|
symbols to be resolved by other shared libraries or by the executable. */
|
||||||
static void export_global_syms(TCCState *s1)
|
static void export_global_syms(TCCState *s1)
|
||||||
{
|
{
|
||||||
int nb_syms, dynindex, index;
|
int dynindex, index;
|
||||||
const char *name;
|
const char *name;
|
||||||
ElfW(Sym) *sym;
|
ElfW(Sym) *sym;
|
||||||
|
|
||||||
nb_syms = symtab_section->data_offset / sizeof(ElfW(Sym));
|
|
||||||
s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms);
|
|
||||||
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
|
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
|
||||||
if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
|
if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
|
||||||
name = (char *) symtab_section->link->data + sym->st_name;
|
name = (char *) symtab_section->link->data + sym->st_name;
|
||||||
dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
|
dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
|
||||||
sym->st_info, 0, sym->st_shndx, name);
|
sym->st_info, 0, sym->st_shndx, name);
|
||||||
index = sym - (ElfW(Sym) *) symtab_section->data;
|
index = sym - (ElfW(Sym) *) symtab_section->data;
|
||||||
s1->symtab_to_dynsym[index] = dynindex;
|
get_sym_attr(s1, index, 1)->dyn_index = dynindex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* relocate the PLT: compute addresses and offsets in the PLT now that final
|
|
||||||
address for PLT and GOT are known (see fill_program_header) */
|
|
||||||
ST_FUNC void relocate_plt(TCCState *s1)
|
|
||||||
{
|
|
||||||
uint8_t *p, *p_end;
|
|
||||||
|
|
||||||
if (!s1->plt)
|
|
||||||
return;
|
|
||||||
|
|
||||||
p = s1->plt->data;
|
|
||||||
p_end = p + s1->plt->data_offset;
|
|
||||||
if (p < p_end) {
|
|
||||||
#if defined(TCC_TARGET_I386)
|
|
||||||
add32le(p + 2, s1->got->sh_addr);
|
|
||||||
add32le(p + 8, s1->got->sh_addr);
|
|
||||||
p += 16;
|
|
||||||
while (p < p_end) {
|
|
||||||
add32le(p + 2, s1->got->sh_addr);
|
|
||||||
p += 16;
|
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_X86_64)
|
|
||||||
int x = s1->got->sh_addr - s1->plt->sh_addr - 6;
|
|
||||||
add32le(p + 2, x);
|
|
||||||
add32le(p + 8, x - 6);
|
|
||||||
p += 16;
|
|
||||||
while (p < p_end) {
|
|
||||||
add32le(p + 2, x + s1->plt->data - p);
|
|
||||||
p += 16;
|
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_ARM)
|
|
||||||
int x = s1->got->sh_addr - s1->plt->sh_addr - 12;
|
|
||||||
write32le(s1->plt->data + 16, x - 16);
|
|
||||||
p += 20;
|
|
||||||
while (p < p_end) {
|
|
||||||
if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */
|
|
||||||
p += 4;
|
|
||||||
add32le(p + 12, x + s1->plt->data - p);
|
|
||||||
p += 16;
|
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_ARM64)
|
|
||||||
uint64_t plt = s1->plt->sh_addr;
|
|
||||||
uint64_t got = s1->got->sh_addr;
|
|
||||||
uint64_t off = (got >> 12) - (plt >> 12);
|
|
||||||
if ((off + ((uint32_t)1 << 20)) >> 21)
|
|
||||||
tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", off, got, plt);
|
|
||||||
write32le(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]!
|
|
||||||
write32le(p + 4, (0x90000010 | // adrp x16,...
|
|
||||||
(off & 0x1ffffc) << 3 | (off & 3) << 29));
|
|
||||||
write32le(p + 8, (0xf9400211 | // ldr x17,[x16,#...]
|
|
||||||
(got & 0xff8) << 7));
|
|
||||||
write32le(p + 12, (0x91000210 | // add x16,x16,#...
|
|
||||||
(got & 0xfff) << 10));
|
|
||||||
write32le(p + 16, 0xd61f0220); // br x17
|
|
||||||
write32le(p + 20, 0xd503201f); // nop
|
|
||||||
write32le(p + 24, 0xd503201f); // nop
|
|
||||||
write32le(p + 28, 0xd503201f); // nop
|
|
||||||
p += 32;
|
|
||||||
while (p < p_end) {
|
|
||||||
uint64_t pc = plt + (p - s1->plt->data);
|
|
||||||
uint64_t addr = got + read64le(p);
|
|
||||||
uint64_t off = (addr >> 12) - (pc >> 12);
|
|
||||||
if ((off + ((uint32_t)1 << 20)) >> 21)
|
|
||||||
tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc);
|
|
||||||
write32le(p, (0x90000010 | // adrp x16,...
|
|
||||||
(off & 0x1ffffc) << 3 | (off & 3) << 29));
|
|
||||||
write32le(p + 4, (0xf9400211 | // ldr x17,[x16,#...]
|
|
||||||
(addr & 0xff8) << 7));
|
|
||||||
write32le(p + 8, (0x91000210 | // add x16,x16,#...
|
|
||||||
(addr & 0xfff) << 10));
|
|
||||||
write32le(p + 12, 0xd61f0220); // br x17
|
|
||||||
p += 16;
|
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_C67)
|
|
||||||
/* XXX: TODO */
|
|
||||||
#else
|
|
||||||
#error unsupported CPU
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate strings for section names and decide if an unallocated section
|
/* Allocate strings for section names and decide if an unallocated section
|
||||||
should be output.
|
should be output.
|
||||||
|
|
||||||
NOTE: the strsec section comes last, so its size is also correct ! */
|
NOTE: the strsec section comes last, so its size is also correct ! */
|
||||||
static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
|
static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
|
||||||
{
|
{
|
||||||
@ -2024,8 +1772,6 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
|
|||||||
for(i = 1; i < s1->nb_sections; i++) {
|
for(i = 1; i < s1->nb_sections; i++) {
|
||||||
s = s1->sections[sec_order[i]];
|
s = s1->sections[sec_order[i]];
|
||||||
if (s->sh_type != SHT_NOBITS) {
|
if (s->sh_type != SHT_NOBITS) {
|
||||||
if (s->sh_type == SHT_DYNSYM)
|
|
||||||
patch_dynsym_undef(s1, s);
|
|
||||||
while (offset < s->sh_offset) {
|
while (offset < s->sh_offset) {
|
||||||
fputc(0, f);
|
fputc(0, f);
|
||||||
offset++;
|
offset++;
|
||||||
@ -2251,22 +1997,12 @@ static int elf_output_file(TCCState *s1, const char *filename)
|
|||||||
/* put in GOT the dynamic section address and relocate PLT */
|
/* put in GOT the dynamic section address and relocate PLT */
|
||||||
write32le(s1->got->data, dynamic->sh_addr);
|
write32le(s1->got->data, dynamic->sh_addr);
|
||||||
if (file_type == TCC_OUTPUT_EXE
|
if (file_type == TCC_OUTPUT_EXE
|
||||||
#if defined(TCC_OUTPUT_DLL_WITH_PLT)
|
|| (RELOCATE_DLLPLT && file_type == TCC_OUTPUT_DLL))
|
||||||
|| file_type == TCC_OUTPUT_DLL
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
relocate_plt(s1);
|
relocate_plt(s1);
|
||||||
|
|
||||||
/* relocate symbols in .dynsym now that final addresses are known */
|
/* relocate symbols in .dynsym now that final addresses are known */
|
||||||
for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
|
for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
|
||||||
if (sym->st_shndx == SHN_UNDEF) {
|
if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) {
|
||||||
/* relocate to PLT if symbol corresponds to a PLT entry,
|
|
||||||
but not if it's a weak symbol */
|
|
||||||
if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
|
|
||||||
sym->st_value = 0;
|
|
||||||
else if (sym->st_value)
|
|
||||||
sym->st_value += s1->plt->sh_addr;
|
|
||||||
} else if (sym->st_shndx < SHN_LORESERVE) {
|
|
||||||
/* do symbol relocation */
|
/* do symbol relocation */
|
||||||
sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
|
sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
|
||||||
}
|
}
|
||||||
@ -2289,11 +2025,8 @@ static int elf_output_file(TCCState *s1, const char *filename)
|
|||||||
/* Create the ELF file with name 'filename' */
|
/* Create the ELF file with name 'filename' */
|
||||||
ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
|
ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
|
||||||
the_end:
|
the_end:
|
||||||
tcc_free(s1->symtab_to_dynsym);
|
|
||||||
tcc_free(sec_order);
|
tcc_free(sec_order);
|
||||||
tcc_free(phdr);
|
tcc_free(phdr);
|
||||||
tcc_free(s1->sym_attrs);
|
|
||||||
s1->sym_attrs = NULL;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,9 +15,12 @@ int add(int a, int b)
|
|||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char hello[] = "Hello World!";
|
||||||
|
|
||||||
char my_program[] =
|
char my_program[] =
|
||||||
"#include <tcclib.h>\n" /* include the "Simple libc header for TCC" */
|
"#include <tcclib.h>\n" /* include the "Simple libc header for TCC" */
|
||||||
"extern int add(int a, int b);\n"
|
"extern int add(int a, int b);\n"
|
||||||
|
"extern const char hello[];\n"
|
||||||
"int fib(int n)\n"
|
"int fib(int n)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" if (n <= 2)\n"
|
" if (n <= 2)\n"
|
||||||
@ -28,7 +31,7 @@ char my_program[] =
|
|||||||
"\n"
|
"\n"
|
||||||
"int foo(int n)\n"
|
"int foo(int n)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" printf(\"Hello World!\\n\");\n"
|
" printf(\"%s\\n\", hello);\n"
|
||||||
" printf(\"fib(%d) = %d\\n\", n, fib(n));\n"
|
" printf(\"fib(%d) = %d\\n\", n, fib(n));\n"
|
||||||
" printf(\"add(%d, %d) = %d\\n\", n, 2 * n, add(n, 2 * n));\n"
|
" printf(\"add(%d, %d) = %d\\n\", n, 2 * n, add(n, 2 * n));\n"
|
||||||
" return 0;\n"
|
" return 0;\n"
|
||||||
@ -65,9 +68,10 @@ int main(int argc, char **argv)
|
|||||||
if (tcc_compile_string(s, my_program) == -1)
|
if (tcc_compile_string(s, my_program) == -1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* as a test, we add a symbol that the compiled program can use.
|
/* as a test, we add symbols that the compiled program can use.
|
||||||
You may also open a dll with tcc_add_dll() and use symbols from that */
|
You may also open a dll with tcc_add_dll() and use symbols from that */
|
||||||
tcc_add_symbol(s, "add", add);
|
tcc_add_symbol(s, "add", add);
|
||||||
|
tcc_add_symbol(s, "hello", hello);
|
||||||
|
|
||||||
/* relocate the code */
|
/* relocate the code */
|
||||||
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0)
|
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0)
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
#define ELF_START_ADDR 0x400000
|
#define ELF_START_ADDR 0x400000
|
||||||
#define ELF_PAGE_SIZE 0x200000
|
#define ELF_PAGE_SIZE 0x200000
|
||||||
|
|
||||||
#define HAVE_SECTION_RELOC
|
|
||||||
#define PCRELATIVE_DLLPLT 1
|
#define PCRELATIVE_DLLPLT 1
|
||||||
|
#define RELOCATE_DLLPLT 1
|
||||||
|
|
||||||
#else /* !TARGET_DEFS_ONLY */
|
#else /* !TARGET_DEFS_ONLY */
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ int code_reloc (int reloc_type)
|
|||||||
case R_X86_64_GOT32:
|
case R_X86_64_GOT32:
|
||||||
case R_X86_64_GLOB_DAT:
|
case R_X86_64_GLOB_DAT:
|
||||||
case R_X86_64_COPY:
|
case R_X86_64_COPY:
|
||||||
|
case R_X86_64_RELATIVE:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case R_X86_64_PC32:
|
case R_X86_64_PC32:
|
||||||
@ -57,6 +58,7 @@ int gotplt_entry_type (int reloc_type)
|
|||||||
case R_X86_64_GLOB_DAT:
|
case R_X86_64_GLOB_DAT:
|
||||||
case R_X86_64_JUMP_SLOT:
|
case R_X86_64_JUMP_SLOT:
|
||||||
case R_X86_64_COPY:
|
case R_X86_64_COPY:
|
||||||
|
case R_X86_64_RELATIVE:
|
||||||
return NO_GOTPLT_ENTRY;
|
return NO_GOTPLT_ENTRY;
|
||||||
|
|
||||||
case R_X86_64_32:
|
case R_X86_64_32:
|
||||||
@ -80,6 +82,71 @@ int gotplt_entry_type (int reloc_type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
|
||||||
|
{
|
||||||
|
Section *plt = s1->plt;
|
||||||
|
uint8_t *p;
|
||||||
|
int modrm;
|
||||||
|
unsigned plt_offset, relofs;
|
||||||
|
|
||||||
|
modrm = 0x25;
|
||||||
|
|
||||||
|
/* empty PLT: create PLT0 entry that pushes the library indentifier
|
||||||
|
(GOT + PTR_SIZE) and jumps to ld.so resolution routine
|
||||||
|
(GOT + 2 * PTR_SIZE) */
|
||||||
|
if (plt->data_offset == 0) {
|
||||||
|
p = section_ptr_add(plt, 16);
|
||||||
|
p[0] = 0xff; /* pushl got + PTR_SIZE */
|
||||||
|
p[1] = modrm + 0x10;
|
||||||
|
write32le(p + 2, PTR_SIZE);
|
||||||
|
p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
|
||||||
|
p[7] = modrm;
|
||||||
|
write32le(p + 8, PTR_SIZE * 2);
|
||||||
|
}
|
||||||
|
plt_offset = plt->data_offset;
|
||||||
|
|
||||||
|
/* The PLT slot refers to the relocation entry it needs via offset.
|
||||||
|
The reloc entry is created below, so its offset is the current
|
||||||
|
data_offset */
|
||||||
|
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
|
||||||
|
|
||||||
|
/* Jump to GOT entry where ld.so initially put the address of ip + 4 */
|
||||||
|
p = section_ptr_add(plt, 16);
|
||||||
|
p[0] = 0xff; /* jmp *(got + x) */
|
||||||
|
p[1] = modrm;
|
||||||
|
write32le(p + 2, got_offset);
|
||||||
|
p[6] = 0x68; /* push $xxx */
|
||||||
|
/* On x86-64, the relocation is referred to by _index_ */
|
||||||
|
write32le(p + 7, relofs / sizeof (ElfW_Rel));
|
||||||
|
p[11] = 0xe9; /* jmp plt_start */
|
||||||
|
write32le(p + 12, -(plt->data_offset));
|
||||||
|
return plt_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relocate the PLT: compute addresses and offsets in the PLT now that final
|
||||||
|
address for PLT and GOT are known (see fill_program_header) */
|
||||||
|
ST_FUNC void relocate_plt(TCCState *s1)
|
||||||
|
{
|
||||||
|
uint8_t *p, *p_end;
|
||||||
|
|
||||||
|
if (!s1->plt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = s1->plt->data;
|
||||||
|
p_end = p + s1->plt->data_offset;
|
||||||
|
|
||||||
|
if (p < p_end) {
|
||||||
|
int x = s1->got->sh_addr - s1->plt->sh_addr - 6;
|
||||||
|
add32le(p + 2, x);
|
||||||
|
add32le(p + 8, x - 6);
|
||||||
|
p += 16;
|
||||||
|
while (p < p_end) {
|
||||||
|
add32le(p + 2, x + s1->plt->data - p);
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
|
static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
|
||||||
|
|
||||||
void relocate_init(Section *sr)
|
void relocate_init(Section *sr)
|
||||||
@ -96,7 +163,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case R_X86_64_64:
|
case R_X86_64_64:
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
if (s1->output_type == TCC_OUTPUT_DLL) {
|
||||||
esym_index = s1->symtab_to_dynsym[sym_index];
|
esym_index = s1->sym_attrs[sym_index].dyn_index;
|
||||||
qrel->r_offset = rel->r_offset;
|
qrel->r_offset = rel->r_offset;
|
||||||
if (esym_index) {
|
if (esym_index) {
|
||||||
qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64);
|
qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64);
|
||||||
@ -127,7 +194,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add
|
|||||||
case R_X86_64_PC32:
|
case R_X86_64_PC32:
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
if (s1->output_type == TCC_OUTPUT_DLL) {
|
||||||
/* DLL relocation */
|
/* DLL relocation */
|
||||||
esym_index = s1->symtab_to_dynsym[sym_index];
|
esym_index = s1->sym_attrs[sym_index].dyn_index;
|
||||||
if (esym_index) {
|
if (esym_index) {
|
||||||
qrel->r_offset = rel->r_offset;
|
qrel->r_offset = rel->r_offset;
|
||||||
qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
|
qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
|
||||||
@ -170,6 +237,9 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add
|
|||||||
/* we load the got offset */
|
/* we load the got offset */
|
||||||
add32le(ptr, s1->sym_attrs[sym_index].got_offset);
|
add32le(ptr, s1->sym_attrs[sym_index].got_offset);
|
||||||
break;
|
break;
|
||||||
|
case R_X86_64_RELATIVE:
|
||||||
|
/* do nothing */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user