Add pic/pie support to i386
Some checks failed
build and test / test-x86_64-linux (push) Has been cancelled
build and test / test-x86_64-osx (push) Has been cancelled
build and test / test-aarch64-osx (push) Has been cancelled
build and test / test-x86_64-win32 (push) Has been cancelled
build and test / test-i386-win32 (push) Has been cancelled
build and test / test-armv7-linux (push) Has been cancelled
build and test / test-aarch64-linux (push) Has been cancelled
build and test / test-riscv64-linux (push) Has been cancelled

Some targets (netbsd) require that code is
compiled with -fPIE.

i386-gen.c:
Lot of changes to load/store code

i386-link.c:
Set PCRELATIVE_DLLPLT depending on configuration

tccelf.c:
Change some relocations and add get_pc_thunk.o

Makefile lib/Makefile lib/get_pc_thunk.S:
Add new file get_pc_thunk.o
This commit is contained in:
herman ten brugge 2025-12-30 20:15:35 +01:00
parent 5ce2c4b454
commit 9a7edb20d3
6 changed files with 332 additions and 60 deletions

View File

@ -372,7 +372,7 @@ IR = $(IM) mkdir -p $2 && cp -r $1/. $2
IM = @echo "-> $2 : $1" ;
BINCHECK = $(if $(wildcard $(PROGS) *-tcc$(EXESUF)),,@echo "Makefile: nothing found to install" && exit 1)
EXTRA_O = runmain.o run_nostdlib.o bt-exe.o bt-dll.o bt-log.o bcheck.o
EXTRA_O = runmain.o run_nostdlib.o bt-exe.o bt-dll.o bt-log.o bcheck.o get_pc_thunk.o
# install progs & libs
install-unx:

View File

