diff --git a/macos.mak b/macos.mak new file mode 100644 index 00000000..20076aec --- /dev/null +++ b/macos.mak @@ -0,0 +1,5 @@ + +EXTRA_SRCS = vendor/c11threads/threads_posix.c +CFLAGS := -I vendor/c11threads $(CFLAGS) + +include makefile diff --git a/vendor/common/perf.c b/vendor/common/perf.c index e8ecadb1..2885a450 100644 --- a/vendor/common/perf.c +++ b/vendor/common/perf.c @@ -165,7 +165,7 @@ void cuikperf_thread_start(void) { #if _WIN32 uint32_t tid = GetCurrentThreadId(); #else - uint32_t tid = pthread_self(); + uint32_t tid = 0; #endif if (profiling) { @@ -201,7 +201,7 @@ void cuikperf_region_start(const char* label, const char* extra) { #if _WIN32 uint32_t tid = GetCurrentThreadId(); #else - uint32_t tid = pthread_self(); + uint32_t tid = 0; #endif spall_buffer_begin_args(&ctx, &muh_buffer, label, strlen(label), extra, extra ? strlen(extra) : 0, nanos, tid, 0); @@ -217,7 +217,7 @@ void cuikperf_region_end(void) { #if _WIN32 uint32_t tid = GetCurrentThreadId(); #else - uint32_t tid = pthread_self(); + uint32_t tid = 0; #endif spall_buffer_end_ex(&ctx, &muh_buffer, nanos, tid, 0); diff --git a/vendor/tb/src/builtins.h b/vendor/tb/src/builtins.h index 8c9c2b5b..6ba4499e 100644 --- a/vendor/tb/src/builtins.h +++ b/vendor/tb/src/builtins.h @@ -97,12 +97,20 @@ static TB_MultiplyResult tb_mul64x128(uint64_t a, uint64_t b) { } static uint64_t tb_div128(uint64_t ahi, uint64_t alo, uint64_t b) { - // We don't want 128 bit software division +#if defined(TB_HOST_X86_64) + // // We don't want 128 bit software division uint64_t d, e; __asm__("divq %[b]" : "=a"(d), "=d"(e) : [b] "r"(b), "a"(alo), "d"(ahi) ); - return d; +#else + __uint128_t x = 0; + x += alo; + x <<= 64; + x += ahi; + x /= b; + return x; +#endif } #endif diff --git a/vendor/tb/src/libtb.c b/vendor/tb/src/libtb.c index 3f431998..7f969442 100644 --- a/vendor/tb/src/libtb.c +++ b/vendor/tb/src/libtb.c @@ -81,7 +81,7 @@ void* tb_jit_stack_create(void) { return VirtualAlloc2(GetCurrentProcess(), NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, ¶m, 1); } #endif /* NTDDI_VERSION >= NTDDI_WIN10_RS4 */ -#elif defined(_POSIX_C_SOURCE) +#elif defined(_POSIX_C_SOURCE) || defined(__APPLE__) void* tb_platform_valloc(size_t size) { return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } diff --git a/vendor/tb/src/tb.c b/vendor/tb/src/tb.c index e5403be6..3cfc1e3d 100644 --- a/vendor/tb/src/tb.c +++ b/vendor/tb/src/tb.c @@ -102,6 +102,8 @@ char* tb__arena_strdup(TB_Module* m, ptrdiff_t len, const char* src) { TB_Module* tb_module_create_for_host(bool is_jit) { #if defined(TB_HOST_X86_64) TB_Arch arch = TB_ARCH_X86_64; + #elif defined(TB_HOST_ARM64) + TB_Arch arch = TB_ARCH_AARCH64; #else TB_Arch arch = TB_ARCH_UNKNOWN; tb_panic("tb_module_create_for_host: cannot detect host platform"); diff --git a/vendor/tcc/arm-asm.c b/vendor/tcc/arm-asm.c new file mode 100644 index 00000000..a6dec4c3 --- /dev/null +++ b/vendor/tcc/arm-asm.c @@ -0,0 +1,3235 @@ +/* + * ARM specific functions for TCC assembler + * + * Copyright (c) 2001, 2002 Fabrice Bellard + * Copyright (c) 2020 Danny Milosavljevic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TARGET_DEFS_ONLY + +#define CONFIG_TCC_ASM +#define NB_ASM_REGS 16 + +ST_FUNC void g(int c); +ST_FUNC void gen_le16(int c); +ST_FUNC void gen_le32(int c); + +/*************************************************************/ +#else +/*************************************************************/ + +#define USING_GLOBALS +#include "tcc.h" + +enum { + OPT_REG32, + OPT_REGSET32, + OPT_IM8, + OPT_IM8N, + OPT_IM32, + OPT_VREG32, + OPT_VREG64, +}; +#define OP_REG32 (1 << OPT_REG32) +#define OP_VREG32 (1 << OPT_VREG32) +#define OP_VREG64 (1 << OPT_VREG64) +#define OP_REG (OP_REG32 | OP_VREG32 | OP_VREG64) +#define OP_IM32 (1 << OPT_IM32) +#define OP_IM8 (1 << OPT_IM8) +#define OP_IM8N (1 << OPT_IM8N) +#define OP_REGSET32 (1 << OPT_REGSET32) + +typedef struct Operand { + uint32_t type; + union { + uint8_t reg; + uint16_t regset; + ExprValue e; + }; +} Operand; + +/* Read the VFP register referred to by token T. + If OK, returns its number. + If not OK, returns -1. */ +static int asm_parse_vfp_regvar(int t, int double_precision) +{ + if (double_precision) { + if (t >= TOK_ASM_d0 && t <= TOK_ASM_d15) + return t - TOK_ASM_d0; + } else { + if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) + return t - TOK_ASM_s0; + } + return -1; +} + +/* Parse a text containing operand and store the result in OP */ +static void parse_operand(TCCState *s1, Operand *op) +{ + ExprValue e; + int8_t reg; + uint16_t regset = 0; + + op->type = 0; + + if (tok == '{') { // regset literal + next(); // skip '{' + while (tok != '}' && tok != TOK_EOF) { + reg = asm_parse_regvar(tok); + if (reg == -1) { + expect("register"); + return; + } else + next(); // skip register name + + if ((1 << reg) < regset) + tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here"); + regset |= 1 << reg; + if (tok != ',') + break; + next(); // skip ',' + } + if (tok != '}') + expect("'}'"); + next(); // skip '}' + if (regset == 0) { + // ARM instructions don't support empty regset. + tcc_error("empty register list is not supported"); + } else { + op->type = OP_REGSET32; + op->regset = regset; + } + return; + } else if ((reg = asm_parse_regvar(tok)) != -1) { + next(); // skip register name + op->type = OP_REG32; + op->reg = (uint8_t) reg; + return; + } else if ((reg = asm_parse_vfp_regvar(tok, 0)) != -1) { + next(); // skip register name + op->type = OP_VREG32; + op->reg = (uint8_t) reg; + return; + } else if ((reg = asm_parse_vfp_regvar(tok, 1)) != -1) { + next(); // skip register name + op->type = OP_VREG64; + op->reg = (uint8_t) reg; + return; + } else if (tok == '#' || tok == '$') { + /* constant value */ + next(); // skip '#' or '$' + } + asm_expr(s1, &e); + op->type = OP_IM32; + op->e = e; + if (!op->e.sym) { + if ((int) op->e.v < 0 && (int) op->e.v >= -255) + op->type = OP_IM8N; + else if (op->e.v == (uint8_t)op->e.v) + op->type = OP_IM8; + } else + expect("operand"); +} + +/* XXX: make it faster ? */ +ST_FUNC void g(int c) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +ST_FUNC void gen_le16 (int i) +{ + g(i); + g(i>>8); +} + +ST_FUNC void gen_le32 (int i) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 4; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind++] = i & 0xFF; + cur_text_section->data[ind++] = (i >> 8) & 0xFF; + cur_text_section->data[ind++] = (i >> 16) & 0xFF; + cur_text_section->data[ind++] = (i >> 24) & 0xFF; +} + +ST_FUNC void gen_expr32(ExprValue *pe) +{ + gen_le32(pe->v); +} + +static uint32_t condition_code_of_token(int token) { + if (token < TOK_ASM_nopeq) { + expect("condition-enabled instruction"); + return 0; + } else + return (token - TOK_ASM_nopeq) & 15; +} + +static void asm_emit_opcode(int token, uint32_t opcode) { + gen_le32((condition_code_of_token(token) << 28) | opcode); +} + +static void asm_emit_unconditional_opcode(uint32_t opcode) { + gen_le32(opcode); +} + +static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, uint8_t cp_opcode, uint8_t cp_destination_register, uint8_t cp_n_operand_register, uint8_t cp_m_operand_register, uint8_t cp_opcode2, int inter_processor_transfer) +{ + uint32_t opcode = 0xe000000; + if (inter_processor_transfer) + opcode |= 1 << 4; + //assert(cp_opcode < 16); + opcode |= cp_opcode << 20; + //assert(cp_n_operand_register < 16); + opcode |= cp_n_operand_register << 16; + //assert(cp_destination_register < 16); + opcode |= cp_destination_register << 12; + //assert(cp_number < 16); + opcode |= cp_number << 8; + //assert(cp_information < 8); + opcode |= cp_opcode2 << 5; + //assert(cp_m_operand_register < 16); + opcode |= cp_m_operand_register; + asm_emit_unconditional_opcode((high_nibble << 28) | opcode); +} + +static void asm_nullary_opcode(int token) +{ + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_nopeq: + asm_emit_opcode(token, 0xd << 21); // mov r0, r0 + break; + case TOK_ASM_wfeeq: + asm_emit_opcode(token, 0x320f002); + case TOK_ASM_wfieq: + asm_emit_opcode(token, 0x320f003); + break; + default: + expect("nullary instruction"); + } +} + +static void asm_unary_opcode(TCCState *s1, int token) +{ + Operand op; + parse_operand(s1, &op); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_swieq: + case TOK_ASM_svceq: + if (op.type != OP_IM8) + expect("immediate 8-bit unsigned integer"); + else { + /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */ + asm_emit_opcode(token, (0xf << 24) | op.e.v); + } + break; + default: + expect("unary instruction"); + } +} + +static void asm_binary_opcode(TCCState *s1, int token) +{ + Operand ops[2]; + Operand rotation; + uint32_t encoded_rotation = 0; + uint64_t amount; + parse_operand(s1, &ops[0]); + if (tok == ',') + next(); + else + expect("','"); + parse_operand(s1, &ops[1]); + if (ops[0].type != OP_REG32) { + expect("(destination operand) register"); + return; + } + + if (ops[0].reg == 15) { + tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL)); + return; + } + + if (ops[0].reg == 13) + tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL)); + + if (ops[1].type != OP_REG32) { + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_movteq: + case TOK_ASM_movweq: + if (ops[1].type == OP_IM8 || ops[1].type == OP_IM8N || ops[1].type == OP_IM32) { + if (ops[1].e.v >= 0 && ops[1].e.v <= 0xFFFF) { + uint16_t immediate_value = ops[1].e.v; + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_movteq: + asm_emit_opcode(token, 0x3400000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF)); + break; + case TOK_ASM_movweq: + asm_emit_opcode(token, 0x3000000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF)); + break; + } + } else + expect("(source operand) immediate 16 bit value"); + } else + expect("(source operand) immediate"); + break; + default: + expect("(source operand) register"); + } + return; + } + + if (ops[1].reg == 15) { + tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL)); + return; + } + + if (ops[1].reg == 13) + tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL)); + + if (tok == ',') { + next(); // skip ',' + if (tok == TOK_ASM_ror) { + next(); // skip 'ror' + parse_operand(s1, &rotation); + if (rotation.type != OP_IM8) { + expect("immediate value for rotation"); + return; + } else { + amount = rotation.e.v; + switch (amount) { + case 8: + encoded_rotation = 1 << 10; + break; + case 16: + encoded_rotation = 2 << 10; + break; + case 24: + encoded_rotation = 3 << 10; + break; + default: + expect("'8' or '16' or '24'"); + return; + } + } + } + } + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_clzeq: + if (encoded_rotation) + tcc_error("clz does not support rotation"); + asm_emit_opcode(token, 0x16f0f10 | (ops[0].reg << 12) | ops[1].reg); + break; + case TOK_ASM_sxtbeq: + asm_emit_opcode(token, 0x6af0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation); + break; + case TOK_ASM_sxtheq: + asm_emit_opcode(token, 0x6bf0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation); + break; + case TOK_ASM_uxtbeq: + asm_emit_opcode(token, 0x6ef0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation); + break; + case TOK_ASM_uxtheq: + asm_emit_opcode(token, 0x6ff0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation); + break; + default: + expect("binary instruction"); + } +} + +static void asm_coprocessor_opcode(TCCState *s1, int token) { + uint8_t coprocessor; + Operand opcode1; + Operand opcode2; + uint8_t registers[3]; + unsigned int i; + uint8_t high_nibble; + uint8_t mrc = 0; + + if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) { + coprocessor = tok - TOK_ASM_p0; + next(); + } else { + expect("'p'"); + return; + } + + if (tok == ',') + next(); + else + expect("','"); + + parse_operand(s1, &opcode1); + if (opcode1.type != OP_IM8 || opcode1.e.v > 15) { + tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL)); + return; + } + + for (i = 0; i < 3; ++i) { + if (tok == ',') + next(); + else + expect("','"); + if (i == 0 && token != TOK_ASM_cdp2 && (ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mrceq || ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mcreq)) { + if (tok >= TOK_ASM_r0 && tok <= TOK_ASM_r15) { + registers[i] = tok - TOK_ASM_r0; + next(); + } else { + expect("'r'"); + return; + } + } else { + if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) { + registers[i] = tok - TOK_ASM_c0; + next(); + } else { + expect("'c'"); + return; + } + } + } + if (tok == ',') { + next(); + parse_operand(s1, &opcode2); + } else { + opcode2.type = OP_IM8; + opcode2.e.v = 0; + } + if (opcode2.type != OP_IM8 || opcode2.e.v > 15) { + tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL)); + return; + } + + if (token == TOK_ASM_cdp2) { + high_nibble = 0xF; + asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0); + return; + } else + high_nibble = condition_code_of_token(token); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_cdpeq: + asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0); + break; + case TOK_ASM_mrceq: + // opcode1 encoding changes! highest and lowest bit gone. + mrc = 1; + /* fallthrough */ + case TOK_ASM_mcreq: + // opcode1 encoding changes! highest and lowest bit gone. + if (opcode1.e.v > 7) { + tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token, NULL)); + return; + } + asm_emit_coprocessor_opcode(high_nibble, coprocessor, (opcode1.e.v << 1) | mrc, registers[0], registers[1], registers[2], opcode2.e.v, 1); + break; + default: + expect("known instruction"); + } +} + +/* data processing and single data transfer instructions only */ +#define ENCODE_RN(register_index) ((register_index) << 16) +#define ENCODE_RD(register_index) ((register_index) << 12) +#define ENCODE_SET_CONDITION_CODES (1 << 20) + +/* Note: For data processing instructions, "1" means immediate. + Note: For single data transfer instructions, "0" means immediate. */ +#define ENCODE_IMMEDIATE_FLAG (1 << 25) + +#define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4) +#define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5) +#define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5) +#define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5) +#define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5) +#define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8) +#define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7) + +static void asm_block_data_transfer_opcode(TCCState *s1, int token) +{ + uint32_t opcode; + int op0_exclam = 0; + Operand ops[2]; + int nb_ops = 1; + parse_operand(s1, &ops[0]); + if (tok == '!') { + op0_exclam = 1; + next(); // skip '!' + } + if (tok == ',') { + next(); // skip comma + parse_operand(s1, &ops[1]); + ++nb_ops; + } + if (nb_ops < 1) { + expect("at least one operand"); + return; + } else if (ops[nb_ops - 1].type != OP_REGSET32) { + expect("(last operand) register list"); + return; + } + + // block data transfer: 1 0 0 P U S W L << 20 (general case): + // operands: + // Rn: bits 19...16 base register + // Register List: bits 15...0 + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_pusheq: // TODO: Optimize 1-register case to: str ?, [sp, #-4]! + // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101 + // operands: + // Rn: base register + // Register List: bits 15...0 + if (nb_ops != 1) + expect("exactly one operand"); + else + asm_emit_opcode(token, (0x92d << 16) | ops[0].regset); // TODO: base register ? + break; + case TOK_ASM_popeq: // TODO: Optimize 1-register case to: ldr ?, [sp], #4 + // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101 + // operands: + // Rn: base register + // Register List: bits 15...0 + if (nb_ops != 1) + expect("exactly one operand"); + else + asm_emit_opcode(token, (0x8bd << 16) | ops[0].regset); // TODO: base register ? + break; + case TOK_ASM_stmdaeq: + case TOK_ASM_ldmdaeq: + case TOK_ASM_stmeq: + case TOK_ASM_ldmeq: + case TOK_ASM_stmiaeq: + case TOK_ASM_ldmiaeq: + case TOK_ASM_stmdbeq: + case TOK_ASM_ldmdbeq: + case TOK_ASM_stmibeq: + case TOK_ASM_ldmibeq: + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_stmdaeq: // post-decrement store + opcode = 0x80 << 20; + break; + case TOK_ASM_ldmdaeq: // post-decrement load + opcode = 0x81 << 20; + break; + case TOK_ASM_stmeq: // post-increment store + case TOK_ASM_stmiaeq: // post-increment store + opcode = 0x88 << 20; + break; + case TOK_ASM_ldmeq: // post-increment load + case TOK_ASM_ldmiaeq: // post-increment load + opcode = 0x89 << 20; + break; + case TOK_ASM_stmdbeq: // pre-decrement store + opcode = 0x90 << 20; + break; + case TOK_ASM_ldmdbeq: // pre-decrement load + opcode = 0x91 << 20; + break; + case TOK_ASM_stmibeq: // pre-increment store + opcode = 0x98 << 20; + break; + case TOK_ASM_ldmibeq: // pre-increment load + opcode = 0x99 << 20; + break; + default: + tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)"); + } + // operands: + // Rn: first operand + // Register List: lower bits + if (nb_ops != 2) + expect("exactly two operands"); + else if (ops[0].type != OP_REG32) + expect("(first operand) register"); + else { + if (op0_exclam) + opcode |= 1 << 21; // writeback + asm_emit_opcode(token, opcode | ENCODE_RN(ops[0].reg) | ops[1].regset); + } + break; + default: + expect("block data transfer instruction"); + } +} + +/* Parses shift directive and returns the parts that would have to be set in the opcode because of it. + Does not encode the actual shift amount. + It's not an error if there is no shift directive. + + NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT. + SHIFT: will be filled in with the shift operand to use, if any. */ +static uint32_t asm_parse_optional_shift(TCCState* s1, int* nb_shift, Operand* shift) +{ + uint32_t opcode = 0; + *nb_shift = 0; + switch (tok) { + case TOK_ASM_asl: + case TOK_ASM_lsl: + case TOK_ASM_asr: + case TOK_ASM_lsr: + case TOK_ASM_ror: + switch (tok) { + case TOK_ASM_asl: + /* fallthrough */ + case TOK_ASM_lsl: + opcode = ENCODE_BARREL_SHIFTER_MODE_LSL; + break; + case TOK_ASM_asr: + opcode = ENCODE_BARREL_SHIFTER_MODE_ASR; + break; + case TOK_ASM_lsr: + opcode = ENCODE_BARREL_SHIFTER_MODE_LSR; + break; + case TOK_ASM_ror: + opcode = ENCODE_BARREL_SHIFTER_MODE_ROR; + break; + } + next(); + parse_operand(s1, shift); + *nb_shift = 1; + break; + case TOK_ASM_rrx: + next(); + opcode = ENCODE_BARREL_SHIFTER_MODE_ROR; + break; + } + return opcode; +} + +static uint32_t asm_encode_shift(Operand* shift) +{ + uint64_t amount; + uint32_t operands = 0; + switch (shift->type) { + case OP_REG32: + if (shift->reg == 15) + tcc_error("r15 cannot be used as a shift count"); + else { + operands = ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER; + operands |= ENCODE_BARREL_SHIFTER_REGISTER(shift->reg); + } + break; + case OP_IM8: + amount = shift->e.v; + if (amount > 0 && amount < 32) + operands = ENCODE_BARREL_SHIFTER_IMMEDIATE(amount); + else + tcc_error("shift count out of range"); + break; + default: + tcc_error("unknown shift amount"); + } + return operands; +} + +static void asm_data_processing_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + int nb_ops; + Operand shift = {0}; + int nb_shift = 0; + uint32_t operands = 0; + + /* modulo 16 entries per instruction for the different condition codes */ + uint32_t opcode_idx = (ARM_INSTRUCTION_GROUP(token) - TOK_ASM_andeq) >> 4; + uint32_t opcode_nos = opcode_idx >> 1; // without "s"; "OpCode" in ARM docs + + for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ) { + if (tok == TOK_ASM_asl || tok == TOK_ASM_lsl || tok == TOK_ASM_lsr || tok == TOK_ASM_asr || tok == TOK_ASM_ror || tok == TOK_ASM_rrx) + break; + parse_operand(s1, &ops[nb_ops]); + ++nb_ops; + if (tok != ',') + break; + next(); // skip ',' + } + if (tok == ',') + next(); + operands |= asm_parse_optional_shift(s1, &nb_shift, &shift); + if (nb_ops < 2) + expect("at least two operands"); + else if (nb_ops == 2) { + memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2] + memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit + nb_ops = 3; + } else if (nb_ops == 3) { + if (opcode_nos == 0xd || opcode_nos == 0xf || opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) { // mov, mvn, cmp, cmn, tst, teq + tcc_error("'%s' cannot be used with three operands", get_tok_str(token, NULL)); + return; + } + } + if (nb_ops != 3) { + expect("two or three operands"); + return; + } else { + uint32_t opcode = 0; + uint32_t immediate_value; + uint8_t half_immediate_rotation; + if (nb_shift && shift.type == OP_REG32) { + if ((ops[0].type == OP_REG32 && ops[0].reg == 15) || + (ops[1].type == OP_REG32 && ops[1].reg == 15)) { + tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM"); + return; + } + } + + // data processing (general case): + // operands: + // Rn: bits 19...16 (first operand) + // Rd: bits 15...12 (destination) + // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate + // operator: + // bits 24...21: "OpCode"--see below + + /* operations in the token list are ordered by opcode */ + opcode = opcode_nos << 21; // drop "s" + if (ops[0].type != OP_REG32) + expect("(destination operand) register"); + else if (opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) // cmp, cmn, tst, teq + operands |= ENCODE_SET_CONDITION_CODES; // force S set, otherwise it's a completely different instruction. + else + operands |= ENCODE_RD(ops[0].reg); + if (ops[1].type != OP_REG32) + expect("(first source operand) register"); + else if (!(opcode_nos == 0xd || opcode_nos == 0xf)) // not: mov, mvn (those have only one source operand) + operands |= ENCODE_RN(ops[1].reg); + switch (ops[2].type) { + case OP_REG32: + operands |= ops[2].reg; + break; + case OP_IM8: + case OP_IM32: + operands |= ENCODE_IMMEDIATE_FLAG; + immediate_value = ops[2].e.v; + for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) { + if (immediate_value >= 0x00 && immediate_value < 0x100) + break; + // rotate left by two + immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30); + } + if (half_immediate_rotation >= 16) { + /* fallthrough */ + } else { + operands |= immediate_value; + operands |= half_immediate_rotation << 8; + break; + } + case OP_IM8N: // immediate negative value + operands |= ENCODE_IMMEDIATE_FLAG; + immediate_value = ops[2].e.v; + /* Instruction swapping: + 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult + 0011 = RSB - Rd:= Op2 - Op1 -> difficult + 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult + 1000 = TST - CC on: Op1 AND Op2 -> difficult + 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult + 1100 = ORR - Rd:= Op1 OR Op2 -> difficult + */ + switch (opcode_nos) { + case 0x0: // AND - Rd:= Op1 AND Op2 + opcode = 0xe << 21; // BIC + immediate_value = ~immediate_value; + break; + case 0x2: // SUB - Rd:= Op1 - Op2 + opcode = 0x4 << 21; // ADD + immediate_value = -immediate_value; + break; + case 0x4: // ADD - Rd:= Op1 + Op2 + opcode = 0x2 << 21; // SUB + immediate_value = -immediate_value; + break; + case 0x5: // ADC - Rd:= Op1 + Op2 + C + opcode = 0x6 << 21; // SBC + immediate_value = ~immediate_value; + break; + case 0x6: // SBC - Rd:= Op1 - Op2 + C + opcode = 0x5 << 21; // ADC + immediate_value = ~immediate_value; + break; + case 0xa: // CMP - CC on: Op1 - Op2 + opcode = 0xb << 21; // CMN + immediate_value = -immediate_value; + break; + case 0xb: // CMN - CC on: Op1 + Op2 + opcode = 0xa << 21; // CMP + immediate_value = -immediate_value; + break; + case 0xd: // MOV - Rd:= Op2 + opcode = 0xf << 21; // MVN + immediate_value = ~immediate_value; + break; + case 0xe: // BIC - Rd:= Op1 AND NOT Op2 + opcode = 0x0 << 21; // AND + immediate_value = ~immediate_value; + break; + case 0xf: // MVN - Rd:= NOT Op2 + opcode = 0xd << 21; // MOV + immediate_value = ~immediate_value; + break; + default: + tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token, NULL)); + } + for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) { + if (immediate_value >= 0x00 && immediate_value < 0x100) + break; + // rotate left by two + immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30); + } + if (half_immediate_rotation >= 16) { + immediate_value = ops[2].e.v; + tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value); + return; + } + operands |= immediate_value; + operands |= half_immediate_rotation << 8; + break; + default: + expect("(second source operand) register or immediate value"); + } + + if (nb_shift) { + if (operands & ENCODE_IMMEDIATE_FLAG) + tcc_error("immediate rotation not implemented"); + else + operands |= asm_encode_shift(&shift); + } + + /* S=0 and S=1 entries alternate one after another, in that order */ + opcode |= (opcode_idx & 1) ? ENCODE_SET_CONDITION_CODES : 0; + asm_emit_opcode(token, opcode | operands); + } +} + +static void asm_shift_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + int nb_ops; + int definitely_neutral = 0; + uint32_t opcode = 0xd << 21; // MOV + uint32_t operands = 0; + + for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) { + parse_operand(s1, &ops[nb_ops]); + if (tok != ',') { + ++nb_ops; + break; + } + next(); // skip ',' + } + if (nb_ops < 2) { + expect("at least two operands"); + return; + } + + if (ops[0].type != OP_REG32) { + expect("(destination operand) register"); + return; + } else + operands |= ENCODE_RD(ops[0].reg); + + if (nb_ops == 2) { + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_rrxseq: + opcode |= ENCODE_SET_CONDITION_CODES; + /* fallthrough */ + case TOK_ASM_rrxeq: + if (ops[1].type == OP_REG32) { + operands |= ops[1].reg; + operands |= ENCODE_BARREL_SHIFTER_MODE_ROR; + asm_emit_opcode(token, opcode | operands); + } else + tcc_error("(first source operand) register"); + return; + default: + memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2] + memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit + nb_ops = 3; + } + } + if (nb_ops != 3) { + expect("two or three operands"); + return; + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_lslseq: + case TOK_ASM_lsrseq: + case TOK_ASM_asrseq: + case TOK_ASM_rorseq: + opcode |= ENCODE_SET_CONDITION_CODES; + break; + } + + switch (ops[1].type) { + case OP_REG32: + operands |= ops[1].reg; + break; + case OP_IM8: + operands |= ENCODE_IMMEDIATE_FLAG; + operands |= ops[1].e.v; + tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token, NULL)); + return; + } + + switch (ops[2].type) { + case OP_REG32: + if ((ops[0].type == OP_REG32 && ops[0].reg == 15) || + (ops[1].type == OP_REG32 && ops[1].reg == 15)) { + tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM"); + } + operands |= asm_encode_shift(&ops[2]); + break; + case OP_IM8: + if (ops[2].e.v) + operands |= asm_encode_shift(&ops[2]); + else + definitely_neutral = 1; + break; + } + + if (!definitely_neutral) switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_lslseq: + case TOK_ASM_lsleq: + operands |= ENCODE_BARREL_SHIFTER_MODE_LSL; + break; + case TOK_ASM_lsrseq: + case TOK_ASM_lsreq: + operands |= ENCODE_BARREL_SHIFTER_MODE_LSR; + break; + case TOK_ASM_asrseq: + case TOK_ASM_asreq: + operands |= ENCODE_BARREL_SHIFTER_MODE_ASR; + break; + case TOK_ASM_rorseq: + case TOK_ASM_roreq: + operands |= ENCODE_BARREL_SHIFTER_MODE_ROR; + break; + default: + expect("shift instruction"); + return; + } + asm_emit_opcode(token, opcode | operands); +} + +static void asm_multiplication_opcode(TCCState *s1, int token) +{ + Operand ops[4]; + int nb_ops = 0; + uint32_t opcode = 0x90; + + for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) { + parse_operand(s1, &ops[nb_ops]); + if (tok != ',') { + ++nb_ops; + break; + } + next(); // skip ',' + } + if (nb_ops < 2) + expect("at least two operands"); + else if (nb_ops == 2) { + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_mulseq: + case TOK_ASM_muleq: + memcpy(&ops[2], &ops[0], sizeof(ops[1])); // ARM is actually like this! + break; + default: + expect("at least three operands"); + return; + } + nb_ops = 3; + } + + // multiply (special case): + // operands: + // Rd: bits 19...16 + // Rm: bits 3...0 + // Rs: bits 11...8 + // Rn: bits 15...12 + + if (ops[0].type == OP_REG32) + opcode |= ops[0].reg << 16; + else + expect("(destination operand) register"); + if (ops[1].type == OP_REG32) + opcode |= ops[1].reg; + else + expect("(first source operand) register"); + if (ops[2].type == OP_REG32) + opcode |= ops[2].reg << 8; + else + expect("(second source operand) register"); + if (nb_ops > 3) { + if (ops[3].type == OP_REG32) + opcode |= ops[3].reg << 12; + else + expect("(third source operand) register"); + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_mulseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_muleq: + if (nb_ops != 3) + expect("three operands"); + else { + asm_emit_opcode(token, opcode); + } + break; + case TOK_ASM_mlaseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_mlaeq: + if (nb_ops != 4) + expect("four operands"); + else { + opcode |= 1 << 21; // Accumulate + asm_emit_opcode(token, opcode); + } + break; + default: + expect("known multiplication instruction"); + } +} + +static void asm_long_multiplication_opcode(TCCState *s1, int token) +{ + Operand ops[4]; + int nb_ops = 0; + uint32_t opcode = 0x90 | (1 << 23); + + for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) { + parse_operand(s1, &ops[nb_ops]); + if (tok != ',') { + ++nb_ops; + break; + } + next(); // skip ',' + } + if (nb_ops != 4) { + expect("four operands"); + return; + } + + // long multiply (special case): + // operands: + // RdLo: bits 15...12 + // RdHi: bits 19...16 + // Rs: bits 11...8 + // Rm: bits 3...0 + + if (ops[0].type == OP_REG32) + opcode |= ops[0].reg << 12; + else + expect("(destination lo accumulator) register"); + if (ops[1].type == OP_REG32) + opcode |= ops[1].reg << 16; + else + expect("(destination hi accumulator) register"); + if (ops[2].type == OP_REG32) + opcode |= ops[2].reg; + else + expect("(first source operand) register"); + if (ops[3].type == OP_REG32) + opcode |= ops[3].reg << 8; + else + expect("(second source operand) register"); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_smullseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_smulleq: + opcode |= 1 << 22; // signed + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_umullseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_umulleq: + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_smlalseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_smlaleq: + opcode |= 1 << 22; // signed + opcode |= 1 << 21; // Accumulate + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_umlalseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_umlaleq: + opcode |= 1 << 21; // Accumulate + asm_emit_opcode(token, opcode); + break; + default: + expect("known long multiplication instruction"); + } +} + +static void asm_single_data_transfer_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + Operand strex_operand; + Operand shift; + int nb_shift = 0; + int exclam = 0; + int closed_bracket = 0; + int op2_minus = 0; + uint32_t opcode = 0; + // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged + // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4 + // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4 + + parse_operand(s1, &ops[0]); + if (ops[0].type == OP_REG32) + opcode |= ENCODE_RD(ops[0].reg); + else { + expect("(destination operand) register"); + return; + } + if (tok != ',') + expect("at least two arguments"); + else + next(); // skip ',' + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_strexbeq: + case TOK_ASM_strexeq: + parse_operand(s1, &strex_operand); + if (strex_operand.type != OP_REG32) { + expect("register"); + return; + } + if (tok != ',') + expect("at least three arguments"); + else + next(); // skip ',' + break; + } + + if (tok != '[') + expect("'['"); + else + next(); // skip '[' + + parse_operand(s1, &ops[1]); + if (ops[1].type == OP_REG32) + opcode |= ENCODE_RN(ops[1].reg); + else { + expect("(first source operand) register"); + return; + } + if (tok == ']') { + next(); + closed_bracket = 1; + // exclam = 1; // implicit in hardware; don't do it in software + } + if (tok == ',') { + next(); // skip ',' + if (tok == '-') { + op2_minus = 1; + next(); + } + parse_operand(s1, &ops[2]); + if (ops[2].type == OP_REG32) { + if (ops[2].reg == 15) { + tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL)); + return; + } + if (tok == ',') { + next(); + opcode |= asm_parse_optional_shift(s1, &nb_shift, &shift); + if (opcode == 0) + expect("shift directive, or no comma"); + } + } + } else { + // end of input expression in brackets--assume 0 offset + ops[2].type = OP_IM8; + ops[2].e.v = 0; + opcode |= 1 << 24; // add offset before transfer + } + if (!closed_bracket) { + if (tok != ']') + expect("']'"); + else + next(); // skip ']' + opcode |= 1 << 24; // add offset before transfer + if (tok == '!') { + exclam = 1; + next(); // skip '!' + } + } + + // single data transfer: 0 1 I P U B W L << 20 (general case): + // operands: + // Rd: destination operand [ok] + // Rn: first source operand [ok] + // Operand2: bits 11...0 [ok] + // I: immediate operand? [ok] + // P: Pre/post indexing is PRE: Add offset before transfer [ok] + // U: Up/down is up? (*adds* offset to base) [ok] + // B: Byte/word is byte? [ok] + // W: Write address back into base? [ok] + // L: Load/store is load? [ok] + if (exclam) + opcode |= 1 << 21; // write offset back into register + + if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) { + int v = ops[2].e.v; + if (op2_minus) + tcc_error("minus before '#' not supported for immediate values"); + if (v >= 0) { + opcode |= 1 << 23; // up + if (v >= 0x1000) + tcc_error("offset out of range for '%s'", get_tok_str(token, NULL)); + else + opcode |= v; + } else { // down + if (v <= -0x1000) + tcc_error("offset out of range for '%s'", get_tok_str(token, NULL)); + else + opcode |= -v; + } + } else if (ops[2].type == OP_REG32) { + if (!op2_minus) + opcode |= 1 << 23; // up + opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */ + opcode |= ops[2].reg; + } else + expect("register"); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_strbeq: + opcode |= 1 << 22; // B + /* fallthrough */ + case TOK_ASM_streq: + opcode |= 1 << 26; // Load/Store + if (nb_shift) + opcode |= asm_encode_shift(&shift); + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_ldrbeq: + opcode |= 1 << 22; // B + /* fallthrough */ + case TOK_ASM_ldreq: + opcode |= 1 << 20; // L + opcode |= 1 << 26; // Load/Store + if (nb_shift) + opcode |= asm_encode_shift(&shift); + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_strexbeq: + opcode |= 1 << 22; // B + /* fallthrough */ + case TOK_ASM_strexeq: + if ((opcode & 0xFFF) || nb_shift) { + tcc_error("neither offset nor shift allowed with 'strex'"); + return; + } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate + tcc_error("offset not allowed with 'strex'"); + return; + } + if ((opcode & (1 << 24)) == 0) { // add offset after transfer + tcc_error("adding offset after transfer not allowed with 'strex'"); + return; + } + + opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL + opcode |= strex_operand.reg; + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_ldrexbeq: + opcode |= 1 << 22; // B + /* fallthrough */ + case TOK_ASM_ldrexeq: + if ((opcode & 0xFFF) || nb_shift) { + tcc_error("neither offset nor shift allowed with 'ldrex'"); + return; + } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate + tcc_error("offset not allowed with 'ldrex'"); + return; + } + if ((opcode & (1 << 24)) == 0) { // add offset after transfer + tcc_error("adding offset after transfer not allowed with 'ldrex'"); + return; + } + opcode |= 1 << 20; // L + opcode |= 0x00f; + opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL + asm_emit_opcode(token, opcode); + break; + default: + expect("data transfer instruction"); + } +} + +// Note: Only call this using a VFP register if you know exactly what you are doing (i.e. cp_number is 10 or 11 and you are doing a vmov) +static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble, uint8_t cp_number, uint8_t CRd, const Operand* Rn, const Operand* offset, int offset_minus, int preincrement, int writeback, int long_transfer, int load) { + uint32_t opcode = 0x0; + opcode |= 1 << 26; // Load/Store + opcode |= 1 << 27; // coprocessor + + if (long_transfer) + opcode |= 1 << 22; // long transfer + + if (load) + opcode |= 1 << 20; // L + + opcode |= cp_number << 8; + + //assert(CRd < 16); + opcode |= ENCODE_RD(CRd); + + if (Rn->type != OP_REG32) { + expect("register"); + return; + } + //assert(Rn->reg < 16); + opcode |= ENCODE_RN(Rn->reg); + if (preincrement) + opcode |= 1 << 24; // add offset before transfer + + if (writeback) + opcode |= 1 << 21; // write offset back into register + + if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type == OP_IM32) { + int v = offset->e.v; + if (offset_minus) + tcc_error("minus before '#' not supported for immediate values"); + if (offset->type == OP_IM8N || v < 0) + v = -v; + else + opcode |= 1 << 23; // up + if (v & 3) { + tcc_error("immediate offset must be a multiple of 4"); + return; + } + v >>= 2; + if (v > 255) { + tcc_error("immediate offset must be between -1020 and 1020"); + return; + } + opcode |= v; + } else if (offset->type == OP_REG32) { + if (!offset_minus) + opcode |= 1 << 23; // up + opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */ + opcode |= offset->reg; + tcc_error("Using register offset to register address is not possible here"); + return; + } else if (offset->type == OP_VREG64) { + opcode |= 16; + opcode |= offset->reg; + } else + expect("immediate or register"); + + asm_emit_unconditional_opcode((high_nibble << 28) | opcode); +} + +// Almost exactly the same as asm_single_data_transfer_opcode. +// Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again. +static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + uint8_t coprocessor; + uint8_t coprocessor_destination_register; + int preincrement = 0; + int exclam = 0; + int closed_bracket = 0; + int op2_minus = 0; + int long_transfer = 0; + // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged + // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4 + // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4 + + if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) { + coprocessor = tok - TOK_ASM_p0; + next(); + } else { + expect("'c'"); + return; + } + + if (tok == ',') + next(); + else + expect("','"); + + if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) { + coprocessor_destination_register = tok - TOK_ASM_c0; + next(); + } else { + expect("'c'"); + return; + } + + if (tok == ',') + next(); + else + expect("','"); + + if (tok != '[') + expect("'['"); + else + next(); // skip '[' + + parse_operand(s1, &ops[1]); + if (ops[1].type != OP_REG32) { + expect("(first source operand) register"); + return; + } + if (tok == ']') { + next(); + closed_bracket = 1; + // exclam = 1; // implicit in hardware; don't do it in software + } + if (tok == ',') { + next(); // skip ',' + if (tok == '-') { + op2_minus = 1; + next(); + } + parse_operand(s1, &ops[2]); + if (ops[2].type == OP_REG32) { + if (ops[2].reg == 15) { + tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL)); + return; + } + } else if (ops[2].type == OP_VREG64) { + tcc_error("'%s' does not support VFP register operand", get_tok_str(token, NULL)); + return; + } + } else { + // end of input expression in brackets--assume 0 offset + ops[2].type = OP_IM8; + ops[2].e.v = 0; + preincrement = 1; // add offset before transfer + } + if (!closed_bracket) { + if (tok != ']') + expect("']'"); + else + next(); // skip ']' + preincrement = 1; // add offset before transfer + if (tok == '!') { + exclam = 1; + next(); // skip '!' + } + } + + // TODO: Support options. + + if (token == TOK_ASM_ldc2 || token == TOK_ASM_stc2 || token == TOK_ASM_ldc2l || token == TOK_ASM_stc2l) { + switch (token) { + case TOK_ASM_ldc2l: + long_transfer = 1; // long transfer + /* fallthrough */ + case TOK_ASM_ldc2: + asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1); + break; + case TOK_ASM_stc2l: + long_transfer = 1; // long transfer + /* fallthrough */ + case TOK_ASM_stc2: + asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0); + break; + } + } else switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_stcleq: + long_transfer = 1; + /* fallthrough */ + case TOK_ASM_stceq: + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0); + break; + case TOK_ASM_ldcleq: + long_transfer = 1; + /* fallthrough */ + case TOK_ASM_ldceq: + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1); + break; + default: + expect("coprocessor data transfer instruction"); + } +} + +#if defined(TCC_ARM_VFP) +#define CP_SINGLE_PRECISION_FLOAT 10 +#define CP_DOUBLE_PRECISION_FLOAT 11 + +static void asm_floating_point_single_data_transfer_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + uint8_t coprocessor = 0; + uint8_t coprocessor_destination_register = 0; + int long_transfer = 0; + // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged + // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4 + // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4 + + parse_operand(s1, &ops[0]); + if (ops[0].type == OP_VREG32) { + coprocessor = CP_SINGLE_PRECISION_FLOAT; + coprocessor_destination_register = ops[0].reg; + long_transfer = coprocessor_destination_register & 1; + coprocessor_destination_register >>= 1; + } else if (ops[0].type == OP_VREG64) { + coprocessor = CP_DOUBLE_PRECISION_FLOAT; + coprocessor_destination_register = ops[0].reg; + next(); + } else { + expect("floating point register"); + return; + } + + if (tok == ',') + next(); + else + expect("','"); + + if (tok != '[') + expect("'['"); + else + next(); // skip '[' + + parse_operand(s1, &ops[1]); + if (ops[1].type != OP_REG32) { + expect("(first source operand) register"); + return; + } + if (tok == ',') { + next(); // skip ',' + parse_operand(s1, &ops[2]); + if (ops[2].type != OP_IM8 && ops[2].type != OP_IM8N) { + expect("immediate offset"); + return; + } + } else { + // end of input expression in brackets--assume 0 offset + ops[2].type = OP_IM8; + ops[2].e.v = 0; + } + if (tok != ']') + expect("']'"); + else + next(); // skip ']' + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vldreq: + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 1); + break; + case TOK_ASM_vstreq: + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 0); + break; + default: + expect("floating point data transfer instruction"); + } +} + +static void asm_floating_point_block_data_transfer_opcode(TCCState *s1, int token) +{ + uint8_t coprocessor = 0; + int first_regset_register; + int last_regset_register; + uint8_t regset_item_count; + uint8_t extra_register_bit = 0; + int op0_exclam = 0; + int load = 0; + int preincrement = 0; + Operand ops[1]; + Operand offset; + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vpusheq: + case TOK_ASM_vpopeq: + ops[0].type = OP_REG32; + ops[0].reg = 13; // sp + op0_exclam = 1; + break; + default: + parse_operand(s1, &ops[0]); + if (tok == '!') { + op0_exclam = 1; + next(); // skip '!' + } + if (tok == ',') + next(); // skip comma + else { + expect("','"); + return; + } + } + + if (tok != '{') { + expect("'{'"); + return; + } + next(); // skip '{' + first_regset_register = asm_parse_vfp_regvar(tok, 1); + if ((first_regset_register = asm_parse_vfp_regvar(tok, 1)) != -1) { + coprocessor = CP_DOUBLE_PRECISION_FLOAT; + next(); + } else if ((first_regset_register = asm_parse_vfp_regvar(tok, 0)) != -1) { + coprocessor = CP_SINGLE_PRECISION_FLOAT; + next(); + } else { + expect("floating-point register"); + return; + } + + if (tok == '-') { + next(); + if ((last_regset_register = asm_parse_vfp_regvar(tok, coprocessor == CP_DOUBLE_PRECISION_FLOAT)) != -1) + next(); + else { + expect("floating-point register"); + return; + } + } else + last_regset_register = first_regset_register; + + if (last_regset_register < first_regset_register) { + tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here"); + return; + } + if (tok != '}') { + expect("'}'"); + return; + } + next(); // skip '}' + + // Note: 0 (one down) is not implemented by us regardless. + regset_item_count = last_regset_register - first_regset_register + 1; + if (coprocessor == CP_DOUBLE_PRECISION_FLOAT) + regset_item_count <<= 1; + else { + extra_register_bit = first_regset_register & 1; + first_regset_register >>= 1; + } + offset.type = OP_IM8; + offset.e.v = regset_item_count << 2; + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vstmeq: // post-increment store + case TOK_ASM_vstmiaeq: // post-increment store + break; + case TOK_ASM_vpopeq: + case TOK_ASM_vldmeq: // post-increment load + case TOK_ASM_vldmiaeq: // post-increment load + load = 1; + break; + case TOK_ASM_vldmdbeq: // pre-decrement load + load = 1; + /* fallthrough */ + case TOK_ASM_vpusheq: + case TOK_ASM_vstmdbeq: // pre-decrement store + offset.type = OP_IM8N; + offset.e.v = -offset.e.v; + preincrement = 1; + break; + default: + expect("floating point block data transfer instruction"); + return; + } + if (ops[0].type != OP_REG32) + expect("(first operand) register"); + else if (ops[0].reg == 15) + tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL)); + else if (!op0_exclam && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmiaeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmiaeq) + tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token, NULL)); + else + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, first_regset_register, &ops[0], &offset, 0, preincrement, op0_exclam, extra_register_bit, load); +} + +#define VMOV_FRACTIONAL_DIGITS 7 +#define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */ + +static uint32_t vmov_parse_fractional_part(const char* s) +{ + uint32_t result = 0; + int i; + for (i = 0; i < VMOV_FRACTIONAL_DIGITS; ++i) { + char c = *s; + result *= 10; + if (c >= '0' && c <= '9') { + result += (c - '0'); + ++s; + } + } + if (*s) + expect("decimal numeral"); + return result; +} + +static int vmov_linear_approx_index(uint32_t beginning, uint32_t end, uint32_t value) +{ + int i; + uint32_t k; + uint32_t xvalue; + + k = (end - beginning)/16; + for (xvalue = beginning, i = 0; i < 16; ++i, xvalue += k) { + if (value == xvalue) + return i; + } + //assert(0); + return -1; +} + +static uint32_t vmov_parse_immediate_value() { + uint32_t value; + unsigned long integral_value; + const char *p; + + if (tok != TOK_PPNUM) { + expect("immediate value"); + return 0; + } + p = tokc.str.data; + errno = 0; + integral_value = strtoul(p, (char **)&p, 0); + + if (errno || integral_value >= 32) { + tcc_error("invalid floating-point immediate value"); + return 0; + } + + value = (uint32_t) integral_value * VMOV_ONE; + if (*p == '.') { + ++p; + value += vmov_parse_fractional_part(p); + } + next(); + return value; +} + +static uint8_t vmov_encode_immediate_value(uint32_t value) +{ + uint32_t limit; + uint32_t end = 0; + uint32_t beginning = 0; + int r = -1; + int n; + int i; + + limit = 32 * VMOV_ONE; + for (i = 0; i < 8; ++i) { + if (value < limit) { + end = limit; + limit >>= 1; + beginning = limit; + r = i; + } else + limit >>= 1; + } + if (r == -1 || value < beginning || value > end) { + tcc_error("invalid decimal number for vmov: %d", value); + return 0; + } + n = vmov_linear_approx_index(beginning, end, value); + return n | (((3 - r) & 0x7) << 4); +} + +// Not standalone. +static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState *s1, int token, uint8_t coprocessor, uint8_t CRd) { + uint8_t opcode1 = 0; + uint8_t opcode2 = 0; + uint8_t operands[3] = {0, 0, 0}; + uint32_t immediate_value = 0; + int op_minus = 0; + uint8_t code; + + operands[0] = CRd; + + if (tok == '#' || tok == '$') { + next(); + } + if (tok == '-') { + op_minus = 1; + next(); + } + immediate_value = vmov_parse_immediate_value(); + + opcode1 = 11; // "Other" instruction + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vcmpeq_f32: + case TOK_ASM_vcmpeq_f64: + opcode2 = 2; + operands[1] = 5; + if (immediate_value) { + expect("Immediate value 0"); + return; + } + break; + case TOK_ASM_vcmpeeq_f32: + case TOK_ASM_vcmpeeq_f64: + opcode2 = 6; + operands[1] = 5; + if (immediate_value) { + expect("Immediate value 0"); + return; + } + break; + case TOK_ASM_vmoveq_f32: + case TOK_ASM_vmoveq_f64: + opcode2 = 0; + if (op_minus) + operands[1] = 0x8; + else + operands[1] = 0x0; + code = vmov_encode_immediate_value(immediate_value); + operands[1] |= code >> 4; + operands[2] = code & 0xF; + break; + default: + expect("known floating point with immediate instruction"); + return; + } + + if (coprocessor == CP_SINGLE_PRECISION_FLOAT) { + if (operands[0] & 1) + opcode1 |= 4; + operands[0] >>= 1; + } + + asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, operands[0], operands[1], operands[2], opcode2, 0); +} + +static void asm_floating_point_reg_arm_reg_transfer_opcode_tail(TCCState *s1, int token, int coprocessor, int nb_arm_regs, int nb_ops, Operand ops[3]) { + uint8_t opcode1 = 0; + uint8_t opcode2 = 0; + switch (coprocessor) { + case CP_SINGLE_PRECISION_FLOAT: + // "vmov.f32 r2, s3" or "vmov.f32 s3, r2" + if (nb_ops != 2 || nb_arm_regs != 1) { + tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands"); + return; + } + if (ops[0].type != OP_REG32) { // determine mode: load or store + // need to swap operands 0 and 1 + memcpy(&ops[2], &ops[1], sizeof(ops[2])); + memcpy(&ops[1], &ops[0], sizeof(ops[1])); + memcpy(&ops[0], &ops[2], sizeof(ops[0])); + } else + opcode1 |= 1; + + if (ops[1].type == OP_VREG32) { + if (ops[1].reg & 1) + opcode2 |= 4; + ops[1].reg >>= 1; + } + + if (ops[0].type == OP_VREG32) { + if (ops[0].reg & 1) + opcode1 |= 4; + ops[0].reg >>= 1; + } + + asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, 0x10, opcode2, 0); + break; + case CP_DOUBLE_PRECISION_FLOAT: + if (nb_ops != 3 || nb_arm_regs != 2) { + tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands"); + return; + } + // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1" + if (ops[0].type == OP_VREG64) { + if (ops[2].type == OP_REG32) { + Operand temp; + // need to rotate operand list to the left + memcpy(&temp, &ops[0], sizeof(temp)); + memcpy(&ops[0], &ops[1], sizeof(ops[0])); + memcpy(&ops[1], &ops[2], sizeof(ops[1])); + memcpy(&ops[2], &temp, sizeof(ops[2])); + } else { + tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands"); + return; + } + } else if (ops[0].type != OP_REG32 || ops[1].type != OP_REG32 || ops[2].type != OP_VREG64) { + tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands"); + return; + } else { + opcode1 |= 1; + } + asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, ops[0].reg, &ops[1], &ops[2], 0, 0, 0, 1, opcode1); + break; + default: + tcc_internal_error("unknown coprocessor"); + } +} + +static void asm_floating_point_vcvt_data_processing_opcode(TCCState *s1, int token) { + uint8_t coprocessor = 0; + Operand ops[3]; + uint8_t opcode1 = 11; + uint8_t opcode2 = 2; + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vcvtreq_s32_f64: + case TOK_ASM_vcvtreq_u32_f64: + case TOK_ASM_vcvteq_s32_f64: + case TOK_ASM_vcvteq_u32_f64: + case TOK_ASM_vcvteq_f64_s32: + case TOK_ASM_vcvteq_f64_u32: + case TOK_ASM_vcvteq_f32_f64: + coprocessor = CP_DOUBLE_PRECISION_FLOAT; + break; + case TOK_ASM_vcvtreq_s32_f32: + case TOK_ASM_vcvtreq_u32_f32: + case TOK_ASM_vcvteq_s32_f32: + case TOK_ASM_vcvteq_u32_f32: + case TOK_ASM_vcvteq_f32_s32: + case TOK_ASM_vcvteq_f32_u32: + case TOK_ASM_vcvteq_f64_f32: + coprocessor = CP_SINGLE_PRECISION_FLOAT; + break; + default: + tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token, NULL)); + return; + } + + parse_operand(s1, &ops[0]); + ops[1].type = OP_IM8; + ops[1].e.v = 8; + /* floating-point -> integer */ + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vcvtreq_s32_f32: + case TOK_ASM_vcvtreq_s32_f64: + case TOK_ASM_vcvteq_s32_f32: + case TOK_ASM_vcvteq_s32_f64: + ops[1].e.v |= 1; // signed + /* fall through */ + case TOK_ASM_vcvteq_u32_f32: + case TOK_ASM_vcvteq_u32_f64: + case TOK_ASM_vcvtreq_u32_f32: + case TOK_ASM_vcvtreq_u32_f64: + ops[1].e.v |= 4; // to_integer (opc2) + break; + /* floating-point size conversion */ + case TOK_ASM_vcvteq_f64_f32: + case TOK_ASM_vcvteq_f32_f64: + ops[1].e.v = 7; + break; + } + + if (tok == ',') + next(); + else + expect("','"); + parse_operand(s1, &ops[2]); + + switch (ARM_INSTRUCTION_GROUP(token)) { + /* floating-point -> integer */ + case TOK_ASM_vcvteq_s32_f32: + case TOK_ASM_vcvteq_s32_f64: + case TOK_ASM_vcvteq_u32_f32: + case TOK_ASM_vcvteq_u32_f64: + opcode2 |= 4; // round_zero + break; + + /* integer -> floating-point */ + case TOK_ASM_vcvteq_f64_s32: + case TOK_ASM_vcvteq_f32_s32: + opcode2 |= 4; // signed--special + break; + + /* floating-point size conversion */ + case TOK_ASM_vcvteq_f64_f32: + case TOK_ASM_vcvteq_f32_f64: + opcode2 |= 4; // always set + break; + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vcvteq_f64_u32: + case TOK_ASM_vcvteq_f64_s32: + case TOK_ASM_vcvteq_f64_f32: + if (ops[0].type == OP_VREG64 && ops[2].type == OP_VREG32) { + } else { + expect("d, s"); + return; + } + break; + default: + if (coprocessor == CP_SINGLE_PRECISION_FLOAT) { + if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG32) { + } else { + expect("s, s"); + return; + } + } else if (coprocessor == CP_DOUBLE_PRECISION_FLOAT) { + if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG64) { + } else { + expect("s, d"); + return; + } + } + } + + if (ops[2].type == OP_VREG32) { + if (ops[2].reg & 1) + opcode2 |= 1; + ops[2].reg >>= 1; + } + if (ops[0].type == OP_VREG32) { + if (ops[0].reg & 1) + opcode1 |= 4; + ops[0].reg >>= 1; + } + asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0); +} + +static void asm_floating_point_data_processing_opcode(TCCState *s1, int token) { + uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT; + uint8_t opcode1 = 0; + uint8_t opcode2 = 0; // (0 || 2) | register selection + Operand ops[3]; + uint8_t nb_ops = 0; + int vmov = 0; + int nb_arm_regs = 0; + +/* TODO: + Instruction opcode opcode2 Reason + ============================================================= + - 1?00 ?1? Undefined + VFNMS 1?01 ?0? Must be unconditional + VFNMA 1?01 ?1? Must be unconditional + VFMA 1?10 ?0? Must be unconditional + VFMS 1?10 ?1? Must be unconditional + + VMOV Fd, Fm + VMOV Sn, Sm, Rd, Rn + VMOV Rd, Rn, Sn, Sm + VMOV Dn[0], Rd + VMOV Rd, Dn[0] + VMOV Dn[1], Rd + VMOV Rd, Dn[1] +*/ + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vmlaeq_f64: + case TOK_ASM_vmlseq_f64: + case TOK_ASM_vnmlseq_f64: + case TOK_ASM_vnmlaeq_f64: + case TOK_ASM_vmuleq_f64: + case TOK_ASM_vnmuleq_f64: + case TOK_ASM_vaddeq_f64: + case TOK_ASM_vsubeq_f64: + case TOK_ASM_vdiveq_f64: + case TOK_ASM_vnegeq_f64: + case TOK_ASM_vabseq_f64: + case TOK_ASM_vsqrteq_f64: + case TOK_ASM_vcmpeq_f64: + case TOK_ASM_vcmpeeq_f64: + case TOK_ASM_vmoveq_f64: + coprocessor = CP_DOUBLE_PRECISION_FLOAT; + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vmoveq_f32: + case TOK_ASM_vmoveq_f64: + vmov = 1; + break; + } + + for (nb_ops = 0; nb_ops < 3; ) { + // Note: Necessary because parse_operand can't parse decimal numerals. + if (nb_ops == 1 && (tok == '#' || tok == '$' || tok == TOK_PPNUM || tok == '-')) { + asm_floating_point_immediate_data_processing_opcode_tail(s1, token, coprocessor, ops[0].reg); + return; + } + parse_operand(s1, &ops[nb_ops]); + if (vmov && ops[nb_ops].type == OP_REG32) { + ++nb_arm_regs; + } else if (ops[nb_ops].type == OP_VREG32) { + if (coprocessor != CP_SINGLE_PRECISION_FLOAT) { + expect("'s'"); + return; + } + } else if (ops[nb_ops].type == OP_VREG64) { + if (coprocessor != CP_DOUBLE_PRECISION_FLOAT) { + expect("'d'"); + return; + } + } else { + expect("floating point register"); + return; + } + ++nb_ops; + if (tok == ',') + next(); + else + break; + } + + if (nb_arm_regs == 0) { + if (nb_ops == 2) { // implicit + memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2] + memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit + nb_ops = 3; + } + if (nb_ops < 3) { + tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token, NULL), nb_ops); + return; + } + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vmlaeq_f32: + case TOK_ASM_vmlaeq_f64: + opcode1 = 0; + opcode2 = 0; + break; + case TOK_ASM_vmlseq_f32: + case TOK_ASM_vmlseq_f64: + opcode1 = 0; + opcode2 = 2; + break; + case TOK_ASM_vnmlseq_f32: + case TOK_ASM_vnmlseq_f64: + opcode1 = 1; + opcode2 = 0; + break; + case TOK_ASM_vnmlaeq_f32: + case TOK_ASM_vnmlaeq_f64: + opcode1 = 1; + opcode2 = 2; + break; + case TOK_ASM_vmuleq_f32: + case TOK_ASM_vmuleq_f64: + opcode1 = 2; + opcode2 = 0; + break; + case TOK_ASM_vnmuleq_f32: + case TOK_ASM_vnmuleq_f64: + opcode1 = 2; + opcode2 = 2; + break; + case TOK_ASM_vaddeq_f32: + case TOK_ASM_vaddeq_f64: + opcode1 = 3; + opcode2 = 0; + break; + case TOK_ASM_vsubeq_f32: + case TOK_ASM_vsubeq_f64: + opcode1 = 3; + opcode2 = 2; + break; + case TOK_ASM_vdiveq_f32: + case TOK_ASM_vdiveq_f64: + opcode1 = 8; + opcode2 = 0; + break; + case TOK_ASM_vnegeq_f32: + case TOK_ASM_vnegeq_f64: + opcode1 = 11; // Other" instruction + opcode2 = 2; + ops[1].type = OP_IM8; + ops[1].e.v = 1; + break; + case TOK_ASM_vabseq_f32: + case TOK_ASM_vabseq_f64: + opcode1 = 11; // "Other" instruction + opcode2 = 6; + ops[1].type = OP_IM8; + ops[1].e.v = 0; + break; + case TOK_ASM_vsqrteq_f32: + case TOK_ASM_vsqrteq_f64: + opcode1 = 11; // "Other" instruction + opcode2 = 6; + ops[1].type = OP_IM8; + ops[1].e.v = 1; + break; + case TOK_ASM_vcmpeq_f32: + case TOK_ASM_vcmpeq_f64: + opcode1 = 11; // "Other" instruction + opcode2 = 2; + ops[1].type = OP_IM8; + ops[1].e.v = 4; + break; + case TOK_ASM_vcmpeeq_f32: + case TOK_ASM_vcmpeeq_f64: + opcode1 = 11; // "Other" instruction + opcode2 = 6; + ops[1].type = OP_IM8; + ops[1].e.v = 4; + break; + case TOK_ASM_vmoveq_f32: + case TOK_ASM_vmoveq_f64: + if (nb_arm_regs > 0) { // vmov.f32 r2, s3 or similar + asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1, token, coprocessor, nb_arm_regs, nb_ops, ops); + return; + } else { + opcode1 = 11; // "Other" instruction + opcode2 = 2; + ops[1].type = OP_IM8; + ops[1].e.v = 0; + } + break; + default: + expect("known floating point instruction"); + return; + } + + if (coprocessor == CP_SINGLE_PRECISION_FLOAT) { + if (ops[2].type == OP_VREG32) { + if (ops[2].reg & 1) + opcode2 |= 1; + ops[2].reg >>= 1; + } + + if (ops[1].type == OP_VREG32) { + if (ops[1].reg & 1) + opcode2 |= 4; + ops[1].reg >>= 1; + } + + if (ops[0].type == OP_VREG32) { + if (ops[0].reg & 1) + opcode1 |= 4; + ops[0].reg >>= 1; + } + } + + asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0); +} + +static int asm_parse_vfp_status_regvar(int t) +{ + switch (t) { + case TOK_ASM_fpsid: + return 0; + case TOK_ASM_fpscr: + return 1; + case TOK_ASM_fpexc: + return 8; + default: + return -1; + } +} + +static void asm_floating_point_status_register_opcode(TCCState* s1, int token) +{ + uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT; + uint8_t opcode; + int vfp_sys_reg = -1; + Operand arm_operand; + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_vmrseq: + opcode = 0xf; + if (tok == TOK_ASM_apsr_nzcv) { + arm_operand.type = OP_REG32; + arm_operand.reg = 15; // not PC + next(); // skip apsr_nzcv + } else { + parse_operand(s1, &arm_operand); + if (arm_operand.type == OP_REG32 && arm_operand.reg == 15) { + tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL)); + return; + } + } + + if (tok != ',') + expect("','"); + else + next(); // skip ',' + vfp_sys_reg = asm_parse_vfp_status_regvar(tok); + next(); // skip vfp sys reg + if (arm_operand.type == OP_REG32 && arm_operand.reg == 15 && vfp_sys_reg != 1) { + tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token, NULL)); + return; + } + break; + case TOK_ASM_vmsreq: + opcode = 0xe; + vfp_sys_reg = asm_parse_vfp_status_regvar(tok); + next(); // skip vfp sys reg + if (tok != ',') + expect("','"); + else + next(); // skip ',' + parse_operand(s1, &arm_operand); + if (arm_operand.type == OP_REG32 && arm_operand.reg == 15) { + tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL)); + return; + } + break; + default: + expect("floating point status register instruction"); + return; + } + if (vfp_sys_reg == -1) { + expect("VFP system register"); + return; + } + if (arm_operand.type != OP_REG32) { + expect("ARM register"); + return; + } + asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode, arm_operand.reg, vfp_sys_reg, 0x10, 0, 0); +} + +#endif + +static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + int exclam = 0; + int closed_bracket = 0; + int op2_minus = 0; + uint32_t opcode = (1 << 7) | (1 << 4); + + /* Note: + The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form. + The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller. + Also, the combination (P=0, W=1) is unpredictable here. + The immediate flag has moved to bit index 22--and its meaning has flipped. + The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0 + bit 26 (Load/Store instruction) is unset here. + bits 7 and 4 are set here. */ + + // Here: 0 0 0 P U I W L << 20 + // [compare single data transfer: 0 1 I P U B W L << 20] + + parse_operand(s1, &ops[0]); + if (ops[0].type == OP_REG32) + opcode |= ENCODE_RD(ops[0].reg); + else { + expect("(destination operand) register"); + return; + } + if (tok != ',') + expect("at least two arguments"); + else + next(); // skip ',' + + if (tok != '[') + expect("'['"); + else + next(); // skip '[' + + parse_operand(s1, &ops[1]); + if (ops[1].type == OP_REG32) + opcode |= ENCODE_RN(ops[1].reg); + else { + expect("(first source operand) register"); + return; + } + if (tok == ']') { + next(); + closed_bracket = 1; + // exclam = 1; // implicit in hardware; don't do it in software + } + if (tok == ',') { + next(); // skip ',' + if (tok == '-') { + op2_minus = 1; + next(); + } + parse_operand(s1, &ops[2]); + } else { + // end of input expression in brackets--assume 0 offset + ops[2].type = OP_IM8; + ops[2].e.v = 0; + opcode |= 1 << 24; // add offset before transfer + } + if (!closed_bracket) { + if (tok != ']') + expect("']'"); + else + next(); // skip ']' + opcode |= 1 << 24; // add offset before transfer + if (tok == '!') { + exclam = 1; + next(); // skip '!' + } + } + + if (exclam) { + if ((opcode & (1 << 24)) == 0) { + tcc_error("result of '%s' would be unpredictable here", get_tok_str(token, NULL)); + return; + } + opcode |= 1 << 21; // write offset back into register + } + + if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) { + int v = ops[2].e.v; + if (op2_minus) + tcc_error("minus before '#' not supported for immediate values"); + if (v >= 0) { + opcode |= 1 << 23; // up + if (v >= 0x100) + tcc_error("offset out of range for '%s'", get_tok_str(token, NULL)); + else { + // bits 11...8: immediate hi nibble + // bits 3...0: immediate lo nibble + opcode |= (v & 0xF0) << 4; + opcode |= v & 0xF; + } + } else { // down + if (v <= -0x100) + tcc_error("offset out of range for '%s'", get_tok_str(token, NULL)); + else { + v = -v; + // bits 11...8: immediate hi nibble + // bits 3...0: immediate lo nibble + opcode |= (v & 0xF0) << 4; + opcode |= v & 0xF; + } + } + opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG; + } else if (ops[2].type == OP_REG32) { + if (!op2_minus) + opcode |= 1 << 23; // up + opcode |= ops[2].reg; + } else + expect("register"); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_ldrsheq: + opcode |= 1 << 5; // halfword, not byte + /* fallthrough */ + case TOK_ASM_ldrsbeq: + opcode |= 1 << 6; // sign extend + opcode |= 1 << 20; // L + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_ldrheq: + opcode |= 1 << 5; // halfword, not byte + opcode |= 1 << 20; // L + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_strheq: + opcode |= 1 << 5; // halfword, not byte + asm_emit_opcode(token, opcode); + break; + } +} + +/* Note: almost dupe of encbranch in arm-gen.c */ +static uint32_t encbranchoffset(int pos, int addr, int fail) +{ + addr-=pos+8; + addr/=4; + if(addr>=0x7fffff || addr<-0x800000) { + if(fail) + tcc_error("branch offset is too far"); + return 0; + } + return /*not 0x0A000000|*/(addr&0xffffff); +} + +static void asm_branch_opcode(TCCState *s1, int token) +{ + int jmp_disp = 0; + Operand op; + ExprValue e; + ElfSym *esym; + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_beq: + case TOK_ASM_bleq: + asm_expr(s1, &e); + esym = elfsym(e.sym); + if (!esym || esym->st_shndx != cur_text_section->sh_num) { + tcc_error("invalid branch target"); + return; + } + jmp_disp = encbranchoffset(ind, e.v + esym->st_value, 1); + break; + default: + parse_operand(s1, &op); + break; + } + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_beq: + asm_emit_opcode(token, (0xa << 24) | (jmp_disp & 0xffffff)); + break; + case TOK_ASM_bleq: + asm_emit_opcode(token, (0xb << 24) | (jmp_disp & 0xffffff)); + break; + case TOK_ASM_bxeq: + if (op.type != OP_REG32) + expect("register"); + else + asm_emit_opcode(token, (0x12fff1 << 4) | op.reg); + break; + case TOK_ASM_blxeq: + if (op.type != OP_REG32) + expect("register"); + else + asm_emit_opcode(token, (0x12fff3 << 4) | op.reg); + break; + default: + expect("branch instruction"); + } +} + +ST_FUNC void asm_opcode(TCCState *s1, int token) +{ + while (token == TOK_LINEFEED) { + next(); + token = tok; + } + if (token == TOK_EOF) + return; + if (token < TOK_ASM_nopeq) { // no condition code + switch (token) { + case TOK_ASM_cdp2: + asm_coprocessor_opcode(s1, token); + return; + case TOK_ASM_ldc2: + case TOK_ASM_ldc2l: + case TOK_ASM_stc2: + case TOK_ASM_stc2l: + asm_coprocessor_data_transfer_opcode(s1, token); + return; + default: + expect("instruction"); + return; + } + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_pusheq: + case TOK_ASM_popeq: + case TOK_ASM_stmdaeq: + case TOK_ASM_ldmdaeq: + case TOK_ASM_stmeq: + case TOK_ASM_ldmeq: + case TOK_ASM_stmiaeq: + case TOK_ASM_ldmiaeq: + case TOK_ASM_stmdbeq: + case TOK_ASM_ldmdbeq: + case TOK_ASM_stmibeq: + case TOK_ASM_ldmibeq: + asm_block_data_transfer_opcode(s1, token); + return; + case TOK_ASM_nopeq: + case TOK_ASM_wfeeq: + case TOK_ASM_wfieq: + asm_nullary_opcode(token); + return; + case TOK_ASM_swieq: + case TOK_ASM_svceq: + asm_unary_opcode(s1, token); + return; + case TOK_ASM_beq: + case TOK_ASM_bleq: + case TOK_ASM_bxeq: + case TOK_ASM_blxeq: + asm_branch_opcode(s1, token); + return; + case TOK_ASM_clzeq: + case TOK_ASM_sxtbeq: + case TOK_ASM_sxtheq: + case TOK_ASM_uxtbeq: + case TOK_ASM_uxtheq: + case TOK_ASM_movteq: + case TOK_ASM_movweq: + asm_binary_opcode(s1, token); + return; + + case TOK_ASM_ldreq: + case TOK_ASM_ldrbeq: + case TOK_ASM_streq: + case TOK_ASM_strbeq: + case TOK_ASM_ldrexeq: + case TOK_ASM_ldrexbeq: + case TOK_ASM_strexeq: + case TOK_ASM_strexbeq: + asm_single_data_transfer_opcode(s1, token); + return; + + case TOK_ASM_ldrheq: + case TOK_ASM_ldrsheq: + case TOK_ASM_ldrsbeq: + case TOK_ASM_strheq: + asm_misc_single_data_transfer_opcode(s1, token); + return; + + case TOK_ASM_andeq: + case TOK_ASM_eoreq: + case TOK_ASM_subeq: + case TOK_ASM_rsbeq: + case TOK_ASM_addeq: + case TOK_ASM_adceq: + case TOK_ASM_sbceq: + case TOK_ASM_rsceq: + case TOK_ASM_tsteq: + case TOK_ASM_teqeq: + case TOK_ASM_cmpeq: + case TOK_ASM_cmneq: + case TOK_ASM_orreq: + case TOK_ASM_moveq: + case TOK_ASM_biceq: + case TOK_ASM_mvneq: + case TOK_ASM_andseq: + case TOK_ASM_eorseq: + case TOK_ASM_subseq: + case TOK_ASM_rsbseq: + case TOK_ASM_addseq: + case TOK_ASM_adcseq: + case TOK_ASM_sbcseq: + case TOK_ASM_rscseq: +// case TOK_ASM_tstseq: +// case TOK_ASM_teqseq: +// case TOK_ASM_cmpseq: +// case TOK_ASM_cmnseq: + case TOK_ASM_orrseq: + case TOK_ASM_movseq: + case TOK_ASM_bicseq: + case TOK_ASM_mvnseq: + asm_data_processing_opcode(s1, token); + return; + + case TOK_ASM_lsleq: + case TOK_ASM_lslseq: + case TOK_ASM_lsreq: + case TOK_ASM_lsrseq: + case TOK_ASM_asreq: + case TOK_ASM_asrseq: + case TOK_ASM_roreq: + case TOK_ASM_rorseq: + case TOK_ASM_rrxseq: + case TOK_ASM_rrxeq: + asm_shift_opcode(s1, token); + return; + + case TOK_ASM_muleq: + case TOK_ASM_mulseq: + case TOK_ASM_mlaeq: + case TOK_ASM_mlaseq: + asm_multiplication_opcode(s1, token); + return; + + case TOK_ASM_smulleq: + case TOK_ASM_smullseq: + case TOK_ASM_umulleq: + case TOK_ASM_umullseq: + case TOK_ASM_smlaleq: + case TOK_ASM_smlalseq: + case TOK_ASM_umlaleq: + case TOK_ASM_umlalseq: + asm_long_multiplication_opcode(s1, token); + return; + + case TOK_ASM_cdpeq: + case TOK_ASM_mcreq: + case TOK_ASM_mrceq: + asm_coprocessor_opcode(s1, token); + return; + + case TOK_ASM_ldceq: + case TOK_ASM_ldcleq: + case TOK_ASM_stceq: + case TOK_ASM_stcleq: + asm_coprocessor_data_transfer_opcode(s1, token); + return; + +#if defined(TCC_ARM_VFP) + case TOK_ASM_vldreq: + case TOK_ASM_vstreq: + asm_floating_point_single_data_transfer_opcode(s1, token); + return; + + case TOK_ASM_vmlaeq_f32: + case TOK_ASM_vmlseq_f32: + case TOK_ASM_vnmlseq_f32: + case TOK_ASM_vnmlaeq_f32: + case TOK_ASM_vmuleq_f32: + case TOK_ASM_vnmuleq_f32: + case TOK_ASM_vaddeq_f32: + case TOK_ASM_vsubeq_f32: + case TOK_ASM_vdiveq_f32: + case TOK_ASM_vnegeq_f32: + case TOK_ASM_vabseq_f32: + case TOK_ASM_vsqrteq_f32: + case TOK_ASM_vcmpeq_f32: + case TOK_ASM_vcmpeeq_f32: + case TOK_ASM_vmoveq_f32: + case TOK_ASM_vmlaeq_f64: + case TOK_ASM_vmlseq_f64: + case TOK_ASM_vnmlseq_f64: + case TOK_ASM_vnmlaeq_f64: + case TOK_ASM_vmuleq_f64: + case TOK_ASM_vnmuleq_f64: + case TOK_ASM_vaddeq_f64: + case TOK_ASM_vsubeq_f64: + case TOK_ASM_vdiveq_f64: + case TOK_ASM_vnegeq_f64: + case TOK_ASM_vabseq_f64: + case TOK_ASM_vsqrteq_f64: + case TOK_ASM_vcmpeq_f64: + case TOK_ASM_vcmpeeq_f64: + case TOK_ASM_vmoveq_f64: + asm_floating_point_data_processing_opcode(s1, token); + return; + + case TOK_ASM_vcvtreq_s32_f32: + case TOK_ASM_vcvtreq_s32_f64: + case TOK_ASM_vcvteq_s32_f32: + case TOK_ASM_vcvteq_s32_f64: + case TOK_ASM_vcvtreq_u32_f32: + case TOK_ASM_vcvtreq_u32_f64: + case TOK_ASM_vcvteq_u32_f32: + case TOK_ASM_vcvteq_u32_f64: + case TOK_ASM_vcvteq_f64_s32: + case TOK_ASM_vcvteq_f32_s32: + case TOK_ASM_vcvteq_f64_u32: + case TOK_ASM_vcvteq_f32_u32: + case TOK_ASM_vcvteq_f64_f32: + case TOK_ASM_vcvteq_f32_f64: + asm_floating_point_vcvt_data_processing_opcode(s1, token); + return; + + case TOK_ASM_vpusheq: + case TOK_ASM_vpopeq: + case TOK_ASM_vldmeq: + case TOK_ASM_vldmiaeq: + case TOK_ASM_vldmdbeq: + case TOK_ASM_vstmeq: + case TOK_ASM_vstmiaeq: + case TOK_ASM_vstmdbeq: + asm_floating_point_block_data_transfer_opcode(s1, token); + return; + + case TOK_ASM_vmsreq: + case TOK_ASM_vmrseq: + asm_floating_point_status_register_opcode(s1, token); + return; +#endif + + default: + expect("known instruction"); + } +} + +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) +{ + int r, reg, size, val; + char buf[64]; + + r = sv->r; + if ((r & VT_VALMASK) == VT_CONST) { + if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' && + modifier != 'P') + cstr_ccat(add_str, '#'); + if (r & VT_SYM) { + const char *name = get_tok_str(sv->sym->v, NULL); + if (sv->sym->v >= SYM_FIRST_ANOM) { + /* In case of anonymous symbols ("L.42", used + for static data labels) we can't find them + in the C symbol table when later looking up + this name. So enter them now into the asm label + list when we still know the symbol. */ + get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym); + } + if (tcc_state->leading_underscore) + cstr_ccat(add_str, '_'); + cstr_cat(add_str, name, -1); + if ((uint32_t) sv->c.i == 0) + goto no_offset; + cstr_ccat(add_str, '+'); + } + val = sv->c.i; + if (modifier == 'n') + val = -val; + snprintf(buf, sizeof(buf), "%d", (int) sv->c.i); + cstr_cat(add_str, buf, -1); + no_offset:; + } else if ((r & VT_VALMASK) == VT_LOCAL) { + snprintf(buf, sizeof(buf), "[fp,#%d]", (int) sv->c.i); + cstr_cat(add_str, buf, -1); + } else if (r & VT_LVAL) { + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + tcc_internal_error(""); + snprintf(buf, sizeof(buf), "[%s]", + get_tok_str(TOK_ASM_r0 + reg, NULL)); + cstr_cat(add_str, buf, -1); + } else { + /* register case */ + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + tcc_internal_error(""); + + /* choose register operand size */ + if ((sv->type.t & VT_BTYPE) == VT_BYTE || + (sv->type.t & VT_BTYPE) == VT_BOOL) + size = 1; + else if ((sv->type.t & VT_BTYPE) == VT_SHORT) + size = 2; + else + size = 4; + + if (modifier == 'b') { + size = 1; + } else if (modifier == 'w') { + size = 2; + } else if (modifier == 'k') { + size = 4; + } + + switch (size) { + default: + reg = TOK_ASM_r0 + reg; + break; + } + snprintf(buf, sizeof(buf), "%s", get_tok_str(reg, NULL)); + cstr_cat(add_str, buf, -1); + } +} + +/* generate prolog and epilog code for asm statement */ +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) +{ + uint8_t regs_allocated[NB_ASM_REGS]; + ASMOperand *op; + int i, reg; + uint32_t saved_regset = 0; + + // TODO: Check non-E ABI. + // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet + static const uint8_t reg_saved[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 }; + + /* mark all used registers */ + memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); + for(i = 0; i < nb_operands;i++) { + op = &operands[i]; + if (op->reg >= 0) + regs_allocated[op->reg] = 1; + } + for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) { + reg = reg_saved[i]; + if (regs_allocated[reg]) + saved_regset |= 1 << reg; + } + + if (!is_output) { // prolog + /* generate reg save code */ + if (saved_regset) + gen_le32(0xe92d0000 | saved_regset); // push {...} + + /* generate load code */ + for(i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && + op->is_memory) { + /* memory reference case (for both input and + output cases) */ + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL; + sv.type.t = VT_PTR; + load(op->reg, &sv); + } else if (i >= nb_outputs || op->is_rw) { // not write-only + /* load value in register */ + load(op->reg, op->vt); + if (op->is_llong) + tcc_error("long long not implemented"); + } + } + } + } else { // epilog + /* generate save code */ + for(i = 0 ; i < nb_outputs; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + if (!op->is_memory) { + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + sv.type.t = VT_PTR; + load(out_reg, &sv); + + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | out_reg; + store(op->reg, &sv); + } + } else { + store(op->reg, op->vt); + if (op->is_llong) + tcc_error("long long not implemented"); + } + } + } + + /* generate reg restore code */ + if (saved_regset) + gen_le32(0xe8bd0000 | saved_regset); // pop {...} + } +} + +/* return the constraint priority (we allocate first the lowest + numbered constraints) */ +static inline int constraint_priority(const char *str) +{ + int priority, c, pr; + + /* we take the lowest priority */ + priority = 0; + for(;;) { + c = *str; + if (c == '\0') + break; + str++; + switch(c) { + case 'l': // in ARM mode, that's an alias for 'r' [ARM]. + case 'r': // register [general] + case 'p': // valid memory address for load,store [general] + pr = 3; + break; + case 'M': // integer constant for shifts [ARM] + case 'I': // integer valid for data processing instruction immediate + case 'J': // integer in range -4095...4095 + + case 'i': // immediate integer operand, including symbolic constants [general] + case 'm': // memory operand [general] + case 'g': // general-purpose-register, memory, immediate integer [general] + pr = 4; + break; + default: + tcc_error("unknown constraint '%c'", c); + pr = 0; + } + if (pr > priority) + priority = pr; + } + return priority; +} + +static const char *skip_constraint_modifiers(const char *p) +{ + /* Constraint modifier: + = Operand is written to by this instruction + + Operand is both read and written to by this instruction + % Instruction is commutative for this operand and the following operand. + + Per-alternative constraint modifier: + & Operand is clobbered before the instruction is done using the input operands + */ + while (*p == '=' || *p == '&' || *p == '+' || *p == '%') + p++; + return p; +} + +#define REG_OUT_MASK 0x01 +#define REG_IN_MASK 0x02 + +#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) + +ST_FUNC void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) +{ + /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */ + /* TODO: Simple constraints + whitespace ignored + o memory operand that is offsetable + V memory but not offsetable + < memory operand with autodecrement addressing is allowed. Restrictions apply. + > memory operand with autoincrement addressing is allowed. Restrictions apply. + n immediate integer operand with a known numeric value + E immediate floating operand (const_double) is allowed, but only if target=host + F immediate floating operand (const_double or const_vector) is allowed + s immediate integer operand whose value is not an explicit integer + X any operand whatsoever + 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed + */ + + /* TODO: ARM constraints: + k the stack pointer register + G the floating-point constant 0.0 + Q memory reference where the exact address is in a single register ("m" is preferable for asm statements) + R an item in the constant pool + S symbol in the text segment of the current file +[ Uv memory reference suitable for VFP load/store insns (reg+constant offset)] +[ Uy memory reference suitable for iWMMXt load/store instructions] + Uq memory reference suitable for the ARMv4 ldrsb instruction + */ + ASMOperand *op; + int sorted_op[MAX_ASM_OPERANDS]; + int i, j, k, p1, p2, tmp, reg, c, reg_mask; + const char *str; + uint8_t regs_allocated[NB_ASM_REGS]; + + /* init fields */ + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + op->input_index = -1; + op->ref_index = -1; + op->reg = -1; + op->is_memory = 0; + op->is_rw = 0; + } + /* compute constraint priority and evaluate references to output + constraints if input constraints */ + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + str = op->constraint; + str = skip_constraint_modifiers(str); + if (isnum(*str) || *str == '[') { + /* this is a reference to another constraint */ + k = find_constraint(operands, nb_operands, str, NULL); + if ((unsigned) k >= i || i < nb_outputs) + tcc_error("invalid reference in constraint %d ('%s')", + i, str); + op->ref_index = k; + if (operands[k].input_index >= 0) + tcc_error("cannot reference twice the same operand"); + operands[k].input_index = i; + op->priority = 5; + } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL + && op->vt->sym + && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) { + op->priority = 1; + op->reg = reg; + } else { + op->priority = constraint_priority(str); + } + } + + /* sort operands according to their priority */ + for (i = 0; i < nb_operands; i++) + sorted_op[i] = i; + for (i = 0; i < nb_operands - 1; i++) { + for (j = i + 1; j < nb_operands; j++) { + p1 = operands[sorted_op[i]].priority; + p2 = operands[sorted_op[j]].priority; + if (p2 < p1) { + tmp = sorted_op[i]; + sorted_op[i] = sorted_op[j]; + sorted_op[j] = tmp; + } + } + } + + for (i = 0; i < NB_ASM_REGS; i++) { + if (clobber_regs[i]) + regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; + else + regs_allocated[i] = 0; + } + /* sp cannot be used */ + regs_allocated[13] = REG_IN_MASK | REG_OUT_MASK; + /* fp cannot be used yet */ + regs_allocated[11] = REG_IN_MASK | REG_OUT_MASK; + + /* allocate registers and generate corresponding asm moves */ + for (i = 0; i < nb_operands; i++) { + j = sorted_op[i]; + op = &operands[j]; + str = op->constraint; + /* no need to allocate references */ + if (op->ref_index >= 0) + continue; + /* select if register is used for output, input or both */ + if (op->input_index >= 0) { + reg_mask = REG_IN_MASK | REG_OUT_MASK; + } else if (j < nb_outputs) { + reg_mask = REG_OUT_MASK; + } else { + reg_mask = REG_IN_MASK; + } + if (op->reg >= 0) { + if (is_reg_allocated(op->reg)) + tcc_error + ("asm regvar requests register that's taken already"); + reg = op->reg; + goto reg_found; + } + try_next: + c = *str++; + switch (c) { + case '=': // Operand is written-to + goto try_next; + case '+': // Operand is both READ and written-to + op->is_rw = 1; + /* FALL THRU */ + case '&': // Operand is clobbered before the instruction is done using the input operands + if (j >= nb_outputs) + tcc_error("'%c' modifier can only be applied to outputs", + c); + reg_mask = REG_IN_MASK | REG_OUT_MASK; + goto try_next; + case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM] + case 'r': // general-purpose register + case 'p': // loadable/storable address + /* any general register */ + for (reg = 0; reg <= 8; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + reg_found: + /* now we can reload in the register */ + op->is_llong = 0; + op->reg = reg; + regs_allocated[reg] |= reg_mask; + break; + case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two) + case 'J': // integer in the range -4095 to 4095 [ARM] + case 'K': // integer that satisfies constraint I when inverted (one's complement) + case 'L': // integer that satisfies constraint I when inverted (two's complement) + case 'i': // immediate integer operand, including symbolic constants + if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) + goto try_next; + break; + case 'M': // integer in the range 0 to 32 + if (! + ((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == + VT_CONST)) + goto try_next; + break; + case 'm': // memory operand + case 'g': + /* nothing special to do because the operand is already in + memory, except if the pointer itself is stored in a + memory variable (VT_LLOCAL case) */ + /* XXX: fix constant case */ + /* if it is a reference to a memory zone, it must lie + in a register, so we reserve the register in the + input registers and a load will be generated + later */ + if (j < nb_outputs || c == 'm') { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + /* any general register */ + for (reg = 0; reg <= 8; reg++) { + if (!(regs_allocated[reg] & REG_IN_MASK)) + goto reg_found1; + } + goto try_next; + reg_found1: + /* now we can reload in the register */ + regs_allocated[reg] |= REG_IN_MASK; + op->reg = reg; + op->is_memory = 1; + } + } + break; + default: + tcc_error("asm constraint %d ('%s') could not be satisfied", + j, op->constraint); + break; + } + /* if a reference is present for that operand, we assign it too */ + if (op->input_index >= 0) { + operands[op->input_index].reg = op->reg; + operands[op->input_index].is_llong = op->is_llong; + } + } + + /* compute out_reg. It is used to store outputs registers to memory + locations references by pointers (VT_LLOCAL case) */ + *pout_reg = -1; + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0 && + (op->vt->r & VT_VALMASK) == VT_LLOCAL && !op->is_memory) { + for (reg = 0; reg <= 8; reg++) { + if (!(regs_allocated[reg] & REG_OUT_MASK)) + goto reg_found2; + } + tcc_error("could not find free output register for reloading"); + reg_found2: + *pout_reg = reg; + break; + } + } + + /* print sorted constraints */ +#ifdef ASM_DEBUG + for (i = 0; i < nb_operands; i++) { + j = sorted_op[i]; + op = &operands[j]; + printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", + j, + op->id ? get_tok_str(op->id, NULL) : "", + op->constraint, op->vt->r, op->reg); + } + if (*pout_reg >= 0) + printf("out_reg=%d\n", *pout_reg); +#endif +} + +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) +{ + int reg; + TokenSym *ts; + + if (!strcmp(str, "memory") || + !strcmp(str, "cc") || + !strcmp(str, "flags")) + return; + ts = tok_alloc(str, strlen(str)); + reg = asm_parse_regvar(ts->tok); + if (reg == -1) { + tcc_error("invalid clobber register '%s'", str); + } + clobber_regs[reg] = 1; +} + +/* If T refers to a register then return the register number and type. + Otherwise return -1. */ +ST_FUNC int asm_parse_regvar (int t) +{ + if (t >= TOK_ASM_r0 && t <= TOK_ASM_pc) { /* register name */ + switch (t) { + case TOK_ASM_fp: + return TOK_ASM_r11 - TOK_ASM_r0; + case TOK_ASM_ip: + return TOK_ASM_r12 - TOK_ASM_r0; + case TOK_ASM_sp: + return TOK_ASM_r13 - TOK_ASM_r0; + case TOK_ASM_lr: + return TOK_ASM_r14 - TOK_ASM_r0; + case TOK_ASM_pc: + return TOK_ASM_r15 - TOK_ASM_r0; + default: + return t - TOK_ASM_r0; + } + } else + return -1; +} + +/*************************************************************/ +#endif /* ndef TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/arm-gen.c b/vendor/tcc/arm-gen.c new file mode 100644 index 00000000..5eea781b --- /dev/null +++ b/vendor/tcc/arm-gen.c @@ -0,0 +1,2391 @@ +/* + * ARMv4 code generator for TCC + * + * Copyright (c) 2003 Daniel Glöckner + * Copyright (c) 2012 Thomas Preud'homme + * + * Based on i386-gen.c by Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TARGET_DEFS_ONLY + +#if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP) +#error "Currently TinyCC only supports float computation with VFP instructions" +#endif + +/* number of available registers */ +#ifdef TCC_ARM_VFP +#define NB_REGS 13 +#else +#define NB_REGS 9 +#endif + +#ifndef TCC_CPU_VERSION +# define TCC_CPU_VERSION 5 +#endif + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_R0 0x0004 +#define RC_R1 0x0008 +#define RC_R2 0x0010 +#define RC_R3 0x0020 +#define RC_R12 0x0040 +#define RC_F0 0x0080 +#define RC_F1 0x0100 +#define RC_F2 0x0200 +#define RC_F3 0x0400 +#ifdef TCC_ARM_VFP +#define RC_F4 0x0800 +#define RC_F5 0x1000 +#define RC_F6 0x2000 +#define RC_F7 0x4000 +#endif +#define RC_IRET RC_R0 /* function return: integer register */ +#define RC_IRE2 RC_R1 /* function return: second integer register */ +#define RC_FRET RC_F0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_R0 = 0, + TREG_R1, + TREG_R2, + TREG_R3, + TREG_R12, + TREG_F0, + TREG_F1, + TREG_F2, + TREG_F3, +#ifdef TCC_ARM_VFP + TREG_F4, + TREG_F5, + TREG_F6, + TREG_F7, +#endif + TREG_SP = 13, + TREG_LR, +}; + +#ifdef TCC_ARM_VFP +#define T2CPR(t) (((t) & VT_BTYPE) != VT_FLOAT ? 0x100 : 0) +#endif + +/* return registers for function */ +#define REG_IRET TREG_R0 /* single word int return register */ +#define REG_IRE2 TREG_R1 /* second word return register (for long long) */ +#define REG_FRET TREG_F0 /* float return register */ + +#ifdef TCC_ARM_EABI +#define TOK___divdi3 TOK___aeabi_ldivmod +#define TOK___moddi3 TOK___aeabi_ldivmod +#define TOK___udivdi3 TOK___aeabi_uldivmod +#define TOK___umoddi3 TOK___aeabi_uldivmod +#endif + +/* defined if function parameters must be evaluated in reverse order */ +#define INVERT_FUNC_PARAMS + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +/* #define FUNC_STRUCT_PARAM_AS_PTR */ + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#ifdef TCC_ARM_VFP +#define LDOUBLE_SIZE 8 +#endif + +#ifndef LDOUBLE_SIZE +#define LDOUBLE_SIZE 8 +#endif + +#ifdef TCC_ARM_EABI +#define LDOUBLE_ALIGN 8 +#else +#define LDOUBLE_ALIGN 4 +#endif + +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +#define CHAR_IS_UNSIGNED + +#ifdef TCC_ARM_HARDFLOAT +# define ARM_FLOAT_ABI ARM_HARD_FLOAT +#else +# define ARM_FLOAT_ABI ARM_SOFTFP_FLOAT +#endif + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#define USING_GLOBALS +#include "tcc.h" + +ST_DATA const char * const target_machine_defs = + "__arm__\0" + "__arm\0" + "arm\0" + "__arm_elf__\0" + "__arm_elf\0" + "arm_elf\0" + "__ARM_ARCH_4__\0" + "__ARMEL__\0" + "__APCS_32__\0" +#if defined TCC_ARM_EABI + "__ARM_EABI__\0" +#endif + ; + +enum float_abi float_abi; + +ST_DATA const int reg_classes[NB_REGS] = { + /* r0 */ RC_INT | RC_R0, + /* r1 */ RC_INT | RC_R1, + /* r2 */ RC_INT | RC_R2, + /* r3 */ RC_INT | RC_R3, + /* r12 */ RC_INT | RC_R12, + /* f0 */ RC_FLOAT | RC_F0, + /* f1 */ RC_FLOAT | RC_F1, + /* f2 */ RC_FLOAT | RC_F2, + /* f3 */ RC_FLOAT | RC_F3, +#ifdef TCC_ARM_VFP + /* d4/s8 */ RC_FLOAT | RC_F4, +/* d5/s10 */ RC_FLOAT | RC_F5, +/* d6/s12 */ RC_FLOAT | RC_F6, +/* d7/s14 */ RC_FLOAT | RC_F7, +#endif +}; + +static int func_sub_sp_offset, last_itod_magic; +static int leaffunc; + +#if defined(CONFIG_TCC_BCHECK) +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +ST_DATA int func_bound_add_epilog; +#endif + +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) +static CType float_type, double_type, func_float_type, func_double_type; +ST_FUNC void arm_init(struct TCCState *s) +{ + float_type.t = VT_FLOAT; + double_type.t = VT_DOUBLE; + func_float_type.t = VT_FUNC; + func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD); + func_double_type.t = VT_FUNC; + func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD); + + float_abi = s->float_abi; +#ifndef TCC_ARM_HARDFLOAT +// XXX: Works on OpenBSD +// # warning "soft float ABI currently not supported: default to softfp" +#endif +} +#else +#define func_float_type func_old_type +#define func_double_type func_old_type +#define func_ldouble_type func_old_type +ST_FUNC void arm_init(struct TCCState *s) +{ +#if 0 +#if !defined (TCC_ARM_VFP) + tcc_warning("Support for FPA is deprecated and will be removed in next" + " release"); +#endif +#if !defined (TCC_ARM_EABI) + tcc_warning("Support for OABI is deprecated and will be removed in next" + " release"); +#endif +#endif +} +#endif + +#define CHECK_R(r) ((r) >= TREG_R0 && (r) <= TREG_LR) + +static int two2mask(int a,int b) { + if (!CHECK_R(a) || !CHECK_R(b)) + tcc_error("compiler error! registers %i,%i is not valid",a,b); + return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT); +} + +static int regmask(int r) { + if (!CHECK_R(r)) + tcc_error("compiler error! register %i is not valid",r); + return reg_classes[r]&~(RC_INT|RC_FLOAT); +} + +/******************************************************/ + +#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP) +const char *default_elfinterp(struct TCCState *s) +{ + if (s->float_abi == ARM_HARD_FLOAT) + return "/lib/ld-linux-armhf.so.3"; + else + return "/lib/ld-linux.so.3"; +} +#endif + +void o(uint32_t i) +{ + /* this is a good place to start adding big-endian support*/ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 4; + if (!cur_text_section) + tcc_error("compiler error! This happens f.ex. if the compiler\n" + "can't evaluate constant expressions outside of a function."); + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind++] = i&255; + i>>=8; + cur_text_section->data[ind++] = i&255; + i>>=8; + cur_text_section->data[ind++] = i&255; + i>>=8; + cur_text_section->data[ind++] = i; +} + +static uint32_t stuff_const(uint32_t op, uint32_t c) +{ + int try_neg=0; + uint32_t nc = 0, negop = 0; + + switch(op&0x1F00000) + { + case 0x800000: //add + case 0x400000: //sub + try_neg=1; + negop=op^0xC00000; + nc=-c; + break; + case 0x1A00000: //mov + case 0x1E00000: //mvn + try_neg=1; + negop=op^0x400000; + nc=~c; + break; + case 0x200000: //xor + if(c==~0) + return (op&0xF010F000)|((op>>16)&0xF)|0x1E00000; + break; + case 0x0: //and + if(c==~0) + return (op&0xF010F000)|((op>>16)&0xF)|0x1A00000; + case 0x1C00000: //bic + try_neg=1; + negop=op^0x1C00000; + nc=~c; + break; + case 0x1800000: //orr + if(c==~0) + return (op&0xFFF0FFFF)|0x1E00000; + break; + } + do { + uint32_t m; + int i; + if(c<256) /* catch undefined <<32 */ + return op|c; + for(i=2;i<32;i+=2) { + m=(0xff>>i)|(0xff<<(32-i)); + if(!(c&~m)) + return op|(i<<7)|(c<>(32-i)); + } + op=negop; + c=nc; + } while(try_neg--); + return 0; +} + + +//only add,sub +void stuff_const_harder(uint32_t op, uint32_t v) { + uint32_t x; + x=stuff_const(op,v); + if(x) + o(x); + else { + uint32_t a[16], nv, no, o2, n2; + int i,j,k; + a[0]=0xff; + o2=(op&0xfff0ffff)|((op&0xf000)<<4);; + for(i=1;i<16;i++) + a[i]=(a[i-1]>>2)|(a[i-1]<<30); + for(i=0;i<12;i++) + for(j=i<4?i+12:15;j>=i+4;j--) + if((v&(a[i]|a[j]))==v) { + o(stuff_const(op,v&a[i])); + o(stuff_const(o2,v&a[j])); + return; + } + no=op^0xC00000; + n2=o2^0xC00000; + nv=-v; + for(i=0;i<12;i++) + for(j=i<4?i+12:15;j>=i+4;j--) + if((nv&(a[i]|a[j]))==nv) { + o(stuff_const(no,nv&a[i])); + o(stuff_const(n2,nv&a[j])); + return; + } + for(i=0;i<8;i++) + for(j=i+4;j<12;j++) + for(k=i<4?i+12:15;k>=j+4;k--) + if((v&(a[i]|a[j]|a[k]))==v) { + o(stuff_const(op,v&a[i])); + o(stuff_const(o2,v&a[j])); + o(stuff_const(o2,v&a[k])); + return; + } + no=op^0xC00000; + nv=-v; + for(i=0;i<8;i++) + for(j=i+4;j<12;j++) + for(k=i<4?i+12:15;k>=j+4;k--) + if((nv&(a[i]|a[j]|a[k]))==nv) { + o(stuff_const(no,nv&a[i])); + o(stuff_const(n2,nv&a[j])); + o(stuff_const(n2,nv&a[k])); + return; + } + o(stuff_const(op,v&a[0])); + o(stuff_const(o2,v&a[4])); + o(stuff_const(o2,v&a[8])); + o(stuff_const(o2,v&a[12])); + } +} + +uint32_t encbranch(int pos, int addr, int fail) +{ + addr-=pos+8; + addr/=4; + if(addr>=0x1000000 || addr<-0x1000000) { + if(fail) + tcc_error("FIXME: function bigger than 32MB"); + return 0; + } + return 0x0A000000|(addr&0xffffff); +} + +int decbranch(int pos) +{ + int x; + x=*(uint32_t *)(cur_text_section->data + pos); + x&=0x00ffffff; + if(x&0x800000) + x-=0x1000000; + return x*4+pos+8; +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + uint32_t *x; + int lt; + while(t) { + x=(uint32_t *)(cur_text_section->data + t); + t=decbranch(lt=t); + if(a==lt+4) + *x=0xE1A00000; // nop + else { + *x &= 0xff000000; + *x |= encbranch(lt,a,1); + } + } +} + +#ifdef TCC_ARM_VFP +static uint32_t vfpr(int r) +{ + if(rTREG_F7) + tcc_error("compiler error! register %i is no vfp register",r); + return r - TREG_F0; +} +#else +static uint32_t fpr(int r) +{ + if(rTREG_F3) + tcc_error("compiler error! register %i is no fpa register",r); + return r - TREG_F0; +} +#endif + +static uint32_t intr(int r) +{ + if(r == TREG_R12) + return 12; + if(r >= TREG_R0 && r <= TREG_R3) + return r - TREG_R0; + if (!(r >= TREG_SP && r <= TREG_LR)) + tcc_error("compiler error! register %i is no int register",r); + return r + (13 - TREG_SP); +} + +static void calcaddr(uint32_t *base, int *off, int *sgn, int maxoff, unsigned shift) +{ + if(*off>maxoff || *off&((1<r & VT_SYM) + greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); + o(sv->c.i); +#else + if(sv->r & VT_SYM) { + if (sv->sym->type.t & VT_STATIC) { + greloc(cur_text_section, sv->sym, ind, R_ARM_REL32); + o(sv->c.i - 12); + o(0xe080000f | (intr(r)<<12) | (intr(r)<<16)); // add rx,rx,pc + } + else { + greloc(cur_text_section, sv->sym, ind, R_ARM_GOT_PREL); + o(-12); + o(0xe080000f | (intr(r)<<12) | (intr(r)<<16)); // add rx,rx,pc + o(0xe5900000 | (intr(r)<<12) | (intr(r)<<16)); // ldr rx,[rx] + if (sv->c.i) + stuff_const_harder(0xe2800000 | (intr(r)<<12) | (intr(r)<<16), + sv->c.i); + } + } + else + o(sv->c.i); +#endif +} + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, ft, fc, fr, sign; + uint32_t op; + SValue v1; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.i; + + if(fc>=0) + sign=0; + else { + sign=1; + fc=-fc; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + uint32_t base = 0xB; // fp + if(v == VT_LLOCAL) { + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.i = sv->c.i; + load(TREG_LR, &v1); + base = 14; /* lr */ + fc=sign=0; + v=VT_LOCAL; + } else if(v == VT_CONST) { + v1.type.t = VT_PTR; + v1.r = fr&~VT_LVAL; + v1.c.i = sv->c.i; + v1.sym=sv->sym; + load(TREG_LR, &v1); + base = 14; /* lr */ + fc=sign=0; + v=VT_LOCAL; + } else if(v < VT_CONST) { + base=intr(v); + fc=sign=0; + v=VT_LOCAL; + } + if(v == VT_LOCAL) { + if(is_float(ft)) { + calcaddr(&base,&fc,&sign,1020,2); +#ifdef TCC_ARM_VFP + op=0xED100A00; /* flds */ + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x100; /* flds -> fldd */ + o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); +#else + op=0xED100100; + if(!sign) + op|=0x800000; +#if LDOUBLE_SIZE == 8 + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x8000; +#else + if ((ft & VT_BTYPE) == VT_DOUBLE) + op|=0x8000; + else if ((ft & VT_BTYPE) == VT_LDOUBLE) + op|=0x400000; +#endif + o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); +#endif + } else if((ft & (VT_BTYPE|VT_UNSIGNED)) == VT_BYTE + || (ft & VT_BTYPE) == VT_SHORT) { + calcaddr(&base,&fc,&sign,255,0); + op=0xE1500090; + if ((ft & VT_BTYPE) == VT_SHORT) + op|=0x20; + if ((ft & VT_UNSIGNED) == 0) + op|=0x40; + if(!sign) + op|=0x800000; + o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); + } else { + calcaddr(&base,&fc,&sign,4095,0); + op=0xE5100000; + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) + op|=0x400000; + o(op|(intr(r)<<12)|fc|(base<<16)); + } + return; + } + } else { + if (v == VT_CONST) { + op=stuff_const(0xE3A00000|(intr(r)<<12),sv->c.i); + if (fr & VT_SYM || !op) + load_value(sv, r); + else + o(op); + return; + } else if (v == VT_LOCAL) { + op=stuff_const(0xE28B0000|(intr(r)<<12),sv->c.i); + if (fr & VT_SYM || !op) { + load_value(sv, r); + o(0xE08B0000|(intr(r)<<12)|intr(r)); + } else + o(op); + return; + } else if(v == VT_CMP) { + o(mapcc(sv->c.i)|0x3A00001|(intr(r)<<12)); + o(mapcc(negcc(sv->c.i))|0x3A00000|(intr(r)<<12)); + return; + } else if (v == VT_JMP || v == VT_JMPI) { + int t; + t = v & 1; + o(0xE3A00000|(intr(r)<<12)|t); + o(0xEA000000); + gsym(sv->c.i); + o(0xE3A00000|(intr(r)<<12)|(t^1)); + return; + } else if (v < VT_CONST) { + if(is_float(ft)) +#ifdef TCC_ARM_VFP + o(0xEEB00A40|(vfpr(r)<<12)|vfpr(v)|T2CPR(ft)); /* fcpyX */ +#else + o(0xEE008180|(fpr(r)<<12)|fpr(v)); +#endif + else + o(0xE1A00000|(intr(r)<<12)|intr(v)); + return; + } + } + tcc_error("load unimplemented!"); +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *sv) +{ + SValue v1; + int v, ft, fc, fr, sign; + uint32_t op; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.i; + + if(fc>=0) + sign=0; + else { + sign=1; + fc=-fc; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL || fr == VT_LOCAL) { + uint32_t base = 0xb; /* fp */ + if(v < VT_CONST) { + base=intr(v); + v=VT_LOCAL; + fc=sign=0; + } else if(v == VT_CONST) { + v1.type.t = ft; + v1.r = fr&~VT_LVAL; + v1.c.i = sv->c.i; + v1.sym=sv->sym; + load(TREG_LR, &v1); + base = 14; /* lr */ + fc=sign=0; + v=VT_LOCAL; + } + if(v == VT_LOCAL) { + if(is_float(ft)) { + calcaddr(&base,&fc,&sign,1020,2); +#ifdef TCC_ARM_VFP + op=0xED000A00; /* fsts */ + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x100; /* fsts -> fstd */ + o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); +#else + op=0xED000100; + if(!sign) + op|=0x800000; +#if LDOUBLE_SIZE == 8 + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x8000; +#else + if ((ft & VT_BTYPE) == VT_DOUBLE) + op|=0x8000; + if ((ft & VT_BTYPE) == VT_LDOUBLE) + op|=0x400000; +#endif + o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); +#endif + return; + } else if((ft & VT_BTYPE) == VT_SHORT) { + calcaddr(&base,&fc,&sign,255,0); + op=0xE14000B0; + if(!sign) + op|=0x800000; + o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); + } else { + calcaddr(&base,&fc,&sign,4095,0); + op=0xE5000000; + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) + op|=0x400000; + o(op|(intr(r)<<12)|fc|(base<<16)); + } + return; + } + } + tcc_error("store unimplemented"); +} + +static void gadd_sp(int val) +{ + stuff_const_harder(0xE28DD000,val); +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + uint32_t x; + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* constant case */ + if(vtop->r & VT_SYM){ + x=encbranch(ind,ind+vtop->c.i,0); + if(x) { + /* relocation case */ + greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24); + o(x|(is_jmp?0xE0000000:0xE1000000)); + } else { + r = TREG_LR; + load_value(vtop, r); + if(is_jmp) + o(0xE1A0F000 | intr(r)); // mov pc, r + else + o(0xe12fff30 | intr(r)); // blx r + } + }else{ + if(!is_jmp) + o(0xE28FE004); // add lr,pc,#4 + o(0xE51FF004); // ldr pc,[pc,#-4] + o(vtop->c.i); + } + } else { + /* otherwise, indirect call */ +#ifdef CONFIG_TCC_BCHECK + vtop->r &= ~VT_MUSTBOUND; +#endif + r = gv(RC_INT); + if(!is_jmp) + o(0xE1A0E00F); // mov lr,pc + o(0xE1A0F000|intr(r)); // mov pc,r + } +} + +#if defined(CONFIG_TCC_BCHECK) + +static void gen_bounds_call(int v) +{ + Sym *sym = external_helper_sym(v); + + greloc(cur_text_section, sym, ind, R_ARM_PC24); + o(0xebfffffe); +} + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + o(0xe1a00000); /* ld r0,lbounds_section->data_offset */ + o(0xe1a00000); + o(0xe1a00000); + o(0xe1a00000); + o(0xe1a00000); /* call __bound_local_new */ +} + +static void gen_bounds_epilog(void) +{ + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, PTR_SIZE); + + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + o(0xe59f0000); /* ldr r0, [pc] */ + o(0xea000000); /* b $+4 */ + greloc(cur_text_section, sym_data, ind, R_ARM_REL32); + o(-12); /* lbounds_section->data_offset */ + o(0xe080000f); /* add r0,r0,pc */ + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + } + + /* generate bound check local freeing */ + o(0xe92d0003); /* push {r0,r1} */ + o(0xed2d0b04); /* vpush {d0,d1} */ + o(0xe59f0000); /* ldr r0, [pc] */ + o(0xea000000); /* b $+4 */ + greloc(cur_text_section, sym_data, ind, R_ARM_REL32); + o(-12); /* lbounds_section->data_offset */ + o(0xe080000f); /* add r0,r0,pc */ + gen_bounds_call(TOK___bound_local_delete); + o(0xecbd0b04); /* vpop {d0,d1} */ + o(0xe8bd0003); /* pop {r0,r1} */ +} +#endif + +static int unalias_ldbl(int btype) +{ +#if LDOUBLE_SIZE == 8 + if (btype == VT_LDOUBLE) + btype = VT_DOUBLE; +#endif + return btype; +} + +/* Return whether a structure is an homogeneous float aggregate or not. + The answer is true if all the elements of the structure are of the same + primitive float type and there is less than 4 elements. + + type: the type corresponding to the structure to be tested */ +static int is_hgen_float_aggr(CType *type) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + struct Sym *ref; + int btype, nb_fields = 0; + + ref = type->ref->next; + if (ref) { + btype = unalias_ldbl(ref->type.t & VT_BTYPE); + if (btype == VT_FLOAT || btype == VT_DOUBLE) { + for(; ref && btype == unalias_ldbl(ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++); + return !ref && nb_fields <= 4; + } + } + } + return 0; +} + +struct avail_regs { + signed char avail[3]; /* 3 holes max with only float and double alignments */ + int first_hole; /* first available hole */ + int last_hole; /* last available hole (none if equal to first_hole) */ + int first_free_reg; /* next free register in the sequence, hole excluded */ +}; + +/* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC + param) according to the rules described in the procedure call standard for + the ARM architecture (AAPCS). If found, the registers are assigned to this + VFP CPRC parameter. Registers are allocated in sequence unless a hole exists + and the parameter is a single float. + + avregs: opaque structure to keep track of available VFP co-processor regs + align: alignment constraints for the param, as returned by type_size() + size: size of the parameter, as returned by type_size() */ +int assign_vfpreg(struct avail_regs *avregs, int align, int size) +{ + int first_reg = 0; + + if (avregs->first_free_reg == -1) + return -1; + if (align >> 3) { /* double alignment */ + first_reg = avregs->first_free_reg; + /* alignment constraint not respected so use next reg and record hole */ + if (first_reg & 1) + avregs->avail[avregs->last_hole++] = first_reg++; + } else { /* no special alignment (float or array of float) */ + /* if single float and a hole is available, assign the param to it */ + if (size == 4 && avregs->first_hole != avregs->last_hole) + return avregs->avail[avregs->first_hole++]; + else + first_reg = avregs->first_free_reg; + } + if (first_reg + size / 4 <= 16) { + avregs->first_free_reg = first_reg + size / 4; + return first_reg; + } + avregs->first_free_reg = -1; + return -1; +} + +/* Returns whether all params need to be passed in core registers or not. + This is the case for function part of the runtime ABI. */ +int floats_in_core_regs(SValue *sval) +{ + if (!sval->sym) + return 0; + + switch (sval->sym->v) { + case TOK___floatundisf: + case TOK___floatundidf: + case TOK___fixunssfdi: + case TOK___fixunsdfdi: +#ifndef TCC_ARM_VFP + case TOK___fixunsxfdi: +#endif + case TOK___floatdisf: + case TOK___floatdidf: + case TOK___fixsfdi: + case TOK___fixdfdi: + return 1; + + default: + return 0; + } +} + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { +#ifdef TCC_ARM_EABI + int size, align; + size = type_size(vt, &align); + if (float_abi == ARM_HARD_FLOAT && !variadic && + (is_float(vt->t) || is_hgen_float_aggr(vt))) { + *ret_align = 8; + *regsize = 8; + ret->ref = NULL; + ret->t = VT_DOUBLE; + return (size + 7) >> 3; + } else if (size > 0 && size <= 4) { + *ret_align = 4; + *regsize = 4; + ret->ref = NULL; + ret->t = VT_INT; + return 1; + } else + return 0; +#else + return 0; +#endif +} + +/* Parameters are classified according to how they are copied to their final + destination for the function call. Because the copying is performed class + after class according to the order in the union below, it is important that + some constraints about the order of the members of this union are respected: + - CORE_STRUCT_CLASS must come after STACK_CLASS; + - CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and + VFP_STRUCT_CLASS; + - VFP_STRUCT_CLASS must come after VFP_CLASS. + See the comment for the main loop in copy_params() for the reason. */ +enum reg_class { + STACK_CLASS = 0, + CORE_STRUCT_CLASS, + VFP_CLASS, + VFP_STRUCT_CLASS, + CORE_CLASS, + NB_CLASSES +}; + +struct param_plan { + int start; /* first reg or addr used depending on the class */ + int end; /* last reg used or next free addr depending on the class */ + SValue *sval; /* pointer to SValue on the value stack */ + struct param_plan *prev; /* previous element in this class */ +}; + +struct plan { + struct param_plan *pplans; /* array of all the param plans */ + struct param_plan *clsplans[NB_CLASSES]; /* per class lists of param plans */ + int nb_plans; +}; + +static void add_param_plan(struct plan* plan, int cls, int start, int end, SValue *v) +{ + struct param_plan *p = &plan->pplans[plan->nb_plans++]; + p->prev = plan->clsplans[cls]; + plan->clsplans[cls] = p; + p->start = start, p->end = end, p->sval = v; +} + +/* Assign parameters to registers and stack with alignment according to the + rules in the procedure call standard for the ARM architecture (AAPCS). + The overall assignment is recorded in an array of per parameter structures + called parameter plans. The parameter plans are also further organized in a + number of linked lists, one per class of parameter (see the comment for the + definition of union reg_class). + + nb_args: number of parameters of the function for which a call is generated + float_abi: float ABI in use for this function call + plan: the structure where the overall assignment is recorded + todo: a bitmap that record which core registers hold a parameter + + Returns the amount of stack space needed for parameter passing + + Note: this function allocated an array in plan->pplans with tcc_malloc. It + is the responsibility of the caller to free this array once used (ie not + before copy_params). */ +static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) +{ + int i, size, align; + int ncrn /* next core register number */, nsaa /* next stacked argument address*/; + struct avail_regs avregs = {{0}}; + + ncrn = nsaa = 0; + *todo = 0; + + for(i = nb_args; i-- ;) { + int j, start_vfpreg = 0; + CType type = vtop[-i].type; + type.t &= ~VT_ARRAY; + size = type_size(&type, &align); + size = (size + 3) & ~3; + align = (align + 3) & ~3; + switch(vtop[-i].type.t & VT_BTYPE) { + case VT_STRUCT: + case VT_FLOAT: + case VT_DOUBLE: + case VT_LDOUBLE: + if (float_abi == ARM_HARD_FLOAT) { + int is_hfa = 0; /* Homogeneous float aggregate */ + + if (is_float(vtop[-i].type.t) + || (is_hfa = is_hgen_float_aggr(&vtop[-i].type))) { + int end_vfpreg; + + start_vfpreg = assign_vfpreg(&avregs, align, size); + end_vfpreg = start_vfpreg + ((size - 1) >> 2); + if (start_vfpreg >= 0) { + add_param_plan(plan, is_hfa ? VFP_STRUCT_CLASS : VFP_CLASS, + start_vfpreg, end_vfpreg, &vtop[-i]); + continue; + } else + break; + } + } + ncrn = (ncrn + (align-1)/4) & ~((align/4) - 1); + if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) { + /* The parameter is allocated both in core register and on stack. As + * such, it can be of either class: it would either be the last of + * CORE_STRUCT_CLASS or the first of STACK_CLASS. */ + for (j = ncrn; j < 4 && j < ncrn + size / 4; j++) + *todo|=(1< 4) + nsaa = (ncrn - 4) * 4; + } else { + ncrn = 4; + break; + } + continue; + default: + if (ncrn < 4) { + int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG; + + if (is_long) { + ncrn = (ncrn + 1) & -2; + if (ncrn == 4) + break; + } + add_param_plan(plan, CORE_CLASS, ncrn, ncrn + is_long, &vtop[-i]); + ncrn += 1 + is_long; + continue; + } + } + nsaa = (nsaa + (align - 1)) & ~(align - 1); + add_param_plan(plan, STACK_CLASS, nsaa, nsaa + size, &vtop[-i]); + nsaa += size; /* size already rounded up before */ + } + return nsaa; +} + +/* Copy parameters to their final destination (core reg, VFP reg or stack) for + function call. + + nb_args: number of parameters the function take + plan: the overall assignment plan for parameters + todo: a bitmap indicating what core reg will hold a parameter + + Returns the number of SValue added by this function on the value stack */ +static int copy_params(int nb_args, struct plan *plan, int todo) +{ + int size, align, r, i, nb_extra_sval = 0; + struct param_plan *pplan; + int pass = 0; + + /* Several constraints require parameters to be copied in a specific order: + - structures are copied to the stack before being loaded in a reg; + - floats loaded to an odd numbered VFP reg are first copied to the + preceding even numbered VFP reg and then moved to the next VFP reg. + + It is thus important that: + - structures assigned to core regs must be copied after parameters + assigned to the stack but before structures assigned to VFP regs because + a structure can lie partly in core registers and partly on the stack; + - parameters assigned to the stack and all structures be copied before + parameters assigned to a core reg since copying a parameter to the stack + require using a core reg; + - parameters assigned to VFP regs be copied before structures assigned to + VFP regs as the copy might use an even numbered VFP reg that already + holds part of a structure. */ +again: + for(i = 0; i < NB_CLASSES; i++) { + for(pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) { + + if (pass + && (i != CORE_CLASS || pplan->sval->r < VT_CONST)) + continue; + + vpushv(pplan->sval); + pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */ + switch(i) { + case STACK_CLASS: + case CORE_STRUCT_CLASS: + case VFP_STRUCT_CLASS: + if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) { + int padding = 0; + size = type_size(&pplan->sval->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + if (i == STACK_CLASS && pplan->prev) + padding = pplan->start - pplan->prev->end; + size += padding; /* Add padding if any */ + /* allocate the necessary size on stack */ + gadd_sp(-size); + /* generate structure store */ + r = get_reg(RC_INT); + o(0xE28D0000|(intr(r)<<12)|padding); /* add r, sp, padding */ + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + /* XXX: optimize. Save all register because memcpy can use them */ + o(0xED2D0A00|(0&1)<<22|(0>>1)<<12|16); /* vpush {s0-s15} */ + vstore(); /* memcpy to current sp + potential padding */ + o(0xECBD0A00|(0&1)<<22|(0>>1)<<12|16); /* vpop {s0-s15} */ + + /* Homogeneous float aggregate are loaded to VFP registers + immediately since there is no way of loading data in multiple + non consecutive VFP registers as what is done for other + structures (see the use of todo). */ + if (i == VFP_STRUCT_CLASS) { + int first = pplan->start, nb = pplan->end - first + 1; + /* vpop.32 {pplan->start, ..., pplan->end} */ + o(0xECBD0A00|(first&1)<<22|(first>>1)<<12|nb); + /* No need to write the register used to a SValue since VFP regs + cannot be used for gcall_or_jmp */ + } + } else { + if (is_float(pplan->sval->type.t)) { +#ifdef TCC_ARM_VFP + r = vfpr(gv(RC_FLOAT)) << 12; + if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else { + size = 8; + r |= 0x101; /* vpush.32 -> vpush.64 */ + } + o(0xED2D0A01 + r); /* vpush */ +#else + r = fpr(gv(RC_FLOAT)) << 12; + if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE) + size = 8; + else + size = LDOUBLE_SIZE; + + if (size == 12) + r |= 0x400000; + else if(size == 8) + r|=0x8000; + + o(0xED2D0100|r|(size>>2)); /* some kind of vpush for FPA */ +#endif + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + size=4; + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { + lexpand(); + size = 8; + r = gv(RC_INT); + o(0xE52D0004|(intr(r)<<12)); /* push r */ + vtop--; + } + r = gv(RC_INT); + o(0xE52D0004|(intr(r)<<12)); /* push r */ + } + if (i == STACK_CLASS && pplan->prev) + gadd_sp(pplan->prev->end - pplan->start); /* Add padding if any */ + } + break; + + case VFP_CLASS: + gv(regmask(TREG_F0 + (pplan->start >> 1))); + if (pplan->start & 1) { /* Must be in upper part of double register */ + o(0xEEF00A40|((pplan->start>>1)<<12)|(pplan->start>>1)); /* vmov.f32 s(n+1), sn */ + vtop->r = VT_CONST; /* avoid being saved on stack by gv for next float */ + } + break; + + case CORE_CLASS: + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { + lexpand(); + gv(regmask(pplan->end)); + pplan->sval->r2 = vtop->r; + vtop--; + } + gv(regmask(pplan->start)); + /* Mark register as used so that gcall_or_jmp use another one + (regs >=4 are free as never used to pass parameters) */ + pplan->sval->r = vtop->r; + break; + } + vtop--; + } + } + + /* second pass to restore registers that were saved on stack by accident. + Maybe redundant after the "lvalue_save" patch in tccgen.c:gv() */ + if (++pass < 2) + goto again; + + /* Manually free remaining registers since next parameters are loaded + * manually, without the help of gv(int). */ + save_regs(nb_args); + + if(todo) { + o(0xE8BD0000|todo); /* pop {todo} */ + for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) { + int r; + pplan->sval->r = pplan->start; + /* An SValue can only pin 2 registers at best (r and r2) but a structure + can occupy more than 2 registers. Thus, we need to push on the value + stack some fake parameter to have on SValue for each registers used + by a structure (r2 is not used). */ + for (r = pplan->start + 1; r <= pplan->end; r++) { + if (todo & (1 << r)) { + nb_extra_sval++; + vpushi(0); + vtop->r = r; + } + } + } + } + return nb_extra_sval; +} + +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ +void gfunc_call(int nb_args) +{ + int r, args_size; + int def_float_abi = float_abi; + int todo; + struct plan plan; +#ifdef TCC_ARM_EABI + int variadic; +#endif + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT) { + variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS); + if (variadic || floats_in_core_regs(&vtop[-nb_args])) + float_abi = ARM_SOFTFP_FLOAT; + } +#endif + /* cannot let cpu flags if other instruction are generated. Also avoid leaving + VT_JMP anywhere except on the top of the stack because it would complicate + the code generator. */ + r = vtop->r & VT_VALMASK; + if (r == VT_CMP || (r & ~1) == VT_JMP) + gv(RC_INT); + + memset(&plan, 0, sizeof plan); + if (nb_args) + plan.pplans = tcc_malloc(nb_args * sizeof(*plan.pplans)); + + args_size = assign_regs(nb_args, float_abi, &plan, &todo); + +#ifdef TCC_ARM_EABI + if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ + args_size = (args_size + 7) & ~7; + o(0xE24DD004); /* sub sp, sp, #4 */ + } +#endif + + nb_args += copy_params(nb_args, &plan, todo); + tcc_free(plan.pplans); + + /* Move fct SValue on top as required by gcall_or_jmp */ + vrotb(nb_args + 1); + gcall_or_jmp(0); + if (args_size) + gadd_sp(args_size); /* pop all parameters passed on the stack */ +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + if(float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) { + if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { + o(0xEE000A10); /*vmov s0, r0 */ + } else { + o(0xEE000B10); /* vmov.32 d0[0], r0 */ + o(0xEE201B10); /* vmov.32 d0[1], r1 */ + } + } +#endif + vtop -= nb_args + 1; /* Pop all params and fct address from value stack */ + leaffunc = 0; /* we are calling a function, so we aren't in a leaf function */ + float_abi = def_float_abi; +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(Sym *func_sym) +{ + CType *func_type = &func_sym->type; + Sym *sym,*sym2; + int n, nf, size, align, rs, struct_ret = 0; + int addr, pn, sn; /* pn=core, sn=stack */ + CType ret_type; + +#ifdef TCC_ARM_EABI + struct avail_regs avregs = {{0}}; +#endif + + sym = func_type->ref; + + n = nf = 0; + if ((func_vt.t & VT_BTYPE) == VT_STRUCT && + !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs)) + { + n++; + struct_ret = 1; + func_vc = 12; /* Offset from fp of the place to store the result */ + } + for(sym2 = sym->next; sym2 && (n < 4 || nf < 16); sym2 = sym2->next) { + size = type_size(&sym2->type, &align); +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT && !func_var && + (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) { + int tmpnf = assign_vfpreg(&avregs, align, size); + tmpnf += (size + 3) / 4; + nf = (tmpnf > nf) ? tmpnf : nf; + } else +#endif + if (n < 4) + n += (size + 3) / 4; + } + o(0xE1A0C00D); /* mov ip,sp */ + if (func_var) + n=4; + if (n) { + if(n>4) + n=4; +#ifdef TCC_ARM_EABI + n=(n+1)&-2; +#endif + o(0xE92D0000|((1<16) + nf=16; + nf=(nf+1)&-2; /* nf => HARDFLOAT => EABI */ + o(0xED2D0A00|nf); /* save s0-s15 on stack if needed */ + } + o(0xE92D5800); /* save fp, ip, lr */ + o(0xE1A0B00D); /* mov fp, sp */ + func_sub_sp_offset = ind; + o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */ + +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT) { + func_vc += nf * 4; + memset(&avregs, 0, sizeof avregs); + } +#endif + pn = struct_ret, sn = 0; + while ((sym = sym->next)) { + CType *type; + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) >> 2; + align = (align + 3) & ~3; +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT && !func_var && (is_float(sym->type.t) + || is_hgen_float_aggr(&sym->type))) { + int fpn = assign_vfpreg(&avregs, align, size << 2); + if (fpn >= 0) + addr = fpn * 4; + else + goto from_stack; + } else +#endif + if (pn < 4) { +#ifdef TCC_ARM_EABI + pn = (pn + (align-1)/4) & -(align/4); +#endif + addr = (nf + pn) * 4; + pn += size; + if (!sn && pn > 4) + sn = (pn - 4); + } else { +#ifdef TCC_ARM_EABI +from_stack: + sn = (sn + (align-1)/4) & -(align/4); +#endif + addr = (n + nf + sn) * 4; + sn += size; + } + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, + addr + 12); + } + last_itod_magic=0; + leaffunc = 1; + loc = 0; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + uint32_t x; + int diff; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif + /* Copy float return value to core register if base standard is used and + float computation is made with VFP */ +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) { + if((func_vt.t & VT_BTYPE) == VT_FLOAT) + o(0xEE100A10); /* fmrs r0, s0 */ + else { + o(0xEE100B10); /* fmrdl r0, d0 */ + o(0xEE301B10); /* fmrdh r1, d0 */ + } + } +#endif + o(0xE89BA800); /* restore fp, sp, pc */ + diff = (-loc + 3) & -4; +#ifdef TCC_ARM_EABI + if(!leaffunc) + diff = ((diff + 11) & -8) - 4; +#endif + if(diff > 0) { + x=stuff_const(0xE24BD000, diff); /* sub sp,fp,# */ + if(x) + *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = x; + else { + int addr; + addr=ind; + o(0xE59FC004); /* ldr ip,[pc+4] */ + o(0xE04BD00C); /* sub sp,fp,ip */ + o(0xE1A0F00E); /* mov pc,lr */ + o(diff); + *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); + } + } +} + +ST_FUNC void gen_fill_nops(int bytes) +{ + if ((bytes & 3)) + tcc_error("alignment of code section not multiple of 4"); + while (bytes > 0) { + o(0xE1A00000); + bytes -= 4; + } +} + +/* generate a jump to a label */ +ST_FUNC int gjmp(int t) +{ + int r; + if (nocode_wanted) + return t; + r=ind; + o(0xE0000000|encbranch(r,t,1)); + return r; +} + +/* generate a jump to a fixed address */ +ST_FUNC void gjmp_addr(int a) +{ + gjmp(a); +} + +ST_FUNC int gjmp_cond(int op, int t) +{ + int r; + if (nocode_wanted) + return t; + r=ind; + op=mapcc(op); + op|=encbranch(r,t,1); + o(op); + return r; +} + +ST_FUNC int gjmp_append(int n, int t) +{ + uint32_t *x; + int p,lp; + if(n) { + p = n; + do { + p = decbranch(lp=p); + } while(p); + x = (uint32_t *)(cur_text_section->data + lp); + *x &= 0xff000000; + *x |= encbranch(lp,t,1); + t = n; + } + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int c, func = 0; + uint32_t opc = 0, r, fr; + unsigned short retreg = REG_IRET; + + c=0; + switch(op) { + case '+': + opc = 0x8; + c=1; + break; + case TOK_ADDC1: /* add with carry generation */ + opc = 0x9; + c=1; + break; + case '-': + opc = 0x4; + c=1; + break; + case TOK_SUBC1: /* sub with carry generation */ + opc = 0x5; + c=1; + break; + case TOK_ADDC2: /* add with carry use */ + opc = 0xA; + c=1; + break; + case TOK_SUBC2: /* sub with carry use */ + opc = 0xC; + c=1; + break; + case '&': + opc = 0x0; + c=1; + break; + case '^': + opc = 0x2; + c=1; + break; + case '|': + opc = 0x18; + c=1; + break; + case '*': + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + o(0xE0000090|(intr(r)<<16)|(intr(r)<<8)|intr(fr)); + return; + case TOK_SHL: + opc = 0; + c=2; + break; + case TOK_SHR: + opc = 1; + c=2; + break; + case TOK_SAR: + opc = 2; + c=2; + break; + case '/': + case TOK_PDIV: + func=TOK___divsi3; + c=3; + break; + case TOK_UDIV: + func=TOK___udivsi3; + c=3; + break; + case '%': +#ifdef TCC_ARM_EABI + func=TOK___aeabi_idivmod; + retreg=REG_IRE2; +#else + func=TOK___modsi3; +#endif + c=3; + break; + case TOK_UMOD: +#ifdef TCC_ARM_EABI + func=TOK___aeabi_uidivmod; + retreg=REG_IRE2; +#else + func=TOK___umodsi3; +#endif + c=3; + break; + case TOK_UMULL: + gv2(RC_INT, RC_INT); + r=intr(vtop[-1].r2=get_reg(RC_INT)); + c=vtop[-1].r; + vtop[-1].r=get_reg_ex(RC_INT,regmask(c)); + vtop--; + o(0xE0800090|(r<<16)|(intr(vtop->r)<<12)|(intr(c)<<8)|intr(vtop[1].r)); + return; + default: + opc = 0x15; + c=1; + break; + } + switch(c) { + case 1: + if((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + if(opc == 4 || opc == 5 || opc == 0xc) { + vswap(); + opc|=2; // sub -> rsb + } + } + if ((vtop->r & VT_VALMASK) == VT_CMP || + (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) + gv(RC_INT); + vswap(); + c=intr(gv(RC_INT)); + vswap(); + opc=0xE0000000|(opc<<20); + if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + uint32_t x; + x=stuff_const(opc|0x2000000|(c<<16),vtop->c.i); + if(x) { + if ((x & 0xfff00000) == 0xe3500000) // cmp rx,#c + o(x); + else { + r=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); + o(x|(r<<12)); + } + goto done; + } + } + fr=intr(gv(RC_INT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + c=intr(gv(RC_INT)); + vswap(); + } +#endif + if ((opc & 0xfff00000) == 0xe1500000) // cmp rx,ry + o(opc|(c<<16)|fr); + else { + r=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); + o(opc|(c<<16)|(r<<12)|fr); + } +done: + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) + vset_VT_CMP(op); + break; + case 2: + opc=0xE1A00000|(opc<<5); + if ((vtop->r & VT_VALMASK) == VT_CMP || + (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) + gv(RC_INT); + vswap(); + r=intr(gv(RC_INT)); + vswap(); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + fr=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); + c = vtop->c.i & 0x1f; + o(opc|r|(c<<7)|(fr<<12)); + } else { + fr=intr(gv(RC_INT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=intr(gv(RC_INT)); + vswap(); + } +#endif + c=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); + o(opc|r|(c<<12)|(fr<<8)|0x10); + } + vtop--; + break; + case 3: + vpush_helper_func(func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = retreg; + break; + default: + tcc_error("gen_opi %i unimplemented!",op); + } +} + +#ifdef TCC_ARM_VFP +static int is_zero(int i) +{ + if((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop[i].type.t == VT_FLOAT) + return (vtop[i].c.f == 0.f); + else if (vtop[i].type.t == VT_DOUBLE) + return (vtop[i].c.d == 0.0); + return (vtop[i].c.ld == 0.l); +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + * two operands are guaranteed to have the same floating point type */ +void gen_opf(int op) +{ + uint32_t x; + int fneg=0,r; + x=0xEE000A00|T2CPR(vtop->type.t); + switch(op) { + case '+': + if(is_zero(-1)) + vswap(); + if(is_zero(0)) { + vtop--; + return; + } + x|=0x300000; + break; + case '-': + x|=0x300040; + if(is_zero(0)) { + vtop--; + return; + } + if(is_zero(-1)) { + x|=0x810000; /* fsubX -> fnegX */ + vswap(); + vtop--; + fneg=1; + } + break; + case '*': + x|=0x200000; + break; + case '/': + x|=0x800000; + break; + default: + if(op < TOK_ULT || op > TOK_GT) { + tcc_error("unknown fp op %x!",op); + return; + } + if(is_zero(-1)) { + vswap(); + switch(op) { + case TOK_LT: op=TOK_GT; break; + case TOK_GE: op=TOK_ULE; break; + case TOK_LE: op=TOK_GE; break; + case TOK_GT: op=TOK_ULT; break; + } + } + x|=0xB40040; /* fcmpX */ + if(op!=TOK_EQ && op!=TOK_NE) + x|=0x80; /* fcmpX -> fcmpeX */ + if(is_zero(0)) { + vtop--; + o(x|0x10000|(vfpr(gv(RC_FLOAT))<<12)); /* fcmp(e)X -> fcmp(e)zX */ + } else { + gv2(RC_FLOAT,RC_FLOAT); + x|=vfpr(vtop[0].r); + o(x|(vfpr(vtop[-1].r) << 12)); + vtop--; + } + o(0xEEF1FA10); /* fmstat */ + + switch(op) { + case TOK_LE: op=TOK_ULE; break; + case TOK_LT: op=TOK_ULT; break; + case TOK_UGE: op=TOK_GE; break; + case TOK_UGT: op=TOK_GT; break; + } + vset_VT_CMP(op); + return; + } + r=gv(RC_FLOAT); + x|=vfpr(r); + r=regmask(r); + if(!fneg) { + int r2; + vswap(); + r2=gv(RC_FLOAT); + x|=vfpr(r2)<<16; + r|=regmask(r2); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=gv(RC_FLOAT); + vswap(); + x=(x&~0xf)|vfpr(r); + } +#endif + } + vtop->r=get_reg_ex(RC_FLOAT,r); + if(!fneg) + vtop--; + o(x|(vfpr(vtop->r)<<12)); +} + +#else +static uint32_t is_fconst() +{ + long double f; + uint32_t r; + if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop->type.t == VT_FLOAT) + f = vtop->c.f; + else if (vtop->type.t == VT_DOUBLE) + f = vtop->c.d; + else + f = vtop->c.ld; + if(!ieee_finite(f)) + return 0; + r=0x8; + if(f<0.0) { + r=0x18; + f=-f; + } + if(f==0.0) + return r; + if(f==1.0) + return r|1; + if(f==2.0) + return r|2; + if(f==3.0) + return r|3; + if(f==4.0) + return r|4; + if(f==5.0) + return r|5; + if(f==0.5) + return r|6; + if(f==10.0) + return r|7; + return 0; +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranteed to have the same floating point type */ +void gen_opf(int op) +{ + uint32_t x, r, r2, c1, c2; + //fputs("gen_opf\n",stderr); + vswap(); + c1 = is_fconst(); + vswap(); + c2 = is_fconst(); + x=0xEE000100; +#if LDOUBLE_SIZE == 8 + if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) + x|=0x80; +#else + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + x|=0x80; + else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) + x|=0x80000; +#endif + switch(op) + { + case '+': + if(!c2) { + vswap(); + c2=c1; + } + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + if(c2) { + if(c2>0xf) + x|=0x200000; // suf + r2=c2&0xf; + } else { + r2=fpr(gv(RC_FLOAT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } +#endif + } + break; + case '-': + if(c2) { + if(c2<=0xf) + x|=0x200000; // suf + r2=c2&0xf; + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } else if(c1 && c1<=0xf) { + x|=0x300000; // rsf + r2=c1; + r=fpr(gv(RC_FLOAT)); + vswap(); + } else { + x|=0x200000; // suf + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + r2=fpr(gv(RC_FLOAT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } +#endif + } + break; + case '*': + if(!c2 || c2>0xf) { + vswap(); + c2=c1; + } + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + if(c2 && c2<=0xf) + r2=c2; + else { + r2=fpr(gv(RC_FLOAT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } +#endif + } + x|=0x100000; // muf + break; + case '/': + if(c2 && c2<=0xf) { + x|=0x400000; // dvf + r2=c2; + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } else if(c1 && c1<=0xf) { + x|=0x500000; // rdf + r2=c1; + r=fpr(gv(RC_FLOAT)); + vswap(); + } else { + x|=0x400000; // dvf + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + r2=fpr(gv(RC_FLOAT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } +#endif + } + break; + default: + if(op >= TOK_ULT && op <= TOK_GT) { + x|=0xd0f110; // cmfe +/* bug (intention?) in Linux FPU emulator + doesn't set carry if equal */ + switch(op) { + case TOK_ULT: + case TOK_UGE: + case TOK_ULE: + case TOK_UGT: + tcc_error("unsigned comparison on floats?"); + break; + case TOK_LT: + op=TOK_Nset; + break; + case TOK_LE: + op=TOK_ULE; /* correct in unordered case only if AC bit in FPSR set */ + break; + case TOK_EQ: + case TOK_NE: + x&=~0x400000; // cmfe -> cmf + break; + } + if(c1 && !c2) { + c2=c1; + vswap(); + switch(op) { + case TOK_Nset: + op=TOK_GT; + break; + case TOK_GE: + op=TOK_ULE; + break; + case TOK_ULE: + op=TOK_GE; + break; + case TOK_GT: + op=TOK_Nset; + break; + } + } + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + if(c2) { + if(c2>0xf) + x|=0x200000; + r2=c2&0xf; + } else { + r2=fpr(gv(RC_FLOAT)); +#ifdef CONFIG_TCC_BCHECK + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } +#endif + } + --vtop; + vset_VT_CMP(op); + ++vtop; + } else { + tcc_error("unknown fp op %x!",op); + return; + } + } + if(vtop[-1].r == VT_CMP) + c1=15; + else { + c1=vtop->r; + if(r2&0x8) + c1=vtop[-1].r; + vtop[-1].r=get_reg_ex(RC_FLOAT,two2mask(vtop[-1].r,c1)); + c1=fpr(vtop[-1].r); + } + vtop--; + o(x|(r<<16)|(c1<<12)|r2); +} +#endif + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +ST_FUNC void gen_cvt_itof(int t) +{ + uint32_t r, r2; + int bt; + bt=vtop->type.t & VT_BTYPE; + if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { +#ifndef TCC_ARM_VFP + uint32_t dsize = 0; +#endif + r=intr(gv(RC_INT)); +#ifdef TCC_ARM_VFP + r2=vfpr(vtop->r=get_reg(RC_FLOAT)); + o(0xEE000A10|(r<<12)|(r2<<16)); /* fmsr */ + r2|=r2<<12; + if(!(vtop->type.t & VT_UNSIGNED)) + r2|=0x80; /* fuitoX -> fsituX */ + o(0xEEB80A40|r2|T2CPR(t)); /* fYitoX*/ +#else + r2=fpr(vtop->r=get_reg(RC_FLOAT)); + if((t & VT_BTYPE) != VT_FLOAT) + dsize=0x80; /* flts -> fltd */ + o(0xEE000110|dsize|(r2<<16)|(r<<12)); /* flts */ + if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { + uint32_t off = 0; + o(0xE3500000|(r<<12)); /* cmp */ + r=fpr(get_reg(RC_FLOAT)); + if(last_itod_magic) { + off=ind+8-last_itod_magic; + off/=4; + if(off>255) + off=0; + } + o(0xBD1F0100|(r<<12)|off); /* ldflts */ + if(!off) { + o(0xEA000000); /* b */ + last_itod_magic=ind; + o(0x4F800000); /* 4294967296.0f */ + } + o(0xBE000100|dsize|(r2<<16)|(r2<<12)|r); /* adflt */ + } +#endif + return; + } else if(bt == VT_LLONG) { + int func; + CType *func_type = 0; + if((t & VT_BTYPE) == VT_FLOAT) { + func_type = &func_float_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundisf; + else + func=TOK___floatdisf; +#if LDOUBLE_SIZE != 8 + } else if((t & VT_BTYPE) == VT_LDOUBLE) { + func_type = &func_ldouble_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundixf; + else + func=TOK___floatdixf; + } else if((t & VT_BTYPE) == VT_DOUBLE) { +#else + } else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) { +#endif + func_type = &func_double_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundidf; + else + func=TOK___floatdidf; + } + if(func_type) { + vpushsym(func_type, external_helper_sym(func)); + vswap(); + gfunc_call(1); + vpushi(0); + vtop->r=TREG_F0; + return; + } + } + tcc_error("unimplemented gen_cvt_itof %x!",vtop->type.t); +} + +/* convert fp to int 't' type */ +void gen_cvt_ftoi(int t) +{ + uint32_t r, r2; + int u, func = 0; + u=t&VT_UNSIGNED; + t&=VT_BTYPE; + r2=vtop->type.t & VT_BTYPE; + if(t==VT_INT) { +#ifdef TCC_ARM_VFP + r=vfpr(gv(RC_FLOAT)); + u=u?0:0x10000; + o(0xEEBC0AC0|(r<<12)|r|T2CPR(r2)|u); /* ftoXizY */ + r2=intr(vtop->r=get_reg(RC_INT)); + o(0xEE100A10|(r<<16)|(r2<<12)); + return; +#else + if(u) { + if(r2 == VT_FLOAT) + func=TOK___fixunssfsi; +#if LDOUBLE_SIZE != 8 + else if(r2 == VT_LDOUBLE) + func=TOK___fixunsxfsi; + else if(r2 == VT_DOUBLE) +#else + else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) +#endif + func=TOK___fixunsdfsi; + } else { + r=fpr(gv(RC_FLOAT)); + r2=intr(vtop->r=get_reg(RC_INT)); + o(0xEE100170|(r2<<12)|r); + return; + } +#endif + } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 + if(r2 == VT_FLOAT) + func=TOK___fixsfdi; +#if LDOUBLE_SIZE != 8 + else if(r2 == VT_LDOUBLE) + func=TOK___fixxfdi; + else if(r2 == VT_DOUBLE) +#else + else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) +#endif + func=TOK___fixdfdi; + } + if(func) { + vpush_helper_func(func); + vswap(); + gfunc_call(1); + vpushi(0); + if(t == VT_LLONG) + vtop->r2 = REG_IRE2; + vtop->r = REG_IRET; + return; + } + tcc_error("unimplemented gen_cvt_ftoi!"); +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ +#ifdef TCC_ARM_VFP + if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) { + uint32_t r = vfpr(gv(RC_FLOAT)); + o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t)); + } +#else + /* all we have to do on i386 and FPA ARM is to put the float in a register */ + gv(RC_FLOAT); +#endif +} + +/* increment tcov counter */ +ST_FUNC void gen_increment_tcov (SValue *sv) +{ + int r1, r2; + + vpushv(sv); + vtop->r = r1 = get_reg(RC_INT); + r2 = get_reg(RC_INT); + o(0xE59F0000 | (intr(r1)<<12)); // ldr r1,[pc] + o(0xEA000000); // b $+4 + greloc(cur_text_section, sv->sym, ind, R_ARM_REL32); + o(-12); + o(0xe080000f | (intr(r1)<<16) | (intr(r1)<<12)); // add r1,r1,pc + o(0xe5900000 | (intr(r1)<<16) | (intr(r2)<<12)); // ldr r2, [r1] + o(0xe2900001 | (intr(r2)<<16) | (intr(r2)<<12)); // adds r2, r2, #1 + o(0xe5800000 | (intr(r1)<<16) | (intr(r2)<<12)); // str r2, [r1] + o(0xe2800004 | (intr(r1)<<16) | (intr(r1)<<12)); // add r1, r1, #4 + o(0xe5900000 | (intr(r1)<<16) | (intr(r2)<<12)); // ldr r2, [r1] + o(0xe2a00000 | (intr(r2)<<16) | (intr(r2)<<12)); // adc r2, r2, #0 + o(0xe5800000 | (intr(r1)<<16) | (intr(r2)<<12)); // str r2, [r1] + vpop(); +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* Save the stack pointer onto the stack and return the location of its address */ +ST_FUNC void gen_vla_sp_save(int addr) { + SValue v; + v.type.t = VT_PTR; + v.r = VT_LOCAL | VT_LVAL; + v.c.i = addr; + store(TREG_SP, &v); +} + +/* Restore the SP from a location on the stack */ +ST_FUNC void gen_vla_sp_restore(int addr) { + SValue v; + v.type.t = VT_PTR; + v.r = VT_LOCAL | VT_LVAL; + v.c.i = addr; + load(TREG_SP, &v); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack */ +ST_FUNC void gen_vla_alloc(CType *type, int align) { + int r; +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) + vpushv(vtop); +#endif + r = intr(gv(RC_INT)); +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) + o(0xe2800001 | (r<<16)|(r<<12)); /* add r,r,#1 */ +#endif + o(0xE04D0000|(r<<12)|r); /* sub r, sp, r */ +#ifdef TCC_ARM_EABI + if (align < 8) + align = 8; +#else + if (align < 4) + align = 4; +#endif + if (align & (align - 1)) + tcc_error("alignment is not a power of 2: %i", align); + o(stuff_const(0xE3C0D000|(r<<16), align - 1)); /* bic sp, r, #align-1 */ + vpop(); +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) { + vpushi(0); + vtop->r = TREG_R0; + o(0xe1a0000d | (vtop->r << 12)); // mov r0,sp + vswap(); + vpush_helper_func(TOK___bound_new_region); + vrott(3); + gfunc_call(2); + func_bound_add_epilog = 1; + } +#endif +} + +/* end of ARM code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/vendor/tcc/arm-link.c b/vendor/tcc/arm-link.c new file mode 100644 index 00000000..242bb945 --- /dev/null +++ b/vendor/tcc/arm-link.c @@ -0,0 +1,444 @@ +#ifdef TARGET_DEFS_ONLY + +#define EM_TCC_TARGET EM_ARM + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_ARM_ABS32 +#define R_DATA_PTR R_ARM_ABS32 +#define R_JMP_SLOT R_ARM_JUMP_SLOT +#define R_GLOB_DAT R_ARM_GLOB_DAT +#define R_COPY R_ARM_COPY +#define R_RELATIVE R_ARM_RELATIVE + +#define R_NUM R_ARM_NUM + +#define ELF_START_ADDR 0x00010000 +#define ELF_PAGE_SIZE 0x10000 + +#define PCRELATIVE_DLLPLT 1 +#define RELOCATE_DLLPLT 1 + +enum float_abi { + ARM_SOFTFP_FLOAT, + ARM_HARD_FLOAT, +}; + +#else /* !TARGET_DEFS_ONLY */ + +#include "tcc.h" + +#ifdef NEED_RELOC_TYPE +/* Returns 1 for a code relocation, 0 for a data relocation. For unknown + relocations, returns -1. */ +int code_reloc (int reloc_type) +{ + switch (reloc_type) { + case R_ARM_MOVT_ABS: + case R_ARM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_ABS32: + case R_ARM_REL32: + case R_ARM_GOTPC: + case R_ARM_GOTOFF: + case R_ARM_GOT32: + case R_ARM_GOT_PREL: + case R_ARM_COPY: + case R_ARM_GLOB_DAT: + case R_ARM_NONE: + case R_ARM_TARGET1: + case R_ARM_MOVT_PREL: + case R_ARM_MOVW_PREL_NC: + return 0; + + case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PLT32: + case R_ARM_THM_PC22: + case R_ARM_THM_JUMP24: + case R_ARM_PREL31: + case R_ARM_V4BX: + case R_ARM_JUMP_SLOT: + return 1; + } + return -1; +} + +/* Returns an enumerator to describe whether and when the relocation needs a + GOT and/or PLT entry to be created. See tcc.h for a description of the + different values. */ +int gotplt_entry_type (int reloc_type) +{ + switch (reloc_type) { + case R_ARM_NONE: + case R_ARM_COPY: + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + return NO_GOTPLT_ENTRY; + + case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PLT32: + case R_ARM_THM_PC22: + case R_ARM_THM_JUMP24: + case R_ARM_MOVT_ABS: + case R_ARM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_PREL31: + case R_ARM_ABS32: + case R_ARM_REL32: + case R_ARM_V4BX: + case R_ARM_TARGET1: + case R_ARM_MOVT_PREL: + case R_ARM_MOVW_PREL_NC: + return AUTO_GOTPLT_ENTRY; + + case R_ARM_GOTPC: + case R_ARM_GOTOFF: + return BUILD_GOT_ONLY; + + case R_ARM_GOT32: + case R_ARM_GOT_PREL: + return ALWAYS_GOTPLT_ENTRY; + } + return -1; +} + +#ifdef NEED_BUILD_GOT +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 example above) */ + + /* 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); + /* save GOT offset for relocate_plt */ + write32le(p + 4, 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 - 4); + p += 20; + while (p < p_end) { + unsigned off = x + read32le(p + 4) + (s1->plt->data - p) + 4; + if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */ + p += 4; + write32le(p, 0xe28fc200 | ((off >> 28) & 0xf)); // add ip, pc, #0xN0000000 + write32le(p + 4, 0xe28cc600 | ((off >> 20) & 0xff)); // add ip, pc, #0xNN00000 + write32le(p + 8, 0xe28cca00 | ((off >> 12) & 0xff)); // add ip, ip, #0xNN000 + write32le(p + 12, 0xe5bcf000 | (off & 0xfff)); // ldr pc, [ip, #0xNNN]! + p += 16; + } + } + + if (s1->plt->reloc) { + ElfW_Rel *rel; + p = s1->got->data; + for_each_elem(s1->plt->reloc, 0, rel, ElfW_Rel) { + write32le(p + rel->r_offset, s1->plt->sh_addr); + } + } +} +#endif +#endif + +void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) +{ + ElfW(Sym) *sym; + int sym_index, esym_index; + + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + + switch(type) { + case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PLT32: + { + int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko; + x = (*(int *) ptr) & 0xffffff; +#ifdef DEBUG_RELOC + printf ("reloc %d: x=0x%x val=0x%x ", type, x, val); +#endif + (*(int *)ptr) &= 0xff000000; + if (x & 0x800000) + x -= 0x1000000; + x <<= 2; + blx_avail = (TCC_CPU_VERSION >= 5); + is_thumb = val & 1; + is_bl = (*(unsigned *) ptr) >> 24 == 0xeb; + is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl)); + x += val - addr; +#ifdef DEBUG_RELOC + printf (" newx=0x%x name=%s\n", x, + (char *) symtab_section->link->data + sym->st_name); +#endif + h = x & 2; + th_ko = (x & 3) && (!blx_avail || !is_call); + if (th_ko || x >= 0x2000000 || x < -0x2000000) + tcc_error_noabort("can't relocate value at %x,%d",addr, type); + x >>= 2; + x &= 0xffffff; + /* Only reached if blx is avail and it is a call */ + if (is_thumb) { + x |= h << 24; + (*(int *)ptr) = 0xfa << 24; /* bl -> blx */ + } + (*(int *) ptr) |= x; + } + return; + /* Since these relocations only concern Thumb-2 and blx instruction was + introduced before Thumb-2, we can assume blx is available and not + guard its use */ + case R_ARM_THM_PC22: + case R_ARM_THM_JUMP24: + { + int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11; + int to_thumb, is_call, to_plt, blx_bit = 1 << 12; + Section *plt; + + /* weak reference */ + if (sym->st_shndx == SHN_UNDEF && + ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + return; + + /* Get initial offset */ + hi = (*(uint16_t *)ptr); + lo = (*(uint16_t *)(ptr+2)); + s = (hi >> 10) & 1; + j1 = (lo >> 13) & 1; + j2 = (lo >> 11) & 1; + i1 = (j1 ^ s) ^ 1; + i2 = (j2 ^ s) ^ 1; + imm10 = hi & 0x3ff; + imm11 = lo & 0x7ff; + x = (s << 24) | (i1 << 23) | (i2 << 22) | + (imm10 << 12) | (imm11 << 1); + if (x & 0x01000000) + x -= 0x02000000; + + /* Relocation infos */ + to_thumb = val & 1; + plt = s1->plt; + to_plt = (val >= plt->sh_addr) && + (val < plt->sh_addr + plt->data_offset); + is_call = (type == R_ARM_THM_PC22); + + if (!to_thumb && !to_plt && !is_call) { + int index; + uint8_t *p; + char *name, buf[1024]; + Section *text; + + name = (char *) symtab_section->link->data + sym->st_name; + text = s1->sections[sym->st_shndx]; + /* Modify reloc to target a thumb stub to switch to ARM */ + snprintf(buf, sizeof(buf), "%s_from_thumb", name); + index = put_elf_sym(symtab_section, + text->data_offset + 1, + sym->st_size, sym->st_info, 0, + sym->st_shndx, buf); + to_thumb = 1; + val = text->data_offset + 1; + rel->r_info = ELFW(R_INFO)(index, type); + /* Create a thumb stub function to switch to ARM mode */ + put_elf_reloc(symtab_section, text, + text->data_offset + 4, R_ARM_JUMP24, + sym_index); + p = section_ptr_add(text, 8); + write32le(p, 0x4778); /* bx pc */ + write32le(p+2, 0x46c0); /* nop */ + write32le(p+4, 0xeafffffe); /* b $sym */ + } + + /* Compute final offset */ + x += val - addr; + if (!to_thumb && is_call) { + blx_bit = 0; /* bl -> blx */ + x = (x + 3) & -4; /* Compute offset from aligned PC */ + } + + /* Check that relocation is possible + * offset must not be out of range + * if target is to be entered in arm mode: + - bit 1 must not set + - instruction must be a call (bl) or a jump to PLT */ + if (!to_thumb || x >= 0x1000000 || x < -0x1000000) + if (to_thumb || (val & 2) || (!is_call && !to_plt)) + tcc_error_noabort("can't relocate value at %x,%d",addr, type); + + /* Compute and store final offset */ + s = (x >> 24) & 1; + i1 = (x >> 23) & 1; + i2 = (x >> 22) & 1; + j1 = s ^ (i1 ^ 1); + j2 = s ^ (i2 ^ 1); + imm10 = (x >> 12) & 0x3ff; + imm11 = (x >> 1) & 0x7ff; + (*(uint16_t *)ptr) = (uint16_t) ((hi & 0xf800) | + (s << 10) | imm10); + (*(uint16_t *)(ptr+2)) = (uint16_t) ((lo & 0xc000) | + (j1 << 13) | blx_bit | (j2 << 11) | + imm11); + } + return; + case R_ARM_MOVT_ABS: + case R_ARM_MOVW_ABS_NC: + { + int x, imm4, imm12; + if (type == R_ARM_MOVT_ABS) + val >>= 16; + imm12 = val & 0xfff; + imm4 = (val >> 12) & 0xf; + x = (imm4 << 16) | imm12; + if (type == R_ARM_THM_MOVT_ABS) + *(int *)ptr |= x; + else + *(int *)ptr += x; + } + return; + case R_ARM_MOVT_PREL: + case R_ARM_MOVW_PREL_NC: + { + int insn = *(int *)ptr; + int addend = ((insn >> 4) & 0xf000) | (insn & 0xfff); + + addend = (addend ^ 0x8000) - 0x8000; + val += addend - addr; + if (type == R_ARM_MOVT_PREL) + val >>= 16; + *(int *)ptr = (insn & 0xfff0f000) | + ((val & 0xf000) << 4) | (val & 0xfff); + } + return; + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + { + int x, i, imm4, imm3, imm8; + if (type == R_ARM_THM_MOVT_ABS) + val >>= 16; + imm8 = val & 0xff; + imm3 = (val >> 8) & 0x7; + i = (val >> 11) & 1; + imm4 = (val >> 12) & 0xf; + x = (imm3 << 28) | (imm8 << 16) | (i << 10) | imm4; + if (type == R_ARM_THM_MOVT_ABS) + *(int *)ptr |= x; + else + *(int *)ptr += x; + } + return; + case R_ARM_PREL31: + { + int x; + x = (*(int *)ptr) & 0x7fffffff; + (*(int *)ptr) &= 0x80000000; + x = (x * 2) / 2; + x += val - addr; + if((x^(x>>1))&0x40000000) + tcc_error_noabort("can't relocate value at %x,%d",addr, type); + (*(int *)ptr) |= x & 0x7fffffff; + } + return; + case R_ARM_ABS32: + case R_ARM_TARGET1: + if (s1->output_type & TCC_OUTPUT_DYN) { + esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index; + qrel->r_offset = rel->r_offset; + if (esym_index) { + qrel->r_info = ELFW(R_INFO)(esym_index, R_ARM_ABS32); + qrel++; + return; + } else { + qrel->r_info = ELFW(R_INFO)(0, R_ARM_RELATIVE); + qrel++; + } + } + *(int *)ptr += val; + return; + case R_ARM_REL32: + *(int *)ptr += val - addr; + return; + case R_ARM_GOTPC: + *(int *)ptr += s1->got->sh_addr - addr; + return; + case R_ARM_GOTOFF: + *(int *)ptr += val - s1->got->sh_addr; + return; + case R_ARM_GOT32: + /* we load the got offset */ + *(int *)ptr += get_sym_attr(s1, sym_index, 0)->got_offset; + return; + case R_ARM_GOT_PREL: + /* we load the pc relative got offset */ + *(int *)ptr += s1->got->sh_addr + + get_sym_attr(s1, sym_index, 0)->got_offset - + addr; + return; + case R_ARM_COPY: + return; + case R_ARM_V4BX: + /* trade Thumb support for ARMv4 support */ + if ((0x0ffffff0 & *(int*)ptr) == 0x012FFF10) + *(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */ + return; + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + *(addr_t *)ptr = val; + return; + case R_ARM_NONE: + /* Nothing to do. Normally used to indicate a dependency + on a certain symbol (like for exception handling under EABI). */ + return; + case R_ARM_RELATIVE: +#ifdef TCC_TARGET_PE + add32le(ptr, val - s1->pe_imagebase); +#endif + /* do nothing */ + return; + default: + fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + return; + } +} + +#endif /* !TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/arm-tok.h b/vendor/tcc/arm-tok.h new file mode 100644 index 00000000..297b1055 --- /dev/null +++ b/vendor/tcc/arm-tok.h @@ -0,0 +1,383 @@ +/* ------------------------------------------------------------------ */ +/* WARNING: relative order of tokens is important. */ + +/* register */ + + DEF_ASM(r0) + DEF_ASM(r1) + DEF_ASM(r2) + DEF_ASM(r3) + DEF_ASM(r4) + DEF_ASM(r5) + DEF_ASM(r6) + DEF_ASM(r7) + DEF_ASM(r8) + DEF_ASM(r9) + DEF_ASM(r10) + DEF_ASM(r11) /* fp */ + DEF_ASM(r12) /* ip[c] */ + DEF_ASM(r13) /* sp */ + DEF_ASM(r14) /* lr */ + DEF_ASM(r15) /* pc */ + +/* register macros */ + + DEF_ASM(fp) /* alias for r11 */ + DEF_ASM(ip) /* alias for r12 */ + DEF_ASM(sp) /* alias for r13 */ + DEF_ASM(lr) /* alias for r14 */ + DEF_ASM(pc) /* alias for r15 */ + + /* coprocessors */ + + DEF_ASM(p0) + DEF_ASM(p1) + DEF_ASM(p2) + DEF_ASM(p3) + DEF_ASM(p4) + DEF_ASM(p5) + DEF_ASM(p6) + DEF_ASM(p7) + DEF_ASM(p8) + DEF_ASM(p9) + DEF_ASM(p10) + DEF_ASM(p11) + DEF_ASM(p12) + DEF_ASM(p13) + DEF_ASM(p14) + DEF_ASM(p15) + + /* coprocessor registers */ + + DEF_ASM(c0) + DEF_ASM(c1) + DEF_ASM(c2) + DEF_ASM(c3) + DEF_ASM(c4) + DEF_ASM(c5) + DEF_ASM(c6) + DEF_ASM(c7) + DEF_ASM(c8) + DEF_ASM(c9) + DEF_ASM(c10) + DEF_ASM(c11) + DEF_ASM(c12) + DEF_ASM(c13) + DEF_ASM(c14) + DEF_ASM(c15) + + /* single-precision VFP registers */ + + DEF_ASM(s0) + DEF_ASM(s1) + DEF_ASM(s2) + DEF_ASM(s3) + DEF_ASM(s4) + DEF_ASM(s5) + DEF_ASM(s6) + DEF_ASM(s7) + DEF_ASM(s8) + DEF_ASM(s9) + DEF_ASM(s10) + DEF_ASM(s11) + DEF_ASM(s12) + DEF_ASM(s13) + DEF_ASM(s14) + DEF_ASM(s15) + DEF_ASM(s16) + DEF_ASM(s17) + DEF_ASM(s18) + DEF_ASM(s19) + DEF_ASM(s20) + DEF_ASM(s21) + DEF_ASM(s22) + DEF_ASM(s23) + DEF_ASM(s24) + DEF_ASM(s25) + DEF_ASM(s26) + DEF_ASM(s27) + DEF_ASM(s28) + DEF_ASM(s29) + DEF_ASM(s30) + DEF_ASM(s31) + + /* double-precision VFP registers */ + + DEF_ASM(d0) + DEF_ASM(d1) + DEF_ASM(d2) + DEF_ASM(d3) + DEF_ASM(d4) + DEF_ASM(d5) + DEF_ASM(d6) + DEF_ASM(d7) + DEF_ASM(d8) + DEF_ASM(d9) + DEF_ASM(d10) + DEF_ASM(d11) + DEF_ASM(d12) + DEF_ASM(d13) + DEF_ASM(d14) + DEF_ASM(d15) + + /* VFP status registers */ + + DEF_ASM(fpsid) + DEF_ASM(fpscr) + DEF_ASM(fpexc) + + /* VFP magical ARM register */ + + DEF_ASM(apsr_nzcv) + + /* data processing directives */ + + DEF_ASM(asl) + + /* instructions that have no condition code */ + + DEF_ASM(cdp2) + DEF_ASM(ldc2) + DEF_ASM(ldc2l) + DEF_ASM(stc2) + DEF_ASM(stc2l) + +#define ARM_INSTRUCTION_GROUP(tok) ((((tok) - TOK_ASM_nopeq) & 0xFFFFFFF0) + TOK_ASM_nopeq) + +/* Note: condition code is 4 bits */ +#define DEF_ASM_CONDED(x) \ + DEF(TOK_ASM_ ## x ## eq, #x "eq") \ + DEF(TOK_ASM_ ## x ## ne, #x "ne") \ + DEF(TOK_ASM_ ## x ## cs, #x "cs") \ + DEF(TOK_ASM_ ## x ## cc, #x "cc") \ + DEF(TOK_ASM_ ## x ## mi, #x "mi") \ + DEF(TOK_ASM_ ## x ## pl, #x "pl") \ + DEF(TOK_ASM_ ## x ## vs, #x "vs") \ + DEF(TOK_ASM_ ## x ## vc, #x "vc") \ + DEF(TOK_ASM_ ## x ## hi, #x "hi") \ + DEF(TOK_ASM_ ## x ## ls, #x "ls") \ + DEF(TOK_ASM_ ## x ## ge, #x "ge") \ + DEF(TOK_ASM_ ## x ## lt, #x "lt") \ + DEF(TOK_ASM_ ## x ## gt, #x "gt") \ + DEF(TOK_ASM_ ## x ## le, #x "le") \ + DEF(TOK_ASM_ ## x, #x) \ + DEF(TOK_ASM_ ## x ## rsvd, #x "rsvd") + +/* Note: condition code is 4 bits */ +#define DEF_ASM_CONDED_WITH_SUFFIX(x, y) \ + DEF(TOK_ASM_ ## x ## eq ## _ ## y, #x "eq." #y) \ + DEF(TOK_ASM_ ## x ## ne ## _ ## y, #x "ne." #y) \ + DEF(TOK_ASM_ ## x ## cs ## _ ## y, #x "cs." #y) \ + DEF(TOK_ASM_ ## x ## cc ## _ ## y, #x "cc." #y) \ + DEF(TOK_ASM_ ## x ## mi ## _ ## y, #x "mi." #y) \ + DEF(TOK_ASM_ ## x ## pl ## _ ## y, #x "pl." #y) \ + DEF(TOK_ASM_ ## x ## vs ## _ ## y, #x "vs." #y) \ + DEF(TOK_ASM_ ## x ## vc ## _ ## y, #x "vc." #y) \ + DEF(TOK_ASM_ ## x ## hi ## _ ## y, #x "hi." #y) \ + DEF(TOK_ASM_ ## x ## ls ## _ ## y, #x "ls." #y) \ + DEF(TOK_ASM_ ## x ## ge ## _ ## y, #x "ge." #y) \ + DEF(TOK_ASM_ ## x ## lt ## _ ## y, #x "lt." #y) \ + DEF(TOK_ASM_ ## x ## gt ## _ ## y, #x "gt." #y) \ + DEF(TOK_ASM_ ## x ## le ## _ ## y, #x "le." #y) \ + DEF(TOK_ASM_ ## x ## _ ## y, #x "." #y) \ + DEF(TOK_ASM_ ## x ## rsvd ## _ ## y, #x "rsvd." #y) + +#define DEF_ASM_CONDED_VFP_F32_F64(x) \ + DEF_ASM_CONDED_WITH_SUFFIX(x, f32) \ + DEF_ASM_CONDED_WITH_SUFFIX(x, f64) + +#define DEF_ASM_CONDED_WITH_TWO_SUFFIXES(x, y, z) \ + DEF(TOK_ASM_ ## x ## eq ## _ ## y ## _ ## z, #x "eq." #y "." #z) \ + DEF(TOK_ASM_ ## x ## ne ## _ ## y ## _ ## z, #x "ne." #y "." #z) \ + DEF(TOK_ASM_ ## x ## cs ## _ ## y ## _ ## z, #x "cs." #y "." #z) \ + DEF(TOK_ASM_ ## x ## cc ## _ ## y ## _ ## z, #x "cc." #y "." #z) \ + DEF(TOK_ASM_ ## x ## mi ## _ ## y ## _ ## z, #x "mi." #y "." #z) \ + DEF(TOK_ASM_ ## x ## pl ## _ ## y ## _ ## z, #x "pl." #y "." #z) \ + DEF(TOK_ASM_ ## x ## vs ## _ ## y ## _ ## z, #x "vs." #y "." #z) \ + DEF(TOK_ASM_ ## x ## vc ## _ ## y ## _ ## z, #x "vc." #y "." #z) \ + DEF(TOK_ASM_ ## x ## hi ## _ ## y ## _ ## z, #x "hi." #y "." #z) \ + DEF(TOK_ASM_ ## x ## ls ## _ ## y ## _ ## z, #x "ls." #y "." #z) \ + DEF(TOK_ASM_ ## x ## ge ## _ ## y ## _ ## z, #x "ge." #y "." #z) \ + DEF(TOK_ASM_ ## x ## lt ## _ ## y ## _ ## z, #x "lt." #y "." #z) \ + DEF(TOK_ASM_ ## x ## gt ## _ ## y ## _ ## z, #x "gt." #y "." #z) \ + DEF(TOK_ASM_ ## x ## le ## _ ## y ## _ ## z, #x "le." #y "." #z) \ + DEF(TOK_ASM_ ## x ## _ ## y ## _ ## z, #x "." #y "." #z) \ + DEF(TOK_ASM_ ## x ## rsvd ## _ ## y ## _ ## z, #x "rsvd." #y "." #z) + +/* Note: add new tokens after nop (MUST always use DEF_ASM_CONDED) */ + + DEF_ASM_CONDED(nop) + DEF_ASM_CONDED(wfe) + DEF_ASM_CONDED(wfi) + DEF_ASM_CONDED(swi) + DEF_ASM_CONDED(svc) + + /* misc */ + DEF_ASM_CONDED(clz) + + /* size conversion */ + + DEF_ASM_CONDED(sxtb) + DEF_ASM_CONDED(sxth) + DEF_ASM_CONDED(uxtb) + DEF_ASM_CONDED(uxth) + DEF_ASM_CONDED(movt) + DEF_ASM_CONDED(movw) + + /* multiplication */ + + DEF_ASM_CONDED(mul) + DEF_ASM_CONDED(muls) + DEF_ASM_CONDED(mla) + DEF_ASM_CONDED(mlas) + DEF_ASM_CONDED(smull) + DEF_ASM_CONDED(smulls) + DEF_ASM_CONDED(umull) + DEF_ASM_CONDED(umulls) + DEF_ASM_CONDED(smlal) + DEF_ASM_CONDED(smlals) + DEF_ASM_CONDED(umlal) + DEF_ASM_CONDED(umlals) + + /* load/store */ + + DEF_ASM_CONDED(ldr) + DEF_ASM_CONDED(ldrb) + DEF_ASM_CONDED(str) + DEF_ASM_CONDED(strb) + DEF_ASM_CONDED(ldrex) + DEF_ASM_CONDED(ldrexb) + DEF_ASM_CONDED(strex) + DEF_ASM_CONDED(strexb) + DEF_ASM_CONDED(ldrh) + DEF_ASM_CONDED(ldrsh) + DEF_ASM_CONDED(ldrsb) + DEF_ASM_CONDED(strh) + + DEF_ASM_CONDED(stmda) + DEF_ASM_CONDED(ldmda) + DEF_ASM_CONDED(stm) + DEF_ASM_CONDED(ldm) + DEF_ASM_CONDED(stmia) + DEF_ASM_CONDED(ldmia) + DEF_ASM_CONDED(stmdb) + DEF_ASM_CONDED(ldmdb) + DEF_ASM_CONDED(stmib) + DEF_ASM_CONDED(ldmib) + + DEF_ASM_CONDED(ldc) + DEF_ASM_CONDED(ldcl) + DEF_ASM_CONDED(stc) + DEF_ASM_CONDED(stcl) + + /* instruction macros */ + + DEF_ASM_CONDED(push) + DEF_ASM_CONDED(pop) + + /* branches */ + + DEF_ASM_CONDED(b) + DEF_ASM_CONDED(bl) + DEF_ASM_CONDED(bx) + DEF_ASM_CONDED(blx) + + /* data processing instructions; order is important */ + + DEF_ASM_CONDED(and) + DEF_ASM_CONDED(ands) + DEF_ASM_CONDED(eor) + DEF_ASM_CONDED(eors) + DEF_ASM_CONDED(sub) + DEF_ASM_CONDED(subs) + DEF_ASM_CONDED(rsb) + DEF_ASM_CONDED(rsbs) + DEF_ASM_CONDED(add) + DEF_ASM_CONDED(adds) + DEF_ASM_CONDED(adc) + DEF_ASM_CONDED(adcs) + DEF_ASM_CONDED(sbc) + DEF_ASM_CONDED(sbcs) + DEF_ASM_CONDED(rsc) + DEF_ASM_CONDED(rscs) + DEF_ASM_CONDED(tst) + DEF_ASM_CONDED(tsts) // necessary here--but not useful to the user + DEF_ASM_CONDED(teq) + DEF_ASM_CONDED(teqs) // necessary here--but not useful to the user + DEF_ASM_CONDED(cmp) + DEF_ASM_CONDED(cmps) // necessary here--but not useful to the user + DEF_ASM_CONDED(cmn) + DEF_ASM_CONDED(cmns) // necessary here--but not useful to the user + DEF_ASM_CONDED(orr) + DEF_ASM_CONDED(orrs) + DEF_ASM_CONDED(mov) + DEF_ASM_CONDED(movs) + DEF_ASM_CONDED(bic) + DEF_ASM_CONDED(bics) + DEF_ASM_CONDED(mvn) + DEF_ASM_CONDED(mvns) + + DEF_ASM_CONDED(lsl) + DEF_ASM_CONDED(lsls) + DEF_ASM_CONDED(lsr) + DEF_ASM_CONDED(lsrs) + DEF_ASM_CONDED(asr) + DEF_ASM_CONDED(asrs) + DEF_ASM_CONDED(ror) + DEF_ASM_CONDED(rors) + DEF_ASM_CONDED(rrx) + DEF_ASM_CONDED(rrxs) + + DEF_ASM_CONDED(cdp) + DEF_ASM_CONDED(mcr) + DEF_ASM_CONDED(mrc) + + // Floating point high-level instructions + + DEF_ASM_CONDED(vldr) + DEF_ASM_CONDED(vstr) + + DEF_ASM_CONDED_VFP_F32_F64(vmla) + DEF_ASM_CONDED_VFP_F32_F64(vmls) + DEF_ASM_CONDED_VFP_F32_F64(vnmls) + DEF_ASM_CONDED_VFP_F32_F64(vnmla) + DEF_ASM_CONDED_VFP_F32_F64(vmul) + DEF_ASM_CONDED_VFP_F32_F64(vnmul) + DEF_ASM_CONDED_VFP_F32_F64(vadd) + DEF_ASM_CONDED_VFP_F32_F64(vsub) + DEF_ASM_CONDED_VFP_F32_F64(vdiv) + DEF_ASM_CONDED_VFP_F32_F64(vneg) + DEF_ASM_CONDED_VFP_F32_F64(vabs) + DEF_ASM_CONDED_VFP_F32_F64(vsqrt) + DEF_ASM_CONDED_VFP_F32_F64(vcmp) + DEF_ASM_CONDED_VFP_F32_F64(vcmpe) + DEF_ASM_CONDED_VFP_F32_F64(vmov) + + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvtr, s32, f64) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvtr, s32, f32) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvtr, u32, f64) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvtr, u32, f32) + + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, s32, f64) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, s32, f32) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, u32, f64) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, u32, f32) + + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, f64, s32) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, f32, s32) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, f64, u32) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, f32, u32) + + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, f64, f32) + DEF_ASM_CONDED_WITH_TWO_SUFFIXES(vcvt, f32, f64) + + DEF_ASM_CONDED(vpush) + DEF_ASM_CONDED(vpop) + DEF_ASM_CONDED(vldm) + DEF_ASM_CONDED(vldmia) + DEF_ASM_CONDED(vldmdb) + DEF_ASM_CONDED(vstm) + DEF_ASM_CONDED(vstmia) + DEF_ASM_CONDED(vstmdb) + DEF_ASM_CONDED(vmsr) + DEF_ASM_CONDED(vmrs) diff --git a/vendor/tcc/arm64-asm.c b/vendor/tcc/arm64-asm.c new file mode 100644 index 00000000..a97fd642 --- /dev/null +++ b/vendor/tcc/arm64-asm.c @@ -0,0 +1,94 @@ +/*************************************************************/ +/* + * ARM64 dummy assembler for TCC + * + */ + +#ifdef TARGET_DEFS_ONLY + +#define CONFIG_TCC_ASM +#define NB_ASM_REGS 16 + +ST_FUNC void g(int c); +ST_FUNC void gen_le16(int c); +ST_FUNC void gen_le32(int c); + +/*************************************************************/ +#else +/*************************************************************/ +#define USING_GLOBALS +#include "tcc.h" + +static void asm_error(void) +{ + tcc_error("ARM asm not implemented."); +} + +/* XXX: make it faster ? */ +ST_FUNC void g(int c) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +ST_FUNC void gen_le16 (int i) +{ + g(i); + g(i>>8); +} + +ST_FUNC void gen_le32 (int i) +{ + gen_le16(i); + gen_le16(i>>16); +} + +ST_FUNC void gen_expr32(ExprValue *pe) +{ + gen_le32(pe->v); +} + +ST_FUNC void asm_opcode(TCCState *s1, int opcode) +{ + asm_error(); +} + +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) +{ + asm_error(); +} + +/* generate prolog and epilog code for asm statement */ +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) +{ +} + +ST_FUNC void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) +{ +} + +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) +{ + asm_error(); +} + +ST_FUNC int asm_parse_regvar (int t) +{ + asm_error(); + return -1; +} + +/*************************************************************/ +#endif /* ndef TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/arm64-gen.c b/vendor/tcc/arm64-gen.c new file mode 100644 index 00000000..e314de58 --- /dev/null +++ b/vendor/tcc/arm64-gen.c @@ -0,0 +1,2172 @@ +/* + * A64 code generator for TCC + * + * Copyright (c) 2014-2015 Edmund Grimley Evans + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + */ + +#ifdef TARGET_DEFS_ONLY + +// Number of registers available to allocator: +#define NB_REGS 28 // x0-x18, x30, v0-v7 + +#define TREG_R(x) (x) // x = 0..18 +#define TREG_R30 19 +#define TREG_F(x) (x + 20) // x = 0..7 + +// Register classes sorted from more general to more precise: +#define RC_INT (1 << 0) +#define RC_FLOAT (1 << 1) +#define RC_R(x) (1 << (2 + (x))) // x = 0..18 +#define RC_R30 (1 << 21) +#define RC_F(x) (1 << (22 + (x))) // x = 0..7 + +#define RC_IRET (RC_R(0)) // int return register class +#define RC_FRET (RC_F(0)) // float return register class + +#define REG_IRET (TREG_R(0)) // int return register number +#define REG_FRET (TREG_F(0)) // float return register number + +#define PTR_SIZE 8 + +#define LDOUBLE_SIZE 16 +#define LDOUBLE_ALIGN 16 + +#define MAX_ALIGN 16 + +#ifndef TCC_TARGET_MACHO +#define CHAR_IS_UNSIGNED +#endif + +/* define if return values need to be extended explicitely + at caller side (for interfacing with non-TCC compilers) */ +#define PROMOTE_RET +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#define USING_GLOBALS +#include "tcc.h" +#include + +ST_DATA const char * const target_machine_defs = + "__aarch64__\0" +#if defined(TCC_TARGET_MACHO) + "__arm64__\0" +#endif + "__AARCH64EL__\0" + ; + +ST_DATA const int reg_classes[NB_REGS] = { + RC_INT | RC_R(0), + RC_INT | RC_R(1), + RC_INT | RC_R(2), + RC_INT | RC_R(3), + RC_INT | RC_R(4), + RC_INT | RC_R(5), + RC_INT | RC_R(6), + RC_INT | RC_R(7), + RC_INT | RC_R(8), + RC_INT | RC_R(9), + RC_INT | RC_R(10), + RC_INT | RC_R(11), + RC_INT | RC_R(12), + RC_INT | RC_R(13), + RC_INT | RC_R(14), + RC_INT | RC_R(15), + RC_INT | RC_R(16), + RC_INT | RC_R(17), + RC_INT | RC_R(18), + RC_R30, // not in RC_INT as we make special use of x30 + RC_FLOAT | RC_F(0), + RC_FLOAT | RC_F(1), + RC_FLOAT | RC_F(2), + RC_FLOAT | RC_F(3), + RC_FLOAT | RC_F(4), + RC_FLOAT | RC_F(5), + RC_FLOAT | RC_F(6), + RC_FLOAT | RC_F(7) +}; + +#if defined(CONFIG_TCC_BCHECK) +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +ST_DATA int func_bound_add_epilog; +#endif + +#define IS_FREG(x) ((x) >= TREG_F(0)) + +static uint32_t intr(int r) +{ + assert(TREG_R(0) <= r && r <= TREG_R30); + return r < TREG_R30 ? r : 30; +} + +static uint32_t fltr(int r) +{ + assert(TREG_F(0) <= r && r <= TREG_F(7)); + return r - TREG_F(0); +} + +// Add an instruction to text section: +ST_FUNC void o(unsigned int c) +{ + int ind1 = ind + 4; + if (nocode_wanted) + return; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + write32le(cur_text_section->data + ind, c); + ind = ind1; +} + +static int arm64_encode_bimm64(uint64_t x) +{ + int neg = x & 1; + int rep, pos, len; + + if (neg) + x = ~x; + if (!x) + return -1; + + if (x >> 2 == (x & (((uint64_t)1 << (64 - 2)) - 1))) + rep = 2, x &= ((uint64_t)1 << 2) - 1; + else if (x >> 4 == (x & (((uint64_t)1 << (64 - 4)) - 1))) + rep = 4, x &= ((uint64_t)1 << 4) - 1; + else if (x >> 8 == (x & (((uint64_t)1 << (64 - 8)) - 1))) + rep = 8, x &= ((uint64_t)1 << 8) - 1; + else if (x >> 16 == (x & (((uint64_t)1 << (64 - 16)) - 1))) + rep = 16, x &= ((uint64_t)1 << 16) - 1; + else if (x >> 32 == (x & (((uint64_t)1 << (64 - 32)) - 1))) + rep = 32, x &= ((uint64_t)1 << 32) - 1; + else + rep = 64; + + pos = 0; + if (!(x & (((uint64_t)1 << 32) - 1))) x >>= 32, pos += 32; + if (!(x & (((uint64_t)1 << 16) - 1))) x >>= 16, pos += 16; + if (!(x & (((uint64_t)1 << 8) - 1))) x >>= 8, pos += 8; + if (!(x & (((uint64_t)1 << 4) - 1))) x >>= 4, pos += 4; + if (!(x & (((uint64_t)1 << 2) - 1))) x >>= 2, pos += 2; + if (!(x & (((uint64_t)1 << 1) - 1))) x >>= 1, pos += 1; + + len = 0; + if (!(~x & (((uint64_t)1 << 32) - 1))) x >>= 32, len += 32; + if (!(~x & (((uint64_t)1 << 16) - 1))) x >>= 16, len += 16; + if (!(~x & (((uint64_t)1 << 8) - 1))) x >>= 8, len += 8; + if (!(~x & (((uint64_t)1 << 4) - 1))) x >>= 4, len += 4; + if (!(~x & (((uint64_t)1 << 2) - 1))) x >>= 2, len += 2; + if (!(~x & (((uint64_t)1 << 1) - 1))) x >>= 1, len += 1; + + if (x) + return -1; + if (neg) { + pos = (pos + len) & (rep - 1); + len = rep - len; + } + return ((0x1000 & rep << 6) | (((rep - 1) ^ 31) << 1 & 63) | + ((rep - pos) & (rep - 1)) << 6 | (len - 1)); +} + +static uint32_t arm64_movi(int r, uint64_t x) +{ + uint64_t m = 0xffff; + int e; + if (!(x & ~m)) + return 0x52800000 | r | x << 5; // movz w(r),#(x) + if (!(x & ~(m << 16))) + return 0x52a00000 | r | x >> 11; // movz w(r),#(x >> 16),lsl #16 + if (!(x & ~(m << 32))) + return 0xd2c00000 | r | x >> 27; // movz x(r),#(x >> 32),lsl #32 + if (!(x & ~(m << 48))) + return 0xd2e00000 | r | x >> 43; // movz x(r),#(x >> 48),lsl #48 + if ((x & ~m) == m << 16) + return (0x12800000 | r | + (~x << 5 & 0x1fffe0)); // movn w(r),#(~x) + if ((x & ~(m << 16)) == m) + return (0x12a00000 | r | + (~x >> 11 & 0x1fffe0)); // movn w(r),#(~x >> 16),lsl #16 + if (!~(x | m)) + return (0x92800000 | r | + (~x << 5 & 0x1fffe0)); // movn x(r),#(~x) + if (!~(x | m << 16)) + return (0x92a00000 | r | + (~x >> 11 & 0x1fffe0)); // movn x(r),#(~x >> 16),lsl #16 + if (!~(x | m << 32)) + return (0x92c00000 | r | + (~x >> 27 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32 + if (!~(x | m << 48)) + return (0x92e00000 | r | + (~x >> 43 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32 + if (!(x >> 32) && (e = arm64_encode_bimm64(x | x << 32)) >= 0) + return 0x320003e0 | r | (uint32_t)e << 10; // movi w(r),#(x) + if ((e = arm64_encode_bimm64(x)) >= 0) + return 0xb20003e0 | r | (uint32_t)e << 10; // movi x(r),#(x) + return 0; +} + +static void arm64_movimm(int r, uint64_t x) +{ + uint32_t i; + if ((i = arm64_movi(r, x))) + o(i); // a single MOV + else { + // MOVZ/MOVN and 1-3 MOVKs + int z = 0, m = 0; + uint32_t mov1 = 0xd2800000; // movz + uint64_t x1 = x; + for (i = 0; i < 64; i += 16) { + z += !(x >> i & 0xffff); + m += !(~x >> i & 0xffff); + } + if (m > z) { + x1 = ~x; + mov1 = 0x92800000; // movn + } + for (i = 0; i < 64; i += 16) + if (x1 >> i & 0xffff) { + o(mov1 | r | (x1 >> i & 0xffff) << 5 | i << 17); + // movz/movn x(r),#(*),lsl #(i) + break; + } + for (i += 16; i < 64; i += 16) + if (x1 >> i & 0xffff) + o(0xf2800000 | r | (x >> i & 0xffff) << 5 | i << 17); + // movk x(r),#(*),lsl #(i) + } +} + +// Patch all branches in list pointed to by t to branch to a: +ST_FUNC void gsym_addr(int t_, int a_) +{ + uint32_t t = t_; + uint32_t a = a_; + while (t) { + unsigned char *ptr = cur_text_section->data + t; + uint32_t next = read32le(ptr); + if (a - t + 0x8000000 >= 0x10000000) + tcc_error("branch out of range"); + write32le(ptr, (a - t == 4 ? 0xd503201f : // nop + 0x14000000 | ((a - t) >> 2 & 0x3ffffff))); // b + t = next; + } +} + +static int arm64_type_size(int t) +{ + /* + * case values are in increasing order (from 1 to 11). + * which 'may' help compiler optimizers. See tcc.h + */ + switch (t & VT_BTYPE) { + case VT_BYTE: return 0; + case VT_SHORT: return 1; + case VT_INT: return 2; + case VT_LLONG: return 3; + case VT_PTR: return 3; + case VT_FUNC: return 3; + case VT_STRUCT: return 3; + case VT_FLOAT: return 2; + case VT_DOUBLE: return 3; + case VT_LDOUBLE: return 4; + case VT_BOOL: return 0; + } + assert(0); + return 0; +} + +static void arm64_spoff(int reg, uint64_t off) +{ + uint32_t sub = off >> 63; + if (sub) + off = -off; + if (off < 4096) + o(0x910003e0 | sub << 30 | reg | off << 10); + // (add|sub) x(reg),sp,#(off) + else { + arm64_movimm(30, off); // use x30 for offset + o(0x8b3e63e0 | sub << 30 | reg); // (add|sub) x(reg),sp,x30 + } +} + +/* invert 0: return value to use for store/load */ +/* invert 1: return value to use for arm64_sym */ +static uint64_t arm64_check_offset(int invert, int sz_, uint64_t off) +{ + uint32_t sz = sz_; + if (!(off & ~((uint32_t)0xfff << sz)) || + (off < 256 || -off <= 256)) + return invert ? off : 0ul; + else if ((off & ((uint32_t)0xfff << sz))) + return invert ? off & ((uint32_t)0xfff << sz) + : off & ~((uint32_t)0xfff << sz); + else if (off & 0x1ff) + return invert ? off & 0x1ff : off & ~0x1ff; + else + return invert ? 0ul : off; +} + +static void arm64_ldrx(int sg, int sz_, int dst, int bas, uint64_t off) +{ + uint32_t sz = sz_; + if (sz >= 2) + sg = 0; + if (!(off & ~((uint32_t)0xfff << sz))) + o(0x39400000 | dst | bas << 5 | off << (10 - sz) | + (uint32_t)!!sg << 23 | sz << 30); // ldr(*) x(dst),[x(bas),#(off)] + else if (off < 256 || -off <= 256) + o(0x38400000 | dst | bas << 5 | (off & 511) << 12 | + (uint32_t)!!sg << 23 | sz << 30); // ldur(*) x(dst),[x(bas),#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x38206800 | dst | bas << 5 | (uint32_t)30 << 16 | + (uint32_t)(!!sg + 1) << 22 | sz << 30); // ldr(*) x(dst),[x(bas),x30] + } +} + +static void arm64_ldrv(int sz_, int dst, int bas, uint64_t off) +{ + uint32_t sz = sz_; + if (!(off & ~((uint32_t)0xfff << sz))) + o(0x3d400000 | dst | bas << 5 | off << (10 - sz) | + (sz & 4) << 21 | (sz & 3) << 30); // ldr (s|d|q)(dst),[x(bas),#(off)] + else if (off < 256 || -off <= 256) + o(0x3c400000 | dst | bas << 5 | (off & 511) << 12 | + (sz & 4) << 21 | (sz & 3) << 30); // ldur (s|d|q)(dst),[x(bas),#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x3c606800 | dst | bas << 5 | (uint32_t)30 << 16 | + sz << 30 | (sz & 4) << 21); // ldr (s|d|q)(dst),[x(bas),x30] + } +} + +static void arm64_ldrs(int reg_, int size) +{ + uint32_t reg = reg_; + // Use x30 for intermediate value in some cases. + switch (size) { + default: assert(0); break; + case 0: + /* Can happen with zero size structs */ + break; + case 1: + arm64_ldrx(0, 0, reg, reg, 0); + break; + case 2: + arm64_ldrx(0, 1, reg, reg, 0); + break; + case 3: + arm64_ldrx(0, 1, 30, reg, 0); + arm64_ldrx(0, 0, reg, reg, 2); + o(0x2a0043c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #16 + break; + case 4: + arm64_ldrx(0, 2, reg, reg, 0); + break; + case 5: + arm64_ldrx(0, 2, 30, reg, 0); + arm64_ldrx(0, 0, reg, reg, 4); + o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32 + break; + case 6: + arm64_ldrx(0, 2, 30, reg, 0); + arm64_ldrx(0, 1, reg, reg, 4); + o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32 + break; + case 7: + arm64_ldrx(0, 2, 30, reg, 0); + arm64_ldrx(0, 2, reg, reg, 3); + o(0x53087c00 | reg | reg << 5); // lsr w(reg), w(reg), #8 + o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32 + break; + case 8: + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 9: + arm64_ldrx(0, 0, reg + 1, reg, 8); + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 10: + arm64_ldrx(0, 1, reg + 1, reg, 8); + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 11: + arm64_ldrx(0, 2, reg + 1, reg, 7); + o(0x53087c00 | (reg+1) | (reg+1) << 5); // lsr w(reg+1), w(reg+1), #8 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 12: + arm64_ldrx(0, 2, reg + 1, reg, 8); + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 13: + arm64_ldrx(0, 3, reg + 1, reg, 5); + o(0xd358fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #24 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 14: + arm64_ldrx(0, 3, reg + 1, reg, 6); + o(0xd350fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #16 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 15: + arm64_ldrx(0, 3, reg + 1, reg, 7); + o(0xd348fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #8 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 16: + o(0xa9400000 | reg | (reg+1) << 10 | reg << 5); + // ldp x(reg),x(reg+1),[x(reg)] + break; + } +} + +static void arm64_strx(int sz_, int dst, int bas, uint64_t off) +{ + uint32_t sz = sz_; + if (!(off & ~((uint32_t)0xfff << sz))) + o(0x39000000 | dst | bas << 5 | off << (10 - sz) | sz << 30); + // str(*) x(dst),[x(bas],#(off)] + else if (off < 256 || -off <= 256) + o(0x38000000 | dst | bas << 5 | (off & 511) << 12 | sz << 30); + // stur(*) x(dst),[x(bas],#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x38206800 | dst | bas << 5 | (uint32_t)30 << 16 | sz << 30); + // str(*) x(dst),[x(bas),x30] + } +} + +static void arm64_strv(int sz_, int dst, int bas, uint64_t off) +{ + uint32_t sz = sz_; + if (!(off & ~((uint32_t)0xfff << sz))) + o(0x3d000000 | dst | bas << 5 | off << (10 - sz) | + (sz & 4) << 21 | (sz & 3) << 30); // str (s|d|q)(dst),[x(bas),#(off)] + else if (off < 256 || -off <= 256) + o(0x3c000000 | dst | bas << 5 | (off & 511) << 12 | + (sz & 4) << 21 | (sz & 3) << 30); // stur (s|d|q)(dst),[x(bas),#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x3c206800 | dst | bas << 5 | (uint32_t)30 << 16 | + sz << 30 | (sz & 4) << 21); // str (s|d|q)(dst),[x(bas),x30] + } +} + +static void arm64_sym(int r, Sym *sym, unsigned long addend) +{ + greloca(cur_text_section, sym, ind, R_AARCH64_ADR_GOT_PAGE, 0); + o(0x90000000 | r); // adrp xr, #sym + greloca(cur_text_section, sym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); + o(0xf9400000 | r | (r << 5)); // ld xr,[xr, #sym] + if (addend) { + // add xr, xr, #addend + if (addend & 0xffful) + o(0x91000000 | r | r << 5 | (addend & 0xfff) << 10); + if (addend > 0xffful) { + // add xr, xr, #addend, lsl #12 + if (addend & 0xfff000ul) + o(0x91400000 | r | r << 5 | ((addend >> 12) & 0xfff) << 10); + if (addend > 0xfffffful) { + /* very unlikely */ + int t = r ? 0 : 1; + o(0xf81f0fe0 | t); /* str xt, [sp, #-16]! */ + arm64_movimm(t, addend & ~0xfffffful); // use xt for addent + o(0x91000000 | r | (t << 5)); /* add xr, xt, #0 */ + o(0xf84107e0 | t); /* ldr xt, [sp], #16 */ + } + } + } +} + +static void arm64_load_cmp(int r, SValue *sv); + +ST_FUNC void load(int r, SValue *sv) +{ + int svtt = sv->type.t; + int svr = sv->r & ~(VT_BOUNDED | VT_NONCONST); + int svrv = svr & VT_VALMASK; + uint64_t svcul = (uint32_t)sv->c.i; + svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul; + + if (svr == (VT_LOCAL | VT_LVAL)) { + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcul); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), 29, svcul); + return; + } + + if (svr == (VT_CONST | VT_LVAL)) { + if (sv->sym) + arm64_sym(30, sv->sym, // use x30 for address + arm64_check_offset(0, arm64_type_size(svtt), sv->c.i)); + else + arm64_movimm (30, sv->c.i); + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), sv->c.i)); + else + arm64_ldrx(!(svtt&VT_UNSIGNED), arm64_type_size(svtt), intr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), sv->c.i)); + return; + } + + if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) { + if ((svtt & VT_BTYPE) != VT_VOID) { + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), intr(svrv), 0); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), intr(svrv), 0); + } + return; + } + + if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { + arm64_sym(30, sv->sym, // use x30 for address + arm64_check_offset(0, arm64_type_size(svtt), svcul)); + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), svcul)); + else + arm64_ldrx(!(svtt&VT_UNSIGNED), arm64_type_size(svtt), intr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), svcul)); + return; + } + + if (svr == (VT_CONST | VT_SYM)) { + arm64_sym(intr(r), sv->sym, svcul); + return; + } + + if (svr == VT_CONST) { + if ((svtt & VT_BTYPE) != VT_VOID) + arm64_movimm(intr(r), arm64_type_size(svtt) == 3 ? + sv->c.i : (uint32_t)svcul); + return; + } + + if (svr < VT_CONST) { + if (IS_FREG(r) && IS_FREG(svr)) + if (svtt == VT_LDOUBLE) + o(0x4ea01c00 | fltr(r) | fltr(svr) << 5); + // mov v(r).16b,v(svr).16b + else + o(0x1e604000 | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr) + else if (!IS_FREG(r) && !IS_FREG(svr)) + o(0xaa0003e0 | intr(r) | intr(svr) << 16); // mov x(r),x(svr) + else + assert(0); + return; + } + + if (svr == VT_LOCAL) { + if (-svcul < 0x1000) + o(0xd10003a0 | intr(r) | -svcul << 10); // sub x(r),x29,#... + else { + arm64_movimm(30, -svcul); // use x30 for offset + o(0xcb0003a0 | intr(r) | (uint32_t)30 << 16); // sub x(r),x29,x30 + } + return; + } + + if (svr == VT_JMP || svr == VT_JMPI) { + int t = (svr == VT_JMPI); + arm64_movimm(intr(r), t); + o(0x14000002); // b .+8 + gsym(svcul); + arm64_movimm(intr(r), t ^ 1); + return; + } + + if (svr == (VT_LLOCAL | VT_LVAL)) { + arm64_ldrx(0, 3, 30, 29, svcul); // use x30 for offset + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), 30, 0); + return; + } + + if (svr == VT_CMP) { + arm64_load_cmp(r, sv); + return; + } + + printf("load(%x, (%x, %x, %lx))\n", r, svtt, sv->r, (long)svcul); + assert(0); +} + +ST_FUNC void store(int r, SValue *sv) +{ + int svtt = sv->type.t; + int svr = sv->r & ~VT_BOUNDED; + int svrv = svr & VT_VALMASK; + uint64_t svcul = (uint32_t)sv->c.i; + svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul; + + if (svr == (VT_LOCAL | VT_LVAL)) { + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcul); + else + arm64_strx(arm64_type_size(svtt), intr(r), 29, svcul); + return; + } + + if (svr == (VT_CONST | VT_LVAL)) { + if (sv->sym) + arm64_sym(30, sv->sym, // use x30 for address + arm64_check_offset(0, arm64_type_size(svtt), sv->c.i)); + else + arm64_movimm (30, sv->c.i); + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), sv->c.i)); + else + arm64_strx(arm64_type_size(svtt), intr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), sv->c.i)); + return; + } + + if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) { + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), intr(svrv), 0); + else + arm64_strx(arm64_type_size(svtt), intr(r), intr(svrv), 0); + return; + } + + if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { + arm64_sym(30, sv->sym, // use x30 for address + arm64_check_offset(0, arm64_type_size(svtt), svcul)); + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), svcul)); + else + arm64_strx(arm64_type_size(svtt), intr(r), 30, + arm64_check_offset(1, arm64_type_size(svtt), svcul)); + return; + } + + printf("store(%x, (%x, %x, %lx))\n", r, svtt, sv->r, (long)svcul); + assert(0); +} + +static void arm64_gen_bl_or_b(int b) +{ + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) { + greloca(cur_text_section, vtop->sym, ind, + b ? R_AARCH64_JUMP26 : R_AARCH64_CALL26, 0); + o(0x14000000 | (uint32_t)!b << 31); // b/bl . + } + else { +#ifdef CONFIG_TCC_BCHECK + vtop->r &= ~VT_MUSTBOUND; +#endif + o(0xd61f0000 | (uint32_t)!b << 21 | intr(gv(RC_R30)) << 5); // br/blr + } +} + +#if defined(CONFIG_TCC_BCHECK) + +static void gen_bounds_call(int v) +{ + Sym *sym = external_helper_sym(v); + + greloca(cur_text_section, sym, ind, R_AARCH64_CALL26, 0); + o(0x94000000); // bl +} + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + o(0xd503201f); /* nop -> mov x0, lbound section pointer */ + o(0xd503201f); + o(0xd503201f); + o(0xd503201f); /* nop -> call __bound_local_new */ +} + +static void gen_bounds_epilog(void) +{ + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, PTR_SIZE); + + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + greloca(cur_text_section, sym_data, ind, R_AARCH64_ADR_GOT_PAGE, 0); + o(0x90000000 | 0); // adrp x0, #sym_data + greloca(cur_text_section, sym_data, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); + o(0xf9400000 | 0 | (0 << 5)); // ld x0,[x0, #sym_data] + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + } + + /* generate bound check local freeing */ + o(0xa9bf07e0); /* stp x0, x1, [sp, #-16]! */ + o(0x3c9f0fe0); /* str q0, [sp, #-16]! */ + greloca(cur_text_section, sym_data, ind, R_AARCH64_ADR_GOT_PAGE, 0); + o(0x90000000 | 0); // adrp x0, #sym_data + greloca(cur_text_section, sym_data, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); + o(0xf9400000 | 0 | (0 << 5)); // ld x0,[x0, #sym_data] + gen_bounds_call(TOK___bound_local_delete); + o(0x3cc107e0); /* ldr q0, [sp], #16 */ + o(0xa8c107e0); /* ldp x0, x1, [sp], #16 */ +} +#endif + +static int arm64_hfa_aux(CType *type, int *fsize, int num) +{ + if (is_float(type->t)) { + int a, n = type_size(type, &a); + if (num >= 4 || (*fsize && *fsize != n)) + return -1; + *fsize = n; + return num + 1; + } + else if ((type->t & VT_BTYPE) == VT_STRUCT) { + int is_struct = 0; // rather than union + Sym *field; + for (field = type->ref->next; field; field = field->next) + if (field->c) { + is_struct = 1; + break; + } + if (is_struct) { + int num0 = num; + for (field = type->ref->next; field; field = field->next) { + if (field->c != (num - num0) * *fsize) + return -1; + num = arm64_hfa_aux(&field->type, fsize, num); + if (num == -1) + return -1; + } + if (type->ref->c != (num - num0) * *fsize) + return -1; + return num; + } + else { // union + int num0 = num; + for (field = type->ref->next; field; field = field->next) { + int num1 = arm64_hfa_aux(&field->type, fsize, num0); + if (num1 == -1) + return -1; + num = num1 < num ? num : num1; + } + if (type->ref->c != (num - num0) * *fsize) + return -1; + return num; + } + } + else if ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR)) { + int num1; + if (!type->ref->c) + return num; + num1 = arm64_hfa_aux(&type->ref->type, fsize, num); + if (num1 == -1 || (num1 != num && type->ref->c > 4)) + return -1; + num1 = num + type->ref->c * (num1 - num); + if (num1 > 4) + return -1; + return num1; + } + return -1; +} + +static int arm64_hfa(CType *type, unsigned *fsize) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT || + ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR))) { + int sz = 0; + int n = arm64_hfa_aux(type, &sz, 0); + if (0 < n && n <= 4) { + if (fsize) + *fsize = sz; + return n; + } + } + return 0; +} + +static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned long *a) +{ + int nx = 0; // next integer register + int nv = 0; // next vector register + unsigned long ns = 32; // next stack offset + int i; + + for (i = 0; i < n; i++) { + int hfa = arm64_hfa(type[i], 0); + int size, align; + + if ((type[i]->t & VT_ARRAY) || + (type[i]->t & VT_BTYPE) == VT_FUNC) + size = align = 8; + else + size = type_size(type[i], &align); + +#if defined(TCC_TARGET_MACHO) + if (variadic && i == variadic) { + nx = 8; + nv = 8; + } +#endif + if (hfa) + // B.2 + ; + else if (size > 16) { + // B.3: replace with pointer + if (nx < 8) + a[i] = nx++ << 1 | 1; + else { + ns = (ns + 7) & ~7; + a[i] = ns | 1; + ns += 8; + } + continue; + } + else if ((type[i]->t & VT_BTYPE) == VT_STRUCT) + // B.4 + size = (size + 7) & ~7; + + // C.1 + if (is_float(type[i]->t) && nv < 8) { + a[i] = 16 + (nv++ << 1); + continue; + } + + // C.2 + if (hfa && nv + hfa <= 8) { + a[i] = 16 + (nv << 1); + nv += hfa; + continue; + } + + // C.3 + if (hfa) { + nv = 8; + size = (size + 7) & ~7; + } + + // C.4 + if (hfa || (type[i]->t & VT_BTYPE) == VT_LDOUBLE) { + ns = (ns + 7) & ~7; + ns = (ns + align - 1) & -align; + } + + // C.5 + if ((type[i]->t & VT_BTYPE) == VT_FLOAT) + size = 8; + + // C.6 + if (hfa || is_float(type[i]->t)) { + a[i] = ns; + ns += size; + continue; + } + + // C.7 + if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size <= 8 && nx < 8) { + a[i] = nx++ << 1; + continue; + } + + // C.8 + if (align == 16) + nx = (nx + 1) & ~1; + + // C.9 + if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size == 16 && nx < 7) { + a[i] = nx << 1; + nx += 2; + continue; + } + + // C.10 + if ((type[i]->t & VT_BTYPE) == VT_STRUCT && size <= (8 - nx) * 8) { + a[i] = nx << 1; + nx += (size + 7) >> 3; + continue; + } + + // C.11 + nx = 8; + + // C.12 + ns = (ns + 7) & ~7; + ns = (ns + align - 1) & -align; + + // C.13 + if ((type[i]->t & VT_BTYPE) == VT_STRUCT) { + a[i] = ns; + ns += size; + continue; + } + + // C.14 + if (size < 8) + size = 8; + + // C.15 + a[i] = ns; + ns += size; + } + + return ns - 32; +} + +static unsigned long arm64_pcs(int variadic, int n, CType **type, unsigned long *a) +{ + unsigned long stack; + + // Return type: + if ((type[0]->t & VT_BTYPE) == VT_VOID) + a[0] = -1; + else { + arm64_pcs_aux(0, 1, type, a); + assert(a[0] == 0 || a[0] == 1 || a[0] == 16); + } + + // Argument types: + stack = arm64_pcs_aux(variadic, n, type + 1, a + 1); + + if (0) { + int i; + for (i = 0; i <= n; i++) { + if (!i) + printf("arm64_pcs return: "); + else + printf("arm64_pcs arg %d: ", i); + if (a[i] == (unsigned long)-1) + printf("void\n"); + else if (a[i] == 1 && !i) + printf("X8 pointer\n"); + else if (a[i] < 16) + printf("X%lu%s\n", a[i] / 2, a[i] & 1 ? " pointer" : ""); + else if (a[i] < 32) + printf("V%lu\n", a[i] / 2 - 8); + else + printf("stack %lu%s\n", + (a[i] - 32) & ~1, a[i] & 1 ? " pointer" : ""); + } + } + + return stack; +} + +static int n_func_args(CType *type) +{ + int n_args = 0; + Sym *arg; + + for (arg = type->ref->next; arg; arg = arg->next) + n_args++; + return n_args; +} + +ST_FUNC void gfunc_call(int nb_args) +{ + CType *return_type; + CType **t; + unsigned long *a, *a1; + unsigned long stack; + int i; + int variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS); + int var_nb_arg = n_func_args(&vtop[-nb_args].type); + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + + return_type = &vtop[-nb_args].type.ref->type; + if ((return_type->t & VT_BTYPE) == VT_STRUCT) + --nb_args; + + t = tcc_malloc((nb_args + 1) * sizeof(*t)); + a = tcc_malloc((nb_args + 1) * sizeof(*a)); + a1 = tcc_malloc((nb_args + 1) * sizeof(*a1)); + + t[0] = return_type; + for (i = 0; i < nb_args; i++) + t[nb_args - i] = &vtop[-i].type; + + stack = arm64_pcs(variadic ? var_nb_arg : 0, nb_args, t, a); + + // Allocate space for structs replaced by pointer: + for (i = nb_args; i; i--) + if (a[i] & 1) { + SValue *arg = &vtop[i - nb_args]; + int align, size = type_size(&arg->type, &align); + assert((arg->type.t & VT_BTYPE) == VT_STRUCT); + stack = (stack + align - 1) & -align; + a1[i] = stack; + stack += size; + } + + stack = (stack + 15) >> 4 << 4; + + /* fetch cpu flag before generating any code */ + if ((vtop->r & VT_VALMASK) == VT_CMP) + gv(RC_INT); + + if (stack >= 0x1000000) // 16Mb + tcc_error("stack size too big %lu", stack); + if (stack & 0xfff) + o(0xd10003ff | (stack & 0xfff) << 10); // sub sp,sp,#(n) + if (stack >> 12) + o(0xd14003ff | (stack >> 12) << 10); + + // First pass: set all values on stack + for (i = nb_args; i; i--) { + vpushv(vtop - nb_args + i); + + if (a[i] & 1) { + // struct replaced by pointer + int r = get_reg(RC_INT); + arm64_spoff(intr(r), a1[i]); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + if (a[i] >= 32) { + // pointer on stack + r = get_reg(RC_INT); + arm64_spoff(intr(r), a1[i]); + arm64_strx(3, intr(r), 31, (a[i] - 32) >> 1 << 1); + } + } + else if (a[i] >= 32) { + // value on stack + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + int r = get_reg(RC_INT); + arm64_spoff(intr(r), a[i] - 32); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + } + else if (is_float(vtop->type.t)) { + gv(RC_FLOAT); + arm64_strv(arm64_type_size(vtop[0].type.t), + fltr(vtop[0].r), 31, a[i] - 32); + } + else { + gv(RC_INT); + arm64_strx(3, // arm64_type_size(vtop[0].type.t), + intr(vtop[0].r), 31, a[i] - 32); + } + } + + --vtop; + } + + // Second pass: assign values to registers + for (i = nb_args; i; i--, vtop--) { + if (a[i] < 16 && !(a[i] & 1)) { + // value in general-purpose registers + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + int align, size = type_size(&vtop->type, &align); + if (size) { + vtop->type.t = VT_PTR; + gaddrof(); + gv(RC_R(a[i] / 2)); + arm64_ldrs(a[i] / 2, size); + } + } + else + gv(RC_R(a[i] / 2)); + } + else if (a[i] < 16) + // struct replaced by pointer in register + arm64_spoff(a[i] / 2, a1[i]); + else if (a[i] < 32) { + // value in floating-point registers + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz); + vtop->type.t = VT_PTR; + gaddrof(); + gv(RC_R30); + for (j = 0; j < n; j++) + o(0x3d4003c0 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + (a[i] / 2 - 8 + j) | + j << 10); // ldr ([sdq])(*),[x30,#(j * sz)] + } + else + gv(RC_F(a[i] / 2 - 8)); + } + } + + if ((return_type->t & VT_BTYPE) == VT_STRUCT) { + if (a[0] == 1) { + // indirect return: set x8 and discard the stack value + gv(RC_R(8)); + --vtop; + } + else + // return in registers: keep the address for after the call + vswap(); + } + + save_regs(0); + arm64_gen_bl_or_b(0); + --vtop; + if (stack & 0xfff) + o(0x910003ff | (stack & 0xfff) << 10); // add sp,sp,#(n) + if (stack >> 12) + o(0x914003ff | (stack >> 12) << 10); + + { + int rt = return_type->t; + int bt = rt & VT_BTYPE; + if (bt == VT_STRUCT && !(a[0] & 1)) { + // A struct was returned in registers, so write it out: + gv(RC_R(8)); + --vtop; + if (a[0] == 0) { + int align, size = type_size(return_type, &align); + assert(size <= 16); + if (size > 8) + o(0xa9000500); // stp x0,x1,[x8] + else if (size) + arm64_strx(size > 4 ? 3 : size > 2 ? 2 : size > 1, 0, 8, 0); + + } + else if (a[0] == 16) { + uint32_t j, sz, n = arm64_hfa(return_type, &sz); + for (j = 0; j < n; j++) + o(0x3d000100 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + (a[i] / 2 - 8 + j) | + j << 10); // str ([sdq])(*),[x8,#(j * sz)] + } + } + } + + tcc_free(a1); + tcc_free(a); + tcc_free(t); +} + +static unsigned long arm64_func_va_list_stack; +static int arm64_func_va_list_gr_offs; +static int arm64_func_va_list_vr_offs; +static int arm64_func_sub_sp_offset; + +ST_FUNC void gfunc_prolog(Sym *func_sym) +{ + CType *func_type = &func_sym->type; + int n = 0; + int i = 0; + Sym *sym; + CType **t; + unsigned long *a; + int use_x8 = 0; + int last_int = 0; + int last_float = 0; + int variadic = func_sym->type.ref->f.func_type == FUNC_ELLIPSIS; + int var_nb_arg = n_func_args(&func_sym->type); + + func_vc = 144; // offset of where x8 is stored + + for (sym = func_type->ref; sym; sym = sym->next) + ++n; + t = n ? tcc_malloc(n * sizeof(*t)) : NULL; + a = n ? tcc_malloc(n * sizeof(*a)) : NULL; + + for (sym = func_type->ref; sym; sym = sym->next) + t[i++] = &sym->type; + + arm64_func_va_list_stack = arm64_pcs(variadic ? var_nb_arg : 0, n - 1, t, a); + +#if !defined(TCC_TARGET_MACHO) + if (variadic) { + use_x8 = 1; + last_int = 4; + last_float = 4; + } +#endif + if (a && a[0] == 1) + use_x8 = 1; + for (i = 1, sym = func_type->ref->next; sym; i++, sym = sym->next) { + if (a[i] < 16) { + int last, align, size = type_size(&sym->type, &align); + last = a[i] / 4 + 1 + (size - 1) / 8; + last_int = last > last_int ? last : last_int; + } + else if (a[i] < 32) { + int last, hfa = arm64_hfa(&sym->type, 0); + last = a[i] / 4 - 3 + (hfa ? hfa - 1 : 0); + last_float = last > last_float ? last : last_float; + } + } + + last_int = last_int > 4 ? 4 : last_int; + last_float = last_float > 4 ? 4 : last_float; + + o(0xa9b27bfd); // stp x29,x30,[sp,#-224]! + for (i = 0; i < last_float; i++) + // stp q0,q1,[sp,#16], stp q2,q3,[sp,#48] + // stp q4,q5,[sp,#80], stp q6,q7,[sp,#112] + o(0xad0087e0 + i * 0x10000 + (i << 11) + (i << 1)); + if (use_x8) + o(0xa90923e8); // stp x8,x8,[sp,#144] + for (i = 0; i < last_int; i++) + // stp x0,x1,[sp,#160], stp x2,x3,[sp,#176] + // stp x4,x5,[sp,#192], stp x6,x7,[sp,#208] + o(0xa90a07e0 + i * 0x10000 + (i << 11) + (i << 1)); + + arm64_func_va_list_gr_offs = -64; + arm64_func_va_list_vr_offs = -128; + + for (i = 1, sym = func_type->ref->next; sym; i++, sym = sym->next) { + int off = (a[i] < 16 ? 160 + a[i] / 2 * 8 : + a[i] < 32 ? 16 + (a[i] - 16) / 2 * 16 : + 224 + ((a[i] - 32) >> 1 << 1)); + sym_push(sym->v & ~SYM_FIELD, &sym->type, + (a[i] & 1 ? VT_LLOCAL : VT_LOCAL) | VT_LVAL, + off); + + if (a[i] < 16) { + int align, size = type_size(&sym->type, &align); + arm64_func_va_list_gr_offs = (a[i] / 2 - 7 + + (!(a[i] & 1) && size > 8)) * 8; + } + else if (a[i] < 32) { + uint32_t hfa = arm64_hfa(&sym->type, 0); + arm64_func_va_list_vr_offs = (a[i] / 2 - 16 + + (hfa ? hfa : 1)) * 16; + } + + // HFAs of float and double need to be written differently: + if (16 <= a[i] && a[i] < 32 && (sym->type.t & VT_BTYPE) == VT_STRUCT) { + uint32_t j, sz, k = arm64_hfa(&sym->type, &sz); + if (sz < 16) + for (j = 0; j < k; j++) { + o(0x3d0003e0 | -(sz & 8) << 27 | (sz & 4) << 29 | + ((a[i] - 16) / 2 + j) | (off / sz + j) << 10); + // str ([sdq])(*),[sp,#(j * sz)] + } + } + } + + tcc_free(a); + tcc_free(t); + + o(0x910003fd); // mov x29,sp + arm64_func_sub_sp_offset = ind; + // In gfunc_epilog these will be replaced with code to decrement SP: + o(0xd503201f); // nop + o(0xd503201f); // nop + loc = 0; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif +} + +ST_FUNC void gen_va_start(void) +{ + int r; + --vtop; // we don't need the "arg" + gaddrof(); + r = intr(gv(RC_INT)); + + if (arm64_func_va_list_stack) { + //xx could use add (immediate) here + arm64_movimm(30, arm64_func_va_list_stack + 224); + o(0x8b1e03be); // add x30,x29,x30 + } + else + o(0x910383be); // add x30,x29,#224 + o(0xf900001e | r << 5); // str x30,[x(r)] + +#if !defined(TCC_TARGET_MACHO) + if (arm64_func_va_list_gr_offs) { + if (arm64_func_va_list_stack) + o(0x910383be); // add x30,x29,#224 + o(0xf900041e | r << 5); // str x30,[x(r),#8] + } + + if (arm64_func_va_list_vr_offs) { + o(0x910243be); // add x30,x29,#144 + o(0xf900081e | r << 5); // str x30,[x(r),#16] + } + + arm64_movimm(30, arm64_func_va_list_gr_offs); + o(0xb900181e | r << 5); // str w30,[x(r),#24] + + arm64_movimm(30, arm64_func_va_list_vr_offs); + o(0xb9001c1e | r << 5); // str w30,[x(r),#28] +#endif + + --vtop; +} + +ST_FUNC void gen_va_arg(CType *t) +{ + int align, size = type_size(t, &align); + unsigned fsize, hfa = arm64_hfa(t, &fsize); + uint32_t r0, r1; + + if (is_float(t->t)) { + hfa = 1; + fsize = size; + } + + gaddrof(); + r0 = intr(gv(RC_INT)); + r1 = get_reg(RC_INT); + vtop[0].r = r1 | VT_LVAL; + r1 = intr(r1); + + if (!hfa) { + uint32_t n = size > 16 ? 8 : (size + 7) & -8; +#if !defined(TCC_TARGET_MACHO) + o(0xb940181e | r0 << 5); // ldr w30,[x(r0),#24] // __gr_offs + if (align == 16) { + assert(0); // this path untested but needed for __uint128_t + o(0x11003fde); // add w30,w30,#15 + o(0x121c6fde); // and w30,w30,#-16 + } + o(0x310003c0 | r1 | n << 10); // adds w(r1),w30,#(n) + o(0x540000ad); // b.le .+20 +#endif + o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack + o(0x9100001e | r1 << 5 | n << 10); // add x30,x(r1),#(n) + o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack +#if !defined(TCC_TARGET_MACHO) + o(0x14000004); // b .+16 + o(0xb9001800 | r1 | r0 << 5); // str w(r1),[x(r0),#24] // __gr_offs + o(0xf9400400 | r1 | r0 << 5); // ldr x(r1),[x(r0),#8] // __gr_top + o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw +#endif + if (size > 16) + o(0xf9400000 | r1 | r1 << 5); // ldr x(r1),[x(r1)] + } + else { + uint32_t ssz = (size + 7) & -(uint32_t)8; +#if !defined(TCC_TARGET_MACHO) + uint32_t rsz = hfa << 4; + uint32_t b1, b2; + o(0xb9401c1e | r0 << 5); // ldr w30,[x(r0),#28] // __vr_offs + o(0x310003c0 | r1 | rsz << 10); // adds w(r1),w30,#(rsz) + b1 = ind; o(0x5400000d); // b.le lab1 +#endif + o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack + if (fsize == 16) { + o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15 + o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16 + } + o(0x9100001e | r1 << 5 | ssz << 10); // add x30,x(r1),#(ssz) + o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack +#if !defined(TCC_TARGET_MACHO) + b2 = ind; o(0x14000000); // b lab2 + // lab1: + write32le(cur_text_section->data + b1, 0x5400000d | (ind - b1) << 3); + o(0xb9001c00 | r1 | r0 << 5); // str w(r1),[x(r0),#28] // __vr_offs + o(0xf9400800 | r1 | r0 << 5); // ldr x(r1),[x(r0),#16] // __vr_top + if (hfa == 1 || fsize == 16) + o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw + else { + // We need to change the layout of this HFA. + // Get some space on the stack using global variable "loc": + loc = (loc - size) & -(uint32_t)align; + o(0x8b3ec000 | 30 | r1 << 5); // add x30,x(r1),w30,sxtw + arm64_movimm(r1, loc); + o(0x8b0003a0 | r1 | r1 << 16); // add x(r1),x29,x(r1) + o(0x4c402bdc | (uint32_t)fsize << 7 | + (uint32_t)(hfa == 2) << 15 | + (uint32_t)(hfa == 3) << 14); // ld1 {v28.(4s|2d),...},[x30] + o(0x0d00801c | r1 << 5 | (fsize == 8) << 10 | + (uint32_t)(hfa != 2) << 13 | + (uint32_t)(hfa != 3) << 21); // st(hfa) {v28.(s|d),...}[0],[x(r1)] + } + // lab2: + write32le(cur_text_section->data + b2, 0x14000000 | (ind - b2) >> 2); +#endif + } +} + +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, + int *align, int *regsize) +{ + return 0; +} + +ST_FUNC void gfunc_return(CType *func_type) +{ + CType *t = func_type; + unsigned long a; + + arm64_pcs(0, 0, &t, &a); + switch (a) { + case -1: + break; + case 0: + if ((func_type->t & VT_BTYPE) == VT_STRUCT) { + int align, size = type_size(func_type, &align); + gaddrof(); + gv(RC_R(0)); + arm64_ldrs(0, size); + } + else + gv(RC_IRET); + break; + case 1: { + CType type = *func_type; + mk_pointer(&type); + vset(&type, VT_LOCAL | VT_LVAL, func_vc); + indir(); + vswap(); + vstore(); + break; + } + case 16: + if ((func_type->t & VT_BTYPE) == VT_STRUCT) { + uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz); + gaddrof(); + gv(RC_R(0)); + for (j = 0; j < n; j++) + o(0x3d400000 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + j | j << 10); // ldr ([sdq])(*),[x0,#(j * sz)] + } + else + gv(RC_FRET); + break; + default: + assert(0); + } + vtop--; +} + +ST_FUNC void gfunc_epilog(void) +{ +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif + + if (loc) { + // Insert instructions to subtract size of stack frame from SP. + unsigned char *ptr = cur_text_section->data + arm64_func_sub_sp_offset; + uint64_t diff = (-loc + 15) & ~15; + if (!(diff >> 24)) { + if (diff & 0xfff) // sub sp,sp,#(diff & 0xfff) + write32le(ptr, 0xd10003ff | (diff & 0xfff) << 10); + if (diff >> 12) // sub sp,sp,#(diff >> 12),lsl #12 + write32le(ptr + 4, 0xd14003ff | (diff >> 12) << 10); + } + else { + // In this case we may subtract more than necessary, + // but always less than 17/16 of what we were aiming for. + int i = 0; + int j = 0; + while (diff >> 20) { + diff = (diff + 0xffff) >> 16; + ++i; + } + while (diff >> 16) { + diff = (diff + 1) >> 1; + ++j; + } + write32le(ptr, 0xd2800010 | diff << 5 | i << 21); + // mov x16,#(diff),lsl #(16 * i) + write32le(ptr + 4, 0xcb3063ff | j << 10); + // sub sp,sp,x16,lsl #(j) + } + } + o(0x910003bf); // mov sp,x29 + o(0xa8ce7bfd); // ldp x29,x30,[sp],#224 + + o(0xd65f03c0); // ret +} + +ST_FUNC void gen_fill_nops(int bytes) +{ + if ((bytes & 3)) + tcc_error("alignment of code section not multiple of 4"); + while (bytes > 0) { + o(0xd503201f); // nop + bytes -= 4; + } +} + +// Generate forward branch to label: +ST_FUNC int gjmp(int t) +{ + int r = ind; + if (nocode_wanted) + return t; + o(t); + return r; +} + +// Generate branch to known address: +ST_FUNC void gjmp_addr(int a) +{ + assert(a - ind + 0x8000000 < 0x10000000); + o(0x14000000 | ((a - ind) >> 2 & 0x3ffffff)); +} + +ST_FUNC int gjmp_append(int n, int t) +{ + void *p; + /* insert vtop->c jump list in t */ + if (n) { + uint32_t n1 = n, n2; + while ((n2 = read32le(p = cur_text_section->data + n1))) + n1 = n2; + write32le(p, t); + t = n; + } + return t; +} + +void arm64_vset_VT_CMP(int op) +{ + if (op >= TOK_ULT && op <= TOK_GT) { + vtop->cmp_r = vtop->r; + vset_VT_CMP(0x80); + } +} + +static void arm64_gen_opil(int op, uint32_t l); + +static void arm64_load_cmp(int r, SValue *sv) +{ + sv->r = sv->cmp_r; + if (sv->c.i & 1) { + vpushi(1); + arm64_gen_opil('^', 0); + } + if (r != sv->r) { + load(r, sv); + sv->r = r; + } +} + +ST_FUNC int gjmp_cond(int op, int t) +{ + int bt = vtop->type.t & VT_BTYPE; + + int inv = op & 1; + vtop->r = vtop->cmp_r; + + if (bt == VT_LDOUBLE) { + uint32_t a, b, f = fltr(gv(RC_FLOAT)); + a = get_reg(RC_INT); + vpushi(0); + vtop[0].r = a; + b = get_reg(RC_INT); + a = intr(a); + b = intr(b); + o(0x4e083c00 | a | f << 5); // mov x(a),v(f).d[0] + o(0x4e183c00 | b | f << 5); // mov x(b),v(f).d[1] + o(0xaa000400 | a | a << 5 | b << 16); // orr x(a),x(a),x(b),lsl #1 + o(0xb4000040 | a | !!inv << 24); // cbz/cbnz x(a),.+8 + --vtop; + } + else if (bt == VT_FLOAT || bt == VT_DOUBLE) { + uint32_t a = fltr(gv(RC_FLOAT)); + o(0x1e202008 | a << 5 | (bt != VT_FLOAT) << 22); // fcmp + o(0x54000040 | !!inv); // b.eq/b.ne .+8 + } + else { + uint32_t ll = (bt == VT_PTR || bt == VT_LLONG); + uint32_t a = intr(gv(RC_INT)); + o(0x34000040 | a | !!inv << 24 | ll << 31); // cbz/cbnz wA,.+8 + } + return gjmp(t); +} + +static int arm64_iconst(uint64_t *val, SValue *sv) +{ + if ((sv->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (val) { + int t = sv->type.t; + int bt = t & VT_BTYPE; + *val = ((bt == VT_LLONG || bt == VT_PTR) ? sv->c.i : + (uint32_t)sv->c.i | + (t & VT_UNSIGNED ? 0 : -(sv->c.i & 0x80000000))); + } + return 1; +} + +static int arm64_gen_opic(int op, uint32_t l, int rev, uint64_t val, + uint32_t x, uint32_t a) +{ + if (op == '-' && !rev) { + val = -val; + op = '+'; + } + val = l ? val : (uint32_t)val; + + switch (op) { + + case '+': { + uint32_t s = l ? val >> 63 : val >> 31; + val = s ? -val : val; + val = l ? val : (uint32_t)val; + if (!(val & ~(uint64_t)0xfff)) + o(0x11000000 | l << 31 | s << 30 | x | a << 5 | val << 10); + else if (!(val & ~(uint64_t)0xfff000)) + o(0x11400000 | l << 31 | s << 30 | x | a << 5 | val >> 12 << 10); + else { + arm64_movimm(30, val); // use x30 + o(0x0b1e0000 | l << 31 | s << 30 | x | a << 5); + } + return 1; + } + + case '-': + if (!val) + o(0x4b0003e0 | l << 31 | x | a << 16); // neg + else if (val == (l ? (uint64_t)-1 : (uint32_t)-1)) + o(0x2a2003e0 | l << 31 | x | a << 16); // mvn + else { + arm64_movimm(30, val); // use x30 + o(0x4b0003c0 | l << 31 | x | a << 16); // sub + } + return 1; + + case '^': + if (val == -1 || (val == 0xffffffff && !l)) { + o(0x2a2003e0 | l << 31 | x | a << 16); // mvn + return 1; + } + // fall through + case '&': + case '|': { + int e = arm64_encode_bimm64(l ? val : val | val << 32); + if (e < 0) + return 0; + o((op == '&' ? 0x12000000 : + op == '|' ? 0x32000000 : 0x52000000) | + l << 31 | x | a << 5 | (uint32_t)e << 10); + return 1; + } + + case TOK_SAR: + case TOK_SHL: + case TOK_SHR: { + uint32_t n = 32 << l; + val = val & (n - 1); + if (rev) + return 0; + if (!val) { + // tcc_warning("shift count >= width of type"); + o(0x2a0003e0 | l << 31 | a << 16); + return 1; + } + else if (op == TOK_SHL) + o(0x53000000 | l << 31 | l << 22 | x | a << 5 | + (n - val) << 16 | (n - 1 - val) << 10); // lsl + else + o(0x13000000 | (op == TOK_SHR) << 30 | l << 31 | l << 22 | + x | a << 5 | val << 16 | (n - 1) << 10); // lsr/asr + return 1; + } + + } + return 0; +} + +static void arm64_gen_opil(int op, uint32_t l) +{ + uint32_t x, a, b; + + // Special treatment for operations with a constant operand: + { + uint64_t val; + int rev = 1; + + if (arm64_iconst(0, &vtop[0])) { + vswap(); + rev = 0; + } + if (arm64_iconst(&val, &vtop[-1])) { + gv(RC_INT); + a = intr(vtop[0].r); + --vtop; + x = get_reg(RC_INT); + ++vtop; + if (arm64_gen_opic(op, l, rev, val, intr(x), a)) { + vtop[0].r = x; + vswap(); + --vtop; + return; + } + } + if (!rev) + vswap(); + } + + gv2(RC_INT, RC_INT); + assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST); + a = intr(vtop[-1].r); + b = intr(vtop[0].r); + vtop -= 2; + x = get_reg(RC_INT); + ++vtop; + vtop[0].r = x; + x = intr(x); + + switch (op) { + case '%': + // Use x30 for quotient: + o(0x1ac00c00 | l << 31 | 30 | a << 5 | b << 16); // sdiv + o(0x1b008000 | l << 31 | x | (uint32_t)30 << 5 | + b << 16 | a << 10); // msub + break; + case '&': + o(0x0a000000 | l << 31 | x | a << 5 | b << 16); // and + break; + case '*': + o(0x1b007c00 | l << 31 | x | a << 5 | b << 16); // mul + break; + case '+': + o(0x0b000000 | l << 31 | x | a << 5 | b << 16); // add + break; + case '-': + o(0x4b000000 | l << 31 | x | a << 5 | b << 16); // sub + break; + case '/': + o(0x1ac00c00 | l << 31 | x | a << 5 | b << 16); // sdiv + break; + case '^': + o(0x4a000000 | l << 31 | x | a << 5 | b << 16); // eor + break; + case '|': + o(0x2a000000 | l << 31 | x | a << 5 | b << 16); // orr + break; + case TOK_EQ: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f17e0 | x); // cset wA,eq + break; + case TOK_GE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fb7e0 | x); // cset wA,ge + break; + case TOK_GT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fd7e0 | x); // cset wA,gt + break; + case TOK_LE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fc7e0 | x); // cset wA,le + break; + case TOK_LT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fa7e0 | x); // cset wA,lt + break; + case TOK_NE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f07e0 | x); // cset wA,ne + break; + case TOK_SAR: + o(0x1ac02800 | l << 31 | x | a << 5 | b << 16); // asr + break; + case TOK_SHL: + o(0x1ac02000 | l << 31 | x | a << 5 | b << 16); // lsl + break; + case TOK_SHR: + o(0x1ac02400 | l << 31 | x | a << 5 | b << 16); // lsr + break; + case TOK_UDIV: + case TOK_PDIV: + o(0x1ac00800 | l << 31 | x | a << 5 | b << 16); // udiv + break; + case TOK_UGE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f37e0 | x); // cset wA,cs + break; + case TOK_UGT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f97e0 | x); // cset wA,hi + break; + case TOK_ULT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f27e0 | x); // cset wA,cc + break; + case TOK_ULE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f87e0 | x); // cset wA,ls + break; + case TOK_UMOD: + // Use x30 for quotient: + o(0x1ac00800 | l << 31 | 30 | a << 5 | b << 16); // udiv + o(0x1b008000 | l << 31 | x | (uint32_t)30 << 5 | + b << 16 | a << 10); // msub + break; + default: + assert(0); + } +} + +ST_FUNC void gen_opi(int op) +{ + arm64_gen_opil(op, 0); + arm64_vset_VT_CMP(op); +} + +ST_FUNC void gen_opl(int op) +{ + arm64_gen_opil(op, 1); + arm64_vset_VT_CMP(op); +} + +ST_FUNC void gen_opf(int op) +{ + uint32_t x, a, b, dbl; + + if (vtop[0].type.t == VT_LDOUBLE) { + CType type = vtop[0].type; + int func = 0; + int cond = -1; + switch (op) { + case '*': func = TOK___multf3; break; + case '+': func = TOK___addtf3; break; + case '-': func = TOK___subtf3; break; + case '/': func = TOK___divtf3; break; + case TOK_EQ: func = TOK___eqtf2; cond = 1; break; + case TOK_NE: func = TOK___netf2; cond = 0; break; + case TOK_LT: func = TOK___lttf2; cond = 10; break; + case TOK_GE: func = TOK___getf2; cond = 11; break; + case TOK_LE: func = TOK___letf2; cond = 12; break; + case TOK_GT: func = TOK___gttf2; cond = 13; break; + default: assert(0); break; + } + vpush_helper_func(func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = cond < 0 ? REG_FRET : REG_IRET; + if (cond < 0) + vtop->type = type; + else { + o(0x7100001f); // cmp w0,#0 + o(0x1a9f07e0 | (uint32_t)cond << 12); // cset w0,(cond) + } + return; + } + + dbl = vtop[0].type.t != VT_FLOAT; + gv2(RC_FLOAT, RC_FLOAT); + assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST); + a = fltr(vtop[-1].r); + b = fltr(vtop[0].r); + vtop -= 2; + switch (op) { + case TOK_EQ: case TOK_NE: + case TOK_LT: case TOK_GE: case TOK_LE: case TOK_GT: + x = get_reg(RC_INT); + ++vtop; + vtop[0].r = x; + x = intr(x); + break; + default: + x = get_reg(RC_FLOAT); + ++vtop; + vtop[0].r = x; + x = fltr(x); + break; + } + + switch (op) { + case '*': + o(0x1e200800 | dbl << 22 | x | a << 5 | b << 16); // fmul + break; + case '+': + o(0x1e202800 | dbl << 22 | x | a << 5 | b << 16); // fadd + break; + case '-': + o(0x1e203800 | dbl << 22 | x | a << 5 | b << 16); // fsub + break; + case '/': + o(0x1e201800 | dbl << 22 | x | a << 5 | b << 16); // fdiv + break; + case TOK_EQ: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f17e0 | x); // cset w(x),eq + break; + case TOK_GE: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9fb7e0 | x); // cset w(x),ge + break; + case TOK_GT: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9fd7e0 | x); // cset w(x),gt + break; + case TOK_LE: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f87e0 | x); // cset w(x),ls + break; + case TOK_LT: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f57e0 | x); // cset w(x),mi + break; + case TOK_NE: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f07e0 | x); // cset w(x),ne + break; + default: + assert(0); + } + arm64_vset_VT_CMP(op); +} + +// Generate sign extension from 32 to 64 bits: +ST_FUNC void gen_cvt_sxtw(void) +{ + uint32_t r = intr(gv(RC_INT)); + o(0x93407c00 | r | r << 5); // sxtw x(r),w(r) +} + +/* char/short to int conversion */ +ST_FUNC void gen_cvt_csti(int t) +{ + int r = intr(gv(RC_INT)); + o(0x13001c00 + | ((t & VT_BTYPE) == VT_SHORT) << 13 + | (uint32_t)!!(t & VT_UNSIGNED) << 30 + | r | r << 5); // [su]xt[bh] w(r),w(r) +} + +ST_FUNC void gen_cvt_itof(int t) +{ + if (t == VT_LDOUBLE) { + int f = vtop->type.t; + int func = (f & VT_BTYPE) == VT_LLONG ? + (f & VT_UNSIGNED ? TOK___floatunditf : TOK___floatditf) : + (f & VT_UNSIGNED ? TOK___floatunsitf : TOK___floatsitf); + vpush_helper_func(func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_FRET; + return; + } + else { + int d, n = intr(gv(RC_INT)); + int s = !(vtop->type.t & VT_UNSIGNED); + uint32_t l = ((vtop->type.t & VT_BTYPE) == VT_LLONG); + --vtop; + d = get_reg(RC_FLOAT); + ++vtop; + vtop[0].r = d; + o(0x1e220000 | (uint32_t)!s << 16 | + (uint32_t)(t != VT_FLOAT) << 22 | fltr(d) | + l << 31 | n << 5); // [us]cvtf [sd](d),[wx](n) + } +} + +ST_FUNC void gen_cvt_ftoi(int t) +{ + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + int func = (t & VT_BTYPE) == VT_LLONG ? + (t & VT_UNSIGNED ? TOK___fixunstfdi : TOK___fixtfdi) : + (t & VT_UNSIGNED ? TOK___fixunstfsi : TOK___fixtfsi); + vpush_helper_func(func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_IRET; + return; + } + else { + int d, n = fltr(gv(RC_FLOAT)); + uint32_t l = ((vtop->type.t & VT_BTYPE) != VT_FLOAT); + --vtop; + d = get_reg(RC_INT); + ++vtop; + vtop[0].r = d; + o(0x1e380000 | + (uint32_t)!!(t & VT_UNSIGNED) << 16 | + (uint32_t)((t & VT_BTYPE) == VT_LLONG) << 31 | intr(d) | + l << 22 | n << 5); // fcvtz[su] [wx](d),[sd](n) + } +} + +ST_FUNC void gen_cvt_ftof(int t) +{ + int f = vtop[0].type.t & VT_BTYPE; + assert(t == VT_FLOAT || t == VT_DOUBLE || t == VT_LDOUBLE); + assert(f == VT_FLOAT || f == VT_DOUBLE || f == VT_LDOUBLE); + if (t == f) + return; + + if (t == VT_LDOUBLE || f == VT_LDOUBLE) { + int func = (t == VT_LDOUBLE) ? + (f == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) : + (t == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2); + vpush_helper_func(func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_FRET; + } + else { + int x, a; + gv(RC_FLOAT); + assert(vtop[0].r < VT_CONST); + a = fltr(vtop[0].r); + --vtop; + x = get_reg(RC_FLOAT); + ++vtop; + vtop[0].r = x; + x = fltr(x); + + if (f == VT_FLOAT) + o(0x1e22c000 | x | a << 5); // fcvt d(x),s(a) + else + o(0x1e624000 | x | a << 5); // fcvt s(x),d(a) + } +} + +/* increment tcov counter */ +ST_FUNC void gen_increment_tcov (SValue *sv) +{ + int r1, r2; + + vpushv(sv); + vtop->r = r1 = get_reg(RC_INT); + r2 = get_reg(RC_INT); + greloca(cur_text_section, sv->sym, ind, R_AARCH64_ADR_GOT_PAGE, 0); + o(0x90000000 | r1); // adrp r1, #sym + greloca(cur_text_section, sv->sym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); + o(0xf9400000 | r1 | (r1 << 5)); // ld xr,[xr, #sym] + o(0xf9400000 | (intr(r1)<<5) | intr(r2)); // ldr r2, [r1] + o(0x91000400 | (intr(r2)<<5) | intr(r2)); // add r2, r2, #1 + o(0xf9000000 | (intr(r1)<<5) | intr(r2)); // str r2, [r1] + vpop(); +} + +ST_FUNC void ggoto(void) +{ + arm64_gen_bl_or_b(1); + --vtop; +} + +ST_FUNC void gen_clear_cache(void) +{ + uint32_t beg, end, dsz, isz, p, lab1, b1; + gv2(RC_INT, RC_INT); + vpushi(0); + vtop->r = get_reg(RC_INT); + vpushi(0); + vtop->r = get_reg(RC_INT); + vpushi(0); + vtop->r = get_reg(RC_INT); + beg = intr(vtop[-4].r); // x0 + end = intr(vtop[-3].r); // x1 + dsz = intr(vtop[-2].r); // x2 + isz = intr(vtop[-1].r); // x3 + p = intr(vtop[0].r); // x4 + vtop -= 5; + + o(0xd53b0020 | isz); // mrs x(isz),ctr_el0 + o(0x52800080 | p); // mov w(p),#4 + o(0x53104c00 | dsz | isz << 5); // ubfx w(dsz),w(isz),#16,#4 + o(0x1ac02000 | dsz | p << 5 | dsz << 16); // lsl w(dsz),w(p),w(dsz) + o(0x12000c00 | isz | isz << 5); // and w(isz),w(isz),#15 + o(0x1ac02000 | isz | p << 5 | isz << 16); // lsl w(isz),w(p),w(isz) + o(0x51000400 | p | dsz << 5); // sub w(p),w(dsz),#1 + o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p) + b1 = ind; o(0x14000000); // b + lab1 = ind; + o(0xd50b7b20 | p); // dc cvau,x(p) + o(0x8b000000 | p | p << 5 | dsz << 16); // add x(p),x(p),x(dsz) + write32le(cur_text_section->data + b1, 0x14000000 | (ind - b1) >> 2); + o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end) + o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1 + o(0xd5033b9f); // dsb ish + o(0x51000400 | p | isz << 5); // sub w(p),w(isz),#1 + o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p) + b1 = ind; o(0x14000000); // b + lab1 = ind; + o(0xd50b7520 | p); // ic ivau,x(p) + o(0x8b000000 | p | p << 5 | isz << 16); // add x(p),x(p),x(isz) + write32le(cur_text_section->data + b1, 0x14000000 | (ind - b1) >> 2); + o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end) + o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1 + o(0xd5033b9f); // dsb ish + o(0xd5033fdf); // isb +} + +ST_FUNC void gen_vla_sp_save(int addr) { + uint32_t r = intr(get_reg(RC_INT)); + o(0x910003e0 | r); // mov x(r),sp + arm64_strx(3, r, 29, addr); +} + +ST_FUNC void gen_vla_sp_restore(int addr) { + // Use x30 because this function can be called when there + // is a live return value in x0 but there is nothing on + // the value stack to prevent get_reg from returning x0. + uint32_t r = 30; + arm64_ldrx(0, 3, r, 29, addr); + o(0x9100001f | r << 5); // mov sp,x(r) +} + +ST_FUNC void gen_vla_alloc(CType *type, int align) { + uint32_t r; +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) + vpushv(vtop); +#endif + r = intr(gv(RC_INT)); +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) + o(0x91004000 | r | r << 5); // add x(r),x(r),#15+1 + else +#endif + o(0x91003c00 | r | r << 5); // add x(r),x(r),#15 + o(0x927cec00 | r | r << 5); // bic x(r),x(r),#15 + o(0xcb2063ff | r << 16); // sub sp,sp,x(r) + vpop(); +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) { + vpushi(0); + vtop->r = TREG_R(0); + o(0x910003e0 | vtop->r); // mov r0,sp + vswap(); + vpush_helper_func(TOK___bound_new_region); + vrott(3); + gfunc_call(2); + func_bound_add_epilog = 1; + } +#endif +} + +/* end of A64 code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/vendor/tcc/arm64-link.c b/vendor/tcc/arm64-link.c new file mode 100644 index 00000000..568ac1fe --- /dev/null +++ b/vendor/tcc/arm64-link.c @@ -0,0 +1,322 @@ +#ifdef TARGET_DEFS_ONLY + +#define EM_TCC_TARGET EM_AARCH64 + +#define R_DATA_32 R_AARCH64_ABS32 +#define R_DATA_PTR R_AARCH64_ABS64 +#define R_JMP_SLOT R_AARCH64_JUMP_SLOT +#define R_GLOB_DAT R_AARCH64_GLOB_DAT +#define R_COPY R_AARCH64_COPY +#define R_RELATIVE R_AARCH64_RELATIVE + +#define R_NUM R_AARCH64_NUM + +#define ELF_START_ADDR 0x00400000 +#define ELF_PAGE_SIZE 0x10000 + +#define PCRELATIVE_DLLPLT 1 +#define RELOCATE_DLLPLT 1 + +#else /* !TARGET_DEFS_ONLY */ + +#include "tcc.h" + +#ifdef NEED_RELOC_TYPE +/* Returns 1 for a code relocation, 0 for a data relocation. For unknown + relocations, returns -1. */ +int code_reloc (int reloc_type) +{ + switch (reloc_type) { + case R_AARCH64_ABS32: + case R_AARCH64_ABS64: + case R_AARCH64_PREL32: + case R_AARCH64_MOVW_UABS_G0_NC: + case R_AARCH64_MOVW_UABS_G1_NC: + case R_AARCH64_MOVW_UABS_G2_NC: + case R_AARCH64_MOVW_UABS_G3: + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_GLOB_DAT: + case R_AARCH64_COPY: + return 0; + + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + case R_AARCH64_JUMP_SLOT: + return 1; + } + return -1; +} + +/* Returns an enumerator to describe whether and when the relocation needs a + GOT and/or PLT entry to be created. See tcc.h for a description of the + different values. */ +int gotplt_entry_type (int reloc_type) +{ + switch (reloc_type) { + case R_AARCH64_PREL32: + case R_AARCH64_MOVW_UABS_G0_NC: + case R_AARCH64_MOVW_UABS_G1_NC: + case R_AARCH64_MOVW_UABS_G2_NC: + case R_AARCH64_MOVW_UABS_G3: + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_GLOB_DAT: + case R_AARCH64_JUMP_SLOT: + case R_AARCH64_COPY: + return NO_GOTPLT_ENTRY; + + case R_AARCH64_ABS32: + case R_AARCH64_ABS64: + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + return AUTO_GOTPLT_ENTRY; + + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + return ALWAYS_GOTPLT_ENTRY; + } + return -1; +} + +#ifdef NEED_BUILD_GOT +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 (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 + 16; + uint64_t off = (got >> 12) - (plt >> 12); + if ((off + ((uint32_t)1 << 20)) >> 21) + tcc_error_noabort("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", (long)off, (long)got, (long)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; + got = s1->got->sh_addr; + 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_noabort("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", (long)off, (long)addr, (long)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; + } + } + + if (s1->plt->reloc) { + ElfW_Rel *rel; + p = s1->got->data; + for_each_elem(s1->plt->reloc, 0, rel, ElfW_Rel) { + write64le(p + rel->r_offset, s1->plt->sh_addr); + } + } +} +#endif +#endif + +void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) +{ + int sym_index = ELFW(R_SYM)(rel->r_info), esym_index; +#ifdef DEBUG_RELOC + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; +#endif + + switch(type) { + case R_AARCH64_ABS64: + if ((s1->output_type & TCC_OUTPUT_DYN)) { + esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index; + qrel->r_offset = rel->r_offset; + if (esym_index) { + qrel->r_info = ELFW(R_INFO)(esym_index, R_AARCH64_ABS64); + qrel->r_addend = rel->r_addend; + qrel++; + break; + } else { + qrel->r_info = ELFW(R_INFO)(0, R_AARCH64_RELATIVE); + qrel->r_addend = read64le(ptr) + val; + qrel++; + } + } + add64le(ptr, val); + return; + case R_AARCH64_ABS32: + if (s1->output_type & TCC_OUTPUT_DYN) { + /* XXX: this logic may depend on TCC's codegen + now TCC uses R_AARCH64_RELATIVE even for a 64bit pointer */ + qrel->r_offset = rel->r_offset; + qrel->r_info = ELFW(R_INFO)(0, R_AARCH64_RELATIVE); + /* Use sign extension! */ + qrel->r_addend = (int)read32le(ptr) + val; + qrel++; + } + add32le(ptr, val); + return; + case R_AARCH64_PREL32: + if (s1->output_type == TCC_OUTPUT_DLL) { + /* DLL relocation */ + esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index; + if (esym_index) { + qrel->r_offset = rel->r_offset; + qrel->r_info = ELFW(R_INFO)(esym_index, R_AARCH64_PREL32); + /* Use sign extension! */ + qrel->r_addend = (int)read32le(ptr) + rel->r_addend; + qrel++; + break; + } + } + write32le(ptr, val - addr); + return; + case R_AARCH64_MOVW_UABS_G0_NC: + write32le(ptr, ((read32le(ptr) & 0xffe0001f) | + (val & 0xffff) << 5)); + return; + case R_AARCH64_MOVW_UABS_G1_NC: + write32le(ptr, ((read32le(ptr) & 0xffe0001f) | + (val >> 16 & 0xffff) << 5)); + return; + case R_AARCH64_MOVW_UABS_G2_NC: + write32le(ptr, ((read32le(ptr) & 0xffe0001f) | + (val >> 32 & 0xffff) << 5)); + return; + case R_AARCH64_MOVW_UABS_G3: + write32le(ptr, ((read32le(ptr) & 0xffe0001f) | + (val >> 48 & 0xffff) << 5)); + return; + case R_AARCH64_ADR_PREL_PG_HI21: { + uint64_t off = (val >> 12) - (addr >> 12); + if ((off + ((uint64_t)1 << 20)) >> 21) + tcc_error_noabort("R_AARCH64_ADR_PREL_PG_HI21 relocation failed"); + write32le(ptr, ((read32le(ptr) & 0x9f00001f) | + (off & 0x1ffffc) << 3 | (off & 3) << 29)); + return; + } + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + write32le(ptr, ((read32le(ptr) & 0xffc003ff) | + (val & 0xfff) << 10)); + return; + case R_AARCH64_LDST16_ABS_LO12_NC: + write32le(ptr, ((read32le(ptr) & 0xffc003ff) | + (val & 0xffe) << 9)); + return; + case R_AARCH64_LDST32_ABS_LO12_NC: + write32le(ptr, ((read32le(ptr) & 0xffc003ff) | + (val & 0xffc) << 8)); + return; + case R_AARCH64_LDST64_ABS_LO12_NC: + write32le(ptr, ((read32le(ptr) & 0xffc003ff) | + (val & 0xff8) << 7)); + return; + case R_AARCH64_LDST128_ABS_LO12_NC: + write32le(ptr, ((read32le(ptr) & 0xffc003ff) | + (val & 0xff0) << 6)); + return; + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: +#ifdef DEBUG_RELOC + printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val, + (char *) symtab_section->link->data + sym->st_name); +#endif + if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc) + tcc_error_noabort("R_AARCH64_(JUMP|CALL)26 relocation failed" + " (val=%lx, addr=%lx)", (long)val, (long)addr); + write32le(ptr, (0x14000000 | + (uint32_t)(type == R_AARCH64_CALL26) << 31 | + ((val - addr) >> 2 & 0x3ffffff))); + return; + case R_AARCH64_ADR_GOT_PAGE: { + uint64_t off = + (((s1->got->sh_addr + + get_sym_attr(s1, sym_index, 0)->got_offset) >> 12) - (addr >> 12)); + if ((off + ((uint64_t)1 << 20)) >> 21) + tcc_error_noabort("R_AARCH64_ADR_GOT_PAGE relocation failed"); + write32le(ptr, ((read32le(ptr) & 0x9f00001f) | + (off & 0x1ffffc) << 3 | (off & 3) << 29)); + return; + } + case R_AARCH64_LD64_GOT_LO12_NC: + write32le(ptr, + ((read32le(ptr) & 0xfff803ff) | + ((s1->got->sh_addr + + get_sym_attr(s1, sym_index, 0)->got_offset) & 0xff8) << 7)); + return; + case R_AARCH64_COPY: + return; + case R_AARCH64_GLOB_DAT: + case R_AARCH64_JUMP_SLOT: + /* They don't need addend */ +#ifdef DEBUG_RELOC + printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, + val - rel->r_addend, + (char *) symtab_section->link->data + sym->st_name); +#endif + write64le(ptr, val - rel->r_addend); + return; + case R_AARCH64_RELATIVE: +#ifdef TCC_TARGET_PE + add32le(ptr, val - s1->pe_imagebase); +#endif + /* do nothing */ + return; + default: + fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + return; + } +} + +#endif /* !TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/c67-gen.c b/vendor/tcc/c67-gen.c new file mode 100644 index 00000000..9490a27f --- /dev/null +++ b/vendor/tcc/c67-gen.c @@ -0,0 +1,2543 @@ +/* + * TMS320C67xx code generator for TCC + * + * Copyright (c) 2001, 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TARGET_DEFS_ONLY + +/* #define ASSEMBLY_LISTING_C67 */ + +/* number of available registers */ +#define NB_REGS 24 + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_EAX 0x0004 +#define RC_ST0 0x0008 +#define RC_ECX 0x0010 +#define RC_EDX 0x0020 +#define RC_INT_BSIDE 0x00000040 /* generic integer register on b side */ +#define RC_C67_A4 0x00000100 +#define RC_C67_A5 0x00000200 +#define RC_C67_B4 0x00000400 +#define RC_C67_B5 0x00000800 +#define RC_C67_A6 0x00001000 +#define RC_C67_A7 0x00002000 +#define RC_C67_B6 0x00004000 +#define RC_C67_B7 0x00008000 +#define RC_C67_A8 0x00010000 +#define RC_C67_A9 0x00020000 +#define RC_C67_B8 0x00040000 +#define RC_C67_B9 0x00080000 +#define RC_C67_A10 0x00100000 +#define RC_C67_A11 0x00200000 +#define RC_C67_B10 0x00400000 +#define RC_C67_B11 0x00800000 +#define RC_C67_A12 0x01000000 +#define RC_C67_A13 0x02000000 +#define RC_C67_B12 0x04000000 +#define RC_C67_B13 0x08000000 +#define RC_IRET RC_C67_A4 /* function return: integer register */ +#define RC_IRE2 RC_C67_A5 /* function return: second integer register */ +#define RC_FRET RC_C67_A4 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_EAX = 0, // really A2 + TREG_ECX, // really A3 + TREG_EDX, // really B0 + TREG_ST0, // really B1 + TREG_C67_A4, + TREG_C67_A5, + TREG_C67_B4, + TREG_C67_B5, + TREG_C67_A6, + TREG_C67_A7, + TREG_C67_B6, + TREG_C67_B7, + TREG_C67_A8, + TREG_C67_A9, + TREG_C67_B8, + TREG_C67_B9, + TREG_C67_A10, + TREG_C67_A11, + TREG_C67_B10, + TREG_C67_B11, + TREG_C67_A12, + TREG_C67_A13, + TREG_C67_B12, + TREG_C67_B13, +}; + +/* return registers for function */ +#define REG_IRET TREG_C67_A4 /* single word int return register */ +#define REG_IRE2 TREG_C67_A5 /* second word return register (for long long) */ +#define REG_FRET TREG_C67_A4 /* float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +/* #define INVERT_FUNC_PARAMS */ + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +/* #define FUNC_STRUCT_PARAM_AS_PTR */ + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 12 +#define LDOUBLE_ALIGN 4 +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +#undef CONFIG_TCC_BCHECK + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#define USING_GLOBALS +#include "tcc.h" + +ST_DATA const char * const target_machine_defs = + "__C67__\0" + ; + +ST_DATA const int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_FLOAT | RC_EAX, + // only allow even regs for floats (allow for doubles) + /* ecx */ RC_INT | RC_ECX, + /* edx */ RC_INT | RC_INT_BSIDE | RC_FLOAT | RC_EDX, + // only allow even regs for floats (allow for doubles) + /* st0 */ RC_INT | RC_INT_BSIDE | RC_ST0, + /* A4 */ RC_C67_A4, + /* A5 */ RC_C67_A5, + /* B4 */ RC_C67_B4, + /* B5 */ RC_C67_B5, + /* A6 */ RC_C67_A6, + /* A7 */ RC_C67_A7, + /* B6 */ RC_C67_B6, + /* B7 */ RC_C67_B7, + /* A8 */ RC_C67_A8, + /* A9 */ RC_C67_A9, + /* B8 */ RC_C67_B8, + /* B9 */ RC_C67_B9, + /* A10 */ RC_C67_A10, + /* A11 */ RC_C67_A11, + /* B10 */ RC_C67_B10, + /* B11 */ RC_C67_B11, + /* A12 */ RC_C67_A10, + /* A13 */ RC_C67_A11, + /* B12 */ RC_C67_B10, + /* B13 */ RC_C67_B11 +}; + +// although tcc thinks it is passing parameters on the stack, +// the C67 really passes up to the first 10 params in special +// regs or regs pairs (for 64 bit params). So keep track of +// the stack offsets so we can translate to the appropriate +// reg (pair) + +#define NoCallArgsPassedOnStack 10 +int NoOfCurFuncArgs; +int TranslateStackToReg[NoCallArgsPassedOnStack]; +int ParamLocOnStack[NoCallArgsPassedOnStack]; +int TotalBytesPushedOnStack; + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +#undef BOOL +#define BOOL int + +#define ALWAYS_ASSERT(x) \ +do {\ + if (!(x))\ + tcc_error("internal compiler error file at %s:%d", __FILE__, __LINE__);\ +} while (0) + +/******************************************************/ +static unsigned long func_sub_sp_offset; +static int func_ret_sub; + +static BOOL C67_invert_test; +static int C67_compare_reg; + +#ifdef ASSEMBLY_LISTING_C67 +FILE *f = NULL; +#endif + +void C67_g(int c) +{ + int ind1; + if (nocode_wanted) + return; +#ifdef ASSEMBLY_LISTING_C67 + fprintf(f, " %08X", c); +#endif + ind1 = ind + 4; + if (ind1 > (int) cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c & 0xff; + cur_text_section->data[ind + 1] = (c >> 8) & 0xff; + cur_text_section->data[ind + 2] = (c >> 16) & 0xff; + cur_text_section->data[ind + 3] = (c >> 24) & 0xff; + ind = ind1; +} + + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + int n, *ptr; + while (t) { + ptr = (int *) (cur_text_section->data + t); + { + Sym *sym; + + // extract 32 bit address from MVKH/MVKL + n = ((*ptr >> 7) & 0xffff); + n |= ((*(ptr + 1) >> 7) & 0xffff) << 16; + + // define a label that will be relocated + + sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0); + greloc(cur_text_section, sym, t, R_C60LO16); + greloc(cur_text_section, sym, t + 4, R_C60HI16); + + // clear out where the pointer was + + *ptr &= ~(0xffff << 7); + *(ptr + 1) &= ~(0xffff << 7); + } + t = n; + } +} + +// these are regs that tcc doesn't really know about, +// but assign them unique values so the mapping routines +// can distinguish them + +#define C67_A0 105 +#define C67_SP 106 +#define C67_B3 107 +#define C67_FP 108 +#define C67_B2 109 +#define C67_CREG_ZERO -1 /* Special code for no condition reg test */ + + +int ConvertRegToRegClass(int r) +{ + // only works for A4-B13 + + return RC_C67_A4 << (r - TREG_C67_A4); +} + + +// map TCC reg to C67 reg number + +int C67_map_regn(int r) +{ + if (r == 0) // normal tcc regs + return 0x2; // A2 + else if (r == 1) // normal tcc regs + return 3; // A3 + else if (r == 2) // normal tcc regs + return 0; // B0 + else if (r == 3) // normal tcc regs + return 1; // B1 + else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs + return (((r & 0xfffffffc) >> 1) | (r & 1)) + 2; + else if (r == C67_A0) + return 0; // set to A0 (offset reg) + else if (r == C67_B2) + return 2; // set to B2 (offset reg) + else if (r == C67_B3) + return 3; // set to B3 (return address reg) + else if (r == C67_SP) + return 15; // set to SP (B15) (offset reg) + else if (r == C67_FP) + return 15; // set to FP (A15) (offset reg) + else if (r == C67_CREG_ZERO) + return 0; // Special code for no condition reg test + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +// mapping from tcc reg number to +// C67 register to condition code field +// +// valid condition code regs are: +// +// tcc reg 2 ->B0 -> 1 +// tcc reg 3 ->B1 -> 2 +// tcc reg 0 -> A2 -> 5 +// tcc reg 1 -> A3 -> X +// tcc reg B2 -> 3 + +int C67_map_regc(int r) +{ + if (r == 0) // normal tcc regs + return 0x5; + else if (r == 2) // normal tcc regs + return 0x1; + else if (r == 3) // normal tcc regs + return 0x2; + else if (r == C67_B2) // normal tcc regs + return 0x3; + else if (r == C67_CREG_ZERO) + return 0; // Special code for no condition reg test + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + + +// map TCC reg to C67 reg side A or B + +int C67_map_regs(int r) +{ + if (r == 0) // normal tcc regs + return 0x0; + else if (r == 1) // normal tcc regs + return 0x0; + else if (r == 2) // normal tcc regs + return 0x1; + else if (r == 3) // normal tcc regs + return 0x1; + else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs + return (r & 2) >> 1; + else if (r == C67_A0) + return 0; // set to A side + else if (r == C67_B2) + return 1; // set to B side + else if (r == C67_B3) + return 1; // set to B side + else if (r == C67_SP) + return 0x1; // set to SP (B15) B side + else if (r == C67_FP) + return 0x0; // set to FP (A15) A side + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +int C67_map_S12(char *s) +{ + if (strstr(s, ".S1") != NULL) + return 0; + else if (strcmp(s, ".S2")) + return 1; + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +int C67_map_D12(char *s) +{ + if (strstr(s, ".D1") != NULL) + return 0; + else if (strcmp(s, ".D2")) + return 1; + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + + + +void C67_asm(const char *s, int a, int b, int c) +{ + BOOL xpath; + +#ifdef ASSEMBLY_LISTING_C67 + if (!f) { + f = fopen("TCC67_out.txt", "wt"); + } + fprintf(f, "%04X ", ind); +#endif + + if (strstr(s, "MVKL") == s) { + C67_g((C67_map_regn(b) << 23) | + ((a & 0xffff) << 7) | (0x0a << 2) | (C67_map_regs(b) << 1)); + } else if (strstr(s, "MVKH") == s) { + C67_g((C67_map_regn(b) << 23) | + (((a >> 16) & 0xffff) << 7) | + (0x1a << 2) | (C67_map_regs(b) << 1)); + } else if (strstr(s, "STW.D SP POST DEC") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //SP B15 + (2 << 13) | //ucst5 (must keep 8 byte boundary !!) + (0xa << 9) | //mode a = post dec ucst + (0 << 8) | //r (LDDW bit 0) + (1 << 7) | //y D1/D2 use B side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D +*") == s) { + ALWAYS_ASSERT(c < 32); + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (c << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D SP PRE INC") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg B15 + (2 << 13) | //ucst5 (must keep 8 byte boundary) + (9 << 9) | //mode 9 = pre inc ucst5 + (0 << 8) | //r (LDDW bit 0) + (1 << 7) | //y D1/D2 B side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D SP PRE INC") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg B15 + (1 << 13) | //ucst5 (must keep 8 byte boundary) + (9 << 9) | //mode 9 = pre inc ucst5 + (1 << 8) | //r (LDDW bit 1) + (1 << 7) | //y D1/D2 B side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (1 << 8) | //r (LDDW bit 1) + (0 << 7) | //y D1/D2 A side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDHU.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDBU.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (1 << 8) | //r (LDDW bit 1) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDH.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDB.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDHU.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDBU.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D +*") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (1 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "CMPLTSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x3a << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x39 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x38 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } + + else if (strstr(s, "CMPLTDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x2a << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x29 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x28 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPLT") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x57 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGT") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x47 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQ") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x53 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPLTU") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x5f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTU") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x4f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "B DISP") == s) { + C67_g((0 << 29) | //creg + (0 << 28) | //z + (a << 7) | //cnst + (0x4 << 2) | //opcode fixed + (0 << 1) | //S0/S1 + (0 << 0)); //parallel + } else if (strstr(s, "B.") == s) { + xpath = C67_map_regs(c) ^ 1; + + C67_g((C67_map_regc(b) << 29) | //creg + (a << 28) | //inv + (0 << 23) | //dst + (C67_map_regn(c) << 18) | //src2 + (0 << 13) | // + (xpath << 12) | //x cross path if !B side + (0xd << 6) | //opcode + (0x8 << 2) | //opcode fixed + (1 << 1) | //must be S2 + (0 << 0)); //parallel + } else if (strstr(s, "MV.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 (cst5) + (xpath << 12) | //x cross path if opposite sides + (0x2 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SPTRUNC.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0xb << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "DPTRUNC.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x1 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x4a << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTSPU.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x49 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x39 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTDPU.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x3b << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SPDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x2 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "DPSP.L") == s) { + ALWAYS_ASSERT(C67_map_regs(b) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (0 << 12) | //x cross path if opposite sides + (0x9 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADD.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x3 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUB.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "OR.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "AND.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7b << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "XOR.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x6f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADDSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x10 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADDDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x18 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUBSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x11 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUBDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x19 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYSP.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x1c << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYDP.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x0e << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYI.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 (cst5) + (xpath << 12) | //x cross path if opposite sides + (0x4 << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHR.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x37 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHRU.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x27 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHL.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x33 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "||ADDK") == s) { + xpath = 0; // no xpath required just use the side of the src/dst + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(b) << 23) | //dst + (a << 07) | //scst16 + (0x14 << 2) | //opcode fixed + (C67_map_regs(b) << 1) | //side of dst + (1 << 0)); //parallel + } else if (strstr(s, "ADDK") == s) { + xpath = 0; // no xpath required just use the side of the src/dst + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(b) << 23) | //dst + (a << 07) | //scst16 + (0x14 << 2) | //opcode fixed + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "NOP") == s) { + C67_g(((a - 1) << 13) | //no of cycles + (0 << 0)); //parallel + } else + ALWAYS_ASSERT(FALSE); + +#ifdef ASSEMBLY_LISTING_C67 + fprintf(f, " %s %d %d %d\n", s, a, b, c); +#endif + +} + +//r=reg to load, fr=from reg, symbol for relocation, constant + +void C67_MVKL(int r, int fc) +{ + C67_asm("MVKL.", fc, r, 0); +} + +void C67_MVKH(int r, int fc) +{ + C67_asm("MVKH.", fc, r, 0); +} + +void C67_STB_SP_A0(int r) +{ + C67_asm("STB.D *+SP[A0]", r, 0, 0); // STB r,*+SP[A0] +} + +void C67_STH_SP_A0(int r) +{ + C67_asm("STH.D *+SP[A0]", r, 0, 0); // STH r,*+SP[A0] +} + +void C67_STW_SP_A0(int r) +{ + C67_asm("STW.D *+SP[A0]", r, 0, 0); // STW r,*+SP[A0] +} + +void C67_STB_PTR(int r, int r2) +{ + C67_asm("STB.D *", r, r2, 0); // STB r, *r2 +} + +void C67_STH_PTR(int r, int r2) +{ + C67_asm("STH.D *", r, r2, 0); // STH r, *r2 +} + +void C67_STW_PTR(int r, int r2) +{ + C67_asm("STW.D *", r, r2, 0); // STW r, *r2 +} + +void C67_STW_PTR_PRE_INC(int r, int r2, int n) +{ + C67_asm("STW.D +*", r, r2, n); // STW r, *+r2 +} + +void C67_PUSH(int r) +{ + C67_asm("STW.D SP POST DEC", r, 0, 0); // STW r,*SP-- +} + +void C67_LDW_SP_A0(int r) +{ + C67_asm("LDW.D *+SP[A0]", r, 0, 0); // LDW *+SP[A0],r +} + +void C67_LDDW_SP_A0(int r) +{ + C67_asm("LDDW.D *+SP[A0]", r, 0, 0); // LDDW *+SP[A0],r +} + +void C67_LDH_SP_A0(int r) +{ + C67_asm("LDH.D *+SP[A0]", r, 0, 0); // LDH *+SP[A0],r +} + +void C67_LDB_SP_A0(int r) +{ + C67_asm("LDB.D *+SP[A0]", r, 0, 0); // LDB *+SP[A0],r +} + +void C67_LDHU_SP_A0(int r) +{ + C67_asm("LDHU.D *+SP[A0]", r, 0, 0); // LDHU *+SP[A0],r +} + +void C67_LDBU_SP_A0(int r) +{ + C67_asm("LDBU.D *+SP[A0]", r, 0, 0); // LDBU *+SP[A0],r +} + +void C67_LDW_PTR(int r, int r2) +{ + C67_asm("LDW.D *", r, r2, 0); // LDW *r,r2 +} + +void C67_LDDW_PTR(int r, int r2) +{ + C67_asm("LDDW.D *", r, r2, 0); // LDDW *r,r2 +} + +void C67_LDH_PTR(int r, int r2) +{ + C67_asm("LDH.D *", r, r2, 0); // LDH *r,r2 +} + +void C67_LDB_PTR(int r, int r2) +{ + C67_asm("LDB.D *", r, r2, 0); // LDB *r,r2 +} + +void C67_LDHU_PTR(int r, int r2) +{ + C67_asm("LDHU.D *", r, r2, 0); // LDHU *r,r2 +} + +void C67_LDBU_PTR(int r, int r2) +{ + C67_asm("LDBU.D *", r, r2, 0); // LDBU *r,r2 +} + +void C67_LDW_PTR_PRE_INC(int r, int r2) +{ + C67_asm("LDW.D +*", r, r2, 0); // LDW *+r,r2 +} + +void C67_POP(int r) +{ + C67_asm("LDW.D SP PRE INC", r, 0, 0); // LDW *++SP,r +} + +void C67_POP_DW(int r) +{ + C67_asm("LDDW.D SP PRE INC", r, 0, 0); // LDDW *++SP,r +} + +void C67_CMPLT(int s1, int s2, int dst) +{ + C67_asm("CMPLT.L1", s1, s2, dst); +} + +void C67_CMPGT(int s1, int s2, int dst) +{ + C67_asm("CMPGT.L1", s1, s2, dst); +} + +void C67_CMPEQ(int s1, int s2, int dst) +{ + C67_asm("CMPEQ.L1", s1, s2, dst); +} + +void C67_CMPLTU(int s1, int s2, int dst) +{ + C67_asm("CMPLTU.L1", s1, s2, dst); +} + +void C67_CMPGTU(int s1, int s2, int dst) +{ + C67_asm("CMPGTU.L1", s1, s2, dst); +} + + +void C67_CMPLTSP(int s1, int s2, int dst) +{ + C67_asm("CMPLTSP.S1", s1, s2, dst); +} + +void C67_CMPGTSP(int s1, int s2, int dst) +{ + C67_asm("CMPGTSP.S1", s1, s2, dst); +} + +void C67_CMPEQSP(int s1, int s2, int dst) +{ + C67_asm("CMPEQSP.S1", s1, s2, dst); +} + +void C67_CMPLTDP(int s1, int s2, int dst) +{ + C67_asm("CMPLTDP.S1", s1, s2, dst); +} + +void C67_CMPGTDP(int s1, int s2, int dst) +{ + C67_asm("CMPGTDP.S1", s1, s2, dst); +} + +void C67_CMPEQDP(int s1, int s2, int dst) +{ + C67_asm("CMPEQDP.S1", s1, s2, dst); +} + + +void C67_IREG_B_REG(int inv, int r1, int r2) // [!R] B r2 +{ + C67_asm("B.S2", inv, r1, r2); +} + + +// call with how many 32 bit words to skip +// (0 would branch to the branch instruction) + +void C67_B_DISP(int disp) // B +2 Branch with constant displacement +{ + // Branch point is relative to the 8 word fetch packet + // + // we will assume the text section always starts on an 8 word (32 byte boundary) + // + // so add in how many words into the fetch packet the branch is + + + C67_asm("B DISP", disp + ((ind & 31) >> 2), 0, 0); +} + +void C67_NOP(int n) +{ + C67_asm("NOP", n, 0, 0); +} + +void C67_ADDK(int n, int r) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + C67_asm("ADDK", n, r, 0); +} + +void C67_ADDK_PARALLEL(int n, int r) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + C67_asm("||ADDK", n, r, 0); +} + +void C67_Adjust_ADDK(int *inst, int n) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + *inst = (*inst & (~(0xffff << 7))) | ((n & 0xffff) << 7); +} + +void C67_MV(int r, int v) +{ + C67_asm("MV.L", 0, r, v); +} + + +void C67_DPTRUNC(int r, int v) +{ + C67_asm("DPTRUNC.L", 0, r, v); +} + +void C67_SPTRUNC(int r, int v) +{ + C67_asm("SPTRUNC.L", 0, r, v); +} + +void C67_INTSP(int r, int v) +{ + C67_asm("INTSP.L", 0, r, v); +} + +void C67_INTDP(int r, int v) +{ + C67_asm("INTDP.L", 0, r, v); +} + +void C67_INTSPU(int r, int v) +{ + C67_asm("INTSPU.L", 0, r, v); +} + +void C67_INTDPU(int r, int v) +{ + C67_asm("INTDPU.L", 0, r, v); +} + +void C67_SPDP(int r, int v) +{ + C67_asm("SPDP.L", 0, r, v); +} + +void C67_DPSP(int r, int v) // note regs must be on the same side +{ + C67_asm("DPSP.L", 0, r, v); +} + +void C67_ADD(int r, int v) +{ + C67_asm("ADD.L", v, r, v); +} + +void C67_SUB(int r, int v) +{ + C67_asm("SUB.L", v, r, v); +} + +void C67_AND(int r, int v) +{ + C67_asm("AND.L", v, r, v); +} + +void C67_OR(int r, int v) +{ + C67_asm("OR.L", v, r, v); +} + +void C67_XOR(int r, int v) +{ + C67_asm("XOR.L", v, r, v); +} + +void C67_ADDSP(int r, int v) +{ + C67_asm("ADDSP.L", v, r, v); +} + +void C67_SUBSP(int r, int v) +{ + C67_asm("SUBSP.L", v, r, v); +} + +void C67_MPYSP(int r, int v) +{ + C67_asm("MPYSP.M", v, r, v); +} + +void C67_ADDDP(int r, int v) +{ + C67_asm("ADDDP.L", v, r, v); +} + +void C67_SUBDP(int r, int v) +{ + C67_asm("SUBDP.L", v, r, v); +} + +void C67_MPYDP(int r, int v) +{ + C67_asm("MPYDP.M", v, r, v); +} + +void C67_MPYI(int r, int v) +{ + C67_asm("MPYI.M", v, r, v); +} + +void C67_SHL(int r, int v) +{ + C67_asm("SHL.S", r, v, v); +} + +void C67_SHRU(int r, int v) +{ + C67_asm("SHRU.S", r, v, v); +} + +void C67_SHR(int r, int v) +{ + C67_asm("SHR.S", r, v, v); +} + + + +/* load 'r' from value 'sv' */ +void load(int r, SValue * sv) +{ + int v, t, ft, fc, fr, size = 0, element; + BOOL Unsigned = FALSE; + SValue v1; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.i; + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + if (v == VT_LLOCAL) { + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.i = fc; + load(r, &v1); + fr = r; + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + tcc_error("long double not supported"); + } else if ((ft & VT_TYPE) == VT_BYTE) { + size = 1; + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + size = 1; + Unsigned = TRUE; + } else if ((ft & VT_TYPE) == VT_SHORT) { + size = 2; + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + size = 2; + Unsigned = TRUE; + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + size = 8; + } else { + size = 4; + } + + // check if fc is a positive reference on the stack, + // if it is tcc is referencing what it thinks is a parameter + // on the stack, so check if it is really in a register. + + + if (v == VT_LOCAL && fc > 0) { + int stack_pos = 8; + + for (t = 0; t < NoCallArgsPassedOnStack; t++) { + if (fc == stack_pos) + break; + + stack_pos += TranslateStackToReg[t]; + } + + // param has been pushed on stack, get it like a local var + + fc = ParamLocOnStack[t] - 8; + } + + if ((fr & VT_VALMASK) < VT_CONST) // check for pure indirect + { + if (size == 1) { + if (Unsigned) + C67_LDBU_PTR(v, r); // LDBU *v,r + else + C67_LDB_PTR(v, r); // LDB *v,r + } else if (size == 2) { + if (Unsigned) + C67_LDHU_PTR(v, r); // LDHU *v,r + else + C67_LDH_PTR(v, r); // LDH *v,r + } else if (size == 4) { + C67_LDW_PTR(v, r); // LDW *v,r + } else if (size == 8) { + C67_LDDW_PTR(v, r); // LDDW *v,r + } + + C67_NOP(4); // NOP 4 + return; + } else if (fr & VT_SYM) { + greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16); + + + C67_MVKL(C67_A0, fc); //r=reg to load, constant + C67_MVKH(C67_A0, fc); //r=reg to load, constant + + + if (size == 1) { + if (Unsigned) + C67_LDBU_PTR(C67_A0, r); // LDBU *A0,r + else + C67_LDB_PTR(C67_A0, r); // LDB *A0,r + } else if (size == 2) { + if (Unsigned) + C67_LDHU_PTR(C67_A0, r); // LDHU *A0,r + else + C67_LDH_PTR(C67_A0, r); // LDH *A0,r + } else if (size == 4) { + C67_LDW_PTR(C67_A0, r); // LDW *A0,r + } else if (size == 8) { + C67_LDDW_PTR(C67_A0, r); // LDDW *A0,r + } + + C67_NOP(4); // NOP 4 + return; + } else { + element = size; + + // divide offset in bytes to create element index + C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + + if (size == 1) { + if (Unsigned) + C67_LDBU_SP_A0(r); // LDBU r, SP[A0] + else + C67_LDB_SP_A0(r); // LDB r, SP[A0] + } else if (size == 2) { + if (Unsigned) + C67_LDHU_SP_A0(r); // LDHU r, SP[A0] + else + C67_LDH_SP_A0(r); // LDH r, SP[A0] + } else if (size == 4) { + C67_LDW_SP_A0(r); // LDW r, SP[A0] + } else if (size == 8) { + C67_LDDW_SP_A0(r); // LDDW r, SP[A0] + } + + + C67_NOP(4); // NOP 4 + return; + } + } else { + if (v == VT_CONST) { + if (fr & VT_SYM) { + greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16); + } + C67_MVKL(r, fc); //r=reg to load, constant + C67_MVKH(r, fc); //r=reg to load, constant + } else if (v == VT_LOCAL) { + C67_MVKL(r, fc + 8); //r=reg to load, constant C67 stack points to next free + C67_MVKH(r, fc + 8); //r=reg to load, constant + C67_ADD(C67_FP, r); // MV v,r v -> r + } else if (v == VT_CMP) { + C67_MV(C67_compare_reg, r); // MV v,r v -> r + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + C67_B_DISP(4); // Branch with constant displacement, skip over this branch, load, nop, load + C67_MVKL(r, t); // r=reg to load, 0 or 1 (do this while branching) + C67_NOP(4); // NOP 4 + gsym(fc); // modifies other branches to branch here + C67_MVKL(r, t ^ 1); // r=reg to load, 0 or 1 + } else if (v != r) { + C67_MV(v, r); // MV v,r v -> r + + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_MV(v + 1, r + 1); // MV v,r v -> r + } + } +} + + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue * v) +{ + int fr, bt, ft, fc, size, t, element; + + ft = v->type.t; + fc = v->c.i; + fr = v->r & VT_VALMASK; + bt = ft & VT_BTYPE; + /* XXX: incorrect if float reg to reg */ + + if (bt == VT_LDOUBLE) { + tcc_error("long double not supported"); + } else { + if (bt == VT_SHORT) + size = 2; + else if (bt == VT_BYTE) + size = 1; + else if (bt == VT_DOUBLE) + size = 8; + else + size = 4; + + if ((v->r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + + if (v->r & VT_SYM) { + greloc(cur_text_section, v->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, v->sym, ind + 4, R_C60HI16); + } + C67_MVKL(C67_A0, fc); //r=reg to load, constant + C67_MVKH(C67_A0, fc); //r=reg to load, constant + + if (size == 1) + C67_STB_PTR(r, C67_A0); // STB r, *A0 + else if (size == 2) + C67_STH_PTR(r, C67_A0); // STH r, *A0 + else if (size == 4 || size == 8) + C67_STW_PTR(r, C67_A0); // STW r, *A0 + + if (size == 8) + C67_STW_PTR_PRE_INC(r + 1, C67_A0, 1); // STW r, *+A0[1] + } else if ((v->r & VT_VALMASK) == VT_LOCAL) { + // check case of storing to passed argument that + // tcc thinks is on the stack but for C67 is + // passed as a reg. However it may have been + // saved to the stack, if that reg was required + // for a call to a child function + + if (fc > 0) // argument ?? + { + // walk through sizes and figure which param + + int stack_pos = 8; + + for (t = 0; t < NoCallArgsPassedOnStack; t++) { + if (fc == stack_pos) + break; + + stack_pos += TranslateStackToReg[t]; + } + + // param has been pushed on stack, get it like a local var + fc = ParamLocOnStack[t] - 8; + } + + if (size == 8) + element = 4; + else + element = size; + + // divide offset in bytes to create word index + C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + + + + if (size == 1) + C67_STB_SP_A0(r); // STB r, SP[A0] + else if (size == 2) + C67_STH_SP_A0(r); // STH r, SP[A0] + else if (size == 4 || size == 8) + C67_STW_SP_A0(r); // STW r, SP[A0] + + if (size == 8) { + C67_ADDK(1, C67_A0); // ADDK 1,A0 + C67_STW_SP_A0(r + 1); // STW r, SP[A0] + } + } else { + if (size == 1) + C67_STB_PTR(r, fr); // STB r, *fr + else if (size == 2) + C67_STH_PTR(r, fr); // STH r, *fr + else if (size == 4 || size == 8) + C67_STW_PTR(r, fr); // STW r, *fr + + if (size == 8) { + C67_STW_PTR_PRE_INC(r + 1, fr, 1); // STW r, *+fr[1] + } + } + } +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + Sym *sym; + + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* constant case */ + if (vtop->r & VT_SYM) { + /* relocation case */ + + // get add into A0, then start the jump B3 + + greloc(cur_text_section, vtop->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, vtop->sym, ind + 4, R_C60HI16); + + C67_MVKL(C67_A0, 0); //r=reg to load, constant + C67_MVKH(C67_A0, 0); //r=reg to load, constant + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // B.S2x A0 + + if (is_jmp) { + C67_NOP(5); // simple jump, just put NOP + } else { + // Call, must load return address into B3 during delay slots + + sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address + greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + C67_MVKL(C67_B3, 0); //r=reg to load, constant + C67_MVKH(C67_B3, 0); //r=reg to load, constant + C67_NOP(3); // put remaining NOPs + } + } else { + /* put an empty PC32 relocation */ + ALWAYS_ASSERT(FALSE); + } + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + C67_IREG_B_REG(0, C67_CREG_ZERO, r); // B.S2x r + + if (is_jmp) { + C67_NOP(5); // simple jump, just put NOP + } else { + // Call, must load return address into B3 during delay slots + + sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address + greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + C67_MVKL(C67_B3, 0); //r=reg to load, constant + C67_MVKH(C67_B3, 0); //r=reg to load, constant + C67_NOP(3); // put remaining NOPs + } + } +} + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { + *ret_align = 1; // Never have to re-align return values for x86-64 + return 0; +} + +/* generate function call with address in (vtop->t, vtop->c) and free function + context. Stack entry is popped */ +void gfunc_call(int nb_args) +{ + int i, r, size = 0; + int args_sizes[NoCallArgsPassedOnStack]; + + if (nb_args > NoCallArgsPassedOnStack) { + tcc_error("more than 10 function params not currently supported"); + // handle more than 10, put some on the stack + } + + for (i = 0; i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + ALWAYS_ASSERT(FALSE); + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + + + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + tcc_error("long long not supported"); + } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + tcc_error("long double not supported"); + } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + size = 8; + } else { + size = 4; + } + + // put the parameter into the corresponding reg (pair) + + r = gv(RC_C67_A4 << (2 * i)); + + // must put on stack because with 1 pass compiler , no way to tell + // if an up coming nested call might overwrite these regs + + C67_PUSH(r); + + if (size == 8) { + C67_STW_PTR_PRE_INC(r + 1, C67_SP, 3); // STW r, *+SP[3] (go back and put the other) + } + args_sizes[i] = size; + } + vtop--; + } + // POP all the params on the stack into registers for the + // immediate call (in reverse order) + + for (i = nb_args - 1; i >= 0; i--) { + + if (args_sizes[i] == 8) + C67_POP_DW(TREG_C67_A4 + i * 2); + else + C67_POP(TREG_C67_A4 + i * 2); + } + gcall_or_jmp(0); + vtop--; +} + + +// to be compatible with Code Composer for the C67 +// the first 10 parameters must be passed in registers +// (pairs for 64 bits) starting wit; A4:A5, then B4:B5 and +// ending with B12:B13. +// +// When a call is made, if the caller has its parameters +// in regs A4-B13 these must be saved before/as the call +// parameters are loaded and restored upon return (or if/when needed). + +/* generate function prolog of type 't' */ +void gfunc_prolog(Sym *func_sym) +{ + CType *func_type = &func_sym->type; + int addr, align, size, func_call, i; + Sym *sym; + CType *type; + + sym = func_type->ref; + func_call = sym->f.func_call; + addr = 8; + /* if the function returns a structure, then add an + implicit pointer parameter */ + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + func_vc = addr; + addr += 4; + } + + NoOfCurFuncArgs = 0; + + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); + size = type_size(type, &align); + size = (size + 3) & ~3; + + // keep track of size of arguments so + // we can translate where tcc thinks they + // are on the stack into the appropriate reg + + TranslateStackToReg[NoOfCurFuncArgs] = size; + NoOfCurFuncArgs++; + +#ifdef FUNC_STRUCT_PARAM_AS_PTR + /* structs are passed as pointer */ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + size = 4; + } +#endif + addr += size; + } + func_ret_sub = 0; + /* pascal type call ? */ + if (func_call == FUNC_STDCALL) + func_ret_sub = addr - 8; + + C67_MV(C67_FP, C67_A0); // move FP -> A0 + C67_MV(C67_SP, C67_FP); // move SP -> FP + + // place all the args passed in regs onto the stack + + loc = 0; + for (i = 0; i < NoOfCurFuncArgs; i++) { + + ParamLocOnStack[i] = loc; // remember where the param is + loc += -8; + + C67_PUSH(TREG_C67_A4 + i * 2); + + if (TranslateStackToReg[i] == 8) { + C67_STW_PTR_PRE_INC(TREG_C67_A4 + i * 2 + 1, C67_SP, 3); // STW r, *+SP[1] (go back and put the other) + } + } + + TotalBytesPushedOnStack = -loc; + + func_sub_sp_offset = ind; // remember where we put the stack instruction + C67_ADDK(0, C67_SP); // ADDK.L2 loc,SP (just put zero temporarily) + + C67_PUSH(C67_A0); + C67_PUSH(C67_B3); +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + { + int local = (-loc + 7) & -8; // stack must stay aligned to 8 bytes for LDDW instr + C67_POP(C67_B3); + C67_NOP(4); // NOP wait for load + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_B3); // B.S2 B3 + C67_POP(C67_FP); + C67_ADDK(local, C67_SP); // ADDK.L2 loc,SP + C67_Adjust_ADDK((int *) (cur_text_section->data + + func_sub_sp_offset), + -local + TotalBytesPushedOnStack); + C67_NOP(3); // NOP + } +} + +ST_FUNC void gen_fill_nops(int bytes) +{ + if ((bytes & 3)) + tcc_error("alignment of code section not multiple of 4"); + while (bytes > 0) { + C67_NOP(4); + bytes -= 4; + } +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + int ind1 = ind; + if (nocode_wanted) + return t; + + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + return ind1; +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + Sym *sym; + // I guess this routine is used for relative short + // local jumps, for now just handle it as the general + // case + + // define a label that will be relocated + + sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0); + greloc(cur_text_section, sym, ind, R_C60LO16); + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + + gjmp(0); // place a zero there later the symbol will be added to it +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +ST_FUNC int gjmp_cond(int op, int t) +{ + int ind1; + int inv = op & 1; + if (nocode_wanted) + return t; + + /* fast case : can jump directly since flags are set */ + // C67 uses B2 sort of as flags register + ind1 = ind; + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + + if (C67_compare_reg != TREG_EAX && // check if not already in a conditional test reg + C67_compare_reg != TREG_EDX && + C67_compare_reg != TREG_ST0 && C67_compare_reg != C67_B2) { + C67_MV(C67_compare_reg, C67_B2); + C67_compare_reg = C67_B2; + } + + C67_IREG_B_REG(C67_invert_test ^ inv, C67_compare_reg, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + t = ind1; //return where we need to patch + + return t; +} + +ST_FUNC int gjmp_append(int n0, int t) +{ + if (n0) { + int n = n0, *p; + /* insert vtop->c jump list in t */ + + // I guess the idea is to traverse to the + // null at the end of the list and store t + // there + while (n != 0) { + p = (int *) (cur_text_section->data + n); + + // extract 32 bit address from MVKH/MVKL + n = ((*p >> 7) & 0xffff); + n |= ((*(p + 1) >> 7) & 0xffff) << 16; + } + *p |= (t & 0xffff) << 7; + *(p + 1) |= ((t >> 16) & 0xffff) << 7; + t = n0; + } + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int r, fr, opc, t; + + switch (op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + + +// C67 can't do const compares, must load into a reg +// so just go to gv2 directly - tktk + + + + if (op >= TOK_ULT && op <= TOK_GT) + gv2(RC_INT_BSIDE, RC_INT); // make sure r (src1) is on the B Side of CPU + else + gv2(RC_INT, RC_INT); + + r = vtop[-1].r; + fr = vtop[0].r; + + C67_compare_reg = C67_B2; + + + if (op == TOK_LT) { + C67_CMPLT(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_GE) { + C67_CMPLT(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_GT) { + C67_CMPGT(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_LE) { + C67_CMPGT(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_EQ) { + C67_CMPEQ(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_NE) { + C67_CMPEQ(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_ULT) { + C67_CMPLTU(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_UGE) { + C67_CMPLTU(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_UGT) { + C67_CMPGTU(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_ULE) { + C67_CMPGTU(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == '+') + C67_ADD(fr, r); // ADD r,fr,r + else if (op == '-') + C67_SUB(fr, r); // SUB r,fr,r + else if (op == '&') + C67_AND(fr, r); // AND r,fr,r + else if (op == '|') + C67_OR(fr, r); // OR r,fr,r + else if (op == '^') + C67_XOR(fr, r); // XOR r,fr,r + else + ALWAYS_ASSERT(FALSE); + + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) + vset_VT_CMP(0x80); + break; + case '-': + case TOK_SUBC1: /* sub with carry generation */ + opc = 5; + goto gen_op8; + case TOK_ADDC2: /* add with carry use */ + opc = 2; + goto gen_op8; + case TOK_SUBC2: /* sub with carry use */ + opc = 3; + goto gen_op8; + case '&': + opc = 4; + goto gen_op8; + case '^': + opc = 6; + goto gen_op8; + case '|': + opc = 1; + goto gen_op8; + case '*': + case TOK_UMULL: + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_MPYI(fr, r); // 32 bit multiply fr,r,fr + C67_NOP(8); // NOP 8 for worst case + break; + case TOK_SHL: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHL(fr, r); // arithmetic/logical shift + break; + + case TOK_SHR: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHRU(fr, r); // logical shift + break; + + case TOK_SAR: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHR(fr, r); // arithmetic shift + break; + + case '/': + t = TOK__divi; + call_func: + vswap(); + /* call generic idiv function */ + vpush_helper_func(t); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = VT_CONST; + break; + case TOK_UDIV: + case TOK_PDIV: + t = TOK__divu; + goto call_func; + case '%': + t = TOK__remi; + goto call_func; + case TOK_UMOD: + t = TOK__remu; + goto call_func; + + default: + opc = 7; + goto gen_op8; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranteed to have the same floating point type */ +/* XXX: need to use ST1 too */ +void gen_opf(int op) +{ + int ft, fc, fr, r; + + if (op >= TOK_ULT && op <= TOK_GT) + gv2(RC_EDX, RC_EAX); // make sure src2 is on b side + else + gv2(RC_FLOAT, RC_FLOAT); // make sure src2 is on b side + + ft = vtop->type.t; + fc = vtop->c.i; + r = vtop->r; + fr = vtop[-1].r; + + + if ((ft & VT_BTYPE) == VT_LDOUBLE) + tcc_error("long doubles not supported"); + + if (op >= TOK_ULT && op <= TOK_GT) { + + r = vtop[-1].r; + fr = vtop[0].r; + + C67_compare_reg = C67_B2; + + if (op == TOK_LT) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPLTDP(r, fr, C67_B2); + else + C67_CMPLTSP(r, fr, C67_B2); + + C67_invert_test = FALSE; + } else if (op == TOK_GE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPLTDP(r, fr, C67_B2); + else + C67_CMPLTSP(r, fr, C67_B2); + + C67_invert_test = TRUE; + } else if (op == TOK_GT) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPGTDP(r, fr, C67_B2); + else + C67_CMPGTSP(r, fr, C67_B2); + + C67_invert_test = FALSE; + } else if (op == TOK_LE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPGTDP(r, fr, C67_B2); + else + C67_CMPGTSP(r, fr, C67_B2); + + C67_invert_test = TRUE; + } else if (op == TOK_EQ) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPEQDP(r, fr, C67_B2); + else + C67_CMPEQSP(r, fr, C67_B2); + + C67_invert_test = FALSE; + } else if (op == TOK_NE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPEQDP(r, fr, C67_B2); + else + C67_CMPEQSP(r, fr, C67_B2); + + C67_invert_test = TRUE; + } else { + ALWAYS_ASSERT(FALSE); + } + vset_VT_CMP(0x80); + } else { + if (op == '+') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_ADDDP(r, fr); // ADD fr,r,fr + C67_NOP(6); + } else { + C67_ADDSP(r, fr); // ADD fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '-') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_SUBDP(r, fr); // SUB fr,r,fr + C67_NOP(6); + } else { + C67_SUBSP(r, fr); // SUB fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '*') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_MPYDP(r, fr); // MPY fr,r,fr + C67_NOP(9); + } else { + C67_MPYSP(r, fr); // MPY fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '/') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + // must call intrinsic DP floating point divide + vswap(); + /* call generic idiv function */ + vpush_helper_func(TOK__divd); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_FRET; + vtop->r2 = REG_IRE2; + + } else { + // must call intrinsic SP floating point divide + vswap(); + /* call generic idiv function */ + vpush_helper_func(TOK__divf); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_FRET; + vtop->r2 = VT_CONST; + } + } else + ALWAYS_ASSERT(FALSE); + + + } +} + + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + int r; + + gv(RC_INT); + r = vtop->r; + + if ((t & VT_BTYPE) == VT_DOUBLE) { + if (t & VT_UNSIGNED) + C67_INTDPU(r, r); + else + C67_INTDP(r, r); + + C67_NOP(4); + vtop->type.t = VT_DOUBLE; + } else { + if (t & VT_UNSIGNED) + C67_INTSPU(r, r); + else + C67_INTSP(r, r); + C67_NOP(3); + vtop->type.t = VT_FLOAT; + } + +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + int r; + + gv(RC_FLOAT); + r = vtop->r; + + if (t != VT_INT) + tcc_error("long long not supported"); + else { + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + C67_DPTRUNC(r, r); + C67_NOP(3); + } else { + C67_SPTRUNC(r, r); + C67_NOP(3); + } + + vtop->type.t = VT_INT; + + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + int r, r2; + + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE && + (t & VT_BTYPE) == VT_FLOAT) { + // convert double to float + + gv(RC_FLOAT); // get it in a register pair + + r = vtop->r; + + C67_DPSP(r, r); // convert it to SP same register + C67_NOP(3); + + vtop->type.t = VT_FLOAT; + vtop->r2 = VT_CONST; // set this as unused + } else if ((vtop->type.t & VT_BTYPE) == VT_FLOAT && + (t & VT_BTYPE) == VT_DOUBLE) { + // convert float to double + + gv(RC_FLOAT); // get it in a register + + r = vtop->r; + + if (r == TREG_EAX) { // make sure the paired reg is avail + r2 = get_reg(RC_ECX); + } else if (r == TREG_EDX) { + r2 = get_reg(RC_ST0); + } else { + ALWAYS_ASSERT(FALSE); + r2 = 0; /* avoid warning */ + } + + C67_SPDP(r, r); // convert it to DP same register + C67_NOP(1); + + vtop->type.t = VT_DOUBLE; + vtop->r2 = r2; // set this as unused + } else { + ALWAYS_ASSERT(FALSE); + } +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* Save the stack pointer onto the stack and return the location of its address */ +ST_FUNC void gen_vla_sp_save(int addr) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* Restore the SP from a location on the stack */ +ST_FUNC void gen_vla_sp_restore(int addr) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack */ +ST_FUNC void gen_vla_alloc(CType *type, int align) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* end of C67 code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/vendor/tcc/c67-link.c b/vendor/tcc/c67-link.c new file mode 100644 index 00000000..8e7a8b2f --- /dev/null +++ b/vendor/tcc/c67-link.c @@ -0,0 +1,125 @@ +#ifdef TARGET_DEFS_ONLY + +#define EM_TCC_TARGET EM_C60 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_C60_32 +#define R_DATA_PTR R_C60_32 +#define R_JMP_SLOT R_C60_JMP_SLOT +#define R_GLOB_DAT R_C60_GLOB_DAT +#define R_COPY R_C60_COPY +#define R_RELATIVE R_C60_RELATIVE + +#define R_NUM R_C60_NUM + +#define ELF_START_ADDR 0x00000400 +#define ELF_PAGE_SIZE 0x1000 + +#define PCRELATIVE_DLLPLT 0 +#define RELOCATE_DLLPLT 0 + +#else /* !TARGET_DEFS_ONLY */ + +#include "tcc.h" + +/* Returns 1 for a code relocation, 0 for a data relocation. For unknown + relocations, returns -1. */ +int code_reloc (int reloc_type) +{ + switch (reloc_type) { + case R_C60_32: + case R_C60LO16: + case R_C60HI16: + case R_C60_GOT32: + case R_C60_GOTOFF: + case R_C60_GOTPC: + case R_C60_COPY: + return 0; + + case R_C60_PLT32: + return 1; + } + return -1; +} + +/* Returns an enumerator to describe whether and when the relocation needs a + GOT and/or PLT entry to be created. See tcc.h for a description of the + different values. */ +int gotplt_entry_type (int reloc_type) +{ + switch (reloc_type) { + case R_C60_32: + case R_C60LO16: + case R_C60HI16: + case R_C60_COPY: + return NO_GOTPLT_ENTRY; + + case R_C60_GOTOFF: + case R_C60_GOTPC: + return BUILD_GOT_ONLY; + + case R_C60_PLT32: + case R_C60_GOT32: + return ALWAYS_GOTPLT_ENTRY; + } + return -1; +} + +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr) +{ + tcc_error_noabort("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(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) +{ + switch(type) { + case R_C60_32: + *(int *)ptr += val; + break; + case R_C60LO16: + { + uint32_t orig; + + /* put the low 16 bits of the absolute address add to what is + already there */ + orig = ((*(int *)(ptr )) >> 7) & 0xffff; + orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16; + + /* patch both at once - assumes always in pairs Low - High */ + *(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) | + (((val+orig) & 0xffff) << 7); + *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | + ((((val+orig)>>16) & 0xffff) << 7); + } + break; + case R_C60HI16: + break; + default: + fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n", + type, (unsigned) addr, ptr, (unsigned) val); + break; + } +} + +#endif /* !TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/conftest.c b/vendor/tcc/conftest.c new file mode 100644 index 00000000..a4450d51 --- /dev/null +++ b/vendor/tcc/conftest.c @@ -0,0 +1,308 @@ +/* ----------------------------------------------------------------------- */ +/* with -D C2STR: convert tccdefs.h to C-strings */ + +#if C2STR + +#include +#include + +/* replace native host macros by compile-time versions */ +const char *platform_macros[] = { + "__i386__", "TCC_TARGET_I386", + "__x86_64__", "TCC_TARGET_X86_64", + "_WIN32", "TCC_TARGET_PE", + "__arm__", "TCC_TARGET_ARM", + "__ARM_EABI__", "TCC_ARM_EABI", + "__aarch64__", "TCC_TARGET_ARM64", + "__riscv", "TCC_TARGET_RISCV64", + "__APPLE__", "TCC_TARGET_MACHO", + "__FreeBSD__", "TARGETOS_FreeBSD", + "__FreeBSD_kernel__", "TARGETOS_FreeBSD_kernel", + "__OpenBSD__", "TARGETOS_OpenBSD", + "__NetBSD__", "TARGETOS_NetBSD", + "__linux__", "TARGETOS_Linux", + "__ANDROID__", "TARGETOS_ANDROID", + + "__SIZEOF_POINTER__", "PTR_SIZE", + "__SIZEOF_LONG__", "LONG_SIZE", + 0 +}; + +int isid(int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') || c == '_'; +} + +int isspc(int c) +{ + return (unsigned char)c <= ' ' && c != 0; +} + +int main(int argc, char **argv) +{ + char l[1000], l2[1000], *p, *q, *p0; + FILE *fp, *op; + int c, e, f, s, cmt, cmt_n; + const char *r; + + if (argc < 3) + return 1; + + fp = fopen(argv[1], "rb"); + op = fopen(argv[2], "wb"); + if (!fp || !op) { + fprintf(stderr, "c2str: file error\n"); + return 1; + } + + cmt = cmt_n = 0; + for (;;) { + p = l; + append: + if (fgets(p, sizeof l - (p - l), fp)) { + p = strchr(p, 0); + while (p > l && isspc(p[-1])) + --p; + *p = 0; + } else if (p == l) + break; + + /* check for continuation */ + if (p > l && p[-1] == '\\') { + p[-1] = ' '; + goto append; + } + + /* count & skip leading spaces */ + p = l, q = l2, f = 0; + while (*p && isspc(*p)) + ++p, ++f; + + /* handle comments */ + if (p[0] == '/' && cmt == 0) { + if (p[1] == '*') + cmt = 2; + if (p[1] == '/') + cmt = 1; + } + if (cmt) { + fprintf(op, "%s", l); + if (++cmt_n == 1) + fprintf(op, " (converted, do not edit this file)"); + fprintf(op, "\n"); + if (cmt == 1) + cmt = 0; + if (cmt == 2) { + p = strchr(l, 0); + if (p >= l + 2 && p[-1] == '/' && p[-2] == '*') + cmt = 0; + } + continue; + } + + if (f < 4) { + do { + /* replace machine/os macros by compile-time counterparts */ + for (e = f = 0; (r = platform_macros[f]); f += 2) { + c = strlen(r); + /* remove 'defined' */ + //e = memcmp(p, "defined ", 8) ? 0 : 8; + if (0 == memcmp(p + e, r, c)) { + p += e + c; + q = strchr(strcpy(q, platform_macros[f + 1]), 0); + break; + } + + } + if (r) + continue; + } while (!!(*q++ = *p++)); + /* output as is */ + fprintf(op, "%s\n", l2); + continue; + + } else { + s = e = f = 0, p0 = p; + for (;;) { + c = *p++; + + if (isspc(c)) { + s = 1; + continue; + } + if (c == '/' && (p[0] == '/' || p[0] == '*')) + c = 0; /* trailing comment detected */ + else if (s && q > l2 + && ((isid(q[-1]) && isid(c)) + // keep space after macro name + || (q >= l2 + 2 + && l2[0] == '#' + && l2[1] == 'd' + && f < 2 && !e + ))) + *q++ = ' ', ++f; + s = 0; + + if (c == '(') + ++e; + if (c == ')') + --e; + if (c == '\\' || c == '\"') + *q++ = '\\'; + *q++ = c; + if (c == 0) + break; + p0 = p; + } + /* output with quotes */ + fprintf(op, " \"%s\\n\"%s\n", l2, p0); + } + } + + fclose(fp); + fclose(op); + return 0; +} + +/* ----------------------------------------------------------------------- */ +/* get some information from the host compiler for configure */ + +#elif 1 + +#include + +#if defined(_WIN32) +#include +#include +int _CRT_glob = 0; +#endif + +/* Define architecture */ +#if defined(__i386__) || defined _M_IX86 +# define TRIPLET_ARCH "i386" +#elif defined(__x86_64__) || defined _M_AMD64 +# define TRIPLET_ARCH "x86_64" +#elif defined(__arm__) +# define TRIPLET_ARCH "arm" +#elif defined(__aarch64__) +# define TRIPLET_ARCH "aarch64" +#elif defined(__riscv) && defined(__LP64__) +# define TRIPLET_ARCH "riscv64" +#else +# define TRIPLET_ARCH "unknown" +#endif + +/* Define OS */ +#if defined (__linux__) +# define TRIPLET_OS "linux" +#elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +# define TRIPLET_OS "kfreebsd" +#elif defined(__NetBSD__) +# define TRIPLET_OS "netbsd" +#elif defined(__OpenBSD__) +# define TRIPLET_OS "openbsd" +#elif defined(_WIN32) +# define TRIPLET_OS "win32" +#elif defined(__APPLE__) +# define TRIPLET_OS "darwin" +#elif !defined (__GNU__) +# define TRIPLET_OS "unknown" +#endif + +#if defined __ANDROID__ +# define ABI_PREFIX "android" +#else +# define ABI_PREFIX "gnu" +#endif + +/* Define calling convention and ABI */ +#if defined (__ARM_EABI__) +# if defined (__ARM_PCS_VFP) +# define TRIPLET_ABI ABI_PREFIX"eabihf" +# else +# define TRIPLET_ABI ABI_PREFIX"eabi" +# endif +#else +# define TRIPLET_ABI ABI_PREFIX +#endif + +#if defined _WIN32 +# define TRIPLET TRIPLET_ARCH "-" TRIPLET_OS +#elif defined __GNU__ +# define TRIPLET TRIPLET_ARCH "-" TRIPLET_ABI +#else +# define TRIPLET TRIPLET_ARCH "-" TRIPLET_OS "-" TRIPLET_ABI +#endif + +int main(int argc, char *argv[]) +{ +#if defined(_WIN32) + _setmode(_fileno(stdout), _O_BINARY); /* don't translate \n to \r\n */ +#endif + switch(argc == 2 ? argv[1][0] : 0) { + case 'b'://igendian + { + volatile unsigned foo = 0x01234567; + puts(*(unsigned char*)&foo == 0x67 ? "no" : "yes"); + break; + } +#if defined(__clang__) + case 'm'://inor + printf("%d\n", __clang_minor__); + break; + case 'v'://ersion + printf("%d\n", __clang_major__); + break; +#elif defined(__TINYC__) + case 'v'://ersion + puts("0"); + break; + case 'm'://inor + printf("%d\n", __TINYC__); + break; +#elif defined(_MSC_VER) + case 'v'://ersion + puts("0"); + break; + case 'm'://inor + printf("%d\n", _MSC_VER); + break; +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) + /* GNU comes last as other compilers may add 'GNU' compatibility */ + case 'm'://inor + printf("%d\n", __GNUC_MINOR__); + break; + case 'v'://ersion + printf("%d\n", __GNUC__); + break; +#else + case 'm'://inor + case 'v'://ersion + puts("0"); + break; +#endif + case 't'://riplet + puts(TRIPLET); + break; + case 'c'://ompiler +#if defined(__clang__) + puts("clang"); +#elif defined(__TINYC__) + puts("tcc"); +#elif defined(_MSC_VER) + puts("msvc"); +#elif defined(__GNUC__) + puts("gcc"); +#else + puts("unknown"); +#endif + break; + default: + break; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ +#endif diff --git a/vendor/tcc/dwarf.h b/vendor/tcc/dwarf.h new file mode 100644 index 00000000..c961bc36 --- /dev/null +++ b/vendor/tcc/dwarf.h @@ -0,0 +1,1046 @@ +/* This file defines standard DWARF types, structures, and macros. + Copyright (C) 2000-2011, 2014, 2016, 2017, 2018 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef _DWARF_H +#define _DWARF_H 1 + +/* DWARF Unit Header Types. */ +enum + { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff + }; + +/* DWARF tags. */ +enum + { + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + /* 0x06 reserved. */ + /* 0x07 reserved. */ + DW_TAG_imported_declaration = 0x08, + /* 0x09 reserved. */ + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + /* 0x0c reserved. */ + DW_TAG_member = 0x0d, + /* 0x0e reserved. */ + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + /* 0x14 reserved. */ + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + /* 0x3e reserved. Was DW_TAG_mutable_type. */ + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + + DW_TAG_lo_user = 0x4080, + + DW_TAG_MIPS_loop = 0x4081, + DW_TAG_format_label = 0x4101, + DW_TAG_function_template = 0x4102, + DW_TAG_class_template = 0x4103, + + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105, + + DW_TAG_GNU_template_template_param = 0x4106, + DW_TAG_GNU_template_parameter_pack = 0x4107, + DW_TAG_GNU_formal_parameter_pack = 0x4108, + DW_TAG_GNU_call_site = 0x4109, + DW_TAG_GNU_call_site_parameter = 0x410a, + + DW_TAG_hi_user = 0xffff + }; + + +/* Children determination encodings. */ +enum + { + DW_CHILDREN_no = 0, + DW_CHILDREN_yes = 1 + }; + + +/* DWARF attributes encodings. */ +enum + { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + /* 0x04 reserved. */ + /* 0x05 reserved. */ + /* 0x06 reserved. */ + /* 0x07 reserved. */ + /* 0x08 reserved. */ + DW_AT_ordering = 0x09, + /* 0x0a reserved. */ + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, /* Deprecated in DWARF4. */ + DW_AT_bit_size = 0x0d, + /* 0x0e reserved. */ + /* 0x0f reserved. */ + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + /* 0x14 reserved. */ + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + /* 0x1f reserved. */ + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + /* 0x23 reserved. */ + /* 0x24 reserved. */ + DW_AT_producer = 0x25, + /* 0x26 reserved. */ + DW_AT_prototyped = 0x27, + /* 0x28 reserved. */ + /* 0x29 reserved. */ + DW_AT_return_addr = 0x2a, + /* 0x2b reserved. */ + DW_AT_start_scope = 0x2c, + /* 0x2d reserved. */ + DW_AT_bit_stride = 0x2e, + DW_AT_upper_bound = 0x2f, + /* 0x30 reserved. */ + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, /* Deprecated in DWARF5. */ + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + /* 0x75 reserved. */ + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c, + + DW_AT_lo_user = 0x2000, + + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + DW_AT_MIPS_stride_byte = 0x200c, + DW_AT_MIPS_stride_elem = 0x200d, + DW_AT_MIPS_ptr_dopetype = 0x200e, + DW_AT_MIPS_allocatable_dopetype = 0x200f, + DW_AT_MIPS_assumed_shape_dopetype = 0x2010, + DW_AT_MIPS_assumed_size = 0x2011, + + /* GNU extensions. */ + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + DW_AT_GNU_guarded_by = 0x2108, + DW_AT_GNU_pt_guarded_by = 0x2109, + DW_AT_GNU_guarded = 0x210a, + DW_AT_GNU_pt_guarded = 0x210b, + DW_AT_GNU_locks_excluded = 0x210c, + DW_AT_GNU_exclusive_locks_required = 0x210d, + DW_AT_GNU_shared_locks_required = 0x210e, + DW_AT_GNU_odr_signature = 0x210f, + DW_AT_GNU_template_name = 0x2110, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_data_value = 0x2112, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_call_site_target_clobbered = 0x2114, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_tail_call_sites = 0x2116, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_GNU_all_source_call_sites = 0x2118, + DW_AT_GNU_locviews = 0x2137, + DW_AT_GNU_entry_view = 0x2138, + DW_AT_GNU_macros = 0x2119, + DW_AT_GNU_deleted = 0x211a, + /* GNU Debug Fission extensions. */ + DW_AT_GNU_dwo_name = 0x2130, + DW_AT_GNU_dwo_id = 0x2131, + DW_AT_GNU_ranges_base = 0x2132, + DW_AT_GNU_addr_base = 0x2133, + DW_AT_GNU_pubnames = 0x2134, + DW_AT_GNU_pubtypes = 0x2135, + + /* https://gcc.gnu.org/wiki/DW_AT_GNU_numerator_denominator */ + DW_AT_GNU_numerator = 0x2303, + DW_AT_GNU_denominator = 0x2304, + /* https://gcc.gnu.org/wiki/DW_AT_GNU_bias */ + DW_AT_GNU_bias = 0x2305, + + DW_AT_hi_user = 0x3fff + }; + +/* Old unofficially attribute names. Should not be used. + Will not appear in known-dwarf.h */ + +/* DWARF1 array subscripts and element data types. */ +#define DW_AT_subscr_data 0x0a +/* DWARF1 enumeration literals. */ +#define DW_AT_element_list 0x0f +/* DWARF1 reference for variable to member structure, class or union. */ +#define DW_AT_member 0x14 + +/* DWARF form encodings. */ +enum + { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, + + /* GNU Debug Fission extensions. */ + DW_FORM_GNU_addr_index = 0x1f01, + DW_FORM_GNU_str_index = 0x1f02, + + DW_FORM_GNU_ref_alt = 0x1f20, /* offset in alternate .debuginfo. */ + DW_FORM_GNU_strp_alt = 0x1f21 /* offset in alternate .debug_str. */ + }; + + +/* DWARF location operation encodings. */ +enum + { + DW_OP_addr = 0x03, /* Constant address. */ + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, /* Unsigned 1-byte constant. */ + DW_OP_const1s = 0x09, /* Signed 1-byte constant. */ + DW_OP_const2u = 0x0a, /* Unsigned 2-byte constant. */ + DW_OP_const2s = 0x0b, /* Signed 2-byte constant. */ + DW_OP_const4u = 0x0c, /* Unsigned 4-byte constant. */ + DW_OP_const4s = 0x0d, /* Signed 4-byte constant. */ + DW_OP_const8u = 0x0e, /* Unsigned 8-byte constant. */ + DW_OP_const8s = 0x0f, /* Signed 8-byte constant. */ + DW_OP_constu = 0x10, /* Unsigned LEB128 constant. */ + DW_OP_consts = 0x11, /* Signed LEB128 constant. */ + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, /* 1-byte stack index. */ + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, /* Unsigned LEB128 addend. */ + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, /* Signed 2-byte constant. */ + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, /* Signed 2-byte constant. */ + DW_OP_lit0 = 0x30, /* Literal 0. */ + DW_OP_lit1 = 0x31, /* Literal 1. */ + DW_OP_lit2 = 0x32, /* Literal 2. */ + DW_OP_lit3 = 0x33, /* Literal 3. */ + DW_OP_lit4 = 0x34, /* Literal 4. */ + DW_OP_lit5 = 0x35, /* Literal 5. */ + DW_OP_lit6 = 0x36, /* Literal 6. */ + DW_OP_lit7 = 0x37, /* Literal 7. */ + DW_OP_lit8 = 0x38, /* Literal 8. */ + DW_OP_lit9 = 0x39, /* Literal 9. */ + DW_OP_lit10 = 0x3a, /* Literal 10. */ + DW_OP_lit11 = 0x3b, /* Literal 11. */ + DW_OP_lit12 = 0x3c, /* Literal 12. */ + DW_OP_lit13 = 0x3d, /* Literal 13. */ + DW_OP_lit14 = 0x3e, /* Literal 14. */ + DW_OP_lit15 = 0x3f, /* Literal 15. */ + DW_OP_lit16 = 0x40, /* Literal 16. */ + DW_OP_lit17 = 0x41, /* Literal 17. */ + DW_OP_lit18 = 0x42, /* Literal 18. */ + DW_OP_lit19 = 0x43, /* Literal 19. */ + DW_OP_lit20 = 0x44, /* Literal 20. */ + DW_OP_lit21 = 0x45, /* Literal 21. */ + DW_OP_lit22 = 0x46, /* Literal 22. */ + DW_OP_lit23 = 0x47, /* Literal 23. */ + DW_OP_lit24 = 0x48, /* Literal 24. */ + DW_OP_lit25 = 0x49, /* Literal 25. */ + DW_OP_lit26 = 0x4a, /* Literal 26. */ + DW_OP_lit27 = 0x4b, /* Literal 27. */ + DW_OP_lit28 = 0x4c, /* Literal 28. */ + DW_OP_lit29 = 0x4d, /* Literal 29. */ + DW_OP_lit30 = 0x4e, /* Literal 30. */ + DW_OP_lit31 = 0x4f, /* Literal 31. */ + DW_OP_reg0 = 0x50, /* Register 0. */ + DW_OP_reg1 = 0x51, /* Register 1. */ + DW_OP_reg2 = 0x52, /* Register 2. */ + DW_OP_reg3 = 0x53, /* Register 3. */ + DW_OP_reg4 = 0x54, /* Register 4. */ + DW_OP_reg5 = 0x55, /* Register 5. */ + DW_OP_reg6 = 0x56, /* Register 6. */ + DW_OP_reg7 = 0x57, /* Register 7. */ + DW_OP_reg8 = 0x58, /* Register 8. */ + DW_OP_reg9 = 0x59, /* Register 9. */ + DW_OP_reg10 = 0x5a, /* Register 10. */ + DW_OP_reg11 = 0x5b, /* Register 11. */ + DW_OP_reg12 = 0x5c, /* Register 12. */ + DW_OP_reg13 = 0x5d, /* Register 13. */ + DW_OP_reg14 = 0x5e, /* Register 14. */ + DW_OP_reg15 = 0x5f, /* Register 15. */ + DW_OP_reg16 = 0x60, /* Register 16. */ + DW_OP_reg17 = 0x61, /* Register 17. */ + DW_OP_reg18 = 0x62, /* Register 18. */ + DW_OP_reg19 = 0x63, /* Register 19. */ + DW_OP_reg20 = 0x64, /* Register 20. */ + DW_OP_reg21 = 0x65, /* Register 21. */ + DW_OP_reg22 = 0x66, /* Register 22. */ + DW_OP_reg23 = 0x67, /* Register 24. */ + DW_OP_reg24 = 0x68, /* Register 24. */ + DW_OP_reg25 = 0x69, /* Register 25. */ + DW_OP_reg26 = 0x6a, /* Register 26. */ + DW_OP_reg27 = 0x6b, /* Register 27. */ + DW_OP_reg28 = 0x6c, /* Register 28. */ + DW_OP_reg29 = 0x6d, /* Register 29. */ + DW_OP_reg30 = 0x6e, /* Register 30. */ + DW_OP_reg31 = 0x6f, /* Register 31. */ + DW_OP_breg0 = 0x70, /* Base register 0. */ + DW_OP_breg1 = 0x71, /* Base register 1. */ + DW_OP_breg2 = 0x72, /* Base register 2. */ + DW_OP_breg3 = 0x73, /* Base register 3. */ + DW_OP_breg4 = 0x74, /* Base register 4. */ + DW_OP_breg5 = 0x75, /* Base register 5. */ + DW_OP_breg6 = 0x76, /* Base register 6. */ + DW_OP_breg7 = 0x77, /* Base register 7. */ + DW_OP_breg8 = 0x78, /* Base register 8. */ + DW_OP_breg9 = 0x79, /* Base register 9. */ + DW_OP_breg10 = 0x7a, /* Base register 10. */ + DW_OP_breg11 = 0x7b, /* Base register 11. */ + DW_OP_breg12 = 0x7c, /* Base register 12. */ + DW_OP_breg13 = 0x7d, /* Base register 13. */ + DW_OP_breg14 = 0x7e, /* Base register 14. */ + DW_OP_breg15 = 0x7f, /* Base register 15. */ + DW_OP_breg16 = 0x80, /* Base register 16. */ + DW_OP_breg17 = 0x81, /* Base register 17. */ + DW_OP_breg18 = 0x82, /* Base register 18. */ + DW_OP_breg19 = 0x83, /* Base register 19. */ + DW_OP_breg20 = 0x84, /* Base register 20. */ + DW_OP_breg21 = 0x85, /* Base register 21. */ + DW_OP_breg22 = 0x86, /* Base register 22. */ + DW_OP_breg23 = 0x87, /* Base register 23. */ + DW_OP_breg24 = 0x88, /* Base register 24. */ + DW_OP_breg25 = 0x89, /* Base register 25. */ + DW_OP_breg26 = 0x8a, /* Base register 26. */ + DW_OP_breg27 = 0x8b, /* Base register 27. */ + DW_OP_breg28 = 0x8c, /* Base register 28. */ + DW_OP_breg29 = 0x8d, /* Base register 29. */ + DW_OP_breg30 = 0x8e, /* Base register 30. */ + DW_OP_breg31 = 0x8f, /* Base register 31. */ + DW_OP_regx = 0x90, /* Unsigned LEB128 register. */ + DW_OP_fbreg = 0x91, /* Signed LEB128 offset. */ + DW_OP_bregx = 0x92, /* ULEB128 register followed by SLEB128 off. */ + DW_OP_piece = 0x93, /* ULEB128 size of piece addressed. */ + DW_OP_deref_size = 0x94, /* 1-byte size of data retrieved. */ + DW_OP_xderef_size = 0x95, /* 1-byte size of data retrieved. */ + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b,/* TLS offset to address in current thread */ + DW_OP_call_frame_cfa = 0x9c,/* CFA as determined by CFI. */ + DW_OP_bit_piece = 0x9d, /* ULEB128 size and ULEB128 offset in bits. */ + DW_OP_implicit_value = 0x9e, /* DW_FORM_block follows opcode. */ + DW_OP_stack_value = 0x9f, /* No operands, special like DW_OP_piece. */ + + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + + /* GNU extensions. */ + DW_OP_GNU_push_tls_address = 0xe0, + DW_OP_GNU_uninit = 0xf0, + DW_OP_GNU_encoded_addr = 0xf1, + DW_OP_GNU_implicit_pointer = 0xf2, + DW_OP_GNU_entry_value = 0xf3, + DW_OP_GNU_const_type = 0xf4, + DW_OP_GNU_regval_type = 0xf5, + DW_OP_GNU_deref_type = 0xf6, + DW_OP_GNU_convert = 0xf7, + DW_OP_GNU_reinterpret = 0xf9, + DW_OP_GNU_parameter_ref = 0xfa, + + /* GNU Debug Fission extensions. */ + DW_OP_GNU_addr_index = 0xfb, + DW_OP_GNU_const_index = 0xfc, + + DW_OP_GNU_variable_value = 0xfd, + + DW_OP_lo_user = 0xe0, /* Implementation-defined range start. */ + DW_OP_hi_user = 0xff /* Implementation-defined range end. */ + }; + + +/* DWARF base type encodings. */ +enum + { + DW_ATE_void = 0x0, + DW_ATE_address = 0x1, + DW_ATE_boolean = 0x2, + DW_ATE_complex_float = 0x3, + DW_ATE_float = 0x4, + DW_ATE_signed = 0x5, + DW_ATE_signed_char = 0x6, + DW_ATE_unsigned = 0x7, + DW_ATE_unsigned_char = 0x8, + DW_ATE_imaginary_float = 0x9, + DW_ATE_packed_decimal = 0xa, + DW_ATE_numeric_string = 0xb, + DW_ATE_edited = 0xc, + DW_ATE_signed_fixed = 0xd, + DW_ATE_unsigned_fixed = 0xe, + DW_ATE_decimal_float = 0xf, + DW_ATE_UTF = 0x10, + DW_ATE_UCS = 0x11, + DW_ATE_ASCII = 0x12, + + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff + }; + + +/* DWARF decimal sign encodings. */ +enum + { + DW_DS_unsigned = 1, + DW_DS_leading_overpunch = 2, + DW_DS_trailing_overpunch = 3, + DW_DS_leading_separate = 4, + DW_DS_trailing_separate = 5, + }; + + +/* DWARF endianity encodings. */ +enum + { + DW_END_default = 0, + DW_END_big = 1, + DW_END_little = 2, + + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff + }; + + +/* DWARF accessibility encodings. */ +enum + { + DW_ACCESS_public = 1, + DW_ACCESS_protected = 2, + DW_ACCESS_private = 3 + }; + + +/* DWARF visibility encodings. */ +enum + { + DW_VIS_local = 1, + DW_VIS_exported = 2, + DW_VIS_qualified = 3 + }; + + +/* DWARF virtuality encodings. */ +enum + { + DW_VIRTUALITY_none = 0, + DW_VIRTUALITY_virtual = 1, + DW_VIRTUALITY_pure_virtual = 2 + }; + + +/* DWARF language encodings. */ +enum + { + DW_LANG_C89 = 0x0001, /* ISO C:1989 */ + DW_LANG_C = 0x0002, /* C */ + DW_LANG_Ada83 = 0x0003, /* ISO Ada:1983 */ + DW_LANG_C_plus_plus = 0x0004, /* ISO C++:1998 */ + DW_LANG_Cobol74 = 0x0005, /* ISO Cobol:1974 */ + DW_LANG_Cobol85 = 0x0006, /* ISO Cobol:1985 */ + DW_LANG_Fortran77 = 0x0007, /* ISO FORTRAN 77 */ + DW_LANG_Fortran90 = 0x0008, /* ISO Fortran 90 */ + DW_LANG_Pascal83 = 0x0009, /* ISO Pascal:1983 */ + DW_LANG_Modula2 = 0x000a, /* ISO Modula-2:1996 */ + DW_LANG_Java = 0x000b, /* Java */ + DW_LANG_C99 = 0x000c, /* ISO C:1999 */ + DW_LANG_Ada95 = 0x000d, /* ISO Ada:1995 */ + DW_LANG_Fortran95 = 0x000e, /* ISO Fortran 95 */ + DW_LANG_PLI = 0x000f, /* ISO PL/1:1976 */ + DW_LANG_ObjC = 0x0010, /* Objective-C */ + DW_LANG_ObjC_plus_plus = 0x0011, /* Objective-C++ */ + DW_LANG_UPC = 0x0012, /* Unified Parallel C */ + DW_LANG_D = 0x0013, /* D */ + DW_LANG_Python = 0x0014, /* Python */ + DW_LANG_OpenCL = 0x0015, /* OpenCL */ + DW_LANG_Go = 0x0016, /* Go */ + DW_LANG_Modula3 = 0x0017, /* Modula-3 */ + DW_LANG_Haskell = 0x0018, /* Haskell */ + DW_LANG_C_plus_plus_03 = 0x0019, /* ISO C++:2003 */ + DW_LANG_C_plus_plus_11 = 0x001a, /* ISO C++:2011 */ + DW_LANG_OCaml = 0x001b, /* OCaml */ + DW_LANG_Rust = 0x001c, /* Rust */ + DW_LANG_C11 = 0x001d, /* ISO C:2011 */ + DW_LANG_Swift = 0x001e, /* Swift */ + DW_LANG_Julia = 0x001f, /* Julia */ + DW_LANG_Dylan = 0x0020, /* Dylan */ + DW_LANG_C_plus_plus_14 = 0x0021, /* ISO C++:2014 */ + DW_LANG_Fortran03 = 0x0022, /* ISO/IEC 1539-1:2004 */ + DW_LANG_Fortran08 = 0x0023, /* ISO/IEC 1539-1:2010 */ + DW_LANG_RenderScript = 0x0024, /* RenderScript Kernal Language */ + DW_LANG_BLISS = 0x0025, /* BLISS */ + + DW_LANG_lo_user = 0x8000, + DW_LANG_Mips_Assembler = 0x8001, /* Assembler */ + DW_LANG_hi_user = 0xffff + }; + +/* Old (typo) '1' != 'I'. */ +#define DW_LANG_PL1 DW_LANG_PLI + +/* DWARF identifier case encodings. */ +enum + { + DW_ID_case_sensitive = 0, + DW_ID_up_case = 1, + DW_ID_down_case = 2, + DW_ID_case_insensitive = 3 + }; + + +/* DWARF calling conventions encodings. + Used as values of DW_AT_calling_convention for subroutines + (normal, program or nocall) or structures, unions and class types + (normal, reference or value). */ +enum + { + DW_CC_normal = 0x1, + DW_CC_program = 0x2, + DW_CC_nocall = 0x3, + DW_CC_pass_by_reference = 0x4, + DW_CC_pass_by_value = 0x5, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff + }; + + +/* DWARF inline encodings. */ +enum + { + DW_INL_not_inlined = 0, + DW_INL_inlined = 1, + DW_INL_declared_not_inlined = 2, + DW_INL_declared_inlined = 3 + }; + + +/* DWARF ordering encodings. */ +enum + { + DW_ORD_row_major = 0, + DW_ORD_col_major = 1 + }; + + +/* DWARF discriminant descriptor encodings. */ +enum + { + DW_DSC_label = 0, + DW_DSC_range = 1 + }; + +/* DWARF defaulted member function encodings. */ +enum + { + DW_DEFAULTED_no = 0, + DW_DEFAULTED_in_class = 1, + DW_DEFAULTED_out_of_class = 2 + }; + +/* DWARF line content descriptions. */ +enum + { + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff + }; + +/* DWARF standard opcode encodings. */ +enum + { + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 + }; + + +/* DWARF extended opcode encodings. */ +enum + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3, + DW_LNE_set_discriminator = 4, + + DW_LNE_lo_user = 128, + + DW_LNE_NVIDIA_inlined_call = 144, + DW_LNE_NVIDIA_set_function_name = 145, + + DW_LNE_hi_user = 255 + }; + + +/* DWARF macinfo type encodings. */ +enum + { + DW_MACINFO_define = 1, + DW_MACINFO_undef = 2, + DW_MACINFO_start_file = 3, + DW_MACINFO_end_file = 4, + DW_MACINFO_vendor_ext = 255 + }; + + +/* DWARF debug_macro type encodings. */ +enum + { + DW_MACRO_define = 0x01, + DW_MACRO_undef = 0x02, + DW_MACRO_start_file = 0x03, + DW_MACRO_end_file = 0x04, + DW_MACRO_define_strp = 0x05, + DW_MACRO_undef_strp = 0x06, + DW_MACRO_import = 0x07, + DW_MACRO_define_sup = 0x08, + DW_MACRO_undef_sup = 0x09, + DW_MACRO_import_sup = 0x0a, + DW_MACRO_define_strx = 0x0b, + DW_MACRO_undef_strx = 0x0c, + DW_MACRO_lo_user = 0xe0, + DW_MACRO_hi_user = 0xff + }; + +/* Old GNU extension names for DWARF5 debug_macro type encodings. + There are no equivalents for the supplementary object file (sup) + and indirect string references (strx). */ +#define DW_MACRO_GNU_define DW_MACRO_define +#define DW_MACRO_GNU_undef DW_MACRO_undef +#define DW_MACRO_GNU_start_file DW_MACRO_start_file +#define DW_MACRO_GNU_end_file DW_MACRO_end_file +#define DW_MACRO_GNU_define_indirect DW_MACRO_define_strp +#define DW_MACRO_GNU_undef_indirect DW_MACRO_undef_strp +#define DW_MACRO_GNU_transparent_include DW_MACRO_import +#define DW_MACRO_GNU_lo_user DW_MACRO_lo_user +#define DW_MACRO_GNU_hi_user DW_MACRO_hi_user + + +/* Range list entry encoding. */ +enum + { + DW_RLE_end_of_list = 0x0, + DW_RLE_base_addressx = 0x1, + DW_RLE_startx_endx = 0x2, + DW_RLE_startx_length = 0x3, + DW_RLE_offset_pair = 0x4, + DW_RLE_base_address = 0x5, + DW_RLE_start_end = 0x6, + DW_RLE_start_length = 0x7 + }; + + +/* Location list entry encoding. */ +enum + { + DW_LLE_end_of_list = 0x0, + DW_LLE_base_addressx = 0x1, + DW_LLE_startx_endx = 0x2, + DW_LLE_startx_length = 0x3, + DW_LLE_offset_pair = 0x4, + DW_LLE_default_location = 0x5, + DW_LLE_base_address = 0x6, + DW_LLE_start_end = 0x7, + DW_LLE_start_length = 0x8 + }; + + +/* GNU DebugFission list entry encodings (.debug_loc.dwo). */ +enum + { + DW_LLE_GNU_end_of_list_entry = 0x0, + DW_LLE_GNU_base_address_selection_entry = 0x1, + DW_LLE_GNU_start_end_entry = 0x2, + DW_LLE_GNU_start_length_entry = 0x3 + }; + +/* DWARF5 package file section identifiers. */ +enum + { + DW_SECT_INFO = 1, + /* Reserved = 2, */ + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOCLISTS = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACRO = 7, + DW_SECT_RNGLISTS = 8, + }; + + +/* DWARF call frame instruction encodings. */ +enum + { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_extended = 0, + + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + DW_CFA_low_user = 0x1c, + DW_CFA_MIPS_advance_loc8 = 0x1d, + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_AARCH64_negate_ra_state = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f, + DW_CFA_high_user = 0x3f + }; + +/* ID indicating CIE as opposed to FDE in .debug_frame. */ +enum + { + DW_CIE_ID_32 = 0xffffffffU, /* In 32-bit format CIE header. */ + DW_CIE_ID_64 = 0xffffffffffffffffULL /* In 64-bit format CIE header. */ + }; + + +/* Information for GNU unwind information. */ +enum + { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_omit = 0xff, + + /* FDE data encoding. */ + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0a, + DW_EH_PE_sdata4 = 0x0b, + DW_EH_PE_sdata8 = 0x0c, + DW_EH_PE_signed = 0x08, + + /* FDE flags. */ + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + + DW_EH_PE_indirect = 0x80 + }; + + +/* DWARF XXX. */ +#define DW_ADDR_none 0 + +/* Section 7.2.2 of the DWARF3 specification defines a range of escape + codes that can appear in the length field of certain DWARF structures. + + These defines enumerate the minimum and maximum values of this range. + Currently only the maximum value is used (to indicate that 64-bit + values are going to be used in the dwarf data that accompanies the + structure). The other values are reserved. + + Note: There is a typo in DWARF3 spec (published Dec 20, 2005). In + sections 7.4, 7.5.1, 7.19, 7.20 the minimum escape code is referred to + as 0xffffff00 whereas in fact it should be 0xfffffff0. */ +#define DWARF3_LENGTH_MIN_ESCAPE_CODE 0xfffffff0u +#define DWARF3_LENGTH_MAX_ESCAPE_CODE 0xffffffffu +#define DWARF3_LENGTH_64_BIT DWARF3_LENGTH_MAX_ESCAPE_CODE + +#endif /* dwarf.h */ diff --git a/vendor/tcc/elf.h b/vendor/tcc/elf.h new file mode 100644 index 00000000..14cfdcff --- /dev/null +++ b/vendor/tcc/elf.h @@ -0,0 +1,3320 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _ELF_H +#define _ELF_H 1 + +#ifndef _WIN32 +#include +#else +#ifndef __int8_t_defined +#define __int8_t_defined +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif +#endif + +/* Standard ELF types. */ + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_OPENVMS 13 +#define ELFOSABI_NSK 14 /* Hewlett-Packard Non-Stop Kernel. */ +#define ELFOSABI_AROS 15 /* Amiga Research OS. */ +#define ELFOSABI_FENIXOS 16 /* FenixOS. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI. */ +#define ELFOSABI_C6000_LINUX 65 /* Linux TMS320C6000. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embedded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STMicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_AARCH64 183 /* ARM AARCH64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_RISCV 243 /* RISC-V */ +#define EM_NUM 253 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 +#define EM_C60 0x9c60 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indices */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ +#define DF_1_PIE 0x08000000 + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxiliary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; /* Entry type */ + union + { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +/* 38? */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +#define R_386_GOT32X 43 /* 32 bit GOT entry, relaxable */ +/* Keep this the last entry. */ +#define R_386_NUM 44 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low middle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +/* Keep this the last entry. */ +#define R_MIPS_NUM 128 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +#define DT_MIPS_NUM 0x35 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indices. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tentatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primarily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_NUM 1 + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_NUM 3 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step. */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD64 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL64 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL64 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_AARCH64_NUM 1033 + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_THM_JUMP24 30 +#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 /* Program base relative. */ +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 +#define R_ARM_MOVW_ABS_NC 43 +#define R_ARM_MOVT_ABS 44 +#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ +#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ +#define R_ARM_THM_MOVW_ABS_NC 47 +#define R_ARM_THM_MOVT_ABS 48 +/* Values from 49 to 89 are not yet used/handled by tcc. */ +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_GOT_PREL 96 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* TMS320C67xx specific declarations */ + +/* XXX: no ELF standard yet*/ + +/* TMS320C67xx relocs. */ +#define R_C60_32 1 +#define R_C60_GOT32 3 /* 32 bit GOT entry */ +#define R_C60_PLT32 4 /* 32 bit PLT address */ +#define R_C60_COPY 5 /* Copy symbol at runtime */ +#define R_C60_GLOB_DAT 6 /* Create GOT entry */ +#define R_C60_JMP_SLOT 7 /* Create PLT entry */ +#define R_C60_RELATIVE 8 /* Adjust by program base */ +#define R_C60_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_C60_GOTPC 10 /* 32 bit PC relative offset to GOT */ + +#define R_C60LO16 0x54 /* low 16 bit MVKL embedded */ +#define R_C60HI16 0x55 /* high 16 bit MVKH embedded */ +/* Keep this the last entry. */ +#define R_C60_NUM 0x56 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ +#define R_X86_64_GOTPCRELX 41 /* like GOTPCREL, but optionally with + linker optimizations */ +#define R_X86_64_REX_GOTPCRELX 42 /* like GOTPCRELX, but a REX prefix + is present */ + +#define R_X86_64_NUM 43 + +/* x86-64 sh_type values. */ +#define SHT_X86_64_UNWIND 0x70000001 /* Unwind information. */ + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ + +#define R_M32R_GOT24 48 /* 24 bit GOT entry */ +#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + +/* RISC-V ELF Flags */ +#define EF_RISCV_RVC 0x0001 +#define EF_RISCV_FLOAT_ABI 0x0006 +#define EF_RISCV_FLOAT_ABI_SOFT 0x0000 +#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002 +#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004 +#define EF_RISCV_FLOAT_ABI_QUAD 0x0006 + +/* RISC-V relocations. */ +#define R_RISCV_NONE 0 +#define R_RISCV_32 1 +#define R_RISCV_64 2 +#define R_RISCV_RELATIVE 3 +#define R_RISCV_COPY 4 +#define R_RISCV_JUMP_SLOT 5 +#define R_RISCV_TLS_DTPMOD32 6 +#define R_RISCV_TLS_DTPMOD64 7 +#define R_RISCV_TLS_DTPREL32 8 +#define R_RISCV_TLS_DTPREL64 9 +#define R_RISCV_TLS_TPREL32 10 +#define R_RISCV_TLS_TPREL64 11 +#define R_RISCV_BRANCH 16 +#define R_RISCV_JAL 17 +#define R_RISCV_CALL 18 +#define R_RISCV_CALL_PLT 19 +#define R_RISCV_GOT_HI20 20 +#define R_RISCV_TLS_GOT_HI20 21 +#define R_RISCV_TLS_GD_HI20 22 +#define R_RISCV_PCREL_HI20 23 +#define R_RISCV_PCREL_LO12_I 24 +#define R_RISCV_PCREL_LO12_S 25 +#define R_RISCV_HI20 26 +#define R_RISCV_LO12_I 27 +#define R_RISCV_LO12_S 28 +#define R_RISCV_TPREL_HI20 29 +#define R_RISCV_TPREL_LO12_I 30 +#define R_RISCV_TPREL_LO12_S 31 +#define R_RISCV_TPREL_ADD 32 +#define R_RISCV_ADD8 33 +#define R_RISCV_ADD16 34 +#define R_RISCV_ADD32 35 +#define R_RISCV_ADD64 36 +#define R_RISCV_SUB8 37 +#define R_RISCV_SUB16 38 +#define R_RISCV_SUB32 39 +#define R_RISCV_SUB64 40 +#define R_RISCV_GNU_VTINHERIT 41 +#define R_RISCV_GNU_VTENTRY 42 +#define R_RISCV_ALIGN 43 +#define R_RISCV_RVC_BRANCH 44 +#define R_RISCV_RVC_JUMP 45 +#define R_RISCV_RVC_LUI 46 +#define R_RISCV_GPREL_I 47 +#define R_RISCV_GPREL_S 48 +#define R_RISCV_TPREL_I 49 +#define R_RISCV_TPREL_S 50 +#define R_RISCV_RELAX 51 +#define R_RISCV_SUB6 52 +#define R_RISCV_SET6 53 +#define R_RISCV_SET8 54 +#define R_RISCV_SET16 55 +#define R_RISCV_SET32 56 +#define R_RISCV_32_PCREL 57 + +#define R_RISCV_NUM 58 + + +#endif /* elf.h */ diff --git a/vendor/tcc/i386-gen.c b/vendor/tcc/i386-gen.c new file mode 100644 index 00000000..7aebc5cd --- /dev/null +++ b/vendor/tcc/i386-gen.c @@ -0,0 +1,1141 @@ +/* + * X86 code generator for TCC + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TARGET_DEFS_ONLY + +/* number of available registers */ +#define NB_REGS 5 +#define NB_ASM_REGS 8 +#define CONFIG_TCC_ASM + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_EAX 0x0004 +#define RC_EDX 0x0008 +#define RC_ECX 0x0010 +#define RC_EBX 0x0020 +#define RC_ST0 0x0040 + +#define RC_IRET RC_EAX /* function return: integer register */ +#define RC_IRE2 RC_EDX /* function return: second integer register */ +#define RC_FRET RC_ST0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_EAX = 0, + TREG_ECX, + TREG_EDX, + TREG_EBX, + TREG_ST0, + TREG_ESP = 4 +}; + +/* 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) */ +#define REG_FRET TREG_ST0 /* float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +#define INVERT_FUNC_PARAMS + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +/* #define FUNC_STRUCT_PARAM_AS_PTR */ + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 12 +#define LDOUBLE_ALIGN 4 +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +/* define if return values need to be extended explicitely + at caller side (for interfacing with non-TCC compilers) */ +#define PROMOTE_RET + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#define USING_GLOBALS +#include "tcc.h" + +ST_DATA const char * const target_machine_defs = + "__i386__\0" + "__i386\0" + ; + +/* define to 1/0 to [not] have EBX as 4th register */ +#define USE_EBX 0 + +ST_DATA const int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_EAX, + /* ecx */ RC_INT | RC_ECX, + /* edx */ RC_INT | RC_EDX, + /* ebx */ (RC_INT | RC_EBX) * USE_EBX, + /* st0 */ RC_FLOAT | RC_ST0, +}; + +static unsigned long func_sub_sp_offset; +static int func_ret_sub; +#ifdef CONFIG_TCC_BCHECK +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +ST_DATA int func_bound_add_epilog; +static void gen_bounds_prolog(void); +static void gen_bounds_epilog(void); +#endif + +/* XXX: make it faster ? */ +ST_FUNC void g(int c) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +ST_FUNC void o(unsigned int c) +{ + while (c) { + g(c); + c = c >> 8; + } +} + +ST_FUNC void gen_le16(int v) +{ + g(v); + g(v >> 8); +} + +ST_FUNC void gen_le32(int c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); +} + +/* output a symbol and patch all calls to it */ +ST_FUNC void gsym_addr(int t, int a) +{ + while (t) { + unsigned char *ptr = cur_text_section->data + t; + uint32_t n = read32le(ptr); /* next value */ + write32le(ptr, a - t - 4); + t = n; + } +} + +/* instruction + 4 bytes data. Return the address of the data */ +static int oad(int c, int s) +{ + int t; + if (nocode_wanted) + return s; + o(c); + t = ind; + gen_le32(s); + return t; +} + +ST_FUNC void gen_fill_nops(int bytes) +{ + while (bytes--) + g(0x90); +} + +/* generate jmp to a label */ +#define gjmp2(instr,lbl) oad(instr,lbl) + +/* output constant with relocation if 'r & VT_SYM' is true */ +ST_FUNC void gen_addr32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_386_32); + gen_le32(c); +} + +ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_386_PC32); + gen_le32(c - 4); +} + +/* 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) +{ + op_reg = op_reg << 3; + if ((r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + o(0x05 | op_reg); + gen_addr32(r, sym, c); + } else if ((r & VT_VALMASK) == VT_LOCAL) { + /* currently, we use only ebp as base */ + if (c == (char)c) { + /* short reference */ + o(0x45 | op_reg); + g(c); + } else { + oad(0x85 | op_reg, c); + } + } else { + g(0x00 | op_reg | (r & VT_VALMASK)); + } +} + +/* load 'r' from value 'sv' */ +ST_FUNC void load(int r, SValue *sv) +{ + int v, t, ft, fc, fr; + SValue v1; + +#ifdef TCC_TARGET_PE + SValue v2; + sv = pe_getimport(sv, &v2); +#endif + + fr = sv->r; + ft = sv->type.t & ~VT_DEFSIGN; + fc = sv->c.i; + + ft &= ~(VT_VOLATILE | VT_CONSTANT); + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + if (v == VT_LLOCAL) { + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.i = fc; + v1.sym = NULL; + fr = r; + if (!(reg_classes[fr] & RC_INT)) + fr = get_reg(RC_INT); + load(fr, &v1); + } + if ((ft & VT_BTYPE) == VT_FLOAT) { + o(0xd9); /* flds */ + r = 0; + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0xdd); /* fldl */ + r = 0; + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xdb); /* fldt */ + r = 5; + } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { + o(0xbe0f); /* movsbl */ + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + o(0xb60f); /* movzbl */ + } else if ((ft & VT_TYPE) == VT_SHORT) { + o(0xbf0f); /* movswl */ + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + o(0xb70f); /* movzwl */ + } else { + o(0x8b); /* movl */ + } + gen_modrm(r, fr, sv->sym, fc); + } else { + if (v == VT_CONST) { + 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); + } else { + o(0x89); + o(0xe8 + r); /* mov %ebp, r */ + } + } else if (v == VT_CMP) { + o(0x0f); /* setxx %br */ + o(fc); + o(0xc0 + r); + o(0xc0b60f + r * 0x90000); /* movzbl %al, %eax */ + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + oad(0xb8 + r, t); /* mov $1, r */ + o(0x05eb); /* jmp after */ + gsym(fc); + oad(0xb8 + r, t ^ 1); /* mov $0, r */ + } else if (v != r) { + o(0x89); + o(0xc0 + r + v * 8); /* mov v, r */ + } + } +} + +/* store register 'r' in lvalue 'v' */ +ST_FUNC void store(int r, SValue *v) +{ + int fr, bt, ft, fc; + +#ifdef TCC_TARGET_PE + SValue v2; + v = pe_getimport(v, &v2); +#endif + + ft = v->type.t; + fc = v->c.i; + fr = v->r & VT_VALMASK; + ft &= ~(VT_VOLATILE | VT_CONSTANT); + bt = ft & VT_BTYPE; + /* XXX: incorrect if float reg to reg */ + if (bt == VT_FLOAT) { + o(0xd9); /* fsts */ + r = 2; + } else if (bt == VT_DOUBLE) { + o(0xdd); /* fstpl */ + r = 2; + } else if (bt == VT_LDOUBLE) { + o(0xc0d9); /* fld %st(0) */ + o(0xdb); /* fstpt */ + r = 7; + } else { + if (bt == VT_SHORT) + o(0x66); + if (bt == VT_BYTE || bt == VT_BOOL) + o(0x88); + else + o(0x89); + } + if (fr == VT_CONST || + fr == VT_LOCAL || + (v->r & VT_LVAL)) { + gen_modrm(r, v->r, v->sym, fc); + } else if (fr != r) { + o(0xc0 + fr + r * 8); /* mov r, fr */ + } +} + +static void gadd_sp(int val) +{ + if (val == (char)val) { + o(0xc483); + g(val); + } else { + oad(0xc481, val); /* add $xxx, %esp */ + } +} + +#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE +static void gen_static_call(int v) +{ + Sym *sym; + + sym = external_helper_sym(v); + oad(0xe8, -4); + greloc(cur_text_section, sym, ind-4, R_386_PC32); +} +#endif + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) { + /* constant and relocation case */ + greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32); + oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */ + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + o(0xff); /* call/jmp *r */ + o(0xd0 + r + (is_jmp << 4)); + } +} + +static const uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; +static const uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) +{ +#if defined(TCC_TARGET_PE) || TARGETOS_FreeBSD || TARGETOS_OpenBSD + int size, align, nregs; + *ret_align = 1; // Never have to re-align return values for x86 + *regsize = 4; + size = type_size(vt, &align); + if (size > 8 || (size & (size - 1))) + return 0; + nregs = 1; + if (size == 8) + ret->t = VT_INT, nregs = 2; + else if (size == 4) + ret->t = VT_INT; + else if (size == 2) + ret->t = VT_SHORT; + else + ret->t = VT_BYTE; + ret->ref = NULL; + return nregs; +#else + *ret_align = 1; // Never have to re-align return values for x86 + return 0; +#endif +} + +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ +ST_FUNC void gfunc_call(int nb_args) +{ + int size, align, r, args_size, i, func_call; + Sym *func_sym; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + + args_size = 0; + for(i = 0;i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + size = type_size(&vtop->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + /* allocate the necessary size on stack */ +#ifdef TCC_TARGET_PE + if (size >= 4096) { + r = get_reg(RC_EAX); + oad(0x68, size); // push size + /* cannot call normal 'alloca' with bound checking */ + gen_static_call(tok_alloc_const("__alloca")); + gadd_sp(4); + } else +#endif + { + oad(0xec81, size); /* sub $xxx, %esp */ + /* generate structure store */ + r = get_reg(RC_INT); + o(0xe089 + (r << 8)); /* mov %esp, r */ + } + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + args_size += size; + } else if (is_float(vtop->type.t)) { + gv(RC_FLOAT); /* only one float register */ + if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + size = 8; + else + size = 12; + oad(0xec81, size); /* sub $xxx, %esp */ + if (size == 12) + o(0x7cdb); + else + o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ + g(0x24); + g(0x00); + args_size += size; + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + r = gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + size = 8; + o(0x50 + vtop->r2); /* push r */ + } else { + size = 4; + } + o(0x50 + r); /* push r */ + args_size += size; + } + vtop--; + } + save_regs(0); /* save used temporary registers */ + func_sym = vtop->type.ref; + func_call = func_sym->f.func_call; + /* fast call case */ + if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) || + func_call == FUNC_FASTCALLW) { + int fastcall_nb_regs; + const uint8_t *fastcall_regs_ptr; + if (func_call == FUNC_FASTCALLW) { + fastcall_regs_ptr = fastcallw_regs; + fastcall_nb_regs = 2; + } else { + fastcall_regs_ptr = fastcall_regs; + fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; + } + for(i = 0;i < fastcall_nb_regs; i++) { + if (args_size <= 0) + break; + o(0x58 + fastcall_regs_ptr[i]); /* pop r */ + /* XXX: incorrect for struct/floats */ + args_size -= 4; + } + } +#if !defined(TCC_TARGET_PE) && !TARGETOS_FreeBSD || TARGETOS_OpenBSD + else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT) + args_size -= 4; +#endif + + gcall_or_jmp(0); + + if (args_size && func_call != FUNC_STDCALL && func_call != FUNC_FASTCALLW) + gadd_sp(args_size); + vtop--; +} + +#ifdef TCC_TARGET_PE +#define FUNC_PROLOG_SIZE (10 + USE_EBX) +#else +#define FUNC_PROLOG_SIZE (9 + USE_EBX) +#endif + +/* generate function prolog of type 't' */ +ST_FUNC void gfunc_prolog(Sym *func_sym) +{ + CType *func_type = &func_sym->type; + int addr, align, size, func_call, fastcall_nb_regs; + int param_index, param_addr; + const uint8_t *fastcall_regs_ptr; + Sym *sym; + CType *type; + + sym = func_type->ref; + func_call = sym->f.func_call; + addr = 8; + loc = 0; + func_vc = 0; + + if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { + fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; + fastcall_regs_ptr = fastcall_regs; + } else if (func_call == FUNC_FASTCALLW) { + fastcall_nb_regs = 2; + fastcall_regs_ptr = fastcallw_regs; + } else { + fastcall_nb_regs = 0; + fastcall_regs_ptr = NULL; + } + param_index = 0; + + ind += FUNC_PROLOG_SIZE; + func_sub_sp_offset = ind; + /* if the function returns a structure, then add an + implicit pointer parameter */ +#if defined(TCC_TARGET_PE) || TARGETOS_FreeBSD || TARGETOS_OpenBSD + size = type_size(&func_vt,&align); + if (((func_vt.t & VT_BTYPE) == VT_STRUCT) + && (size > 8 || (size & (size - 1)))) { +#else + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { +#endif + /* XXX: fastcall case ? */ + func_vc = addr; + addr += 4; + param_index++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) & ~3; +#ifdef FUNC_STRUCT_PARAM_AS_PTR + /* structs are passed as pointer */ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + size = 4; + } +#endif + 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); + param_addr = loc; + } else { + param_addr = addr; + addr += size; + } + sym_push(sym->v & ~SYM_FIELD, type, + VT_LOCAL | VT_LVAL, param_addr); + param_index++; + } + func_ret_sub = 0; + /* pascal type call or fastcall ? */ + if (func_call == FUNC_STDCALL || func_call == FUNC_FASTCALLW) + func_ret_sub = addr - 8; +#if !defined(TCC_TARGET_PE) && !TARGETOS_FreeBSD || TARGETOS_OpenBSD + else if (func_vc) + func_ret_sub = 4; +#endif + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif +} + +/* generate function epilog */ +ST_FUNC void gfunc_epilog(void) +{ + addr_t v, saved_ind; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif + + /* align local size to word & save local variables */ + v = (-loc + 3) & -4; + +#if USE_EBX + o(0x8b); + gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4)); +#endif + + o(0xc9); /* leave */ + if (func_ret_sub == 0) { + o(0xc3); /* ret */ + } else { + o(0xc2); /* ret n */ + g(func_ret_sub); + g(func_ret_sub >> 8); + } + saved_ind = ind; + ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; +#ifdef TCC_TARGET_PE + if (v >= 4096) { + oad(0xb8, v); /* mov stacksize, %eax */ + gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */ + } else +#endif + { + o(0xe58955); /* push %ebp, mov %esp, %ebp */ + o(0xec81); /* sub esp, stacksize */ + gen_le32(v); +#ifdef TCC_TARGET_PE + o(0x90); /* adjust to FUNC_PROLOG_SIZE */ +#endif + } + o(0x53 * USE_EBX); /* push ebx */ + ind = saved_ind; +} + +/* generate a jump to a label */ +ST_FUNC int gjmp(int t) +{ + return gjmp2(0xe9, t); +} + +/* generate a jump to a fixed address */ +ST_FUNC void gjmp_addr(int a) +{ + int r; + r = a - ind - 2; + if (r == (char)r) { + g(0xeb); + g(r); + } else { + oad(0xe9, a - ind - 5); + } +} + +#if 0 +/* generate a jump to a fixed address */ +ST_FUNC void gjmp_cond_addr(int a, int op) +{ + int r = a - ind - 2; + if (r == (char)r) + g(op - 32), g(r); + else + g(0x0f), gjmp2(op - 16, r - 4); +} +#endif + +ST_FUNC int gjmp_append(int n, int t) +{ + void *p; + /* insert vtop->c jump list in t */ + if (n) { + uint32_t n1 = n, n2; + while ((n2 = read32le(p = cur_text_section->data + n1))) + n1 = n2; + write32le(p, t); + t = n; + } + return t; +} + +ST_FUNC int gjmp_cond(int op, int t) +{ + g(0x0f); + t = gjmp2(op - 16, t); + return t; +} + +ST_FUNC void gen_opi(int op) +{ + int r, fr, opc, c; + + switch(op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); + c = vtop->c.i; + if (c == (char)c) { + /* generate inc and dec for smaller code */ + if ((c == 1 || c == -1) && (op == '+' || op == '-')) { + opc = (c == 1) ^ (op == '+'); + o (0x40 | (opc << 3) | r); // inc,dec + } else { + o(0x83); + o(0xc0 | (opc << 3) | r); + g(c); + } + } else { + o(0x81); + oad(0xc0 | (opc << 3) | r, c); + } + } else { + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + o((opc << 3) | 0x01); + o(0xc0 + r + fr * 8); + } + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) + vset_VT_CMP(op); + break; + case '-': + case TOK_SUBC1: /* sub with carry generation */ + opc = 5; + goto gen_op8; + case TOK_ADDC2: /* add with carry use */ + opc = 2; + goto gen_op8; + case TOK_SUBC2: /* sub with carry use */ + opc = 3; + goto gen_op8; + case '&': + opc = 4; + goto gen_op8; + case '^': + opc = 6; + goto gen_op8; + case '|': + opc = 1; + goto gen_op8; + case '*': + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + o(0xaf0f); /* imul fr, r */ + o(0xc0 + fr + r * 8); + break; + case TOK_SHL: + opc = 4; + goto gen_shift; + case TOK_SHR: + opc = 5; + goto gen_shift; + case TOK_SAR: + opc = 7; + gen_shift: + opc = 0xc0 | (opc << 3); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); + c = vtop->c.i & 0x1f; + o(0xc1); /* shl/shr/sar $xxx, r */ + o(opc | r); + g(c); + } else { + /* we generate the shift in ecx */ + gv2(RC_INT, RC_ECX); + r = vtop[-1].r; + o(0xd3); /* shl/shr/sar %cl, r */ + o(opc | r); + } + vtop--; + break; + case '/': + case TOK_UDIV: + case TOK_PDIV: + case '%': + case TOK_UMOD: + case TOK_UMULL: + /* first operand must be in eax */ + /* XXX: need better constraint for second operand */ + gv2(RC_EAX, RC_ECX); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + save_reg(TREG_EDX); + /* save EAX too if used otherwise */ + save_reg_upstack(TREG_EAX, 1); + if (op == TOK_UMULL) { + o(0xf7); /* mul fr */ + o(0xe0 + fr); + vtop->r2 = TREG_EDX; + r = TREG_EAX; + } else { + if (op == TOK_UDIV || op == TOK_UMOD) { + o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ + o(0xf0 + fr); + } else { + o(0xf799); /* cltd, idiv fr, %eax */ + o(0xf8 + fr); + } + if (op == '%' || op == TOK_UMOD) + r = TREG_EDX; + else + r = TREG_EAX; + } + vtop->r = r; + break; + default: + opc = 7; + goto gen_op8; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranteed to have the same floating point type */ +/* XXX: need to use ST1 too */ +ST_FUNC void gen_opf(int op) +{ + int a, ft, fc, swapped, r; + + if (op == TOK_NEG) { /* unary minus */ + gv(RC_FLOAT); + o(0xe0d9); /* fchs */ + return; + } + + /* convert constants to memory references */ + if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + vswap(); + gv(RC_FLOAT); + vswap(); + } + if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) + gv(RC_FLOAT); + + /* must put at least one value in the floating point register */ + if ((vtop[-1].r & VT_LVAL) && + (vtop[0].r & VT_LVAL)) { + vswap(); + gv(RC_FLOAT); + vswap(); + } + swapped = 0; + /* swap the stack if needed so that t1 is the register and t2 is + the memory reference */ + if (vtop[-1].r & VT_LVAL) { + vswap(); + swapped = 1; + } + if (op >= TOK_ULT && op <= TOK_GT) { + /* load on stack second operand */ + load(TREG_ST0, vtop); + save_reg(TREG_EAX); /* eax is used by FP comparison code */ + if (op == TOK_GE || op == TOK_GT) + swapped = !swapped; + else if (op == TOK_EQ || op == TOK_NE) + swapped = 0; + if (swapped) + o(0xc9d9); /* fxch %st(1) */ + if (op == TOK_EQ || op == TOK_NE) + o(0xe9da); /* fucompp */ + else + o(0xd9de); /* fcompp */ + o(0xe0df); /* fnstsw %ax */ + if (op == TOK_EQ) { + o(0x45e480); /* and $0x45, %ah */ + o(0x40fC80); /* cmp $0x40, %ah */ + } else if (op == TOK_NE) { + o(0x45e480); /* and $0x45, %ah */ + o(0x40f480); /* xor $0x40, %ah */ + op = TOK_NE; + } else if (op == TOK_GE || op == TOK_LE) { + o(0x05c4f6); /* test $0x05, %ah */ + op = TOK_EQ; + } else { + o(0x45c4f6); /* test $0x45, %ah */ + op = TOK_EQ; + } + vtop--; + vset_VT_CMP(op); + } else { + /* no memory reference possible for long double operations */ + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + load(TREG_ST0, vtop); + swapped = !swapped; + } + + switch(op) { + default: + case '+': + a = 0; + break; + case '-': + a = 4; + if (swapped) + a++; + break; + case '*': + a = 1; + break; + case '/': + a = 6; + if (swapped) + a++; + break; + } + ft = vtop->type.t; + fc = vtop->c.i; + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xde); /* fxxxp %st, %st(1) */ + o(0xc1 + (a << 3)); + } else { + /* if saved lvalue, then we must reload it */ + r = vtop->r; + if ((r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.i = fc; + v1.sym = NULL; + load(r, &v1); + fc = 0; + } + + if ((ft & VT_BTYPE) == VT_DOUBLE) + o(0xdc); + else + o(0xd8); + gen_modrm(a, r, vtop->sym, fc); + } + vtop--; + } +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +ST_FUNC void gen_cvt_itof(int t) +{ + save_reg(TREG_ST0); + gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + /* signed long long to float/double/long double (unsigned case + is handled generically) */ + o(0x50 + vtop->r2); /* push r2 */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%esp) */ + o(0x08c483); /* add $8, %esp */ + vtop->r2 = VT_CONST; + } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED)) { + /* unsigned int to float/double/long double */ + o(0x6a); /* push $0 */ + g(0x00); + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%esp) */ + o(0x08c483); /* add $8, %esp */ + } else { + /* int to float/double/long double */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x2404db); /* fildl (%esp) */ + o(0x04c483); /* add $4, %esp */ + } + vtop->r2 = VT_CONST; + vtop->r = TREG_ST0; +} + +/* convert fp to int 't' type */ +ST_FUNC void gen_cvt_ftoi(int t) +{ + int bt = vtop->type.t & VT_BTYPE; + if (bt == VT_FLOAT) + vpush_helper_func(TOK___fixsfdi); + else if (bt == VT_LDOUBLE) + vpush_helper_func(TOK___fixxfdi); + else + vpush_helper_func(TOK___fixdfdi); + vswap(); + gfunc_call(1); + vpushi(0); + vtop->r = REG_IRET; + if ((t & VT_BTYPE) == VT_LLONG) + vtop->r2 = REG_IRE2; +} + +/* convert from one floating point type to another */ +ST_FUNC void gen_cvt_ftof(int t) +{ + /* all we have to do on i386 is to put the float in a register */ + gv(RC_FLOAT); +} + +/* char/short to int conversion */ +ST_FUNC void gen_cvt_csti(int t) +{ + int r, sz, xl; + r = gv(RC_INT); + sz = !(t & VT_UNSIGNED); + xl = (t & VT_BTYPE) == VT_SHORT; + o(0xc0b60f /* mov[sz] %a[xl], %eax */ + | (sz << 3 | xl) << 8 + | (r << 3 | r) << 16 + ); +} + +/* 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); + o(1); + o(0x1583); /* addcl $0, xxx */ + greloc(cur_text_section, sv->sym, ind, R_386_32); + gen_le32(4); + g(0); +} + +/* computed goto support */ +ST_FUNC void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* bound check support functions */ +#ifdef CONFIG_TCC_BCHECK + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + oad(0xb8, 0); /* lbound section pointer */ + oad(0xb8, 0); /* call to function */ +} + +static void gen_bounds_epilog(void) +{ + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, PTR_SIZE); + + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + greloc(cur_text_section, sym_data, ind + 1, R_386_32); + ind = ind + 5; + gen_static_call(TOK___bound_local_new); + ind = saved_ind; + } + + /* generate bound check local freeing */ + o(0x5250); /* save returned value, if any */ + greloc(cur_text_section, sym_data, ind + 1, R_386_32); + oad(0xb8, 0); /* mov %eax, xxx */ + gen_static_call(TOK___bound_local_delete); + o(0x585a); /* restore returned value, if any */ +} +#endif + +/* 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); +} + +/* 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); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack */ +ST_FUNC void gen_vla_alloc(CType *type, int align) { + int use_call = 0; + +#if defined(CONFIG_TCC_BCHECK) + use_call = tcc_state->do_bounds_check; +#endif +#ifdef TCC_TARGET_PE /* alloca does more than just adjust %rsp on Windows */ + use_call = 1; +#endif + if (use_call) + { + vpush_helper_func(TOK_alloca); + vswap(); /* Move alloca ref past allocation size */ + gfunc_call(1); + } + else { + int r; + r = gv(RC_INT); /* allocation size */ + /* sub r,%rsp */ + o(0x2b); + o(0xe0 | r); + /* We align to 16 bytes rather than align */ + /* and ~15, %esp */ + o(0xf0e483); + vpop(); + } +} + +/* end of X86 code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/vendor/tcc/i386-link.c b/vendor/tcc/i386-link.c new file mode 100644 index 00000000..2fb14637 --- /dev/null +++ b/vendor/tcc/i386-link.c @@ -0,0 +1,325 @@ +#ifdef TARGET_DEFS_ONLY + +#define EM_TCC_TARGET EM_386 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_386_32 +#define R_DATA_PTR R_386_32 +#define R_JMP_SLOT R_386_JMP_SLOT +#define R_GLOB_DAT R_386_GLOB_DAT +#define R_COPY R_386_COPY +#define R_RELATIVE R_386_RELATIVE + +#define R_NUM R_386_NUM + +#define ELF_START_ADDR 0x08048000 +#define ELF_PAGE_SIZE 0x1000 + +#define PCRELATIVE_DLLPLT 0 +#define RELOCATE_DLLPLT 1 + +#else /* !TARGET_DEFS_ONLY */ + +#include "tcc.h" + +#ifdef NEED_RELOC_TYPE +/* Returns 1 for a code relocation, 0 for a data relocation. For unknown + relocations, returns -1. */ +int code_reloc (int reloc_type) +{ + switch (reloc_type) { + case R_386_RELATIVE: + case R_386_16: + case R_386_32: + case R_386_GOTPC: + case R_386_GOTOFF: + case R_386_GOT32: + case R_386_GOT32X: + case R_386_GLOB_DAT: + case R_386_COPY: + case R_386_TLS_GD: + case R_386_TLS_LDM: + case R_386_TLS_LDO_32: + case R_386_TLS_LE: + return 0; + + case R_386_PC16: + case R_386_PC32: + case R_386_PLT32: + case R_386_JMP_SLOT: + return 1; + } + return -1; +} + +/* Returns an enumerator to describe whether and when the relocation needs a + GOT and/or PLT entry to be created. See tcc.h for a description of the + different values. */ +int gotplt_entry_type (int reloc_type) +{ + switch (reloc_type) { + case R_386_RELATIVE: + case R_386_16: + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + case R_386_COPY: + return NO_GOTPLT_ENTRY; + + case R_386_32: + /* This relocations shouldn't normally need GOT or PLT + slots if it weren't for simplicity in the code generator. + See our caller for comments. */ + return AUTO_GOTPLT_ENTRY; + + case R_386_PC16: + case R_386_PC32: + return AUTO_GOTPLT_ENTRY; + + case R_386_GOTPC: + case R_386_GOTOFF: + return BUILD_GOT_ONLY; + + case R_386_GOT32: + case R_386_GOT32X: + case R_386_PLT32: + case R_386_TLS_GD: + case R_386_TLS_LDM: + case R_386_TLS_LDO_32: + case R_386_TLS_LE: + return ALWAYS_GOTPLT_ENTRY; + } + return -1; +} + +#ifdef NEED_BUILD_GOT +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_DYN) + modrm = 0xa3; + else + modrm = 0x25; + + /* empty PLT: create PLT0 entry that pushes the library identifier + (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->plt->reloc ? s1->plt->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 - 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 (!(s1->output_type & TCC_OUTPUT_DYN) && 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; + } + } + + if (s1->plt->reloc) { + ElfW_Rel *rel; + int x = s1->plt->sh_addr + 16 + 6; + p = s1->got->data; + for_each_elem(s1->plt->reloc, 0, rel, ElfW_Rel) { + write32le(p + rel->r_offset, x); + x += 16; + } + } +} +#endif +#endif + +void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) +{ + int sym_index, esym_index; + + sym_index = ELFW(R_SYM)(rel->r_info); + + switch (type) { + case R_386_32: + if (s1->output_type & TCC_OUTPUT_DYN) { + esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index; + qrel->r_offset = rel->r_offset; + if (esym_index) { + qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32); + qrel++; + return; + } else { + qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE); + qrel++; + } + } + add32le(ptr, val); + return; + case R_386_PC32: + if (s1->output_type == TCC_OUTPUT_DLL) { + /* DLL relocation */ + esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index; + if (esym_index) { + qrel->r_offset = rel->r_offset; + qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32); + qrel++; + return; + } + } + add32le(ptr, val - addr); + return; + case R_386_PLT32: + add32le(ptr, val - addr); + return; + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + write32le(ptr, val); + return; + case R_386_GOTPC: + add32le(ptr, s1->got->sh_addr - addr); + return; + case R_386_GOTOFF: + add32le(ptr, val - s1->got->sh_addr); + return; + case R_386_GOT32: + case R_386_GOT32X: + /* we load the got offset */ + add32le(ptr, get_sym_attr(s1, sym_index, 0)->got_offset); + return; + case R_386_16: + if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) { + output_file: + tcc_error_noabort("can only produce 16-bit binary files"); + } + write16le(ptr, read16le(ptr) + val); + return; + case R_386_PC16: + if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) + goto output_file; + write16le(ptr, read16le(ptr) + val - addr); + return; + case R_386_RELATIVE: +#ifdef TCC_TARGET_PE + add32le(ptr, val - s1->pe_imagebase); +#endif + /* do nothing */ + return; + case R_386_COPY: + /* This relocation must copy initialized data from the library + to the program .bss segment. Currently made like for ARM + (to remove noise of default case). Is this true? + */ + return; + case R_386_TLS_GD: + { + static const unsigned char expect[] = { + /* lea 0(,%ebx,1),%eax */ + 0x8d, 0x04, 0x1d, 0x00, 0x00, 0x00, 0x00, + /* call __tls_get_addr@PLT */ + 0xe8, 0xfc, 0xff, 0xff, 0xff }; + static const unsigned char replace[] = { + /* mov %gs:0,%eax */ + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, + /* sub 0,%eax */ + 0x81, 0xe8, 0x00, 0x00, 0x00, 0x00 }; + + if (memcmp (ptr-3, expect, sizeof(expect)) == 0) { + ElfW(Sym) *sym; + Section *sec; + int32_t x; + + memcpy(ptr-3, replace, sizeof(replace)); + rel[1].r_info = ELFW(R_INFO)(0, R_386_NONE); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + sec = s1->sections[sym->st_shndx]; + x = sym->st_value - sec->sh_addr - sec->data_offset; + add32le(ptr + 5, -x); + } + else + tcc_error_noabort("unexpected R_386_TLS_GD pattern"); + } + return; + case R_386_TLS_LDM: + { + static const unsigned char expect[] = { + /* lea 0(%ebx),%eax */ + 0x8d, 0x83, 0x00, 0x00, 0x00, 0x00, + /* call __tls_get_addr@PLT */ + 0xe8, 0xfc, 0xff, 0xff, 0xff }; + static const unsigned char replace[] = { + /* mov %gs:0,%eax */ + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, + /* nop */ + 0x90, + /* lea 0(%esi,%eiz,1),%esi */ + 0x8d, 0x74, 0x26, 0x00 }; + + if (memcmp (ptr-2, expect, sizeof(expect)) == 0) { + memcpy(ptr-2, replace, sizeof(replace)); + rel[1].r_info = ELFW(R_INFO)(0, R_386_NONE); + } + else + tcc_error_noabort("unexpected R_386_TLS_LDM pattern"); + } + return; + case R_386_TLS_LDO_32: + case R_386_TLS_LE: + { + ElfW(Sym) *sym; + Section *sec; + int32_t x; + + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + sec = s1->sections[sym->st_shndx]; + x = val - sec->sh_addr - sec->data_offset; + add32le(ptr, x); + } + return; + case R_386_NONE: + return; + default: + fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + return; + } +} + +#endif /* !TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/il-gen.c b/vendor/tcc/il-gen.c new file mode 100644 index 00000000..bb670ccb --- /dev/null +++ b/vendor/tcc/il-gen.c @@ -0,0 +1,657 @@ +/* + * CIL code generator for TCC + * + * Copyright (c) 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#error this code has bit-rotted since 2003 + +/* number of available registers */ +#define NB_REGS 3 + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_ST 0x0001 /* any stack entry */ +#define RC_ST0 0x0002 /* top of stack */ +#define RC_ST1 0x0004 /* top - 1 */ + +#define RC_INT RC_ST +#define RC_FLOAT RC_ST +#define RC_IRET RC_ST0 /* function return: integer register */ +#define RC_LRET RC_ST0 /* function return: second integer register */ +#define RC_FRET RC_ST0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + REG_ST0 = 0, + REG_ST1, + REG_ST2, +}; + +const int reg_classes[NB_REGS] = { + /* ST0 */ RC_ST | RC_ST0, + /* ST1 */ RC_ST | RC_ST1, + /* ST2 */ RC_ST, +}; + +/* return registers for function */ +#define REG_IRET REG_ST0 /* single word int return register */ +#define REG_LRET REG_ST0 /* second word return register (for long long) */ +#define REG_FRET REG_ST0 /* float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +/* #define INVERT_FUNC_PARAMS */ + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +/* #define FUNC_STRUCT_PARAM_AS_PTR */ + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 8 +#define LDOUBLE_ALIGN 8 + +/* function call context */ +typedef struct GFuncContext { + int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */ +} GFuncContext; + +/******************************************************/ +/* opcode definitions */ + +#define IL_OP_PREFIX 0xFE + +enum ILOPCodes { +#define OP(name, str, n) IL_OP_ ## name = n, +#include "il-opcodes.h" +#undef OP +}; + +char *il_opcodes_str[] = { +#define OP(name, str, n) [n] = str, +#include "il-opcodes.h" +#undef OP +}; + +/******************************************************/ + +/* arguments variable numbers start from there */ +#define ARG_BASE 0x70000000 + +static FILE *il_outfile; + +static void out_byte(int c) +{ + *(char *)ind++ = c; +} + +static void out_le32(int c) +{ + out_byte(c); + out_byte(c >> 8); + out_byte(c >> 16); + out_byte(c >> 24); +} + +static void init_outfile(void) +{ + if (!il_outfile) { + il_outfile = stdout; + fprintf(il_outfile, + ".assembly extern mscorlib\n" + "{\n" + ".ver 1:0:2411:0\n" + "}\n\n"); + } +} + +static void out_op1(int op) +{ + if (op & 0x100) + out_byte(IL_OP_PREFIX); + out_byte(op & 0xff); +} + +/* output an opcode with prefix */ +static void out_op(int op) +{ + out_op1(op); + fprintf(il_outfile, " %s\n", il_opcodes_str[op]); +} + +static void out_opb(int op, int c) +{ + out_op1(op); + out_byte(c); + fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c); +} + +static void out_opi(int op, int c) +{ + out_op1(op); + out_le32(c); + fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c); +} + +/* XXX: not complete */ +static void il_type_to_str(char *buf, int buf_size, + int t, const char *varstr) +{ + int bt; + Sym *s, *sa; + char buf1[256]; + const char *tstr; + + t = t & VT_TYPE; + bt = t & VT_BTYPE; + buf[0] = '\0'; + if (t & VT_UNSIGNED) + pstrcat(buf, buf_size, "unsigned "); + switch(bt) { + case VT_VOID: + tstr = "void"; + goto add_tstr; + case VT_BOOL: + tstr = "bool"; + goto add_tstr; + case VT_BYTE: + tstr = "int8"; + goto add_tstr; + case VT_SHORT: + tstr = "int16"; + goto add_tstr; + case VT_ENUM: + case VT_INT: + case VT_LONG: + tstr = "int32"; + goto add_tstr; + case VT_LLONG: + tstr = "int64"; + goto add_tstr; + case VT_FLOAT: + tstr = "float32"; + goto add_tstr; + case VT_DOUBLE: + case VT_LDOUBLE: + tstr = "float64"; + add_tstr: + pstrcat(buf, buf_size, tstr); + break; + case VT_STRUCT: + tcc_error("structures not handled yet"); + break; + case VT_FUNC: + s = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + il_type_to_str(buf, buf_size, s->t, varstr); + pstrcat(buf, buf_size, "("); + sa = s->next; + while (sa != NULL) { + il_type_to_str(buf1, sizeof(buf1), sa->t, NULL); + pstrcat(buf, buf_size, buf1); + sa = sa->next; + if (sa) + pstrcat(buf, buf_size, ", "); + } + pstrcat(buf, buf_size, ")"); + goto no_var; + case VT_PTR: + s = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + pstrcpy(buf1, sizeof(buf1), "*"); + if (varstr) + pstrcat(buf1, sizeof(buf1), varstr); + il_type_to_str(buf, buf_size, s->t, buf1); + goto no_var; + } + if (varstr) { + pstrcat(buf, buf_size, " "); + pstrcat(buf, buf_size, varstr); + } + no_var: ; +} + + +/* patch relocation entry with value 'val' */ +void greloc_patch1(Reloc *p, int val) +{ +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(t, a) +{ +} + +/* output jump and return symbol */ +static int out_opj(int op, int c) +{ + out_op1(op); + out_le32(0); + if (c == 0) { + c = ind - (int)cur_text_section->data; + } + fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c); + return c; +} + +void gsym(int t) +{ + fprintf(il_outfile, "L%d:\n", t); +} + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, fc, ft; + + v = sv->r & VT_VALMASK; + fc = sv->c.i; + ft = sv->t; + + if (sv->r & VT_LVAL) { + if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_LDARG_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_LDARG_S, fc); + } else { + out_opi(IL_OP_LDARG, fc); + } + } else { + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_LDLOC_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_LDLOC_S, fc); + } else { + out_opi(IL_OP_LDLOC, fc); + } + } + } else if (v == VT_CONST) { + /* XXX: handle globals */ + out_opi(IL_OP_LDSFLD, 0); + } else { + if ((ft & VT_BTYPE) == VT_FLOAT) { + out_op(IL_OP_LDIND_R4); + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + out_op(IL_OP_LDIND_R8); + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + out_op(IL_OP_LDIND_R8); + } else if ((ft & VT_TYPE) == VT_BYTE) + out_op(IL_OP_LDIND_I1); + else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) + out_op(IL_OP_LDIND_U1); + else if ((ft & VT_TYPE) == VT_SHORT) + out_op(IL_OP_LDIND_I2); + else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) + out_op(IL_OP_LDIND_U2); + else + out_op(IL_OP_LDIND_I4); + } + } else { + if (v == VT_CONST) { + /* XXX: handle globals */ + if (fc >= -1 && fc <= 8) { + out_op(IL_OP_LDC_I4_M1 + fc + 1); + } else { + out_opi(IL_OP_LDC_I4, fc); + } + } else if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + if (fc <= 0xff) { + out_opb(IL_OP_LDARGA_S, fc); + } else { + out_opi(IL_OP_LDARGA, fc); + } + } else { + if (fc <= 0xff) { + out_opb(IL_OP_LDLOCA_S, fc); + } else { + out_opi(IL_OP_LDLOCA, fc); + } + } + } else { + /* XXX: do it */ + } + } +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *sv) +{ + int v, fc, ft; + + v = sv->r & VT_VALMASK; + fc = sv->c.i; + ft = sv->t; + if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + /* XXX: check IL arg store semantics */ + if (fc <= 0xff) { + out_opb(IL_OP_STARG_S, fc); + } else { + out_opi(IL_OP_STARG, fc); + } + } else { + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_STLOC_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_STLOC_S, fc); + } else { + out_opi(IL_OP_STLOC, fc); + } + } + } else if (v == VT_CONST) { + /* XXX: handle globals */ + out_opi(IL_OP_STSFLD, 0); + } else { + if ((ft & VT_BTYPE) == VT_FLOAT) + out_op(IL_OP_STIND_R4); + else if ((ft & VT_BTYPE) == VT_DOUBLE) + out_op(IL_OP_STIND_R8); + else if ((ft & VT_BTYPE) == VT_LDOUBLE) + out_op(IL_OP_STIND_R8); + else if ((ft & VT_BTYPE) == VT_BYTE) + out_op(IL_OP_STIND_I1); + else if ((ft & VT_BTYPE) == VT_SHORT) + out_op(IL_OP_STIND_I2); + else + out_op(IL_OP_STIND_I4); + } +} + +/* start function call and return function call context */ +void gfunc_start(GFuncContext *c, int func_call) +{ + c->func_call = func_call; +} + +/* push function parameter which is in (vtop->t, vtop->c). Stack entry + is then popped. */ +void gfunc_param(GFuncContext *c) +{ + if ((vtop->t & VT_BTYPE) == VT_STRUCT) { + tcc_error("structures passed as value not handled yet"); + } else { + /* simply push on stack */ + gv(RC_ST0); + } + vtop--; +} + +/* generate function call with address in (vtop->t, vtop->c) and free function + context. Stack entry is popped */ +void gfunc_call(GFuncContext *c) +{ + char buf[1024]; + + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* XXX: more info needed from tcc */ + il_type_to_str(buf, sizeof(buf), vtop->t, "xxx"); + fprintf(il_outfile, " call %s\n", buf); + } else { + /* indirect call */ + gv(RC_INT); + il_type_to_str(buf, sizeof(buf), vtop->t, NULL); + fprintf(il_outfile, " calli %s\n", buf); + } + vtop--; +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(int t) +{ + int addr, u, func_call; + Sym *sym; + char buf[1024]; + + init_outfile(); + + /* XXX: pass function name to gfunc_prolog */ + il_type_to_str(buf, sizeof(buf), t, funcname); + fprintf(il_outfile, ".method static %s il managed\n", buf); + fprintf(il_outfile, "{\n"); + /* XXX: cannot do better now */ + fprintf(il_outfile, " .maxstack %d\n", NB_REGS); + fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n"); + + if (!strcmp(funcname, "main")) + fprintf(il_outfile, " .entrypoint\n"); + + sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + func_call = sym->r; + + addr = ARG_BASE; + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->t; + func_var = (sym->c == FUNC_ELLIPSIS); + if ((func_vt & VT_BTYPE) == VT_STRUCT) { + func_vc = addr; + addr++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + u = sym->t; + sym_push(sym->v & ~SYM_FIELD, u, + VT_LOCAL | lvalue_type(sym->type.t), addr); + addr++; + } +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + out_op(IL_OP_RET); + fprintf(il_outfile, "}\n\n"); +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + return out_opj(IL_OP_BR, t); +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + /* XXX: handle syms */ + out_opi(IL_OP_BR, a); +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, *p, c; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + c = vtop->c.i ^ inv; + switch(c) { + case TOK_EQ: + c = IL_OP_BEQ; + break; + case TOK_NE: + c = IL_OP_BNE_UN; + break; + case TOK_LT: + c = IL_OP_BLT; + break; + case TOK_LE: + c = IL_OP_BLE; + break; + case TOK_GT: + c = IL_OP_BGT; + break; + case TOK_GE: + c = IL_OP_BGE; + break; + case TOK_ULT: + c = IL_OP_BLT_UN; + break; + case TOK_ULE: + c = IL_OP_BLE_UN; + break; + case TOK_UGT: + c = IL_OP_BGT_UN; + break; + case TOK_UGE: + c = IL_OP_BGE_UN; + break; + } + t = out_opj(c, t); + } else if (v == VT_JMP || v == VT_JMPI) { + /* && or || optimization */ + if ((v & 1) == inv) { + /* insert vtop->c jump list in t */ + p = &vtop->c.i; + while (*p != 0) + p = (int *)*p; + *p = t; + t = vtop->c.i; + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + gv2(RC_ST1, RC_ST0); + switch(op) { + case '+': + out_op(IL_OP_ADD); + goto std_op; + case '-': + out_op(IL_OP_SUB); + goto std_op; + case '&': + out_op(IL_OP_AND); + goto std_op; + case '^': + out_op(IL_OP_XOR); + goto std_op; + case '|': + out_op(IL_OP_OR); + goto std_op; + case '*': + out_op(IL_OP_MUL); + goto std_op; + case TOK_SHL: + out_op(IL_OP_SHL); + goto std_op; + case TOK_SHR: + out_op(IL_OP_SHR_UN); + goto std_op; + case TOK_SAR: + out_op(IL_OP_SHR); + goto std_op; + case '/': + case TOK_PDIV: + out_op(IL_OP_DIV); + goto std_op; + case TOK_UDIV: + out_op(IL_OP_DIV_UN); + goto std_op; + case '%': + out_op(IL_OP_REM); + goto std_op; + case TOK_UMOD: + out_op(IL_OP_REM_UN); + std_op: + vtop--; + vtop[0].r = REG_ST0; + break; + case TOK_EQ: + case TOK_NE: + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + case TOK_ULT: + case TOK_ULE: + case TOK_UGT: + case TOK_UGE: + vtop--; + vtop[0].r = VT_CMP; + vtop[0].c.i = op; + break; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranteed to have the same floating point type */ +void gen_opf(int op) +{ + /* same as integer */ + gen_opi(op); +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + gv(RC_ST0); + if (t == VT_FLOAT) + out_op(IL_OP_CONV_R4); + else + out_op(IL_OP_CONV_R8); +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + gv(RC_ST0); + switch(t) { + case VT_INT | VT_UNSIGNED: + out_op(IL_OP_CONV_U4); + break; + case VT_LLONG: + out_op(IL_OP_CONV_I8); + break; + case VT_LLONG | VT_UNSIGNED: + out_op(IL_OP_CONV_U8); + break; + default: + out_op(IL_OP_CONV_I4); + break; + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + gv(RC_ST0); + if (t == VT_FLOAT) { + out_op(IL_OP_CONV_R4); + } else { + out_op(IL_OP_CONV_R8); + } +} + +/* end of CIL code generator */ +/*************************************************************/ + diff --git a/vendor/tcc/il-opcodes.h b/vendor/tcc/il-opcodes.h new file mode 100644 index 00000000..d53ffb2c --- /dev/null +++ b/vendor/tcc/il-opcodes.h @@ -0,0 +1,251 @@ +/* + * CIL opcode definition + * + * Copyright (c) 2002 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +OP(NOP, "nop", 0x00) +OP(BREAK, "break", 0x01) +OP(LDARG_0, "ldarg.0", 0x02) +OP(LDARG_1, "ldarg.1", 0x03) +OP(LDARG_2, "ldarg.2", 0x04) +OP(LDARG_3, "ldarg.3", 0x05) +OP(LDLOC_0, "ldloc.0", 0x06) +OP(LDLOC_1, "ldloc.1", 0x07) +OP(LDLOC_2, "ldloc.2", 0x08) +OP(LDLOC_3, "ldloc.3", 0x09) +OP(STLOC_0, "stloc.0", 0x0a) +OP(STLOC_1, "stloc.1", 0x0b) +OP(STLOC_2, "stloc.2", 0x0c) +OP(STLOC_3, "stloc.3", 0x0d) +OP(LDARG_S, "ldarg.s", 0x0e) +OP(LDARGA_S, "ldarga.s", 0x0f) +OP(STARG_S, "starg.s", 0x10) +OP(LDLOC_S, "ldloc.s", 0x11) +OP(LDLOCA_S, "ldloca.s", 0x12) +OP(STLOC_S, "stloc.s", 0x13) +OP(LDNULL, "ldnull", 0x14) +OP(LDC_I4_M1, "ldc.i4.m1", 0x15) +OP(LDC_I4_0, "ldc.i4.0", 0x16) +OP(LDC_I4_1, "ldc.i4.1", 0x17) +OP(LDC_I4_2, "ldc.i4.2", 0x18) +OP(LDC_I4_3, "ldc.i4.3", 0x19) +OP(LDC_I4_4, "ldc.i4.4", 0x1a) +OP(LDC_I4_5, "ldc.i4.5", 0x1b) +OP(LDC_I4_6, "ldc.i4.6", 0x1c) +OP(LDC_I4_7, "ldc.i4.7", 0x1d) +OP(LDC_I4_8, "ldc.i4.8", 0x1e) +OP(LDC_I4_S, "ldc.i4.s", 0x1f) +OP(LDC_I4, "ldc.i4", 0x20) +OP(LDC_I8, "ldc.i8", 0x21) +OP(LDC_R4, "ldc.r4", 0x22) +OP(LDC_R8, "ldc.r8", 0x23) +OP(LDPTR, "ldptr", 0x24) +OP(DUP, "dup", 0x25) +OP(POP, "pop", 0x26) +OP(JMP, "jmp", 0x27) +OP(CALL, "call", 0x28) +OP(CALLI, "calli", 0x29) +OP(RET, "ret", 0x2a) +OP(BR_S, "br.s", 0x2b) +OP(BRFALSE_S, "brfalse.s", 0x2c) +OP(BRTRUE_S, "brtrue.s", 0x2d) +OP(BEQ_S, "beq.s", 0x2e) +OP(BGE_S, "bge.s", 0x2f) +OP(BGT_S, "bgt.s", 0x30) +OP(BLE_S, "ble.s", 0x31) +OP(BLT_S, "blt.s", 0x32) +OP(BNE_UN_S, "bne.un.s", 0x33) +OP(BGE_UN_S, "bge.un.s", 0x34) +OP(BGT_UN_S, "bgt.un.s", 0x35) +OP(BLE_UN_S, "ble.un.s", 0x36) +OP(BLT_UN_S, "blt.un.s", 0x37) +OP(BR, "br", 0x38) +OP(BRFALSE, "brfalse", 0x39) +OP(BRTRUE, "brtrue", 0x3a) +OP(BEQ, "beq", 0x3b) +OP(BGE, "bge", 0x3c) +OP(BGT, "bgt", 0x3d) +OP(BLE, "ble", 0x3e) +OP(BLT, "blt", 0x3f) +OP(BNE_UN, "bne.un", 0x40) +OP(BGE_UN, "bge.un", 0x41) +OP(BGT_UN, "bgt.un", 0x42) +OP(BLE_UN, "ble.un", 0x43) +OP(BLT_UN, "blt.un", 0x44) +OP(SWITCH, "switch", 0x45) +OP(LDIND_I1, "ldind.i1", 0x46) +OP(LDIND_U1, "ldind.u1", 0x47) +OP(LDIND_I2, "ldind.i2", 0x48) +OP(LDIND_U2, "ldind.u2", 0x49) +OP(LDIND_I4, "ldind.i4", 0x4a) +OP(LDIND_U4, "ldind.u4", 0x4b) +OP(LDIND_I8, "ldind.i8", 0x4c) +OP(LDIND_I, "ldind.i", 0x4d) +OP(LDIND_R4, "ldind.r4", 0x4e) +OP(LDIND_R8, "ldind.r8", 0x4f) +OP(LDIND_REF, "ldind.ref", 0x50) +OP(STIND_REF, "stind.ref", 0x51) +OP(STIND_I1, "stind.i1", 0x52) +OP(STIND_I2, "stind.i2", 0x53) +OP(STIND_I4, "stind.i4", 0x54) +OP(STIND_I8, "stind.i8", 0x55) +OP(STIND_R4, "stind.r4", 0x56) +OP(STIND_R8, "stind.r8", 0x57) +OP(ADD, "add", 0x58) +OP(SUB, "sub", 0x59) +OP(MUL, "mul", 0x5a) +OP(DIV, "div", 0x5b) +OP(DIV_UN, "div.un", 0x5c) +OP(REM, "rem", 0x5d) +OP(REM_UN, "rem.un", 0x5e) +OP(AND, "and", 0x5f) +OP(OR, "or", 0x60) +OP(XOR, "xor", 0x61) +OP(SHL, "shl", 0x62) +OP(SHR, "shr", 0x63) +OP(SHR_UN, "shr.un", 0x64) +OP(NEG, "neg", 0x65) +OP(NOT, "not", 0x66) +OP(CONV_I1, "conv.i1", 0x67) +OP(CONV_I2, "conv.i2", 0x68) +OP(CONV_I4, "conv.i4", 0x69) +OP(CONV_I8, "conv.i8", 0x6a) +OP(CONV_R4, "conv.r4", 0x6b) +OP(CONV_R8, "conv.r8", 0x6c) +OP(CONV_U4, "conv.u4", 0x6d) +OP(CONV_U8, "conv.u8", 0x6e) +OP(CALLVIRT, "callvirt", 0x6f) +OP(CPOBJ, "cpobj", 0x70) +OP(LDOBJ, "ldobj", 0x71) +OP(LDSTR, "ldstr", 0x72) +OP(NEWOBJ, "newobj", 0x73) +OP(CASTCLASS, "castclass", 0x74) +OP(ISINST, "isinst", 0x75) +OP(CONV_R_UN, "conv.r.un", 0x76) +OP(ANN_DATA_S, "ann.data.s", 0x77) +OP(UNBOX, "unbox", 0x79) +OP(THROW, "throw", 0x7a) +OP(LDFLD, "ldfld", 0x7b) +OP(LDFLDA, "ldflda", 0x7c) +OP(STFLD, "stfld", 0x7d) +OP(LDSFLD, "ldsfld", 0x7e) +OP(LDSFLDA, "ldsflda", 0x7f) +OP(STSFLD, "stsfld", 0x80) +OP(STOBJ, "stobj", 0x81) +OP(CONV_OVF_I1_UN, "conv.ovf.i1.un", 0x82) +OP(CONV_OVF_I2_UN, "conv.ovf.i2.un", 0x83) +OP(CONV_OVF_I4_UN, "conv.ovf.i4.un", 0x84) +OP(CONV_OVF_I8_UN, "conv.ovf.i8.un", 0x85) +OP(CONV_OVF_U1_UN, "conv.ovf.u1.un", 0x86) +OP(CONV_OVF_U2_UN, "conv.ovf.u2.un", 0x87) +OP(CONV_OVF_U4_UN, "conv.ovf.u4.un", 0x88) +OP(CONV_OVF_U8_UN, "conv.ovf.u8.un", 0x89) +OP(CONV_OVF_I_UN, "conv.ovf.i.un", 0x8a) +OP(CONV_OVF_U_UN, "conv.ovf.u.un", 0x8b) +OP(BOX, "box", 0x8c) +OP(NEWARR, "newarr", 0x8d) +OP(LDLEN, "ldlen", 0x8e) +OP(LDELEMA, "ldelema", 0x8f) +OP(LDELEM_I1, "ldelem.i1", 0x90) +OP(LDELEM_U1, "ldelem.u1", 0x91) +OP(LDELEM_I2, "ldelem.i2", 0x92) +OP(LDELEM_U2, "ldelem.u2", 0x93) +OP(LDELEM_I4, "ldelem.i4", 0x94) +OP(LDELEM_U4, "ldelem.u4", 0x95) +OP(LDELEM_I8, "ldelem.i8", 0x96) +OP(LDELEM_I, "ldelem.i", 0x97) +OP(LDELEM_R4, "ldelem.r4", 0x98) +OP(LDELEM_R8, "ldelem.r8", 0x99) +OP(LDELEM_REF, "ldelem.ref", 0x9a) +OP(STELEM_I, "stelem.i", 0x9b) +OP(STELEM_I1, "stelem.i1", 0x9c) +OP(STELEM_I2, "stelem.i2", 0x9d) +OP(STELEM_I4, "stelem.i4", 0x9e) +OP(STELEM_I8, "stelem.i8", 0x9f) +OP(STELEM_R4, "stelem.r4", 0xa0) +OP(STELEM_R8, "stelem.r8", 0xa1) +OP(STELEM_REF, "stelem.ref", 0xa2) +OP(CONV_OVF_I1, "conv.ovf.i1", 0xb3) +OP(CONV_OVF_U1, "conv.ovf.u1", 0xb4) +OP(CONV_OVF_I2, "conv.ovf.i2", 0xb5) +OP(CONV_OVF_U2, "conv.ovf.u2", 0xb6) +OP(CONV_OVF_I4, "conv.ovf.i4", 0xb7) +OP(CONV_OVF_U4, "conv.ovf.u4", 0xb8) +OP(CONV_OVF_I8, "conv.ovf.i8", 0xb9) +OP(CONV_OVF_U8, "conv.ovf.u8", 0xba) +OP(REFANYVAL, "refanyval", 0xc2) +OP(CKFINITE, "ckfinite", 0xc3) +OP(MKREFANY, "mkrefany", 0xc6) +OP(ANN_CALL, "ann.call", 0xc7) +OP(ANN_CATCH, "ann.catch", 0xc8) +OP(ANN_DEAD, "ann.dead", 0xc9) +OP(ANN_HOISTED, "ann.hoisted", 0xca) +OP(ANN_HOISTED_CALL, "ann.hoisted.call", 0xcb) +OP(ANN_LAB, "ann.lab", 0xcc) +OP(ANN_DEF, "ann.def", 0xcd) +OP(ANN_REF_S, "ann.ref.s", 0xce) +OP(ANN_PHI, "ann.phi", 0xcf) +OP(LDTOKEN, "ldtoken", 0xd0) +OP(CONV_U2, "conv.u2", 0xd1) +OP(CONV_U1, "conv.u1", 0xd2) +OP(CONV_I, "conv.i", 0xd3) +OP(CONV_OVF_I, "conv.ovf.i", 0xd4) +OP(CONV_OVF_U, "conv.ovf.u", 0xd5) +OP(ADD_OVF, "add.ovf", 0xd6) +OP(ADD_OVF_UN, "add.ovf.un", 0xd7) +OP(MUL_OVF, "mul.ovf", 0xd8) +OP(MUL_OVF_UN, "mul.ovf.un", 0xd9) +OP(SUB_OVF, "sub.ovf", 0xda) +OP(SUB_OVF_UN, "sub.ovf.un", 0xdb) +OP(ENDFINALLY, "endfinally", 0xdc) +OP(LEAVE, "leave", 0xdd) +OP(LEAVE_S, "leave.s", 0xde) +OP(STIND_I, "stind.i", 0xdf) +OP(CONV_U, "conv.u", 0xe0) + +/* prefix instructions. we use an opcode >= 256 to ease coding */ + +OP(ARGLIST, "arglist", 0x100) +OP(CEQ, "ceq", 0x101) +OP(CGT, "cgt", 0x102) +OP(CGT_UN, "cgt.un", 0x103) +OP(CLT, "clt", 0x104) +OP(CLT_UN, "clt.un", 0x105) +OP(LDFTN, "ldftn", 0x106) +OP(LDVIRTFTN, "ldvirtftn", 0x107) +OP(JMPI, "jmpi", 0x108) +OP(LDARG, "ldarg", 0x109) +OP(LDARGA, "ldarga", 0x10a) +OP(STARG, "starg", 0x10b) +OP(LDLOC, "ldloc", 0x10c) +OP(LDLOCA, "ldloca", 0x10d) +OP(STLOC, "stloc", 0x10e) +OP(LOCALLOC, "localloc", 0x10f) +OP(ENDFILTER, "endfilter", 0x111) +OP(UNALIGNED, "unaligned", 0x112) +OP(VOLATILE, "volatile", 0x113) +OP(TAIL, "tail", 0x114) +OP(INITOBJ, "initobj", 0x115) +OP(ANN_LIVE, "ann.live", 0x116) +OP(CPBLK, "cpblk", 0x117) +OP(INITBLK, "initblk", 0x118) +OP(ANN_REF, "ann.ref", 0x119) +OP(RETHROW, "rethrow", 0x11a) +OP(SIZEOF, "sizeof", 0x11c) +OP(REFANYTYPE, "refanytype", 0x11d) +OP(ANN_DATA, "ann.data", 0x122) +OP(ANN_ARG, "ann.arg", 0x123) diff --git a/vendor/tcc/libtcc.c b/vendor/tcc/libtcc.c index 602bb0b2..71b8d36a 100644 --- a/vendor/tcc/libtcc.c +++ b/vendor/tcc/libtcc.c @@ -25,9 +25,39 @@ #include "tccasm.c" #include "tccelf.c" #include "tccrun.c" +#ifdef TCC_TARGET_I386 +#include "i386-gen.c" +#include "i386-link.c" +#include "i386-asm.c" +#elif defined(TCC_TARGET_ARM) +#include "arm-gen.c" +#include "arm-link.c" +#include "arm-asm.c" +#elif defined(TCC_TARGET_ARM64) +#include "arm64-gen.c" +#include "arm64-link.c" +#include "arm-asm.c" +#elif defined(TCC_TARGET_C67) +#include "c67-gen.c" +#include "c67-link.c" +#include "tcccoff.c" +#elif defined(TCC_TARGET_X86_64) #include "x86_64-gen.c" #include "x86_64-link.c" #include "i386-asm.c" +#elif defined(TCC_TARGET_RISCV64) +#include "riscv64-gen.c" +#include "riscv64-link.c" +#include "riscv64-asm.c" +#else +#error unknown target +#endif +#ifdef TCC_TARGET_PE +#include "tccpe.c" +#endif +#ifdef TCC_TARGET_MACHO +#include "tccmacho.c" +#endif #endif /* ONE_SOURCE */ #include "tcc.h" diff --git a/vendor/tcc/riscv64-asm.c b/vendor/tcc/riscv64-asm.c new file mode 100644 index 00000000..7f61d4cb --- /dev/null +++ b/vendor/tcc/riscv64-asm.c @@ -0,0 +1,1513 @@ +/*************************************************************/ +/* + * RISCV64 assembler for TCC + * + */ + +#ifdef TARGET_DEFS_ONLY + +#define CONFIG_TCC_ASM +#define NB_ASM_REGS 32 + +ST_FUNC void g(int c); +ST_FUNC void gen_le16(int c); +ST_FUNC void gen_le32(int c); + +/*************************************************************/ +#else +/*************************************************************/ +#define USING_GLOBALS +#include "tcc.h" + +enum { + OPT_REG, + OPT_IM12S, + OPT_IM32, +}; +#define C_ENCODE_RS1(register_index) ((register_index) << 7) +#define C_ENCODE_RS2(register_index) ((register_index) << 2) +#define ENCODE_RD(register_index) ((register_index) << 7) +#define ENCODE_RS1(register_index) ((register_index) << 15) +#define ENCODE_RS2(register_index) ((register_index) << 20) +#define NTH_BIT(b, n) ((b >> n) & 1) +#define OP_IM12S (1 << OPT_IM12S) +#define OP_IM32 (1 << OPT_IM32) +#define OP_REG (1 << OPT_REG) + +typedef struct Operand { + uint32_t type; + union { + uint8_t reg; + uint16_t regset; + ExprValue e; + }; +} Operand; + +static void asm_binary_opcode(TCCState* s1, int token); +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str); +ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg); +static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm); +static void asm_emit_i(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2); +static void asm_emit_j(int token, uint32_t opcode, const Operand *rd, const Operand *rs2); +static void asm_emit_opcode(uint32_t opcode); +static void asm_emit_r(int token, uint32_t opcode, const Operand *rd, const Operand *rs1, const Operand *rs2); +static void asm_emit_s(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm); +static void asm_emit_u(int token, uint32_t opcode, const Operand *rd, const Operand *rs2); +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg); +static void asm_nullary_opcode(TCCState *s1, int token); +ST_FUNC void asm_opcode(TCCState *s1, int token); +static int asm_parse_csrvar(int t); +ST_FUNC int asm_parse_regvar(int t); +static void asm_ternary_opcode(TCCState *s1, int token); +static void asm_unary_opcode(TCCState *s1, int token); +ST_FUNC void gen_expr32(ExprValue *pe); +static void parse_operand(TCCState *s1, Operand *op); +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier); +/* C extension */ +static void asm_emit_ca(int token, uint16_t opcode, const Operand *rd, const Operand *rs2); +static void asm_emit_cb(int token, uint16_t opcode, const Operand *rs1, const Operand *imm); +static void asm_emit_ci(int token, uint16_t opcode, const Operand *rd, const Operand *imm); +static void asm_emit_ciw(int token, uint16_t opcode, const Operand *rd, const Operand *imm); +static void asm_emit_cj(int token, uint16_t opcode, const Operand *imm); +static void asm_emit_cl(int token, uint16_t opcode, const Operand *rd, const Operand *rs1, const Operand *imm); +static void asm_emit_cr(int token, uint16_t opcode, const Operand *rd, const Operand *rs2); +static void asm_emit_cs(int token, uint16_t opcode, const Operand *rs2, const Operand *rs1, const Operand *imm); +static void asm_emit_css(int token, uint16_t opcode, const Operand *rs2, const Operand *imm); + +/* XXX: make it faster ? */ +ST_FUNC void g(int c) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +ST_FUNC void gen_le16 (int i) +{ + g(i); + g(i>>8); +} + +ST_FUNC void gen_le32 (int i) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 4; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind++] = i & 0xFF; + cur_text_section->data[ind++] = (i >> 8) & 0xFF; + cur_text_section->data[ind++] = (i >> 16) & 0xFF; + cur_text_section->data[ind++] = (i >> 24) & 0xFF; +} + +ST_FUNC void gen_expr32(ExprValue *pe) +{ + gen_le32(pe->v); +} + +static void asm_emit_opcode(uint32_t opcode) { + gen_le32(opcode); +} + +static void asm_nullary_opcode(TCCState *s1, int token) +{ + static const Operand nil = {.type = OP_REG}; + static const Operand zimm = {.type = OP_IM12S}; + + switch (token) { + // Sync instructions + + case TOK_ASM_fence: // I + asm_emit_opcode((0x3 << 2) | 3 | (0 << 12)); + return; + case TOK_ASM_fence_i: // I + asm_emit_opcode((0x3 << 2) | 3| (1 << 12)); + return; + + // System calls + + case TOK_ASM_ecall: // I (pseudo) + asm_emit_opcode((0x1C << 2) | 3 | (0 << 12)); + return; + case TOK_ASM_ebreak: // I (pseudo) + asm_emit_opcode((0x1C << 2) | 3 | (0 << 12) | (1 << 20)); + return; + + // Other + + case TOK_ASM_nop: + asm_emit_i(token, (4 << 2) | 3, &nil, &nil, &zimm); + return; + + case TOK_ASM_wfi: + asm_emit_opcode((0x1C << 2) | 3 | (0x105 << 20)); + return; + + /* C extension */ + case TOK_ASM_c_ebreak: + asm_emit_cr(token, 2 | (9 << 12), &nil, &nil); + return; + case TOK_ASM_c_nop: + asm_emit_ci(token, 1, &nil, &zimm); + return; + + default: + expect("nullary instruction"); + } +} + +/* Parse a text containing operand and store the result in OP */ +static void parse_operand(TCCState *s1, Operand *op) +{ + ExprValue e = {0}; + Sym label = {0}; + int8_t reg; + + op->type = 0; + + if ((reg = asm_parse_regvar(tok)) != -1) { + next(); // skip register name + op->type = OP_REG; + op->reg = (uint8_t) reg; + return; + } else if (tok == '$') { + /* constant value */ + next(); // skip '#' or '$' + } else if ((e.v = asm_parse_csrvar(tok)) != -1) { + next(); + } else { + asm_expr(s1, &e); + } + op->type = OP_IM32; + op->e = e; + /* compare against unsigned 12-bit maximum */ + if (!op->e.sym) { + if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000) + op->type = OP_IM12S; + } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) { + label.type.t = VT_VOID | VT_STATIC; + + /* use the medium PIC model: GOT, auipc, lw */ + if (op->e.sym->type.t & VT_STATIC) + greloca(cur_text_section, op->e.sym, ind, R_RISCV_PCREL_HI20, 0); + else + greloca(cur_text_section, op->e.sym, ind, R_RISCV_GOT_HI20, 0); + put_extern_sym(&label, cur_text_section, ind, 0); + greloca(cur_text_section, &label, ind+4, R_RISCV_PCREL_LO12_I, 0); + + op->type = OP_IM12S; + op->e.v = 0; + } else { + expect("operand"); + } +} + +static void asm_unary_opcode(TCCState *s1, int token) +{ + uint32_t opcode = (0x1C << 2) | 3 | (2 << 12); + Operand op; + static const Operand nil = {.type = OP_REG}; + + parse_operand(s1, &op); + /* Note: Those all map to CSR--so they are pseudo-instructions. */ + opcode |= ENCODE_RD(op.reg); + + switch (token) { + /* pseudoinstructions */ + case TOK_ASM_rdcycle: + asm_emit_opcode(opcode | (0xC00 << 20)); + return; + case TOK_ASM_rdcycleh: + asm_emit_opcode(opcode | (0xC80 << 20)); + return; + case TOK_ASM_rdtime: + asm_emit_opcode(opcode | (0xC01 << 20) | ENCODE_RD(op.reg)); + return; + case TOK_ASM_rdtimeh: + asm_emit_opcode(opcode | (0xC81 << 20) | ENCODE_RD(op.reg)); + return; + case TOK_ASM_rdinstret: + asm_emit_opcode(opcode | (0xC02 << 20) | ENCODE_RD(op.reg)); + return; + case TOK_ASM_rdinstreth: + asm_emit_opcode(opcode | (0xC82 << 20) | ENCODE_RD(op.reg)); + return; + /* C extension */ + case TOK_ASM_c_j: + asm_emit_cj(token, 1 | (5 << 13), &op); + return; + case TOK_ASM_c_jal: /* RV32C-only */ + asm_emit_cj(token, 1 | (1 << 13), &op); + return; + case TOK_ASM_c_jalr: + asm_emit_cr(token, 2 | (9 << 12), &op, &nil); + return; + case TOK_ASM_c_jr: + asm_emit_cr(token, 2 | (8 << 12), &op, &nil); + return; + default: + expect("unary instruction"); + } +} + +static void asm_emit_u(int token, uint32_t opcode, const Operand* rd, const Operand* rs2) +{ + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs2->type != OP_IM12S && rs2->type != OP_IM32) { + tcc_error("'%s': Expected second source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } else if (rs2->e.v >= 0x100000) { + tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 0xfffff", get_tok_str(token, NULL)); + return; + } + /* U-type instruction: + 31...12 imm[31:12] + 11...7 rd + 6...0 opcode */ + gen_le32(opcode | ENCODE_RD(rd->reg) | (rs2->e.v << 12)); +} + +static void asm_binary_opcode(TCCState* s1, int token) +{ + Operand ops[2]; + parse_operand(s1, &ops[0]); + if (tok == ',') + next(); + else + expect("','"); + parse_operand(s1, &ops[1]); + + switch (token) { + case TOK_ASM_lui: + asm_emit_u(token, (0xD << 2) | 3, &ops[0], &ops[1]); + return; + case TOK_ASM_auipc: + asm_emit_u(token, (0x05 << 2) | 3, &ops[0], &ops[1]); + return; + case TOK_ASM_jal: + asm_emit_j(token, 0x6f, ops, ops + 1); + return; + + /* C extension */ + case TOK_ASM_c_add: + asm_emit_cr(token, 2 | (9 << 12), ops, ops + 1); + return; + case TOK_ASM_c_mv: + asm_emit_cr(token, 2 | (8 << 12), ops, ops + 1); + return; + + case TOK_ASM_c_addi16sp: + asm_emit_ci(token, 1 | (3 << 13), ops, ops + 1); + return; + case TOK_ASM_c_addi: + asm_emit_ci(token, 1, ops, ops + 1); + return; + case TOK_ASM_c_addiw: + asm_emit_ci(token, 1 | (1 << 13), ops, ops + 1); + return; + case TOK_ASM_c_fldsp: + asm_emit_ci(token, 2 | (1 << 13), ops, ops + 1); + return; + case TOK_ASM_c_flwsp: /* RV32FC-only */ + asm_emit_ci(token, 2 | (3 << 13), ops, ops + 1); + return; + case TOK_ASM_c_ldsp: + asm_emit_ci(token, 2 | (3 << 13), ops, ops + 1); + return; + case TOK_ASM_c_li: + asm_emit_ci(token, 1 | (2 << 13), ops, ops + 1); + return; + case TOK_ASM_c_lui: + asm_emit_ci(token, 1 | (3 << 13), ops, ops + 1); + return; + case TOK_ASM_c_lwsp: + asm_emit_ci(token, 2 | (2 << 13), ops, ops + 1); + return; + case TOK_ASM_c_slli: + asm_emit_ci(token, 2, ops, ops + 1); + return; + + case TOK_ASM_c_addi4spn: + asm_emit_ciw(token, 0, ops, ops + 1); + return; + +#define CA (1 | (3 << 10) | (4 << 13)) + case TOK_ASM_c_addw: + asm_emit_ca(token, CA | (1 << 5) | (1 << 12), ops, ops + 1); + return; + case TOK_ASM_c_and: + asm_emit_ca(token, CA | (3 << 5), ops, ops + 1); + return; + case TOK_ASM_c_or: + asm_emit_ca(token, CA | (2 << 5), ops, ops + 1); + return; + case TOK_ASM_c_sub: + asm_emit_ca(token, CA, ops, ops + 1); + return; + case TOK_ASM_c_subw: + asm_emit_ca(token, CA | (1 << 12), ops, ops + 1); + return; + case TOK_ASM_c_xor: + asm_emit_ca(token, CA | (1 << 5), ops, ops + 1); + return; +#undef CA + + case TOK_ASM_c_andi: + asm_emit_cb(token, 1 | (2 << 10) | (4 << 13), ops, ops + 1); + return; + case TOK_ASM_c_beqz: + asm_emit_cb(token, 1 | (6 << 13), ops, ops + 1); + return; + case TOK_ASM_c_bnez: + asm_emit_cb(token, 1 | (7 << 13), ops, ops + 1); + return; + case TOK_ASM_c_srai: + asm_emit_cb(token, 1 | (1 << 10) | (4 << 13), ops, ops + 1); + return; + case TOK_ASM_c_srli: + asm_emit_cb(token, 1 | (4 << 13), ops, ops + 1); + return; + + case TOK_ASM_c_sdsp: + asm_emit_css(token, 2 | (7 << 13), ops, ops + 1); + return; + case TOK_ASM_c_swsp: + asm_emit_css(token, 2 | (6 << 13), ops, ops + 1); + return; + case TOK_ASM_c_fswsp: /* RV32FC-only */ + asm_emit_css(token, 2 | (7 << 13), ops, ops + 1); + return; + case TOK_ASM_c_fsdsp: + asm_emit_css(token, 2 | (5 << 13), ops, ops + 1); + return; + + /* pseudoinstructions */ + /* rd, sym */ + case TOK_ASM_la: + /* auipc rd, 0 */ + asm_emit_u(token, 3 | (5 << 2), ops, ops + 1); + /* lw rd, rd, 0 */ + asm_emit_i(token, 3 | (2 << 12), ops, ops, ops + 1); + return; + case TOK_ASM_lla: + /* auipc rd, 0 */ + asm_emit_u(token, 3 | (5 << 2), ops, ops + 1); + /* addi rd, rd, 0 */ + asm_emit_i(token, 3 | (4 << 2), ops, ops, ops + 1); + return; + + default: + expect("binary instruction"); + } +} + +/* caller: Add funct3, funct7 into opcode */ +static void asm_emit_r(int token, uint32_t opcode, const Operand* rd, const Operand* rs1, const Operand* rs2) +{ + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected second source operand that is a register or immediate", get_tok_str(token, NULL)); + return; + } + /* R-type instruction: + 31...25 funct7 + 24...20 rs2 + 19...15 rs1 + 14...12 funct3 + 11...7 rd + 6...0 opcode */ + gen_le32(opcode | ENCODE_RD(rd->reg) | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg)); +} + +/* caller: Add funct3 into opcode */ +static void asm_emit_i(int token, uint32_t opcode, const Operand* rd, const Operand* rs1, const Operand* rs2) +{ + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs2->type != OP_IM12S) { + tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL)); + return; + } + /* I-type instruction: + 31...20 imm[11:0] + 19...15 rs1 + 14...12 funct3 + 11...7 rd + 6...0 opcode */ + + gen_le32(opcode | ENCODE_RD(rd->reg) | ENCODE_RS1(rs1->reg) | (rs2->e.v << 20)); +} + +static void asm_emit_j(int token, uint32_t opcode, const Operand* rd, const Operand* rs2) +{ + uint32_t imm; + + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs2->type != OP_IM12S && rs2->type != OP_IM32) { + tcc_error("'%s': Expected second source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + imm = rs2->e.v; + + /* even offsets in a +- 1 MiB range */ + if (imm > 0x1ffffe) { + tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 0x1fffff", get_tok_str(token, NULL)); + return; + } + + if (imm & 1) { + tcc_error("'%s': Expected second source operand that is an even immediate value", get_tok_str(token, NULL)); + return; + } + /* J-type instruction: + 31 imm[20] + 30...21 imm[10:1] + 20 imm[11] + 19...12 imm[19:12] + 11...7 rd + 6...0 opcode */ + gen_le32(opcode | ENCODE_RD(rd->reg) | (((imm >> 20) & 1) << 31) | (((imm >> 1) & 0x3ff) << 21) | (((imm >> 11) & 1) << 20) | (((imm >> 12) & 0xff) << 12)); +} + +static void asm_ternary_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + parse_operand(s1, &ops[0]); + if (tok == ',') + next(); + else + expect("','"); + parse_operand(s1, &ops[1]); + if (tok == ',') + next(); + else + expect("','"); + parse_operand(s1, &ops[2]); + + switch (token) { + case TOK_ASM_sll: + asm_emit_r(token, (0xC << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_slli: + asm_emit_i(token, (4 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_srl: + asm_emit_r(token, (0xC << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_srli: + asm_emit_i(token, (0x4 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sra: + asm_emit_r(token, (0xC << 2) | 3 | (5 << 12) | (32 << 25), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_srai: + asm_emit_i(token, (0x4 << 2) | 3 | (5 << 12) | (16 << 26), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sllw: + asm_emit_r(token, (0xE << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_slliw: + asm_emit_i(token, (6 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_srlw: + asm_emit_r(token, (0xE << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_srliw: + asm_emit_i(token, (0x6 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sraw: + asm_emit_r(token, (0xE << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sraiw: + asm_emit_i(token, (0x6 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); + return; + + // Arithmetic (RD,RS1,(RS2|IMM)); R-format, I-format or U-format + + case TOK_ASM_add: + asm_emit_r(token, (0xC << 2) | 3, &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_addi: + asm_emit_i(token, (4 << 2) | 3, &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sub: + asm_emit_r(token, (0xC << 2) | 3 | (32 << 25), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_addw: + asm_emit_r(token, (0xE << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_addiw: // 64 bit + asm_emit_i(token, (0x6 << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_subw: + asm_emit_r(token, (0xE << 2) | 3 | (0 << 12) | (32 << 25), &ops[0], &ops[1], &ops[2]); + return; + + // Logical (RD,RS1,(RS2|IMM)); R-format or I-format + + case TOK_ASM_xor: + asm_emit_r(token, (0xC << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_xori: + asm_emit_i(token, (0x4 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_or: + asm_emit_r(token, (0xC << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_ori: + asm_emit_i(token, (0x4 << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_and: + asm_emit_r(token, (0xC << 2) | 3 | (7 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_andi: + asm_emit_i(token, (0x4 << 2) | 3 | (7 << 12), &ops[0], &ops[1], &ops[2]); + return; + + // Compare (RD,RS1,(RS2|IMM)); R-format or I-format + + case TOK_ASM_slt: + asm_emit_r(token, (0xC << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_slti: + asm_emit_i(token, (0x4 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sltu: + asm_emit_r(token, (0xC << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sltiu: + asm_emit_i(token, (0x4 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); + return; + + /* indirect jump (RD, RS1, IMM); I-format */ + case TOK_ASM_jalr: + asm_emit_i(token, 0x67 | (0 << 12), ops, ops + 1, ops + 2); + return; + + /* branch (RS1, RS2, IMM); B-format */ + case TOK_ASM_beq: + asm_emit_b(token, 0x63 | (0 << 12), ops, ops + 1, ops + 2); + return; + case TOK_ASM_bne: + asm_emit_b(token, 0x63 | (1 << 12), ops, ops + 1, ops + 2); + return; + case TOK_ASM_blt: + asm_emit_b(token, 0x63 | (4 << 12), ops, ops + 1, ops + 2); + return; + case TOK_ASM_bge: + asm_emit_b(token, 0x63 | (5 << 12), ops, ops + 1, ops + 2); + return; + case TOK_ASM_bltu: + asm_emit_b(token, 0x63 | (6 << 12), ops, ops + 1, ops + 2); + return; + case TOK_ASM_bgeu: + asm_emit_b(token, 0x63 | (7 << 12), ops, ops + 1, ops + 2); + return; + + // Loads (RD,RS1,I); I-format + + case TOK_ASM_lb: + asm_emit_i(token, (0x0 << 2) | 3, &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_lh: + asm_emit_i(token, (0x0 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_lw: + asm_emit_i(token, (0x0 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_lbu: + asm_emit_i(token, (0x0 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_lhu: + asm_emit_i(token, (0x0 << 2) | 3 | (5 << 12), &ops[0], &ops[1], &ops[2]); + return; + // 64 bit + case TOK_ASM_ld: + asm_emit_i(token, (0x0 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_lwu: + asm_emit_i(token, (0x0 << 2) | 3 | (6 << 12), &ops[0], &ops[1], &ops[2]); + return; + + // Stores (RS1,RS2,I); S-format + + case TOK_ASM_sb: + asm_emit_s(token, (0x8 << 2) | 3 | (0 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sh: + asm_emit_s(token, (0x8 << 2) | 3 | (1 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sw: + asm_emit_s(token, (0x8 << 2) | 3 | (2 << 12), &ops[0], &ops[1], &ops[2]); + return; + case TOK_ASM_sd: + asm_emit_s(token, (0x8 << 2) | 3 | (3 << 12), &ops[0], &ops[1], &ops[2]); + return; + + /* M extension */ + case TOK_ASM_div: + asm_emit_r(token, 0x33 | (4 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_divu: + asm_emit_r(token, 0x33 | (5 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_divuw: + asm_emit_r(token, 0x3b | (5 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_divw: + asm_emit_r(token, 0x3b | (4 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_mul: + asm_emit_r(token, 0x33 | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_mulh: + asm_emit_r(token, 0x33 | (1 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_mulhsu: + asm_emit_r(token, 0x33 | (2 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_mulhu: + asm_emit_r(token, 0x33 | (3 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_mulw: + asm_emit_r(token, 0x3b | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_rem: + asm_emit_r(token, 0x33 | (6 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_remu: + asm_emit_r(token, 0x33 | (7 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_remuw: + asm_emit_r(token, 0x3b | (7 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + case TOK_ASM_remw: + asm_emit_r(token, 0x3b | (6 << 12) | (1 << 25), ops, ops + 1, ops + 2); + return; + + /* Zicsr extension; (rd, csr, rs/uimm) */ + case TOK_ASM_csrrc: + asm_emit_i(token, 0x73 | (3 << 12), ops, ops + 2, ops + 1); + return; + case TOK_ASM_csrrci: + /* using rs1 field for uimmm */ + ops[2].type = OP_REG; + asm_emit_i(token, 0x73 | (7 << 12), ops, ops + 2, ops + 1); + return; + case TOK_ASM_csrrs: + asm_emit_i(token, 0x73 | (2 << 12), ops, ops + 2, ops + 1); + return; + case TOK_ASM_csrrsi: + ops[2].type = OP_REG; + asm_emit_i(token, 0x73 | (6 << 12), ops, ops + 2, ops + 1); + return; + case TOK_ASM_csrrw: + asm_emit_i(token, 0x73 | (1 << 12), ops, ops + 2, ops + 1); + return; + case TOK_ASM_csrrwi: + ops[2].type = OP_REG; + asm_emit_i(token, 0x73 | (5 << 12), ops, ops + 2, ops + 1); + return; + + /* C extension */ + /* register-based loads and stores (RD, RS1, IMM); CL-format */ + case TOK_ASM_c_fld: + asm_emit_cl(token, 1 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_flw: /* RV32FC-only */ + asm_emit_cl(token, 3 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_fsd: + asm_emit_cs(token, 5 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_fsw: /* RV32FC-only */ + asm_emit_cs(token, 7 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_ld: + asm_emit_cl(token, 3 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_lw: + asm_emit_cl(token, 2 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_sd: + asm_emit_cs(token, 7 << 13, ops, ops + 1, ops + 2); + return; + case TOK_ASM_c_sw: + asm_emit_cs(token, 6 << 13, ops, ops + 1, ops + 2); + return; + + default: + expect("ternary instruction"); + } +} + +/* caller: Add funct3 to opcode */ +static void asm_emit_s(int token, uint32_t opcode, const Operand* rs1, const Operand* rs2, const Operand* imm) +{ + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected second source operand that is a register", get_tok_str(token, NULL)); + return; + } + if (imm->type != OP_IM12S) { + tcc_error("'%s': Expected third operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL)); + return; + } + { + uint16_t v = imm->e.v; + /* S-type instruction: + 31...25 imm[11:5] + 24...20 rs2 + 19...15 rs1 + 14...12 funct3 + 11...7 imm[4:0] + 6...0 opcode + opcode always fixed pos. */ + gen_le32(opcode | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg) | ((v & 0x1F) << 7) | ((v >> 5) << 25)); + } +} + +static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Operand *rs2, const Operand *imm) +{ + uint32_t offset; + + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected first source operand that is a register", get_tok_str(token, NULL)); + return; + } + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + if (imm->type != OP_IM12S) { + tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL)); + return; + } + + offset = imm->e.v; + + /* B-type instruction: + 31 imm[12] + 30...25 imm[10:5] + 24...20 rs2 + 19...15 rs1 + 14...12 funct3 + 8...11 imm[4:1] + 7 imm[11] + 6...0 opcode */ + asm_emit_opcode(opcode | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg) | (((offset >> 1) & 0xF) << 8) | (((offset >> 5) & 0x1f) << 25) | (((offset >> 11) & 1) << 7) | (((offset >> 12) & 1) << 31)); +} + +ST_FUNC void asm_opcode(TCCState *s1, int token) +{ + switch (token) { + case TOK_ASM_ebreak: + case TOK_ASM_ecall: + case TOK_ASM_fence: + case TOK_ASM_fence_i: + case TOK_ASM_hrts: + case TOK_ASM_mrth: + case TOK_ASM_mrts: + case TOK_ASM_wfi: + asm_nullary_opcode(s1, token); + return; + + case TOK_ASM_rdcycle: + case TOK_ASM_rdcycleh: + case TOK_ASM_rdtime: + case TOK_ASM_rdtimeh: + case TOK_ASM_rdinstret: + case TOK_ASM_rdinstreth: + asm_unary_opcode(s1, token); + return; + + case TOK_ASM_lui: + case TOK_ASM_auipc: + case TOK_ASM_jal: + asm_binary_opcode(s1, token); + return; + + case TOK_ASM_add: + case TOK_ASM_addi: + case TOK_ASM_addiw: + case TOK_ASM_addw: + case TOK_ASM_and: + case TOK_ASM_andi: + case TOK_ASM_beq: + case TOK_ASM_bge: + case TOK_ASM_bgeu: + case TOK_ASM_blt: + case TOK_ASM_bltu: + case TOK_ASM_bne: + case TOK_ASM_jalr: + case TOK_ASM_lb: + case TOK_ASM_lbu: + case TOK_ASM_ld: + case TOK_ASM_lh: + case TOK_ASM_lhu: + case TOK_ASM_lw: + case TOK_ASM_lwu: + case TOK_ASM_or: + case TOK_ASM_ori: + case TOK_ASM_sb: + case TOK_ASM_sd: + case TOK_ASM_sh: + case TOK_ASM_sll: + case TOK_ASM_slli: + case TOK_ASM_slliw: + case TOK_ASM_sllw: + case TOK_ASM_slt: + case TOK_ASM_slti: + case TOK_ASM_sltiu: + case TOK_ASM_sltu: + case TOK_ASM_sra: + case TOK_ASM_srai: + case TOK_ASM_sraiw: + case TOK_ASM_sraw: + case TOK_ASM_srl: + case TOK_ASM_srli: + case TOK_ASM_srliw: + case TOK_ASM_srlw: + case TOK_ASM_sub: + case TOK_ASM_subw: + case TOK_ASM_sw: + case TOK_ASM_xor: + case TOK_ASM_xori: + /* M extension */ + case TOK_ASM_div: + case TOK_ASM_divu: + case TOK_ASM_divuw: + case TOK_ASM_divw: + case TOK_ASM_mul: + case TOK_ASM_mulh: + case TOK_ASM_mulhsu: + case TOK_ASM_mulhu: + case TOK_ASM_mulw: + case TOK_ASM_rem: + case TOK_ASM_remu: + case TOK_ASM_remuw: + case TOK_ASM_remw: + /* Zicsr extension */ + case TOK_ASM_csrrc: + case TOK_ASM_csrrci: + case TOK_ASM_csrrs: + case TOK_ASM_csrrsi: + case TOK_ASM_csrrw: + case TOK_ASM_csrrwi: + asm_ternary_opcode(s1, token); + return; + + /* C extension */ + case TOK_ASM_c_ebreak: + case TOK_ASM_c_nop: + asm_nullary_opcode(s1, token); + return; + + case TOK_ASM_c_j: + case TOK_ASM_c_jal: + case TOK_ASM_c_jalr: + case TOK_ASM_c_jr: + asm_unary_opcode(s1, token); + return; + + case TOK_ASM_c_add: + case TOK_ASM_c_addi16sp: + case TOK_ASM_c_addi4spn: + case TOK_ASM_c_addi: + case TOK_ASM_c_addiw: + case TOK_ASM_c_addw: + case TOK_ASM_c_and: + case TOK_ASM_c_andi: + case TOK_ASM_c_beqz: + case TOK_ASM_c_bnez: + case TOK_ASM_c_fldsp: + case TOK_ASM_c_flwsp: + case TOK_ASM_c_fsdsp: + case TOK_ASM_c_fswsp: + case TOK_ASM_c_ldsp: + case TOK_ASM_c_li: + case TOK_ASM_c_lui: + case TOK_ASM_c_lwsp: + case TOK_ASM_c_mv: + case TOK_ASM_c_or: + case TOK_ASM_c_sdsp: + case TOK_ASM_c_slli: + case TOK_ASM_c_srai: + case TOK_ASM_c_srli: + case TOK_ASM_c_sub: + case TOK_ASM_c_subw: + case TOK_ASM_c_swsp: + case TOK_ASM_c_xor: + asm_binary_opcode(s1, token); + return; + + case TOK_ASM_c_fld: + case TOK_ASM_c_flw: + case TOK_ASM_c_fsd: + case TOK_ASM_c_fsw: + case TOK_ASM_c_ld: + case TOK_ASM_c_lw: + case TOK_ASM_c_sd: + case TOK_ASM_c_sw: + asm_ternary_opcode(s1, token); + return; + + /* pseudoinstructions */ + case TOK_ASM_nop: + asm_nullary_opcode(s1, token); + return; + + case TOK_ASM_la: + case TOK_ASM_lla: + asm_binary_opcode(s1, token); + return; + + default: + expect("known instruction"); + } +} + +static int asm_parse_csrvar(int t) +{ + switch (t) { + case TOK_ASM_cycle: + return 0xc00; + case TOK_ASM_fcsr: + return 3; + case TOK_ASM_fflags: + return 1; + case TOK_ASM_frm: + return 2; + case TOK_ASM_instret: + return 0xc02; + case TOK_ASM_time: + return 0xc01; + case TOK_ASM_cycleh: + return 0xc80; + case TOK_ASM_instreth: + return 0xc82; + case TOK_ASM_timeh: + return 0xc81; + default: + return -1; + } +} + +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) +{ + tcc_error("RISCV64 asm not implemented."); +} + +/* generate prolog and epilog code for asm statement */ +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) +{ +} + +ST_FUNC void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) +{ +} + +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) +{ + int reg; + TokenSym *ts; + + if (!strcmp(str, "memory") || + !strcmp(str, "cc") || + !strcmp(str, "flags")) + return; + ts = tok_alloc(str, strlen(str)); + reg = asm_parse_regvar(ts->tok); + if (reg == -1) { + tcc_error("invalid clobber register '%s'", str); + } + clobber_regs[reg] = 1; +} + +ST_FUNC int asm_parse_regvar (int t) +{ + /* PC register not implemented */ + if (t >= TOK_ASM_pc || t < TOK_ASM_x0) + return -1; + + if (t < TOK_ASM_f0) + return t - TOK_ASM_x0; + + if (t < TOK_ASM_zero) + return t - TOK_ASM_f0; + + /* ABI mnemonic */ + if (t < TOK_ASM_ft0) + return t - TOK_ASM_zero; + + return t - TOK_ASM_ft0; +} + +/*************************************************************/ +/* C extension */ + +/* caller: Add funct6, funct2 into opcode */ +static void asm_emit_ca(int token, uint16_t opcode, const Operand *rd, const Operand *rs2) +{ + uint8_t dst, src; + + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL)); + return; + } + + /* subtract index of x8 */ + dst = rd->reg - 8; + src = rs2->reg - 8; + + /* only registers {x,f}8 to {x,f}15 are valid (3-bit) */ + if (dst > 7) { + tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + if (src > 7) { + tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + /* CA-type instruction: + 15...10 funct6 + 9...7 rd'/rs1' + 6..5 funct2 + 4...2 rs2' + 1...0 opcode */ + + gen_le16(opcode | C_ENCODE_RS2(src) | C_ENCODE_RS1(dst)); +} + +static void asm_emit_cb(int token, uint16_t opcode, const Operand *rs1, const Operand *imm) +{ + uint32_t offset; + uint8_t src; + + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (imm->type != OP_IM12S && imm->type != OP_IM32) { + tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + offset = imm->e.v; + + if (offset & 1) { + tcc_error("'%s': Expected source operand that is an even immediate value", get_tok_str(token, NULL)); + return; + } + + src = rs1->reg - 8; + + if (src > 7) { + tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + /* CB-type instruction: + 15...13 funct3 + 12...10 offset + 9..7 rs1' + 6...2 offset + 1...0 opcode */ + + /* non-branch also using CB: + 15...13 funct3 + 12 imm + 11..10 funct2 + 9...7 rd'/rs1' + 6..2 imm + 1...0 opcode */ + + switch (token) { + case TOK_ASM_c_beqz: + case TOK_ASM_c_bnez: + gen_le16(opcode | C_ENCODE_RS1(src) | ((NTH_BIT(offset, 5) | (((offset >> 1) & 3) << 1) | (((offset >> 6) & 3) << 3)) << 2) | ((((offset >> 3) & 3) | NTH_BIT(offset, 8)) << 10)); + return; + default: + gen_le16(opcode | C_ENCODE_RS1(src) | ((offset & 0x1f) << 2) | (NTH_BIT(offset, 5) << 12)); + return; + } +} + +static void asm_emit_ci(int token, uint16_t opcode, const Operand *rd, const Operand *imm) +{ + uint32_t immediate; + + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (imm->type != OP_IM12S && imm->type != OP_IM32) { + tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + immediate = imm->e.v; + + /* CI-type instruction: + 15...13 funct3 + 12 imm + 11...7 rd/rs1 + 6...2 imm + 1...0 opcode */ + + switch (token) { + case TOK_ASM_c_addi: + case TOK_ASM_c_addiw: + case TOK_ASM_c_li: + case TOK_ASM_c_slli: + gen_le16(opcode | ((immediate & 0x1f) << 2) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 5) << 12)); + return; + case TOK_ASM_c_addi16sp: + gen_le16(opcode | NTH_BIT(immediate, 5) << 2 | (((immediate >> 7) & 3) << 3) | NTH_BIT(immediate, 6) << 5 | NTH_BIT(immediate, 4) << 6 | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 9) << 12)); + return; + case TOK_ASM_c_lui: + gen_le16(opcode | (((immediate >> 12) & 0x1f) << 2) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 17) << 12)); + return; + case TOK_ASM_c_fldsp: + case TOK_ASM_c_ldsp: + gen_le16(opcode | (((immediate >> 6) & 7) << 2) | (((immediate >> 3) & 2) << 5) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 5) << 12)); + return; + case TOK_ASM_c_flwsp: + case TOK_ASM_c_lwsp: + gen_le16(opcode | (((immediate >> 6) & 3) << 2) | (((immediate >> 2) & 7) << 4) | ENCODE_RD(rd->reg) | (NTH_BIT(immediate, 5) << 12)); + return; + case TOK_ASM_c_nop: + gen_le16(opcode); + return; + default: + expect("known instruction"); + } +} + +/* caller: Add funct3 into opcode */ +static void asm_emit_ciw(int token, uint16_t opcode, const Operand *rd, const Operand *imm) +{ + uint32_t nzuimm; + uint8_t dst; + + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (imm->type != OP_IM12S && imm->type != OP_IM32) { + tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + dst = rd->reg - 8; + + if (dst > 7) { + tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + nzuimm = imm->e.v; + + if (nzuimm > 0x3fc) { + tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0x3ff", get_tok_str(token, NULL)); + return; + } + + if (nzuimm & 3) { + tcc_error("'%s': Expected source operand that is a non-zero immediate value divisible by 4", get_tok_str(token, NULL)); + return; + } + + /* CIW-type instruction: + 15...13 funct3 + 12...5 imm + 4...2 rd' + 1...0 opcode */ + + gen_le16(opcode | ENCODE_RS2(rd->reg) | ((NTH_BIT(nzuimm, 3) | (NTH_BIT(nzuimm, 2) << 1) | (((nzuimm >> 6) & 0xf) << 2) | (((nzuimm >> 4) & 3) << 6)) << 5)); +} + +/* caller: Add funct3 into opcode */ +static void asm_emit_cj(int token, uint16_t opcode, const Operand *imm) +{ + uint32_t offset; + + /* +-2 KiB range */ + if (imm->type != OP_IM12S) { + tcc_error("'%s': Expected source operand that is a 12-bit immediate value", get_tok_str(token, NULL)); + return; + } + + offset = imm->e.v; + + if (offset & 1) { + tcc_error("'%s': Expected source operand that is an even immediate value", get_tok_str(token, NULL)); + return; + } + + /* CJ-type instruction: + 15...13 funct3 + 12...2 offset[11|4|9:8|10|6|7|3:1|5] + 1...0 opcode */ + + gen_le16(opcode | (NTH_BIT(offset, 5) << 2) | (((offset >> 1) & 7) << 3) | (NTH_BIT(offset, 7) << 6) | (NTH_BIT(offset, 6) << 7) | (NTH_BIT(offset, 10) << 8) | (((offset >> 8) & 3) << 9) | (NTH_BIT(offset, 4) << 11) | (NTH_BIT(offset, 11) << 12)); +} + +/* caller: Add funct3 into opcode */ +static void asm_emit_cl(int token, uint16_t opcode, const Operand *rd, const Operand *rs1, const Operand *imm) +{ + uint32_t offset; + uint8_t dst, src; + + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (imm->type != OP_IM12S && imm->type != OP_IM32) { + tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + dst = rd->reg - 8; + src = rs1->reg - 8; + + if (dst > 7) { + tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + if (src > 7) { + tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + offset = imm->e.v; + + if (offset > 0xff) { + tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0xff", get_tok_str(token, NULL)); + return; + } + + if (offset & 3) { + tcc_error("'%s': Expected source operand that is an immediate value divisible by 4", get_tok_str(token, NULL)); + return; + } + + /* CL-type instruction: + 15...13 funct3 + 12...10 imm + 9...7 rs1' + 6...5 imm + 4...2 rd' + 1...0 opcode */ + + switch (token) { + /* imm variant 1 */ + case TOK_ASM_c_flw: + case TOK_ASM_c_lw: + gen_le16(opcode | C_ENCODE_RS2(dst) | C_ENCODE_RS1(src) | (NTH_BIT(offset, 6) << 5) | (NTH_BIT(offset, 2) << 6) | (((offset >> 3) & 7) << 10)); + return; + /* imm variant 2 */ + case TOK_ASM_c_fld: + case TOK_ASM_c_ld: + gen_le16(opcode | C_ENCODE_RS2(dst) | C_ENCODE_RS1(src) | (((offset >> 6) & 3) << 5) | (((offset >> 3) & 7) << 10)); + return; + default: + expect("known instruction"); + } +} + +/* caller: Add funct4 into opcode */ +static void asm_emit_cr(int token, uint16_t opcode, const Operand *rd, const Operand *rs2) +{ + if (rd->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL)); + return; + } + + /* CR-type instruction: + 15...12 funct4 + 11..7 rd/rs1 + 6...2 rs2 + 1...0 opcode */ + + gen_le16(opcode | C_ENCODE_RS1(rd->reg) | C_ENCODE_RS2(rs2->reg)); +} + +/* caller: Add funct3 into opcode */ +static void asm_emit_cs(int token, uint16_t opcode, const Operand *rs2, const Operand *rs1, const Operand *imm) +{ + uint32_t offset; + uint8_t base, src; + + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (rs1->type != OP_REG) { + tcc_error("'%s': Expected source operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (imm->type != OP_IM12S && imm->type != OP_IM32) { + tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + base = rs1->reg - 8; + src = rs2->reg - 8; + + if (base > 7) { + tcc_error("'%s': Expected destination operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + if (src > 7) { + tcc_error("'%s': Expected source operand that is a valid C-extension register", get_tok_str(token, NULL)); + return; + } + + offset = imm->e.v; + + if (offset > 0xff) { + tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0xff", get_tok_str(token, NULL)); + return; + } + + if (offset & 3) { + tcc_error("'%s': Expected source operand that is an immediate value divisible by 4", get_tok_str(token, NULL)); + return; + } + + /* CS-type instruction: + 15...13 funct3 + 12...10 imm + 9...7 rs1' + 6...5 imm + 4...2 rs2' + 1...0 opcode */ + switch (token) { + /* imm variant 1 */ + case TOK_ASM_c_fsw: + case TOK_ASM_c_sw: + gen_le16(opcode | C_ENCODE_RS2(base) | C_ENCODE_RS1(src) | (NTH_BIT(offset, 6) << 5) | (NTH_BIT(offset, 2) << 6) | (((offset >> 3) & 7) << 10)); + return; + /* imm variant 2 */ + case TOK_ASM_c_fsd: + case TOK_ASM_c_sd: + gen_le16(opcode | C_ENCODE_RS2(base) | C_ENCODE_RS1(src) | (((offset >> 6) & 3) << 5) | (((offset >> 3) & 7) << 10)); + return; + default: + expect("known instruction"); + } +} + +/* caller: Add funct3 into opcode */ +static void asm_emit_css(int token, uint16_t opcode, const Operand *rs2, const Operand *imm) +{ + uint32_t offset; + + if (rs2->type != OP_REG) { + tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); + return; + } + + if (imm->type != OP_IM12S && imm->type != OP_IM32) { + tcc_error("'%s': Expected source operand that is an immediate value", get_tok_str(token, NULL)); + return; + } + + offset = imm->e.v; + + if (offset > 0xff) { + tcc_error("'%s': Expected source operand that is an immediate value between 0 and 0xff", get_tok_str(token, NULL)); + return; + } + + if (offset & 3) { + tcc_error("'%s': Expected source operand that is an immediate value divisible by 4", get_tok_str(token, NULL)); + return; + } + + /* CSS-type instruction: + 15...13 funct3 + 12...7 imm + 6...2 rs2 + 1...0 opcode */ + + switch (token) { + /* imm variant 1 */ + case TOK_ASM_c_fswsp: + case TOK_ASM_c_swsp: + gen_le16(opcode | ENCODE_RS2(rs2->reg) | (((offset >> 6) & 3) << 7) | (((offset >> 2) & 0xf) << 9)); + return; + /* imm variant 2 */ + case TOK_ASM_c_fsdsp: + case TOK_ASM_c_sdsp: + gen_le16(opcode | ENCODE_RS2(rs2->reg) | (((offset >> 6) & 7) << 7) | (((offset >> 3) & 7) << 10)); + return; + default: + expect("known instruction"); + } +} + +/*************************************************************/ +#endif /* ndef TARGET_DEFS_ONLY */ diff --git a/vendor/tcc/riscv64-gen.c b/vendor/tcc/riscv64-gen.c new file mode 100644 index 00000000..8a17b701 --- /dev/null +++ b/vendor/tcc/riscv64-gen.c @@ -0,0 +1,1442 @@ +#ifdef TARGET_DEFS_ONLY + +// Number of registers available to allocator: +#define NB_REGS 19 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa7, xxx, ra, sp +#define NB_ASM_REGS 32 +#define CONFIG_TCC_ASM + +#define TREG_R(x) (x) // x = 0..7 +#define TREG_F(x) (x + 8) // x = 0..7 + +// Register classes sorted from more general to more precise: +#define RC_INT (1 << 0) +#define RC_FLOAT (1 << 1) +#define RC_R(x) (1 << (2 + (x))) // x = 0..7 +#define RC_F(x) (1 << (10 + (x))) // x = 0..7 + +#define RC_IRET (RC_R(0)) // int return register class +#define RC_IRE2 (RC_R(1)) // int 2nd return register class +#define RC_FRET (RC_F(0)) // float return register class + +#define REG_IRET (TREG_R(0)) // int return register number +#define REG_IRE2 (TREG_R(1)) // int 2nd return register number +#define REG_FRET (TREG_F(0)) // float return register number + +#define PTR_SIZE 8 + +#define LDOUBLE_SIZE 16 +#define LDOUBLE_ALIGN 16 + +#define MAX_ALIGN 16 + +#define CHAR_IS_UNSIGNED + +#else +#define USING_GLOBALS +#include "tcc.h" +#include + +ST_DATA const char * const target_machine_defs = + "__riscv\0" + "__riscv_xlen 64\0" + "__riscv_flen 64\0" + "__riscv_div\0" + "__riscv_mul\0" + "__riscv_fdiv\0" + "__riscv_fsqrt\0" + "__riscv_float_abi_double\0" + ; + +#define XLEN 8 + +#define TREG_RA 17 +#define TREG_SP 18 + +ST_DATA const int reg_classes[NB_REGS] = { + RC_INT | RC_R(0), + RC_INT | RC_R(1), + RC_INT | RC_R(2), + RC_INT | RC_R(3), + RC_INT | RC_R(4), + RC_INT | RC_R(5), + RC_INT | RC_R(6), + RC_INT | RC_R(7), + RC_FLOAT | RC_F(0), + RC_FLOAT | RC_F(1), + RC_FLOAT | RC_F(2), + RC_FLOAT | RC_F(3), + RC_FLOAT | RC_F(4), + RC_FLOAT | RC_F(5), + RC_FLOAT | RC_F(6), + RC_FLOAT | RC_F(7), + 0, + 1 << TREG_RA, + 1 << TREG_SP +}; + +#if defined(CONFIG_TCC_BCHECK) +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +ST_DATA int func_bound_add_epilog; +#endif + +static int ireg(int r) +{ + if (r == TREG_RA) + return 1; // ra + if (r == TREG_SP) + return 2; // sp + assert(r >= 0 && r < 8); + return r + 10; // tccrX --> aX == x(10+X) +} + +static int is_ireg(int r) +{ + return (unsigned)r < 8 || r == TREG_RA || r == TREG_SP; +} + +static int freg(int r) +{ + assert(r >= 8 && r < 16); + return r - 8 + 10; // tccfX --> faX == f(10+X) +} + +static int is_freg(int r) +{ + return r >= 8 && r < 16; +} + +ST_FUNC void o(unsigned int c) +{ + int ind1 = ind + 4; + if (nocode_wanted) + return; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + write32le(cur_text_section->data + ind, c); + ind = ind1; +} + +static void EIu(uint32_t opcode, uint32_t func3, + uint32_t rd, uint32_t rs1, uint32_t imm) +{ + o(opcode | (func3 << 12) | (rd << 7) | (rs1 << 15) | (imm << 20)); +} + +static void ER(uint32_t opcode, uint32_t func3, + uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t func7) +{ + o(opcode | func3 << 12 | rd << 7 | rs1 << 15 | rs2 << 20 | func7 << 25); +} + +static void EI(uint32_t opcode, uint32_t func3, + uint32_t rd, uint32_t rs1, uint32_t imm) +{ + assert(! ((imm + (1 << 11)) >> 12)); + EIu(opcode, func3, rd, rs1, imm); +} + +static void ES(uint32_t opcode, uint32_t func3, + uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + assert(! ((imm + (1 << 11)) >> 12)); + o(opcode | (func3 << 12) | ((imm & 0x1f) << 7) | (rs1 << 15) + | (rs2 << 20) | ((imm >> 5) << 25)); +} + +// Patch all branches in list pointed to by t to branch to a: +ST_FUNC void gsym_addr(int t_, int a_) +{ + uint32_t t = t_; + uint32_t a = a_; + while (t) { + unsigned char *ptr = cur_text_section->data + t; + uint32_t next = read32le(ptr); + uint32_t r = a - t, imm; + if ((r + (1 << 21)) & ~((1U << 22) - 2)) + tcc_error("out-of-range branch chain"); + imm = (((r >> 12) & 0xff) << 12) + | (((r >> 11) & 1) << 20) + | (((r >> 1) & 0x3ff) << 21) + | (((r >> 20) & 1) << 31); + write32le(ptr, r == 4 ? 0x33 : 0x6f | imm); // nop || j imm + t = next; + } +} + +static int load_symofs(int r, SValue *sv, int forstore) +{ + int rr, doload = 0; + int fc = sv->c.i, v = sv->r & VT_VALMASK; + if (sv->r & VT_SYM) { + Sym label = {0}; + assert(v == VT_CONST); + if (sv->sym->type.t & VT_STATIC) { // XXX do this per linker relax + greloca(cur_text_section, sv->sym, ind, + R_RISCV_PCREL_HI20, sv->c.i); + sv->c.i = 0; + } else { + if (((unsigned)fc + (1 << 11)) >> 12) + tcc_error("unimp: large addend for global address (0x%lx)", (long)sv->c.i); + greloca(cur_text_section, sv->sym, ind, + R_RISCV_GOT_HI20, 0); + doload = 1; + } + label.type.t = VT_VOID | VT_STATIC; + if (!nocode_wanted) + put_extern_sym(&label, cur_text_section, ind, 0); + rr = is_ireg(r) ? ireg(r) : 5; + o(0x17 | (rr << 7)); // auipc RR, 0 %pcrel_hi(sym)+addend + greloca(cur_text_section, &label, ind, + doload || !forstore + ? R_RISCV_PCREL_LO12_I : R_RISCV_PCREL_LO12_S, 0); + if (doload) { + EI(0x03, 3, rr, rr, 0); // ld RR, 0(RR) + } + } else if (v == VT_LOCAL || v == VT_LLOCAL) { + rr = 8; // s0 + if (fc != sv->c.i) + tcc_error("unimp: store(giant local off) (0x%lx)", (long)sv->c.i); + if (((unsigned)fc + (1 << 11)) >> 12) { + rr = is_ireg(r) ? ireg(r) : 5; // t0 + o(0x37 | (rr << 7) | ((0x800 + fc) & 0xfffff000)); //lui RR, upper(fc) + ER(0x33, 0, rr, rr, 8, 0); // add RR, RR, s0 + sv->c.i = fc << 20 >> 20; + } + } else + tcc_error("uhh"); + return rr; +} + +static void load_large_constant(int rr, int fc, uint32_t pi) +{ + if (fc < 0) + pi++; + o(0x37 | (rr << 7) | (((pi + 0x800) & 0xfffff000))); // lui RR, up(up(fc)) + EI(0x13, 0, rr, rr, (int)pi << 20 >> 20); // addi RR, RR, lo(up(fc)) + EI(0x13, 1, rr, rr, 12); // slli RR, RR, 12 + EI(0x13, 0, rr, rr, (fc + (1 << 19)) >> 20); // addi RR, RR, up(lo(fc)) + EI(0x13, 1, rr, rr, 12); // slli RR, RR, 12 + fc = fc << 12 >> 12; + EI(0x13, 0, rr, rr, fc >> 8); // addi RR, RR, lo1(lo(fc)) + EI(0x13, 1, rr, rr, 8); // slli RR, RR, 8 +} + +ST_FUNC void load(int r, SValue *sv) +{ + int fr = sv->r; + int v = fr & VT_VALMASK; + int rr = is_ireg(r) ? ireg(r) : freg(r); + int fc = sv->c.i; + int bt = sv->type.t & VT_BTYPE; + int align, size; + if (fr & VT_LVAL) { + int func3, opcode = is_freg(r) ? 0x07 : 0x03, br; + size = type_size(&sv->type, &align); + assert (!is_freg(r) || bt == VT_FLOAT || bt == VT_DOUBLE); + if (bt == VT_FUNC) /* XXX should be done in generic code */ + size = PTR_SIZE; + func3 = size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3; + if (size < 4 && !is_float(sv->type.t) && (sv->type.t & VT_UNSIGNED)) + func3 |= 4; + if (v == VT_LOCAL || (fr & VT_SYM)) { + br = load_symofs(r, sv, 0); + fc = sv->c.i; + } else if (v < VT_CONST) { + br = ireg(v); + /*if (((unsigned)fc + (1 << 11)) >> 12) + tcc_error("unimp: load(large addend) (0x%x)", fc);*/ + fc = 0; // XXX store ofs in LVAL(reg) + } else if (v == VT_LLOCAL) { + br = load_symofs(r, sv, 0); + fc = sv->c.i; + EI(0x03, 3, rr, br, fc); // ld RR, fc(BR) + br = rr; + fc = 0; + } else if (v == VT_CONST) { + int64_t si = sv->c.i; + si >>= 32; + if (si != 0) { + load_large_constant(rr, fc, si); + fc &= 0xff; + } else { + o(0x37 | (rr << 7) | ((0x800 + fc) & 0xfffff000)); //lui RR, upper(fc) + fc = fc << 20 >> 20; + } + br = rr; + } else { + tcc_error("unimp: load(non-local lval)"); + } + EI(opcode, func3, rr, br, fc); // l[bhwd][u] / fl[wd] RR, fc(BR) + } else if (v == VT_CONST) { + int rb = 0, do32bit = 8, zext = 0; + assert((!is_float(sv->type.t) && is_ireg(r)) || bt == VT_LDOUBLE); + if (fr & VT_SYM) { + rb = load_symofs(r, sv, 0); + fc = sv->c.i; + do32bit = 0; + } + if (is_float(sv->type.t) && bt != VT_LDOUBLE) + tcc_error("unimp: load(float)"); + if (fc != sv->c.i) { + int64_t si = sv->c.i; + si >>= 32; + if (si != 0) { + load_large_constant(rr, fc, si); + fc &= 0xff; + rb = rr; + do32bit = 0; + } else if (bt == VT_LLONG) { + /* A 32bit unsigned constant for a 64bit type. + lui always sign extends, so we need to do an explicit zext.*/ + zext = 1; + } + } + if (((unsigned)fc + (1 << 11)) >> 12) + o(0x37 | (rr << 7) | ((0x800 + fc) & 0xfffff000)), rb = rr; //lui RR, upper(fc) + if (fc || (rr != rb) || do32bit || (fr & VT_SYM)) + EI(0x13 | do32bit, 0, rr, rb, fc << 20 >> 20); // addi[w] R, x0|R, FC + if (zext) { + EI(0x13, 1, rr, rr, 32); // slli RR, RR, 32 + EI(0x13, 5, rr, rr, 32); // srli RR, RR, 32 + } + } else if (v == VT_LOCAL) { + int br = load_symofs(r, sv, 0); + assert(is_ireg(r)); + fc = sv->c.i; + EI(0x13, 0, rr, br, fc); // addi R, s0, FC + } else if (v < VT_CONST) { /* reg-reg */ + //assert(!fc); XXX support offseted regs + if (is_freg(r) && is_freg(v)) + ER(0x53, 0, rr, freg(v), freg(v), bt == VT_DOUBLE ? 0x11 : 0x10); //fsgnj.[sd] RR, V, V == fmv.[sd] RR, V + else if (is_ireg(r) && is_ireg(v)) + EI(0x13, 0, rr, ireg(v), 0); // addi RR, V, 0 == mv RR, V + else { + int func7 = is_ireg(r) ? 0x70 : 0x78; + size = type_size(&sv->type, &align); + if (size == 8) + func7 |= 1; + assert(size == 4 || size == 8); + o(0x53 | (rr << 7) | ((is_freg(v) ? freg(v) : ireg(v)) << 15) + | (func7 << 25)); // fmv.{w.x, x.w, d.x, x.d} RR, VR + } + } else if (v == VT_CMP) { + int op = vtop->cmp_op; + int a = vtop->cmp_r & 0xff; + int b = (vtop->cmp_r >> 8) & 0xff; + int inv = 0; + switch (op) { + case TOK_ULT: + case TOK_UGE: + case TOK_ULE: + case TOK_UGT: + case TOK_LT: + case TOK_GE: + case TOK_LE: + case TOK_GT: + if (op & 1) { // remove [U]GE,GT + inv = 1; + op--; + } + if ((op & 7) == 6) { // [U]LE + int t = a; a = b; b = t; + inv ^= 1; + } + ER(0x33, (op > TOK_UGT) ? 2 : 3, rr, a, b, 0); // slt[u] d, a, b + if (inv) + EI(0x13, 4, rr, rr, 1); // xori d, d, 1 + break; + case TOK_NE: + case TOK_EQ: + if (rr != a || b) + ER(0x33, 0, rr, a, b, 0x20); // sub d, a, b + if (op == TOK_NE) + ER(0x33, 3, rr, 0, rr, 0); // sltu d, x0, d == snez d,d + else + EI(0x13, 3, rr, rr, 1); // sltiu d, d, 1 == seqz d,d + break; + } + } else if ((v & ~1) == VT_JMP) { + int t = v & 1; + assert(is_ireg(r)); + EI(0x13, 0, rr, 0, t); // addi RR, x0, t + gjmp_addr(ind + 8); + gsym(fc); + EI(0x13, 0, rr, 0, t ^ 1); // addi RR, x0, !t + } else + tcc_error("unimp: load(non-const)"); +} + +ST_FUNC void store(int r, SValue *sv) +{ + int fr = sv->r & VT_VALMASK; + int rr = is_ireg(r) ? ireg(r) : freg(r), ptrreg; + int fc = sv->c.i; + int bt = sv->type.t & VT_BTYPE; + int align, size = type_size(&sv->type, &align); + assert(!is_float(bt) || is_freg(r) || bt == VT_LDOUBLE); + /* long doubles are in two integer registers, but the load/store + primitives only deal with one, so do as if it's one reg. */ + if (bt == VT_LDOUBLE) + size = align = 8; + if (bt == VT_STRUCT) + tcc_error("unimp: store(struct)"); + if (size > 8) + tcc_error("unimp: large sized store"); + assert(sv->r & VT_LVAL); + if (fr == VT_LOCAL || (sv->r & VT_SYM)) { + ptrreg = load_symofs(-1, sv, 1); + fc = sv->c.i; + } else if (fr < VT_CONST) { + ptrreg = ireg(fr); + /*if (((unsigned)fc + (1 << 11)) >> 12) + tcc_error("unimp: store(large addend) (0x%x)", fc);*/ + fc = 0; // XXX support offsets regs + } else if (fr == VT_CONST) { + int64_t si = sv->c.i; + ptrreg = 8; // s0 + si >>= 32; + if (si != 0) { + load_large_constant(ptrreg, fc, si); + fc &= 0xff; + } else { + o(0x37 | (ptrreg << 7) | ((0x800 + fc) & 0xfffff000)); //lui RR, upper(fc) + fc = fc << 20 >> 20; + } + } else + tcc_error("implement me: %s(!local)", __FUNCTION__); + ES(is_freg(r) ? 0x27 : 0x23, // fs... | s... + size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3, // ... [wd] | [bhwd] + ptrreg, rr, fc); // RR, fc(base) +} + +static void gcall_or_jmp(int docall) +{ + int tr = docall ? 1 : 5; // ra or t0 + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && + ((vtop->r & VT_SYM) && vtop->c.i == (int)vtop->c.i)) { + /* constant symbolic case -> simple relocation */ + greloca(cur_text_section, vtop->sym, ind, + R_RISCV_CALL_PLT, (int)vtop->c.i); + o(0x17 | (tr << 7)); // auipc TR, 0 %call(func) + EI(0x67, 0, tr, tr, 0);// jalr TR, r(TR) + } else if (vtop->r < VT_CONST) { + int r = ireg(vtop->r); + EI(0x67, 0, tr, r, 0); // jalr TR, 0(R) + } else { + int r = TREG_RA; + load(r, vtop); + r = ireg(r); + EI(0x67, 0, tr, r, 0); // jalr TR, 0(R) + } +} + +#if defined(CONFIG_TCC_BCHECK) + +static void gen_bounds_call(int v) +{ + Sym *sym = external_helper_sym(v); + + greloca(cur_text_section, sym, ind, R_RISCV_CALL_PLT, 0); + o(0x17 | (1 << 7)); // auipc TR, 0 %call(func) + EI(0x67, 0, 1, 1, 0); // jalr TR, r(TR) +} + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + o(0x00000013); /* ld a0,#lbound section pointer */ + o(0x00000013); + o(0x00000013); /* nop -> call __bound_local_new */ + o(0x00000013); +} + +static void gen_bounds_epilog(void) +{ + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + Sym label = {0}; + + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, PTR_SIZE); + + label.type.t = VT_VOID | VT_STATIC; + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + put_extern_sym(&label, cur_text_section, ind, 0); + greloca(cur_text_section, sym_data, ind, R_RISCV_GOT_HI20, 0); + o(0x17 | (10 << 7)); // auipc a0, 0 %pcrel_hi(sym)+addend + greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0); + EI(0x03, 3, 10, 10, 0); // ld a0, 0(a0) + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + label.c = 0; /* force new local ELF symbol */ + } + + /* generate bound check local freeing */ + o(0xe02a1101); /* addi sp,sp,-32 sd a0,0(sp) */ + o(0xa82ae42e); /* sd a1,8(sp) fsd fa0,16(sp) */ + put_extern_sym(&label, cur_text_section, ind, 0); + greloca(cur_text_section, sym_data, ind, R_RISCV_GOT_HI20, 0); + o(0x17 | (10 << 7)); // auipc a0, 0 %pcrel_hi(sym)+addend + greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0); + EI(0x03, 3, 10, 10, 0); // ld a0, 0(a0) + gen_bounds_call(TOK___bound_local_delete); + o(0x65a26502); /* ld a0,0(sp) ld a1,8(sp) */ + o(0x61052542); /* fld fa0,16(sp) addi sp,sp,32 */ +} +#endif + +static void reg_pass_rec(CType *type, int *rc, int *fieldofs, int ofs) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + Sym *f; + if (type->ref->type.t == VT_UNION) + rc[0] = -1; + else for (f = type->ref->next; f; f = f->next) + reg_pass_rec(&f->type, rc, fieldofs, ofs + f->c); + } else if (type->t & VT_ARRAY) { + if (type->ref->c < 0 || type->ref->c > 2) + rc[0] = -1; + else { + int a, sz = type_size(&type->ref->type, &a); + reg_pass_rec(&type->ref->type, rc, fieldofs, ofs); + if (rc[0] > 2 || (rc[0] == 2 && type->ref->c > 1)) + rc[0] = -1; + else if (type->ref->c == 2 && rc[0] && rc[1] == RC_FLOAT) { + rc[++rc[0]] = RC_FLOAT; + fieldofs[rc[0]] = ((ofs + sz) << 4) + | (type->ref->type.t & VT_BTYPE); + } else if (type->ref->c == 2) + rc[0] = -1; + } + } else if (rc[0] == 2 || rc[0] < 0 || (type->t & VT_BTYPE) == VT_LDOUBLE) + rc[0] = -1; + else if (!rc[0] || rc[1] == RC_FLOAT || is_float(type->t)) { + rc[++rc[0]] = is_float(type->t) ? RC_FLOAT : RC_INT; + fieldofs[rc[0]] = (ofs << 4) | ((type->t & VT_BTYPE) == VT_PTR ? VT_LLONG : type->t & VT_BTYPE); + } else + rc[0] = -1; +} + +static void reg_pass(CType *type, int *prc, int *fieldofs, int named) +{ + prc[0] = 0; + reg_pass_rec(type, prc, fieldofs, 0); + if (prc[0] <= 0 || !named) { + int align, size = type_size(type, &align); + prc[0] = (size + 7) >> 3; + prc[1] = prc[2] = RC_INT; + fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG); + fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG); + } +} + +ST_FUNC void gfunc_call(int nb_args) +{ + int i, align, size, areg[2]; + int *info = tcc_malloc((nb_args + 1) * sizeof (int)); + int stack_adj = 0, tempspace = 0, stack_add, ofs, splitofs = 0; + SValue *sv; + Sym *sa; + +#ifdef CONFIG_TCC_BCHECK + int bc_save = tcc_state->do_bounds_check; + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + + areg[0] = 0; /* int arg regs */ + areg[1] = 8; /* float arg regs */ + sa = vtop[-nb_args].type.ref->next; + for (i = 0; i < nb_args; i++) { + int nregs, byref = 0, tempofs; + int prc[3], fieldofs[3]; + sv = &vtop[1 + i - nb_args]; + sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c + size = type_size(&sv->type, &align); + if (size > 16) { + if (align < XLEN) + align = XLEN; + tempspace = (tempspace + align - 1) & -align; + tempofs = tempspace; + tempspace += size; + size = align = 8; + byref = 64 | (tempofs << 7); + } + reg_pass(&sv->type, prc, fieldofs, sa != 0); + if (!sa && align == 2*XLEN && size <= 2*XLEN) + areg[0] = (areg[0] + 1) & ~1; + nregs = prc[0]; + if (size == 0) + info[i] = 0; + else if ((prc[1] == RC_INT && areg[0] >= 8) + || (prc[1] == RC_FLOAT && areg[1] >= 16) + || (nregs == 2 && prc[1] == RC_FLOAT && prc[2] == RC_FLOAT + && areg[1] >= 15) + || (nregs == 2 && prc[1] != prc[2] + && (areg[1] >= 16 || areg[0] >= 8))) { + info[i] = 32; + if (align < XLEN) + align = XLEN; + stack_adj += (size + align - 1) & -align; + if (!sa) /* one vararg on stack forces the rest on stack */ + areg[0] = 8, areg[1] = 16; + } else { + info[i] = areg[prc[1] - 1]++; + if (!byref) + info[i] |= (fieldofs[1] & VT_BTYPE) << 12; + assert(!(fieldofs[1] >> 4)); + if (nregs == 2) { + if (prc[2] == RC_FLOAT || areg[0] < 8) + info[i] |= (1 + areg[prc[2] - 1]++) << 7; + else { + info[i] |= 16; + stack_adj += 8; + } + if (!byref) { + assert((fieldofs[2] >> 4) < 2048); + info[i] |= fieldofs[2] << (12 + 4); // includes offset + } + } + } + info[i] |= byref; + if (sa) + sa = sa->next; + } + stack_adj = (stack_adj + 15) & -16; + tempspace = (tempspace + 15) & -16; + stack_add = stack_adj + tempspace; + + /* fetch cpu flag before generating any code */ + if ((vtop->r & VT_VALMASK) == VT_CMP) + gv(RC_INT); + + + if (stack_add) { + if (stack_add >= 0x800) { + unsigned int bit11 = (((unsigned int)-stack_add) >> 11) & 1; + o(0x37 | (5 << 7) | + ((-stack_add + (bit11 << 12)) & 0xfffff000)); //lui t0, upper(v) + EI(0x13, 0, 5, 5, ((-stack_add & 0xfff) - bit11 * (1 << 12))); + // addi t0, t0, lo(v) + ER(0x33, 0, 2, 2, 5, 0); // add sp, sp, t0 + } + else + EI(0x13, 0, 2, 2, -stack_add); // addi sp, sp, -adj + for (i = ofs = 0; i < nb_args; i++) { + if (info[i] & (64 | 32)) { + vrotb(nb_args - i); + size = type_size(&vtop->type, &align); + if (info[i] & 64) { + vset(&char_pointer_type, TREG_SP, 0); + vpushi(stack_adj + (info[i] >> 7)); + gen_op('+'); + vpushv(vtop); // this replaces the old argument + vrott(3); + indir(); + vtop->type = vtop[-1].type; + vswap(); + vstore(); + vpop(); + size = align = 8; + } + if (info[i] & 32) { + if (align < XLEN) + align = XLEN; + /* Once we support offseted regs we can do this: + vset(&vtop->type, TREG_SP | VT_LVAL, ofs); + to construct the lvalue for the outgoing stack slot, + until then we have to jump through hoops. */ + vset(&char_pointer_type, TREG_SP, 0); + ofs = (ofs + align - 1) & -align; + vpushi(ofs); + gen_op('+'); + indir(); + vtop->type = vtop[-1].type; + vswap(); + vstore(); + vtop->r = vtop->r2 = VT_CONST; // this arg is done + ofs += size; + } + vrott(nb_args - i); + } else if (info[i] & 16) { + assert(!splitofs); + splitofs = ofs; + ofs += 8; + } + } + } + for (i = 0; i < nb_args; i++) { + int ii = info[nb_args - 1 - i], r = ii, r2 = r; + if (!(r & 32)) { + CType origtype; + int loadt; + r &= 15; + r2 = r2 & 64 ? 0 : (r2 >> 7) & 31; + assert(r2 <= 16); + vrotb(i+1); + origtype = vtop->type; + size = type_size(&vtop->type, &align); + if (size == 0) + goto done; + loadt = vtop->type.t & VT_BTYPE; + if (loadt == VT_STRUCT) { + loadt = (ii >> 12) & VT_BTYPE; + } + if (info[nb_args - 1 - i] & 16) { + assert(!r2); + r2 = 1 + TREG_RA; + } + if (loadt == VT_LDOUBLE) { + assert(r2); + r2--; + } else if (r2) { + test_lvalue(); + vpushv(vtop); + } + vtop->type.t = loadt | (vtop->type.t & VT_UNSIGNED); + gv(r < 8 ? RC_R(r) : RC_F(r - 8)); + vtop->type = origtype; + + if (r2 && loadt != VT_LDOUBLE) { + r2--; + assert(r2 < 16 || r2 == TREG_RA); + vswap(); + gaddrof(); + vtop->type = char_pointer_type; + vpushi(ii >> 20); +#ifdef CONFIG_TCC_BCHECK + if ((origtype.t & VT_BTYPE) == VT_STRUCT) + tcc_state->do_bounds_check = 0; +#endif + gen_op('+'); +#ifdef CONFIG_TCC_BCHECK + tcc_state->do_bounds_check = bc_save; +#endif + indir(); + vtop->type = origtype; + loadt = vtop->type.t & VT_BTYPE; + if (loadt == VT_STRUCT) { + loadt = (ii >> 16) & VT_BTYPE; + } + save_reg_upstack(r2, 1); + vtop->type.t = loadt | (vtop->type.t & VT_UNSIGNED); + load(r2, vtop); + assert(r2 < VT_CONST); + vtop--; + vtop->r2 = r2; + } + if (info[nb_args - 1 - i] & 16) { + ES(0x23, 3, 2, ireg(vtop->r2), splitofs); // sd t0, ofs(sp) + vtop->r2 = VT_CONST; + } else if (loadt == VT_LDOUBLE && vtop->r2 != r2) { + assert(vtop->r2 <= 7 && r2 <= 7); + /* XXX we'd like to have 'gv' move directly into + the right class instead of us fixing it up. */ + EI(0x13, 0, ireg(r2), ireg(vtop->r2), 0); // mv Ra+1, RR2 + vtop->r2 = r2; + } +done: + vrott(i+1); + } + } + vrotb(nb_args + 1); + save_regs(nb_args + 1); + gcall_or_jmp(1); + vtop -= nb_args + 1; + if (stack_add) { + if (stack_add >= 0x800) { + unsigned int bit11 = ((unsigned int)stack_add >> 11) & 1; + o(0x37 | (5 << 7) | + ((stack_add + (bit11 << 12)) & 0xfffff000)); //lui t0, upper(v) + EI(0x13, 0, 5, 5, (stack_add & 0xfff) - bit11 * (1 << 12)); + // addi t0, t0, lo(v) + ER(0x33, 0, 2, 2, 5, 0); // add sp, sp, t0 + } + else + EI(0x13, 0, 2, 2, stack_add); // addi sp, sp, adj + } + tcc_free(info); +} + +static int func_sub_sp_offset, num_va_regs, func_va_list_ofs; + +ST_FUNC void gfunc_prolog(Sym *func_sym) +{ + CType *func_type = &func_sym->type; + int i, addr, align, size; + int param_addr = 0; + int areg[2]; + Sym *sym; + CType *type; + + sym = func_type->ref; + loc = -16; // for ra and s0 + func_sub_sp_offset = ind; + ind += 5 * 4; + + areg[0] = 0, areg[1] = 0; + addr = 0; + /* if the function returns by reference, then add an + implicit pointer parameter */ + size = type_size(&func_vt, &align); + if (size > 2 * XLEN) { + loc -= 8; + func_vc = loc; + ES(0x23, 3, 8, 10 + areg[0]++, loc); // sd a0, loc(s0) + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + int byref = 0; + int regcount; + int prc[3], fieldofs[3]; + type = &sym->type; + size = type_size(type, &align); + if (size > 2 * XLEN) { + type = &char_pointer_type; + size = align = byref = 8; + } + reg_pass(type, prc, fieldofs, 1); + regcount = prc[0]; + if (areg[prc[1] - 1] >= 8 + || (regcount == 2 + && ((prc[1] == RC_FLOAT && prc[2] == RC_FLOAT && areg[1] >= 7) + || (prc[1] != prc[2] && (areg[1] >= 8 || areg[0] >= 8))))) { + if (align < XLEN) + align = XLEN; + addr = (addr + align - 1) & -align; + param_addr = addr; + addr += size; + } else { + loc -= regcount * 8; // XXX could reserve only 'size' bytes + param_addr = loc; + for (i = 0; i < regcount; i++) { + if (areg[prc[1+i] - 1] >= 8) { + assert(i == 1 && regcount == 2 && !(addr & 7)); + EI(0x03, 3, 5, 8, addr); // ld t0, addr(s0) + addr += 8; + ES(0x23, 3, 8, 5, loc + i*8); // sd t0, loc(s0) + } else if (prc[1+i] == RC_FLOAT) { + ES(0x27, (size / regcount) == 4 ? 2 : 3, 8, 10 + areg[1]++, loc + (fieldofs[i+1] >> 4)); // fs[wd] FAi, loc(s0) + } else { + ES(0x23, 3, 8, 10 + areg[0]++, loc + i*8); // sd aX, loc(s0) // XXX + } + } + } + sym_push(sym->v & ~SYM_FIELD, &sym->type, + (byref ? VT_LLOCAL : VT_LOCAL) | VT_LVAL, + param_addr); + } + func_va_list_ofs = addr; + num_va_regs = 0; + if (func_var) { + for (; areg[0] < 8; areg[0]++) { + num_va_regs++; + ES(0x23, 3, 8, 10 + areg[0], -8 + num_va_regs * 8); // sd aX, loc(s0) + } + } +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif +} + +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, + int *ret_align, int *regsize) +{ + int align, size = type_size(vt, &align), nregs; + int prc[3], fieldofs[3]; + *ret_align = 1; + *regsize = 8; + if (size > 16) + return 0; + reg_pass(vt, prc, fieldofs, 1); + nregs = prc[0]; + if (nregs == 2 && prc[1] != prc[2]) + return -1; /* generic code can't deal with this case */ + if (prc[1] == RC_FLOAT) { + *regsize = size / nregs; + } + ret->t = fieldofs[1] & VT_BTYPE; + ret->ref = NULL; + return nregs; +} + +ST_FUNC void arch_transfer_ret_regs(int aftercall) +{ + int prc[3], fieldofs[3]; + reg_pass(&vtop->type, prc, fieldofs, 1); + assert(prc[0] == 2 && prc[1] != prc[2] && !(fieldofs[1] >> 4)); + assert(vtop->r == (VT_LOCAL | VT_LVAL)); + vpushv(vtop); + vtop->type.t = fieldofs[1] & VT_BTYPE; + (aftercall ? store : load)(prc[1] == RC_INT ? REG_IRET : REG_FRET, vtop); + vtop->c.i += fieldofs[2] >> 4; + vtop->type.t = fieldofs[2] & VT_BTYPE; + (aftercall ? store : load)(prc[2] == RC_INT ? REG_IRET : REG_FRET, vtop); + vtop--; +} + +ST_FUNC void gfunc_epilog(void) +{ + int v, saved_ind, d, large_ofs_ind; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif + + loc = (loc - num_va_regs * 8); + d = v = (-loc + 15) & -16; + + if (v >= (1 << 11)) { + d = 16; + o(0x37 | (5 << 7) | ((0x800 + (v-16)) & 0xfffff000)); //lui t0, upper(v) + EI(0x13, 0, 5, 5, (v-16) << 20 >> 20); // addi t0, t0, lo(v) + ER(0x33, 0, 2, 2, 5, 0); // add sp, sp, t0 + } + EI(0x03, 3, 1, 2, d - 8 - num_va_regs * 8); // ld ra, v-8(sp) + EI(0x03, 3, 8, 2, d - 16 - num_va_regs * 8); // ld s0, v-16(sp) + EI(0x13, 0, 2, 2, d); // addi sp, sp, v + EI(0x67, 0, 0, 1, 0); // jalr x0, 0(x1), aka ret + large_ofs_ind = ind; + if (v >= (1 << 11)) { + EI(0x13, 0, 8, 2, d - num_va_regs * 8); // addi s0, sp, d + o(0x37 | (5 << 7) | ((0x800 + (v-16)) & 0xfffff000)); //lui t0, upper(v) + EI(0x13, 0, 5, 5, (v-16) << 20 >> 20); // addi t0, t0, lo(v) + ER(0x33, 0, 2, 2, 5, 0x20); // sub sp, sp, t0 + gjmp_addr(func_sub_sp_offset + 5*4); + } + saved_ind = ind; + + ind = func_sub_sp_offset; + EI(0x13, 0, 2, 2, -d); // addi sp, sp, -d + ES(0x23, 3, 2, 1, d - 8 - num_va_regs * 8); // sd ra, d-8(sp) + ES(0x23, 3, 2, 8, d - 16 - num_va_regs * 8); // sd s0, d-16(sp) + if (v < (1 << 11)) + EI(0x13, 0, 8, 2, d - num_va_regs * 8); // addi s0, sp, d + else + gjmp_addr(large_ofs_ind); + if ((ind - func_sub_sp_offset) != 5*4) + EI(0x13, 0, 0, 0, 0); // addi x0, x0, 0 == nop + ind = saved_ind; +} + +ST_FUNC void gen_va_start(void) +{ + vtop--; + vset(&char_pointer_type, VT_LOCAL, func_va_list_ofs); +} + +ST_FUNC void gen_fill_nops(int bytes) +{ + if ((bytes & 3)) + tcc_error("alignment of code section not multiple of 4"); + while (bytes > 0) { + EI(0x13, 0, 0, 0, 0); // addi x0, x0, 0 == nop + bytes -= 4; + } +} + +// Generate forward branch to label: +ST_FUNC int gjmp(int t) +{ + if (nocode_wanted) + return t; + o(t); + return ind - 4; +} + +// Generate branch to known address: +ST_FUNC void gjmp_addr(int a) +{ + uint32_t r = a - ind, imm; + if ((r + (1 << 21)) & ~((1U << 22) - 2)) { + o(0x17 | (5 << 7) | (((r + 0x800) & 0xfffff000))); // lui RR, up(r) + r = (int)r << 20 >> 20; + EI(0x67, 0, 0, 5, r); // jalr x0, r(t0) + } else { + imm = (((r >> 12) & 0xff) << 12) + | (((r >> 11) & 1) << 20) + | (((r >> 1) & 0x3ff) << 21) + | (((r >> 20) & 1) << 31); + o(0x6f | imm); // jal x0, imm == j imm + } +} + +ST_FUNC int gjmp_cond(int op, int t) +{ + int tmp; + int a = vtop->cmp_r & 0xff; + int b = (vtop->cmp_r >> 8) & 0xff; + switch (op) { + case TOK_ULT: op = 6; break; + case TOK_UGE: op = 7; break; + case TOK_ULE: op = 7; tmp = a; a = b; b = tmp; break; + case TOK_UGT: op = 6; tmp = a; a = b; b = tmp; break; + case TOK_LT: op = 4; break; + case TOK_GE: op = 5; break; + case TOK_LE: op = 5; tmp = a; a = b; b = tmp; break; + case TOK_GT: op = 4; tmp = a; a = b; b = tmp; break; + case TOK_NE: op = 1; break; + case TOK_EQ: op = 0; break; + } + o(0x63 | (op ^ 1) << 12 | a << 15 | b << 20 | 8 << 7); // bOP a,b,+4 + return gjmp(t); +} + +ST_FUNC int gjmp_append(int n, int t) +{ + void *p; + /* insert jump list n into t */ + if (n) { + uint32_t n1 = n, n2; + while ((n2 = read32le(p = cur_text_section->data + n1))) + n1 = n2; + write32le(p, t); + t = n; + } + return t; +} + +static void gen_opil(int op, int ll) +{ + int a, b, d; + int func3 = 0; + ll = ll ? 0 : 8; + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + int fc = vtop->c.i; + if (fc == vtop->c.i && !(((unsigned)fc + (1 << 11)) >> 12)) { + int cll = 0; + int m = ll ? 31 : 63; + vswap(); + gv(RC_INT); + a = ireg(vtop[0].r); + --vtop; + d = get_reg(RC_INT); + ++vtop; + vswap(); + switch (op) { + case '-': + if (fc <= -(1 << 11)) + break; + fc = -fc; + case '+': + func3 = 0; // addi d, a, fc + cll = ll; + do_cop: + EI(0x13 | cll, func3, ireg(d), a, fc); + --vtop; + if (op >= TOK_ULT && op <= TOK_GT) { + vset_VT_CMP(TOK_NE); + vtop->cmp_r = ireg(d) | 0 << 8; + } else + vtop[0].r = d; + return; + case TOK_LE: + if (fc >= (1 << 11) - 1) + break; + ++fc; + case TOK_LT: func3 = 2; goto do_cop; // slti d, a, fc + case TOK_ULE: + if (fc >= (1 << 11) - 1 || fc == -1) + break; + ++fc; + case TOK_ULT: func3 = 3; goto do_cop; // sltiu d, a, fc + case '^': func3 = 4; goto do_cop; // xori d, a, fc + case '|': func3 = 6; goto do_cop; // ori d, a, fc + case '&': func3 = 7; goto do_cop; // andi d, a, fc + case TOK_SHL: func3 = 1; cll = ll; fc &= m; goto do_cop; // slli d, a, fc + case TOK_SHR: func3 = 5; cll = ll; fc &= m; goto do_cop; // srli d, a, fc + case TOK_SAR: func3 = 5; cll = ll; fc = 1024 | (fc & m); goto do_cop; + + case TOK_UGE: /* -> TOK_ULT */ + case TOK_UGT: /* -> TOK_ULE */ + case TOK_GE: /* -> TOK_LT */ + case TOK_GT: /* -> TOK_LE */ + gen_opil(op - 1, !ll); + vtop->cmp_op ^= 1; + return; + + case TOK_NE: + case TOK_EQ: + if (fc) + gen_opil('-', !ll), a = ireg(vtop++->r); + --vtop; + vset_VT_CMP(op); + vtop->cmp_r = a | 0 << 8; + return; + } + } + } + gv2(RC_INT, RC_INT); + a = ireg(vtop[-1].r); + b = ireg(vtop[0].r); + vtop -= 2; + d = get_reg(RC_INT); + vtop++; + vtop[0].r = d; + d = ireg(d); + switch (op) { + default: + if (op >= TOK_ULT && op <= TOK_GT) { + vset_VT_CMP(op); + vtop->cmp_r = a | b << 8; + break; + } + tcc_error("implement me: %s(%s)", __FUNCTION__, get_tok_str(op, NULL)); + break; + + case '+': + ER(0x33 | ll, 0, d, a, b, 0); // add d, a, b + break; + case '-': + ER(0x33 | ll, 0, d, a, b, 0x20); // sub d, a, b + break; + case TOK_SAR: + ER(0x33 | ll | ll, 5, d, a, b, 0x20); // sra d, a, b + break; + case TOK_SHR: + ER(0x33 | ll | ll, 5, d, a, b, 0); // srl d, a, b + break; + case TOK_SHL: + ER(0x33 | ll, 1, d, a, b, 0); // sll d, a, b + break; + case '*': + ER(0x33 | ll, 0, d, a, b, 1); // mul d, a, b + break; + case '/': + ER(0x33 | ll, 4, d, a, b, 1); // div d, a, b + break; + case '&': + ER(0x33, 7, d, a, b, 0); // and d, a, b + break; + case '^': + ER(0x33, 4, d, a, b, 0); // xor d, a, b + break; + case '|': + ER(0x33, 6, d, a, b, 0); // or d, a, b + break; + case '%': + ER(ll ? 0x3b: 0x33, 6, d, a, b, 1); // rem d, a, b + break; + case TOK_UMOD: + ER(0x33 | ll, 7, d, a, b, 1); // remu d, a, b + break; + case TOK_PDIV: + case TOK_UDIV: + ER(0x33 | ll, 5, d, a, b, 1); // divu d, a, b + break; + } +} + +ST_FUNC void gen_opi(int op) +{ + gen_opil(op, 0); +} + +ST_FUNC void gen_opl(int op) +{ + gen_opil(op, 1); +} + +ST_FUNC void gen_opf(int op) +{ + int rs1, rs2, rd, dbl, invert; + if (vtop[0].type.t == VT_LDOUBLE) { + CType type = vtop[0].type; + int func = 0; + int cond = -1; + switch (op) { + case '*': func = TOK___multf3; break; + case '+': func = TOK___addtf3; break; + case '-': func = TOK___subtf3; break; + case '/': func = TOK___divtf3; break; + case TOK_EQ: func = TOK___eqtf2; cond = 1; break; + case TOK_NE: func = TOK___netf2; cond = 0; break; + case TOK_LT: func = TOK___lttf2; cond = 10; break; + case TOK_GE: func = TOK___getf2; cond = 11; break; + case TOK_LE: func = TOK___letf2; cond = 12; break; + case TOK_GT: func = TOK___gttf2; cond = 13; break; + default: assert(0); break; + } + vpush_helper_func(func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = cond < 0 ? TREG_R(1) : VT_CONST; + if (cond < 0) + vtop->type = type; + else { + vpushi(0); + gen_opil(op, 1); + } + return; + } + + gv2(RC_FLOAT, RC_FLOAT); + assert(vtop->type.t == VT_DOUBLE || vtop->type.t == VT_FLOAT); + dbl = vtop->type.t == VT_DOUBLE; + rs1 = freg(vtop[-1].r); + rs2 = freg(vtop->r); + vtop--; + invert = 0; + switch(op) { + default: + assert(0); + case '+': + op = 0; // fadd + arithop: + rd = get_reg(RC_FLOAT); + vtop->r = rd; + rd = freg(rd); + ER(0x53, 7, rd, rs1, rs2, dbl | (op << 2)); // fop.[sd] RD, RS1, RS2 (dyn rm) + break; + case '-': + op = 1; // fsub + goto arithop; + case '*': + op = 2; // fmul + goto arithop; + case '/': + op = 3; // fdiv + goto arithop; + case TOK_EQ: + op = 2; // EQ + cmpop: + rd = get_reg(RC_INT); + vtop->r = rd; + rd = ireg(rd); + ER(0x53, op, rd, rs1, rs2, dbl | 0x50); // fcmp.[sd] RD, RS1, RS2 (op == eq/lt/le) + if (invert) + EI(0x13, 4, rd, rd, 1); // xori RD, 1 + break; + case TOK_NE: + invert = 1; + op = 2; // EQ + goto cmpop; + case TOK_LT: + op = 1; // LT + goto cmpop; + case TOK_LE: + op = 0; // LE + goto cmpop; + case TOK_GT: + op = 1; // LT + rd = rs1, rs1 = rs2, rs2 = rd; + goto cmpop; + case TOK_GE: + op = 0; // LE + rd = rs1, rs1 = rs2, rs2 = rd; + goto cmpop; + } +} + +ST_FUNC void gen_cvt_sxtw(void) +{ + /* XXX on risc-v the registers are usually sign-extended already. + Let's try to not do anything here. */ +} + +ST_FUNC void gen_cvt_itof(int t) +{ + int rr = ireg(gv(RC_INT)), dr; + int u = vtop->type.t & VT_UNSIGNED; + int l = (vtop->type.t & VT_BTYPE) == VT_LLONG; + if (t == VT_LDOUBLE) { + int func = l ? + (u ? TOK___floatunditf : TOK___floatditf) : + (u ? TOK___floatunsitf : TOK___floatsitf); + vpush_helper_func(func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_IRET; + vtop->r2 = TREG_R(1); + } else { + vtop--; + dr = get_reg(RC_FLOAT); + vtop++; + vtop->r = dr; + dr = freg(dr); + EIu(0x53, 7, dr, rr, ((0x68 | (t == VT_DOUBLE ? 1 : 0)) << 5) | (u ? 1 : 0) | (l ? 2 : 0)); // fcvt.[sd].[wl][u] + } +} + +ST_FUNC void gen_cvt_ftoi(int t) +{ + int ft = vtop->type.t & VT_BTYPE; + int l = (t & VT_BTYPE) == VT_LLONG; + int u = t & VT_UNSIGNED; + if (ft == VT_LDOUBLE) { + int func = l ? + (u ? TOK___fixunstfdi : TOK___fixtfdi) : + (u ? TOK___fixunstfsi : TOK___fixtfsi); + vpush_helper_func(func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_IRET; + } else { + int rr = freg(gv(RC_FLOAT)), dr; + vtop--; + dr = get_reg(RC_INT); + vtop++; + vtop->r = dr; + dr = ireg(dr); + EIu(0x53, 1, dr, rr, ((0x60 | (ft == VT_DOUBLE ? 1 : 0)) << 5) | (u ? 1 : 0) | (l ? 2 : 0)); // fcvt.[wl][u].[sd] rtz + } +} + +ST_FUNC void gen_cvt_ftof(int dt) +{ + int st = vtop->type.t & VT_BTYPE, rs, rd; + dt &= VT_BTYPE; + if (st == dt) + return; + if (dt == VT_LDOUBLE || st == VT_LDOUBLE) { + int func = (dt == VT_LDOUBLE) ? + (st == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) : + (dt == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2); + /* We can't use gfunc_call, as func_old_type works like vararg + functions, and on riscv unnamed float args are passed like + integers. But we really need them in the float argument registers + for extendsftf2/extenddftf2. So, do it explicitely. */ + save_regs(1); + if (dt == VT_LDOUBLE) + gv(RC_F(0)); + else { + gv(RC_R(0)); + assert(vtop->r2 < 7); + if (vtop->r2 != 1 + vtop->r) { + EI(0x13, 0, ireg(vtop->r) + 1, ireg(vtop->r2), 0); // mv Ra+1, RR2 + vtop->r2 = 1 + vtop->r; + } + } + vpush_helper_func(func); + gcall_or_jmp(1); + vtop -= 2; + vpushi(0); + vtop->type.t = dt; + if (dt == VT_LDOUBLE) + vtop->r = REG_IRET, vtop->r2 = REG_IRET+1; + else + vtop->r = REG_FRET; + } else { + assert (dt == VT_FLOAT || dt == VT_DOUBLE); + assert (st == VT_FLOAT || st == VT_DOUBLE); + rs = gv(RC_FLOAT); + rd = get_reg(RC_FLOAT); + if (dt == VT_DOUBLE) + EI(0x53, 0, freg(rd), freg(rs), 0x21 << 5); // fcvt.d.s RD, RS (no rm) + else + EI(0x53, 7, freg(rd), freg(rs), (0x20 << 5) | 1); // fcvt.s.d RD, RS (dyn rm) + vtop->r = rd; + } +} + +/* increment tcov counter */ +ST_FUNC void gen_increment_tcov (SValue *sv) +{ + int r1, r2; + Sym label = {0}; + label.type.t = VT_VOID | VT_STATIC; + + vpushv(sv); + vtop->r = r1 = get_reg(RC_INT); + r2 = get_reg(RC_INT); + r1 = ireg(r1); + r2 = ireg(r2); + greloca(cur_text_section, sv->sym, ind, R_RISCV_PCREL_HI20, 0); + put_extern_sym(&label, cur_text_section, ind, 0); + o(0x17 | (r1 << 7)); // auipc RR, 0 %pcrel_hi(sym) + greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0); + EI(0x03, 3, r2, r1, 0); // ld r2, x[r1] + EI(0x13, 0, r2, r2, 1); // addi r2, r2, #1 + greloca(cur_text_section, sv->sym, ind, R_RISCV_PCREL_HI20, 0); + label.c = 0; /* force new local ELF symbol */ + put_extern_sym(&label, cur_text_section, ind, 0); + o(0x17 | (r1 << 7)); // auipc RR, 0 %pcrel_hi(sym) + greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_S, 0); + ES(0x23, 3, r1, r2, 0); // sd r2, [r1] + vpop(); +} + +ST_FUNC void ggoto(void) +{ + gcall_or_jmp(0); + vtop--; +} + +ST_FUNC void gen_vla_sp_save(int addr) +{ + if (((unsigned)addr + (1 << 11)) >> 12) { + o(0x37 | (5 << 7) | ((0x800 + addr) & 0xfffff000)); //lui t0,upper(addr) + ER(0x33, 0, 5, 5, 8, 0); // add t0, t0, s0 + ES(0x23, 3, 5, 2, (int)addr << 20 >> 20); // sd sp, fc(t0) + } + else + ES(0x23, 3, 8, 2, addr); // sd sp, fc(s0) +} + +ST_FUNC void gen_vla_sp_restore(int addr) +{ + if (((unsigned)addr + (1 << 11)) >> 12) { + o(0x37 | (5 << 7) | ((0x800 + addr) & 0xfffff000)); //lui t0,upper(addr) + ER(0x33, 0, 5, 5, 8, 0); // add t0, t0, s0 + EI(0x03, 3, 2, 5, (int)addr << 20 >> 20); // ld sp, fc(t0) + } + else + EI(0x03, 3, 2, 8, addr); // ld sp, fc(s0) +} + +ST_FUNC void gen_vla_alloc(CType *type, int align) +{ + int rr; +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) + vpushv(vtop); +#endif + rr = ireg(gv(RC_INT)); +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) + EI(0x13, 0, rr, rr, 15+1); // addi RR, RR, 15+1 + else +#endif + EI(0x13, 0, rr, rr, 15); // addi RR, RR, 15 + EI(0x13, 7, rr, rr, -16); // andi, RR, RR, -16 + ER(0x33, 0, 2, 2, rr, 0x20); // sub sp, sp, rr + vpop(); +#if defined(CONFIG_TCC_BCHECK) + if (tcc_state->do_bounds_check) { + vpushi(0); + vtop->r = TREG_R(0); + o(0x00010513); /* mv a0,sp */ + vswap(); + vpush_helper_func(TOK___bound_new_region); + vrott(3); + gfunc_call(2); + func_bound_add_epilog = 1; + } +#endif +} +#endif diff --git a/vendor/tcc/riscv64-link.c b/vendor/tcc/riscv64-link.c new file mode 100644 index 00000000..39c0c5d2 --- /dev/null +++ b/vendor/tcc/riscv64-link.c @@ -0,0 +1,367 @@ +#ifdef TARGET_DEFS_ONLY + +#define EM_TCC_TARGET EM_RISCV + +#define R_DATA_32 R_RISCV_32 +#define R_DATA_PTR R_RISCV_64 +#define R_JMP_SLOT R_RISCV_JUMP_SLOT +#define R_GLOB_DAT R_RISCV_64 +#define R_COPY R_RISCV_COPY +#define R_RELATIVE R_RISCV_RELATIVE + +#define R_NUM R_RISCV_NUM + +#define ELF_START_ADDR 0x00010000 +#define ELF_PAGE_SIZE 0x1000 + +#define PCRELATIVE_DLLPLT 1 +#define RELOCATE_DLLPLT 1 + +#else /* !TARGET_DEFS_ONLY */ + +//#define DEBUG_RELOC +#include "tcc.h" + +/* Returns 1 for a code relocation, 0 for a data relocation. For unknown + relocations, returns -1. */ +int code_reloc (int reloc_type) +{ + switch (reloc_type) { + + case R_RISCV_BRANCH: + case R_RISCV_CALL: + case R_RISCV_JAL: + return 1; + + case R_RISCV_GOT_HI20: + case R_RISCV_PCREL_HI20: + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + case R_RISCV_32_PCREL: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SUB6: + case R_RISCV_ADD16: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SUB8: + case R_RISCV_SUB16: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + case R_RISCV_32: + case R_RISCV_64: + return 0; + + case R_RISCV_CALL_PLT: + return 1; + } + return -1; +} + +/* Returns an enumerator to describe whether and when the relocation needs a + GOT and/or PLT entry to be created. See tcc.h for a description of the + different values. */ +int gotplt_entry_type (int reloc_type) +{ + switch (reloc_type) { + case R_RISCV_ALIGN: + case R_RISCV_RELAX: + case R_RISCV_RVC_BRANCH: + case R_RISCV_RVC_JUMP: + case R_RISCV_JUMP_SLOT: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SUB6: + case R_RISCV_ADD16: + case R_RISCV_SUB8: + case R_RISCV_SUB16: + return NO_GOTPLT_ENTRY; + + case R_RISCV_BRANCH: + case R_RISCV_CALL: + case R_RISCV_PCREL_HI20: + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + case R_RISCV_32_PCREL: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + case R_RISCV_32: + case R_RISCV_64: + case R_RISCV_JAL: + case R_RISCV_CALL_PLT: + return AUTO_GOTPLT_ENTRY; + + case R_RISCV_GOT_HI20: + return ALWAYS_GOTPLT_ENTRY; + } + 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 (plt->data_offset == 0) + section_ptr_add(plt, 32); + plt_offset = plt->data_offset; + + p = section_ptr_add(plt, 16); + write64le(p, 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) { + uint64_t plt = s1->plt->sh_addr; + uint64_t got = s1->got->sh_addr; + uint64_t off = (got - plt + 0x800) >> 12; + if ((off + ((uint32_t)1 << 20)) >> 21) + tcc_error_noabort("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", (long)off, (long)got, (long)plt); + write32le(p, 0x397 | (off << 12)); // auipc t2, %pcrel_hi(got) + write32le(p + 4, 0x41c30333); // sub t1, t1, t3 + write32le(p + 8, 0x0003be03 // ld t3, %pcrel_lo(got)(t2) + | (((got - plt) & 0xfff) << 20)); + write32le(p + 12, 0xfd430313); // addi t1, t1, -(32+12) + write32le(p + 16, 0x00038293 // addi t0, t2, %pcrel_lo(got) + | (((got - plt) & 0xfff) << 20)); + write32le(p + 20, 0x00135313); // srli t1, t1, log2(16/PTRSIZE) + write32le(p + 24, 0x0082b283); // ld t0, PTRSIZE(t0) + write32le(p + 28, 0x000e0067); // jr t3 + p += 32; + while (p < p_end) { + uint64_t pc = plt + (p - s1->plt->data); + uint64_t addr = got + read64le(p); + uint64_t off = (addr - pc + 0x800) >> 12; + if ((off + ((uint32_t)1 << 20)) >> 21) + tcc_error_noabort("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", (long)off, (long)addr, (long)pc); + write32le(p, 0xe17 | (off << 12)); // auipc t3, %pcrel_hi(func@got) + write32le(p + 4, 0x000e3e03 // ld t3, %pcrel_lo(func@got)(t3) + | (((addr - pc) & 0xfff) << 20)); + write32le(p + 8, 0x000e0367); // jalr t1, t3 + write32le(p + 12, 0x00000013); // nop + p += 16; + } + } + + if (s1->plt->reloc) { + ElfW_Rel *rel; + p = s1->got->data; + for_each_elem(s1->plt->reloc, 0, rel, ElfW_Rel) { + write64le(p + rel->r_offset, s1->plt->sh_addr); + } + } +} + +void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, + addr_t addr, addr_t val) +{ + uint64_t off64; + uint32_t off32; + int sym_index = ELFW(R_SYM)(rel->r_info), esym_index; + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + + switch(type) { + case R_RISCV_ALIGN: + case R_RISCV_RELAX: + return; + + case R_RISCV_BRANCH: + off64 = val - addr; + if ((off64 + (1 << 12)) & ~(uint64_t)0x1ffe) + tcc_error_noabort("R_RISCV_BRANCH relocation failed" + " (val=%lx, addr=%lx)", (long)val, (long)addr); + off32 = off64 >> 1; + write32le(ptr, (read32le(ptr) & ~0xfe000f80) + | ((off32 & 0x800) << 20) + | ((off32 & 0x3f0) << 21) + | ((off32 & 0x00f) << 8) + | ((off32 & 0x400) >> 3)); + return; + case R_RISCV_JAL: + off64 = val - addr; + if ((off64 + (1 << 21)) & ~(((uint64_t)1 << 22) - 2)) + tcc_error_noabort("R_RISCV_JAL relocation failed" + " (val=%lx, addr=%lx)", (long)val, (long)addr); + off32 = off64; + write32le(ptr, (read32le(ptr) & 0xfff) + | (((off32 >> 12) & 0xff) << 12) + | (((off32 >> 11) & 1) << 20) + | (((off32 >> 1) & 0x3ff) << 21) + | (((off32 >> 20) & 1) << 31)); + return; + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + write32le(ptr, (read32le(ptr) & 0xfff) + | ((val - addr + 0x800) & ~0xfff)); + write32le(ptr + 4, (read32le(ptr + 4) & 0xfffff) + | (((val - addr) & 0xfff) << 20)); + return; + case R_RISCV_PCREL_HI20: +#ifdef DEBUG_RELOC + printf("PCREL_HI20: val=%lx addr=%lx\n", (long)val, (long)addr); +#endif + off64 = (int64_t)(val - addr + 0x800) >> 12; + if ((off64 + ((uint64_t)1 << 20)) >> 21) + tcc_error_noabort("R_RISCV_PCREL_HI20 relocation failed: off=%lx cond=%lx sym=%s", + (long)off64, (long)((int64_t)(off64 + ((uint64_t)1 << 20)) >> 21), + symtab_section->link->data + sym->st_name); + write32le(ptr, (read32le(ptr) & 0xfff) + | ((off64 & 0xfffff) << 12)); + last_hi.addr = addr; + last_hi.val = val; + return; + case R_RISCV_GOT_HI20: + val = s1->got->sh_addr + get_sym_attr(s1, sym_index, 0)->got_offset; + off64 = (int64_t)(val - addr + 0x800) >> 12; + if ((off64 + ((uint64_t)1 << 20)) >> 21) + tcc_error_noabort("R_RISCV_GOT_HI20 relocation failed"); + last_hi.addr = addr; + last_hi.val = val; + write32le(ptr, (read32le(ptr) & 0xfff) + | ((off64 & 0xfffff) << 12)); + return; + case R_RISCV_PCREL_LO12_I: +#ifdef DEBUG_RELOC + printf("PCREL_LO12_I: val=%lx addr=%lx\n", (long)val, (long)addr); +#endif + if (val != last_hi.addr) + tcc_error_noabort("unsupported hi/lo pcrel reloc scheme"); + val = last_hi.val; + addr = last_hi.addr; + write32le(ptr, (read32le(ptr) & 0xfffff) + | (((val - addr) & 0xfff) << 20)); + return; + case R_RISCV_PCREL_LO12_S: + if (val != last_hi.addr) + tcc_error_noabort("unsupported hi/lo pcrel reloc scheme"); + val = last_hi.val; + addr = last_hi.addr; + off32 = val - addr; + write32le(ptr, (read32le(ptr) & ~0xfe000f80) + | ((off32 & 0xfe0) << 20) + | ((off32 & 0x01f) << 7)); + return; + + case R_RISCV_RVC_BRANCH: + off64 = (val - addr); + if ((off64 + (1 << 8)) & ~(uint64_t)0x1fe) + tcc_error_noabort("R_RISCV_RVC_BRANCH relocation failed" + " (val=%lx, addr=%lx)", (long)val, (long)addr); + off32 = off64; + write16le(ptr, (read16le(ptr) & 0xe383) + | (((off32 >> 5) & 1) << 2) + | (((off32 >> 1) & 3) << 3) + | (((off32 >> 6) & 3) << 5) + | (((off32 >> 3) & 3) << 10) + | (((off32 >> 8) & 1) << 12)); + return; + case R_RISCV_RVC_JUMP: + off64 = (val - addr); + if ((off64 + (1 << 11)) & ~(uint64_t)0xffe) + tcc_error_noabort("R_RISCV_RVC_BRANCH relocation failed" + " (val=%lx, addr=%lx)", (long)val, (long)addr); + off32 = off64; + write16le(ptr, (read16le(ptr) & 0xe003) + | (((off32 >> 5) & 1) << 2) + | (((off32 >> 1) & 7) << 3) + | (((off32 >> 7) & 1) << 6) + | (((off32 >> 6) & 1) << 7) + | (((off32 >> 10) & 1) << 8) + | (((off32 >> 8) & 3) << 9) + | (((off32 >> 4) & 1) << 11) + | (((off32 >> 11) & 1) << 12)); + return; + + case R_RISCV_32: + if (s1->output_type & TCC_OUTPUT_DYN) { + /* XXX: this logic may depend on TCC's codegen + now TCC uses R_RISCV_RELATIVE even for a 64bit pointer */ + qrel->r_offset = rel->r_offset; + qrel->r_info = ELFW(R_INFO)(0, R_RISCV_RELATIVE); + /* Use sign extension! */ + qrel->r_addend = (int)read32le(ptr) + val; + qrel++; + } + add32le(ptr, val); + return; + case R_RISCV_64: + if (s1->output_type & TCC_OUTPUT_DYN) { + esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index; + qrel->r_offset = rel->r_offset; + if (esym_index) { + qrel->r_info = ELFW(R_INFO)(esym_index, R_RISCV_64); + qrel->r_addend = rel->r_addend; + qrel++; + break; + } else { + qrel->r_info = ELFW(R_INFO)(0, R_RISCV_RELATIVE); + qrel->r_addend = read64le(ptr) + val; + qrel++; + } + } + case R_RISCV_JUMP_SLOT: + add64le(ptr, val); + return; + case R_RISCV_ADD64: + write64le(ptr, read64le(ptr) + val); + return; + case R_RISCV_ADD32: + write32le(ptr, read32le(ptr) + val); + return; + case R_RISCV_SUB64: + write64le(ptr, read64le(ptr) - val); + return; + case R_RISCV_SUB32: + write32le(ptr, read32le(ptr) - val); + return; + case R_RISCV_ADD16: + write16le(ptr, read16le(ptr) + val); + return; + case R_RISCV_SUB8: + *ptr -= val; + return; + case R_RISCV_SUB16: + write16le(ptr, read16le(ptr) - val); + return; + case R_RISCV_SET6: + *ptr = (*ptr & ~0x3f) | (val & 0x3f); + return; + case R_RISCV_SET8: + *ptr = (*ptr & ~0xff) | (val & 0xff); + return; + case R_RISCV_SET16: + *ptr = (*ptr & ~0xffff) | (val & 0xffff); + return; + case R_RISCV_SUB6: + *ptr = (*ptr & ~0x3f) | ((*ptr - val) & 0x3f); + return; + + case R_RISCV_32_PCREL: + case R_RISCV_COPY: + /* XXX */ + return; + + default: + fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + return; + } +} +#endif diff --git a/vendor/tcc/riscv64-tok.h b/vendor/tcc/riscv64-tok.h new file mode 100644 index 00000000..ecf448ef --- /dev/null +++ b/vendor/tcc/riscv64-tok.h @@ -0,0 +1,412 @@ +/* ------------------------------------------------------------------ */ +/* WARNING: relative order of tokens is important. */ + +/* + * The specifications are available under https://riscv.org/technical/specifications/ + */ + +#define DEF_ASM_WITH_SUFFIX(x, y) \ + DEF(TOK_ASM_ ## x ## _ ## y, #x "." #y) + +/* register */ + /* integer */ + DEF_ASM(x0) + DEF_ASM(x1) + DEF_ASM(x2) + DEF_ASM(x3) + DEF_ASM(x4) + DEF_ASM(x5) + DEF_ASM(x6) + DEF_ASM(x7) + DEF_ASM(x8) + DEF_ASM(x9) + DEF_ASM(x10) + DEF_ASM(x11) + DEF_ASM(x12) + DEF_ASM(x13) + DEF_ASM(x14) + DEF_ASM(x15) + DEF_ASM(x16) + DEF_ASM(x17) + DEF_ASM(x18) + DEF_ASM(x19) + DEF_ASM(x20) + DEF_ASM(x21) + DEF_ASM(x22) + DEF_ASM(x23) + DEF_ASM(x24) + DEF_ASM(x25) + DEF_ASM(x26) + DEF_ASM(x27) + DEF_ASM(x28) + DEF_ASM(x29) + DEF_ASM(x30) + DEF_ASM(x31) + /* float */ + DEF_ASM(f0) + DEF_ASM(f1) + DEF_ASM(f2) + DEF_ASM(f3) + DEF_ASM(f4) + DEF_ASM(f5) + DEF_ASM(f6) + DEF_ASM(f7) + DEF_ASM(f8) + DEF_ASM(f9) + DEF_ASM(f10) + DEF_ASM(f11) + DEF_ASM(f12) + DEF_ASM(f13) + DEF_ASM(f14) + DEF_ASM(f15) + DEF_ASM(f16) + DEF_ASM(f17) + DEF_ASM(f18) + DEF_ASM(f19) + DEF_ASM(f20) + DEF_ASM(f21) + DEF_ASM(f22) + DEF_ASM(f23) + DEF_ASM(f24) + DEF_ASM(f25) + DEF_ASM(f26) + DEF_ASM(f27) + DEF_ASM(f28) + DEF_ASM(f29) + DEF_ASM(f30) + DEF_ASM(f31) + +/* register ABI mnemonics, refer to RISC-V ABI 1.0 */ + /* integer */ + DEF_ASM(zero) + DEF_ASM(ra) + DEF_ASM(sp) + DEF_ASM(gp) + DEF_ASM(tp) + DEF_ASM(t0) + DEF_ASM(t1) + DEF_ASM(t2) + DEF_ASM(s0) + DEF_ASM(s1) + DEF_ASM(a0) + DEF_ASM(a1) + DEF_ASM(a2) + DEF_ASM(a3) + DEF_ASM(a4) + DEF_ASM(a5) + DEF_ASM(a6) + DEF_ASM(a7) + DEF_ASM(s2) + DEF_ASM(s3) + DEF_ASM(s4) + DEF_ASM(s5) + DEF_ASM(s6) + DEF_ASM(s7) + DEF_ASM(s8) + DEF_ASM(s9) + DEF_ASM(s10) + DEF_ASM(s11) + DEF_ASM(t3) + DEF_ASM(t4) + DEF_ASM(t5) + DEF_ASM(t6) + /* float */ + DEF_ASM(ft0) + DEF_ASM(ft1) + DEF_ASM(ft2) + DEF_ASM(ft3) + DEF_ASM(ft4) + DEF_ASM(ft5) + DEF_ASM(ft6) + DEF_ASM(ft7) + DEF_ASM(fs0) + DEF_ASM(fs1) + DEF_ASM(fa0) + DEF_ASM(fa1) + DEF_ASM(fa2) + DEF_ASM(fa3) + DEF_ASM(fa4) + DEF_ASM(fa5) + DEF_ASM(fa6) + DEF_ASM(fa7) + DEF_ASM(fs2) + DEF_ASM(fs3) + DEF_ASM(fs4) + DEF_ASM(fs5) + DEF_ASM(fs6) + DEF_ASM(fs7) + DEF_ASM(fs8) + DEF_ASM(fs9) + DEF_ASM(fs10) + DEF_ASM(fs11) + DEF_ASM(ft8) + DEF_ASM(ft9) + DEF_ASM(ft10) + DEF_ASM(ft11) + /* not in the ABI */ + DEF_ASM(pc) + +/* Loads */ + + DEF_ASM(lb) + DEF_ASM(lh) + DEF_ASM(lw) + DEF_ASM(lbu) + DEF_ASM(lhu) + /* RV64 */ + DEF_ASM(ld) + DEF_ASM(lwu) + +/* Stores */ + + DEF_ASM(sb) + DEF_ASM(sh) + DEF_ASM(sw) + /* RV64 */ + DEF_ASM(sd) + +/* Shifts */ + + DEF_ASM(sll) + DEF_ASM(srl) + DEF_ASM(sra) + /* RV64 */ + DEF_ASM(slli) + DEF_ASM(srli) + DEF_ASM(sllw) + DEF_ASM(slliw) + DEF_ASM(srlw) + DEF_ASM(srliw) + DEF_ASM(srai) + DEF_ASM(sraw) + DEF_ASM(sraiw) + +/* Arithmetic */ + + DEF_ASM(add) + DEF_ASM(addi) + DEF_ASM(sub) + DEF_ASM(lui) + DEF_ASM(auipc) + /* RV64 */ + DEF_ASM(addw) + DEF_ASM(addiw) + DEF_ASM(subw) + +/* Logical */ + + DEF_ASM(xor) + DEF_ASM(xori) + DEF_ASM(or) + DEF_ASM(ori) + DEF_ASM(and) + DEF_ASM(andi) + +/* Compare */ + + DEF_ASM(slt) + DEF_ASM(slti) + DEF_ASM(sltu) + DEF_ASM(sltiu) + +/* Branch */ + + DEF_ASM(beq) + DEF_ASM(bne) + DEF_ASM(blt) + DEF_ASM(bge) + DEF_ASM(bltu) + DEF_ASM(bgeu) + +/* Jump */ + + DEF_ASM(jal) + DEF_ASM(jalr) + +/* Sync */ + + DEF_ASM(fence) + /* Zifencei extension */ + DEF_ASM_WITH_SUFFIX(fence, i) + +/* System call */ + + /* used to be called scall and sbreak */ + DEF_ASM(ecall) + DEF_ASM(ebreak) + +/* Counters */ + + DEF_ASM(rdcycle) + DEF_ASM(rdcycleh) + DEF_ASM(rdtime) + DEF_ASM(rdtimeh) + DEF_ASM(rdinstret) + DEF_ASM(rdinstreth) + +/* “M” Standard Extension for Integer Multiplication and Division, V2.0 */ + DEF_ASM(mul) + DEF_ASM(mulh) + DEF_ASM(mulhsu) + DEF_ASM(mulhu) + DEF_ASM(div) + DEF_ASM(divu) + DEF_ASM(rem) + DEF_ASM(remu) + /* RV64 */ + DEF_ASM(mulw) + DEF_ASM(divw) + DEF_ASM(divuw) + DEF_ASM(remw) + DEF_ASM(remuw) + +/* "C" Extension for Compressed Instructions, V2.0 */ + DEF_ASM_WITH_SUFFIX(c, nop) +/* Loads */ + DEF_ASM_WITH_SUFFIX(c, li) + DEF_ASM_WITH_SUFFIX(c, lw) + DEF_ASM_WITH_SUFFIX(c, lwsp) + /* single float */ + DEF_ASM_WITH_SUFFIX(c, flw) + DEF_ASM_WITH_SUFFIX(c, flwsp) + /* double float */ + DEF_ASM_WITH_SUFFIX(c, fld) + DEF_ASM_WITH_SUFFIX(c, fldsp) + /* RV64 */ + DEF_ASM_WITH_SUFFIX(c, ld) + DEF_ASM_WITH_SUFFIX(c, ldsp) + +/* Stores */ + + DEF_ASM_WITH_SUFFIX(c, sw) + DEF_ASM_WITH_SUFFIX(c, sd) + DEF_ASM_WITH_SUFFIX(c, swsp) + DEF_ASM_WITH_SUFFIX(c, sdsp) + /* single float */ + DEF_ASM_WITH_SUFFIX(c, fsw) + DEF_ASM_WITH_SUFFIX(c, fswsp) + /* double float */ + DEF_ASM_WITH_SUFFIX(c, fsd) + DEF_ASM_WITH_SUFFIX(c, fsdsp) + +/* Shifts */ + DEF_ASM_WITH_SUFFIX(c, slli) + DEF_ASM_WITH_SUFFIX(c, srli) + DEF_ASM_WITH_SUFFIX(c, srai) + +/* Arithmetic */ + DEF_ASM_WITH_SUFFIX(c, add) + DEF_ASM_WITH_SUFFIX(c, addi) + DEF_ASM_WITH_SUFFIX(c, addi16sp) + DEF_ASM_WITH_SUFFIX(c, addi4spn) + DEF_ASM_WITH_SUFFIX(c, lui) + DEF_ASM_WITH_SUFFIX(c, sub) + DEF_ASM_WITH_SUFFIX(c, mv) + /* RV64 */ + DEF_ASM_WITH_SUFFIX(c, addw) + DEF_ASM_WITH_SUFFIX(c, addiw) + DEF_ASM_WITH_SUFFIX(c, subw) + +/* Logical */ + DEF_ASM_WITH_SUFFIX(c, xor) + DEF_ASM_WITH_SUFFIX(c, or) + DEF_ASM_WITH_SUFFIX(c, and) + DEF_ASM_WITH_SUFFIX(c, andi) + +/* Branch */ + DEF_ASM_WITH_SUFFIX(c, beqz) + DEF_ASM_WITH_SUFFIX(c, bnez) + +/* Jump */ + DEF_ASM_WITH_SUFFIX(c, j) + DEF_ASM_WITH_SUFFIX(c, jr) + DEF_ASM_WITH_SUFFIX(c, jal) + DEF_ASM_WITH_SUFFIX(c, jalr) + +/* System call */ + DEF_ASM_WITH_SUFFIX(c, ebreak) + +/* XXX F Extension: Single-Precision Floating Point */ +/* XXX D Extension: Double-Precision Floating Point */ +/* from the spec: Tables 16.5–16.7 list the RVC instructions. */ + +/* “Zicsr”, Control and Status Register (CSR) Instructions, V2.0 */ + DEF_ASM(csrrw) + DEF_ASM(csrrs) + DEF_ASM(csrrc) + DEF_ASM(csrrwi) + DEF_ASM(csrrsi) + DEF_ASM(csrrci) + /* registers */ + DEF_ASM(cycle) + DEF_ASM(fcsr) + DEF_ASM(fflags) + DEF_ASM(frm) + DEF_ASM(instret) + DEF_ASM(time) + /* RV32I-only */ + DEF_ASM(cycleh) + DEF_ASM(instreth) + DEF_ASM(timeh) + /* pseudo */ + DEF_ASM(csrc) + DEF_ASM(csrci) + DEF_ASM(csrr) + DEF_ASM(csrs) + DEF_ASM(csrsi) + DEF_ASM(csrw) + DEF_ASM(csrwi) + DEF_ASM(frcsr) + DEF_ASM(frflags) + DEF_ASM(frrm) + DEF_ASM(fscsr) + DEF_ASM(fsflags) + DEF_ASM(fsrm) + +/* Privileged Instructions */ + + DEF_ASM(mrts) + DEF_ASM(mrth) + DEF_ASM(hrts) + DEF_ASM(wfi) + +/* pseudoinstructions */ + DEF_ASM(beqz) + DEF_ASM(bgez) + DEF_ASM(bgt) + DEF_ASM(bgtu) + DEF_ASM(bgtz) + DEF_ASM(ble) + DEF_ASM(bleu) + DEF_ASM(blez) + DEF_ASM(bltz) + DEF_ASM(bnez) + DEF_ASM(call) + DEF_ASM_WITH_SUFFIX(fabs, d) + DEF_ASM_WITH_SUFFIX(fabs, s) + DEF_ASM(fld) + DEF_ASM(flw) + DEF_ASM_WITH_SUFFIX(fmv, d) + DEF_ASM_WITH_SUFFIX(fmv, s) + DEF_ASM_WITH_SUFFIX(fneg, d) + DEF_ASM_WITH_SUFFIX(fneg, s) + DEF_ASM(fsd) + DEF_ASM(fsw) + DEF_ASM(j) + DEF_ASM(la) + DEF_ASM(li) + DEF_ASM(lla) + DEF_ASM(mv) + DEF_ASM(neg) + DEF_ASM(negw) + DEF_ASM(nop) + DEF_ASM(not) + DEF_ASM(ret) + DEF_ASM(seqz) + DEF_ASM_WITH_SUFFIX(sext, w) + DEF_ASM(sgtz) + DEF_ASM(sltz) + DEF_ASM(snez) + DEF_ASM(tail) + +#undef DEF_ASM_WITH_SUFFIX diff --git a/vendor/tcc/tcc.c b/vendor/tcc/tcc.c new file mode 100644 index 00000000..b38dae9b --- /dev/null +++ b/vendor/tcc/tcc.c @@ -0,0 +1,414 @@ +/* + * TCC - Tiny C Compiler + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tcc.h" +#if ONE_SOURCE +# include "libtcc.c" +#endif +#include "tcctools.c" + +static const char help[] = + "Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n" + "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n" + " tcc [options...] -run infile (or --) [arguments...]\n" + "General options:\n" + " -c compile only - generate an object file\n" + " -o outfile set output filename\n" + " -run run compiled source\n" + " -fflag set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n" + " -std=c99 Conform to the ISO 1999 C standard (default).\n" + " -std=c11 Conform to the ISO 2011 C standard.\n" + " -Wwarning set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n" + " -w disable all warnings\n" + " -v --version show version\n" + " -vv show search paths or loaded files\n" + " -h -hh show this, show more help\n" + " -bench show compilation statistics\n" + " - use stdin pipe as infile\n" + " @listfile read arguments from listfile\n" + "Preprocessor options:\n" + " -Idir add include path 'dir'\n" + " -Dsym[=val] define 'sym' with value 'val'\n" + " -Usym undefine 'sym'\n" + " -E preprocess only\n" + "Linker options:\n" + " -Ldir add library path 'dir'\n" + " -llib link with dynamic or static library 'lib'\n" + " -r generate (relocatable) object file\n" + " -shared generate a shared library/dll\n" + " -rdynamic export all global symbols to dynamic linker\n" + " -soname set name for shared library to be used at runtime\n" + " -Wl,-opt[=val] set linker option (see tcc -hh)\n" + "Debugger options:\n" + " -g generate stab runtime debug info\n" + " -gdwarf[-x] generate dwarf runtime debug info\n" +#ifdef TCC_TARGET_PE + " -g.pdb create .pdb debug database\n" +#endif +#ifdef CONFIG_TCC_BCHECK + " -b compile with built-in memory and bounds checker (implies -g)\n" +#endif +#ifdef CONFIG_TCC_BACKTRACE + " -bt[N] link with backtrace (stack dump) support [show max N callers]\n" +#endif + "Misc. options:\n" + " -x[c|a|b|n] specify type of the next infile (C,ASM,BIN,NONE)\n" + " -nostdinc do not use standard system include paths\n" + " -nostdlib do not link with standard crt and libraries\n" + " -Bdir set tcc's private include/library dir\n" + " -M[M]D generate make dependency file [ignore system files]\n" + " -M[M] as above but no other output\n" + " -MF file specify dependency file name\n" +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + " -m32/64 defer to i386/x86_64 cross compiler\n" +#endif + "Tools:\n" + " create library : tcc -ar [crstvx] lib [files]\n" +#ifdef TCC_TARGET_PE + " create def file : tcc -impdef lib.dll [-v] [-o lib.def]\n" +#endif + ; + +static const char help2[] = + "Tiny C Compiler "TCC_VERSION" - More Options\n" + "Special options:\n" + " -P -P1 with -E: no/alternative #line output\n" + " -dD -dM with -E: output #define directives\n" + " -pthread same as -D_REENTRANT and -lpthread\n" + " -On same as -D__OPTIMIZE__ for n > 0\n" + " -Wp,-opt same as -opt\n" + " -include file include 'file' above each input file\n" + " -isystem dir add 'dir' to system include path\n" + " -static link to static libraries (not recommended)\n" + " -dumpversion print version\n" + " -print-search-dirs print search paths\n" + " -dt with -run/-E: auto-define 'test_...' macros\n" + "Ignored options:\n" + " -arch -C --param -pedantic -pipe -s -traditional\n" + "-W[no-]... warnings:\n" + " all turn on some (*) warnings\n" + " error[=warning] stop after warning (any or specified)\n" + " write-strings strings are const\n" + " unsupported warn about ignored options, pragmas, etc.\n" + " implicit-function-declaration warn for missing prototype (*)\n" + " discarded-qualifiers warn when const is dropped (*)\n" + "-f[no-]... flags:\n" + " unsigned-char default char is unsigned\n" + " signed-char default char is signed\n" + " common use common section instead of bss\n" + " leading-underscore decorate extern symbols\n" + " ms-extensions allow anonymous struct in struct\n" + " dollars-in-identifiers allow '$' in C symbols\n" + " test-coverage create code coverage code\n" + "-m... target specific options:\n" + " ms-bitfields use MSVC bitfield layout\n" +#ifdef TCC_TARGET_ARM + " float-abi hard/softfp on arm\n" +#endif +#ifdef TCC_TARGET_X86_64 + " no-sse disable floats on x86_64\n" +#endif + "-Wl,... linker options:\n" + " -nostdlib do not link with standard crt/libs\n" + " -[no-]whole-archive load lib(s) fully/only as needed\n" + " -export-all-symbols same as -rdynamic\n" + " -export-dynamic same as -rdynamic\n" + " -image-base= -Ttext= set base address of executable\n" + " -section-alignment= set section alignment in executable\n" +#ifdef TCC_TARGET_PE + " -file-alignment= set PE file alignment\n" + " -stack= set PE stack reserve\n" + " -large-address-aware set related PE option\n" + " -subsystem=[console/windows] set PE subsystem\n" + " -oformat=[pe-* binary] set executable output format\n" + "Predefined macros:\n" + " tcc -E -dM - < nul\n" +#else + " -rpath= set dynamic library search path\n" + " -enable-new-dtags set DT_RUNPATH instead of DT_RPATH\n" + " -soname= set DT_SONAME elf tag\n" +#if defined(TCC_TARGET_MACHO) + " -install_name= set DT_SONAME elf tag (soname macOS alias)\n" +#endif + " -Bsymbolic set DT_SYMBOLIC elf tag\n" + " -oformat=[elf32/64-* binary] set executable output format\n" + " -init= -fini= -Map= -as-needed -O (ignored)\n" + "Predefined macros:\n" + " tcc -E -dM - < /dev/null\n" +#endif + "See also the manual for more details.\n" + ; + +static const char version[] = + "tcc version "TCC_VERSION +#ifdef TCC_GITHASH + " "TCC_GITHASH +#endif + " (" +#ifdef TCC_TARGET_I386 + "i386" +#elif defined TCC_TARGET_X86_64 + "x86_64" +#elif defined TCC_TARGET_C67 + "C67" +#elif defined TCC_TARGET_ARM + "ARM" +# ifdef TCC_ARM_EABI + " eabi" +# ifdef TCC_ARM_HARDFLOAT + "hf" +# endif +# endif +#elif defined TCC_TARGET_ARM64 + "AArch64" +#elif defined TCC_TARGET_RISCV64 + "riscv64" +#endif +#ifdef TCC_TARGET_PE + " Windows" +#elif defined(TCC_TARGET_MACHO) + " Darwin" +#elif TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel + " FreeBSD" +#elif TARGETOS_OpenBSD + " OpenBSD" +#elif TARGETOS_NetBSD + " NetBSD" +#else + " Linux" +#endif + ")\n" + ; + +static void print_dirs(const char *msg, char **paths, int nb_paths) +{ + int i; + printf("%s:\n%s", msg, nb_paths ? "" : " -\n"); + for(i = 0; i < nb_paths; i++) + printf(" %s\n", paths[i]); +} + +static void print_search_dirs(TCCState *s) +{ + printf("install: %s\n", s->tcc_lib_path); + /* print_dirs("programs", NULL, 0); */ + print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths); + print_dirs("libraries", s->library_paths, s->nb_library_paths); + printf("libtcc1:\n %s/%s\n", s->library_paths[0], CONFIG_TCC_CROSSPREFIX TCC_LIBTCC1); +#if !defined TCC_TARGET_PE && !defined TCC_TARGET_MACHO + print_dirs("crt", s->crt_paths, s->nb_crt_paths); + printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s)); +#endif +} + +static void set_environment(TCCState *s) +{ + char * path; + + path = getenv("C_INCLUDE_PATH"); + if(path != NULL) { + tcc_add_sysinclude_path(s, path); + } + path = getenv("CPATH"); + if(path != NULL) { + tcc_add_include_path(s, path); + } + path = getenv("LIBRARY_PATH"); + if(path != NULL) { + tcc_add_library_path(s, path); + } +} + +static char *default_outputfile(TCCState *s, const char *first_file) +{ + char buf[1024]; + char *ext; + const char *name = "a"; + + if (first_file && strcmp(first_file, "-")) + name = tcc_basename(first_file); + snprintf(buf, sizeof(buf), "%s", name); + ext = tcc_fileextension(buf); +#ifdef TCC_TARGET_PE + if (s->output_type == TCC_OUTPUT_DLL) + strcpy(ext, ".dll"); + else + if (s->output_type == TCC_OUTPUT_EXE) + strcpy(ext, ".exe"); + else +#endif + if ((s->just_deps || s->output_type == TCC_OUTPUT_OBJ) && !s->option_r && *ext) + strcpy(ext, ".o"); + else + strcpy(buf, "a.out"); + return tcc_strdup(buf); +} + +static unsigned getclock_ms(void) +{ +#ifdef _WIN32 + return GetTickCount(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec*1000 + (tv.tv_usec+500)/1000; +#endif +} + +int main(int argc0, char **argv0) +{ + TCCState *s, *s1; + int ret, opt, n = 0, t = 0, done; + unsigned start_time = 0, end_time = 0; + const char *first_file; + int argc; char **argv; + FILE *ppfp = stdout; + +redo: + argc = argc0, argv = argv0; + s = s1 = tcc_new(); +#ifdef CONFIG_TCC_SWITCHES /* predefined options */ + tcc_set_options(s, CONFIG_TCC_SWITCHES); +#endif + opt = tcc_parse_args(s, &argc, &argv, 1); + if (opt < 0) + return 1; + + if (n == 0) { + if (opt == OPT_HELP) { + fputs(help, stdout); + if (!s->verbose) + return 0; + ++opt; + } + if (opt == OPT_HELP2) { + fputs(help2, stdout); + return 0; + } + if (opt == OPT_M32 || opt == OPT_M64) + return tcc_tool_cross(s, argv, opt); + if (s->verbose) + printf("%s", version); + if (opt == OPT_AR) + return tcc_tool_ar(s, argc, argv); +#ifdef TCC_TARGET_PE + if (opt == OPT_IMPDEF) + return tcc_tool_impdef(s, argc, argv); +#endif + if (opt == OPT_V) + return 0; + if (opt == OPT_PRINT_DIRS) { + /* initialize search dirs */ + set_environment(s); + tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + print_search_dirs(s); + return 0; + } + + if (s->nb_files == 0) { + tcc_error_noabort("no input files"); + } else if (s->output_type == TCC_OUTPUT_PREPROCESS) { + if (s->outfile && 0!=strcmp("-",s->outfile)) { + ppfp = fopen(s->outfile, "w"); + if (!ppfp) + tcc_error_noabort("could not write '%s'", s->outfile); + } + } else if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) { + if (s->nb_libraries) + tcc_error_noabort("cannot specify libraries with -c"); + else if (s->nb_files > 1 && s->outfile) + tcc_error_noabort("cannot specify output file with -c many files"); + } + if (s->nb_errors) + return 1; + if (s->do_bench) + start_time = getclock_ms(); + } + + set_environment(s); + if (s->output_type == 0) + s->output_type = TCC_OUTPUT_EXE; + tcc_set_output_type(s, s->output_type); + s->ppfp = ppfp; + + if ((s->output_type == TCC_OUTPUT_MEMORY + || s->output_type == TCC_OUTPUT_PREPROCESS) + && (s->dflag & 16)) { /* -dt option */ + if (t) + s->dflag |= 32; + s->run_test = ++t; + if (n) + --n; + } + + /* compile or add each files or library */ + first_file = NULL, ret = 0; + do { + struct filespec *f = s->files[n]; + s->filetype = f->type; + if (f->type & AFF_TYPE_LIB) { + if (tcc_add_library_err(s, f->name) < 0) + ret = 1; + } else { + if (1 == s->verbose) + printf("-> %s\n", f->name); + if (!first_file) + first_file = f->name; + if (tcc_add_file(s, f->name) < 0) + ret = 1; + } + done = ret || ++n >= s->nb_files; + } while (!done && (s->output_type != TCC_OUTPUT_OBJ || s->option_r)); + + if (s->do_bench) + end_time = getclock_ms(); + + if (s->run_test) { + t = 0; + } else if (s->output_type == TCC_OUTPUT_PREPROCESS) { + ; + } else if (0 == ret) { + if (s->output_type == TCC_OUTPUT_MEMORY) { +#ifdef TCC_IS_NATIVE + ret = tcc_run(s, argc, argv); +#endif + } else { + if (!s->outfile) + s->outfile = default_outputfile(s, first_file); + if (!s->just_deps && tcc_output_file(s, s->outfile)) + ret = 1; + else if (s->gen_deps) + ret = gen_makedeps(s, s->outfile, s->deps_outfile); + } + } + + if (done && 0 == t && 0 == ret && s->do_bench) + tcc_print_stats(s, end_time - start_time); + + tcc_delete(s); + if (!done) + goto redo; /* compile more files with -c */ + if (t) + goto redo; /* run more tests with -dt -run */ + + if (ppfp && ppfp != stdout) + fclose(ppfp); + return ret; +} diff --git a/vendor/tcc/tcccoff.c b/vendor/tcc/tcccoff.c new file mode 100644 index 00000000..56064cdd --- /dev/null +++ b/vendor/tcc/tcccoff.c @@ -0,0 +1,951 @@ +/* + * COFF file handling for TCC + * + * Copyright (c) 2003, 2004 TK + * Copyright (c) 2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tcc.h" + +/* XXX: this file uses tcc_error() to the effect of exit(1) */ +#undef _tcc_error + +#define MAXNSCNS 255 /* MAXIMUM NUMBER OF SECTIONS */ +#define MAX_STR_TABLE 1000000 +AOUTHDR o_filehdr; /* OPTIONAL (A.OUT) FILE HEADER */ + +SCNHDR section_header[MAXNSCNS]; + +#define MAX_FUNCS 1000 +#define MAX_FUNC_NAME_LENGTH 128 + +int nFuncs; +char Func[MAX_FUNCS][MAX_FUNC_NAME_LENGTH]; +char AssociatedFile[MAX_FUNCS][MAX_FUNC_NAME_LENGTH]; +int LineNoFilePtr[MAX_FUNCS]; +int EndAddress[MAX_FUNCS]; +int LastLineNo[MAX_FUNCS]; +int FuncEntries[MAX_FUNCS]; + +int OutputTheSection(Section * sect); +short int GetCoffFlags(const char *s); +void SortSymbolTable(TCCState *s1); +Section *FindSection(TCCState * s1, const char *sname); + +int C67_main_entry_point; + +int FindCoffSymbolIndex(TCCState * s1, const char *func_name); +int nb_syms; + +typedef struct { + long tag; + long size; + long fileptr; + long nextsym; + short int dummy; +} AUXFUNC; + +typedef struct { + long regmask; + unsigned short lineno; + unsigned short nentries; + int localframe; + int nextentry; + short int dummy; +} AUXBF; + +typedef struct { + long dummy; + unsigned short lineno; + unsigned short dummy1; + int dummy2; + int dummy3; + unsigned short dummy4; +} AUXEF; + +ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f) +{ + Section *tcc_sect; + SCNHDR *coff_sec; + int file_pointer; + char *Coff_str_table, *pCoff_str_table; + int CoffTextSectionNo, coff_nb_syms; + FILHDR file_hdr; /* FILE HEADER STRUCTURE */ + Section *stext, *sdata, *sbss; + int i, NSectionsToOutput = 0; + + Coff_str_table = pCoff_str_table = NULL; + + stext = FindSection(s1, ".text"); + sdata = FindSection(s1, ".data"); + sbss = FindSection(s1, ".bss"); + + nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym); + coff_nb_syms = FindCoffSymbolIndex(s1, "XXXXXXXXXX1"); + + file_hdr.f_magic = COFF_C67_MAGIC; /* magic number */ + file_hdr.f_timdat = 0; /* time & date stamp */ + file_hdr.f_opthdr = sizeof(AOUTHDR); /* sizeof(optional hdr) */ + file_hdr.f_flags = 0x1143; /* flags (copied from what code composer does) */ + file_hdr.f_TargetID = 0x99; /* for C6x = 0x0099 */ + + o_filehdr.magic = 0x0108; /* see magic.h */ + o_filehdr.vstamp = 0x0190; /* version stamp */ + o_filehdr.tsize = stext->data_offset; /* text size in bytes, padded to FW bdry */ + o_filehdr.dsize = sdata->data_offset; /* initialized data " " */ + o_filehdr.bsize = sbss->data_offset; /* uninitialized data " " */ + o_filehdr.entrypt = C67_main_entry_point; /* entry pt. */ + o_filehdr.text_start = stext->sh_addr; /* base of text used for this file */ + o_filehdr.data_start = sdata->sh_addr; /* base of data used for this file */ + + + // create all the section headers + + file_pointer = FILHSZ + sizeof(AOUTHDR); + + CoffTextSectionNo = -1; + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + NSectionsToOutput++; + + if (CoffTextSectionNo == -1 && tcc_sect == stext) + CoffTextSectionNo = NSectionsToOutput; // rem which coff sect number the .text sect is + + strcpy(coff_sec->s_name, tcc_sect->name); /* section name */ + + coff_sec->s_paddr = tcc_sect->sh_addr; /* physical address */ + coff_sec->s_vaddr = tcc_sect->sh_addr; /* virtual address */ + coff_sec->s_size = tcc_sect->data_offset; /* section size */ + coff_sec->s_scnptr = 0; /* file ptr to raw data for section */ + coff_sec->s_relptr = 0; /* file ptr to relocation */ + coff_sec->s_lnnoptr = 0; /* file ptr to line numbers */ + coff_sec->s_nreloc = 0; /* number of relocation entries */ + coff_sec->s_flags = GetCoffFlags(coff_sec->s_name); /* flags */ + coff_sec->s_reserved = 0; /* reserved byte */ + coff_sec->s_page = 0; /* memory page id */ + + file_pointer += sizeof(SCNHDR); + } + } + + file_hdr.f_nscns = NSectionsToOutput; /* number of sections */ + + // now loop through and determine file pointer locations + // for the raw data + + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put raw data + coff_sec->s_scnptr = file_pointer; /* file ptr to raw data for section */ + file_pointer += coff_sec->s_size; + } + } + + // now loop through and determine file pointer locations + // for the relocation data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put relocations data + if (coff_sec->s_nreloc > 0) { + coff_sec->s_relptr = file_pointer; /* file ptr to relocation */ + file_pointer += coff_sec->s_nreloc * sizeof(struct reloc); + } + } + } + + // now loop through and determine file pointer locations + // for the line number data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + coff_sec->s_nlnno = 0; + coff_sec->s_lnnoptr = 0; + + if (s1->do_debug && tcc_sect == stext) { + // count how many line nos data + + // also find association between source file name and function + // so we can sort the symbol table + + + Stab_Sym *sym, *sym_end; + char func_name[MAX_FUNC_NAME_LENGTH], + last_func_name[MAX_FUNC_NAME_LENGTH]; + unsigned long func_addr, last_pc, pc; + const char *incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num; + const char *str, *p; + + coff_sec->s_lnnoptr = file_pointer; /* file ptr to linno */ + + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = 0xffffffff; + last_line_num = 1; + sym = (Stab_Sym *) stab_section->data + 1; + sym_end = + (Stab_Sym *) (stab_section->data + + stab_section->data_offset); + + nFuncs = 0; + while (sym < sym_end) { + switch (sym->n_type) { + /* function start or end */ + case N_FUN: + if (sym->n_strx == 0) { + // end of function + + coff_sec->s_nlnno++; + file_pointer += LINESZ; + + pc = sym->n_value + func_addr; + func_name[0] = '\0'; + func_addr = 0; + EndAddress[nFuncs] = pc; + FuncEntries[nFuncs] = + (file_pointer - + LineNoFilePtr[nFuncs]) / LINESZ - 1; + LastLineNo[nFuncs++] = last_line_num + 1; + } else { + // beginning of function + + LineNoFilePtr[nFuncs] = file_pointer; + coff_sec->s_nlnno++; + file_pointer += LINESZ; + + str = + (const char *) stabstr_section->data + + sym->n_strx; + + p = strchr(str, ':'); + if (!p) { + pstrcpy(func_name, sizeof(func_name), str); + pstrcpy(Func[nFuncs], sizeof(func_name), str); + } else { + len = p - str; + if (len > sizeof(func_name) - 1) + len = sizeof(func_name) - 1; + memcpy(func_name, str, len); + memcpy(Func[nFuncs], str, len); + func_name[len] = '\0'; + } + + // save the file that it came in so we can sort later + pstrcpy(AssociatedFile[nFuncs], sizeof(func_name), + incl_files[incl_index - 1]); + + func_addr = sym->n_value; + } + break; + + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + + last_pc = pc; + last_line_num = sym->n_desc; + + /* XXX: slow! */ + strcpy(last_func_name, func_name); + + coff_sec->s_nlnno++; + file_pointer += LINESZ; + break; + /* include files */ + case N_BINCL: + str = + (const char *) stabstr_section->data + sym->n_strx; + add_incl: + if (incl_index < INCLUDE_STACK_SIZE) { + incl_files[incl_index++] = str; + } + break; + case N_EINCL: + if (incl_index > 1) + incl_index--; + break; + case N_SO: + if (sym->n_strx == 0) { + incl_index = 0; /* end of translation unit */ + } else { + str = + (const char *) stabstr_section->data + + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl; + } + break; + } + sym++; + } + } + + } + + file_hdr.f_symptr = file_pointer; /* file pointer to symtab */ + + if (s1->do_debug) + file_hdr.f_nsyms = coff_nb_syms; /* number of symtab entries */ + else + file_hdr.f_nsyms = 0; + + file_pointer += file_hdr.f_nsyms * SYMNMLEN; + + // OK now we are all set to write the file + + + fwrite(&file_hdr, FILHSZ, 1, f); + fwrite(&o_filehdr, sizeof(o_filehdr), 1, f); + + // write section headers + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + fwrite(coff_sec, sizeof(SCNHDR), 1, f); + } + } + + // write raw data + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + fwrite(tcc_sect->data, tcc_sect->data_offset, 1, f); + } + } + + // write relocation data + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put relocations data + if (coff_sec->s_nreloc > 0) { + fwrite(tcc_sect->reloc, + coff_sec->s_nreloc * sizeof(struct reloc), 1, f); + } + } + } + + + // group the symbols in order of filename, func1, func2, etc + // finally global symbols + + if (s1->do_debug) + SortSymbolTable(s1); + + // write line no data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (s1->do_debug && tcc_sect == stext) { + // count how many line nos data + + + Stab_Sym *sym, *sym_end; + char func_name[128], last_func_name[128]; + unsigned long func_addr, last_pc, pc; + const char *incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num; + const char *str, *p; + + LINENO CoffLineNo; + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = 0; + last_line_num = 1; + sym = (Stab_Sym *) stab_section->data + 1; + sym_end = + (Stab_Sym *) (stab_section->data + + stab_section->data_offset); + + while (sym < sym_end) { + switch (sym->n_type) { + /* function start or end */ + case N_FUN: + if (sym->n_strx == 0) { + // end of function + + CoffLineNo.l_addr.l_paddr = last_pc; + CoffLineNo.l_lnno = last_line_num + 1; + fwrite(&CoffLineNo, 6, 1, f); + + pc = sym->n_value + func_addr; + func_name[0] = '\0'; + func_addr = 0; + } else { + // beginning of function + + str = + (const char *) stabstr_section->data + + sym->n_strx; + + + p = strchr(str, ':'); + if (!p) { + pstrcpy(func_name, sizeof(func_name), str); + } else { + len = p - str; + if (len > sizeof(func_name) - 1) + len = sizeof(func_name) - 1; + memcpy(func_name, str, len); + func_name[len] = '\0'; + } + func_addr = sym->n_value; + last_pc = func_addr; + last_line_num = -1; + + // output a function begin + + CoffLineNo.l_addr.l_symndx = + FindCoffSymbolIndex(s1, func_name); + CoffLineNo.l_lnno = 0; + + fwrite(&CoffLineNo, 6, 1, f); + } + break; + + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + + + /* XXX: slow! */ + strcpy(last_func_name, func_name); + + // output a line reference + + CoffLineNo.l_addr.l_paddr = last_pc; + + if (last_line_num == -1) { + CoffLineNo.l_lnno = sym->n_desc; + } else { + CoffLineNo.l_lnno = last_line_num + 1; + } + + fwrite(&CoffLineNo, 6, 1, f); + + last_pc = pc; + last_line_num = sym->n_desc; + + break; + + /* include files */ + case N_BINCL: + str = + (const char *) stabstr_section->data + sym->n_strx; + add_incl2: + if (incl_index < INCLUDE_STACK_SIZE) { + incl_files[incl_index++] = str; + } + break; + case N_EINCL: + if (incl_index > 1) + incl_index--; + break; + case N_SO: + if (sym->n_strx == 0) { + incl_index = 0; /* end of translation unit */ + } else { + str = + (const char *) stabstr_section->data + + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl2; + } + break; + } + sym++; + } + } + } + + // write symbol table + if (s1->do_debug) { + int k; + struct syment csym; + AUXFUNC auxfunc; + AUXBF auxbf; + AUXEF auxef; + int i; + Elf32_Sym *p; + const char *name; + int nstr; + int n = 0; + + Coff_str_table = (char *) tcc_malloc(MAX_STR_TABLE); + pCoff_str_table = Coff_str_table; + nstr = 0; + + p = (Elf32_Sym *) symtab_section->data; + + + for (i = 0; i < nb_syms; i++) { + + name = symtab_section->link->data + p->st_name; + + for (k = 0; k < 8; k++) + csym._n._n_name[k] = 0; + + if (strlen(name) <= 8) { + strcpy(csym._n._n_name, name); + } else { + if (pCoff_str_table - Coff_str_table + strlen(name) > + MAX_STR_TABLE - 1) + tcc_error("String table too large"); + + csym._n._n_n._n_zeroes = 0; + csym._n._n_n._n_offset = + pCoff_str_table - Coff_str_table + 4; + + strcpy(pCoff_str_table, name); + pCoff_str_table += strlen(name) + 1; // skip over null + nstr++; + } + + if (p->st_info == 4) { + // put a filename symbol + csym.n_value = 33; // ????? + csym.n_scnum = N_DEBUG; + csym.n_type = 0; + csym.n_sclass = C_FILE; + csym.n_numaux = 0; + fwrite(&csym, 18, 1, f); + n++; + + } else if (p->st_info == 0x12) { + // find the function data + + for (k = 0; k < nFuncs; k++) { + if (strcmp(name, Func[k]) == 0) + break; + } + + if (k >= nFuncs) { + tcc_error("debug info can't find function: %s", name); + } + // put a Function Name + + csym.n_value = p->st_value; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = MKTYPE(T_INT, DT_FCN, 0, 0, 0, 0, 0); + csym.n_sclass = C_EXT; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxfunc.tag = 0; + auxfunc.size = EndAddress[k] - p->st_value; + auxfunc.fileptr = LineNoFilePtr[k]; + auxfunc.nextsym = n + 6; // tktk + auxfunc.dummy = 0; + fwrite(&auxfunc, 18, 1, f); + + // put a .bf + + strcpy(csym._n._n_name, ".bf"); + csym.n_value = p->st_value; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = 0; + csym.n_sclass = C_FCN; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxbf.regmask = 0; + auxbf.lineno = 0; + auxbf.nentries = FuncEntries[k]; + auxbf.localframe = 0; + auxbf.nextentry = n + 6; + auxbf.dummy = 0; + fwrite(&auxbf, 18, 1, f); + + // put a .ef + + strcpy(csym._n._n_name, ".ef"); + csym.n_value = EndAddress[k]; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = 0; + csym.n_sclass = C_FCN; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxef.dummy = 0; + auxef.lineno = LastLineNo[k]; + auxef.dummy1 = 0; + auxef.dummy2 = 0; + auxef.dummy3 = 0; + auxef.dummy4 = 0; + fwrite(&auxef, 18, 1, f); + + n += 6; + + } else { + // try an put some type info + + if ((p->st_other & VT_BTYPE) == VT_DOUBLE) { + csym.n_type = T_DOUBLE; // int + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_FLOAT) { + csym.n_type = T_FLOAT; + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_INT) { + csym.n_type = T_INT; // int + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_SHORT) { + csym.n_type = T_SHORT; + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_BYTE) { + csym.n_type = T_CHAR; + csym.n_sclass = C_EXT; + } else { + csym.n_type = T_INT; // just mark as a label + csym.n_sclass = C_LABEL; + } + + + csym.n_value = p->st_value; + csym.n_scnum = 2; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + auxfunc.tag = 0; + auxfunc.size = 0x20; + auxfunc.fileptr = 0; + auxfunc.nextsym = 0; + auxfunc.dummy = 0; + fwrite(&auxfunc, 18, 1, f); + n++; + n++; + + } + + p++; + } + } + + if (s1->do_debug) { + // write string table + + // first write the size + i = pCoff_str_table - Coff_str_table; + fwrite(&i, 4, 1, f); + + // then write the strings + fwrite(Coff_str_table, i, 1, f); + + tcc_free(Coff_str_table); + } + + return 0; +} + + + +// group the symbols in order of filename, func1, func2, etc +// finally global symbols + +void SortSymbolTable(TCCState *s1) +{ + int i, j, k, n = 0; + Elf32_Sym *p, *p2, *NewTable; + char *name, *name2; + + NewTable = (Elf32_Sym *) tcc_malloc(nb_syms * sizeof(Elf32_Sym)); + + p = (Elf32_Sym *) symtab_section->data; + + + // find a file symbol, copy it over + // then scan the whole symbol list and copy any function + // symbols that match the file association + + for (i = 0; i < nb_syms; i++) { + if (p->st_info == 4) { + name = (char *) symtab_section->link->data + p->st_name; + + // this is a file symbol, copy it over + + NewTable[n++] = *p; + + p2 = (Elf32_Sym *) symtab_section->data; + + for (j = 0; j < nb_syms; j++) { + if (p2->st_info == 0x12) { + // this is a func symbol + + name2 = + (char *) symtab_section->link->data + p2->st_name; + + // find the function data index + + for (k = 0; k < nFuncs; k++) { + if (strcmp(name2, Func[k]) == 0) + break; + } + + if (k >= nFuncs) { + tcc_error("debug (sort) info can't find function: %s", name2); + } + + if (strcmp(AssociatedFile[k], name) == 0) { + // yes they match copy it over + + NewTable[n++] = *p2; + } + } + p2++; + } + } + p++; + } + + // now all the filename and func symbols should have been copied over + // copy all the rest over (all except file and funcs) + + p = (Elf32_Sym *) symtab_section->data; + for (i = 0; i < nb_syms; i++) { + if (p->st_info != 4 && p->st_info != 0x12) { + NewTable[n++] = *p; + } + p++; + } + + if (n != nb_syms) + tcc_error("Internal Compiler error, debug info"); + + // copy it all back + + p = (Elf32_Sym *) symtab_section->data; + for (i = 0; i < nb_syms; i++) { + *p++ = NewTable[i]; + } + + tcc_free(NewTable); +} + + +int FindCoffSymbolIndex(TCCState *s1, const char *func_name) +{ + int i, n = 0; + Elf32_Sym *p; + char *name; + + p = (Elf32_Sym *) symtab_section->data; + + for (i = 0; i < nb_syms; i++) { + + name = (char *) symtab_section->link->data + p->st_name; + + if (p->st_info == 4) { + // put a filename symbol + n++; + } else if (p->st_info == 0x12) { + + if (strcmp(func_name, name) == 0) + return n; + + n += 6; + + // put a Function Name + + // now put aux info + + // put a .bf + + // now put aux info + + // put a .ef + + // now put aux info + + } else { + n += 2; + } + + p++; + } + + return n; // total number of symbols +} + +int OutputTheSection(Section * sect) +{ + const char *s = sect->name; + + if (!strcmp(s, ".text")) + return 1; + else if (!strcmp(s, ".data")) + return 1; + else + return 0; +} + +short int GetCoffFlags(const char *s) +{ + if (!strcmp(s, ".text")) + return STYP_TEXT | STYP_DATA | STYP_ALIGN | 0x400; + else if (!strcmp(s, ".data")) + return STYP_DATA; + else if (!strcmp(s, ".bss")) + return STYP_BSS; + else if (!strcmp(s, ".stack")) + return STYP_BSS | STYP_ALIGN | 0x200; + else if (!strcmp(s, ".cinit")) + return STYP_COPY | STYP_DATA | STYP_ALIGN | 0x200; + else + return 0; +} + +Section *FindSection(TCCState * s1, const char *sname) +{ + Section *s; + int i; + + for (i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + + if (!strcmp(sname, s->name)) + return s; + } + + tcc_error("could not find section %s", sname); + return 0; +} + +ST_FUNC int tcc_load_coff(TCCState * s1, int fd) +{ +// tktk TokenSym *ts; + + FILE *f; + unsigned int str_size; + char *Coff_str_table, *name; + int i, k; + struct syment csym; + char name2[9]; + FILHDR file_hdr; /* FILE HEADER STRUCTURE */ + + f = fdopen(fd, "rb"); + if (!f) { + tcc_error("Unable to open .out file for input"); + } + + if (fread(&file_hdr, FILHSZ, 1, f) != 1) + tcc_error("error reading .out file for input"); + + if (fread(&o_filehdr, sizeof(o_filehdr), 1, f) != 1) + tcc_error("error reading .out file for input"); + + // first read the string table + + if (fseek(f, file_hdr.f_symptr + file_hdr.f_nsyms * SYMESZ, SEEK_SET)) + tcc_error("error reading .out file for input"); + + if (fread(&str_size, sizeof(int), 1, f) != 1) + tcc_error("error reading .out file for input"); + + + Coff_str_table = (char *) tcc_malloc(str_size); + + if (fread(Coff_str_table, str_size - 4, 1, f) != 1) + tcc_error("error reading .out file for input"); + + // read/process all the symbols + + // seek back to symbols + + if (fseek(f, file_hdr.f_symptr, SEEK_SET)) + tcc_error("error reading .out file for input"); + + for (i = 0; i < file_hdr.f_nsyms; i++) { + if (fread(&csym, SYMESZ, 1, f) != 1) + tcc_error("error reading .out file for input"); + + if (csym._n._n_n._n_zeroes == 0) { + name = Coff_str_table + csym._n._n_n._n_offset - 4; + } else { + name = csym._n._n_name; + + if (name[7] != 0) { + for (k = 0; k < 8; k++) + name2[k] = name[k]; + + name2[8] = 0; + + name = name2; + } + } +// if (strcmp("_DAC_Buffer",name)==0) // tktk +// name[0]=0; + + if (((csym.n_type & 0x30) == 0x20 && csym.n_sclass == 0x2) || ((csym.n_type & 0x30) == 0x30 && csym.n_sclass == 0x2) || (csym.n_type == 0x4 && csym.n_sclass == 0x2) || (csym.n_type == 0x8 && csym.n_sclass == 0x2) || // structures + (csym.n_type == 0x18 && csym.n_sclass == 0x2) || // pointer to structure + (csym.n_type == 0x7 && csym.n_sclass == 0x2) || // doubles + (csym.n_type == 0x6 && csym.n_sclass == 0x2)) // floats + { + // strip off any leading underscore (except for other main routine) + + if (name[0] == '_' && strcmp(name, "_main") != 0) + name++; + + tcc_add_symbol(s1, name, (void*)(uintptr_t)csym.n_value); + } + // skip any aux records + + if (csym.n_numaux == 1) { + if (fread(&csym, SYMESZ, 1, f) != 1) + tcc_error("error reading .out file for input"); + i++; + } + } + + return 0; +} diff --git a/vendor/tcc/tccgen.c b/vendor/tcc/tccgen.c index 29dae91e..d5e9bf51 100644 --- a/vendor/tcc/tccgen.c +++ b/vendor/tcc/tccgen.c @@ -5876,9 +5876,7 @@ ST_FUNC void unary(void) next(); skip('('); expr_type(&controlling_type, expr_eq); - controlling_type.t &= ~(VT_CONSTANT | VT_VOLATILE | VT_ARRAY); - if ((controlling_type.t & VT_BTYPE) == VT_FUNC) - mk_pointer(&controlling_type); + convert_parameter_type (&controlling_type); nocode_wanted = saved_nocode_wanted; @@ -6547,14 +6545,14 @@ static void expr_eq(void) ST_FUNC void gexpr(void) { - while (1) { - expr_eq(); - if (tok != ',') - break; + expr_eq(); + while (tok == ',') { constant_p &= (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && !((vtop->r & VT_SYM) && vtop->sym->a.addrtaken); vpop(); next(); + expr_eq(); + convert_parameter_type (&vtop->type); } } @@ -7268,7 +7266,7 @@ static void block(int is_expr) if (is_expr) { vpop(); gexpr(); - } else { + } else if (!decl(VT_JMP)) { gexpr(); vpop(); } diff --git a/vendor/tcc/tccmacho.c b/vendor/tcc/tccmacho.c new file mode 100644 index 00000000..0b70457c --- /dev/null +++ b/vendor/tcc/tccmacho.c @@ -0,0 +1,2480 @@ +/* + * Mach-O file handling for TCC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tcc.h" + +/* In order to make life easy for us we are generating Mach-O files which + don't make use of some modern features, but which aren't entirely classic + either in that they do use some modern features. We're also only + generating 64bit Mach-O files, and only native endian at that. + + In particular we're generating executables that don't make use of + DYLD_INFO for dynamic linking info, as that requires us building a + trie of exported names. We're simply using classic symbol tables which + are still supported by modern dyld. + + But we do use LC_MAIN, which is a "modern" feature in order to not have + to setup our own crt code. We're not using lazy linking, so even function + calls are resolved at startup. */ + +#if !defined TCC_TARGET_X86_64 && !defined TCC_TARGET_ARM64 +#error Platform not supported +#endif + +/* XXX: this file uses tcc_error() to the effect of exit(1) */ +#undef _tcc_error + +#define DEBUG_MACHO 0 +#define dprintf if (DEBUG_MACHO) printf + +#define MH_EXECUTE (0x2) +#define MH_DYLDLINK (0x4) +#define MH_DYLIB (0x6) +#define MH_PIE (0x200000) + +#define CPU_SUBTYPE_LIB64 (0x80000000) +#define CPU_SUBTYPE_X86_ALL (3) +#define CPU_SUBTYPE_ARM64_ALL (0) + +#define CPU_ARCH_ABI64 (0x01000000) + +#define CPU_TYPE_X86 (7) +#define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) +#define CPU_TYPE_ARM (12) +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) + +struct fat_header { + uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */ + uint32_t nfat_arch; /* number of structs that follow */ +}; + +struct fat_arch { + int cputype; /* cpu specifier (int) */ + int cpusubtype; /* machine specifier (int) */ + uint32_t offset; /* file offset to this object file */ + uint32_t size; /* size of this object file */ + uint32_t align; /* alignment as a power of 2 */ +}; + +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca +#define FAT_MAGIC_64 0xcafebabf +#define FAT_CIGAM_64 0xbfbafeca + +struct mach_header { + uint32_t magic; /* mach magic number identifier */ + int cputype; /* cpu specifier */ + int cpusubtype; /* machine specifier */ + uint32_t filetype; /* type of file */ + uint32_t ncmds; /* number of load commands */ + uint32_t sizeofcmds; /* the size of all the load commands */ + uint32_t flags; /* flags */ +}; + +struct mach_header_64 { + struct mach_header mh; + uint32_t reserved; /* reserved, pad to 64bit */ +}; + +/* Constant for the magic field of the mach_header (32-bit architectures) */ +#define MH_MAGIC 0xfeedface /* the mach magic number */ +#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ +#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ +#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ + +struct load_command { + uint32_t cmd; /* type of load command */ + uint32_t cmdsize; /* total size of command in bytes */ +}; + +#define LC_REQ_DYLD 0x80000000 +#define LC_SYMTAB 0x2 +#define LC_DYSYMTAB 0xb +#define LC_LOAD_DYLIB 0xc +#define LC_ID_DYLIB 0xd +#define LC_LOAD_DYLINKER 0xe +#define LC_SEGMENT_64 0x19 +#define LC_RPATH (0x1c | LC_REQ_DYLD) +#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) +#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) +#define LC_MAIN (0x28|LC_REQ_DYLD) +#define LC_SOURCE_VERSION 0x2A +#define LC_BUILD_VERSION 0x32 +#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) +#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) + +#define SG_READ_ONLY 0x10 /* This segment is made read-only after fixups */ + +typedef int vm_prot_t; + +struct segment_command_64 { /* for 64-bit architectures */ + uint32_t cmd; /* LC_SEGMENT_64 */ + uint32_t cmdsize; /* includes sizeof section_64 structs */ + char segname[16]; /* segment name */ + uint64_t vmaddr; /* memory address of this segment */ + uint64_t vmsize; /* memory size of this segment */ + uint64_t fileoff; /* file offset of this segment */ + uint64_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t initprot; /* initial VM protection */ + uint32_t nsects; /* number of sections in segment */ + uint32_t flags; /* flags */ +}; + +struct section_64 { /* for 64-bit architectures */ + char sectname[16]; /* name of this section */ + char segname[16]; /* segment this section goes in */ + uint64_t addr; /* memory address of this section */ + uint64_t size; /* size in bytes of this section */ + uint32_t offset; /* file offset of this section */ + uint32_t align; /* section alignment (power of 2) */ + uint32_t reloff; /* file offset of relocation entries */ + uint32_t nreloc; /* number of relocation entries */ + uint32_t flags; /* flags (section type and attributes)*/ + uint32_t reserved1; /* reserved (for offset or index) */ + uint32_t reserved2; /* reserved (for count or sizeof) */ + uint32_t reserved3; /* reserved */ +}; + +enum { + DYLD_CHAINED_IMPORT = 1, +}; + +struct dyld_chained_fixups_header { + uint32_t fixups_version; ///< 0 + uint32_t starts_offset; ///< Offset of dyld_chained_starts_in_image. + uint32_t imports_offset; ///< Offset of imports table in chain_data. + uint32_t symbols_offset; ///< Offset of symbol strings in chain_data. + uint32_t imports_count; ///< Number of imported symbol names. + uint32_t imports_format; ///< DYLD_CHAINED_IMPORT* + uint32_t symbols_format; ///< 0 => uncompressed, 1 => zlib compressed +}; + +struct dyld_chained_starts_in_image +{ + uint32_t seg_count; + uint32_t seg_info_offset[1]; // each entry is offset into this struct for that segment + // followed by pool of dyld_chain_starts_in_segment data +}; + +enum { + DYLD_CHAINED_PTR_64 = 2, // target is vmaddr + DYLD_CHAINED_PTR_64_OFFSET = 6, // target is vm offset +}; + +enum { + DYLD_CHAINED_PTR_START_NONE = 0xFFFF, // used in page_start[] to denote a page with no fixups +}; + +#define SEG_PAGE_SIZE 16384 + +struct dyld_chained_starts_in_segment +{ + uint32_t size; // size of this (amount kernel needs to copy) + uint16_t page_size; // 0x1000 or 0x4000 + uint16_t pointer_format; // DYLD_CHAINED_PTR_* + uint64_t segment_offset; // offset in memory to start of segment + uint32_t max_valid_pointer; // for 32-bit OS, any value beyond this is not a pointer + uint16_t page_count; // how many pages are in array + uint16_t page_start[1]; // each entry is offset in each page of first element in chain + // or DYLD_CHAINED_PTR_START_NONE if no fixups on page +}; + +enum BindSpecialDylib { + BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2, +}; + +struct dyld_chained_import +{ + uint32_t lib_ordinal : 8, + weak_import : 1, + name_offset : 23; +}; + +struct dyld_chained_ptr_64_rebase +{ + uint64_t target : 36, // vmaddr, 64GB max image size + high8 : 8, // top 8 bits set to this after slide added + reserved : 7, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 0 +}; + +struct dyld_chained_ptr_64_bind +{ + uint64_t ordinal : 24, + addend : 8, // 0 thru 255 + reserved : 19, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 1 +}; + +#define S_REGULAR 0x0 +#define S_ZEROFILL 0x1 +#define S_NON_LAZY_SYMBOL_POINTERS 0x6 +#define S_LAZY_SYMBOL_POINTERS 0x7 +#define S_SYMBOL_STUBS 0x8 +#define S_MOD_INIT_FUNC_POINTERS 0x9 +#define S_MOD_TERM_FUNC_POINTERS 0xa + +#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 +#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 +#define S_ATTR_DEBUG 0x02000000 + + +typedef uint32_t lc_str; + +struct dylib_command { + uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, + LC_REEXPORT_DYLIB */ + uint32_t cmdsize; /* includes pathname string */ + lc_str name; /* library's path name */ + uint32_t timestamp; /* library's build time stamp */ + uint32_t current_version; /* library's current version number */ + uint32_t compatibility_version; /* library's compatibility vers number*/ +}; + +struct rpath_command { + uint32_t cmd; /* LC_RPATH */ + uint32_t cmdsize; /* includes string */ + lc_str path; /* path to add to run path */ +}; + +struct dylinker_command { + uint32_t cmd; /* LC_ID_DYLINKER, LC_LOAD_DYLINKER or + LC_DYLD_ENVIRONMENT */ + uint32_t cmdsize; /* includes pathname string */ + lc_str name; /* dynamic linker's path name */ +}; + +struct linkedit_data_command { + uint32_t cmd; /* LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, + LC_FUNCTION_STARTS, LC_DATA_IN_CODE, + LC_DYLIB_CODE_SIGN_DRS, + LC_LINKER_OPTIMIZATION_HINT, + LC_DYLD_EXPORTS_TRIE, or + LC_DYLD_CHAINED_FIXUPS. */ + uint32_t cmdsize; /* sizeof(struct linkedit_data_command) */ + uint32_t dataoff; /* file offset of data in __LINKEDIT segment */ + uint32_t datasize; /* file size of data in __LINKEDIT segment */ +}; + +#define PLATFORM_MACOS 1 + +struct build_version_command { + uint32_t cmd; /* LC_BUILD_VERSION */ + uint32_t cmdsize; /* sizeof(struct build_version_command) plus */ + /* ntools * sizeof(struct build_tool_version) */ + uint32_t platform; /* platform */ + uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t ntools; /* number of tool entries following this */ +}; + +struct source_version_command { + uint32_t cmd; /* LC_SOURCE_VERSION */ + uint32_t cmdsize; /* 16 */ + uint64_t version; /* A.B.C.D.E packed as a24.b10.c10.d10.e10 */ +}; + +struct symtab_command { + uint32_t cmd; /* LC_SYMTAB */ + uint32_t cmdsize; /* sizeof(struct symtab_command) */ + uint32_t symoff; /* symbol table offset */ + uint32_t nsyms; /* number of symbol table entries */ + uint32_t stroff; /* string table offset */ + uint32_t strsize; /* string table size in bytes */ +}; + +struct dysymtab_command { + uint32_t cmd; /* LC_DYSYMTAB */ + uint32_t cmdsize; /* sizeof(struct dysymtab_command) */ + + uint32_t ilocalsym; /* index to local symbols */ + uint32_t nlocalsym; /* number of local symbols */ + + uint32_t iextdefsym;/* index to externally defined symbols */ + uint32_t nextdefsym;/* number of externally defined symbols */ + + uint32_t iundefsym; /* index to undefined symbols */ + uint32_t nundefsym; /* number of undefined symbols */ + + uint32_t tocoff; /* file offset to table of contents */ + uint32_t ntoc; /* number of entries in table of contents */ + + uint32_t modtaboff; /* file offset to module table */ + uint32_t nmodtab; /* number of module table entries */ + + uint32_t extrefsymoff; /* offset to referenced symbol table */ + uint32_t nextrefsyms; /* number of referenced symbol table entries */ + + uint32_t indirectsymoff;/* file offset to the indirect symbol table */ + uint32_t nindirectsyms; /* number of indirect symbol table entries */ + + uint32_t extreloff; /* offset to external relocation entries */ + uint32_t nextrel; /* number of external relocation entries */ + uint32_t locreloff; /* offset to local relocation entries */ + uint32_t nlocrel; /* number of local relocation entries */ +}; + +#define BIND_OPCODE_DONE 0x00 +#define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30 +#define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40 +#define BIND_OPCODE_SET_TYPE_IMM 0x50 +#define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70 +#define BIND_OPCODE_DO_BIND 0x90 + +#define BIND_SYMBOL_FLAGS_WEAK_IMPORT 0x1 + +#define BIND_TYPE_POINTER 1 +#define BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2 + +#define REBASE_OPCODE_DONE 0x00 +#define REBASE_OPCODE_SET_TYPE_IMM 0x10 +#define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20 +#define REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50 + +#define REBASE_TYPE_POINTER 1 + +#define EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00 +#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04 + +struct dyld_info_command { + uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */ + uint32_t cmdsize; /* sizeof(struct dyld_info_command) */ + uint32_t rebase_off; /* file offset to rebase info */ + uint32_t rebase_size; /* size of rebase info */ + uint32_t bind_off; /* file offset to binding info */ + uint32_t bind_size; /* size of binding info */ + uint32_t weak_bind_off; /* file offset to weak binding info */ + uint32_t weak_bind_size; /* size of weak binding info */ + uint32_t lazy_bind_off; /* file offset to lazy binding info */ + uint32_t lazy_bind_size; /* size of lazy binding infs */ + uint32_t export_off; /* file offset to lazy binding info */ + uint32_t export_size; /* size of lazy binding infs */ +}; + +#define INDIRECT_SYMBOL_LOCAL 0x80000000 + +struct entry_point_command { + uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */ + uint32_t cmdsize; /* 24 */ + uint64_t entryoff; /* file (__TEXT) offset of main() */ + uint64_t stacksize;/* if not zero, initial stack size */ +}; + +enum skind { + sk_unknown = 0, + sk_discard, + sk_text, + sk_stubs, + sk_stub_helper, + sk_ro_data, + sk_uw_info, + sk_nl_ptr, // non-lazy pointers, aka GOT + sk_debug_info, + sk_debug_abbrev, + sk_debug_line, + sk_debug_aranges, + sk_debug_str, + sk_debug_line_str, + sk_stab, + sk_stab_str, + sk_la_ptr, // lazy pointers + sk_init, + sk_fini, + sk_rw_data, + sk_bss, + sk_linkedit, + sk_last +}; + +struct nlist_64 { + uint32_t n_strx; /* index into the string table */ + uint8_t n_type; /* type flag, see below */ + uint8_t n_sect; /* section number or NO_SECT */ + uint16_t n_desc; /* see */ + uint64_t n_value; /* value of this symbol (or stab offset) */ +}; + +#define N_UNDF 0x0 +#define N_ABS 0x2 +#define N_EXT 0x1 +#define N_SECT 0xe + +#define N_WEAK_REF 0x0040 +#define N_WEAK_DEF 0x0080 + +struct macho { + struct mach_header_64 mh; + int *seg2lc, nseg; + struct load_command **lc; + struct entry_point_command *ep; + int nlc; + struct { + Section *s; + int machosect; + } sk_to_sect[sk_last]; + int *elfsectomacho; + int *e2msym; + Section *symtab, *strtab, *indirsyms, *stubs, *exports; + uint32_t ilocal, iextdef, iundef; + int stubsym, n_got, nr_plt; + int segment[sk_last]; +#ifdef CONFIG_NEW_MACHO + Section *chained_fixups; + int n_bind; + int n_bind_rebase; + struct bind_rebase { + int section; + int bind; + ElfW_Rel rel; + } *bind_rebase; +#else + Section *rebase, *binding, *weak_binding, *lazy_binding; + Section *stub_helper, *la_symbol_ptr; + struct dyld_info_command *dyldinfo; + int helpsym, lasym, dyld_private, dyld_stub_binder; + int n_lazy_bind; + struct s_lazy_bind { + int section; + int bind_offset; + int la_symbol_offset; + ElfW_Rel rel; + } *s_lazy_bind; + int n_rebase; + struct s_rebase { + int section; + ElfW_Rel rel; + } *s_rebase; + int n_bind; + struct bind { + int section; + ElfW_Rel rel; + } *bind; +#endif +}; + +#define SHT_LINKEDIT (SHT_LOOS + 42) +#define SHN_FROMDLL (SHN_LOOS + 2) /* Symbol is undefined, comes from a DLL */ + +static void * add_lc(struct macho *mo, uint32_t cmd, uint32_t cmdsize) +{ + struct load_command *lc = tcc_mallocz(cmdsize); + lc->cmd = cmd; + lc->cmdsize = cmdsize; + mo->lc = tcc_realloc(mo->lc, sizeof(mo->lc[0]) * (mo->nlc + 1)); + mo->lc[mo->nlc++] = lc; + return lc; +} + +static struct segment_command_64 * add_segment(struct macho *mo, const char *name) +{ + struct segment_command_64 *sc = add_lc(mo, LC_SEGMENT_64, sizeof(*sc)); + strncpy(sc->segname, name, 16); + mo->seg2lc = tcc_realloc(mo->seg2lc, sizeof(*mo->seg2lc) * (mo->nseg + 1)); + mo->seg2lc[mo->nseg++] = mo->nlc - 1; + return sc; +} + +static struct segment_command_64 * get_segment(struct macho *mo, int i) +{ + return (struct segment_command_64 *) (mo->lc[mo->seg2lc[i]]); +} + +static int add_section(struct macho *mo, struct segment_command_64 **_seg, const char *name) +{ + struct segment_command_64 *seg = *_seg; + int ret = seg->nsects; + struct section_64 *sec; + seg->nsects++; + seg->cmdsize += sizeof(*sec); + seg = tcc_realloc(seg, sizeof(*seg) + seg->nsects * sizeof(*sec)); + sec = (struct section_64*)((char*)seg + sizeof(*seg)) + ret; + memset(sec, 0, sizeof(*sec)); + strncpy(sec->sectname, name, 16); + strncpy(sec->segname, seg->segname, 16); + *_seg = seg; + return ret; +} + +static struct section_64 *get_section(struct segment_command_64 *seg, int i) +{ + return (struct section_64*)((char*)seg + sizeof(*seg)) + i; +} + +static void * add_dylib(struct macho *mo, char *name) +{ + struct dylib_command *lc; + int sz = (sizeof(*lc) + strlen(name) + 1 + 7) & -8; + lc = add_lc(mo, LC_LOAD_DYLIB, sz); + lc->name = sizeof(*lc); + strcpy((char*)lc + lc->name, name); + lc->timestamp = 2; + lc->current_version = 1 << 16; + lc->compatibility_version = 1 << 16; + return lc; +} + +static int uleb128_size (unsigned long long value) +{ + int size = 0; + + do { + value >>= 7; + size++; + } while (value != 0); + return size; +} + +static void write_uleb128(Section *section, uint64_t value) +{ + do { + unsigned char byte = value & 0x7f; + uint8_t *ptr = section_ptr_add(section, 1); + + value >>= 7; + *ptr = byte | (value ? 0x80 : 0); + } while (value != 0); +} + +static void tcc_macho_add_destructor(TCCState *s1) +{ + int init_sym, mh_execute_header, at_exit_sym; + Section *s; + ElfW_Rel *rel; + uint8_t *ptr; + + mh_execute_header = put_elf_sym(s1->symtab, -4096, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), 0, + text_section->sh_num, "__mh_execute_header"); + s = find_section(s1, ".fini_array"); + if (s->data_offset == 0) + return; + init_sym = put_elf_sym(s1->symtab, text_section->data_offset, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_FUNC), 0, + text_section->sh_num, "___GLOBAL_init_65535"); + at_exit_sym = put_elf_sym(s1->symtab, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, + SHN_UNDEF, "___cxa_atexit"); +#ifdef TCC_TARGET_X86_64 + ptr = section_ptr_add(text_section, 4); + ptr[0] = 0x55; // pushq %rbp + ptr[1] = 0x48; // movq %rsp, %rbp + ptr[2] = 0x89; + ptr[3] = 0xe5; + for_each_elem(s->reloc, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + + ptr = section_ptr_add(text_section, 26); + ptr[0] = 0x48; // lea destructor(%rip),%rax + ptr[1] = 0x8d; + ptr[2] = 0x05; + put_elf_reloca(s1->symtab, text_section, + text_section->data_offset - 23, + R_X86_64_PC32, sym_index, -4); + ptr[7] = 0x48; // mov %rax,%rdi + ptr[8] = 0x89; + ptr[9] = 0xc7; + ptr[10] = 0x31; // xorl %ecx, %ecx + ptr[11] = 0xc9; + ptr[12] = 0x89; // movl %ecx, %esi + ptr[13] = 0xce; + ptr[14] = 0x48; // lea mh_execute_header(%rip),%rdx + ptr[15] = 0x8d; + ptr[16] = 0x15; + put_elf_reloca(s1->symtab, text_section, + text_section->data_offset - 9, + R_X86_64_PC32, mh_execute_header, -4); + ptr[21] = 0xe8; // call __cxa_atexit + put_elf_reloca(s1->symtab, text_section, + text_section->data_offset - 4, + R_X86_64_PLT32, at_exit_sym, -4); + } + ptr = section_ptr_add(text_section, 2); + ptr[0] = 0x5d; // pop %rbp + ptr[1] = 0xc3; // ret +#elif defined TCC_TARGET_ARM64 + ptr = section_ptr_add(text_section, 8); + write32le(ptr, 0xa9bf7bfd); // stp x29, x30, [sp, #-16]! + write32le(ptr + 4, 0x910003fd); // mov x29, sp + for_each_elem(s->reloc, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + + ptr = section_ptr_add(text_section, 24); + put_elf_reloc(s1->symtab, text_section, + text_section->data_offset - 24, + R_AARCH64_ADR_PREL_PG_HI21, sym_index); + write32le(ptr, 0x90000000); // adrp x0, destructor@page + put_elf_reloc(s1->symtab, text_section, + text_section->data_offset - 20, + R_AARCH64_LDST8_ABS_LO12_NC, sym_index); + write32le(ptr + 4, 0x91000000); // add x0,x0,destructor@pageoff + write32le(ptr + 8, 0xd2800001); // mov x1, #0 + put_elf_reloc(s1->symtab, text_section, + text_section->data_offset - 12, + R_AARCH64_ADR_PREL_PG_HI21, mh_execute_header); + write32le(ptr + 12, 0x90000002); // adrp x2, mh_execute_header@page + put_elf_reloc(s1->symtab, text_section, + text_section->data_offset - 8, + R_AARCH64_LDST8_ABS_LO12_NC, mh_execute_header); + write32le(ptr + 16, 0x91000042); // add x2,x2,mh_execute_header@pageoff + put_elf_reloc(s1->symtab, text_section, + text_section->data_offset - 4, + R_AARCH64_CALL26, at_exit_sym); + write32le(ptr + 20, 0x94000000); // bl __cxa_atexit + } + ptr = section_ptr_add(text_section, 8); + write32le(ptr, 0xa8c17bfd); // ldp x29, x30, [sp], #16 + write32le(ptr + 4, 0xd65f03c0); // ret +#endif + s->reloc->data_offset = s->data_offset = 0; + s->sh_flags &= ~SHF_ALLOC; + add_array (s1, ".init_array", init_sym); +} + +#ifdef CONFIG_NEW_MACHO +static void bind_rebase_add(struct macho *mo, int bind, int sh_info, + ElfW_Rel *rel, struct sym_attr *attr) +{ + mo->bind_rebase = tcc_realloc(mo->bind_rebase, (mo->n_bind_rebase + 1) * + sizeof(struct bind_rebase)); + mo->bind_rebase[mo->n_bind_rebase].section = sh_info; + mo->bind_rebase[mo->n_bind_rebase].bind = bind; + mo->bind_rebase[mo->n_bind_rebase].rel = *rel; + if (attr) + mo->bind_rebase[mo->n_bind_rebase].rel.r_offset = attr->got_offset; + mo->n_bind_rebase++; + mo->n_bind += bind; +} + +static void check_relocs(TCCState *s1, struct macho *mo) +{ + Section *s; + ElfW_Rel *rel, save_rel; + ElfW(Sym) *sym; + int i, j, type, gotplt_entry, sym_index, for_code; + uint32_t *pi, *goti; + struct sym_attr *attr; + + goti = NULL; + mo->nr_plt = mo->n_got = 0; + for (i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->sh_type != SHT_RELX || + !strncmp(s1->sections[s->sh_info]->name, ".debug_", 7)) + continue; + for_each_elem(s, 0, rel, ElfW_Rel) { + save_rel = *rel; + type = ELFW(R_TYPE)(rel->r_info); + gotplt_entry = gotplt_entry_type(type); + for_code = code_reloc(type); + /* We generate a non-lazy pointer for used undefined symbols + and for defined symbols that must have a place for their + address due to codegen (i.e. a reloc requiring a got slot). */ + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if (sym->st_shndx == SHN_UNDEF + || gotplt_entry == ALWAYS_GOTPLT_ENTRY) { + attr = get_sym_attr(s1, sym_index, 1); + if (!attr->dyn_index) { + attr->got_offset = s1->got->data_offset; + attr->plt_offset = -1; + attr->dyn_index = 1; /* used as flag */ + section_ptr_add(s1->got, PTR_SIZE); + put_elf_reloc(s1->symtab, s1->got, attr->got_offset, + R_JMP_SLOT, sym_index); + goti = tcc_realloc(goti, (mo->n_got + 1) * sizeof(*goti)); + if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) { + if (sym->st_shndx == SHN_UNDEF) + tcc_error("undefined local symbo: '%s'", + (char *) symtab_section->link->data + sym->st_name); + goti[mo->n_got++] = INDIRECT_SYMBOL_LOCAL; + } else { + goti[mo->n_got++] = mo->e2msym[sym_index]; + if (sym->st_shndx == SHN_UNDEF +#ifdef TCC_TARGET_X86_64 + && type == R_X86_64_GOTPCREL +#elif defined TCC_TARGET_ARM64 + && type == R_AARCH64_ADR_GOT_PAGE +#endif + ) { + attr->plt_offset = -mo->n_bind_rebase - 2; + bind_rebase_add(mo, 1, s1->got->reloc->sh_info, &save_rel, attr); + s1->got->reloc->data_offset -= sizeof (ElfW_Rel); + } + if (for_code && sym->st_shndx == SHN_UNDEF) + s1->got->reloc->data_offset -= sizeof (ElfW_Rel); + } + } + if (for_code && sym->st_shndx == SHN_UNDEF) { + if ((int)attr->plt_offset < -1) { + /* remove above bind and replace with plt */ + mo->bind_rebase[-attr->plt_offset - 2].bind = 2; + attr->plt_offset = -1; + } + if (attr->plt_offset == -1) { + uint8_t *jmp; + + attr->plt_offset = mo->stubs->data_offset; +#ifdef TCC_TARGET_X86_64 + if (type != R_X86_64_PLT32) + continue; + jmp = section_ptr_add(mo->stubs, 6); + jmp[0] = 0xff; /* jmpq *ofs(%rip) */ + jmp[1] = 0x25; + put_elf_reloc(s1->symtab, mo->stubs, + attr->plt_offset + 2, + R_X86_64_GOTPCREL, sym_index); +#elif defined TCC_TARGET_ARM64 + if (type != R_AARCH64_CALL26) + continue; + jmp = section_ptr_add(mo->stubs, 12); + put_elf_reloc(s1->symtab, mo->stubs, + attr->plt_offset, + R_AARCH64_ADR_GOT_PAGE, sym_index); + write32le(jmp, // adrp x16, #sym + 0x90000010); + put_elf_reloc(s1->symtab, mo->stubs, + attr->plt_offset + 4, + R_AARCH64_LD64_GOT_LO12_NC, sym_index); + write32le(jmp + 4, // ld x16,[x16, #sym] + 0xf9400210); + write32le(jmp + 8, // br x16 + 0xd61f0200); +#endif + bind_rebase_add(mo, 1, s1->got->reloc->sh_info, &save_rel, attr); + pi = section_ptr_add(mo->indirsyms, sizeof(*pi)); + *pi = mo->e2msym[sym_index]; + mo->nr_plt++; + } + rel->r_info = ELFW(R_INFO)(mo->stubsym, type); + rel->r_addend += attr->plt_offset; + } + } + if (type == R_DATA_PTR || type == R_JMP_SLOT) + bind_rebase_add(mo, sym->st_shndx == SHN_UNDEF ? 1 : 0, + s->sh_info, &save_rel, NULL); + } + } + /* remove deleted binds */ + for (i = 0, j = 0; i < mo->n_bind_rebase; i++) + if (mo->bind_rebase[i].bind == 2) + mo->n_bind--; + else + mo->bind_rebase[j++] = mo->bind_rebase[i]; + mo->n_bind_rebase = j; + pi = section_ptr_add(mo->indirsyms, mo->n_got * sizeof(*pi)); + memcpy(pi, goti, mo->n_got * sizeof(*pi)); + tcc_free(goti); +} + +#else + +static void check_relocs(TCCState *s1, struct macho *mo) +{ + uint8_t *jmp; + Section *s; + ElfW_Rel *rel, save_rel; + ElfW(Sym) *sym; + int i, type, gotplt_entry, sym_index, for_code; + int bind_offset, la_symbol_offset; + uint32_t *pi, *goti; + struct sym_attr *attr; + +#ifdef TCC_TARGET_X86_64 + jmp = section_ptr_add(mo->stub_helper, 16); + jmp[0] = 0x4c; /* leaq _dyld_private(%rip), %r11 */ + jmp[1] = 0x8d; + jmp[2] = 0x1d; + put_elf_reloca(s1->symtab, mo->stub_helper, 3, + R_X86_64_PC32, mo->dyld_private, -4); + jmp[7] = 0x41; /* pushq %r11 */ + jmp[8] = 0x53; + jmp[9] = 0xff; /* jmpq *dyld_stub_binder@GOT(%rip) */ + jmp[10] = 0x25; + put_elf_reloca(s1->symtab, mo->stub_helper, 11, + R_X86_64_GOTPCREL, mo->dyld_stub_binder, -4); + jmp[15] = 0x90; /* nop */ +#elif defined TCC_TARGET_ARM64 + jmp = section_ptr_add(mo->stub_helper, 24); + put_elf_reloc(s1->symtab, mo->stub_helper, 0, + R_AARCH64_ADR_PREL_PG_HI21, mo->dyld_private); + write32le(jmp, 0x90000011); // adrp x17, _dyld_private@page + put_elf_reloc(s1->symtab, mo->stub_helper, 4, + R_AARCH64_LDST64_ABS_LO12_NC, mo->dyld_private); + write32le(jmp + 4, 0x91000231); // add x17,x17,_dyld_private@pageoff + write32le(jmp + 8, 0xa9bf47f0); // stp x16/x17, [sp, #-16]! + put_elf_reloc(s1->symtab, mo->stub_helper, 12, + R_AARCH64_ADR_GOT_PAGE, mo->dyld_stub_binder); + write32le(jmp + 12, 0x90000010); // adrp x16, dyld_stub_binder@page + put_elf_reloc(s1->symtab, mo->stub_helper, 16, + R_AARCH64_LD64_GOT_LO12_NC, mo->dyld_stub_binder); + write32le(jmp + 16, 0xf9400210); // ldr x16,[x16,dyld_stub_binder@pageoff] + write32le(jmp + 20, 0xd61f0200); // br x16 +#endif + + goti = NULL; + mo->nr_plt = mo->n_got = 0; + for (i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->sh_type != SHT_RELX || + !strncmp(s1->sections[s->sh_info]->name, ".debug_", 7)) + continue; + for_each_elem(s, 0, rel, ElfW_Rel) { + save_rel = *rel; + type = ELFW(R_TYPE)(rel->r_info); + gotplt_entry = gotplt_entry_type(type); + for_code = code_reloc(type); + /* We generate a non-lazy pointer for used undefined symbols + and for defined symbols that must have a place for their + address due to codegen (i.e. a reloc requiring a got slot). */ + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if (sym->st_shndx == SHN_UNDEF + || gotplt_entry == ALWAYS_GOTPLT_ENTRY) { + attr = get_sym_attr(s1, sym_index, 1); + if (!attr->dyn_index) { + attr->got_offset = s1->got->data_offset; + attr->plt_offset = -1; + attr->dyn_index = 1; /* used as flag */ + section_ptr_add(s1->got, PTR_SIZE); + put_elf_reloc(s1->symtab, s1->got, attr->got_offset, + R_JMP_SLOT, sym_index); + goti = tcc_realloc(goti, (mo->n_got + 1) * sizeof(*goti)); + if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) { + if (sym->st_shndx == SHN_UNDEF) + tcc_error("undefined local symbo: '%s'", + (char *) symtab_section->link->data + sym->st_name); + goti[mo->n_got++] = INDIRECT_SYMBOL_LOCAL; + } else { + goti[mo->n_got++] = mo->e2msym[sym_index]; + if (sym->st_shndx == SHN_UNDEF +#ifdef TCC_TARGET_X86_64 + && type == R_X86_64_GOTPCREL +#elif defined TCC_TARGET_ARM64 + && type == R_AARCH64_ADR_GOT_PAGE +#endif + ) { + mo->bind = + tcc_realloc(mo->bind, + (mo->n_bind + 1) * + sizeof(struct bind)); + mo->bind[mo->n_bind].section = s1->got->reloc->sh_info; + mo->bind[mo->n_bind].rel = save_rel; + mo->bind[mo->n_bind].rel.r_offset = attr->got_offset; + mo->n_bind++; + s1->got->reloc->data_offset -= sizeof (ElfW_Rel); + } + } + } + if (for_code && sym->st_shndx == SHN_UNDEF) { + if (attr->plt_offset == -1) { + attr->plt_offset = mo->stubs->data_offset; +#ifdef TCC_TARGET_X86_64 + if (type != R_X86_64_PLT32) + continue; + /* __stubs */ + jmp = section_ptr_add(mo->stubs, 6); + jmp[0] = 0xff; /* jmpq *__la_symbol_ptr(%rip) */ + jmp[1] = 0x25; + put_elf_reloca(s1->symtab, mo->stubs, + mo->stubs->data_offset - 4, + R_X86_64_PC32, mo->lasym, + mo->la_symbol_ptr->data_offset - 4); + + /* __stub_helper */ + bind_offset = mo->stub_helper->data_offset + 1; + jmp = section_ptr_add(mo->stub_helper, 10); + jmp[0] = 0x68; /* pushq $bind_offset */ + jmp[5] = 0xe9; /* jmpq __stub_helper */ + write32le(jmp + 6, -mo->stub_helper->data_offset); + + /* __la_symbol_ptr */ + la_symbol_offset = mo->la_symbol_ptr->data_offset; + put_elf_reloca(s1->symtab, mo->la_symbol_ptr, + mo->la_symbol_ptr->data_offset, + R_DATA_PTR, mo->helpsym, + mo->stub_helper->data_offset - 10); + section_ptr_add(mo->la_symbol_ptr, PTR_SIZE); +#elif defined TCC_TARGET_ARM64 + if (type != R_AARCH64_CALL26) + continue; + /* __stubs */ + jmp = section_ptr_add(mo->stubs, 12); + put_elf_reloca(s1->symtab, mo->stubs, + mo->stubs->data_offset - 12, + R_AARCH64_ADR_PREL_PG_HI21, mo->lasym, + mo->la_symbol_ptr->data_offset); + write32le(jmp, // adrp x16, __la_symbol_ptr@page + 0x90000010); + put_elf_reloca(s1->symtab, mo->stubs, + mo->stubs->data_offset - 8, + R_AARCH64_LDST64_ABS_LO12_NC, mo->lasym, + mo->la_symbol_ptr->data_offset); + write32le(jmp + 4, // ldr x16,[x16, __la_symbol_ptr@pageoff] + 0xf9400210); + write32le(jmp + 8, // br x16 + 0xd61f0200); + + /* __stub_helper */ + bind_offset = mo->stub_helper->data_offset + 8; + jmp = section_ptr_add(mo->stub_helper, 12); + write32le(jmp + 0, // ldr w16, l0 + 0x18000050); + write32le(jmp + 4, // b stubHelperHeader + 0x14000000 + + ((-(mo->stub_helper->data_offset - 8) / 4) & + 0x3ffffff)); + write32le(jmp + 8, 0); // l0: .long bind_offset + + /* __la_symbol_ptr */ + la_symbol_offset = mo->la_symbol_ptr->data_offset; + put_elf_reloca(s1->symtab, mo->la_symbol_ptr, + mo->la_symbol_ptr->data_offset, + R_DATA_PTR, mo->helpsym, + mo->stub_helper->data_offset - 12); + section_ptr_add(mo->la_symbol_ptr, PTR_SIZE); +#endif + mo->s_lazy_bind = + tcc_realloc(mo->s_lazy_bind, (mo->n_lazy_bind + 1) * + sizeof(struct s_lazy_bind)); + mo->s_lazy_bind[mo->n_lazy_bind].section = + mo->stub_helper->reloc->sh_info; + mo->s_lazy_bind[mo->n_lazy_bind].bind_offset = + bind_offset; + mo->s_lazy_bind[mo->n_lazy_bind].la_symbol_offset = + la_symbol_offset; + mo->s_lazy_bind[mo->n_lazy_bind].rel = save_rel; + mo->s_lazy_bind[mo->n_lazy_bind].rel.r_offset = + attr->plt_offset; + mo->n_lazy_bind++; + pi = section_ptr_add(mo->indirsyms, sizeof(*pi)); + *pi = mo->e2msym[sym_index]; + mo->nr_plt++; + } + rel->r_info = ELFW(R_INFO)(mo->stubsym, type); + rel->r_addend += attr->plt_offset; + } + } + if (type == R_DATA_PTR || type == R_JMP_SLOT) { + if (sym->st_shndx == SHN_UNDEF) { + mo->bind = tcc_realloc(mo->bind, + (mo->n_bind + 1) * + sizeof(struct bind)); + mo->bind[mo->n_bind].section = s->sh_info; + mo->bind[mo->n_bind].rel = save_rel; + mo->n_bind++; + } + else { + mo->s_rebase = + tcc_realloc(mo->s_rebase, (mo->n_rebase + 1) * + sizeof(struct s_rebase)); + mo->s_rebase[mo->n_rebase].section = s->sh_info; + mo->s_rebase[mo->n_rebase].rel = save_rel; + mo->n_rebase++; + } + } + } + } + pi = section_ptr_add(mo->indirsyms, mo->n_got * sizeof(*pi)); + memcpy(pi, goti, mo->n_got * sizeof(*pi)); + pi = section_ptr_add(mo->indirsyms, mo->nr_plt * sizeof(*pi)); + memcpy(pi, mo->indirsyms->data, mo->nr_plt * sizeof(*pi)); + tcc_free(goti); +} +#endif + +static int check_symbols(TCCState *s1, struct macho *mo) +{ + int sym_index, sym_end; + int ret = 0; + + mo->ilocal = mo->iextdef = mo->iundef = -1; + sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + int elf_index = ((struct nlist_64 *)mo->symtab->data + sym_index - 1)->n_value; + ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + elf_index; + const char *name = (char*)symtab_section->link->data + sym->st_name; + unsigned type = ELFW(ST_TYPE)(sym->st_info); + unsigned bind = ELFW(ST_BIND)(sym->st_info); + unsigned vis = ELFW(ST_VISIBILITY)(sym->st_other); + + dprintf("%4d (%4d): %09lx %4d %4d %4d %3d %s\n", + sym_index, elf_index, (long)sym->st_value, + type, bind, vis, sym->st_shndx, name); + if (bind == STB_LOCAL) { + if (mo->ilocal == -1) + mo->ilocal = sym_index - 1; + if (mo->iextdef != -1 || mo->iundef != -1) + tcc_error("local syms after global ones"); + } else if (sym->st_shndx != SHN_UNDEF) { + if (mo->iextdef == -1) + mo->iextdef = sym_index - 1; + if (mo->iundef != -1) + tcc_error("external defined symbol after undefined"); + } else if (sym->st_shndx == SHN_UNDEF) { + if (mo->iundef == -1) + mo->iundef = sym_index - 1; + if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK + || s1->output_type != TCC_OUTPUT_EXE + || find_elf_sym(s1->dynsymtab_section, name)) { + /* Mark the symbol as coming from a dylib so that + relocate_syms doesn't complain. Normally bind_exe_dynsyms + would do this check, and place the symbol into dynsym + which is checked by relocate_syms. But Mach-O doesn't use + bind_exe_dynsyms. */ + sym->st_shndx = SHN_FROMDLL; + continue; + } + tcc_error_noabort("undefined symbol '%s'", name); + ret = -1; + } + } + return ret; +} + +static void convert_symbol(TCCState *s1, struct macho *mo, struct nlist_64 *pn) +{ + struct nlist_64 n = *pn; + ElfSym *sym = (ElfW(Sym) *)symtab_section->data + pn->n_value; + const char *name = (char*)symtab_section->link->data + sym->st_name; + switch(ELFW(ST_TYPE)(sym->st_info)) { + case STT_NOTYPE: + case STT_OBJECT: + case STT_FUNC: + case STT_SECTION: + n.n_type = N_SECT; + break; + case STT_FILE: + n.n_type = N_ABS; + break; + default: + tcc_error("unhandled ELF symbol type %d %s", + ELFW(ST_TYPE)(sym->st_info), name); + } + if (sym->st_shndx == SHN_UNDEF) + tcc_error("should have been rewritten to SHN_FROMDLL: %s", name); + else if (sym->st_shndx == SHN_FROMDLL) + n.n_type = N_UNDF, n.n_sect = 0; + else if (sym->st_shndx == SHN_ABS) + n.n_type = N_ABS, n.n_sect = 0; + else if (sym->st_shndx >= SHN_LORESERVE) + tcc_error("unhandled ELF symbol section %d %s", sym->st_shndx, name); + else if (!mo->elfsectomacho[sym->st_shndx]) { + if (strncmp(s1->sections[sym->st_shndx]->name, ".debug_", 7)) + tcc_error("ELF section %d(%s) not mapped into Mach-O for symbol %s", + sym->st_shndx, s1->sections[sym->st_shndx]->name, name); + } + else + n.n_sect = mo->elfsectomacho[sym->st_shndx]; + if (ELFW(ST_BIND)(sym->st_info) == STB_GLOBAL) + n.n_type |= N_EXT; + else if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + n.n_desc |= N_WEAK_REF | (n.n_type != N_UNDF ? N_WEAK_DEF : 0); + n.n_strx = pn->n_strx; + n.n_value = sym->st_value; + *pn = n; +} + +static void convert_symbols(TCCState *s1, struct macho *mo) +{ + struct nlist_64 *pn; + for_each_elem(mo->symtab, 0, pn, struct nlist_64) + convert_symbol(s1, mo, pn); +} + +static int machosymcmp(const void *_a, const void *_b, void *arg) +{ + TCCState *s1 = arg; + int ea = ((struct nlist_64 *)_a)->n_value; + int eb = ((struct nlist_64 *)_b)->n_value; + ElfSym *sa = (ElfSym *)symtab_section->data + ea; + ElfSym *sb = (ElfSym *)symtab_section->data + eb; + int r; + /* locals, then defined externals, then undefined externals, the + last two sections also by name, otherwise stable sort */ + r = (ELFW(ST_BIND)(sb->st_info) == STB_LOCAL) + - (ELFW(ST_BIND)(sa->st_info) == STB_LOCAL); + if (r) + return r; + r = (sa->st_shndx == SHN_UNDEF) - (sb->st_shndx == SHN_UNDEF); + if (r) + return r; + if (ELFW(ST_BIND)(sa->st_info) != STB_LOCAL) { + const char * na = (char*)symtab_section->link->data + sa->st_name; + const char * nb = (char*)symtab_section->link->data + sb->st_name; + r = strcmp(na, nb); + if (r) + return r; + } + return ea - eb; +} + +/* cannot use qsort because code has to be reentrant */ +static void tcc_qsort (void *base, size_t nel, size_t width, + int (*comp)(const void *, const void *, void *), void *arg) +{ + size_t wnel, gap, wgap, i, j, k; + char *a, *b, tmp; + + wnel = width * nel; + for (gap = 0; ++gap < nel;) + gap *= 3; + while ( gap /= 3 ) { + wgap = width * gap; + for (i = wgap; i < wnel; i += width) { + for (j = i - wgap; ;j -= wgap) { + a = j + (char *)base; + b = a + wgap; + if ( (*comp)(a, b, arg) <= 0 ) + break; + k = width; + do { + tmp = *a; + *a++ = *b; + *b++ = tmp; + } while ( --k ); + if (j < wgap) + break; + } + } + } +} + +static void create_symtab(TCCState *s1, struct macho *mo) +{ + int sym_index, sym_end; + struct nlist_64 *pn; + + /* Stub creation belongs to check_relocs, but we need to create + the symbol now, so its included in the sorting. */ + mo->stubs = new_section(s1, "__stubs", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); + s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + mo->stubsym = put_elf_sym(s1->symtab, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, + mo->stubs->sh_num, ".__stubs"); +#ifdef CONFIG_NEW_MACHO + mo->chained_fixups = new_section(s1, "CHAINED_FIXUPS", + SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); +#else + mo->stub_helper = new_section(s1, "__stub_helper", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); + mo->la_symbol_ptr = new_section(s1, "__la_symbol_ptr", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + mo->helpsym = put_elf_sym(s1->symtab, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, + mo->stub_helper->sh_num, ".__stub_helper"); + mo->lasym = put_elf_sym(s1->symtab, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, + mo->la_symbol_ptr->sh_num, ".__la_symbol_ptr"); + section_ptr_add(data_section, -data_section->data_offset & (PTR_SIZE - 1)); + mo->dyld_private = put_elf_sym(s1->symtab, data_section->data_offset, PTR_SIZE, + ELFW(ST_INFO)(STB_LOCAL, STT_OBJECT), 0, + data_section->sh_num, ".__dyld_private"); + section_ptr_add(data_section, PTR_SIZE); + mo->dyld_stub_binder = put_elf_sym(s1->symtab, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), 0, + SHN_UNDEF, "dyld_stub_binder"); + mo->rebase = new_section(s1, "REBASE", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + mo->binding = new_section(s1, "BINDING", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + mo->weak_binding = new_section(s1, "WEAK_BINDING", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + mo->lazy_binding = new_section(s1, "LAZY_BINDING", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); +#endif + mo->exports = new_section(s1, "EXPORT", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + mo->indirsyms = new_section(s1, "LEINDIR", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + + mo->symtab = new_section(s1, "LESYMTAB", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + mo->strtab = new_section(s1, "LESTRTAB", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); + put_elf_str(mo->strtab, " "); /* Mach-O starts strtab with a space */ + sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + pn = section_ptr_add(mo->symtab, sizeof(*pn) * (sym_end - 1)); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + sym_index; + const char *name = (char*)symtab_section->link->data + sym->st_name; + pn[sym_index - 1].n_strx = put_elf_str(mo->strtab, name); + pn[sym_index - 1].n_value = sym_index; + } + section_ptr_add(mo->strtab, -mo->strtab->data_offset & (PTR_SIZE - 1)); + tcc_qsort(pn, sym_end - 1, sizeof(*pn), machosymcmp, s1); + mo->e2msym = tcc_malloc(sym_end * sizeof(*mo->e2msym)); + mo->e2msym[0] = -1; + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + mo->e2msym[pn[sym_index - 1].n_value] = sym_index - 1; + } +} + +const struct { + int seg_initial; + uint32_t flags; + const char *name; +} skinfo[sk_last] = { + /*[sk_unknown] =*/ { 0 }, + /*[sk_discard] =*/ { 0 }, + /*[sk_text] =*/ { 1, S_REGULAR | S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS, "__text" }, + /*[sk_stubs] =*/ { 1, S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_SYMBOL_STUBS + | S_ATTR_SOME_INSTRUCTIONS , "__stubs" }, + /*[sk_stub_helper] =*/ { 1, S_REGULAR | S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS , "__stub_helper" }, + /*[sk_ro_data] =*/ { 2, S_REGULAR, "__rodata" }, + /*[sk_uw_info] =*/ { 0 }, + /*[sk_nl_ptr] =*/ { 2, S_NON_LAZY_SYMBOL_POINTERS, "__got" }, + /*[sk_debug_info] =*/ { 3, S_REGULAR | S_ATTR_DEBUG, "__debug_info" }, + /*[sk_debug_abbrev] =*/ { 3, S_REGULAR | S_ATTR_DEBUG, "__debug_abbrev" }, + /*[sk_debug_line] =*/ { 3, S_REGULAR | S_ATTR_DEBUG, "__debug_line" }, + /*[sk_debug_aranges] =*/ { 3, S_REGULAR | S_ATTR_DEBUG, "__debug_aranges" }, + /*[sk_debug_str] =*/ { 3, S_REGULAR | S_ATTR_DEBUG, "__debug_str" }, + /*[sk_debug_line_str] =*/ { 3, S_REGULAR | S_ATTR_DEBUG, "__debug_line_str" }, + /*[sk_stab] =*/ { 4, S_REGULAR, "__stab" }, + /*[sk_stab_str] =*/ { 4, S_REGULAR, "__stab_str" }, + /*[sk_la_ptr] =*/ { 4, S_LAZY_SYMBOL_POINTERS, "__la_symbol_ptr" }, + /*[sk_init] =*/ { 4, S_MOD_INIT_FUNC_POINTERS, "__mod_init_func" }, + /*[sk_fini] =*/ { 4, S_MOD_TERM_FUNC_POINTERS, "__mod_term_func" }, + /*[sk_rw_data] =*/ { 4, S_REGULAR, "__data" }, + /*[sk_bss] =*/ { 4, S_ZEROFILL, "__bss" }, + /*[sk_linkedit] =*/ { 5, S_REGULAR, NULL }, +}; + +#define START ((uint64_t)1 << 32) + +const struct { + int used; + const char *name; + uint64_t vmaddr; + uint64_t vmsize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t flags; +} all_segment[] = { + { 1, "__PAGEZERO", 0, START, 0, 0, 0 }, + { 0, "__TEXT", START, 0, 5, 5, 0 }, + { 0, "__DATA_CONST", -1, 0, 3, 3, SG_READ_ONLY }, + { 0, "__DWARF", -1, 0, 7, 3, 0 }, + { 0, "__DATA", -1, 0, 3, 3, 0 }, + { 1, "__LINKEDIT", -1, 0, 1, 1, 0 }, +}; + +#define N_SEGMENT (sizeof(all_segment)/sizeof(all_segment[0])) + +#ifdef CONFIG_NEW_MACHO +static void calc_fixup_size(TCCState *s1, struct macho *mo) +{ + int i, size; + + size = (sizeof(struct dyld_chained_fixups_header) + 7) & -8; + size += (sizeof(struct dyld_chained_starts_in_image) + (mo->nseg - 1) * sizeof(uint32_t) + 7) & -8; + for (i = (s1->output_type == TCC_OUTPUT_EXE); i < mo->nseg - 1; i++) { + int page_count = (get_segment(mo, i)->vmsize + SEG_PAGE_SIZE - 1) / SEG_PAGE_SIZE; + size += (sizeof(struct dyld_chained_starts_in_segment) + (page_count - 1) * sizeof(uint16_t) + 7) & -8; + } + size += mo->n_bind * sizeof (struct dyld_chained_import) + 1; + for (i = 0; i < mo->n_bind_rebase; i++) { + if (mo->bind_rebase[i].bind) { + int sym_index = ELFW(R_SYM)(mo->bind_rebase[i].rel.r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + const char *name = (char *) symtab_section->link->data + sym->st_name; + size += strlen(name) + 1; + } + } + size = (size + 7) & -8; + section_ptr_add(mo->chained_fixups, size); +} + +#else + +static void set_segment_and_offset(TCCState *s1, struct macho *mo, addr_t addr, + uint8_t *ptr, int opcode, + Section *sec, addr_t offset) +{ + int i; + struct segment_command_64 *seg = NULL; + + for (i = (s1->output_type == TCC_OUTPUT_EXE); i < mo->nseg - 1; i++) { + seg = get_segment(mo, i); + if (addr >= seg->vmaddr && addr < (seg->vmaddr + seg->vmsize)) + break; + } + *ptr = opcode | i; + write_uleb128(sec, offset - seg->vmaddr); +} + +static void bind_rebase(TCCState *s1, struct macho *mo) +{ + int i; + uint8_t *ptr; + ElfW(Sym) *sym; + const char *name; + + for (i = 0; i < mo->n_lazy_bind; i++) { + int sym_index = ELFW(R_SYM)(mo->s_lazy_bind[i].rel.r_info); + + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + name = (char *) symtab_section->link->data + sym->st_name; + write32le(mo->stub_helper->data + + mo->s_lazy_bind[i].bind_offset, + mo->lazy_binding->data_offset); + ptr = section_ptr_add(mo->lazy_binding, 1); + set_segment_and_offset(s1, mo, mo->la_symbol_ptr->sh_addr, ptr, + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, + mo->lazy_binding, + mo->s_lazy_bind[i].la_symbol_offset + + mo->la_symbol_ptr->sh_addr); + ptr = section_ptr_add(mo->lazy_binding, 5 + strlen(name)); + *ptr++ = BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | + (BIND_SPECIAL_DYLIB_FLAT_LOOKUP & 0xf); + *ptr++ = BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0; + strcpy((char *)ptr, name); + ptr += strlen(name) + 1; + *ptr++ = BIND_OPCODE_DO_BIND; + *ptr = BIND_OPCODE_DONE; + } + for (i = 0; i < mo->n_rebase; i++) { + Section *s = s1->sections[mo->s_rebase[i].section]; + + ptr = section_ptr_add(mo->rebase, 2); + *ptr++ = REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER; + set_segment_and_offset(s1, mo, s->sh_addr, ptr, + REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, + mo->rebase, + mo->s_rebase[i].rel.r_offset + + s->sh_addr); + ptr = section_ptr_add(mo->rebase, 1); + *ptr = REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1; + } + for (i = 0; i < mo->n_bind; i++) { + int sym_index = ELFW(R_SYM)(mo->bind[i].rel.r_info); + Section *s = s1->sections[mo->bind[i].section]; + Section *binding; + + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + name = (char *) symtab_section->link->data + sym->st_name; + binding = ELFW(ST_BIND)(sym->st_info) == STB_WEAK + ? mo->weak_binding : mo->binding; + ptr = section_ptr_add(binding, 4 + (binding == mo->binding) + + strlen(name)); + if (binding == mo->binding) + *ptr++ = BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | + (BIND_SPECIAL_DYLIB_FLAT_LOOKUP & 0xf); + *ptr++ = BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | + (binding == mo->weak_binding + ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0); + strcpy((char *)ptr, name); + ptr += strlen(name) + 1; + *ptr++ = BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER; + set_segment_and_offset(s1, mo, s->sh_addr, ptr, + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, + binding, + mo->bind[i].rel.r_offset + s->sh_addr); + ptr = section_ptr_add(binding, 1); + *ptr++ = BIND_OPCODE_DO_BIND; + } + if (mo->rebase->data_offset) { + ptr = section_ptr_add(mo->rebase, 1); + *ptr = REBASE_OPCODE_DONE; + } + if (mo->binding->data_offset) { + ptr = section_ptr_add(mo->binding, 1); + *ptr = BIND_OPCODE_DONE; + } + if (mo->weak_binding->data_offset) { + ptr = section_ptr_add(mo->weak_binding, 1); + *ptr = BIND_OPCODE_DONE; + } + tcc_free(mo->s_lazy_bind); + tcc_free(mo->s_rebase); + tcc_free(mo->bind); +} +#endif + +struct trie_info { + const char *name; + int flag; + addr_t addr; + int str_size; + int term_size; +}; + +struct trie_node { + int start; + int end; + int index_start; + int index_end; + int n_child; + struct trie_node *child; +}; + +struct trie_seq { + int n_child; + struct trie_node *node; + int offset; + int nest_offset; +}; + +static void create_trie(struct trie_node *node, + int from, int to, int index_start, + int n_trie, struct trie_info *trie) +{ + int i; + int start, end, index_end; + char cur; + struct trie_node *child; + + for (i = from; i < to; i = end) { + cur = trie[i].name[index_start]; + start = i++; + for (; i < to; i++) + if (cur != trie[i].name[index_start]) + break; + end = i; + if (start == end - 1 || + (trie[start].name[index_start] && + trie[start].name[index_start + 1] == 0)) + index_end = trie[start].str_size - 1; + else { + index_end = index_start + 1; + for (;;) { + cur = trie[start].name[index_end]; + for (i = start + 1; i < end; i++) + if (cur != trie[i].name[index_end]) + break; + if (trie[start].name[index_end] && + trie[start].name[index_end + 1] == 0) { + end = start + 1; + index_end = trie[start].str_size - 1; + break; + } + if (i != end) + break; + index_end++; + } + } + node->child = tcc_realloc(node->child, + (node->n_child + 1) * + sizeof(struct trie_node)); + child = &node->child[node->n_child]; + child->start = start; + child->end = end; + child->index_start = index_start; + child->index_end = index_end; + child->n_child = 0; + child->child = NULL; + node->n_child++; + if (start != end - 1) + create_trie(child, start, end, index_end, n_trie, trie); + } +} + +static int create_seq(int *offset, int *n_seq, struct trie_seq **seq, + struct trie_node *node, + int n_trie, struct trie_info *trie) +{ + int i, nest_offset, last_seq = *n_seq, retval = *offset; + struct trie_seq *p_seq; + struct trie_node *p_nest; + + for (i = 0; i < node->n_child; i++) { + p_nest = &node->child[i]; + *seq = tcc_realloc(*seq, (*n_seq + 1) * sizeof(struct trie_seq)); + p_seq = &(*seq)[(*n_seq)++]; + p_seq->n_child = i == 0 ? node->n_child : -1; + p_seq->node = p_nest; + p_seq->offset = *offset; + p_seq->nest_offset = 0; + *offset += (i == 0 ? 1 + 1 : 0) + + p_nest->index_end - p_nest->index_start + 1 + 3; + } + for (i = 0; i < node->n_child; i++) { + nest_offset = + create_seq(offset, n_seq, seq, &node->child[i], n_trie, trie); + p_seq = &(*seq)[last_seq + i]; + p_seq->nest_offset = nest_offset; + } + return retval; +} + +static void node_free(struct trie_node *node) +{ + int i; + + for (i = 0; i < node->n_child; i++) + node_free(&node->child[i]); + tcc_free(node->child); +} + +static int triecmp(const void *_a, const void *_b, void *arg) +{ + struct trie_info *a = (struct trie_info *) _a; + struct trie_info *b = (struct trie_info *) _b; + int len_a = strlen(a->name); + int len_b = strlen(b->name); + + /* strange sorting needed. Name 'xx' should be after 'xx1' */ + if (!strncmp(a->name, b->name, len_a < len_b ? len_a : len_b)) + return len_a < len_b ? 1 : (len_a > len_b ? -1 : 0); + return strcmp(a->name, b->name); +} + +static void export_trie(TCCState *s1, struct macho *mo) +{ + int i, size, offset = 0, save_offset; + uint8_t *ptr; + int sym_index; + int sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + int n_trie = 0, n_seq = 0; + struct trie_info *trie = NULL, *p_trie; + struct trie_node node, *p_node; + struct trie_seq *seq = NULL; + addr_t vm_addr = get_segment(mo, s1->output_type == TCC_OUTPUT_EXE)->vmaddr; + + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + sym_index; + const char *name = (char*)symtab_section->link->data + sym->st_name; + + if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE && + (ELFW(ST_BIND)(sym->st_info) == STB_GLOBAL || + ELFW(ST_BIND)(sym->st_info) == STB_WEAK)) { + int flag = EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + addr_t addr = + sym->st_value + s1->sections[sym->st_shndx]->sh_addr - vm_addr; + + if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + flag |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + dprintf ("%s %d %llx\n", name, flag, (long long)addr + vm_addr); + trie = tcc_realloc(trie, (n_trie + 1) * sizeof(struct trie_info)); + trie[n_trie].name = name; + trie[n_trie].flag = flag; + trie[n_trie].addr = addr; + trie[n_trie].str_size = strlen(name) + 1; + trie[n_trie].term_size = uleb128_size(flag) + uleb128_size(addr); + n_trie++; + } + } + if (n_trie) { + tcc_qsort(trie, n_trie, sizeof(struct trie_info), triecmp, NULL); + memset(&node, 0, sizeof(node)); + create_trie(&node, 0, n_trie, 0, n_trie, trie); + create_seq(&offset, &n_seq, &seq, &node, n_trie, trie); + save_offset = offset; + for (i = 0; i < n_seq; i++) { + p_node = seq[i].node; + if (p_node->n_child == 0) { + p_trie = &trie[p_node->start]; + seq[i].nest_offset = offset; + offset += 1 + p_trie->term_size + 1; + } + } + for (i = 0; i < n_seq; i++) { + p_node = seq[i].node; + p_trie = &trie[p_node->start]; + if (seq[i].n_child >= 0) { + section_ptr_add(mo->exports, + seq[i].offset - mo->exports->data_offset); + ptr = section_ptr_add(mo->exports, 2); + *ptr++ = 0; + *ptr = seq[i].n_child; + } + size = p_node->index_end - p_node->index_start; + ptr = section_ptr_add(mo->exports, size + 1); + memcpy(ptr, &p_trie->name[p_node->index_start], size); + ptr[size] = 0; + write_uleb128(mo->exports, seq[i].nest_offset); + } + section_ptr_add(mo->exports, save_offset - mo->exports->data_offset); + for (i = 0; i < n_seq; i++) { + p_node = seq[i].node; + if (p_node->n_child == 0) { + p_trie = &trie[p_node->start]; + write_uleb128(mo->exports, p_trie->term_size); + write_uleb128(mo->exports, p_trie->flag); + write_uleb128(mo->exports, p_trie->addr); + ptr = section_ptr_add(mo->exports, 1); + *ptr = 0; + } + } + section_ptr_add(mo->exports, -mo->exports->data_offset & 7); + node_free(&node); + tcc_free(seq); + } + tcc_free(trie); +} + +static void collect_sections(TCCState *s1, struct macho *mo, const char *filename) +{ + int i, sk, numsec; + int used_segment[N_SEGMENT]; + uint64_t curaddr, fileofs; + Section *s; + struct segment_command_64 *seg; + struct dylib_command *dylib; +#ifdef CONFIG_NEW_MACHO + struct linkedit_data_command *chained_fixups_lc; + struct linkedit_data_command *export_trie_lc; +#endif + struct build_version_command *dyldbv; + struct source_version_command *dyldsv; + struct rpath_command *rpath; + struct dylinker_command *dyldlc; + struct symtab_command *symlc; + struct dysymtab_command *dysymlc; + char *str; + + for (i = 0; i < N_SEGMENT; i++) + used_segment[i] = all_segment[i].used; + + memset (mo->sk_to_sect, 0, sizeof(mo->sk_to_sect)); + for (i = s1->nb_sections; i-- > 1;) { + int type, flags; + s = s1->sections[i]; + type = s->sh_type; + flags = s->sh_flags; + sk = sk_unknown; + /* debug sections have sometimes no SHF_ALLOC */ + if ((flags & SHF_ALLOC) || !strncmp(s->name, ".debug_", 7)) { + switch (type) { + default: sk = sk_unknown; break; + case SHT_INIT_ARRAY: sk = sk_init; break; + case SHT_FINI_ARRAY: sk = sk_fini; break; + case SHT_NOBITS: sk = sk_bss; break; + case SHT_SYMTAB: sk = sk_discard; break; + case SHT_STRTAB: + if (s == stabstr_section) + sk = sk_stab_str; + else + sk = sk_discard; + break; + case SHT_RELX: sk = sk_discard; break; + case SHT_LINKEDIT: sk = sk_linkedit; break; + case SHT_PROGBITS: + if (s == mo->stubs) + sk = sk_stubs; +#ifndef CONFIG_NEW_MACHO + else if (s == mo->stub_helper) + sk = sk_stub_helper; + else if (s == mo->la_symbol_ptr) + sk = sk_la_ptr; +#endif + else if (s == rodata_section) + sk = sk_ro_data; + else if (s == s1->got) + sk = sk_nl_ptr; + else if (s == stab_section) + sk = sk_stab; + else if (s == dwarf_info_section) + sk = sk_debug_info; + else if (s == dwarf_abbrev_section) + sk = sk_debug_abbrev; + else if (s == dwarf_line_section) + sk = sk_debug_line; + else if (s == dwarf_aranges_section) + sk = sk_debug_aranges; + else if (s == dwarf_str_section) + sk = sk_debug_str; + else if (s == dwarf_line_str_section) + sk = sk_debug_line_str; + else if (flags & SHF_EXECINSTR) + sk = sk_text; + else if (flags & SHF_WRITE) + sk = sk_rw_data; + else + sk = sk_ro_data; + break; + } + } else + sk = sk_discard; + s->prev = mo->sk_to_sect[sk].s; + mo->sk_to_sect[sk].s = s; + used_segment[skinfo[sk].seg_initial] = 1; + } + + if (s1->output_type != TCC_OUTPUT_EXE) + used_segment[0] = 0; + + for (i = 0; i < N_SEGMENT; i++) + if (used_segment[i]) { + seg = add_segment(mo, all_segment[i].name); + if (i == 1 && s1->output_type != TCC_OUTPUT_EXE) + seg->vmaddr = 0; + else + seg->vmaddr = all_segment[i].vmaddr; + seg->vmsize = all_segment[i].vmsize; + seg->maxprot = all_segment[i].maxprot; + seg->initprot = all_segment[i].initprot; + seg->flags = all_segment[i].flags; + for (sk = sk_unknown; sk < sk_last; sk++) + if (skinfo[sk].seg_initial == i) + mo->segment[sk] = mo->nseg - 1; + } + + if (s1->output_type != TCC_OUTPUT_EXE) { + const char *name = s1->install_name ? s1->install_name : filename; + i = (sizeof(*dylib) + strlen(name) + 1 + 7) &-8; + dylib = add_lc(mo, LC_ID_DYLIB, i); + dylib->name = sizeof(*dylib); + dylib->timestamp = 1; + dylib->current_version = + s1->current_version ? s1->current_version : 1 << 16; + dylib->compatibility_version = + s1->compatibility_version ? s1->compatibility_version : 1 << 16; + str = (char*)dylib + dylib->name; + strcpy(str, name); + } + +#ifdef CONFIG_NEW_MACHO + chained_fixups_lc = add_lc(mo, LC_DYLD_CHAINED_FIXUPS, + sizeof(struct linkedit_data_command)); + export_trie_lc = add_lc(mo, LC_DYLD_EXPORTS_TRIE, + sizeof(struct linkedit_data_command)); +#else + mo->dyldinfo = add_lc(mo, LC_DYLD_INFO_ONLY, sizeof(*mo->dyldinfo)); +#endif + + symlc = add_lc(mo, LC_SYMTAB, sizeof(*symlc)); + dysymlc = add_lc(mo, LC_DYSYMTAB, sizeof(*dysymlc)); + + if (s1->output_type == TCC_OUTPUT_EXE) { + i = (sizeof(*dyldlc) + strlen("/usr/lib/dyld") + 1 + 7) &-8; + dyldlc = add_lc(mo, LC_LOAD_DYLINKER, i); + dyldlc->name = sizeof(*dyldlc); + str = (char*)dyldlc + dyldlc->name; + strcpy(str, "/usr/lib/dyld"); + } + + dyldbv = add_lc(mo, LC_BUILD_VERSION, sizeof(*dyldbv)); + dyldbv->platform = PLATFORM_MACOS; + dyldbv->minos = (10 << 16) + (6 << 8); + dyldbv->sdk = (10 << 16) + (6 << 8); + dyldbv->ntools = 0; + + dyldsv = add_lc(mo, LC_SOURCE_VERSION, sizeof(*dyldsv)); + dyldsv->version = 0; + + if (s1->output_type == TCC_OUTPUT_EXE) { + mo->ep = add_lc(mo, LC_MAIN, sizeof(*mo->ep)); + mo->ep->entryoff = 4096; + } + + for(i = 0; i < s1->nb_loaded_dlls; i++) { + DLLReference *dllref = s1->loaded_dlls[i]; + if (dllref->level == 0) + add_dylib(mo, dllref->name); + } + + if (s1->rpath) { + char *path = s1->rpath, *end; + do { + end = strchr(path, ':'); + if (!end) + end = strchr(path, 0); + i = (sizeof(*rpath) + (end - path) + 1 + 7) &-8; + rpath = add_lc(mo, LC_RPATH, i); + rpath->path = sizeof(*rpath); + str = (char*)rpath + rpath->path; + memcpy(str, path, end - path); + str[end - path] = 0; + path = end + 1; + } while (*end); + } + + fileofs = 4096; /* leave space for mach-o headers */ + curaddr = get_segment(mo, s1->output_type == TCC_OUTPUT_EXE)->vmaddr; + curaddr += 4096; + seg = NULL; + numsec = 0; + mo->elfsectomacho = tcc_mallocz(sizeof(*mo->elfsectomacho) * s1->nb_sections); + for (sk = sk_unknown; sk < sk_last; sk++) { + struct section_64 *sec = NULL; + if (seg) { + seg->vmsize = curaddr - seg->vmaddr; + seg->filesize = fileofs - seg->fileoff; + } +#ifdef CONFIG_NEW_MACHO + if (sk == sk_linkedit) { + calc_fixup_size(s1, mo); + export_trie(s1, mo); + } +#else + if (sk == sk_linkedit) { + bind_rebase(s1, mo); + export_trie(s1, mo); + } +#endif + if (skinfo[sk].seg_initial && + (s1->output_type != TCC_OUTPUT_EXE || mo->segment[sk]) && + mo->sk_to_sect[sk].s) { + uint64_t al = 0; + int si; + seg = get_segment(mo, mo->segment[sk]); + if (skinfo[sk].name) { + si = add_section(mo, &seg, skinfo[sk].name); + numsec++; + mo->lc[mo->seg2lc[mo->segment[sk]]] = (struct load_command*)seg; + mo->sk_to_sect[sk].machosect = si; + sec = get_section(seg, si); + sec->flags = skinfo[sk].flags; + if (sk == sk_stubs) +#ifdef TCC_TARGET_X86_64 + sec->reserved2 = 6; +#elif defined TCC_TARGET_ARM64 + sec->reserved2 = 12; +#endif + if (sk == sk_nl_ptr) + sec->reserved1 = mo->nr_plt; +#ifndef CONFIG_NEW_MACHO + if (sk == sk_la_ptr) + sec->reserved1 = mo->nr_plt + mo->n_got; +#endif + } + if (seg->vmaddr == -1) { + curaddr = (curaddr + SEG_PAGE_SIZE - 1) & -SEG_PAGE_SIZE; + seg->vmaddr = curaddr; + fileofs = (fileofs + SEG_PAGE_SIZE - 1) & -SEG_PAGE_SIZE; + seg->fileoff = fileofs; + } + + for (s = mo->sk_to_sect[sk].s; s; s = s->prev) { + int a = exact_log2p1(s->sh_addralign); + if (a && al < (a - 1)) + al = a - 1; + s->sh_size = s->data_offset; + } + if (sec) + sec->align = al; + al = 1ULL << al; + if (al > 4096) + tcc_warning("alignment > 4096"), sec->align = 12, al = 4096; + curaddr = (curaddr + al - 1) & -al; + fileofs = (fileofs + al - 1) & -al; + if (sec) { + sec->addr = curaddr; + sec->offset = fileofs; + } + for (s = mo->sk_to_sect[sk].s; s; s = s->prev) { + al = s->sh_addralign; + curaddr = (curaddr + al - 1) & -al; + dprintf("%s: curaddr now 0x%lx\n", s->name, (long)curaddr); + s->sh_addr = curaddr; + curaddr += s->sh_size; + if (s->sh_type != SHT_NOBITS) { + fileofs = (fileofs + al - 1) & -al; + s->sh_offset = fileofs; + fileofs += s->sh_size; + dprintf("%s: fileofs now %ld\n", s->name, (long)fileofs); + } + if (sec) + mo->elfsectomacho[s->sh_num] = numsec; + } + if (sec) + sec->size = curaddr - sec->addr; + } + if (DEBUG_MACHO) + for (s = mo->sk_to_sect[sk].s; s; s = s->prev) { + int type = s->sh_type; + int flags = s->sh_flags; + printf("%d section %-16s %-10s %09lx %04x %02d %s,%s,%s\n", + sk, + s->name, + type == SHT_PROGBITS ? "progbits" : + type == SHT_NOBITS ? "nobits" : + type == SHT_SYMTAB ? "symtab" : + type == SHT_STRTAB ? "strtab" : + type == SHT_INIT_ARRAY ? "init" : + type == SHT_FINI_ARRAY ? "fini" : + type == SHT_RELX ? "rel" : "???", + (long)s->sh_addr, + (unsigned)s->data_offset, + s->sh_addralign, + flags & SHF_ALLOC ? "alloc" : "", + flags & SHF_WRITE ? "write" : "", + flags & SHF_EXECINSTR ? "exec" : "" + ); + } + } + if (seg) { + seg->vmsize = curaddr - seg->vmaddr; + seg->filesize = fileofs - seg->fileoff; + } + + /* Fill symtab info */ + symlc->symoff = mo->symtab->sh_offset; + symlc->nsyms = mo->symtab->data_offset / sizeof(struct nlist_64); + symlc->stroff = mo->strtab->sh_offset; + symlc->strsize = mo->strtab->data_offset; + + dysymlc->iundefsym = mo->iundef == -1 ? symlc->nsyms : mo->iundef; + dysymlc->iextdefsym = mo->iextdef == -1 ? dysymlc->iundefsym : mo->iextdef; + dysymlc->ilocalsym = mo->ilocal == -1 ? dysymlc->iextdefsym : mo->ilocal; + dysymlc->nlocalsym = dysymlc->iextdefsym - dysymlc->ilocalsym; + dysymlc->nextdefsym = dysymlc->iundefsym - dysymlc->iextdefsym; + dysymlc->nundefsym = symlc->nsyms - dysymlc->iundefsym; + dysymlc->indirectsymoff = mo->indirsyms->sh_offset; + dysymlc->nindirectsyms = mo->indirsyms->data_offset / sizeof(uint32_t); + +#ifdef CONFIG_NEW_MACHO + if (mo->chained_fixups->data_offset) { + chained_fixups_lc->dataoff = mo->chained_fixups->sh_offset; + chained_fixups_lc->datasize = mo->chained_fixups->data_offset; + } + if (mo->exports->data_offset) { + export_trie_lc->dataoff = mo->exports->sh_offset; + export_trie_lc->datasize = mo->exports->data_offset; + } +#else + if (mo->rebase->data_offset) { + mo->dyldinfo->rebase_off = mo->rebase->sh_offset; + mo->dyldinfo->rebase_size = mo->rebase->data_offset; + } + if (mo->binding->data_offset) { + mo->dyldinfo->bind_off = mo->binding->sh_offset; + mo->dyldinfo->bind_size = mo->binding->data_offset; + } + if (mo->weak_binding->data_offset) { + mo->dyldinfo->weak_bind_off = mo->weak_binding->sh_offset; + mo->dyldinfo->weak_bind_size = mo->weak_binding->data_offset; + } + if (mo->lazy_binding->data_offset) { + mo->dyldinfo->lazy_bind_off = mo->lazy_binding->sh_offset; + mo->dyldinfo->lazy_bind_size = mo->lazy_binding->data_offset; + } + if (mo->exports->data_offset) { + mo->dyldinfo->export_off = mo->exports->sh_offset; + mo->dyldinfo->export_size = mo->exports->data_offset; + } +#endif +} + +static void macho_write(TCCState *s1, struct macho *mo, FILE *fp) +{ + int i, sk; + uint64_t fileofs = 0; + Section *s; + mo->mh.mh.magic = MH_MAGIC_64; +#ifdef TCC_TARGET_X86_64 + mo->mh.mh.cputype = CPU_TYPE_X86_64; + mo->mh.mh.cpusubtype = CPU_SUBTYPE_LIB64 | CPU_SUBTYPE_X86_ALL; +#elif defined TCC_TARGET_ARM64 + mo->mh.mh.cputype = CPU_TYPE_ARM64; + mo->mh.mh.cpusubtype = CPU_SUBTYPE_ARM64_ALL; +#endif + if (s1->output_type == TCC_OUTPUT_EXE) { + mo->mh.mh.filetype = MH_EXECUTE; + mo->mh.mh.flags = MH_DYLDLINK | MH_PIE; + } + else { + mo->mh.mh.filetype = MH_DYLIB; + mo->mh.mh.flags = MH_DYLDLINK; + } + mo->mh.mh.ncmds = mo->nlc; + mo->mh.mh.sizeofcmds = 0; + for (i = 0; i < mo->nlc; i++) + mo->mh.mh.sizeofcmds += mo->lc[i]->cmdsize; + + fwrite(&mo->mh, 1, sizeof(mo->mh), fp); + fileofs += sizeof(mo->mh); + for (i = 0; i < mo->nlc; i++) { + fwrite(mo->lc[i], 1, mo->lc[i]->cmdsize, fp); + fileofs += mo->lc[i]->cmdsize; + } + + for (sk = sk_unknown; sk < sk_last; sk++) { + //struct segment_command_64 *seg; + if (skinfo[sk].seg_initial == 0 || + (s1->output_type == TCC_OUTPUT_EXE && !mo->segment[sk]) || + !mo->sk_to_sect[sk].s) + continue; + /*seg =*/ get_segment(mo, mo->segment[sk]); + for (s = mo->sk_to_sect[sk].s; s; s = s->prev) { + if (s->sh_type != SHT_NOBITS) { + while (fileofs < s->sh_offset) + fputc(0, fp), fileofs++; + if (s->sh_size) { + fwrite(s->data, 1, s->sh_size, fp); + fileofs += s->sh_size; + } + } + } + } +} + +#ifdef CONFIG_NEW_MACHO +static int bind_rebase_cmp(const void *_a, const void *_b, void *arg) +{ + TCCState *s1 = arg; + struct bind_rebase *a = (struct bind_rebase *) _a; + struct bind_rebase *b = (struct bind_rebase *) _b; + addr_t aa = s1->sections[a->section]->sh_addr + a->rel.r_offset; + addr_t ab = s1->sections[b->section]->sh_addr + b->rel.r_offset; + + return aa > ab ? 1 : aa < ab ? -1 : 0; +} + +ST_FUNC void bind_rebase_import(TCCState *s1, struct macho *mo) +{ + int i, j, k, bind_index, size, page_count, sym_index; + const char *name; + ElfW(Sym) *sym; + unsigned char *data = mo->chained_fixups->data; + struct segment_command_64 *seg; + struct dyld_chained_fixups_header *header; + struct dyld_chained_starts_in_image *image; + struct dyld_chained_starts_in_segment *segment; + struct dyld_chained_import *import; + + tcc_qsort(mo->bind_rebase, mo->n_bind_rebase, sizeof(struct bind_rebase), + bind_rebase_cmp, s1); + for (i = 0; i < mo->n_bind_rebase - 1; i++) + if (mo->bind_rebase[i].section == mo->bind_rebase[i + 1].section && + mo->bind_rebase[i].rel.r_offset == mo->bind_rebase[i + 1].rel.r_offset) { + sym_index = ELFW(R_SYM)(mo->bind_rebase[i].rel.r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + name = (char *) symtab_section->link->data + sym->st_name; + tcc_error("Overlap %s/%s %s:%s", + mo->bind_rebase[i].bind ? "bind" : "rebase", + mo->bind_rebase[i + 1].bind ? "bind" : "rebase", + s1->sections[mo->bind_rebase[i].section]->name, name); + } + header = (struct dyld_chained_fixups_header *) data; + data += (sizeof(struct dyld_chained_fixups_header) + 7) & -8; + header->starts_offset = data - mo->chained_fixups->data; + header->imports_count = mo->n_bind; + header->imports_format = DYLD_CHAINED_IMPORT; + header->symbols_format = 0; + size = sizeof(struct dyld_chained_starts_in_image) + + (mo->nseg - 1) * sizeof(uint32_t); + image = (struct dyld_chained_starts_in_image *) data; + data += (size + 7) & -8; + image->seg_count = mo->nseg; + for (i = (s1->output_type == TCC_OUTPUT_EXE); i < mo->nseg - 1; i++) { + image->seg_info_offset[i] = (data - mo->chained_fixups->data) - + header->starts_offset; + seg = get_segment(mo, i); + page_count = (seg->vmsize + SEG_PAGE_SIZE - 1) / SEG_PAGE_SIZE; + size = sizeof(struct dyld_chained_starts_in_segment) + + (page_count - 1) * sizeof(uint16_t); + segment = (struct dyld_chained_starts_in_segment *) data; + data += (size + 7) & -8; + segment->size = size; + segment->page_size = SEG_PAGE_SIZE; +#if 1 +#define PTR_64_OFFSET 0 +#define PTR_64_MASK 0x7FFFFFFFFFFULL + segment->pointer_format = DYLD_CHAINED_PTR_64; +#else +#define PTR_64_OFFSET 0x100000000ULL +#define PTR_64_MASK 0xFFFFFFFFFFFFFFULL + segment->pointer_format = DYLD_CHAINED_PTR_64_OFFSET; +#endif + segment->segment_offset = seg->fileoff; + segment->max_valid_pointer = 0; + segment->page_count = page_count; + // add bind/rebase + bind_index = 0; + k = 0; + for (j = 0; j < page_count; j++) { + addr_t start = seg->vmaddr + j * SEG_PAGE_SIZE; + addr_t end = start + SEG_PAGE_SIZE; + void *last = NULL; + addr_t last_o = 0; + addr_t cur_o, cur; + struct dyld_chained_ptr_64_rebase *rebase; + struct dyld_chained_ptr_64_bind *bind; + + segment->page_start[j] = DYLD_CHAINED_PTR_START_NONE; + for (; k < mo->n_bind_rebase; k++) { + Section *s = s1->sections[mo->bind_rebase[k].section]; + addr_t r_offset = mo->bind_rebase[k].rel.r_offset; + addr_t addr = s->sh_addr + r_offset; + + if ((addr & 3) || + (addr & (SEG_PAGE_SIZE - 1)) > SEG_PAGE_SIZE - PTR_SIZE) + tcc_error("Illegal rel_offset %s %lld", + s->name, (long long)r_offset); + if (addr >= end) + break; + if (addr >= start) { + cur_o = addr - start; + if (mo->bind_rebase[k].bind) { + if (segment->page_start[j] == DYLD_CHAINED_PTR_START_NONE) + segment->page_start[j] = cur_o; + else { + bind = (struct dyld_chained_ptr_64_bind *) last; + bind->next = (cur_o - last_o) / 4; + } + bind = (struct dyld_chained_ptr_64_bind *) + (s->data + r_offset); + last = bind; + last_o = cur_o; + bind->ordinal = bind_index; + bind->addend = 0; + bind->reserved = 0; + bind->next = 0; + bind->bind = 1; + } + else { + if (segment->page_start[j] == DYLD_CHAINED_PTR_START_NONE) + segment->page_start[j] = cur_o; + else { + rebase = (struct dyld_chained_ptr_64_rebase *) last; + rebase->next = (cur_o - last_o) / 4; + } + rebase = (struct dyld_chained_ptr_64_rebase *) + (s->data + r_offset); + last = rebase; + last_o = cur_o; + cur = (*(uint64_t *) (s->data + r_offset)) - + PTR_64_OFFSET; + rebase->target = cur & PTR_64_MASK; + rebase->high8 = cur >> (64 - 8); + if (cur != ((uint64_t)rebase->high8 << (64 - 8)) + rebase->target) + tcc_error("rebase error"); + rebase->reserved = 0; + rebase->next = 0; + rebase->bind = 0; + } + } + bind_index += mo->bind_rebase[k].bind; + } + } + } + // add imports + header->imports_offset = data - mo->chained_fixups->data; + import = (struct dyld_chained_import *) data; + data += mo->n_bind * sizeof (struct dyld_chained_import); + header->symbols_offset = data - mo->chained_fixups->data; + data++; + for (i = 0, bind_index = 0; i < mo->n_bind_rebase; i++) { + if (mo->bind_rebase[i].bind) { + import[bind_index].lib_ordinal = + BIND_SPECIAL_DYLIB_FLAT_LOOKUP & 0xffu; + import[bind_index].name_offset = + (data - mo->chained_fixups->data) - header->symbols_offset; + sym_index = ELFW(R_SYM)(mo->bind_rebase[i].rel.r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + import[bind_index].weak_import = + ELFW(ST_BIND)(sym->st_info) == STB_WEAK; + name = (char *) symtab_section->link->data + sym->st_name; + strcpy((char *) data, name); + data += strlen(name) + 1; + bind_index++; + } + } + tcc_free(mo->bind_rebase); +} +#endif + +ST_FUNC int macho_output_file(TCCState *s1, const char *filename) +{ + int fd, mode, file_type; + FILE *fp; + int i, ret = -1; + struct macho mo; + + (void)memset(&mo, 0, sizeof(mo)); + + 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 || (fp = fdopen(fd, "wb")) == NULL) { + tcc_error_noabort("could not write '%s: %s'", filename, strerror(errno)); + return -1; + } + if (s1->verbose) + printf("<- %s\n", filename); + + tcc_add_runtime(s1); + tcc_macho_add_destructor(s1); + resolve_common_syms(s1); + create_symtab(s1, &mo); + check_relocs(s1, &mo); + ret = check_symbols(s1, &mo); + if (!ret) { + int save_output = s1->output_type; + + collect_sections(s1, &mo, filename); + relocate_syms(s1, s1->symtab, 0); + if (s1->output_type == TCC_OUTPUT_EXE) + mo.ep->entryoff = get_sym_addr(s1, "main", 1, 1) + - get_segment(&mo, 1)->vmaddr; + if (s1->nb_errors) + goto do_ret; + // Macho uses bind/rebase instead of dynsym + s1->output_type = TCC_OUTPUT_EXE; + relocate_sections(s1); + s1->output_type = save_output; +#ifdef CONFIG_NEW_MACHO + bind_rebase_import(s1, &mo); +#endif + convert_symbols(s1, &mo); + macho_write(s1, &mo, fp); + } + + do_ret: + for (i = 0; i < mo.nlc; i++) + tcc_free(mo.lc[i]); + tcc_free(mo.seg2lc); + tcc_free(mo.lc); + tcc_free(mo.elfsectomacho); + tcc_free(mo.e2msym); + + fclose(fp); +#ifdef CONFIG_CODESIGN + if (!ret) { + char command[1024]; + int retval; + + snprintf(command, sizeof(command), "codesign -f -s - %s", filename); + retval = system (command); + if (retval == -1 || !(WIFEXITED(retval) && WEXITSTATUS(retval) == 0)) + tcc_error ("command failed '%s'", command); + } +#endif + return ret; +} + +static uint32_t macho_swap32(uint32_t x) +{ + return (x >> 24) | (x << 24) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8); +} +#define SWAP(x) (swap ? macho_swap32(x) : (x)) +#define tbd_parse_movepast(s) \ + (pos = (pos = strstr(pos, s)) ? pos + strlen(s) : NULL) +#define tbd_parse_movetoany(cs) (pos = strpbrk(pos, cs)) +#define tbd_parse_skipws while (*pos && (*pos==' '||*pos=='\n')) ++pos +#define tbd_parse_tramplequote if(*pos=='\''||*pos=='"') tbd_parse_trample +#define tbd_parse_tramplespace if(*pos==' ') tbd_parse_trample +#define tbd_parse_trample *pos++=0 + +#ifdef TCC_IS_NATIVE +/* Looks for the active developer SDK set by xcode-select (or the default + one set during installation.) */ +ST_FUNC void tcc_add_macos_sdkpath(TCCState* s) +{ + char *sdkroot = NULL, *pos = NULL; + void* xcs = dlopen("libxcselect.dylib", RTLD_GLOBAL | RTLD_LAZY); + CString path; + int (*f)(unsigned int, char**) = dlsym(xcs, "xcselect_host_sdk_path"); + cstr_new(&path); + if (f) f(1, &sdkroot); + if (sdkroot) + pos = strstr(sdkroot,"SDKs/MacOSX"); + if (pos) + cstr_printf(&path, "%.*s.sdk/usr/lib", (int)(pos - sdkroot + 11), sdkroot); + /* must use free from libc directly */ +#pragma push_macro("free") +#undef free + free(sdkroot); +#pragma pop_macro("free") + if (path.size) + tcc_add_library_path(s, (char*)path.data); + else + tcc_add_library_path(s, + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ":" "/Applications/Xcode.app/Developer/SDKs/MacOSX.sdk/usr/lib" + ); + cstr_free(&path); +} + +ST_FUNC const char* macho_tbd_soname(const char* filename) { + char *soname, *data, *pos; + const char *ret = filename; + + int fd = open(filename,O_RDONLY); + if (fd<0) return ret; + pos = data = tcc_load_text(fd); + if (!tbd_parse_movepast("install-name: ")) goto the_end; + tbd_parse_skipws; + tbd_parse_tramplequote; + soname = pos; + if (!tbd_parse_movetoany("\n \"'")) goto the_end; + tbd_parse_trample; + ret = tcc_strdup(soname); +the_end: + tcc_free(data); + return ret; +} +#endif /* TCC_IS_NATIVE */ + +ST_FUNC int macho_load_tbd(TCCState* s1, int fd, const char* filename, int lev) +{ + char *soname, *data, *pos; + int ret = -1; + + pos = data = tcc_load_text(fd); + if (!tbd_parse_movepast("install-name: ")) goto the_end; + tbd_parse_skipws; + tbd_parse_tramplequote; + soname = pos; + if (!tbd_parse_movetoany("\n \"'")) goto the_end; + tbd_parse_trample; + ret = 0; + if (tcc_add_dllref(s1, soname, lev)->found) + goto the_end; + while(pos) { + char* sym = NULL; + int cont = 1; + if (!tbd_parse_movepast("symbols: ")) break; + if (!tbd_parse_movepast("[")) break; + while (cont) { + tbd_parse_skipws; + tbd_parse_tramplequote; + sym = pos; + if (!tbd_parse_movetoany(",] \"'")) break; + tbd_parse_tramplequote; + tbd_parse_tramplespace; + tbd_parse_skipws; + if (*pos==0||*pos==']') cont=0; + tbd_parse_trample; + set_elf_sym(s1->dynsymtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, SHN_UNDEF, sym); + } + } + +the_end: + tcc_free(data); + return ret; +} + +ST_FUNC int macho_load_dll(TCCState * s1, int fd, const char* filename, int lev) +{ + unsigned char buf[sizeof(struct mach_header_64)]; + void *buf2; + uint32_t machofs = 0; + struct fat_header fh; + struct mach_header mh; + struct load_command *lc; + int i, swap = 0; + const char *soname = filename; + struct nlist_64 *symtab = 0; + uint32_t nsyms = 0; + char *strtab = 0; + uint32_t strsize = 0; + uint32_t iextdef = 0; + uint32_t nextdef = 0; + + again: + if (full_read(fd, buf, sizeof(buf)) != sizeof(buf)) + return -1; + memcpy(&fh, buf, sizeof(fh)); + if (fh.magic == FAT_MAGIC || fh.magic == FAT_CIGAM) { + struct fat_arch *fa = load_data(fd, sizeof(fh), + fh.nfat_arch * sizeof(*fa)); + swap = fh.magic == FAT_CIGAM; + for (i = 0; i < SWAP(fh.nfat_arch); i++) +#ifdef TCC_TARGET_X86_64 + if (SWAP(fa[i].cputype) == CPU_TYPE_X86_64 + && SWAP(fa[i].cpusubtype) == CPU_SUBTYPE_X86_ALL) +#elif defined TCC_TARGET_ARM64 + if (SWAP(fa[i].cputype) == CPU_TYPE_ARM64 + && SWAP(fa[i].cpusubtype) == CPU_SUBTYPE_ARM64_ALL) +#endif + break; + if (i == SWAP(fh.nfat_arch)) { + tcc_free(fa); + return -1; + } + machofs = SWAP(fa[i].offset); + tcc_free(fa); + lseek(fd, machofs, SEEK_SET); + goto again; + } else if (fh.magic == FAT_MAGIC_64 || fh.magic == FAT_CIGAM_64) { + tcc_warning("%s: Mach-O fat 64bit files of type 0x%x not handled", + filename, fh.magic); + return -1; + } + + memcpy(&mh, buf, sizeof(mh)); + if (mh.magic != MH_MAGIC_64) + return -1; + dprintf("found Mach-O at %d\n", machofs); + buf2 = load_data(fd, machofs + sizeof(struct mach_header_64), mh.sizeofcmds); + for (i = 0, lc = buf2; i < mh.ncmds; i++) { + dprintf("lc %2d: 0x%08x\n", i, lc->cmd); + switch (lc->cmd) { + case LC_SYMTAB: + { + struct symtab_command *sc = (struct symtab_command*)lc; + nsyms = sc->nsyms; + symtab = load_data(fd, machofs + sc->symoff, nsyms * sizeof(*symtab)); + strsize = sc->strsize; + strtab = load_data(fd, machofs + sc->stroff, strsize); + break; + } + case LC_ID_DYLIB: + { + struct dylib_command *dc = (struct dylib_command*)lc; + soname = (char*)lc + dc->name; + dprintf(" ID_DYLIB %d 0x%x 0x%x %s\n", + dc->timestamp, dc->current_version, + dc->compatibility_version, soname); + break; + } + case LC_REEXPORT_DYLIB: + { + struct dylib_command *dc = (struct dylib_command*)lc; + char *name = (char*)lc + dc->name; + int subfd = open(name, O_RDONLY | O_BINARY); + dprintf(" REEXPORT %s\n", name); + if (subfd < 0) + tcc_warning("can't open %s (reexported from %s)", name, filename); + else { + /* Hopefully the REEXPORTs never form a cycle, we don't check + for that! */ + macho_load_dll(s1, subfd, name, lev + 1); + close(subfd); + } + break; + } + case LC_DYSYMTAB: + { + struct dysymtab_command *dc = (struct dysymtab_command*)lc; + iextdef = dc->iextdefsym; + nextdef = dc->nextdefsym; + break; + } + } + lc = (struct load_command*) ((char*)lc + lc->cmdsize); + } + + if (tcc_add_dllref(s1, soname, lev)->found) + goto the_end; + + if (!nsyms || !nextdef) + tcc_warning("%s doesn't export any symbols?", filename); + + //dprintf("symbols (all):\n"); + dprintf("symbols (exported):\n"); + dprintf(" n: typ sec desc value name\n"); + //for (i = 0; i < nsyms; i++) { + for (i = iextdef; i < iextdef + nextdef; i++) { + struct nlist_64 *sym = symtab + i; + dprintf("%5d: %3d %3d 0x%04x 0x%016lx %s\n", + i, sym->n_type, sym->n_sect, sym->n_desc, (long)sym->n_value, + strtab + sym->n_strx); + set_elf_sym(s1->dynsymtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), + 0, SHN_UNDEF, strtab + sym->n_strx); + } + + the_end: + tcc_free(strtab); + tcc_free(symtab); + tcc_free(buf2); + return 0; +} diff --git a/vendor/tcc/tccpe.c b/vendor/tcc/tccpe.c new file mode 100644 index 00000000..11ec6df6 --- /dev/null +++ b/vendor/tcc/tccpe.c @@ -0,0 +1,2095 @@ +/* + * TCCPE.C - PE file output for the Tiny C Compiler + * + * Copyright (c) 2005-2007 grischka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tcc.h" + +#define PE_MERGE_DATA 1 +#define PE_PRINT_SECTIONS 0 + +#ifndef _WIN32 +#define stricmp strcasecmp +#define strnicmp strncasecmp +#include /* chmod() */ +#endif + +#ifdef TCC_TARGET_X86_64 +# define ADDR3264 ULONGLONG +# define PE_IMAGE_REL IMAGE_REL_BASED_DIR64 +# define REL_TYPE_DIRECT R_X86_64_64 +# define R_XXX_THUNKFIX R_X86_64_PC32 +# define R_XXX_RELATIVE R_X86_64_RELATIVE +# define R_XXX_FUNCCALL R_X86_64_PC32 +# define IMAGE_FILE_MACHINE 0x8664 +# define RSRC_RELTYPE 3 + +#elif defined TCC_TARGET_ARM +# define ADDR3264 DWORD +# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW +# define REL_TYPE_DIRECT R_ARM_ABS32 +# define R_XXX_THUNKFIX R_ARM_ABS32 +# define R_XXX_RELATIVE R_ARM_RELATIVE +# define R_XXX_FUNCCALL R_ARM_PC24 +# define R_XXX_FUNCCALL2 R_ARM_ABS32 +# define IMAGE_FILE_MACHINE 0x01C0 +# define RSRC_RELTYPE 7 /* ??? (not tested) */ + +#elif defined TCC_TARGET_I386 +# define ADDR3264 DWORD +# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW +# define REL_TYPE_DIRECT R_386_32 +# define R_XXX_THUNKFIX R_386_32 +# define R_XXX_RELATIVE R_386_RELATIVE +# define R_XXX_FUNCCALL R_386_PC32 +# define IMAGE_FILE_MACHINE 0x014C +# define RSRC_RELTYPE 7 /* DIR32NB */ + +#endif + +#ifndef IMAGE_NT_SIGNATURE +/* ----------------------------------------------------------- */ +/* definitions below are from winnt.h */ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef unsigned long long ULONGLONG; +#pragma pack(push, 1) + +typedef struct _IMAGE_DOS_HEADER { /* DOS .EXE header */ + WORD e_magic; /* Magic number */ + WORD e_cblp; /* Bytes on last page of file */ + WORD e_cp; /* Pages in file */ + WORD e_crlc; /* Relocations */ + WORD e_cparhdr; /* Size of header in paragraphs */ + WORD e_minalloc; /* Minimum extra paragraphs needed */ + WORD e_maxalloc; /* Maximum extra paragraphs needed */ + WORD e_ss; /* Initial (relative) SS value */ + WORD e_sp; /* Initial SP value */ + WORD e_csum; /* Checksum */ + WORD e_ip; /* Initial IP value */ + WORD e_cs; /* Initial (relative) CS value */ + WORD e_lfarlc; /* File address of relocation table */ + WORD e_ovno; /* Overlay number */ + WORD e_res[4]; /* Reserved words */ + WORD e_oemid; /* OEM identifier (for e_oeminfo) */ + WORD e_oeminfo; /* OEM information; e_oemid specific */ + WORD e_res2[10]; /* Reserved words */ + DWORD e_lfanew; /* File address of new exe header */ +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ +#define SIZE_OF_NT_SIGNATURE 4 + +typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + + +#define IMAGE_SIZEOF_FILE_HEADER 20 + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + + +typedef struct _IMAGE_OPTIONAL_HEADER { + /* Standard fields. */ + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; +#ifndef TCC_TARGET_X86_64 + DWORD BaseOfData; +#endif + /* NT additional fields. */ + ADDR3264 ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + ADDR3264 SizeOfStackReserve; + ADDR3264 SizeOfStackCommit; + ADDR3264 SizeOfHeapReserve; + ADDR3264 SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, IMAGE_OPTIONAL_HEADER; + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */ +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */ +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */ +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */ +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */ +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */ +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */ +/* IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 (X86 usage) */ +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 /* Architecture Specific Data */ +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* RVA of GP */ +#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */ +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */ +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */ +#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 /* Delay Load Import Descriptors */ +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 /* COM Runtime descriptor */ + +/* Section header format. */ +#define IMAGE_SIZEOF_SHORT_NAME 8 + +typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; + +#define IMAGE_SIZEOF_SECTION_HEADER 40 + +typedef struct _IMAGE_EXPORT_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Name; + DWORD Base; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; + DWORD AddressOfNames; + DWORD AddressOfNameOrdinals; +} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; + +typedef struct _IMAGE_IMPORT_DESCRIPTOR { + union { + DWORD Characteristics; + DWORD OriginalFirstThunk; + }; + DWORD TimeDateStamp; + DWORD ForwarderChain; + DWORD Name; + DWORD FirstThunk; +} IMAGE_IMPORT_DESCRIPTOR; + +typedef struct _IMAGE_BASE_RELOCATION { + DWORD VirtualAddress; + DWORD SizeOfBlock; +// WORD TypeOffset[1]; +} IMAGE_BASE_RELOCATION; + +#define IMAGE_SIZEOF_BASE_RELOCATION 8 + +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL32 7 +#define IMAGE_REL_BASED_DIR64 10 + +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +#pragma pack(pop) + +/* ----------------------------------------------------------- */ +#endif /* ndef IMAGE_NT_SIGNATURE */ +/* ----------------------------------------------------------- */ + +#ifndef IMAGE_REL_BASED_DIR64 +# define IMAGE_REL_BASED_DIR64 10 +#endif + +#pragma pack(push, 1) +struct pe_header +{ + IMAGE_DOS_HEADER doshdr; + BYTE dosstub[0x40]; + DWORD nt_sig; + IMAGE_FILE_HEADER filehdr; +#ifdef TCC_TARGET_X86_64 + IMAGE_OPTIONAL_HEADER64 opthdr; +#else +#ifdef _WIN64 + IMAGE_OPTIONAL_HEADER32 opthdr; +#else + IMAGE_OPTIONAL_HEADER opthdr; +#endif +#endif +}; + +struct pe_reloc_header { + DWORD offset; + DWORD size; +}; + +struct pe_rsrc_header { + struct _IMAGE_FILE_HEADER filehdr; + struct _IMAGE_SECTION_HEADER sectionhdr; +}; + +struct pe_rsrc_reloc { + DWORD offset; + DWORD size; + WORD type; +}; +#pragma pack(pop) + +/* ------------------------------------------------------------- */ +/* internal temporary structures */ + +enum { + sec_text = 0, + sec_rdata , + sec_data , + sec_bss , + sec_idata , + sec_pdata , + sec_other , + sec_rsrc , + sec_debug , + sec_reloc , + sec_last +}; + +#if 0 +static const DWORD pe_sec_flags[] = { + 0x60000020, /* ".text" , */ + 0xC0000040, /* ".data" , */ + 0xC0000080, /* ".bss" , */ + 0x40000040, /* ".idata" , */ + 0x40000040, /* ".pdata" , */ + 0xE0000060, /* < other > , */ + 0x40000040, /* ".rsrc" , */ + 0x42000802, /* ".stab" , */ + 0x42000040, /* ".reloc" , */ +}; +#endif + +struct section_info { + int cls; + char name[32]; + ADDR3264 sh_addr; + DWORD sh_size; + DWORD pe_flags; + Section *sec; + DWORD data_size; + IMAGE_SECTION_HEADER ish; +}; + +struct import_symbol { + int sym_index; + int iat_index; + int thk_offset; +}; + +struct pe_import_info { + int dll_index; + int sym_count; + struct import_symbol **symbols; +}; + +struct pe_info { + TCCState *s1; + Section *reloc; + Section *thunk; + const char *filename; + int type; + DWORD sizeofheaders; + ADDR3264 imagebase; + const char *start_symbol; + DWORD start_addr; + DWORD imp_offs; + DWORD imp_size; + DWORD iat_offs; + DWORD iat_size; + DWORD exp_offs; + DWORD exp_size; + int subsystem; + DWORD section_align; + DWORD file_align; + struct section_info **sec_info; + int sec_count; + struct pe_import_info **imp_info; + int imp_count; +}; + +#define PE_NUL 0 +#define PE_DLL 1 +#define PE_GUI 2 +#define PE_EXE 3 +#define PE_RUN 4 + +/* --------------------------------------------*/ + +static const char *pe_export_name(TCCState *s1, ElfW(Sym) *sym) +{ + const char *name = (char*)symtab_section->link->data + sym->st_name; + if (s1->leading_underscore && name[0] == '_' && !(sym->st_other & ST_PE_STDCALL)) + return name + 1; + return name; +} + +static int pe_find_import(TCCState * s1, ElfW(Sym) *sym) +{ + char buffer[200]; + const char *s, *p; + int sym_index = 0, n = 0; + int a, err = 0; + + do { + s = pe_export_name(s1, sym); + a = 0; + if (n) { + /* second try: */ + if (sym->st_other & ST_PE_STDCALL) { + /* try w/0 stdcall deco (windows API convention) */ + p = strrchr(s, '@'); + if (!p || s[0] != '_') + break; + strcpy(buffer, s+1)[p-s-1] = 0; + } else if (s[0] != '_') { /* try non-ansi function */ + buffer[0] = '_', strcpy(buffer + 1, s); + } else if (0 == memcmp(s, "__imp_", 6)) { /* mingw 2.0 */ + strcpy(buffer, s + 6), a = 1; + } else if (0 == memcmp(s, "_imp__", 6)) { /* mingw 3.7 */ + strcpy(buffer, s + 6), a = 1; + } else { + continue; + } + s = buffer; + } + sym_index = find_elf_sym(s1->dynsymtab_section, s); + // printf("find (%d) %d %s\n", n, sym_index, s); + if (sym_index + && ELFW(ST_TYPE)(sym->st_info) == STT_OBJECT + && 0 == (sym->st_other & ST_PE_IMPORT) + && 0 == a + ) err = -1, sym_index = 0; + } while (0 == sym_index && ++n < 2); + return n == 2 ? err : sym_index; +} + +/*----------------------------------------------------------------------------*/ + +static int dynarray_assoc(void **pp, int n, int key) +{ + int i; + for (i = 0; i < n; ++i, ++pp) + if (key == **(int **) pp) + return i; + return -1; +} + +static DWORD umin(DWORD a, DWORD b) +{ + return a < b ? a : b; +} + +static DWORD umax(DWORD a, DWORD b) +{ + return a < b ? b : a; +} + +static DWORD pe_file_align(struct pe_info *pe, DWORD n) +{ + return (n + (pe->file_align - 1)) & ~(pe->file_align - 1); +} + +static ADDR3264 pe_virtual_align(struct pe_info *pe, ADDR3264 n) +{ + return (n + (pe->section_align - 1)) & ~(ADDR3264)(pe->section_align - 1); +} + +static void pe_align_section(Section *s, int a) +{ + int i = s->data_offset & (a-1); + if (i) + section_ptr_add(s, a - i); +} + +static void pe_set_datadir(struct pe_header *hdr, int dir, DWORD addr, DWORD size) +{ + hdr->opthdr.DataDirectory[dir].VirtualAddress = addr; + hdr->opthdr.DataDirectory[dir].Size = size; +} + +struct pe_file { + FILE *op; + DWORD sum; + unsigned pos; +}; + +static int pe_fwrite(const void *data, int len, struct pe_file *pf) +{ + const WORD *p = data; + DWORD sum; + int ret, i; + pf->pos += (ret = fwrite(data, 1, len, pf->op)); + sum = pf->sum; + for (i = len; i > 0; i -= 2) { + sum += (i >= 2) ? *p++ : *(BYTE*)p; + sum = (sum + (sum >> 16)) & 0xFFFF; + } + pf->sum = sum; + return len == ret ? 0 : -1; +} + +static void pe_fpad(struct pe_file *pf, DWORD new_pos) +{ + char buf[256]; + int n, diff = new_pos - pf->pos; + memset(buf, 0, sizeof buf); + while (diff > 0) { + diff -= n = umin(diff, sizeof buf); + fwrite(buf, n, 1, pf->op); + } + pf->pos = new_pos; +} + +/*----------------------------------------------------------------------------*/ +/* PE-DWARF/COFF support + does not work with a mingw-gdb really but works with cv2pdb + (https://github.com/rainers/cv2pdb) */ + +#define N_COFF_SYMS 0 + +static const char dwarf_secs[] = +{ + ".debug_info\0" + ".debug_abbrev\0" + ".debug_line\0" + ".debug_aranges\0" + ".debug_str\0" + ".debug_line_str\0" +}; + +static const unsigned coff_strtab_size = 4 + sizeof dwarf_secs - 1; + +static int pe_put_long_secname(char *secname, const char *name) +{ + const char *d = dwarf_secs; + do { + if (0 == strcmp(d, name)) { + sprintf(secname, "/%d", (int)(d - dwarf_secs + 4)); + return 1; + } + d = strchr(d, 0) + 1; + } while (*d); + return 0; +} + +static void pe_create_pdb(TCCState *s1, const char *exename) +{ + char buf[300]; int r; + snprintf(buf, sizeof buf, "cv2pdb.exe %s", exename); + r = system(buf); + strcpy(tcc_fileextension(strcpy(buf, exename)), ".pdb"); + if (r) { + tcc_error_noabort("could not create '%s'\n(need working cv2pdb from https://github.com/rainers/cv2pdb)", buf); + } else if (s1->verbose) { + printf("<- %s\n", buf); + } +} + +/*----------------------------------------------------------------------------*/ +static int pe_write(struct pe_info *pe) +{ + static const struct pe_header pe_template = { + { + /* IMAGE_DOS_HEADER doshdr */ + 0x5A4D, /*WORD e_magic; Magic number */ + 0x0090, /*WORD e_cblp; Bytes on last page of file */ + 0x0003, /*WORD e_cp; Pages in file */ + 0x0000, /*WORD e_crlc; Relocations */ + + 0x0004, /*WORD e_cparhdr; Size of header in paragraphs */ + 0x0000, /*WORD e_minalloc; Minimum extra paragraphs needed */ + 0xFFFF, /*WORD e_maxalloc; Maximum extra paragraphs needed */ + 0x0000, /*WORD e_ss; Initial (relative) SS value */ + + 0x00B8, /*WORD e_sp; Initial SP value */ + 0x0000, /*WORD e_csum; Checksum */ + 0x0000, /*WORD e_ip; Initial IP value */ + 0x0000, /*WORD e_cs; Initial (relative) CS value */ + 0x0040, /*WORD e_lfarlc; File address of relocation table */ + 0x0000, /*WORD e_ovno; Overlay number */ + {0,0,0,0}, /*WORD e_res[4]; Reserved words */ + 0x0000, /*WORD e_oemid; OEM identifier (for e_oeminfo) */ + 0x0000, /*WORD e_oeminfo; OEM information; e_oemid specific */ + {0,0,0,0,0,0,0,0,0,0}, /*WORD e_res2[10]; Reserved words */ + 0x00000080 /*DWORD e_lfanew; File address of new exe header */ + },{ + /* BYTE dosstub[0x40] */ + /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */ + 0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68, + 0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f, + 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20, + 0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + }, + 0x00004550, /* DWORD nt_sig = IMAGE_NT_SIGNATURE */ + { + /* IMAGE_FILE_HEADER filehdr */ + IMAGE_FILE_MACHINE, /*WORD Machine; */ + 0x0003, /*WORD NumberOfSections; */ + 0x00000000, /*DWORD TimeDateStamp; */ + 0x00000000, /*DWORD PointerToSymbolTable; */ + 0x00000000, /*DWORD NumberOfSymbols; */ +#if defined(TCC_TARGET_X86_64) + 0x00F0, /*WORD SizeOfOptionalHeader; */ + 0x022F /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x222E +#elif defined(TCC_TARGET_I386) + 0x00E0, /*WORD SizeOfOptionalHeader; */ + 0x030F /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x230E +#elif defined(TCC_TARGET_ARM) + 0x00E0, /*WORD SizeOfOptionalHeader; */ + 0x010F, /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x230F +#endif +},{ + /* IMAGE_OPTIONAL_HEADER opthdr */ + /* Standard fields. */ +#ifdef TCC_TARGET_X86_64 + 0x020B, /*WORD Magic; */ +#else + 0x010B, /*WORD Magic; */ +#endif + 0x06, /*BYTE MajorLinkerVersion; */ + 0x00, /*BYTE MinorLinkerVersion; */ + 0x00000000, /*DWORD SizeOfCode; */ + 0x00000000, /*DWORD SizeOfInitializedData; */ + 0x00000000, /*DWORD SizeOfUninitializedData; */ + 0x00000000, /*DWORD AddressOfEntryPoint; */ + 0x00000000, /*DWORD BaseOfCode; */ +#ifndef TCC_TARGET_X86_64 + 0x00000000, /*DWORD BaseOfData; */ +#endif + /* NT additional fields. */ +#if defined(TCC_TARGET_ARM) + 0x00100000, /*DWORD ImageBase; */ +#else + 0x00400000, /*DWORD ImageBase; */ +#endif + 0x00001000, /*DWORD SectionAlignment; */ + 0x00000200, /*DWORD FileAlignment; */ + 0x0004, /*WORD MajorOperatingSystemVersion; */ + 0x0000, /*WORD MinorOperatingSystemVersion; */ + 0x0000, /*WORD MajorImageVersion; */ + 0x0000, /*WORD MinorImageVersion; */ + 0x0004, /*WORD MajorSubsystemVersion; */ + 0x0000, /*WORD MinorSubsystemVersion; */ + 0x00000000, /*DWORD Win32VersionValue; */ + 0x00000000, /*DWORD SizeOfImage; */ + 0x00000200, /*DWORD SizeOfHeaders; */ + 0x00000000, /*DWORD CheckSum; */ + 0x0002, /*WORD Subsystem; */ + 0x0000, /*WORD DllCharacteristics; */ + 0x00100000, /*DWORD SizeOfStackReserve; */ + 0x00001000, /*DWORD SizeOfStackCommit; */ + 0x00100000, /*DWORD SizeOfHeapReserve; */ + 0x00001000, /*DWORD SizeOfHeapCommit; */ + 0x00000000, /*DWORD LoaderFlags; */ + 0x00000010, /*DWORD NumberOfRvaAndSizes; */ + + /* IMAGE_DATA_DIRECTORY DataDirectory[16]; */ + {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, + {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} + }}; + + struct pe_header pe_header = pe_template; + + int i; + struct pe_file pf = {0}; + DWORD file_offset; + struct section_info *si; + IMAGE_SECTION_HEADER *psh; + TCCState *s1 = pe->s1; + int need_strtab = 0; + + pf.op = fopen(pe->filename, "wb"); + if (NULL == pf.op) + return tcc_error_noabort("could not write '%s': %s", pe->filename, strerror(errno)); + + pe->sizeofheaders = pe_file_align(pe, + sizeof (struct pe_header) + + pe->sec_count * sizeof (IMAGE_SECTION_HEADER) + ); + + file_offset = pe->sizeofheaders; + + if (2 == pe->s1->verbose) + printf("-------------------------------" + "\n virt file size section" "\n"); + for (i = 0; i < pe->sec_count; ++i) { + DWORD addr, size; + const char *sh_name; + + si = pe->sec_info[i]; + sh_name = si->name; + addr = si->sh_addr - pe->imagebase; + size = si->sh_size; + psh = &si->ish; + + if (2 == pe->s1->verbose) + printf("%6x %6x %6x %s\n", + (unsigned)addr, (unsigned)file_offset, (unsigned)size, sh_name); + + switch (si->cls) { + case sec_text: + if (!pe_header.opthdr.BaseOfCode) + pe_header.opthdr.BaseOfCode = addr; + break; + + case sec_data: +#ifndef TCC_TARGET_X86_64 + if (!pe_header.opthdr.BaseOfData) + pe_header.opthdr.BaseOfData = addr; +#endif + break; + + case sec_bss: + break; + + case sec_reloc: + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size); + break; + + case sec_rsrc: + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size); + break; + + case sec_pdata: + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXCEPTION, addr, size); + break; + } + + if (pe->imp_size) { + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IMPORT, + pe->imp_offs, pe->imp_size); + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IAT, + pe->iat_offs, pe->iat_size); + } + if (pe->exp_size) { + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXPORT, + pe->exp_offs, pe->exp_size); + } + + memcpy(psh->Name, sh_name, umin(strlen(sh_name), sizeof psh->Name)); + if (si->cls == sec_debug) + need_strtab += pe_put_long_secname(psh->Name, sh_name); + + psh->Characteristics = si->pe_flags; + psh->VirtualAddress = addr; + psh->Misc.VirtualSize = size; + pe_header.opthdr.SizeOfImage = + umax(pe_virtual_align(pe, size + addr), pe_header.opthdr.SizeOfImage); + + if (si->data_size) { + psh->PointerToRawData = file_offset; + file_offset = pe_file_align(pe, file_offset + si->data_size); + psh->SizeOfRawData = file_offset - psh->PointerToRawData; + if (si->cls == sec_text) + pe_header.opthdr.SizeOfCode += psh->SizeOfRawData; + else + pe_header.opthdr.SizeOfInitializedData += psh->SizeOfRawData; + } + } + + //pe_header.filehdr.TimeDateStamp = time(NULL); + pe_header.filehdr.NumberOfSections = pe->sec_count; + pe_header.opthdr.AddressOfEntryPoint = pe->start_addr; + pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders; + pe_header.opthdr.ImageBase = pe->imagebase; + pe_header.opthdr.Subsystem = pe->subsystem; + if (pe->s1->pe_stack_size) + pe_header.opthdr.SizeOfStackReserve = pe->s1->pe_stack_size; + if (PE_DLL == pe->type) + pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; + pe_header.filehdr.Characteristics |= pe->s1->pe_characteristics; + if (need_strtab) { + pe_header.filehdr.PointerToSymbolTable = file_offset; + pe_header.filehdr.NumberOfSymbols = N_COFF_SYMS; + } + pe_fwrite(&pe_header, sizeof pe_header, &pf); + for (i = 0; i < pe->sec_count; ++i) + pe_fwrite(&pe->sec_info[i]->ish, sizeof(IMAGE_SECTION_HEADER), &pf); + + file_offset = pe->sizeofheaders; + for (i = 0; i < pe->sec_count; ++i) { + Section *s; + si = pe->sec_info[i]; + if (!si->data_size) + continue; + for (s = si->sec; s; s = s->prev) { + pe_fpad(&pf, file_offset); + pe_fwrite(s->data, s->data_offset, &pf); + if (s->prev) + file_offset += s->prev->sh_addr - s->sh_addr; + } + file_offset = si->ish.PointerToRawData + si->ish.SizeOfRawData; + pe_fpad(&pf, file_offset); + } + + if (need_strtab) { + /* create a tiny COFF string table with the long section names */ + pe_fwrite(&coff_strtab_size, sizeof coff_strtab_size, &pf); + pe_fwrite(dwarf_secs, sizeof dwarf_secs - 1, &pf); + file_offset = pf.pos; + } + + pf.sum += file_offset; + fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET); + pe_fwrite(&pf.sum, sizeof (DWORD), &pf); + + fclose (pf.op); +#ifndef _WIN32 + chmod(pe->filename, 0777); +#endif + + if (2 == pe->s1->verbose) + printf("-------------------------------\n"); + if (pe->s1->verbose) + printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset); + + if (s1->do_debug & 16) + pe_create_pdb(s1, pe->filename); + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static struct import_symbol *pe_add_import(struct pe_info *pe, int sym_index) +{ + int i; + int dll_index; + struct pe_import_info *p; + struct import_symbol *s; + ElfW(Sym) *isym; + + isym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index; + dll_index = isym->st_size; + + i = dynarray_assoc ((void**)pe->imp_info, pe->imp_count, dll_index); + if (-1 != i) { + p = pe->imp_info[i]; + goto found_dll; + } + p = tcc_mallocz(sizeof *p); + p->dll_index = dll_index; + dynarray_add(&pe->imp_info, &pe->imp_count, p); + +found_dll: + i = dynarray_assoc ((void**)p->symbols, p->sym_count, sym_index); + if (-1 != i) + return p->symbols[i]; + + s = tcc_mallocz(sizeof *s); + dynarray_add(&p->symbols, &p->sym_count, s); + s->sym_index = sym_index; + return s; +} + +void pe_free_imports(struct pe_info *pe) +{ + int i; + for (i = 0; i < pe->imp_count; ++i) { + struct pe_import_info *p = pe->imp_info[i]; + dynarray_reset(&p->symbols, &p->sym_count); + } + dynarray_reset(&pe->imp_info, &pe->imp_count); +} + +/*----------------------------------------------------------------------------*/ +static void pe_build_imports(struct pe_info *pe) +{ + int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i; + DWORD rva_base = pe->thunk->sh_addr - pe->imagebase; + int ndlls = pe->imp_count; + TCCState *s1 = pe->s1; + + for (sym_cnt = i = 0; i < ndlls; ++i) + sym_cnt += pe->imp_info[i]->sym_count; + + if (0 == sym_cnt) + return; + + pe_align_section(pe->thunk, 16); + pe->imp_size = (ndlls + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR); + pe->iat_size = (sym_cnt + ndlls) * sizeof(ADDR3264); + dll_ptr = pe->thunk->data_offset; + thk_ptr = dll_ptr + pe->imp_size; + ent_ptr = thk_ptr + pe->iat_size; + pe->imp_offs = dll_ptr + rva_base; + pe->iat_offs = thk_ptr + rva_base; + section_ptr_add(pe->thunk, pe->imp_size + 2*pe->iat_size); + + for (i = 0; i < pe->imp_count; ++i) { + IMAGE_IMPORT_DESCRIPTOR *hdr; + int k, n, dllindex; + ADDR3264 v; + struct pe_import_info *p = pe->imp_info[i]; + const char *name; + DLLReference *dllref; + + dllindex = p->dll_index; + if (dllindex) + name = tcc_basename((dllref = pe->s1->loaded_dlls[dllindex-1])->name); + else + name = "", dllref = NULL; + + /* put the dll name into the import header */ + v = put_elf_str(pe->thunk, name); + hdr = (IMAGE_IMPORT_DESCRIPTOR*)(pe->thunk->data + dll_ptr); + hdr->FirstThunk = thk_ptr + rva_base; + hdr->OriginalFirstThunk = ent_ptr + rva_base; + hdr->Name = v + rva_base; + + for (k = 0, n = p->sym_count; k <= n; ++k) { + if (k < n) { + int iat_index = p->symbols[k]->iat_index; + int sym_index = p->symbols[k]->sym_index; + ElfW(Sym) *imp_sym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index; + ElfW(Sym) *org_sym = (ElfW(Sym) *)symtab_section->data + iat_index; + const char *name = (char*)pe->s1->dynsymtab_section->link->data + imp_sym->st_name; + int ordinal; + + org_sym->st_value = thk_ptr; + org_sym->st_shndx = pe->thunk->sh_num; + + if (dllref) + v = 0, ordinal = imp_sym->st_value; /* ordinal from pe_load_def */ + else + ordinal = 0, v = imp_sym->st_value; /* address from tcc_add_symbol() */ + +#ifdef TCC_IS_NATIVE + if (pe->type == PE_RUN) { + if (dllref) { + if ( !dllref->handle ) + dllref->handle = LoadLibrary(dllref->name); + v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(char*)0+ordinal:name); + } + if (!v) + tcc_error_noabort("could not resolve symbol '%s'", name); + } else +#endif + if (ordinal) { + v = ordinal | (ADDR3264)1 << (sizeof(ADDR3264)*8 - 1); + } else { + v = pe->thunk->data_offset + rva_base; + section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */ + put_elf_str(pe->thunk, name); + } + + } else { + v = 0; /* last entry is zero */ + } + + *(ADDR3264*)(pe->thunk->data+thk_ptr) = + *(ADDR3264*)(pe->thunk->data+ent_ptr) = v; + thk_ptr += sizeof (ADDR3264); + ent_ptr += sizeof (ADDR3264); + } + dll_ptr += sizeof(IMAGE_IMPORT_DESCRIPTOR); + } +} + +/* ------------------------------------------------------------- */ + +struct pe_sort_sym +{ + int index; + const char *name; +}; + +static int sym_cmp(const void *va, const void *vb) +{ + const char *ca = (*(struct pe_sort_sym**)va)->name; + const char *cb = (*(struct pe_sort_sym**)vb)->name; + return strcmp(ca, cb); +} + +static void pe_build_exports(struct pe_info *pe) +{ + ElfW(Sym) *sym; + int sym_index, sym_end; + DWORD rva_base, base_o, func_o, name_o, ord_o, str_o; + IMAGE_EXPORT_DIRECTORY *hdr; + int sym_count, ord; + struct pe_sort_sym **sorted, *p; + TCCState *s1 = pe->s1; + + FILE *op; + char buf[260]; + const char *dllname; + const char *name; + + rva_base = pe->thunk->sh_addr - pe->imagebase; + sym_count = 0, sorted = NULL, op = NULL; + + sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + sym = (ElfW(Sym)*)symtab_section->data + sym_index; + name = pe_export_name(pe->s1, sym); + if (sym->st_other & ST_PE_EXPORT) { + p = tcc_malloc(sizeof *p); + p->index = sym_index; + p->name = name; + dynarray_add(&sorted, &sym_count, p); + } +#if 0 + if (sym->st_other & ST_PE_EXPORT) + printf("export: %s\n", name); + if (sym->st_other & ST_PE_STDCALL) + printf("stdcall: %s\n", name); +#endif + } + + if (0 == sym_count) + return; + + qsort (sorted, sym_count, sizeof *sorted, sym_cmp); + + pe_align_section(pe->thunk, 16); + dllname = tcc_basename(pe->filename); + + base_o = pe->thunk->data_offset; + func_o = base_o + sizeof(IMAGE_EXPORT_DIRECTORY); + name_o = func_o + sym_count * sizeof (DWORD); + ord_o = name_o + sym_count * sizeof (DWORD); + str_o = ord_o + sym_count * sizeof(WORD); + + hdr = section_ptr_add(pe->thunk, str_o - base_o); + hdr->Characteristics = 0; + hdr->Base = 1; + hdr->NumberOfFunctions = sym_count; + hdr->NumberOfNames = sym_count; + hdr->AddressOfFunctions = func_o + rva_base; + hdr->AddressOfNames = name_o + rva_base; + hdr->AddressOfNameOrdinals = ord_o + rva_base; + hdr->Name = str_o + rva_base; + put_elf_str(pe->thunk, dllname); + +#if 1 + /* automatically write exports to .def */ + pstrcpy(buf, sizeof buf, pe->filename); + strcpy(tcc_fileextension(buf), ".def"); + op = fopen(buf, "wb"); + if (NULL == op) { + tcc_error_noabort("could not create '%s': %s", buf, strerror(errno)); + } else { + fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname); + if (pe->s1->verbose) + printf("<- %s (%d symbol%s)\n", buf, sym_count, &"s"[sym_count < 2]); + } +#endif + + for (ord = 0; ord < sym_count; ++ord) + { + p = sorted[ord], sym_index = p->index, name = p->name; + /* insert actual address later in relocate_sections() */ + put_elf_reloc(symtab_section, pe->thunk, + func_o, R_XXX_RELATIVE, sym_index); + *(DWORD*)(pe->thunk->data + name_o) + = pe->thunk->data_offset + rva_base; + *(WORD*)(pe->thunk->data + ord_o) + = ord; + put_elf_str(pe->thunk, name); + func_o += sizeof (DWORD); + name_o += sizeof (DWORD); + ord_o += sizeof (WORD); + if (op) + fprintf(op, "%s\n", name); + } + + pe->exp_offs = base_o + rva_base; + pe->exp_size = pe->thunk->data_offset - base_o; + dynarray_reset(&sorted, &sym_count); + if (op) + fclose(op); +} + +/* ------------------------------------------------------------- */ +static void pe_build_reloc (struct pe_info *pe) +{ + DWORD offset, block_ptr, sh_addr, addr; + int count, i; + ElfW_Rel *rel, *rel_end; + Section *s = NULL, *sr; + struct pe_reloc_header *hdr; + + sh_addr = offset = block_ptr = count = i = 0; + rel = rel_end = NULL; + + for(;;) { + if (rel < rel_end) { + int type = ELFW(R_TYPE)(rel->r_info); + addr = rel->r_offset + sh_addr; + ++ rel; + if (type != REL_TYPE_DIRECT) + continue; + if (count == 0) { /* new block */ + block_ptr = pe->reloc->data_offset; + section_ptr_add(pe->reloc, sizeof(struct pe_reloc_header)); + offset = addr & 0xFFFFFFFF<<12; + } + if ((addr -= offset) < (1<<12)) { /* one block spans 4k addresses */ + WORD *wp = section_ptr_add(pe->reloc, sizeof (WORD)); + *wp = addr | PE_IMAGE_REL<<12; + ++count; + continue; + } + -- rel; + + } else if (s) { + sr = s->reloc; + if (sr) { + rel = (ElfW_Rel *)sr->data; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + sh_addr = s->sh_addr; + } + s = s->prev; + continue; + + } else if (i < pe->sec_count) { + s = pe->sec_info[i]->sec, ++i; + continue; + + } else if (!count) + break; + + /* fill the last block and ready for a new one */ + if (count & 1) /* align for DWORDS */ + section_ptr_add(pe->reloc, sizeof(WORD)), ++count; + hdr = (struct pe_reloc_header *)(pe->reloc->data + block_ptr); + hdr -> offset = offset - pe->imagebase; + hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header); + count = 0; + } +} + +/* ------------------------------------------------------------- */ +static int pe_section_class(Section *s) +{ + int type, flags; + const char *name; + type = s->sh_type; + flags = s->sh_flags; + name = s->name; + + if (0 == memcmp(name, ".stab", 5) || 0 == memcmp(name, ".debug_", 7)) { + if (s->s1->do_debug) + return sec_debug; + + } else if (flags & SHF_ALLOC) { + if (type == SHT_PROGBITS + || type == SHT_INIT_ARRAY + || type == SHT_FINI_ARRAY) { + if (flags & SHF_EXECINSTR) + return sec_text; + if (flags & SHF_WRITE) + return sec_data; + if (0 == strcmp(name, ".rsrc")) + return sec_rsrc; + if (0 == strcmp(name, ".iedat")) + return sec_idata; + if (0 == strcmp(name, ".pdata")) + return sec_pdata; + return sec_rdata; + } else if (type == SHT_NOBITS) { + return sec_bss; + } + return sec_other; + } else { + if (0 == strcmp(name, ".reloc")) + return sec_reloc; + } + return sec_last; +} + +static int pe_assign_addresses (struct pe_info *pe) +{ + int i, k, n, c, nbs; + ADDR3264 addr; + int *sec_order, *sec_cls; + struct section_info *si; + Section *s; + TCCState *s1 = pe->s1; + + if (PE_DLL == pe->type) + pe->reloc = new_section(pe->s1, ".reloc", SHT_PROGBITS, 0); + //pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); + + nbs = s1->nb_sections; + sec_order = tcc_mallocz(2 * sizeof (int) * nbs); + sec_cls = sec_order + nbs; + for (i = 1; i < nbs; ++i) { + s = s1->sections[i]; + k = pe_section_class(s); + for (n = i; n > 1 && k < (c = sec_cls[n - 1]); --n) + sec_cls[n] = c, sec_order[n] = sec_order[n - 1]; + sec_cls[n] = k, sec_order[n] = i; + } + si = NULL; + addr = pe->imagebase + 1; + + for (i = 1; (c = sec_cls[i]) < sec_last; ++i) { + s = s1->sections[sec_order[i]]; + + if (PE_MERGE_DATA && c == sec_bss) + c = sec_data; + + if (si && c == si->cls && c != sec_debug) { + /* merge with previous section */ + s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1; + } else { + si = NULL; + s->sh_addr = addr = pe_virtual_align(pe, addr); + } + + if (NULL == pe->thunk + && c == (data_section == rodata_section ? sec_data : sec_rdata)) + pe->thunk = s; + + if (s == pe->thunk) { + pe_build_imports(pe); + pe_build_exports(pe); + } + if (s == pe->reloc) + pe_build_reloc (pe); + + if (0 == s->data_offset) + continue; + + if (si) + goto add_section; + + si = tcc_mallocz(sizeof *si); + dynarray_add(&pe->sec_info, &pe->sec_count, si); + + strcpy(si->name, s->name); + si->cls = c; + si->sh_addr = addr; + + si->pe_flags = IMAGE_SCN_MEM_READ; + if (s->sh_flags & SHF_EXECINSTR) + si->pe_flags |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE; + else if (s->sh_type == SHT_NOBITS) + si->pe_flags |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; + else + si->pe_flags |= IMAGE_SCN_CNT_INITIALIZED_DATA; + if (s->sh_flags & SHF_WRITE) + si->pe_flags |= IMAGE_SCN_MEM_WRITE; + if (0 == (s->sh_flags & SHF_ALLOC)) + si->pe_flags |= IMAGE_SCN_MEM_DISCARDABLE; + +add_section: + addr += s->data_offset; + si->sh_size = addr - si->sh_addr; + if (s->sh_type != SHT_NOBITS) { + Section **ps = &si->sec; + while (*ps) + ps = &(*ps)->prev; + *ps = s, s->prev = NULL; + si->data_size = si->sh_size; + } + //printf("%08x %05x %08x %s\n", si->sh_addr, si->sh_size, si->pe_flags, s->name); + } +#if 0 + for (i = 1; i < nbs; ++i) { + Section *s = s1->sections[sec_order[i]]; + int type = s->sh_type; + int flags = s->sh_flags; + printf("section %-16s %-10s %p %04x %s,%s,%s\n", + s->name, + type == SHT_PROGBITS ? "progbits" : + type == SHT_INIT_ARRAY ? "initarr" : + type == SHT_FINI_ARRAY ? "finiarr" : + type == SHT_NOBITS ? "nobits" : + type == SHT_SYMTAB ? "symtab" : + type == SHT_STRTAB ? "strtab" : + type == SHT_RELX ? "rel" : "???", + s->sh_addr, + (unsigned)s->data_offset, + flags & SHF_ALLOC ? "alloc" : "", + flags & SHF_WRITE ? "write" : "", + flags & SHF_EXECINSTR ? "exec" : "" + ); + fflush(stdout); + } + s1->verbose = 2; +#endif + tcc_free(sec_order); + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static int pe_isafunc(TCCState *s1, int sym_index) +{ + Section *sr = text_section->reloc; + ElfW_Rel *rel, *rel_end; + ElfW(Addr)info = ELFW(R_INFO)(sym_index, R_XXX_FUNCCALL); +#ifdef R_XXX_FUNCCALL2 + ElfW(Addr)info2 = ELFW(R_INFO)(sym_index, R_XXX_FUNCCALL2); +#endif + if (!sr) + return 0; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + for (rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) { + if (rel->r_info == info) + return 1; +#ifdef R_XXX_FUNCCALL2 + if (rel->r_info == info2) + return 1; +#endif + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int pe_check_symbols(struct pe_info *pe) +{ + ElfW(Sym) *sym; + int sym_index, sym_end; + int ret = 0; + TCCState *s1 = pe->s1; + + pe_align_section(text_section, 8); + + sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + + sym = (ElfW(Sym) *)symtab_section->data + sym_index; + if (sym->st_shndx == SHN_UNDEF) { + + const char *name = (char*)symtab_section->link->data + sym->st_name; + unsigned type = ELFW(ST_TYPE)(sym->st_info); + int imp_sym = pe_find_import(pe->s1, sym); + struct import_symbol *is; + + if (imp_sym <= 0) + goto not_found; + + if (type == STT_NOTYPE) { + /* symbols from assembler have no type, find out which */ + if (pe_isafunc(s1, sym_index)) + type = STT_FUNC; + else + type = STT_OBJECT; + } + + is = pe_add_import(pe, imp_sym); + + if (type == STT_FUNC) { + unsigned offset = is->thk_offset; + if (offset) { + /* got aliased symbol, like stricmp and _stricmp */ + } else { + char buffer[100]; + unsigned char *p; + + /* add a helper symbol, will be patched later in + pe_build_imports */ + sprintf(buffer, "IAT.%s", name); + is->iat_index = put_elf_sym( + symtab_section, 0, sizeof(DWORD), + ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), + 0, SHN_UNDEF, buffer); + + offset = text_section->data_offset; + is->thk_offset = offset; + + /* add the 'jmp IAT[x]' instruction */ +#ifdef TCC_TARGET_ARM + p = section_ptr_add(text_section, 8+4); // room for code and address + write32le(p + 0, 0xE59FC000); // arm code ldr ip, [pc] ; PC+8+0 = 0001xxxx + write32le(p + 4, 0xE59CF000); // arm code ldr pc, [ip] + put_elf_reloc(symtab_section, text_section, + offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position +#else + p = section_ptr_add(text_section, 8); + write16le(p, 0x25FF); +#ifdef TCC_TARGET_X86_64 + write32le(p + 2, (DWORD)-4); +#endif + put_elf_reloc(symtab_section, text_section, + offset + 2, R_XXX_THUNKFIX, is->iat_index); +#endif + } + /* tcc_realloc might have altered sym's address */ + sym = (ElfW(Sym) *)symtab_section->data + sym_index; + + /* patch the original symbol */ + sym->st_value = offset; + sym->st_shndx = text_section->sh_num; + sym->st_other &= ~ST_PE_EXPORT; /* do not export */ + continue; + } + + if (type == STT_OBJECT) { /* data, ptr to that should be */ + if (0 == is->iat_index) { + /* original symbol will be patched later in pe_build_imports */ + is->iat_index = sym_index; + continue; + } + } + + not_found: + if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + /* STB_WEAK undefined symbols are accepted */ + continue; + ret = tcc_error_noabort("undefined symbol '%s'%s", name, + imp_sym < 0 ? ", missing __declspec(dllimport)?":""); + + } else if (pe->s1->rdynamic + && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { + /* if -rdynamic option, then export all non local symbols */ + sym->st_other |= ST_PE_EXPORT; + } + } + return ret; +} + +/*----------------------------------------------------------------------------*/ +#if PE_PRINT_SECTIONS +static void pe_print_section(FILE * f, Section * s) +{ + /* just if you're curious */ + BYTE *p, *e, b; + int i, n, l, m; + p = s->data; + e = s->data + s->data_offset; + l = e - p; + + fprintf(f, "section \"%s\"", s->name); + if (s->link) + fprintf(f, "\nlink \"%s\"", s->link->name); + if (s->reloc) + fprintf(f, "\nreloc \"%s\"", s->reloc->name); + fprintf(f, "\nv_addr %08X", (unsigned)s->sh_addr); + fprintf(f, "\ncontents %08X", (unsigned)l); + fprintf(f, "\n\n"); + + if (s->sh_type == SHT_NOBITS) + return; + + if (0 == l) + return; + + if (s->sh_type == SHT_SYMTAB) + m = sizeof(ElfW(Sym)); + else if (s->sh_type == SHT_RELX) + m = sizeof(ElfW_Rel); + else + m = 16; + + fprintf(f, "%-8s", "offset"); + for (i = 0; i < m; ++i) + fprintf(f, " %02x", i); + n = 56; + + if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_RELX) { + const char *fields1[] = { + "name", + "value", + "size", + "bind", + "type", + "other", + "shndx", + NULL + }; + + const char *fields2[] = { + "offs", + "type", + "symb", + NULL + }; + + const char **p; + + if (s->sh_type == SHT_SYMTAB) + p = fields1, n = 106; + else + p = fields2, n = 58; + + for (i = 0; p[i]; ++i) + fprintf(f, "%6s", p[i]); + fprintf(f, " symbol"); + } + + fprintf(f, "\n"); + for (i = 0; i < n; ++i) + fprintf(f, "-"); + fprintf(f, "\n"); + + for (i = 0; i < l;) + { + fprintf(f, "%08X", i); + for (n = 0; n < m; ++n) { + if (n + i < l) + fprintf(f, " %02X", p[i + n]); + else + fprintf(f, " "); + } + + if (s->sh_type == SHT_SYMTAB) { + ElfW(Sym) *sym = (ElfW(Sym) *) (p + i); + const char *name = s->link->data + sym->st_name; + fprintf(f, " %04X %04X %04X %02X %02X %02X %04X \"%s\"", + (unsigned)sym->st_name, + (unsigned)sym->st_value, + (unsigned)sym->st_size, + (unsigned)ELFW(ST_BIND)(sym->st_info), + (unsigned)ELFW(ST_TYPE)(sym->st_info), + (unsigned)sym->st_other, + (unsigned)sym->st_shndx, + name); + + } else if (s->sh_type == SHT_RELX) { + ElfW_Rel *rel = (ElfW_Rel *) (p + i); + ElfW(Sym) *sym = + (ElfW(Sym) *) s->link->data + ELFW(R_SYM)(rel->r_info); + const char *name = s->link->link->data + sym->st_name; + fprintf(f, " %04X %02X %04X \"%s\"", + (unsigned)rel->r_offset, + (unsigned)ELFW(R_TYPE)(rel->r_info), + (unsigned)ELFW(R_SYM)(rel->r_info), + name); + } else { + fprintf(f, " "); + for (n = 0; n < m; ++n) { + if (n + i < l) { + b = p[i + n]; + if (b < 32 || b >= 127) + b = '.'; + fprintf(f, "%c", b); + } + } + } + i += m; + fprintf(f, "\n"); + } + fprintf(f, "\n\n"); +} + +static void pe_print_sections(TCCState *s1, const char *fname) +{ + Section *s; + FILE *f; + int i; + f = fopen(fname, "w"); + for (i = 1; i < s1->nb_sections; ++i) { + s = s1->sections[i]; + pe_print_section(f, s); + } + pe_print_section(f, s1->dynsymtab_section); + fclose(f); +} +#endif + +/* ------------------------------------------------------------- */ +/* helper function for load/store to insert one more indirection */ + +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2) +{ + int r2; + if ((sv->r & (VT_VALMASK|VT_SYM)) != (VT_CONST|VT_SYM) || (sv->r2 != VT_CONST)) + return sv; + if (!sv->sym->a.dllimport) + return sv; + // printf("import %04x %04x %04x %s\n", sv->type.t, sv->sym->type.t, sv->r, get_tok_str(sv->sym->v, NULL)); + memset(v2, 0, sizeof *v2); + v2->type.t = VT_PTR; + v2->r = VT_CONST | VT_SYM | VT_LVAL; + v2->sym = sv->sym; + + r2 = get_reg(RC_INT); + load(r2, v2); + v2->r = r2; + if ((uint32_t)sv->c.i) { + vpushv(v2); + vpushi(sv->c.i); + gen_opi('+'); + *v2 = *vtop--; + } + v2->type.t = sv->type.t; + v2->r |= sv->r & VT_LVAL; + return v2; +} +#endif + +ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value) +{ + return set_elf_sym( + s1->dynsymtab_section, + value, + dllindex, /* st_size */ + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), + 0, + value ? SHN_ABS : SHN_UNDEF, + name + ); +} + +static int read_mem(int fd, unsigned offset, void *buffer, unsigned len) +{ + lseek(fd, offset, SEEK_SET); + return len == read(fd, buffer, len); +} + +/* ------------------------------------------------------------- */ + +static int get_dllexports(int fd, char **pp) +{ + int l, i, n, n0, ret; + char *p; + + IMAGE_SECTION_HEADER ish; + IMAGE_EXPORT_DIRECTORY ied; + IMAGE_DOS_HEADER dh; + IMAGE_FILE_HEADER ih; + DWORD sig, ref, addr, ptr, namep; + + int pef_hdroffset, opt_hdroffset, sec_hdroffset; + + n = n0 = 0; + p = NULL; + ret = 1; + if (!read_mem(fd, 0, &dh, sizeof dh)) + goto the_end; + if (!read_mem(fd, dh.e_lfanew, &sig, sizeof sig)) + goto the_end; + if (sig != 0x00004550) + goto the_end; + pef_hdroffset = dh.e_lfanew + sizeof sig; + if (!read_mem(fd, pef_hdroffset, &ih, sizeof ih)) + goto the_end; + opt_hdroffset = pef_hdroffset + sizeof ih; + if (ih.Machine == 0x014C) { + IMAGE_OPTIONAL_HEADER32 oh; + sec_hdroffset = opt_hdroffset + sizeof oh; + if (!read_mem(fd, opt_hdroffset, &oh, sizeof oh)) + goto the_end; + if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh.NumberOfRvaAndSizes) + goto the_end_0; + addr = oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + } else if (ih.Machine == 0x8664) { + IMAGE_OPTIONAL_HEADER64 oh; + sec_hdroffset = opt_hdroffset + sizeof oh; + if (!read_mem(fd, opt_hdroffset, &oh, sizeof oh)) + goto the_end; + if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh.NumberOfRvaAndSizes) + goto the_end_0; + addr = oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + } else + goto the_end; + + //printf("addr: %08x\n", addr); + for (i = 0; i < ih.NumberOfSections; ++i) { + if (!read_mem(fd, sec_hdroffset + i * sizeof ish, &ish, sizeof ish)) + goto the_end; + //printf("vaddr: %08x\n", ish.VirtualAddress); + if (addr >= ish.VirtualAddress && addr < ish.VirtualAddress + ish.SizeOfRawData) + goto found; + } + goto the_end_0; + +found: + ref = ish.VirtualAddress - ish.PointerToRawData; + if (!read_mem(fd, addr - ref, &ied, sizeof ied)) + goto the_end; + + namep = ied.AddressOfNames - ref; + for (i = 0; i < ied.NumberOfNames; ++i) { + if (!read_mem(fd, namep, &ptr, sizeof ptr)) + goto the_end; + namep += sizeof ptr; + for (l = 0;;) { + if (n+1 >= n0) + p = tcc_realloc(p, n0 = n0 ? n0 * 2 : 256); + if (!read_mem(fd, ptr - ref + l++, p + n, 1)) { + tcc_free(p), p = NULL; + goto the_end; + } + if (p[n++] == 0) + break; + } + } + if (p) + p[n] = 0; +the_end_0: + ret = 0; +the_end: + *pp = p; + return ret; +} + +/* ------------------------------------------------------------- + * This is for compiled windows resources in 'coff' format + * as generated by 'windres.exe -O coff ...'. + */ + +static int pe_load_res(TCCState *s1, int fd) +{ + struct pe_rsrc_header hdr; + Section *rsrc_section; + int i, ret = -1, sym_index; + BYTE *ptr; + unsigned offs; + + if (!read_mem(fd, 0, &hdr, sizeof hdr)) + goto quit; + + if (hdr.filehdr.Machine != IMAGE_FILE_MACHINE + || hdr.filehdr.NumberOfSections != 1 + || strcmp((char*)hdr.sectionhdr.Name, ".rsrc") != 0) + goto quit; + + rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC); + ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData); + offs = hdr.sectionhdr.PointerToRawData; + if (!read_mem(fd, offs, ptr, hdr.sectionhdr.SizeOfRawData)) + goto quit; + offs = hdr.sectionhdr.PointerToRelocations; + sym_index = put_elf_sym(symtab_section, 0, 0, 0, 0, rsrc_section->sh_num, ".rsrc"); + for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i) { + struct pe_rsrc_reloc rel; + if (!read_mem(fd, offs, &rel, sizeof rel)) + goto quit; + // printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type); + if (rel.type != RSRC_RELTYPE) + goto quit; + put_elf_reloc(symtab_section, rsrc_section, + rel.offset, R_XXX_RELATIVE, sym_index); + offs += sizeof rel; + } + ret = 0; +quit: + return ret; +} + +/* ------------------------------------------------------------- */ + +static char *trimfront(char *p) +{ + while ((unsigned char)*p <= ' ' && *p && *p != '\n') + ++p; + return p; +} + +/* +static char *trimback(char *a, char *e) +{ + while (e > a && (unsigned char)e[-1] <= ' ') + --e; + *e = 0;; + return a; +}*/ + +static char *get_token(char **s, char *f) +{ + char *p = *s, *e; + p = e = trimfront(p); + while ((unsigned char)*e > ' ') + ++e; + *s = trimfront(e); + *f = **s; *e = 0; + return p; +} + +static int pe_load_def(TCCState *s1, int fd) +{ + int state = 0, ret = -1, dllindex = 0, ord; + char dllname[80], *buf, *line, *p, *x, next; + + buf = tcc_load_text(fd); + for (line = buf;; ++line) { + p = get_token(&line, &next); + if (!(*p && *p != ';')) + goto skip; + switch (state) { + case 0: + if (0 != stricmp(p, "LIBRARY") || next == '\n') + goto quit; + pstrcpy(dllname, sizeof dllname, get_token(&line, &next)); + ++state; + break; + case 1: + if (0 != stricmp(p, "EXPORTS")) + goto quit; + ++state; + break; + case 2: + dllindex = tcc_add_dllref(s1, dllname, 0)->index; + ++state; + /* fall through */ + default: + /* get ordinal and will store in sym->st_value */ + ord = 0; + if (next == '@') { + x = get_token(&line, &next); + ord = (int)strtol(x + 1, &x, 10); + } + //printf("token %s ; %s : %d\n", dllname, p, ord); + pe_putimport(s1, dllindex, p, ord); + break; + } +skip: + while ((unsigned char)next > ' ') + get_token(&line, &next); + if (next != '\n') + break; + } + ret = 0; +quit: + tcc_free(buf); + return ret; +} + +/* ------------------------------------------------------------- */ + +static int pe_load_dll(TCCState *s1, int fd, const char *filename) +{ + char *p, *q; + int index, ret; + + ret = get_dllexports(fd, &p); + if (ret) { + return -1; + } else if (p) { + index = tcc_add_dllref(s1, filename, 0)->index; + for (q = p; *q; q += 1 + strlen(q)) + pe_putimport(s1, index, q, 0); + tcc_free(p); + } + return 0; +} + +ST_FUNC int pe_load_file(struct TCCState *s1, int fd, const char *filename) +{ + int ret = -1; + char buf[10]; + if (0 == strcmp(tcc_fileextension(filename), ".def")) + ret = pe_load_def(s1, fd); + else if (pe_load_res(s1, fd) == 0) + ret = 0; + else if (read_mem(fd, 0, buf, 4) && 0 == memcmp(buf, "MZ", 2)) + ret = pe_load_dll(s1, fd, filename); + return ret; +} + +PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp) +{ + int ret, fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + ret = get_dllexports(fd, pp); + close(fd); + return ret; +} + +/* ------------------------------------------------------------- */ +#ifdef TCC_TARGET_X86_64 +static unsigned pe_add_uwwind_info(TCCState *s1) +{ + if (NULL == s1->uw_pdata) { + s1->uw_pdata = find_section(s1, ".pdata"); + s1->uw_pdata->sh_addralign = 4; + } + if (0 == s1->uw_sym) + s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0, text_section->sh_num, ".uw_base"); + if (0 == s1->uw_offs) { + /* As our functions all have the same stackframe, we use one entry for all */ + static const unsigned char uw_info[] = { + 0x01, // UBYTE: 3 Version , UBYTE: 5 Flags + 0x04, // UBYTE Size of prolog + 0x02, // UBYTE Count of unwind codes + 0x05, // UBYTE: 4 Frame Register (rbp), UBYTE: 4 Frame Register offset (scaled) + // USHORT * n Unwind codes array + // 0x0b, 0x01, 0xff, 0xff, // stack size + 0x04, 0x03, // set frame ptr (mov rsp -> rbp) + 0x01, 0x50 // push reg (rbp) + }; + + Section *s = text_section; + unsigned char *p; + + section_ptr_add(s, -s->data_offset & 3); /* align */ + s1->uw_offs = s->data_offset; + p = section_ptr_add(s, sizeof uw_info); + memcpy(p, uw_info, sizeof uw_info); + } + + return s1->uw_offs; +} + +ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack) +{ + TCCState *s1 = tcc_state; + Section *pd; + unsigned o, n, d; + struct /* _RUNTIME_FUNCTION */ { + DWORD BeginAddress; + DWORD EndAddress; + DWORD UnwindData; + } *p; + + d = pe_add_uwwind_info(s1); + pd = s1->uw_pdata; + o = pd->data_offset; + p = section_ptr_add(pd, sizeof *p); + + /* record this function */ + p->BeginAddress = start; + p->EndAddress = end; + p->UnwindData = d; + + /* put relocations on it */ + for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress) + put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym); +} +#endif +/* ------------------------------------------------------------- */ +#ifdef TCC_TARGET_X86_64 +#define PE_STDSYM(n,s) n +#else +#define PE_STDSYM(n,s) "_" n s +#endif + +static void pe_add_runtime(TCCState *s1, struct pe_info *pe) +{ + const char *start_symbol; + int pe_type; + + if (TCC_OUTPUT_DLL == s1->output_type) { + pe_type = PE_DLL; + start_symbol = PE_STDSYM("__dllstart","@12"); + } else { + const char *run_symbol; + if (find_elf_sym(symtab_section, PE_STDSYM("WinMain","@16"))) { + start_symbol = "__winstart"; + run_symbol = "__runwinmain"; + pe_type = PE_GUI; + } else if (find_elf_sym(symtab_section, PE_STDSYM("wWinMain","@16"))) { + start_symbol = "__wwinstart"; + run_symbol = "__runwwinmain"; + pe_type = PE_GUI; + } else if (find_elf_sym(symtab_section, "wmain")) { + start_symbol = "__wstart"; + run_symbol = "__runwmain"; + pe_type = PE_EXE; + } else { + start_symbol = "__start"; + run_symbol = "__runmain"; + pe_type = PE_EXE; + + } + if (TCC_OUTPUT_MEMORY == s1->output_type) + start_symbol = run_symbol; + } + + pe->start_symbol = start_symbol + 1; + if (!s1->leading_underscore || strchr(start_symbol, '@')) + ++start_symbol; + +#ifdef CONFIG_TCC_BACKTRACE + if (s1->do_backtrace) { +#ifdef CONFIG_TCC_BCHECK + if (s1->do_bounds_check && s1->output_type != TCC_OUTPUT_DLL) + tcc_add_support(s1, "bcheck.o"); +#endif + if (s1->output_type == TCC_OUTPUT_EXE) + tcc_add_support(s1, "bt-exe.o"); + if (s1->output_type == TCC_OUTPUT_DLL) + tcc_add_support(s1, "bt-dll.o"); + if (s1->output_type != TCC_OUTPUT_DLL) + tcc_add_support(s1, "bt-log.o"); + if (s1->output_type != TCC_OUTPUT_MEMORY) + tcc_add_btstub(s1); + } +#endif + + /* grab the startup code from libtcc1.a */ +#ifdef TCC_IS_NATIVE + if (TCC_OUTPUT_MEMORY != s1->output_type || s1->runtime_main) +#endif + set_global_sym(s1, start_symbol, NULL, 0); + + if (0 == s1->nostdlib) { + static const char * const libs[] = { + "msvcrt", "kernel32", "", "user32", "gdi32", NULL + }; + const char * const *pp, *p; + if (TCC_LIBTCC1[0]) + tcc_add_support(s1, TCC_LIBTCC1); + for (pp = libs; 0 != (p = *pp); ++pp) { + if (*p) + tcc_add_library_err(s1, p); + else if (PE_DLL != pe_type && PE_GUI != pe_type) + break; + } + } + + /* need this for 'tccelf.c:relocate_sections()' */ + if (TCC_OUTPUT_DLL == s1->output_type) + s1->output_type = TCC_OUTPUT_EXE; + if (TCC_OUTPUT_MEMORY == s1->output_type) + pe_type = PE_RUN; + pe->type = pe_type; +} + +static void pe_set_options(TCCState * s1, struct pe_info *pe) +{ + if (PE_DLL == pe->type) { + /* XXX: check if is correct for arm-pe target */ + pe->imagebase = 0x10000000; + } else { +#if defined(TCC_TARGET_ARM) + pe->imagebase = 0x00010000; +#else + pe->imagebase = 0x00400000; +#endif + } + +#if defined(TCC_TARGET_ARM) + /* we use "console" subsystem by default */ + pe->subsystem = 9; +#else + if (PE_DLL == pe->type || PE_GUI == pe->type) + pe->subsystem = 2; + else + pe->subsystem = 3; +#endif + /* Allow override via -Wl,-subsystem=... option */ + if (s1->pe_subsystem != 0) + pe->subsystem = s1->pe_subsystem; + + /* set default file/section alignment */ + if (pe->subsystem == 1) { + pe->section_align = 0x20; + pe->file_align = 0x20; + } else { + pe->section_align = 0x1000; + pe->file_align = 0x200; + } + + if (s1->section_align != 0) + pe->section_align = s1->section_align; + if (s1->pe_file_align != 0) + pe->file_align = s1->pe_file_align; + + if ((pe->subsystem >= 10) && (pe->subsystem <= 12)) + pe->imagebase = 0; + + if (s1->has_text_addr) + pe->imagebase = s1->text_addr; +} + +ST_FUNC int pe_output_file(TCCState *s1, const char *filename) +{ + struct pe_info pe; + + memset(&pe, 0, sizeof pe); + pe.filename = filename; + pe.s1 = s1; + s1->filetype = 0; + +#ifdef CONFIG_TCC_BCHECK + tcc_add_bcheck(s1); +#endif + tcc_add_pragma_libs(s1); + pe_add_runtime(s1, &pe); + resolve_common_syms(s1); + pe_set_options(s1, &pe); + pe_check_symbols(&pe); + + if (s1->nb_errors) + ; + else if (filename) { + pe_assign_addresses(&pe); + relocate_syms(s1, s1->symtab, 0); + s1->pe_imagebase = pe.imagebase; + relocate_sections(s1); + pe.start_addr = (DWORD) + (get_sym_addr(s1, pe.start_symbol, 1, 1) - pe.imagebase); + if (0 == s1->nb_errors) + pe_write(&pe); + dynarray_reset(&pe.sec_info, &pe.sec_count); + } else { +#ifdef TCC_IS_NATIVE + pe.thunk = data_section; + pe_build_imports(&pe); + s1->runtime_main = pe.start_symbol; +#ifdef TCC_TARGET_X86_64 + s1->uw_pdata = find_section(s1, ".pdata"); +#endif +#endif + } + pe_free_imports(&pe); +#if PE_PRINT_SECTIONS + if (s1->g_debug & 8) + pe_print_sections(s1, "tcc.log"); +#endif + return s1->nb_errors ? -1 : 0; +} + +/* ------------------------------------------------------------- */ diff --git a/vendor/tcc/tccpp.c b/vendor/tcc/tccpp.c index 8b588cb2..dc68dc17 100644 --- a/vendor/tcc/tccpp.c +++ b/vendor/tcc/tccpp.c @@ -3622,7 +3622,11 @@ static void tcc_predefs(TCCState *s1, CString *cs, int is_asm) cstr_printf(cs, "#define __STDC_VERSION__ %dL\n", s1->cversion); cstr_cat(cs, /* load more predefs and __builtins */ +#if CONFIG_TCC_PREDEFS #include "tccdefs_.h" /* include as strings */ +#else + "#include \n" /* load at runtime */ +#endif , -1); } cstr_printf(cs, "#define __BASE_FILE__ \"%s\"\n", file->filename);