handle R_X86_64_PC32 relocation
[ld.git] / ld.c
blobe0143dcfd64af6732963ffe978b838accd628a54
1 #include <elf.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
8 #define MAXSECS (1 << 10)
9 #define MAXOBJS (1 << 7)
10 #define PAGESIZE (1 << 12)
12 #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
13 #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
15 struct obj {
16 char *mem;
17 Elf64_Ehdr *ehdr;
18 Elf64_Shdr *shdr;
19 Elf64_Sym *syms;
20 int nsyms;
21 char *symstr;
22 char *shstr;
25 struct secmap {
26 Elf64_Shdr *o_shdr;
27 Elf64_Phdr *phdr;
28 struct obj *obj;
31 struct outelf {
32 Elf64_Ehdr ehdr;
33 Elf64_Phdr phdr[MAXSECS];
34 int nph;
35 struct secmap secs[MAXSECS];
36 int nsecs;
37 struct obj objs[MAXOBJS];
38 int nobjs;
39 unsigned long faddr;
40 unsigned long vaddr;
43 static int obj_find(struct obj *obj, char *name, int *s_idx, int *s_off)
45 int i;
46 for (i = 0; i < obj->nsyms; i++) {
47 Elf64_Sym *sym = &obj->syms[i];
48 if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL ||
49 sym->st_shndx == SHN_UNDEF)
50 continue;
51 if (!strcmp(name, obj->symstr + sym->st_name)) {
52 *s_idx = sym->st_shndx;
53 *s_off = sym->st_value;
54 return 0;
57 return 1;
60 static void obj_init(struct obj *obj, char *mem)
62 int i;
63 obj->mem = mem;
64 obj->ehdr = (void *) mem;
65 obj->shdr = (void *) (mem + obj->ehdr->e_shoff);
66 obj->shstr = mem + obj->shdr[obj->ehdr->e_shstrndx].sh_offset;
67 for (i = 0; i < obj->ehdr->e_shnum; i++) {
68 if (obj->shdr[i].sh_type != SHT_SYMTAB)
69 continue;
70 obj->symstr = mem + obj->shdr[obj->shdr[i].sh_link].sh_offset;
71 obj->syms = (void *) (mem + obj->shdr[i].sh_offset);
72 obj->nsyms = obj->shdr[i].sh_size / sizeof(*obj->syms);
76 static void outelf_init(struct outelf *oe)
78 memset(oe, 0, sizeof(*oe));
79 oe->ehdr.e_ident[0] = 0x7f;
80 oe->ehdr.e_ident[1] = 'E';
81 oe->ehdr.e_ident[2] = 'L';
82 oe->ehdr.e_ident[3] = 'F';
83 oe->ehdr.e_ident[4] = ELFCLASS64;
84 oe->ehdr.e_ident[5] = ELFDATA2LSB;
85 oe->ehdr.e_ident[6] = EV_CURRENT;
86 oe->ehdr.e_ident[7] = ELFOSABI_LINUX;
87 oe->ehdr.e_type = ET_EXEC;
88 oe->ehdr.e_machine = EM_X86_64;
89 oe->ehdr.e_version = EV_CURRENT;
90 oe->ehdr.e_shstrndx = SHN_UNDEF;
91 oe->ehdr.e_ehsize = sizeof(oe->ehdr);
92 oe->faddr = sizeof(oe->ehdr);
93 oe->ehdr.e_phentsize = sizeof(oe->phdr[0]);
94 oe->ehdr.e_shentsize = sizeof(Elf64_Shdr);
95 oe->vaddr = 0x800000l + oe->faddr;
98 static struct secmap *outelf_mapping(struct outelf *oe, Elf64_Shdr *shdr)
100 int i;
101 for (i = 0; i < oe->nsecs; i++)
102 if (oe->secs[i].o_shdr == shdr)
103 return &oe->secs[i];
104 return NULL;
107 static unsigned long outelf_find(struct outelf *oe, char *name)
109 int s_idx, s_off;
110 int i;
111 for (i = 0; i < oe->nobjs; i++) {
112 struct obj *obj = &oe->objs[i];
113 if (!obj_find(obj, name, &s_idx, &s_off)) {
114 struct secmap *sec;
115 if ((sec = outelf_mapping(oe, &obj->shdr[s_idx])))
116 return sec->phdr->p_vaddr + s_off;
119 return 0;
122 static unsigned long symval(struct outelf *oe, struct obj *obj, Elf64_Sym *sym)
124 struct secmap *sec;
125 char *name = obj->symstr + sym->st_name;
126 switch (ELF64_ST_TYPE(sym->st_info)) {
127 case STT_SECTION:
128 if ((sec = outelf_mapping(oe, &obj->shdr[sym->st_shndx])))
129 return sec->phdr->p_vaddr;
130 break;
131 case STT_NOTYPE:
132 case STT_OBJECT:
133 case STT_FUNC:
134 if (name && *name)
135 return outelf_find(oe, name);
136 break;
138 return 0;
141 static void outelf_reloc_sec(struct outelf *oe, int o_idx, int s_idx)
143 struct obj *obj = &oe->objs[o_idx];
144 Elf64_Shdr *rel_shdr = &obj->shdr[s_idx];
145 Elf64_Rela *rel = (void *) obj->mem + obj->shdr[s_idx].sh_offset;
146 Elf64_Shdr *other_shdr = &obj->shdr[rel_shdr->sh_info];
147 void *other = (void *) obj->mem + other_shdr->sh_offset;
148 int nrel = rel_shdr->sh_size / sizeof(*rel);
149 unsigned long addr;
150 int i;
151 for (i = 0; i < nrel; i++) {
152 int sym_idx = ELF64_R_SYM(rel[i].r_info);
153 Elf64_Sym *sym = &obj->syms[sym_idx];
154 unsigned long val = symval(oe, obj, sym);
155 char *name = obj->symstr + sym->st_name;
156 unsigned long *dst = other + rel[i].r_offset;
157 switch (ELF64_R_TYPE(rel[i].r_info)) {
158 case R_X86_64_32:
159 *(unsigned int *) dst = val + rel[i].r_addend;
160 break;
161 case R_X86_64_64:
162 *dst = val + rel[i].r_addend;
163 break;
164 case R_X86_64_PC32:
165 addr = outelf_mapping(oe, other_shdr)->phdr->p_vaddr +
166 rel[i].r_offset;
167 *(unsigned int *) dst = val - addr + rel[i].r_addend - 4;
168 break;
173 static void outelf_reloc(struct outelf *oe)
175 int i, j;
176 for (i = 0; i < oe->nobjs; i++) {
177 struct obj *obj = &oe->objs[i];
178 for (j = 0; j < obj->ehdr->e_shnum; j++)
179 if (obj->shdr[j].sh_type == SHT_RELA)
180 outelf_reloc_sec(oe, i, j);
184 static void outelf_write(struct outelf *oe, int fd)
186 int i;
187 oe->ehdr.e_entry = outelf_find(oe, "_start");
189 oe->ehdr.e_phnum = oe->nph;
190 oe->ehdr.e_phoff = oe->faddr;
191 oe->ehdr.e_shnum = 0;
192 oe->ehdr.e_shoff = 0;
193 write(fd, &oe->ehdr, sizeof(oe->ehdr));
194 for (i = 0; i < oe->nsecs; i++) {
195 struct secmap *sec = &oe->secs[i];
196 char *buf = sec->obj->mem + sec->o_shdr->sh_offset;
197 int len = sec->o_shdr->sh_size;
198 lseek(fd, sec->phdr->p_offset, SEEK_SET);
199 write(fd, buf, len);
201 lseek(fd, oe->faddr, SEEK_SET);
202 write(fd, &oe->phdr, oe->nph * sizeof(oe->phdr[0]));
205 static void outelf_link(struct outelf *oe, char *mem)
207 Elf64_Ehdr *ehdr = (void *) mem;
208 Elf64_Shdr *shdr = (void *) (mem + ehdr->e_shoff);
209 struct obj *obj = &oe->objs[oe->nobjs++];
210 int i;
211 if (ehdr->e_type != ET_REL)
212 return;
213 obj_init(obj, mem);
214 for (i = 0; i < ehdr->e_shnum; i++) {
215 struct secmap *sec;
216 if (!(shdr[i].sh_flags & 0x7))
217 continue;
218 sec = &oe->secs[oe->nsecs++];
219 sec->o_shdr = &shdr[i];
220 sec->phdr = &oe->phdr[oe->nph++];
221 sec->obj = obj;
222 sec->phdr->p_type = PT_LOAD;
223 sec->phdr->p_flags = PF_R | PF_W | PF_X;
224 sec->phdr->p_vaddr = oe->vaddr;
225 sec->phdr->p_paddr = oe->vaddr;
226 sec->phdr->p_offset = oe->faddr;
227 sec->phdr->p_filesz = shdr[i].sh_size;
228 sec->phdr->p_memsz = shdr[i].sh_size;
229 sec->phdr->p_align = PAGESIZE;
230 oe->faddr += shdr[i].sh_size;
231 oe->vaddr += shdr[i].sh_size;
235 static void outelf_free(struct outelf *oe)
237 int i;
238 for (i = 0; i < oe->nobjs; i++)
239 free(oe->objs[i].mem);
242 static long filesize(int fd)
244 struct stat stat;
245 fstat(fd, &stat);
246 return stat.st_size;
249 static char *fileread(char *path)
251 int fd = open(path, O_RDONLY);
252 int size = filesize(fd);
253 char *buf = malloc(size);
254 read(fd, buf, size);
255 close(fd);
256 return buf;
259 static void die(char *msg)
261 write(1, msg, strlen(msg));
262 exit(1);
265 int main(int argc, char **argv)
267 char out[1 << 10] = "a.out";
268 char *buf;
269 struct outelf oe;
270 int fd;
271 int i = 0;
272 if (argc < 2)
273 die("no object given\n");
274 outelf_init(&oe);
276 while (++i < argc) {
277 if (!strcmp("-o", argv[i])) {
278 strcpy(out, argv[++i]);
279 continue;
281 buf = fileread(argv[i]);
282 if (!buf)
283 die("cannot open object\n");
284 outelf_link(&oe, buf);
286 fd = open(out, O_WRONLY | O_TRUNC | O_CREAT, 0700);
287 outelf_reloc(&oe);
288 outelf_write(&oe, fd);
289 close(fd);
290 outelf_free(&oe);
291 return 0;