@ -47,9 +47,13 @@ enum {
TREG_EDX,
TREG_EBX,
TREG_ST0,
TREG_ESP = 4
TREG_ESP = 4,
TREG_MEM = 0x20
};
#define REG_VALUE(reg) ((reg) & 7)
/* return registers for function */
#define REG_IRET TREG_EAX /* single word int return register */
#define REG_IRE2 TREG_EDX /* second word return register (for long long) */
@ -88,6 +92,14 @@ ST_DATA const char * const target_machine_defs =
/* define to 1/0 to [not] have EBX as 4th register */
#define USE_EBX 0
#if defined CONFIG_TCC_PIC
#if USE_EBX > 0
#error only USE_EBX or GOT_EBX can be set
#endif
#define GOT_EBX 1
#else
#define GOT_EBX 0
#endif
ST_DATA const int reg_classes[NB_REGS] = {
/* eax */ RC_INT | RC_EAX,
@ -171,6 +183,56 @@ ST_FUNC void gen_fill_nops(int bytes)
g(0x90);
}
#if defined CONFIG_TCC_PIC
static void get_pc_thunk(int r, int add)
{
static const char *pc_thunk_name[] = {
"__x86.get_pc_thunk.ax",
"__x86.get_pc_thunk.cx",
"__x86.get_pc_thunk.dx",
"__x86.get_pc_thunk.bx"
};
int pc_thunk;
if (nocode_wanted)
return;
r = REG_VALUE(r);
pc_thunk = set_elf_sym(tcc_state->symtab, 0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC),
ELFW(ST_VISIBILITY)(STV_HIDDEN),
SHN_UNDEF, pc_thunk_name[r]);
oad(0xe8, -4); /* call __x86.get_pc_thunk.rx */
put_elf_reloc(symtab_section, cur_text_section, ind - 4,
R_386_PC32, pc_thunk);
if (add) {
Sym label = {0};
label.type.t = VT_VOID | VT_STATIC;
put_extern_sym(&label, cur_text_section, ind, 0);
if (r == 0)
oad(0x05, 1); /* add _GLOBAL_OFFSET_TABLE_, %eax */
else
oad(0xc081 + r * 0x100, 2); /* add _GLOBAL_OFFSET_TABLE_, %erx */
greloc(cur_text_section, &label, ind - 4, R_386_GOTPC);
}
}
ST_FUNC void gen_gotpcrel(int r, Sym *sym, int c)
{
greloc(cur_text_section, sym, ind, R_386_GOT32X);
gen_le32(0);
if (c) {
r = REG_VALUE(r);
if (r == 0)
oad(0x05, c);
else
oad(0xc081 + r * 0x100, c);
}
}
#endif
/* generate jmp to a label */
#define gjmp2(instr,lbl) oad(instr,lbl)
@ -191,14 +253,40 @@ ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
/* generate a modrm reference. 'op_reg' contains the additional 3
opcode bits */
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
static void gen_modrm(int opc, int op_reg, int r, Sym *sym, int c)
{
op_reg = op_reg << 3;
#if defined CONFIG_TCC_PIC
int is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC);
#endif
op_reg = REG_VALUE(op_reg) << 3;
if ((r & VT_VALMASK) == VT_CONST) {
/* constant memory reference */
o(0x05 | op_reg);
gen_addr32(r, sym, c);
#if defined CONFIG_TCC_PIC
if (r & VT_SYM) {
get_pc_thunk(TREG_EBX, is_got);
o(opc);
o(0x83 | op_reg);
if (is_got)
gen_gotpcrel(r, sym, c);
else {
int off = 6;
off += opc & 0xff00 ? 1 : 0;
off += opc & 0xff0000 ? 1 : 0;
off += opc & 0xff000000 ? 1 : 0;
gen_addrpc32(r, sym, c + off);
}
}
else
#endif
{
o(opc);
o(0x05 | op_reg);
gen_addr32(r, sym, c);
}
} else if ((r & VT_VALMASK) == VT_LOCAL) {
o(opc);
/* currently, we use only ebp as base */
if (c == (char)c) {
/* short reference */
@ -207,7 +295,16 @@ static void gen_modrm(int op_reg, int r, Sym *sym, int c)
} else {
oad(0x85 | op_reg, c);
}
} else if ((r & VT_VALMASK) >= TREG_MEM) {
o(opc);
if (c) {
g(0x80 | op_reg | REG_VALUE(r));
gen_le32(c);
} else {
g(0x00 | op_reg | REG_VALUE(r));
}
} else {
o(opc);
g(0x00 | op_reg | (r & VT_VALMASK));
}
}
@ -215,7 +312,7 @@ static void gen_modrm(int op_reg, int r, Sym *sym, int c)
/* load 'r' from value 'sv' */
ST_FUNC void load(int r, SValue *sv)
{
int v, t, ft, fc, fr;
int v, t, ft, fc, fr, opc;
SValue v1;
fr = sv->r;
@ -224,6 +321,25 @@ ST_FUNC void load(int r, SValue *sv)
ft &= ~(VT_VOLATILE | VT_CONSTANT);
#ifndef TCC_TARGET_PE
#if defined CONFIG_TCC_PIC
/* we use indirect access via got */
if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) &&
(fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) {
/* use the result register as a temporal register */
int tr = r | TREG_MEM;
if (is_float(ft)) {
/* we cannot use float registers as a temporal register */
tr = get_reg(RC_INT) | TREG_MEM;
}
gen_modrm(0x8b, tr, fr, sv->sym, 0);
/* load from the temporal register */
fr = tr | VT_LVAL;
}
#endif
#endif
v = fr & VT_VALMASK;
if (fr & VT_LVAL) {
if (v == VT_LLOCAL) {
@ -237,34 +353,51 @@ ST_FUNC void load(int r, SValue *sv)
load(fr, &v1);
}
if ((ft & VT_BTYPE) == VT_FLOAT) {
o(0xd9); /* flds */
opc = 0xd9; /* flds */
r = 0;
} else if ((ft & VT_BTYPE) == VT_DOUBLE) {
o(0xdd); /* fldl */
opc = 0xdd; /* fldl */
r = 0;
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
o(0xdb); /* fldt */
opc = 0xdb; /* fldt */
r = 5;
} else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
o(0xbe0f); /* movsbl */
opc = 0xbe0f; /* movsbl */
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
o(0xb60f); /* movzbl */
opc = 0xb60f; /* movzbl */
} else if ((ft & VT_TYPE) == VT_SHORT) {
o(0xbf0f); /* movswl */
opc = 0xbf0f; /* movswl */
} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
o(0xb70f); /* movzwl */
opc = 0xb70f; /* movzwl */
} else {
o(0x8b); /* movl */
opc = 0x8b; /* movl */
}
gen_modrm(r, fr, sv->sym, fc);
gen_modrm(opc, r, fr, sv->sym, fc);
} else {
if (v == VT_CONST) {
o(0xb8 + r); /* mov $xx, r */
gen_addr32(fr, sv->sym, fc);
#if defined CONFIG_TCC_PIC
if (fr & VT_SYM) {
if (sv->sym->type.t & VT_STATIC) {
get_pc_thunk(r, 0);
o(0x808d | REG_VALUE(r) * 0x900); /* lea $xx(r), r */
gen_addrpc32(fr, sv->sym, fc + 6);
}
else {
get_pc_thunk(r, 1);
o(0x808b | REG_VALUE(r) * 0x900); /* mov $xx(r), r */
gen_gotpcrel(r, sv->sym, fc);
}
}
else
#endif
{
o(0xb8 + r); /* mov $xx, r */
gen_addr32(fr, sv->sym, fc);
}
} else if (v == VT_LOCAL) {
if (fc) {
o(0x8d); /* lea xxx(%ebp), r */
gen_modrm(r, VT_LOCAL, sv->sym, fc);
/* lea xxx(%ebp), r */
gen_modrm(0x8d, r, VT_LOCAL, sv->sym, fc);
} else {
o(0x89);
o(0xe8 + r); /* mov %ebp, r */
@ -290,39 +423,58 @@ ST_FUNC void load(int r, SValue *sv)
/* store register 'r' in lvalue 'v' */
ST_FUNC void store(int r, SValue *v)
{
int fr, bt, ft, fc;
int fr, bt, ft, fc, opc, pic = 0;
ft = v->type.t;
fc = v->c.i;
fr = v->r & VT_VALMASK;
ft &= ~(VT_VOLATILE | VT_CONSTANT);
bt = ft & VT_BTYPE;
#ifndef TCC_TARGET_PE
#if defined CONFIG_TCC_PIC
/* we need to access the variable via got */
if (fr == VT_CONST
&& (v->r & VT_SYM)
&& !(v->sym->type.t & VT_STATIC)) {
get_pc_thunk(TREG_EBX, 1);
o(0x9b8b); /* mov xx(%ebx),%ebx */
gen_gotpcrel(TREG_EBX, v->sym, v->c.i);
pic = 1;
}
#endif
#endif
/* XXX: incorrect if float reg to reg */
if (bt == VT_FLOAT) {
o(0xd9); /* fsts */
opc = 0xd9; /* fsts */
r = 2;
} else if (bt == VT_DOUBLE) {
o(0xdd); /* fstpl */
opc = 0xdd; /* fstpl */
r = 2;
} else if (bt == VT_LDOUBLE) {
o(0xc0d9); /* fld %st(0) */
o(0xdb); /* fstpt */
opc = 0xdbc0d9; /* fld %st(0), fstpt */
r = 7;
} else {
if (bt == VT_SHORT)
o(0x66);
if (bt == VT_BYTE || bt == VT_BOOL)
o(0x88);
else
o(0x89);
} else if (bt == VT_SHORT)
opc = 0x8966;
else if (bt == VT_BYTE || bt == VT_BOOL)
opc = 0x88;
else
opc = 0x89;
if (pic) {
o(opc);
o(3 + (r << 3));
}
if (fr == VT_CONST ||
fr == VT_LOCAL ||
(v->r & VT_LVAL)) {
gen_modrm(r, v->r, v->sym, fc);
else if (fr == VT_CONST ||
fr == VT_LOCAL ||
(v->r & VT_LVAL)) {
gen_modrm(opc, r, v->r, v->sym, fc);
} else if (fr != r) {
o(opc);
o(0xc0 + fr + r * 8); /* mov r, fr */
}
else
tcc_error("store problem");
}
static void gadd_sp(int val)
@ -341,8 +493,14 @@ static void gen_static_call(int v)
Sym *sym;
sym = external_helper_sym(v);
#if defined CONFIG_TCC_PIC
get_pc_thunk(TREG_EBX, 1);
oad(0xe8, -4);
greloc(cur_text_section, sym, ind-4, R_386_PC32);
greloc(cur_text_section, sym, ind - 4, R_386_PLT32);
#else
oad(0xe8, -4);
greloc(cur_text_section, sym, ind - 4, R_386_PC32);
#endif
}
#endif
@ -352,8 +510,20 @@ static void gcall_or_jmp(int is_jmp)
int r;
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) {
/* constant and relocation case */
#if defined CONFIG_TCC_PIC
if (vtop->sym->type.t & VT_STATIC) {
oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
greloc(cur_text_section, vtop->sym, ind - 4, R_386_PC32);
}
else {
get_pc_thunk(TREG_EBX, 1);
oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
greloc(cur_text_section, vtop->sym, ind - 4, R_386_PLT32);
}
#else
greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32);
oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
#endif
} else {
/* otherwise, indirect call */
r = gv(RC_INT);
@ -507,7 +677,7 @@ ST_FUNC void gfunc_call(int nb_args)
#ifdef TCC_TARGET_PE
#define FUNC_PROLOG_SIZE (10 + USE_EBX)
#else
#define FUNC_PROLOG_SIZE (9 + USE_EBX)
#define FUNC_PROLOG_SIZE (9 + (USE_EBX || GOT_EBX))
#endif
/* generate function prolog of type 't' */
@ -571,8 +741,8 @@ ST_FUNC void gfunc_prolog(Sym *func_sym)
if (param_index < fastcall_nb_regs) {
/* save FASTCALL register */
loc -= 4;
o(0x89); /* movl */
gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
/* movl */
gen_modrm(0x89, fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
param_addr = loc;
} else {
param_addr = addr;
@ -610,9 +780,9 @@ ST_FUNC void gfunc_epilog(void)
v = (-loc + 3) & -4;
#if USE_EBX
o(0x8b);
gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4));
gen_modrm(0x8b, TREG_EBX, VT_LOCAL, NULL, -(v+4));
#endif
o(0x5b * GOT_EBX); /* pop ebx */
o(0xc9); /* leave */
if (func_ret_sub == 0) {
@ -638,7 +808,7 @@ ST_FUNC void gfunc_epilog(void)
o(0x90); /* adjust to FUNC_PROLOG_SIZE */
#endif
}
o(0x53 * USE_EBX); /* push ebx */
o(0x53 * (USE_EBX || GOT_EBX)); /* push ebx */
ind = saved_ind;
}
@ -942,11 +1112,8 @@ ST_FUNC void gen_opf(int op)
fc = 0;
}
if ((ft & VT_BTYPE) == VT_DOUBLE)
o(0xdc);
else
o(0xd8);
gen_modrm(a, r, vtop->sym, fc);
gen_modrm((ft & VT_BTYPE) == VT_DOUBLE ? 0xdc : 0xd8,
a, r, vtop->sym, fc);
}
vtop--;
}
@ -1025,13 +1192,26 @@ ST_FUNC void gen_cvt_csti(int t)
/* increment tcov counter */
ST_FUNC void gen_increment_tcov (SValue *sv)
{
o(0x0583); /* addl $1, xxx */
greloc(cur_text_section, sv->sym, ind, R_386_32);
gen_le32(0);
int indir, rel, add1, add2;
#if defined CONFIG_TCC_PIC
get_pc_thunk(TREG_EBX, 0);
indir = 0x8300;
rel = R_386_PC32;
add1 = 2;
add2 = 13;
#else
indir = 0x0500;
rel = R_386_32;
add1 = 0;
add2 = 4;
#endif
o(0x0083 + indir); /* addl $1, xxx(%ebx) */
greloc(cur_text_section, sv->sym, ind, rel);
gen_le32(add1);
o(1);
o(0x1583); /* addcl $0, xxx */
greloc(cur_text_section, sv->sym, ind, R_386_32);
gen_le32(4);
o(0x1083 + indir); /* adcl $0, xxx(%ebx) */
greloc(cur_text_section, sv->sym, ind, rel);
gen_le32(add2);
g(0);
}
@ -1051,8 +1231,16 @@ static void gen_bounds_prolog(void)
func_bound_offset = lbounds_section->data_offset;
func_bound_ind = ind;
func_bound_add_epilog = 0;
#if defined CONFIG_TCC_PIC
oad(0xb8, 0); /* call to pc_thunk function */
oad(0x808d, 0); /* lea lbound section pointer */
oad(0xb8, 0); /* call to pc_thunk function */
oad(0xc381, 0); /* add _GLOBAL_OFFSET_TABLE_ */
oad(0xb8, 0); /* call to function */
#else
oad(0xb8, 0); /* lbound section pointer */
oad(0xb8, 0); /* call to function */
#endif
}
static void gen_bounds_epilog(void)
@ -1076,16 +1264,30 @@ static void gen_bounds_epilog(void)
if (offset_modified) {
saved_ind = ind;
ind = func_bound_ind;
#if defined CONFIG_TCC_PIC
get_pc_thunk(TREG_EAX, 0);
o(0x808d | TREG_EAX * 0x900); /* lea $xx(r), r */
greloc(cur_text_section, sym_data, ind, R_386_PC32);
gen_le32(2);
#else
greloc(cur_text_section, sym_data, ind + 1, R_386_32);
ind = ind + 5;
#endif
gen_static_call(TOK___bound_local_new);
ind = saved_ind;
}
/* generate bound check local freeing */
o(0x5250); /* save returned value, if any */
#if defined CONFIG_TCC_PIC
get_pc_thunk(TREG_EAX, 0);
o(0x808d | TREG_EAX * 0x900); /* lea $xx(r), r */
greloc(cur_text_section, sym_data, ind, R_386_PC32);
gen_le32(2);
#else
greloc(cur_text_section, sym_data, ind + 1, R_386_32);
oad(0xb8, 0); /* mov %eax, xxx */
#endif
gen_static_call(TOK___bound_local_delete);
o(0x585a); /* restore returned value, if any */
}
@ -1094,14 +1296,12 @@ static void gen_bounds_epilog(void)
/* Save the stack pointer onto the stack */
ST_FUNC void gen_vla_sp_save(int addr) {
/* mov %esp,addr(%ebp)*/
o(0x89);
gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
gen_modrm(0x89, TREG_ESP, VT_LOCAL, NULL, addr);
}
/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
o(0x8b);
gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
gen_modrm(0x8b, TREG_ESP, VT_LOCAL, NULL, addr);
}
/* Subtract from the stack pointer, and push the resulting value onto the stack */

