162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* This is included from relocs_32/64.c */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#define ElfW(type) _ElfW(ELF_BITS, type) 562306a36Sopenharmony_ci#define _ElfW(bits, type) __ElfW(bits, type) 662306a36Sopenharmony_ci#define __ElfW(bits, type) Elf##bits##_##type 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define Elf_Addr ElfW(Addr) 962306a36Sopenharmony_ci#define Elf_Ehdr ElfW(Ehdr) 1062306a36Sopenharmony_ci#define Elf_Phdr ElfW(Phdr) 1162306a36Sopenharmony_ci#define Elf_Shdr ElfW(Shdr) 1262306a36Sopenharmony_ci#define Elf_Sym ElfW(Sym) 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic Elf_Ehdr ehdr; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct relocs { 1762306a36Sopenharmony_ci uint32_t *offset; 1862306a36Sopenharmony_ci unsigned long count; 1962306a36Sopenharmony_ci unsigned long size; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic struct relocs relocs; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct section { 2562306a36Sopenharmony_ci Elf_Shdr shdr; 2662306a36Sopenharmony_ci struct section *link; 2762306a36Sopenharmony_ci Elf_Sym *symtab; 2862306a36Sopenharmony_ci Elf_Rel *reltab; 2962306a36Sopenharmony_ci char *strtab; 3062306a36Sopenharmony_ci long shdr_offset; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_cistatic struct section *secs; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic const char * const regex_sym_kernel = { 3562306a36Sopenharmony_ci/* Symbols matching these regex's should never be relocated */ 3662306a36Sopenharmony_ci "^(__crc_)", 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic regex_t sym_regex_c; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int regex_skip_reloc(const char *sym_name) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return !regexec(&sym_regex_c, sym_name, 0, NULL, 0); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void regex_init(void) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci char errbuf[128]; 4962306a36Sopenharmony_ci int err; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci err = regcomp(&sym_regex_c, regex_sym_kernel, 5262306a36Sopenharmony_ci REG_EXTENDED|REG_NOSUB); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (err) { 5562306a36Sopenharmony_ci regerror(err, &sym_regex_c, errbuf, sizeof(errbuf)); 5662306a36Sopenharmony_ci die("%s", errbuf); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const char *rel_type(unsigned type) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci static const char * const type_name[] = { 6362306a36Sopenharmony_ci#define REL_TYPE(X)[X] = #X 6462306a36Sopenharmony_ci REL_TYPE(R_MIPS_NONE), 6562306a36Sopenharmony_ci REL_TYPE(R_MIPS_16), 6662306a36Sopenharmony_ci REL_TYPE(R_MIPS_32), 6762306a36Sopenharmony_ci REL_TYPE(R_MIPS_REL32), 6862306a36Sopenharmony_ci REL_TYPE(R_MIPS_26), 6962306a36Sopenharmony_ci REL_TYPE(R_MIPS_HI16), 7062306a36Sopenharmony_ci REL_TYPE(R_MIPS_LO16), 7162306a36Sopenharmony_ci REL_TYPE(R_MIPS_GPREL16), 7262306a36Sopenharmony_ci REL_TYPE(R_MIPS_LITERAL), 7362306a36Sopenharmony_ci REL_TYPE(R_MIPS_GOT16), 7462306a36Sopenharmony_ci REL_TYPE(R_MIPS_PC16), 7562306a36Sopenharmony_ci REL_TYPE(R_MIPS_CALL16), 7662306a36Sopenharmony_ci REL_TYPE(R_MIPS_GPREL32), 7762306a36Sopenharmony_ci REL_TYPE(R_MIPS_64), 7862306a36Sopenharmony_ci REL_TYPE(R_MIPS_HIGHER), 7962306a36Sopenharmony_ci REL_TYPE(R_MIPS_HIGHEST), 8062306a36Sopenharmony_ci REL_TYPE(R_MIPS_PC21_S2), 8162306a36Sopenharmony_ci REL_TYPE(R_MIPS_PC26_S2), 8262306a36Sopenharmony_ci#undef REL_TYPE 8362306a36Sopenharmony_ci }; 8462306a36Sopenharmony_ci const char *name = "unknown type rel type name"; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (type < ARRAY_SIZE(type_name) && type_name[type]) 8762306a36Sopenharmony_ci name = type_name[type]; 8862306a36Sopenharmony_ci return name; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const char *sec_name(unsigned shndx) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci const char *sec_strtab; 9462306a36Sopenharmony_ci const char *name; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci sec_strtab = secs[ehdr.e_shstrndx].strtab; 9762306a36Sopenharmony_ci if (shndx < ehdr.e_shnum) 9862306a36Sopenharmony_ci name = sec_strtab + secs[shndx].shdr.sh_name; 9962306a36Sopenharmony_ci else if (shndx == SHN_ABS) 10062306a36Sopenharmony_ci name = "ABSOLUTE"; 10162306a36Sopenharmony_ci else if (shndx == SHN_COMMON) 10262306a36Sopenharmony_ci name = "COMMON"; 10362306a36Sopenharmony_ci else 10462306a36Sopenharmony_ci name = "<noname>"; 10562306a36Sopenharmony_ci return name; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct section *sec_lookup(const char *secname) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int i; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) 11362306a36Sopenharmony_ci if (strcmp(secname, sec_name(i)) == 0) 11462306a36Sopenharmony_ci return &secs[i]; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const char *sym_name(const char *sym_strtab, Elf_Sym *sym) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci const char *name; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (sym->st_name) 12462306a36Sopenharmony_ci name = sym_strtab + sym->st_name; 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci name = sec_name(sym->st_shndx); 12762306a36Sopenharmony_ci return name; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#if BYTE_ORDER == LITTLE_ENDIAN 13162306a36Sopenharmony_ci#define le16_to_cpu(val) (val) 13262306a36Sopenharmony_ci#define le32_to_cpu(val) (val) 13362306a36Sopenharmony_ci#define le64_to_cpu(val) (val) 13462306a36Sopenharmony_ci#define be16_to_cpu(val) bswap_16(val) 13562306a36Sopenharmony_ci#define be32_to_cpu(val) bswap_32(val) 13662306a36Sopenharmony_ci#define be64_to_cpu(val) bswap_64(val) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define cpu_to_le16(val) (val) 13962306a36Sopenharmony_ci#define cpu_to_le32(val) (val) 14062306a36Sopenharmony_ci#define cpu_to_le64(val) (val) 14162306a36Sopenharmony_ci#define cpu_to_be16(val) bswap_16(val) 14262306a36Sopenharmony_ci#define cpu_to_be32(val) bswap_32(val) 14362306a36Sopenharmony_ci#define cpu_to_be64(val) bswap_64(val) 14462306a36Sopenharmony_ci#endif 14562306a36Sopenharmony_ci#if BYTE_ORDER == BIG_ENDIAN 14662306a36Sopenharmony_ci#define le16_to_cpu(val) bswap_16(val) 14762306a36Sopenharmony_ci#define le32_to_cpu(val) bswap_32(val) 14862306a36Sopenharmony_ci#define le64_to_cpu(val) bswap_64(val) 14962306a36Sopenharmony_ci#define be16_to_cpu(val) (val) 15062306a36Sopenharmony_ci#define be32_to_cpu(val) (val) 15162306a36Sopenharmony_ci#define be64_to_cpu(val) (val) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define cpu_to_le16(val) bswap_16(val) 15462306a36Sopenharmony_ci#define cpu_to_le32(val) bswap_32(val) 15562306a36Sopenharmony_ci#define cpu_to_le64(val) bswap_64(val) 15662306a36Sopenharmony_ci#define cpu_to_be16(val) (val) 15762306a36Sopenharmony_ci#define cpu_to_be32(val) (val) 15862306a36Sopenharmony_ci#define cpu_to_be64(val) (val) 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic uint16_t elf16_to_cpu(uint16_t val) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) 16462306a36Sopenharmony_ci return le16_to_cpu(val); 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci return be16_to_cpu(val); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic uint32_t elf32_to_cpu(uint32_t val) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) 17262306a36Sopenharmony_ci return le32_to_cpu(val); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci return be32_to_cpu(val); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic uint32_t cpu_to_elf32(uint32_t val) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) 18062306a36Sopenharmony_ci return cpu_to_le32(val); 18162306a36Sopenharmony_ci else 18262306a36Sopenharmony_ci return cpu_to_be32(val); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#define elf_half_to_cpu(x) elf16_to_cpu(x) 18662306a36Sopenharmony_ci#define elf_word_to_cpu(x) elf32_to_cpu(x) 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#if ELF_BITS == 64 18962306a36Sopenharmony_cistatic uint64_t elf64_to_cpu(uint64_t val) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) 19262306a36Sopenharmony_ci return le64_to_cpu(val); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci return be64_to_cpu(val); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci#define elf_addr_to_cpu(x) elf64_to_cpu(x) 19762306a36Sopenharmony_ci#define elf_off_to_cpu(x) elf64_to_cpu(x) 19862306a36Sopenharmony_ci#define elf_xword_to_cpu(x) elf64_to_cpu(x) 19962306a36Sopenharmony_ci#else 20062306a36Sopenharmony_ci#define elf_addr_to_cpu(x) elf32_to_cpu(x) 20162306a36Sopenharmony_ci#define elf_off_to_cpu(x) elf32_to_cpu(x) 20262306a36Sopenharmony_ci#define elf_xword_to_cpu(x) elf32_to_cpu(x) 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void read_ehdr(FILE *fp) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) 20862306a36Sopenharmony_ci die("Cannot read ELF header: %s\n", strerror(errno)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) 21162306a36Sopenharmony_ci die("No ELF magic\n"); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) 21462306a36Sopenharmony_ci die("Not a %d bit executable\n", ELF_BITS); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && 21762306a36Sopenharmony_ci (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) 21862306a36Sopenharmony_ci die("Unknown ELF Endianness\n"); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) 22162306a36Sopenharmony_ci die("Unknown ELF version\n"); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Convert the fields to native endian */ 22462306a36Sopenharmony_ci ehdr.e_type = elf_half_to_cpu(ehdr.e_type); 22562306a36Sopenharmony_ci ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine); 22662306a36Sopenharmony_ci ehdr.e_version = elf_word_to_cpu(ehdr.e_version); 22762306a36Sopenharmony_ci ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry); 22862306a36Sopenharmony_ci ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff); 22962306a36Sopenharmony_ci ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff); 23062306a36Sopenharmony_ci ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags); 23162306a36Sopenharmony_ci ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize); 23262306a36Sopenharmony_ci ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize); 23362306a36Sopenharmony_ci ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum); 23462306a36Sopenharmony_ci ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize); 23562306a36Sopenharmony_ci ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum); 23662306a36Sopenharmony_ci ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) 23962306a36Sopenharmony_ci die("Unsupported ELF header type\n"); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (ehdr.e_machine != ELF_MACHINE) 24262306a36Sopenharmony_ci die("Not for %s\n", ELF_MACHINE_NAME); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (ehdr.e_version != EV_CURRENT) 24562306a36Sopenharmony_ci die("Unknown ELF version\n"); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) 24862306a36Sopenharmony_ci die("Bad ELF header size\n"); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (ehdr.e_phentsize != sizeof(Elf_Phdr)) 25162306a36Sopenharmony_ci die("Bad program header entry\n"); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (ehdr.e_shentsize != sizeof(Elf_Shdr)) 25462306a36Sopenharmony_ci die("Bad section header entry\n"); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (ehdr.e_shstrndx >= ehdr.e_shnum) 25762306a36Sopenharmony_ci die("String table index out of bounds\n"); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void read_shdrs(FILE *fp) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci int i; 26362306a36Sopenharmony_ci Elf_Shdr shdr; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci secs = calloc(ehdr.e_shnum, sizeof(struct section)); 26662306a36Sopenharmony_ci if (!secs) 26762306a36Sopenharmony_ci die("Unable to allocate %d section headers\n", ehdr.e_shnum); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) 27062306a36Sopenharmony_ci die("Seek to %d failed: %s\n", ehdr.e_shoff, strerror(errno)); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 27362306a36Sopenharmony_ci struct section *sec = &secs[i]; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci sec->shdr_offset = ftell(fp); 27662306a36Sopenharmony_ci if (fread(&shdr, sizeof(shdr), 1, fp) != 1) 27762306a36Sopenharmony_ci die("Cannot read ELF section headers %d/%d: %s\n", 27862306a36Sopenharmony_ci i, ehdr.e_shnum, strerror(errno)); 27962306a36Sopenharmony_ci sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name); 28062306a36Sopenharmony_ci sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type); 28162306a36Sopenharmony_ci sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags); 28262306a36Sopenharmony_ci sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr); 28362306a36Sopenharmony_ci sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset); 28462306a36Sopenharmony_ci sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size); 28562306a36Sopenharmony_ci sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link); 28662306a36Sopenharmony_ci sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info); 28762306a36Sopenharmony_ci sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign); 28862306a36Sopenharmony_ci sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize); 28962306a36Sopenharmony_ci if (sec->shdr.sh_link < ehdr.e_shnum) 29062306a36Sopenharmony_ci sec->link = &secs[sec->shdr.sh_link]; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void read_strtabs(FILE *fp) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int i; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 29962306a36Sopenharmony_ci struct section *sec = &secs[i]; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (sec->shdr.sh_type != SHT_STRTAB) 30262306a36Sopenharmony_ci continue; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sec->strtab = malloc(sec->shdr.sh_size); 30562306a36Sopenharmony_ci if (!sec->strtab) 30662306a36Sopenharmony_ci die("malloc of %d bytes for strtab failed\n", 30762306a36Sopenharmony_ci sec->shdr.sh_size); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) 31062306a36Sopenharmony_ci die("Seek to %d failed: %s\n", 31162306a36Sopenharmony_ci sec->shdr.sh_offset, strerror(errno)); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) != 31462306a36Sopenharmony_ci sec->shdr.sh_size) 31562306a36Sopenharmony_ci die("Cannot read symbol table: %s\n", strerror(errno)); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void read_symtabs(FILE *fp) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int i, j; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 32462306a36Sopenharmony_ci struct section *sec = &secs[i]; 32562306a36Sopenharmony_ci if (sec->shdr.sh_type != SHT_SYMTAB) 32662306a36Sopenharmony_ci continue; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci sec->symtab = malloc(sec->shdr.sh_size); 32962306a36Sopenharmony_ci if (!sec->symtab) 33062306a36Sopenharmony_ci die("malloc of %d bytes for symtab failed\n", 33162306a36Sopenharmony_ci sec->shdr.sh_size); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) 33462306a36Sopenharmony_ci die("Seek to %d failed: %s\n", 33562306a36Sopenharmony_ci sec->shdr.sh_offset, strerror(errno)); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) != 33862306a36Sopenharmony_ci sec->shdr.sh_size) 33962306a36Sopenharmony_ci die("Cannot read symbol table: %s\n", strerror(errno)); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 34262306a36Sopenharmony_ci Elf_Sym *sym = &sec->symtab[j]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci sym->st_name = elf_word_to_cpu(sym->st_name); 34562306a36Sopenharmony_ci sym->st_value = elf_addr_to_cpu(sym->st_value); 34662306a36Sopenharmony_ci sym->st_size = elf_xword_to_cpu(sym->st_size); 34762306a36Sopenharmony_ci sym->st_shndx = elf_half_to_cpu(sym->st_shndx); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void read_relocs(FILE *fp) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci static unsigned long base; 35562306a36Sopenharmony_ci int i, j; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!base) { 35862306a36Sopenharmony_ci struct section *sec = sec_lookup(".text"); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!sec) 36162306a36Sopenharmony_ci die("Could not find .text section\n"); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci base = sec->shdr.sh_addr; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 36762306a36Sopenharmony_ci struct section *sec = &secs[i]; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (sec->shdr.sh_type != SHT_REL_TYPE) 37062306a36Sopenharmony_ci continue; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci sec->reltab = malloc(sec->shdr.sh_size); 37362306a36Sopenharmony_ci if (!sec->reltab) 37462306a36Sopenharmony_ci die("malloc of %d bytes for relocs failed\n", 37562306a36Sopenharmony_ci sec->shdr.sh_size); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) 37862306a36Sopenharmony_ci die("Seek to %d failed: %s\n", 37962306a36Sopenharmony_ci sec->shdr.sh_offset, strerror(errno)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) != 38262306a36Sopenharmony_ci sec->shdr.sh_size) 38362306a36Sopenharmony_ci die("Cannot read symbol table: %s\n", strerror(errno)); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 38662306a36Sopenharmony_ci Elf_Rel *rel = &sec->reltab[j]; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci rel->r_offset = elf_addr_to_cpu(rel->r_offset); 38962306a36Sopenharmony_ci /* Set offset into kernel image */ 39062306a36Sopenharmony_ci rel->r_offset -= base; 39162306a36Sopenharmony_ci#if (ELF_BITS == 32) 39262306a36Sopenharmony_ci rel->r_info = elf_xword_to_cpu(rel->r_info); 39362306a36Sopenharmony_ci#else 39462306a36Sopenharmony_ci /* Convert MIPS64 RELA format - only the symbol 39562306a36Sopenharmony_ci * index needs converting to native endianness 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci rel->r_info = rel->r_info; 39862306a36Sopenharmony_ci ELF_R_SYM(rel->r_info) = elf32_to_cpu(ELF_R_SYM(rel->r_info)); 39962306a36Sopenharmony_ci#endif 40062306a36Sopenharmony_ci#if (SHT_REL_TYPE == SHT_RELA) 40162306a36Sopenharmony_ci rel->r_addend = elf_xword_to_cpu(rel->r_addend); 40262306a36Sopenharmony_ci#endif 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void remove_relocs(FILE *fp) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci int i; 41062306a36Sopenharmony_ci Elf_Shdr shdr; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 41362306a36Sopenharmony_ci struct section *sec = &secs[i]; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (sec->shdr.sh_type != SHT_REL_TYPE) 41662306a36Sopenharmony_ci continue; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (fseek(fp, sec->shdr_offset, SEEK_SET) < 0) 41962306a36Sopenharmony_ci die("Seek to %d failed: %s\n", 42062306a36Sopenharmony_ci sec->shdr_offset, strerror(errno)); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (fread(&shdr, sizeof(shdr), 1, fp) != 1) 42362306a36Sopenharmony_ci die("Cannot read ELF section headers %d/%d: %s\n", 42462306a36Sopenharmony_ci i, ehdr.e_shnum, strerror(errno)); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Set relocation section size to 0, effectively removing it. 42762306a36Sopenharmony_ci * This is necessary due to lack of support for relocations 42862306a36Sopenharmony_ci * in objcopy when creating 32bit elf from 64bit elf. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci shdr.sh_size = 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (fseek(fp, sec->shdr_offset, SEEK_SET) < 0) 43362306a36Sopenharmony_ci die("Seek to %d failed: %s\n", 43462306a36Sopenharmony_ci sec->shdr_offset, strerror(errno)); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (fwrite(&shdr, sizeof(shdr), 1, fp) != 1) 43762306a36Sopenharmony_ci die("Cannot write ELF section headers %d/%d: %s\n", 43862306a36Sopenharmony_ci i, ehdr.e_shnum, strerror(errno)); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void add_reloc(struct relocs *r, uint32_t offset, unsigned type) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci /* Relocation representation in binary table: 44562306a36Sopenharmony_ci * |76543210|76543210|76543210|76543210| 44662306a36Sopenharmony_ci * | Type | offset from _text >> 2 | 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci offset >>= 2; 44962306a36Sopenharmony_ci if (offset > 0x00FFFFFF) 45062306a36Sopenharmony_ci die("Kernel image exceeds maximum size for relocation!\n"); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci offset = (offset & 0x00FFFFFF) | ((type & 0xFF) << 24); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (r->count == r->size) { 45562306a36Sopenharmony_ci unsigned long newsize = r->size + 50000; 45662306a36Sopenharmony_ci void *mem = realloc(r->offset, newsize * sizeof(r->offset[0])); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!mem) 45962306a36Sopenharmony_ci die("realloc failed\n"); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci r->offset = mem; 46262306a36Sopenharmony_ci r->size = newsize; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci r->offset[r->count++] = offset; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel, 46862306a36Sopenharmony_ci Elf_Sym *sym, const char *symname)) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci int i; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Walk through the relocations */ 47362306a36Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 47462306a36Sopenharmony_ci char *sym_strtab; 47562306a36Sopenharmony_ci Elf_Sym *sh_symtab; 47662306a36Sopenharmony_ci struct section *sec_applies, *sec_symtab; 47762306a36Sopenharmony_ci int j; 47862306a36Sopenharmony_ci struct section *sec = &secs[i]; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (sec->shdr.sh_type != SHT_REL_TYPE) 48162306a36Sopenharmony_ci continue; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci sec_symtab = sec->link; 48462306a36Sopenharmony_ci sec_applies = &secs[sec->shdr.sh_info]; 48562306a36Sopenharmony_ci if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) 48662306a36Sopenharmony_ci continue; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci sh_symtab = sec_symtab->symtab; 48962306a36Sopenharmony_ci sym_strtab = sec_symtab->link->strtab; 49062306a36Sopenharmony_ci for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 49162306a36Sopenharmony_ci Elf_Rel *rel = &sec->reltab[j]; 49262306a36Sopenharmony_ci Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 49362306a36Sopenharmony_ci const char *symname = sym_name(sym_strtab, sym); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci process(sec, rel, sym, symname); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 50162306a36Sopenharmony_ci const char *symname) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci unsigned r_type = ELF_R_TYPE(rel->r_info); 50462306a36Sopenharmony_ci unsigned bind = ELF_ST_BIND(sym->st_info); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if ((bind == STB_WEAK) && (sym->st_value == 0)) { 50762306a36Sopenharmony_ci /* Don't relocate weak symbols without a target */ 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (regex_skip_reloc(symname)) 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci switch (r_type) { 51562306a36Sopenharmony_ci case R_MIPS_NONE: 51662306a36Sopenharmony_ci case R_MIPS_REL32: 51762306a36Sopenharmony_ci case R_MIPS_PC16: 51862306a36Sopenharmony_ci case R_MIPS_PC21_S2: 51962306a36Sopenharmony_ci case R_MIPS_PC26_S2: 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * NONE can be ignored and PC relative relocations don't 52262306a36Sopenharmony_ci * need to be adjusted. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci case R_MIPS_HIGHEST: 52562306a36Sopenharmony_ci case R_MIPS_HIGHER: 52662306a36Sopenharmony_ci /* We support relocating within the same 4Gb segment only, 52762306a36Sopenharmony_ci * thus leaving the top 32bits unchanged 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci case R_MIPS_LO16: 53062306a36Sopenharmony_ci /* We support relocating by 64k jumps only 53162306a36Sopenharmony_ci * thus leaving the bottom 16bits unchanged 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci case R_MIPS_64: 53662306a36Sopenharmony_ci case R_MIPS_32: 53762306a36Sopenharmony_ci case R_MIPS_26: 53862306a36Sopenharmony_ci case R_MIPS_HI16: 53962306a36Sopenharmony_ci add_reloc(&relocs, rel->r_offset, r_type); 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci default: 54362306a36Sopenharmony_ci die("Unsupported relocation type: %s (%d)\n", 54462306a36Sopenharmony_ci rel_type(r_type), r_type); 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int write_reloc_as_bin(uint32_t v, FILE *f) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci unsigned char buf[4]; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci v = cpu_to_elf32(v); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci memcpy(buf, &v, sizeof(uint32_t)); 55862306a36Sopenharmony_ci return fwrite(buf, 1, 4, f); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int write_reloc_as_text(uint32_t v, FILE *f) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int res; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci res = fprintf(f, "\t.long 0x%08"PRIx32"\n", v); 56662306a36Sopenharmony_ci if (res < 0) 56762306a36Sopenharmony_ci return res; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci return sizeof(uint32_t); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void emit_relocs(int as_text, int as_bin, FILE *outf) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci int i; 57562306a36Sopenharmony_ci int (*write_reloc)(uint32_t, FILE *) = write_reloc_as_bin; 57662306a36Sopenharmony_ci int size = 0; 57762306a36Sopenharmony_ci int size_reserved; 57862306a36Sopenharmony_ci struct section *sec_reloc; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci sec_reloc = sec_lookup(".data.reloc"); 58162306a36Sopenharmony_ci if (!sec_reloc) 58262306a36Sopenharmony_ci die("Could not find relocation section\n"); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci size_reserved = sec_reloc->shdr.sh_size; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Collect up the relocations */ 58762306a36Sopenharmony_ci walk_relocs(do_reloc); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Print the relocations */ 59062306a36Sopenharmony_ci if (as_text) { 59162306a36Sopenharmony_ci /* Print the relocations in a form suitable that 59262306a36Sopenharmony_ci * gas will like. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci printf(".section \".data.reloc\",\"a\"\n"); 59562306a36Sopenharmony_ci printf(".balign 4\n"); 59662306a36Sopenharmony_ci /* Output text to stdout */ 59762306a36Sopenharmony_ci write_reloc = write_reloc_as_text; 59862306a36Sopenharmony_ci outf = stdout; 59962306a36Sopenharmony_ci } else if (as_bin) { 60062306a36Sopenharmony_ci /* Output raw binary to stdout */ 60162306a36Sopenharmony_ci outf = stdout; 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci /* Seek to offset of the relocation section. 60462306a36Sopenharmony_ci * Each relocation is then written into the 60562306a36Sopenharmony_ci * vmlinux kernel image. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci if (fseek(outf, sec_reloc->shdr.sh_offset, SEEK_SET) < 0) { 60862306a36Sopenharmony_ci die("Seek to %d failed: %s\n", 60962306a36Sopenharmony_ci sec_reloc->shdr.sh_offset, strerror(errno)); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci for (i = 0; i < relocs.count; i++) 61462306a36Sopenharmony_ci size += write_reloc(relocs.offset[i], outf); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Print a stop, but only if we've actually written some relocs */ 61762306a36Sopenharmony_ci if (size) 61862306a36Sopenharmony_ci size += write_reloc(0, outf); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (size > size_reserved) 62162306a36Sopenharmony_ci /* Die, but suggest a value for CONFIG_RELOCATION_TABLE_SIZE 62262306a36Sopenharmony_ci * which will fix this problem and allow a bit of headroom 62362306a36Sopenharmony_ci * if more kernel features are enabled 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci die("Relocations overflow available space!\n" \ 62662306a36Sopenharmony_ci "Please adjust CONFIG_RELOCATION_TABLE_SIZE " \ 62762306a36Sopenharmony_ci "to at least 0x%08x\n", (size + 0x1000) & ~0xFFF); 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci/* 63162306a36Sopenharmony_ci * As an aid to debugging problems with different linkers 63262306a36Sopenharmony_ci * print summary information about the relocs. 63362306a36Sopenharmony_ci * Since different linkers tend to emit the sections in 63462306a36Sopenharmony_ci * different orders we use the section names in the output. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_cistatic int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, 63762306a36Sopenharmony_ci const char *symname) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci printf("%16s 0x%08x %16s %40s %16s\n", 64062306a36Sopenharmony_ci sec_name(sec->shdr.sh_info), 64162306a36Sopenharmony_ci (unsigned int)rel->r_offset, 64262306a36Sopenharmony_ci rel_type(ELF_R_TYPE(rel->r_info)), 64362306a36Sopenharmony_ci symname, 64462306a36Sopenharmony_ci sec_name(sym->st_shndx)); 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void print_reloc_info(void) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci printf("%16s %10s %16s %40s %16s\n", 65162306a36Sopenharmony_ci "reloc section", 65262306a36Sopenharmony_ci "offset", 65362306a36Sopenharmony_ci "reloc type", 65462306a36Sopenharmony_ci "symbol", 65562306a36Sopenharmony_ci "symbol section"); 65662306a36Sopenharmony_ci walk_relocs(do_reloc_info); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci#if ELF_BITS == 64 66062306a36Sopenharmony_ci# define process process_64 66162306a36Sopenharmony_ci#else 66262306a36Sopenharmony_ci# define process process_32 66362306a36Sopenharmony_ci#endif 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_civoid process(FILE *fp, int as_text, int as_bin, 66662306a36Sopenharmony_ci int show_reloc_info, int keep_relocs) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci regex_init(); 66962306a36Sopenharmony_ci read_ehdr(fp); 67062306a36Sopenharmony_ci read_shdrs(fp); 67162306a36Sopenharmony_ci read_strtabs(fp); 67262306a36Sopenharmony_ci read_symtabs(fp); 67362306a36Sopenharmony_ci read_relocs(fp); 67462306a36Sopenharmony_ci if (show_reloc_info) { 67562306a36Sopenharmony_ci print_reloc_info(); 67662306a36Sopenharmony_ci return; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci emit_relocs(as_text, as_bin, fp); 67962306a36Sopenharmony_ci if (!keep_relocs) 68062306a36Sopenharmony_ci remove_relocs(fp); 68162306a36Sopenharmony_ci} 682