Generate PLT thumb stub for an ARM PLT entry only when at least one Thumb instruction branches to that entry. Warning: To save space, this commit reuses the bit 0 of entries of got_offsets array. The GOT offset is thus saved in a 31 bit value. Make sure to divide by 2 (right shift by 1) an offset before storing it there and conversely to multiply the value by 2 (left shift by 1) before using it.
Unified · Split
| @@ -33,6 +33,7 @@ · 32 unmodified lines | |||
| 33 | 33 | ||
| 34 | #include <stdlib.h> | 34 | #include <stdlib.h> |
| 35 | #include <stdio.h> | 35 | #include <stdio.h> |
| 36 | #include <limits.h> | ||
| 36 | #include <stdarg.h> | 37 | #include <stdarg.h> |
| 37 | #include <string.h> | 38 | #include <string.h> |
| 38 | #include <errno.h> | 39 | #include <errno.h> |
| @@ -524,11 +525,15 @@ struct TCCState { · 485 unmodified lines | |||
| 524 | Section **priv_sections; | 525 | Section **priv_sections; |
| 525 | int nb_priv_sections; /* number of private sections */ | 526 | int nb_priv_sections; /* number of private sections */ |
| 526 | 527 | ||
| 527 | /* got handling */ | 528 | /* got & plt handling */ |
| 528 | Section *got; | 529 | Section *got; |
| 529 | Section *plt; | 530 | Section *plt; |
| 530 | unsigned long *got_offsets; | 531 | struct { |
| 531 | int nb_got_offsets; | 532 | unsigned long plt_thumb_stub:1; |
| 533 | /* mult by 2 (or left shift by 1) before use */ | ||
| 534 | unsigned long got_offset:(sizeof(long)*CHAR_BIT-1); | ||
| 535 | } *sym_infos; | ||
| 536 | int nb_sym_infos; | ||
| 532 | /* give the correspondance from symtab indexes to dynsym indexes */ | 537 | /* give the correspondance from symtab indexes to dynsym indexes */ |
| 533 | int *symtab_to_dynsym; | 538 | int *symtab_to_dynsym; |
| 534 | 539 | ||
| @@ -581,7 +581,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s) · 580 unmodified lines | |||
| 581 | break; | 581 | break; |
| 582 | case R_386_GOT32: | 582 | case R_386_GOT32: |
| 583 | /* we load the got offset */ | 583 | /* we load the got offset */ |
| 584 | *(int *)ptr += s1->got_offsets[sym_index]; | 584 | *(int *)ptr += s1->sym_infos[sym_index].got_offset << 1; |
| 585 | break; | 585 | break; |
| 586 | case R_386_16: | 586 | case R_386_16: |
| 587 | if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) { | 587 | if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) { |
| @@ -758,7 +758,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s) · 170 unmodified lines | |||
| 758 | break; | 758 | break; |
| 759 | case R_ARM_GOT_BREL: | 759 | case R_ARM_GOT_BREL: |
| 760 | /* we load the got offset */ | 760 | /* we load the got offset */ |
| 761 | *(int *)ptr += s1->got_offsets[sym_index]; | 761 | *(int *)ptr += s1->sym_infos[sym_index].got_offset << 1; |
| 762 | break; | 762 | break; |
| 763 | case R_ARM_COPY: | 763 | case R_ARM_COPY: |
| 764 | break; | 764 | break; |
| @@ -864,14 +864,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s) · 99 unmodified lines | |||
| 864 | } | 864 | } |
| 865 | #endif | 865 | #endif |
| 866 | *(int *)ptr += (s1->got->sh_addr - addr + | 866 | *(int *)ptr += (s1->got->sh_addr - addr + |
| 867 | s1->got_offsets[sym_index] - 4); | 867 | (s1->sym_infos[sym_index].got_offset << 1) - 4); |
| 868 | break; | 868 | break; |
| 869 | case R_X86_64_GOTTPOFF: | 869 | case R_X86_64_GOTTPOFF: |
| 870 | *(int *)ptr += val - s1->got->sh_addr; | 870 | *(int *)ptr += val - s1->got->sh_addr; |
| 871 | break; | 871 | break; |
| 872 | case R_X86_64_GOT32: | 872 | case R_X86_64_GOT32: |
| 873 | /* we load the got offset */ | 873 | /* we load the got offset */ |
| 874 | *(int *)ptr += s1->got_offsets[sym_index]; | 874 | *(int *)ptr += s1->sym_infos[sym_index].got_offset << 1; |
| 875 | break; | 875 | break; |
| 876 | #else | 876 | #else |
| 877 | #error unsupported processor | 877 | #error unsupported processor |
| @@ -941,23 +941,36 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr) · 63 unmodified lines | |||
| 941 | return count; | 941 | return count; |
| 942 | } | 942 | } |
| 943 | 943 | ||
| 944 | static void put_got_offset(TCCState *s1, int index, unsigned long val) | 944 | static void put_sym_info(TCCState *s1, int index) |
| 945 | { | 945 | { |
| 946 | int n; | 946 | int n; |
| 947 | unsigned long *tab; | 947 | typeof(s1->sym_infos) tab; |
| 948 | 948 | ||
| 949 | if (index >= s1->nb_got_offsets) { | 949 | if (index >= s1->nb_sym_infos) { |
| 950 | /* find immediately bigger power of 2 and reallocate array */ | 950 | /* find immediately bigger power of 2 and reallocate array */ |
| 951 | n = 1; | 951 | n = 1; |
| 952 | while (index >= n) | 952 | while (index >= n) |
| 953 | n *= 2; | 953 | n *= 2; |
| 954 | tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long)); | 954 | tab = tcc_realloc(s1->sym_infos, n * sizeof(unsigned long)); |
| 955 | s1->got_offsets = tab; | 955 | s1->sym_infos = tab; |
| 956 | memset(s1->got_offsets + s1->nb_got_offsets, 0, | 956 | memset(s1->sym_infos + s1->nb_sym_infos, 0, |
| 957 | (n - s1->nb_got_offsets) * sizeof(unsigned long)); | 957 | (n - s1->nb_sym_infos) * sizeof(unsigned long)); |
| 958 | s1->nb_got_offsets = n; | 958 | s1->nb_sym_infos = n; |
| 959 | } | 959 | } |
| 960 | s1->got_offsets[index] = val; | 960 | } |
| 961 | |||
| 962 | #ifdef TCC_TARGET_ARM | ||
| 963 | static void put_plt_thumb_stub(TCCState *s1, int index, unsigned char bit) | ||
| 964 | { | ||
| 965 | put_sym_info(s1, index); | ||
| 966 | s1->sym_infos[index].plt_thumb_stub = bit; | ||
| 967 | } | ||
| 968 | #endif | ||
| 969 | |||
| 970 | static void put_got_offset(TCCState *s1, int index, unsigned long val) | ||
| 971 | { | ||
| 972 | put_sym_info(s1, index); | ||
| 973 | s1->sym_infos[index].got_offset = val >> 1; | ||
| 961 | } | 974 | } |
| 962 | 975 | ||
| 963 | /* XXX: suppress that */ | 976 | /* XXX: suppress that */ |
| @@ -1021,8 +1034,8 @@ static void put_got_entry(TCCState *s1, · 57 unmodified lines | |||
| 1021 | build_got(s1); | 1034 | build_got(s1); |
| 1022 | 1035 | ||
| 1023 | /* if a got entry already exists for that symbol, no need to add one */ | 1036 | /* if a got entry already exists for that symbol, no need to add one */ |
| 1024 | if (sym_index < s1->nb_got_offsets && | 1037 | if (sym_index < s1->nb_sym_infos && |
| 1025 | s1->got_offsets[sym_index] != 0) | 1038 | s1->sym_infos[sym_index].got_offset << 1) |
| 1026 | return; | 1039 | return; |
| 1027 | 1040 | ||
| 1028 | put_got_offset(s1, sym_index, s1->got->data_offset); | 1041 | put_got_offset(s1, sym_index, s1->got->data_offset); |
| @@ -1102,10 +1115,13 @@ static void put_got_entry(TCCState *s1, · 73 unmodified lines | |||
| 1102 | put32(p + 12, 0xe5bef008); | 1115 | put32(p + 12, 0xe5bef008); |
| 1103 | } | 1116 | } |
| 1104 | 1117 | ||
| 1105 | p = section_ptr_add(plt, 20); | 1118 | if (s1->sym_infos[sym_index].plt_thumb_stub) { |
| 1106 | put32(p , 0x4778); // bx pc | 1119 | p = section_ptr_add(plt, 20); |
| 1107 | put32(p+2, 0x46c0); // nop | 1120 | put32(p , 0x4778); // bx pc |
| 1108 | p += 4; | 1121 | put32(p+2, 0x46c0); // nop |
| 1122 | p += 4; | ||
| 1123 | } else | ||
| 1124 | p = section_ptr_add(plt, 16); | ||
| 1109 | put32(p , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT | 1125 | put32(p , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT |
| 1110 | put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset | 1126 | put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset |
| 1111 | put32(p+8, 0xe59cf000); // ldr pc, [ip] // load absolute address or load offset | 1127 | put32(p+8, 0xe59cf000); // ldr pc, [ip] // load absolute address or load offset |
| @@ -1492,9 +1508,9 @@ ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel) · 380 unmodified lines | |||
| 1492 | ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; | 1508 | ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; |
| 1493 | unsigned long offset; | 1509 | unsigned long offset; |
| 1494 | 1510 | ||
| 1495 | if (sym_index >= s1->nb_got_offsets) | 1511 | if (sym_index >= s1->nb_sym_infos) |
| 1496 | return; | 1512 | return; |
| 1497 | offset = s1->got_offsets[sym_index]; | 1513 | offset = s1->sym_infos[sym_index].got_offset << 1; |
| 1498 | section_reserve(s1->got, offset + PTR_SIZE); | 1514 | section_reserve(s1->got, offset + PTR_SIZE); |
| 1499 | #ifdef TCC_TARGET_X86_64 | 1515 | #ifdef TCC_TARGET_X86_64 |
| 1500 | /* only works for x86-64 */ | 1516 | /* only works for x86-64 */ |
| @@ -2068,7 +2084,8 @@ static int elf_output_file(TCCState *s1, const char *filename) · 567 unmodified lines | |||
| 2068 | x=s1->got->sh_addr - s1->plt->sh_addr - 12; | 2084 | x=s1->got->sh_addr - s1->plt->sh_addr - 12; |
| 2069 | p += 16; | 2085 | p += 16; |
| 2070 | while (p < p_end) { | 2086 | while (p < p_end) { |
| 2071 | p += 4; | 2087 | if (get32(p) == 0x46c04778) /* PLT Thumb stub present */ |
| 2088 | p += 4; | ||
| 2072 | put32(p + 12, x + get32(p + 12) + s1->plt->data - p); | 2089 | put32(p + 12, x + get32(p + 12) + s1->plt->data - p); |
| 2073 | p += 16; | 2090 | p += 16; |
| 2074 | } | 2091 | } |
| @@ -2302,7 +2319,7 @@ static int elf_output_file(TCCState *s1, const char *filename) · 227 unmodified lines | |||
| 2302 | tcc_free(s1->symtab_to_dynsym); | 2319 | tcc_free(s1->symtab_to_dynsym); |
| 2303 | tcc_free(section_order); | 2320 | tcc_free(section_order); |
| 2304 | tcc_free(phdr); | 2321 | tcc_free(phdr); |
| 2305 | tcc_free(s1->got_offsets); | 2322 | tcc_free(s1->sym_infos); |
| 2306 | return ret; | 2323 | return ret; |
| 2307 | } | 2324 | } |
| 2308 | 2325 | ||
| @@ -2597,6 +2614,19 @@ ST_FUNC int tcc_load_object_file(TCCState *s1, · 288 unmodified lines | |||
| 2597 | rel->r_info = ELFW(R_INFO)(sym_index, type); | 2614 | rel->r_info = ELFW(R_INFO)(sym_index, type); |
| 2598 | /* offset the relocation offset */ | 2615 | /* offset the relocation offset */ |
| 2599 | rel->r_offset += offseti; | 2616 | rel->r_offset += offseti; |
| 2617 | #ifdef TCC_TARGET_ARM | ||
| 2618 | /* Jumps and branches from a Thumb code to a PLT entry need | ||
| 2619 | special handling since PLT entries are ARM code. | ||
| 2620 | Unconditional bl instructions referencing PLT entries are | ||
| 2621 | handled by converting these instructions into blx | ||
| 2622 | instructions. Other case of instructions referencing a PLT | ||
| 2623 | entry require to add a Thumb stub before the PLT entry to | ||
| 2624 | switch to ARM mode. We set bit 0 of the got offset of a | ||
| 2625 | symbol to indicate such a case. */ | ||
| 2626 | if (type == R_ARM_THM_JUMP24) { | ||
| 2627 | put_plt_thumb_stub(s1, sym_index, 1); | ||
| 2628 | } | ||
| 2629 | #endif | ||
| 2600 | } | 2630 | } |
| 2601 | break; | 2631 | break; |
| 2602 | default: | 2632 | default: |