View File

@ -15,7 +15,11 @@
#define ELF_START_ADDR 0x08048000
#define ELF_PAGE_SIZE 0x1000
#if defined CONFIG_TCC_PIC
#define PCRELATIVE_DLLPLT 1
#else
#define PCRELATIVE_DLLPLT 0
#endif
#define RELOCATE_DLLPLT 1
#else /* !TARGET_DEFS_ONLY */

View File

@ -35,7 +35,7 @@ endif
XFLAGS += -I$(TOP)
I386_O = libtcc1.o $(COMMON_O)
I386_O = libtcc1.o get_pc_thunk.o $(COMMON_O)
X86_64_O = libtcc1.o $(COMMON_O)
ARM_O = libtcc1.o armeabi.o armflush.o $(COMMON_O)
ARM64_O = lib-arm64.o $(COMMON_O)
@ -56,7 +56,7 @@ $(Cbt)WIN_O += bt-dll.o
$(Cbc)COMMON_O += bcheck.o
# not in libtcc1.a
EXTRA_O = runmain.o run_nostdlib.o bt-exe.o bt-dll.o bt-log.o bcheck.o
EXTRA_O = runmain.o run_nostdlib.o bt-exe.o bt-dll.o bt-log.o bcheck.o get_pc_thunk.o
OBJ-i386 = $(I386_O) $(LIN_O)
OBJ-x86_64 = $(X86_64_O) $(LIN_O)
@ -90,6 +90,9 @@ $(X)%.o : %.c $(TCC)
$(X)%.o : %.S $(TCC)
$S$(XCC) -c $< -o $@ $(XFLAGS)
$(TOP)/%.o : %.S $(TCC)
$S$(XCC) -c $< -o $@ $(XFLAGS)
$(TOP)/%.o : %.c $(TCC)
$S$(XCC) -c $< -o $@ $(XFLAGS)

