litex: reorganize things, first work working version
[litex.git] / litex / soc / software / libdyld / dyld.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <dyld.h>
5
6 static int fixup_rela(struct dyld_info *info, const Elf32_Rela *rela,
7 Elf32_Addr (*resolve_import)(const char *),
8 const char **error_out)
9 {
10 const Elf32_Sym *sym = NULL;
11 if(ELF32_R_SYM(rela->r_info) != 0)
12 sym = &info->symtab[ELF32_R_SYM(rela->r_info)];
13 Elf32_Addr value;
14
15 switch(ELF32_R_TYPE(rela->r_info)) {
16 case R_OR1K_NONE:
17 return 1; // Does nothing.
18
19 case R_OR1K_RELATIVE:
20 value = info->base + rela->r_addend;
21 break;
22
23 case R_OR1K_32:
24 case R_OR1K_GLOB_DAT:
25 case R_OR1K_JMP_SLOT:
26 value = (Elf32_Addr)dyld_lookup(&info->strtab[sym->st_name], info);
27 if(value != 0)
28 break;
29
30 value = resolve_import(&info->strtab[sym->st_name]);
31 if(value == 0) {
32 static char error[256];
33 snprintf(error, sizeof(error),
34 "ELF object has an unresolved symbol: %s",
35 &info->strtab[sym->st_name]);
36 *error_out = error;
37 return 0;
38 }
39 break;
40
41 default:
42 *error_out = "ELF object uses an unsupported relocation type";
43 return 0;
44 }
45
46 memcpy((Elf32_Addr*)(info->base + rela->r_offset), &value,
47 sizeof(Elf32_Addr));
48
49 return 1;
50 }
51
52 int dyld_load(const void *shlib, Elf32_Addr base,
53 Elf32_Addr (*resolve_import)(const char *),
54 struct dyld_info *info, const char **error_out)
55 {
56 const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)shlib;
57
58 const unsigned char expected_ident[EI_NIDENT] = {
59 ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
60 ELFCLASS32, ELFDATA2MSB, EV_CURRENT,
61 ELFOSABI_NONE, /* ABI version */ 0
62 };
63 if(memcmp(ehdr->e_ident, expected_ident, EI_NIDENT) ||
64 ehdr->e_type != ET_DYN) {
65 *error_out = "ELF object is not a shared library";
66 return 0;
67 }
68
69 #ifdef __or1k__
70 if(ehdr->e_machine != EM_OPENRISC) {
71 *error_out = "ELF object does not contain OpenRISC machine code";
72 return 0;
73 }
74 #else
75 #error Unsupported architecture
76 #endif
77
78 const Elf32_Phdr *phdr = (const Elf32_Phdr *)((intptr_t)shlib + ehdr->e_phoff);
79 const Elf32_Dyn *dyn = NULL;
80 for(int i = 0; i < ehdr->e_phnum; i++) {
81 if(phdr[i].p_type == PT_DYNAMIC)
82 dyn = (const Elf32_Dyn *)((intptr_t)shlib + phdr[i].p_offset);
83
84 memcpy((void*)(base + phdr[i].p_vaddr),
85 (const void*)((intptr_t)shlib + phdr[i].p_offset),
86 phdr[i].p_filesz);
87 }
88
89 if(dyn == NULL) {
90 *error_out = "ELF object does not have a PT_DYNAMIC header";
91 return 0;
92 }
93
94 const char *strtab = NULL;
95 const Elf32_Sym *symtab = NULL;
96 const Elf32_Rela *rela = NULL, *pltrel = NULL;
97 const Elf32_Word *hash = NULL;
98 Elf32_Word init = 0;
99 size_t syment = sizeof(Elf32_Sym), relaent = sizeof(Elf32_Rela),
100 relanum = 0, pltrelnum = 0;
101 while(dyn->d_tag != DT_NULL) {
102 switch(dyn->d_tag) {
103 case DT_STRTAB: strtab = (const char *)(base + dyn->d_un.d_ptr); break;
104 case DT_SYMTAB: symtab = (const Elf32_Sym *)(base + dyn->d_un.d_ptr); break;
105 case DT_SYMENT: syment = dyn->d_un.d_val; break;
106 case DT_RELA: rela = (const Elf32_Rela *)(base + dyn->d_un.d_ptr); break;
107 case DT_RELAENT: relaent = dyn->d_un.d_val; break;
108 case DT_RELASZ: relanum = dyn->d_un.d_val / sizeof(Elf32_Rela); break;
109 case DT_JMPREL: pltrel = (const Elf32_Rela *)(base + dyn->d_un.d_ptr); break;
110 case DT_PLTRELSZ: pltrelnum = dyn->d_un.d_val / sizeof(Elf32_Rela); break;
111 case DT_HASH: hash = (const Elf32_Word *)(base + dyn->d_un.d_ptr); break;
112 case DT_INIT: init = dyn->d_un.d_val; break;
113
114 case DT_REL:
115 *error_out = "ELF object uses Rel relocations, which are not supported";
116 return 0;
117 }
118
119 ++dyn;
120 }
121
122 if(symtab == NULL || syment == 0 || strtab == NULL) {
123 *error_out = "ELF object must contain a symbol table";
124 return 0;
125 }
126
127 if(syment != sizeof(Elf32_Sym) || relaent != sizeof(Elf32_Rela)) {
128 *error_out = "ELF object uses an unknown format for symbols and relocations";
129 return 0;
130 }
131
132 info->base = base;
133 info->init = (void*)(base + init);
134 info->strtab = strtab;
135 info->symtab = symtab;
136 info->hash.nbucket = hash[0];
137 info->hash.nchain = hash[1];
138 info->hash.bucket = &hash[2];
139 info->hash.chain = &hash[2 + info->hash.nbucket];
140
141 for(int i = 0; i < relanum; i++) {
142 if(!fixup_rela(info, &rela[i], resolve_import, error_out))
143 return 0;
144 }
145
146 for(int i = 0; i < pltrelnum; i++) {
147 if(!fixup_rela(info, &pltrel[i], resolve_import, error_out))
148 return 0;
149 }
150
151 return 1;
152 }
153
154 static unsigned long elf_hash(const unsigned char *name)
155 {
156 unsigned long h = 0, g;
157 while(*name) {
158 h = (h << 4) + *name++;
159 if((g = h & 0xf0000000)) {
160 h ^= g >> 24;
161 h &= ~g;
162 }
163 }
164 return h;
165 }
166
167 void *dyld_lookup(const char *symbol, struct dyld_info *info)
168 {
169 unsigned hash = elf_hash((const unsigned char*) symbol);
170 unsigned index = info->hash.bucket[hash % info->hash.nbucket];
171 while(strcmp(&info->strtab[info->symtab[index].st_name], symbol)) {
172 if(index == STN_UNDEF)
173 return NULL;
174 index = info->hash.chain[index];
175 }
176
177 Elf32_Addr value = info->symtab[index].st_value;
178 if(value != 0)
179 return (void*)(info->base + value);
180 else
181 return NULL;
182 }