/* we have to relocate these symbols, although they are absolute */
#define LNKSYMCNT 5
static const char *linkersyms[LNKSYMCNT] = {
"_edata", "__bss_start", "end", "_end", "__end"
};
static int relocate(uint8_t *, uint32_t);
static int fixreloclist(uint8_t *, struct elf32_rela *, int relacnt,
struct elf32_sym *, char *, uint32_t, uint32_t);
static int islnksym(char *, struct elf32_sym *);
int main(int argc, char *argv[])
{
void *buf;
FILE *f;
long len;
int err;
/* relocate program entry and vaddr of first segment */
phdr = (struct elf32_phdr *)(kern + hdr->e_phoff);
origaddr = phdr[0].p_vaddr;
phdr[0].p_vaddr = phdr[0].p_paddr = relocaddr;
hdr->e_entry = hdr->e_entry - origaddr + relocaddr;
/* locate symbol table and string table */
shdr = (struct elf32_shdr *)(kern + hdr->e_shoff);
for (i = 0, sym = -1; i < hdr->e_shnum; i++)
if (shdr[i].sh_type == SHT_SYMTAB) {
sym = i;
str = shdr[i].sh_link;
strtab = kern + shdr[str].sh_offset;
break;
}
if (sym == -1) {
fprintf(stderr, "Missing symbol table!\n");
return 0;
}
if (shdr[sym].sh_size % shdr[sym].sh_entsize != 0) {
fprintf(stderr, "Corrupted symbol table!\n");
return 0;
}
symcnt = shdr[sym].sh_size / shdr[sym].sh_entsize;
/*
* Relocate all symbols. Take care of some linker symbols,
* which also have to be relocated.
*/
symbols = (struct elf32_sym *)(kern + shdr[sym].sh_offset);
for (i = 0; i < symcnt; i++) {
if (symbols[i].st_shndx == SHN_UNDEF ||
symbols[i].st_shndx >= SHN_ABS)
if (!islnksym(strtab, &symbols[i]))
continue;
symbols[i].st_value =
symbols[i].st_value - origaddr + relocaddr;
}
/* fix all relocation entries and the VA of allocated sections */
for (i = 0; i < hdr->e_shnum; i++) {
if ((shdr[i].sh_type == SHT_PROGBITS ||
shdr[i].sh_type == SHT_NOBITS ||
shdr[i].sh_type == SHT_NOTE) &&
(shdr[i].sh_flags & SHF_ALLOC) != 0)
shdr[i].sh_addr =
shdr[i].sh_addr - origaddr + relocaddr;
/* fix all relocation of a section */
static int fixreloclist(uint8_t *text, struct elf32_rela *rela, int relacnt,
struct elf32_sym *syms, char *strtab, uint32_t origaddr, uint32_t relocaddr)
{
uint32_t o, v;
int i, j;
for (i = 0; i < relacnt; i++) {
j = ELF32_R_SYM(rela[i].r_info);
if (syms[j].st_shndx == SHN_UNDEF ||
syms[j].st_shndx >= SHN_ABS)
if (!islnksym(strtab, &syms[j]))
continue;
v = syms[j].st_value + rela[i].r_addend;
o = rela[i].r_offset - origaddr;
rela[i].r_offset = relocaddr + o;
switch (ELF32_R_TYPE(rela[i].r_info)) {
case R_PPC_ADDR32:
*(uint32_t *)(text + o) = v;
break;
case R_PPC_ADDR16_HI:
*(uint16_t *)(text + o) = v >> 16;
break;
case R_PPC_ADDR16_HA:
*(uint16_t *)(text + o) =
(v >> 16) + (v & 0x8000 ? 1 : 0);
break;
case R_PPC_ADDR16_LO:
*(uint16_t *)(text + o) = v & 0xffff;
break;
case R_NONE:
case R_PPC_REL32:
case R_PPC_REL24:
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
break;
default:
fprintf(stderr, "Unknown relocation type!\n");
return 0;
}
}
return 1;
}
/*
* Check whether this is a linker symbol, which needs to be relocated
* despite being absolute.
* XXX Did we get all of them? What about new ones?
*/
static int islnksym(char *str, struct elf32_sym *sym)
{
int i;
char *name;
name = str + sym->st_name;
if (sym->st_shndx == SHN_ABS) {
if (!strncmp(name, "__start_link_set", 16) ||
!strncmp(name, "__stop_link_set", 15))
return 1;
for (i = 0; i < LNKSYMCNT; i++) {
if (!strcmp(name, linkersyms[i]))
return 1;
}
}
return 0;
}