39
lib/get_pc_thunk.S Normal file
View File

@ -0,0 +1,39 @@
/* ---------------------------------------------- */
/* get_pc_thunk.S */
#ifdef __leading_underscore
# define _(s) _##s
#else
# define _(s) s
#endif
/* ---------------------------------------------- */
.text
.globl _(__x86.get_pc_thunk.ax)
.hidden _(__x86.get_pc_thunk.ax)
_(__x86.get_pc_thunk.ax):
mov (%esp),%eax
ret
.size _(__x86.get_pc_thunk.ax), .-_(__x86.get_pc_thunk.ax)
.globl _(__x86.get_pc_thunk.bx)
.hidden _(__x86.get_pc_thunk.bx)
_(__x86.get_pc_thunk.bx):
mov (%esp),%ebx
ret
.size _(__x86.get_pc_thunk.bx), .-_(__x86.get_pc_thunk.bx)
.globl _(__x86.get_pc_thunk.cx)
.hidden _(__x86.get_pc_thunk.cx)
_(__x86.get_pc_thunk.cx):
mov (%esp),%ecx
ret
.size _(__x86.get_pc_thunk.cx), .-_(__x86.get_pc_thunk.cx)
.globl _(__x86.get_pc_thunk.dx)
.hidden _(__x86.get_pc_thunk.dx)
_(__x86.get_pc_thunk.dx):
mov (%esp),%edx
ret
.size _(__x86.get_pc_thunk.dx), .-_(__x86.get_pc_thunk.dx)

