Alien-TinyCCx
view release on metacpan or search on metacpan
src/tccelf.c view on Meta::CPAN
".dynstrtab",
".dynhashtab", SHF_PRIVATE);
}
#ifdef CONFIG_TCC_BCHECK
ST_FUNC void tccelf_bounds_new(TCCState *s)
{
/* create bounds sections */
bounds_section = new_section(s, ".bounds",
SHT_PROGBITS, SHF_ALLOC);
lbounds_section = new_section(s, ".lbounds",
SHT_PROGBITS, SHF_ALLOC);
}
#endif
ST_FUNC void tccelf_stab_new(TCCState *s)
{
stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
stab_section->sh_entsize = sizeof(Stab_Sym);
stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
put_elf_str(stabstr_section, "");
stab_section->link = stabstr_section;
/* put first entry */
put_stabs("", 0, 0, 0, 0);
}
static void free_section(Section *s)
{
tcc_free(s->data);
}
ST_FUNC void tccelf_delete(TCCState *s1)
{
int i;
/* free all sections */
for(i = 1; i < s1->nb_sections; i++)
free_section(s1->sections[i]);
dynarray_reset(&s1->sections, &s1->nb_sections);
for(i = 0; i < s1->nb_priv_sections; i++)
free_section(s1->priv_sections[i]);
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
/* free any loaded DLLs */
#ifdef TCC_IS_NATIVE
for ( i = 0; i < s1->nb_loaded_dlls; i++) {
DLLReference *ref = s1->loaded_dlls[i];
if ( ref->handle )
# ifdef _WIN32
FreeLibrary((HMODULE)ref->handle);
# else
dlclose(ref->handle);
# endif
}
#endif
/* free loaded dlls array */
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
}
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
Section *sec;
sec = tcc_mallocz(sizeof(Section) + strlen(name));
strcpy(sec->name, name);
sec->sh_type = sh_type;
sec->sh_flags = sh_flags;
switch(sh_type) {
case SHT_HASH:
case SHT_REL:
case SHT_RELA:
case SHT_DYNSYM:
case SHT_SYMTAB:
case SHT_DYNAMIC:
sec->sh_addralign = 4;
break;
case SHT_STRTAB:
sec->sh_addralign = 1;
break;
default:
sec->sh_addralign = PTR_SIZE; /* gcc/pcc default aligment */
break;
}
if (sh_flags & SHF_PRIVATE) {
dynarray_add((void ***)&s1->priv_sections, &s1->nb_priv_sections, sec);
} else {
sec->sh_num = s1->nb_sections;
dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
}
return sec;
}
/* realloc section and set its content to zero */
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
{
unsigned long size;
unsigned char *data;
size = sec->data_allocated;
if (size == 0)
size = 1;
while (size < new_size)
size = size * 2;
data = tcc_realloc(sec->data, size);
memset(data + sec->data_allocated, 0, size - sec->data_allocated);
sec->data = data;
sec->data_allocated = size;
}
/* reserve at least 'size' bytes in section 'sec' from
sec->data_offset. */
ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
{
size_t offset, offset1;
offset = sec->data_offset;
offset1 = offset + size;
if (offset1 > sec->data_allocated)
section_realloc(sec, offset1);
sec->data_offset = offset1;
return sec->data + offset;
}
/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
{
if (size > sec->data_allocated)
section_realloc(sec, size);
if (size > sec->data_offset)
sec->data_offset = size;
}
/* return a reference to a section, and create it if it does not
exists */
ST_FUNC Section *find_section(TCCState *s1, const char *name)
{
Section *sec;
int i;
for(i = 1; i < s1->nb_sections; i++) {
sec = s1->sections[i];
if (!strcmp(name, sec->name))
return sec;
}
src/tccelf.c view on Meta::CPAN
/* ignore adding of undefined symbol if the
corresponding symbol is already defined */
} else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
/* global overrides weak, so patch */
goto do_patch;
} else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
/* weak is ignored if already global */
} else if (sym_bind == STB_WEAK && esym_bind == STB_WEAK) {
/* keep first-found weak definition, ignore subsequents */
} else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
/* ignore hidden symbols after */
} else if ((esym->st_shndx == SHN_COMMON
|| esym->st_shndx == bss_section->sh_num)
&& (sh_num < SHN_LORESERVE
&& sh_num != bss_section->sh_num)) {
/* data symbol gets precedence over common/bss */
goto do_patch;
} else if (sh_num == SHN_COMMON || sh_num == bss_section->sh_num) {
/* data symbol keeps precedence over common/bss */
} else if (s == tcc_state->dynsymtab_section) {
/* we accept that two DLL define the same symbol */
} else {
#if 0
printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
sym_bind, sh_num, new_vis, esym_bind, esym->st_shndx, esym_vis);
#endif
tcc_error_noabort("'%s' defined twice", name);
}
} else {
do_patch:
esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);
esym->st_shndx = sh_num;
new_undef_sym = 1;
esym->st_value = value;
esym->st_size = size;
esym->st_other = other;
}
} else {
do_def:
sym_index = put_elf_sym(s, value, size,
ELFW(ST_INFO)(sym_bind, sym_type), other,
sh_num, name);
}
return sym_index;
}
/* put relocation */
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
int type, int symbol, addr_t addend)
{
char buf[256];
Section *sr;
ElfW_Rel *rel;
sr = s->reloc;
if (!sr) {
/* if no relocation section, create it */
snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name);
/* if the symtab is allocated, then we consider the relocation
are also */
sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags);
sr->sh_entsize = sizeof(ElfW_Rel);
sr->link = symtab;
sr->sh_info = s->sh_num;
s->reloc = sr;
}
rel = section_ptr_add(sr, sizeof(ElfW_Rel));
rel->r_offset = offset;
rel->r_info = ELFW(R_INFO)(symbol, type);
#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
rel->r_addend = addend;
#else
if (addend)
tcc_error("non-zero addend on REL architecture");
#endif
}
ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
int type, int symbol)
{
put_elf_reloca(symtab, s, offset, type, symbol, 0);
}
/* put stab debug information */
ST_FUNC void put_stabs(const char *str, int type, int other, int desc,
unsigned long value)
{
Stab_Sym *sym;
sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
if (str) {
sym->n_strx = put_elf_str(stabstr_section, str);
} else {
sym->n_strx = 0;
}
sym->n_type = type;
sym->n_other = other;
sym->n_desc = desc;
sym->n_value = value;
}
ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc,
unsigned long value, Section *sec, int sym_index)
{
put_stabs(str, type, other, desc, value);
put_elf_reloc(symtab_section, stab_section,
stab_section->data_offset - sizeof(unsigned int),
R_DATA_32, sym_index);
}
ST_FUNC void put_stabn(int type, int other, int desc, int value)
{
put_stabs(NULL, type, other, desc, value);
}
ST_FUNC void put_stabd(int type, int other, int desc)
{
put_stabs(NULL, type, other, desc, 0);
}
src/tccelf.c view on Meta::CPAN
if (s1->output_type == TCC_OUTPUT_DLL) {
/* DLL relocation */
esym_index = s1->symtab_to_dynsym[sym_index];
if (esym_index) {
qrel->r_offset = rel->r_offset;
qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
/* Use sign extension! */
qrel->r_addend = (int)read32le(ptr) + rel->r_addend;
qrel++;
break;
}
}
goto plt32pc32;
case R_X86_64_PLT32:
/* We've put the PLT slot offset into r_addend when generating
it, and that's what we must use as relocation value (adjusted
by section offset of course). */
val = s1->plt->sh_addr + rel->r_addend;
/* fallthrough. */
plt32pc32:
{
long long diff;
diff = (long long)val - addr;
if (diff < -2147483648LL || diff > 2147483647LL) {
tcc_error("internal error: relocation failed");
}
add32le(ptr, diff);
}
break;
case R_X86_64_GLOB_DAT:
case R_X86_64_JUMP_SLOT:
/* They don't need addend */
write64le(ptr, val - rel->r_addend);
break;
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
add32le(ptr, s1->got->sh_addr - addr
+ s1->sym_attrs[sym_index].got_offset - 4);
break;
case R_X86_64_GOTTPOFF:
add32le(ptr, val - s1->got->sh_addr);
break;
case R_X86_64_GOT32:
/* we load the got offset */
add32le(ptr, s1->sym_attrs[sym_index].got_offset);
break;
#ifdef TCC_TARGET_PE
case R_X86_64_RELATIVE: /* handled in pe_relocate_rva() */
break;
#endif
#else
#error unsupported processor
#endif
}
}
/* if the relocation is allocated, we change its symbol table */
if (sr->sh_flags & SHF_ALLOC)
sr->link = s1->dynsym;
}
/* relocate relocation table in 'sr' */
static void relocate_rel(TCCState *s1, Section *sr)
{
Section *s;
ElfW_Rel *rel;
s = s1->sections[sr->sh_info];
for_each_elem(sr, 0, rel, ElfW_Rel)
rel->r_offset += s->sh_addr;
}
/* count the number of dynamic relocations so that we can reserve
their space */
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
{
ElfW_Rel *rel;
int sym_index, esym_index, type, count;
count = 0;
for_each_elem(sr, 0, rel, ElfW_Rel) {
sym_index = ELFW(R_SYM)(rel->r_info);
type = ELFW(R_TYPE)(rel->r_info);
switch(type) {
#if defined(TCC_TARGET_I386)
case R_386_32:
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_32:
case R_X86_64_32S:
case R_X86_64_64:
#endif
count++;
break;
#if defined(TCC_TARGET_I386)
case R_386_PC32:
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_PC32:
#endif
esym_index = s1->symtab_to_dynsym[sym_index];
if (esym_index)
count++;
break;
default:
break;
}
}
if (count) {
/* allocate the section */
sr->sh_flags |= SHF_ALLOC;
sr->sh_size = count * sizeof(ElfW_Rel);
}
return count;
}
static struct sym_attr *alloc_sym_attr(TCCState *s1, int index)
{
int n;
struct sym_attr *tab;
if (index >= s1->nb_sym_attrs) {
/* find immediately bigger power of 2 and reallocate array */
n = 1;
while (index >= n)
n *= 2;
tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
s1->sym_attrs = tab;
memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
(n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
s1->nb_sym_attrs = n;
}
return &s1->sym_attrs[index];
}
static void build_got(TCCState *s1)
{
unsigned char *ptr;
/* if no got, then create it */
s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
s1->got->sh_entsize = 4;
add_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
ptr = section_ptr_add(s1->got, 3 * PTR_SIZE);
#if PTR_SIZE == 4
/* 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
}
/* put a got or plt entry corresponding to a symbol in symtab_section. 'size'
and 'info' can be modifed if more precise info comes from the DLL.
Returns offset of GOT or PLT slot. */
static unsigned long put_got_entry(TCCState *s1,
int reloc_type, unsigned long size, int info,
int sym_index)
{
int index, need_plt_entry;
src/tccelf.c view on Meta::CPAN
sym_index = ELFW(R_SYM)(rel->r_info);
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
/* look at the symbol got offset. If none, then add one */
if (type == R_C60_GOT32)
reloc_type = R_C60_GLOB_DAT;
else
reloc_type = R_C60_JMP_SLOT;
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
sym_index);
}
break;
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_GOT32:
case R_X86_64_GOTTPOFF:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PLT32:
sym_index = ELFW(R_SYM)(rel->r_info);
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (type == R_X86_64_PLT32 &&
ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT)
{
rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32);
break;
}
if (!s1->got) {
build_got(s1);
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
}
if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL ||
type == R_X86_64_GOTPCRELX ||
type == R_X86_64_REX_GOTPCRELX ||
type == R_X86_64_PLT32) {
unsigned long ofs;
/* look at the symbol got offset. If none, then add one */
if (type == R_X86_64_PLT32)
reloc_type = R_X86_64_JUMP_SLOT;
else
reloc_type = R_X86_64_GLOB_DAT;
ofs = put_got_entry(s1, reloc_type, sym->st_size,
sym->st_info, sym_index);
if (type == R_X86_64_PLT32)
/* We store the place of the generated PLT slot
in our addend. */
rel->r_addend += ofs;
}
break;
#else
#error unsupported CPU
#endif
default:
break;
}
}
}
}
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 */
static void put_dt(Section *dynamic, int dt, addr_t val)
{
ElfW(Dyn) *dyn;
dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
dyn->d_tag = dt;
dyn->d_un.d_val = val;
}
#ifndef TCC_TARGET_PE
static void add_init_array_defines(TCCState *s1, const char *section_name)
{
Section *s;
long end_offset;
char sym_start[1024];
char sym_end[1024];
snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);
s = find_section(s1, section_name);
if (!s) {
end_offset = 0;
s = data_section;
} else {
end_offset = s->data_offset;
}
add_elf_sym(symtab_section,
0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, sym_start);
add_elf_sym(symtab_section,
end_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, sym_end);
}
#endif
static int tcc_add_support(TCCState *s1, const char *filename)
{
char buf[1024];
snprintf(buf, sizeof(buf), "%s/"TCC_ARCH_DIR"%s", s1->tcc_lib_path, filename);
return tcc_add_file(s1, buf);
}
ST_FUNC void tcc_add_bcheck(TCCState *s1)
{
#ifdef CONFIG_TCC_BCHECK
src/tccelf.c view on Meta::CPAN
put_elf_reloc(symtab_section, init_section,
init_section->data_offset - 4, R_386_PC32, sym_index);
/* R_386_PC32 = R_X86_64_PC32 = 2 */
}
#endif
}
/* add tcc runtime libraries */
ST_FUNC void tcc_add_runtime(TCCState *s1)
{
tcc_add_bcheck(s1);
tcc_add_pragma_libs(s1);
/* add libc */
if (!s1->nostdlib) {
tcc_add_library_err(s1, "c");
#ifdef CONFIG_USE_LIBGCC
if (!s1->static_link) {
tcc_add_file(s1, TCC_LIBGCC);
}
#endif
tcc_add_support(s1, "libtcc1.a");
/* add crt end if not memory output */
if (s1->output_type != TCC_OUTPUT_MEMORY)
tcc_add_crt(s1, "crtn.o");
}
}
/* add various standard linker symbols (must be done after the
sections are filled (for example after allocating common
symbols)) */
ST_FUNC void tcc_add_linker_symbols(TCCState *s1)
{
char buf[1024];
int i;
Section *s;
add_elf_sym(symtab_section,
text_section->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
text_section->sh_num, "_etext");
add_elf_sym(symtab_section,
data_section->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
data_section->sh_num, "_edata");
add_elf_sym(symtab_section,
bss_section->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
bss_section->sh_num, "_end");
#ifndef TCC_TARGET_PE
/* horrible new standard ldscript defines */
add_init_array_defines(s1, ".preinit_array");
add_init_array_defines(s1, ".init_array");
add_init_array_defines(s1, ".fini_array");
#endif
/* add start and stop symbols for sections whose name can be
expressed in C */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_type == SHT_PROGBITS &&
(s->sh_flags & SHF_ALLOC)) {
const char *p;
int ch;
/* check if section name can be expressed in C */
p = s->name;
for(;;) {
ch = *p;
if (!ch)
break;
if (!isid(ch) && !isnum(ch))
goto next_sec;
p++;
}
snprintf(buf, sizeof(buf), "__start_%s", s->name);
add_elf_sym(symtab_section,
0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, buf);
snprintf(buf, sizeof(buf), "__stop_%s", s->name);
add_elf_sym(symtab_section,
s->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, buf);
}
next_sec: ;
}
}
static void tcc_output_binary(TCCState *s1, FILE *f,
const int *sec_order)
{
Section *s;
int i, offset, size;
offset = 0;
for(i=1;i<s1->nb_sections;i++) {
s = s1->sections[sec_order[i]];
if (s->sh_type != SHT_NOBITS &&
(s->sh_flags & SHF_ALLOC)) {
while (offset < s->sh_offset) {
fputc(0, f);
offset++;
}
size = s->sh_size;
fwrite(s->data, 1, size, f);
offset += size;
}
}
}
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define HAVE_PHDR 1
#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
#define HAVE_PHDR 1
#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
ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
{
int sym_index = ELFW(R_SYM) (rel->r_info);
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
unsigned long offset;
if (sym_index >= s1->nb_sym_attrs)
return;
offset = s1->sym_attrs[sym_index].got_offset;
section_reserve(s1->got, offset + PTR_SIZE);
#ifdef TCC_TARGET_X86_64
/* only works for x86-64 */
write32le(s1->got->data + offset + 4, sym->st_value >> 32);
#endif
write32le(s1->got->data + offset, sym->st_value & 0xffffffff);
src/tccelf.c view on Meta::CPAN
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
should be output.
NOTE: the strsec section comes last, so its size is also correct ! */
static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
{
int i;
Section *s;
/* Allocate strings for section names */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
s->sh_name = put_elf_str(strsec, s->name);
/* when generating a DLL, we include relocations but we may
patch them */
if (file_type == TCC_OUTPUT_DLL &&
s->sh_type == SHT_RELX &&
!(s->sh_flags & SHF_ALLOC)) {
/* gr: avoid bogus relocs for empty (debug) sections */
if (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC)
prepare_dynamic_rel(s1, s);
else if (s1->do_debug)
s->sh_size = s->data_offset;
} else if (s1->do_debug ||
file_type == TCC_OUTPUT_OBJ ||
(s->sh_flags & SHF_ALLOC) ||
i == (s1->nb_sections - 1)) {
/* we output all sections if debug or object file */
s->sh_size = s->data_offset;
}
}
}
/* Info to be copied in dynamic section */
struct dyn_inf {
Section *dynamic;
Section *dynstr;
unsigned long dyn_rel_off;
addr_t rel_addr;
addr_t rel_size;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
addr_t bss_addr;
addr_t bss_size;
#endif
};
/* Assign sections to segments and decide how are sections laid out when loaded
in memory. This function also fills corresponding program headers. */
static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
Section *interp, Section* strsec,
struct dyn_inf *dyninf, int *sec_order)
{
int i, j, k, file_type, sh_order_index, file_offset;
unsigned long s_align;
long long tmp;
addr_t addr;
ElfW(Phdr) *ph;
Section *s;
file_type = s1->output_type;
sh_order_index = 1;
file_offset = 0;
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
s_align = ELF_PAGE_SIZE;
if (s1->section_align)
s_align = s1->section_align;
if (phnum > 0) {
if (s1->has_text_addr) {
int a_offset, p_offset;
addr = s1->text_addr;
/* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
ELF_PAGE_SIZE */
a_offset = (int) (addr & (s_align - 1));
p_offset = file_offset & (s_align - 1);
if (a_offset < p_offset)
a_offset += s_align;
file_offset += (a_offset - p_offset);
} else {
if (file_type == TCC_OUTPUT_DLL)
addr = 0;
else
addr = ELF_START_ADDR;
/* compute address after headers */
addr += (file_offset & (s_align - 1));
}
ph = &phdr[0];
/* Leave one program headers for the program interpreter and one for
the program header table itself if needed. These are done later as
they require section layout to be done first. */
if (interp)
ph += 1 + HAVE_PHDR;
/* dynamic relocation table information, for .dynamic section */
dyninf->rel_addr = dyninf->rel_size = 0;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
dyninf->bss_addr = dyninf->bss_size = 0;
#endif
for(j = 0; j < 2; j++) {
ph->p_type = PT_LOAD;
if (j == 0)
ph->p_flags = PF_R | PF_X;
else
ph->p_flags = PF_R | PF_W;
ph->p_align = s_align;
/* Decide the layout of sections loaded in memory. This must
be done before program headers are filled since they contain
info about the layout. We do the following ordering: interp,
symbol tables, relocations, progbits, nobits */
/* XXX: do faster and simpler sorting */
for(k = 0; k < 5; k++) {
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
/* compute if section should be included */
if (j == 0) {
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
SHF_ALLOC)
continue;
} else {
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
(SHF_ALLOC | SHF_WRITE))
continue;
}
if (s == interp) {
if (k != 0)
continue;
} else if (s->sh_type == SHT_DYNSYM ||
s->sh_type == SHT_STRTAB ||
s->sh_type == SHT_HASH) {
if (k != 1)
continue;
} else if (s->sh_type == SHT_RELX) {
if (k != 2)
continue;
} else if (s->sh_type == SHT_NOBITS) {
if (k != 4)
continue;
} else {
if (k != 3)
continue;
}
sec_order[sh_order_index++] = i;
/* section matches: we align it and add its size */
tmp = addr;
addr = (addr + s->sh_addralign - 1) &
~(s->sh_addralign - 1);
file_offset += (int) ( addr - tmp );
s->sh_offset = file_offset;
s->sh_addr = addr;
/* update program header infos */
if (ph->p_offset == 0) {
ph->p_offset = file_offset;
ph->p_vaddr = addr;
ph->p_paddr = ph->p_vaddr;
}
/* update dynamic relocation infos */
if (s->sh_type == SHT_RELX) {
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
if (!strcmp(strsec->data + s->sh_name, ".rel.got")) {
dyninf->rel_addr = addr;
dyninf->rel_size += s->sh_size; /* XXX only first rel. */
}
if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) {
dyninf->bss_addr = addr;
dyninf->bss_size = s->sh_size; /* XXX only first rel. */
}
#else
if (dyninf->rel_size == 0)
dyninf->rel_addr = addr;
dyninf->rel_size += s->sh_size;
#endif
}
addr += s->sh_size;
if (s->sh_type != SHT_NOBITS)
file_offset += s->sh_size;
}
}
if (j == 0) {
/* Make the first PT_LOAD segment include the program
headers itself (and the ELF header as well), it'll
come out with same memory use but will make various
tools like binutils strip work better. */
ph->p_offset &= ~(ph->p_align - 1);
ph->p_vaddr &= ~(ph->p_align - 1);
ph->p_paddr &= ~(ph->p_align - 1);
}
ph->p_filesz = file_offset - ph->p_offset;
ph->p_memsz = addr - ph->p_vaddr;
ph++;
if (j == 0) {
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
/* if in the middle of a page, we duplicate the page in
memory so that one copy is RX and the other is RW */
if ((addr & (s_align - 1)) != 0)
addr += s_align;
} else {
addr = (addr + s_align - 1) & ~(s_align - 1);
file_offset = (file_offset + s_align - 1) & ~(s_align - 1);
}
}
}
}
/* all other sections come after */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
continue;
sec_order[sh_order_index++] = i;
file_offset = (file_offset + s->sh_addralign - 1) &
~(s->sh_addralign - 1);
s->sh_offset = file_offset;
if (s->sh_type != SHT_NOBITS)
file_offset += s->sh_size;
}
return file_offset;
}
static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
Section *dynamic)
{
ElfW(Phdr) *ph;
/* if interpreter, then add corresponding program header */
if (interp) {
ph = &phdr[0];
if (HAVE_PHDR)
{
int len = phnum * sizeof(ElfW(Phdr));
ph->p_type = PT_PHDR;
ph->p_offset = sizeof(ElfW(Ehdr));
ph->p_vaddr = interp->sh_addr - len;
ph->p_paddr = ph->p_vaddr;
ph->p_filesz = ph->p_memsz = len;
ph->p_flags = PF_R | PF_X;
ph->p_align = 4; /* interp->sh_addralign; */
ph++;
}
ph->p_type = PT_INTERP;
ph->p_offset = interp->sh_offset;
ph->p_vaddr = interp->sh_addr;
ph->p_paddr = ph->p_vaddr;
ph->p_filesz = interp->sh_size;
ph->p_memsz = interp->sh_size;
ph->p_flags = PF_R;
ph->p_align = interp->sh_addralign;
}
/* if dynamic section, then add corresponding program header */
if (dynamic) {
ph = &phdr[phnum - 1];
ph->p_type = PT_DYNAMIC;
ph->p_offset = dynamic->sh_offset;
ph->p_vaddr = dynamic->sh_addr;
ph->p_paddr = ph->p_vaddr;
ph->p_filesz = dynamic->sh_size;
ph->p_memsz = dynamic->sh_size;
ph->p_flags = PF_R | PF_W;
ph->p_align = dynamic->sh_addralign;
}
}
/* Fill the dynamic section with tags describing the address and size of
sections */
static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf)
{
Section *dynamic;
dynamic = dyninf->dynamic;
/* put dynamic section entries */
dynamic->data_offset = dyninf->dyn_rel_off;
put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr);
put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset);
put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym)));
#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
put_dt(dynamic, DT_RELA, dyninf->rel_addr);
put_dt(dynamic, DT_RELASZ, dyninf->rel_size);
put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel));
#else
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr);
put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size);
put_dt(dynamic, DT_JMPREL, dyninf->rel_addr);
put_dt(dynamic, DT_PLTREL, DT_REL);
put_dt(dynamic, DT_REL, dyninf->bss_addr);
put_dt(dynamic, DT_RELSZ, dyninf->bss_size);
#else
put_dt(dynamic, DT_REL, dyninf->rel_addr);
put_dt(dynamic, DT_RELSZ, dyninf->rel_size);
put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel));
#endif
#endif
if (s1->do_debug)
put_dt(dynamic, DT_DEBUG, 0);
put_dt(dynamic, DT_NULL, 0);
}
/* Relocate remaining sections and symbols (that is those not related to
dynamic linking) */
static int final_sections_reloc(TCCState *s1)
{
int i;
Section *s;
relocate_syms(s1, 0);
if (s1->nb_errors != 0)
return -1;
/* relocate sections */
/* XXX: ignore sections with allocated relocations ? */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
#ifdef TCC_TARGET_I386
if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
/* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
checking is removed */
#else
if (s->reloc && s != s1->got)
/* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
#endif
relocate_section(s1, s);
}
/* relocate relocation entries if the relocation tables are
allocated in the executable */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if ((s->sh_flags & SHF_ALLOC) &&
s->sh_type == SHT_RELX) {
relocate_rel(s1, s);
}
}
return 0;
}
/* Create an ELF file on disk.
This function handle ELF specific layout requirements */
static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
int file_offset, int *sec_order)
{
int i, shnum, offset, size, file_type;
Section *s;
ElfW(Ehdr) ehdr;
ElfW(Shdr) shdr, *sh;
file_type = s1->output_type;
shnum = s1->nb_sections;
memset(&ehdr, 0, sizeof(ehdr));
if (phnum > 0) {
ehdr.e_phentsize = sizeof(ElfW(Phdr));
ehdr.e_phnum = phnum;
ehdr.e_phoff = sizeof(ElfW(Ehdr));
}
/* align to 4 */
file_offset = (file_offset + 3) & -4;
/* fill header */
ehdr.e_ident[0] = ELFMAG0;
ehdr.e_ident[1] = ELFMAG1;
ehdr.e_ident[2] = ELFMAG2;
ehdr.e_ident[3] = ELFMAG3;
ehdr.e_ident[4] = ELFCLASSW;
ehdr.e_ident[5] = ELFDATA2LSB;
ehdr.e_ident[6] = EV_CURRENT;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
#endif
#ifdef TCC_TARGET_ARM
#ifdef TCC_ARM_EABI
ehdr.e_ident[EI_OSABI] = 0;
ehdr.e_flags = EF_ARM_EABI_VER4;
if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL)
ehdr.e_flags |= EF_ARM_HASENTRY;
if (s1->float_abi == ARM_HARD_FLOAT)
ehdr.e_flags |= EF_ARM_VFP_FLOAT;
else
ehdr.e_flags |= EF_ARM_SOFT_FLOAT;
#else
ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
#endif
#endif
switch(file_type) {
default:
case TCC_OUTPUT_EXE:
ehdr.e_type = ET_EXEC;
ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
break;
case TCC_OUTPUT_DLL:
ehdr.e_type = ET_DYN;
ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
break;
case TCC_OUTPUT_OBJ:
ehdr.e_type = ET_REL;
break;
}
ehdr.e_machine = EM_TCC_TARGET;
ehdr.e_version = EV_CURRENT;
ehdr.e_shoff = file_offset;
ehdr.e_ehsize = sizeof(ElfW(Ehdr));
ehdr.e_shentsize = sizeof(ElfW(Shdr));
ehdr.e_shnum = shnum;
ehdr.e_shstrndx = shnum - 1;
fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f);
fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f);
offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
sort_syms(s1, symtab_section);
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[sec_order[i]];
if (s->sh_type != SHT_NOBITS) {
if (s->sh_type == SHT_DYNSYM)
patch_dynsym_undef(s1, s);
while (offset < s->sh_offset) {
fputc(0, f);
offset++;
}
size = s->sh_size;
if (size)
fwrite(s->data, 1, size, f);
offset += size;
}
}
/* output section headers */
while (offset < ehdr.e_shoff) {
fputc(0, f);
offset++;
}
for(i = 0; i < s1->nb_sections; i++) {
sh = &shdr;
memset(sh, 0, sizeof(ElfW(Shdr)));
s = s1->sections[i];
if (s) {
sh->sh_name = s->sh_name;
sh->sh_type = s->sh_type;
sh->sh_flags = s->sh_flags;
sh->sh_entsize = s->sh_entsize;
sh->sh_info = s->sh_info;
if (s->link)
sh->sh_link = s->link->sh_num;
sh->sh_addralign = s->sh_addralign;
sh->sh_addr = s->sh_addr;
sh->sh_offset = s->sh_offset;
sh->sh_size = s->sh_size;
}
fwrite(sh, 1, sizeof(ElfW(Shdr)), f);
}
}
/* Write an elf, coff or "binary" file */
static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
ElfW(Phdr) *phdr, int file_offset, int *sec_order)
{
int fd, mode, file_type;
FILE *f;
file_type = s1->output_type;
if (file_type == TCC_OUTPUT_OBJ)
mode = 0666;
else
mode = 0777;
unlink(filename);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
if (fd < 0) {
tcc_error_noabort("could not write '%s'", filename);
return -1;
}
f = fdopen(fd, "wb");
if (s1->verbose)
printf("<- %s\n", filename);
#ifdef TCC_TARGET_COFF
if (s1->output_format == TCC_OUTPUT_FORMAT_COFF)
tcc_output_coff(s1, f);
else
#endif
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order);
else
tcc_output_binary(s1, f, sec_order);
fclose(f);
return 0;
}
/* Output an elf, coff or binary file */
/* XXX: suppress unneeded sections */
static int elf_output_file(TCCState *s1, const char *filename)
{
int i, ret, phnum, shnum, file_type, file_offset, *sec_order;
struct dyn_inf dyninf;
ElfW(Phdr) *phdr;
ElfW(Sym) *sym;
Section *strsec, *interp, *dynamic, *dynstr;
file_type = s1->output_type;
src/tccelf.c view on Meta::CPAN
for(i = 1; i < ehdr.e_shnum; i++) {
sh = &shdr[i];
if (sh->sh_type == SHT_SYMTAB) {
if (symtab) {
tcc_error_noabort("object must contain only one symtab");
fail:
ret = -1;
goto the_end;
}
nb_syms = sh->sh_size / sizeof(ElfW(Sym));
symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
sm_table[i].s = symtab_section;
/* now load strtab */
sh = &shdr[sh->sh_link];
strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
}
}
/* now examine each section and try to merge its content with the
ones in memory */
for(i = 1; i < ehdr.e_shnum; i++) {
/* no need to examine section name strtab */
if (i == ehdr.e_shstrndx)
continue;
sh = &shdr[i];
sh_name = (char *) strsec + sh->sh_name;
/* ignore sections types we do not handle */
if (sh->sh_type != SHT_PROGBITS &&
sh->sh_type != SHT_RELX &&
#ifdef TCC_ARM_EABI
sh->sh_type != SHT_ARM_EXIDX &&
#endif
sh->sh_type != SHT_NOBITS &&
sh->sh_type != SHT_PREINIT_ARRAY &&
sh->sh_type != SHT_INIT_ARRAY &&
sh->sh_type != SHT_FINI_ARRAY &&
strcmp(sh_name, ".stabstr")
)
continue;
if (sh->sh_addralign < 1)
sh->sh_addralign = 1;
/* find corresponding section, if any */
for(j = 1; j < s1->nb_sections;j++) {
s = s1->sections[j];
if (!strcmp(s->name, sh_name)) {
if (!strncmp(sh_name, ".gnu.linkonce",
sizeof(".gnu.linkonce") - 1)) {
/* if a 'linkonce' section is already present, we
do not add it again. It is a little tricky as
symbols can still be defined in
it. */
sm_table[i].link_once = 1;
goto next;
} else {
goto found;
}
}
}
/* not found: create new section */
s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags);
/* take as much info as possible from the section. sh_link and
sh_info will be updated later */
s->sh_addralign = sh->sh_addralign;
s->sh_entsize = sh->sh_entsize;
sm_table[i].new_section = 1;
found:
if (sh->sh_type != s->sh_type) {
tcc_error_noabort("invalid section type");
goto fail;
}
/* align start of section */
offset = s->data_offset;
if (0 == strcmp(sh_name, ".stab")) {
stab_index = i;
goto no_align;
}
if (0 == strcmp(sh_name, ".stabstr")) {
stabstr_index = i;
goto no_align;
}
size = sh->sh_addralign - 1;
offset = (offset + size) & ~size;
if (sh->sh_addralign > s->sh_addralign)
s->sh_addralign = sh->sh_addralign;
s->data_offset = offset;
no_align:
sm_table[i].offset = offset;
sm_table[i].s = s;
/* concatenate sections */
size = sh->sh_size;
if (sh->sh_type != SHT_NOBITS) {
unsigned char *ptr;
lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
ptr = section_ptr_add(s, size);
read(fd, ptr, size);
} else {
s->data_offset += size;
}
next: ;
}
/* gr relocate stab strings */
if (stab_index && stabstr_index) {
Stab_Sym *a, *b;
unsigned o;
s = sm_table[stab_index].s;
a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
b = (Stab_Sym *)(s->data + s->data_offset);
o = sm_table[stabstr_index].offset;
while (a < b)
a->n_strx += o, a++;
}
/* second short pass to update sh_link and sh_info fields of new
sections */
for(i = 1; i < ehdr.e_shnum; i++) {
s = sm_table[i].s;
( run in 1.326 second using v1.01-cache-2.11-cpan-5735350b133 )