mirror of
https://github.com/mirror/tinycc.git
synced 2025-03-28 12:10:05 +08:00
Improve put_got_entry doc and structure
This commit is contained in:
parent
64b5ee2dea
commit
f924d0ca96
3
tcc.h
3
tcc.h
@ -1325,6 +1325,9 @@ 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);
|
||||||
|
144
tccelf.c
144
tccelf.c
@ -1295,44 +1295,35 @@ static void build_got(TCCState *s1)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* put a got or plt entry corresponding to a symbol in symtab_section. 'size'
|
/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
|
||||||
and 'info' can be modifed if more precise info comes from the DLL.
|
in s1->symtab. When creating the dynamic symbol table entry for the GOT
|
||||||
Returns offset of GOT or PLT slot. */
|
relocation, use 'size' and 'info' for the corresponding symbol metadata.
|
||||||
|
Returns the offset of the GOT or (if any) PLT entry. */
|
||||||
static unsigned long put_got_entry(TCCState *s1,
|
static unsigned long put_got_entry(TCCState *s1,
|
||||||
int reloc_type, unsigned long size, int info,
|
int reloc_type, unsigned long size, int info,
|
||||||
int sym_index)
|
int sym_index)
|
||||||
{
|
{
|
||||||
int index, need_plt_entry;
|
int index, need_plt_entry = 0;
|
||||||
const char *name;
|
const char *name;
|
||||||
ElfW(Sym) *sym;
|
ElfW(Sym) *sym;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
int *ptr;
|
int *ptr;
|
||||||
|
size_t got_offset;
|
||||||
struct sym_attr *symattr;
|
struct sym_attr *symattr;
|
||||||
|
|
||||||
|
need_plt_entry = (reloc_type == R_JMP_SLOT);
|
||||||
|
|
||||||
if (!s1->got)
|
if (!s1->got)
|
||||||
build_got(s1);
|
build_got(s1);
|
||||||
|
|
||||||
need_plt_entry =
|
/* create PLT if needed */
|
||||||
#ifdef TCC_TARGET_X86_64
|
|
||||||
(reloc_type == R_X86_64_JUMP_SLOT);
|
|
||||||
#elif defined(TCC_TARGET_I386)
|
|
||||||
(reloc_type == R_386_JMP_SLOT);
|
|
||||||
#elif defined(TCC_TARGET_ARM)
|
|
||||||
(reloc_type == R_ARM_JUMP_SLOT);
|
|
||||||
#elif defined(TCC_TARGET_ARM64)
|
|
||||||
(reloc_type == R_AARCH64_JUMP_SLOT);
|
|
||||||
#else
|
|
||||||
0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (need_plt_entry && !s1->plt) {
|
if (need_plt_entry && !s1->plt) {
|
||||||
/* add PLT */
|
|
||||||
s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
|
s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
|
||||||
SHF_ALLOC | SHF_EXECINSTR);
|
SHF_ALLOC | SHF_EXECINSTR);
|
||||||
s1->plt->sh_entsize = 4;
|
s1->plt->sh_entsize = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a got/plt entry already exists for that symbol, no need to add one */
|
/* already a GOT and/or PLT entry, no need to add one */
|
||||||
if (sym_index < s1->nb_sym_attrs) {
|
if (sym_index < s1->nb_sym_attrs) {
|
||||||
if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset)
|
if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset)
|
||||||
return s1->sym_attrs[sym_index].plt_offset;
|
return s1->sym_attrs[sym_index].plt_offset;
|
||||||
@ -1342,15 +1333,27 @@ static unsigned long put_got_entry(TCCState *s1,
|
|||||||
|
|
||||||
symattr = alloc_sym_attr(s1, sym_index);
|
symattr = alloc_sym_attr(s1, sym_index);
|
||||||
|
|
||||||
/* Only store the GOT offset if it's not generated for the PLT entry. */
|
/* 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
|
||||||
|
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
|
||||||
|
got_offset field since it might overwrite the offset of a GOT entry.
|
||||||
|
Besides, for PLT entry the static relocation is against the PLT entry
|
||||||
|
and the dynamic relocation for PLTGOT is created in this function. */
|
||||||
if (!need_plt_entry)
|
if (!need_plt_entry)
|
||||||
symattr->got_offset = s1->got->data_offset;
|
symattr->got_offset = got_offset;
|
||||||
|
|
||||||
sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
|
sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
|
||||||
name = (char *) symtab_section->link->data + sym->st_name;
|
name = (char *) symtab_section->link->data + sym->st_name;
|
||||||
offset = sym->st_value;
|
offset = sym->st_value;
|
||||||
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
|
|
||||||
|
/* create PLT entry */
|
||||||
if (need_plt_entry) {
|
if (need_plt_entry) {
|
||||||
|
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
|
||||||
Section *plt;
|
Section *plt;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
int modrm;
|
int modrm;
|
||||||
@ -1366,10 +1369,11 @@ static unsigned long put_got_entry(TCCState *s1,
|
|||||||
modrm = 0x25;
|
modrm = 0x25;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* add a PLT entry */
|
|
||||||
plt = s1->plt;
|
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) {
|
if (plt->data_offset == 0) {
|
||||||
/* first plt entry */
|
|
||||||
p = section_ptr_add(plt, 16);
|
p = section_ptr_add(plt, 16);
|
||||||
p[0] = 0xff; /* pushl got + PTR_SIZE */
|
p[0] = 0xff; /* pushl got + PTR_SIZE */
|
||||||
p[1] = modrm + 0x10;
|
p[1] = modrm + 0x10;
|
||||||
@ -1379,18 +1383,20 @@ static unsigned long put_got_entry(TCCState *s1,
|
|||||||
write32le(p + 8, PTR_SIZE * 2);
|
write32le(p + 8, PTR_SIZE * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The PLT slot refers to the relocation entry it needs
|
/* The PLT slot refers to the relocation entry it needs via offset.
|
||||||
via offset. The reloc entry is created below, so its
|
The reloc entry is created below, so its offset is the current
|
||||||
offset is the current data_offset. */
|
data_offset */
|
||||||
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
|
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
|
||||||
symattr->plt_offset = plt->data_offset;
|
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 = section_ptr_add(plt, 16);
|
||||||
p[0] = 0xff; /* jmp *(got + x) */
|
p[0] = 0xff; /* jmp *(got + x) */
|
||||||
p[1] = modrm;
|
p[1] = modrm;
|
||||||
write32le(p + 2, s1->got->data_offset);
|
write32le(p + 2, got_offset);
|
||||||
p[6] = 0x68; /* push $xxx */
|
p[6] = 0x68; /* push $xxx */
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
/* On x86-64, the relocation is referred to by _index_. */
|
/* On x86-64, the relocation is referred to by _index_ */
|
||||||
write32le(p + 7, relofs / sizeof (ElfW_Rel));
|
write32le(p + 7, relofs / sizeof (ElfW_Rel));
|
||||||
#else
|
#else
|
||||||
write32le(p + 7, relofs);
|
write32le(p + 7, relofs);
|
||||||
@ -1398,25 +1404,23 @@ static unsigned long put_got_entry(TCCState *s1,
|
|||||||
p[11] = 0xe9; /* jmp plt_start */
|
p[11] = 0xe9; /* jmp plt_start */
|
||||||
write32le(p + 12, -(plt->data_offset));
|
write32le(p + 12, -(plt->data_offset));
|
||||||
|
|
||||||
/* If this was an UNDEF symbol set the offset in the
|
/* If this was an UNDEF symbol set the offset in the dynsymtab to the
|
||||||
dynsymtab to the PLT slot, so that PC32 relocs to it
|
PLT slot, so that PC32 relocs to it can be resolved */
|
||||||
can be resolved. */
|
|
||||||
if (sym->st_shndx == SHN_UNDEF)
|
if (sym->st_shndx == SHN_UNDEF)
|
||||||
offset = plt->data_offset - 16;
|
offset = plt->data_offset - 16;
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_ARM)
|
#elif defined(TCC_TARGET_ARM)
|
||||||
if (need_plt_entry) {
|
|
||||||
Section *plt;
|
Section *plt;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
|
|
||||||
/* if we build a DLL, we add a %ebx 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)
|
if (s1->output_type == TCC_OUTPUT_DLL)
|
||||||
tcc_error("DLLs unimplemented!");
|
tcc_error("DLLs unimplemented!");
|
||||||
|
|
||||||
/* add a PLT entry */
|
|
||||||
plt = s1->plt;
|
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) {
|
if (plt->data_offset == 0) {
|
||||||
/* first plt entry */
|
|
||||||
p = section_ptr_add(plt, 16);
|
p = section_ptr_add(plt, 16);
|
||||||
write32le(p, 0xe52de004); /* push {lr} */
|
write32le(p, 0xe52de004); /* push {lr} */
|
||||||
write32le(p+4, 0xe59fe010); /* ldr lr, [pc, #16] */
|
write32le(p+4, 0xe59fe010); /* ldr lr, [pc, #16] */
|
||||||
@ -1426,24 +1430,22 @@ static unsigned long put_got_entry(TCCState *s1,
|
|||||||
|
|
||||||
symattr->plt_offset = plt->data_offset;
|
symattr->plt_offset = plt->data_offset;
|
||||||
if (symattr->plt_thumb_stub) {
|
if (symattr->plt_thumb_stub) {
|
||||||
p = section_ptr_add(plt, 20);
|
p = section_ptr_add(plt, 4);
|
||||||
write32le(p, 0x4778); /* bx pc */
|
write32le(p, 0x4778); /* bx pc */
|
||||||
write32le(p+2, 0x46c0); /* nop */
|
write32le(p+2, 0x46c0); /* nop */
|
||||||
p += 4;
|
}
|
||||||
} else
|
|
||||||
p = section_ptr_add(plt, 16);
|
p = section_ptr_add(plt, 16);
|
||||||
write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] ; GOT entry offset */
|
/* Jump to GOT entry where ld.so initially put address of PLT0 */
|
||||||
write32le(p+4, 0xe08fc00c); /* add ip, pc, ip ; addr of GOT entry */
|
write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] */
|
||||||
write32le(p+8, 0xe59cf000); /* ldr pc, [ip] ; jump to GOT entry */
|
write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */
|
||||||
write32le(p+12, s1->got->data_offset); /* GOT entry off once patched */
|
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 symbol is modified so that it will be relocated to the PLT */
|
||||||
the PLT */
|
|
||||||
if (sym->st_shndx == SHN_UNDEF)
|
if (sym->st_shndx == SHN_UNDEF)
|
||||||
offset = plt->data_offset - 16;
|
offset = plt->data_offset - 16;
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_ARM64)
|
#elif defined(TCC_TARGET_ARM64)
|
||||||
if (need_plt_entry) {
|
|
||||||
Section *plt;
|
Section *plt;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
|
|
||||||
@ -1455,39 +1457,37 @@ static unsigned long put_got_entry(TCCState *s1,
|
|||||||
section_ptr_add(plt, 32);
|
section_ptr_add(plt, 32);
|
||||||
symattr->plt_offset = plt->data_offset;
|
symattr->plt_offset = plt->data_offset;
|
||||||
p = section_ptr_add(plt, 16);
|
p = section_ptr_add(plt, 16);
|
||||||
write32le(p, s1->got->data_offset);
|
write32le(p, got_offset);
|
||||||
write32le(p + 4, (uint64_t)s1->got->data_offset >> 32);
|
write32le(p + 4, (uint64_t) got_offset >> 32);
|
||||||
|
|
||||||
if (sym->st_shndx == SHN_UNDEF)
|
if (sym->st_shndx == SHN_UNDEF)
|
||||||
offset = plt->data_offset - 16;
|
offset = plt->data_offset - 16;
|
||||||
}
|
|
||||||
#elif defined(TCC_TARGET_C67)
|
#elif defined(TCC_TARGET_C67)
|
||||||
if (s1->dynsym) {
|
|
||||||
tcc_error("C67 got not implemented");
|
tcc_error("C67 got not implemented");
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
#error unsupported CPU
|
#error unsupported CPU
|
||||||
#endif
|
#endif
|
||||||
if (s1->dynsym) {
|
|
||||||
/* XXX This might generate multiple syms for name. */
|
|
||||||
index = put_elf_sym(s1->dynsym, offset,
|
|
||||||
size, info, 0, sym->st_shndx, name);
|
|
||||||
/* Create the relocation (it's against the GOT for PLT
|
|
||||||
and GOT relocs). */
|
|
||||||
put_elf_reloc(s1->dynsym, s1->got,
|
|
||||||
s1->got->data_offset,
|
|
||||||
reloc_type, index);
|
|
||||||
} else {
|
|
||||||
/* Without .dynsym (i.e. static link or memory output) we
|
|
||||||
still need relocs against the generated got, so as to fill
|
|
||||||
the entries with the symbol values (determined later). */
|
|
||||||
put_elf_reloc(symtab_section, s1->got,
|
|
||||||
s1->got->data_offset,
|
|
||||||
reloc_type, sym_index);
|
|
||||||
}
|
}
|
||||||
/* And now create the GOT slot itself. */
|
|
||||||
ptr = section_ptr_add(s1->got, PTR_SIZE);
|
/* Create the GOT relocation that will insert the address of the object or
|
||||||
*ptr = 0;
|
function of interest in the GOT entry. This is a static relocation for
|
||||||
|
memory output (dlsym will give us the address of symbols) and dynamic
|
||||||
|
relocation otherwise (executable and DLLs). The relocation should be
|
||||||
|
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
|
||||||
|
unknown reason. */
|
||||||
|
if (s1->dynsym) {
|
||||||
|
/* create the dynamic symbol table entry that the relocation refers to
|
||||||
|
in its r_info field to identify the symbol */
|
||||||
|
/* XXX This might generate multiple syms for name. */
|
||||||
|
index = put_elf_sym(s1->dynsym, offset, size, info, 0, sym->st_shndx,
|
||||||
|
name);
|
||||||
|
put_elf_reloc(s1->dynsym, s1->got, got_offset, reloc_type, index);
|
||||||
|
} else {
|
||||||
|
put_elf_reloc(symtab_section, s1->got, got_offset, reloc_type,
|
||||||
|
sym_index);
|
||||||
|
}
|
||||||
|
|
||||||
if (need_plt_entry)
|
if (need_plt_entry)
|
||||||
return symattr->plt_offset;
|
return symattr->plt_offset;
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user