View File

@ -1237,6 +1237,17 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
break;
#if defined(TCC_TARGET_I386)
case R_386_PC32:
{
ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
/* Hidden defined symbols can and must be resolved locally.
We're misusing a PLT32 reloc for this, as that's always
resolved to its address even in shared libs. */
if (sym->st_shndx != SHN_UNDEF &&
ELFW(ST_VISIBILITY)(sym->st_other) == STV_HIDDEN) {
rel->r_info = ELFW(R_INFO)(sym_index, R_386_PLT32);
break;
}
}
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_PC32:
{
@ -1455,6 +1466,18 @@ redo:
continue;
}
#ifdef TCC_TARGET_I386
if ((type == R_386_PLT32 || type == R_386_PC32) &&
sym->st_shndx != SHN_UNDEF &&
(ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT ||
ELFW(ST_BIND)(sym->st_info) == STB_LOCAL ||
s1->output_type & TCC_OUTPUT_EXE)) {
if (pass != 0)
continue;
rel->r_info = ELFW(R_INFO)(sym_index, R_386_PC32);
continue;
}
#endif
#ifdef TCC_TARGET_X86_64
if ((type == R_X86_64_PLT32 || type == R_X86_64_PC32) &&
sym->st_shndx != SHN_UNDEF &&
@ -1817,6 +1840,9 @@ ST_FUNC void tcc_add_runtime(TCCState *s1)
tcc_add_dll(s1, TCC_LIBGCC, AFF_PRINT_ERROR);
}
#endif
#if defined CONFIG_TCC_PIC && defined TCC_TARGET_I386
tcc_add_support(s1, "get_pc_thunk.o");
#endif
#if defined TCC_TARGET_ARM && TARGETOS_FreeBSD
tcc_add_library(s1, "gcc_s"); // unwind code
#endif