├── Makefile ├── nld.1 └── nld.c /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -Wall -O2 3 | LDFLAGS = 4 | 5 | all: nld 6 | .c.o: 7 | $(CC) -c $(CFLAGS) $< 8 | nld: nld.o 9 | $(CC) $(LDFLAGS) -o $@ $^ 10 | clean: 11 | rm -f nld *.o 12 | -------------------------------------------------------------------------------- /nld.1: -------------------------------------------------------------------------------- 1 | .TH NEATLD 1 2 | .SH NAME 3 | neatld \- the neatld ARM/X86(-64) static linker 4 | .SH SYNOPSIS 5 | .B neatld 6 | [ 7 | .I options 8 | ] 9 | .I objfile ... 10 | .br 11 | .SH DESCRIPTION 12 | neatld links the given object and archive files into an executable 13 | file. The architecture of the output is determined based on the 14 | architecture of input files. Options are: 15 | .TP 16 | .BI -o " out" 17 | Write the output to the given file instead of 18 | .I a.out. 19 | .TP 20 | .BI -l lib 21 | Link with library 22 | .IR lib . 23 | .TP 24 | .BI -L libdir 25 | Search 26 | .IR libdir 27 | for finding libraries specified with 28 | .B -l. 29 | .TP 30 | .BI -s 31 | Do not include a symbol table. 32 | .TP 33 | .BI -m X=vaddr:laddr 34 | Set the virtual/load address of a section. 35 | .I X 36 | defines the section and can be 37 | .I c 38 | for code section, 39 | .I d 40 | for data section and 41 | .I b 42 | for BSS section. 43 | .IR laddr 44 | can be omitted, in which case it would be the same as 45 | .IR vaddr . 46 | .TP 47 | .BI -p 48 | Page-align sections. 49 | .TP 50 | .BI -e sym 51 | Entry point symbol. 52 | .SH "SEE ALSO" 53 | .IR neatcc (1), 54 | .IR neatas (1) 55 | -------------------------------------------------------------------------------- /nld.c: -------------------------------------------------------------------------------- 1 | /* 2 | * NEATLD ARM/X86(-64) STATIC LINKER 3 | * 4 | * Copyright (C) 2010-2023 Ali Gholami Rudi 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define I_CS 0 28 | #define I_DS 1 29 | #define I_BSS 2 30 | 31 | static unsigned long sec_vaddr[3] = {0x800000}; /* virtual address of sections */ 32 | static unsigned long sec_laddr[3] = {0x800000}; /* load address of sections */ 33 | static int sec_set[3] = {1}; /* set address for section */ 34 | static int secalign = 16; /* section alignment */ 35 | static char *entry = "_start"; /* entry symbol */ 36 | static int e_machine; /* target machine */ 37 | static int e_flags; /* elf ehdr flags */ 38 | 39 | #define MAXSECS (1 << 10) 40 | #define MAXOBJS (1 << 7) 41 | #define MAXSYMS (1 << 12) 42 | #define PAGE_SIZE (1 << 12) 43 | #define PAGE_MASK (PAGE_SIZE - 1) 44 | #define MAXFILES (1 << 10) 45 | #define MAXPHDRS 4 46 | 47 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 48 | #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) 49 | 50 | /* simplified elf struct and macro names */ 51 | #ifdef __x86_64__ 52 | # define Elf_Phdr Elf64_Phdr 53 | # define Elf_Ehdr Elf64_Ehdr 54 | # define Elf_Shdr Elf64_Shdr 55 | # define Elf_Sym Elf64_Sym 56 | # define Elf_Rela Elf64_Rela 57 | # define SHT_REL_ SHT_RELA 58 | # define REL_ADDEND(r) ((r)->r_addend) 59 | # define ELF_ST_INFO ELF64_ST_INFO 60 | # define ELF_ST_BIND ELF64_ST_BIND 61 | # define ELF_R_SYM ELF64_R_SYM 62 | # define ELF_R_TYPE ELF64_R_TYPE 63 | # define ELF_ST_TYPE ELF64_ST_TYPE 64 | #else 65 | # define Elf_Phdr Elf32_Phdr 66 | # define Elf_Ehdr Elf32_Ehdr 67 | # define Elf_Shdr Elf32_Shdr 68 | # define Elf_Sym Elf32_Sym 69 | # define Elf_Rela Elf32_Rel 70 | # define SHT_REL_ SHT_REL 71 | # define REL_ADDEND(r) (0) 72 | # define ELF_ST_INFO ELF32_ST_INFO 73 | # define ELF_ST_BIND ELF32_ST_BIND 74 | # define ELF_R_SYM ELF32_R_SYM 75 | # define ELF_R_TYPE ELF32_R_TYPE 76 | # define ELF_ST_TYPE ELF32_ST_TYPE 77 | #endif 78 | 79 | struct obj { 80 | char *mem; 81 | Elf_Ehdr *ehdr; 82 | Elf_Shdr *shdr; 83 | Elf_Sym *syms; 84 | int nsyms; 85 | char *symstr; 86 | char *shstr; 87 | }; 88 | 89 | struct secmap { 90 | Elf_Shdr *o_shdr; 91 | struct obj *obj; 92 | unsigned long vaddr; 93 | unsigned long faddr; 94 | }; 95 | 96 | struct bss_sym { 97 | Elf_Sym *sym; 98 | int off; 99 | }; 100 | 101 | struct outelf { 102 | Elf_Ehdr ehdr; 103 | Elf_Phdr phdr[MAXSECS]; 104 | int nph; 105 | struct secmap secs[MAXSECS]; 106 | int nsecs; 107 | struct obj objs[MAXOBJS]; 108 | int nobjs; 109 | 110 | /* code section */ 111 | unsigned long code_addr; 112 | 113 | /* bss section */ 114 | struct bss_sym bss_syms[MAXSYMS]; 115 | int nbss_syms; 116 | unsigned long bss_vaddr; 117 | int bss_len; 118 | 119 | /* symtab section */ 120 | Elf_Shdr shdr[MAXSECS]; 121 | int nsh; 122 | char symstr[MAXSYMS]; 123 | Elf_Sym syms[MAXSYMS]; 124 | int nsyms; 125 | int nsymstr; 126 | unsigned long shdr_faddr; 127 | unsigned long syms_faddr; 128 | unsigned long symstr_faddr; 129 | }; 130 | 131 | static int nosyms = 0; 132 | 133 | static Elf_Sym *obj_find(struct obj *obj, char *name) 134 | { 135 | int i; 136 | for (i = 0; i < obj->nsyms; i++) { 137 | Elf_Sym *sym = &obj->syms[i]; 138 | if (ELF_ST_BIND(sym->st_info) == STB_LOCAL || 139 | sym->st_shndx == SHN_UNDEF) 140 | continue; 141 | if (!strcmp(name, obj->symstr + sym->st_name)) 142 | return sym; 143 | } 144 | return NULL; 145 | } 146 | 147 | static void obj_init(struct obj *obj, char *mem) 148 | { 149 | int i; 150 | obj->mem = mem; 151 | obj->ehdr = (void *) mem; 152 | obj->shdr = (void *) (mem + obj->ehdr->e_shoff); 153 | obj->shstr = mem + obj->shdr[obj->ehdr->e_shstrndx].sh_offset; 154 | for (i = 0; i < obj->ehdr->e_shnum; i++) { 155 | if (obj->shdr[i].sh_type != SHT_SYMTAB) 156 | continue; 157 | obj->symstr = mem + obj->shdr[obj->shdr[i].sh_link].sh_offset; 158 | obj->syms = (void *) (mem + obj->shdr[i].sh_offset); 159 | obj->nsyms = obj->shdr[i].sh_size / sizeof(*obj->syms); 160 | } 161 | } 162 | 163 | static void outelf_init(struct outelf *oe) 164 | { 165 | memset(oe, 0, sizeof(*oe)); 166 | oe->ehdr.e_ident[0] = 0x7f; 167 | oe->ehdr.e_ident[1] = 'E'; 168 | oe->ehdr.e_ident[2] = 'L'; 169 | oe->ehdr.e_ident[3] = 'F'; 170 | oe->ehdr.e_ident[4] = sizeof(long) == 8 ? ELFCLASS64 : ELFCLASS32; 171 | oe->ehdr.e_ident[5] = ELFDATA2LSB; 172 | oe->ehdr.e_ident[6] = EV_CURRENT; 173 | oe->ehdr.e_type = ET_EXEC; 174 | oe->ehdr.e_version = EV_CURRENT; 175 | oe->ehdr.e_shstrndx = SHN_UNDEF; 176 | oe->ehdr.e_ehsize = sizeof(oe->ehdr); 177 | oe->ehdr.e_phentsize = sizeof(oe->phdr[0]); 178 | oe->ehdr.e_shentsize = sizeof(Elf_Shdr); 179 | } 180 | 181 | static struct secmap *outelf_mapping(struct outelf *oe, Elf_Shdr *shdr) 182 | { 183 | int i; 184 | for (i = 0; i < oe->nsecs; i++) 185 | if (oe->secs[i].o_shdr == shdr) 186 | return &oe->secs[i]; 187 | return NULL; 188 | } 189 | 190 | static int outelf_find(struct outelf *oe, char *name, 191 | struct obj **sym_obj, Elf_Sym **sym_sym) 192 | { 193 | int i; 194 | for (i = 0; i < oe->nobjs; i++) { 195 | struct obj *obj = &oe->objs[i]; 196 | Elf_Sym *sym; 197 | if ((sym = obj_find(obj, name))) { 198 | *sym_obj = obj; 199 | *sym_sym = sym; 200 | return 0; 201 | } 202 | } 203 | return 1; 204 | } 205 | 206 | static unsigned long bss_addr(struct outelf *oe, Elf_Sym *sym) 207 | { 208 | int i; 209 | for (i = 0; i < oe->nbss_syms; i++) 210 | if (oe->bss_syms[i].sym == sym) 211 | return oe->bss_vaddr + oe->bss_syms[i].off; 212 | return 0; 213 | } 214 | 215 | static void die(char *msg) 216 | { 217 | fprintf(stderr, "%s\n", msg); 218 | exit(1); 219 | } 220 | 221 | static void die_undef(char *name) 222 | { 223 | fprintf(stderr, "%s undefined\n", name); 224 | exit(1); 225 | } 226 | 227 | static unsigned long symval(struct outelf *oe, struct obj *obj, Elf_Sym *sym) 228 | { 229 | struct secmap *sec; 230 | char *name = obj ? obj->symstr + sym->st_name : NULL; 231 | int s_idx, s_off; 232 | switch (ELF_ST_TYPE(sym->st_info)) { 233 | case STT_SECTION: 234 | if ((sec = outelf_mapping(oe, &obj->shdr[sym->st_shndx]))) 235 | return sec->vaddr; 236 | break; 237 | case STT_NOTYPE: 238 | case STT_OBJECT: 239 | case STT_FUNC: 240 | if (name && *name && sym->st_shndx == SHN_UNDEF) 241 | if (outelf_find(oe, name, &obj, &sym)) 242 | die_undef(name); 243 | if (sym->st_shndx == SHN_COMMON) 244 | return bss_addr(oe, sym); 245 | s_idx = sym->st_shndx; 246 | s_off = sym->st_value; 247 | sec = outelf_mapping(oe, &obj->shdr[s_idx]); 248 | if ((sec = outelf_mapping(oe, &obj->shdr[s_idx]))) 249 | return sec->vaddr + s_off; 250 | } 251 | return 0; 252 | } 253 | 254 | static unsigned long outelf_addr(struct outelf *oe, char *name) 255 | { 256 | struct obj *obj; 257 | Elf_Sym *sym; 258 | if (outelf_find(oe, name, &obj, &sym)) 259 | die_undef(name); 260 | return symval(oe, obj, sym); 261 | } 262 | 263 | #define REL_ARM 0x10000 264 | #define REL_X64 0x20000 265 | #define REL_X86 0x40000 266 | 267 | static int arch_rel(int r) 268 | { 269 | if (e_machine == EM_ARM) 270 | return REL_ARM | r; 271 | if (e_machine == EM_X86_64) 272 | return REL_X64 | r; 273 | if (e_machine == EM_386) 274 | return REL_X86 | r; 275 | return 0; 276 | } 277 | 278 | static void outelf_reloc_sec(struct outelf *oe, int o_idx, int s_idx) 279 | { 280 | struct obj *obj = &oe->objs[o_idx]; 281 | Elf_Shdr *rel_shdr = &obj->shdr[s_idx]; 282 | Elf_Rela *rels = (void *) obj->mem + obj->shdr[s_idx].sh_offset; 283 | Elf_Shdr *other_shdr = &obj->shdr[rel_shdr->sh_info]; 284 | void *other = (void *) obj->mem + other_shdr->sh_offset; 285 | int nrels = rel_shdr->sh_size / sizeof(*rels); 286 | unsigned long addr; 287 | int i; 288 | for (i = 0; i < nrels; i++) { 289 | Elf_Rela *rel = (void *) &rels[i]; 290 | int sym_idx = ELF_R_SYM(rel->r_info); 291 | Elf_Sym *sym = &obj->syms[sym_idx]; 292 | unsigned long val = symval(oe, obj, sym) + REL_ADDEND(rel); 293 | unsigned long *dst = other + rel->r_offset; 294 | switch (arch_rel(ELF_R_TYPE(rel->r_info))) { 295 | case REL_ARM | R_ARM_NONE: 296 | case REL_X86 | R_386_NONE: 297 | case REL_X64 | R_X86_64_NONE: 298 | break; 299 | case REL_ARM | R_ARM_ABS16: 300 | case REL_X86 | R_386_16: 301 | case REL_X64 | R_X86_64_16: 302 | *(unsigned short *) dst += val; 303 | break; 304 | case REL_ARM | R_ARM_ABS32: 305 | case REL_X86 | R_386_32: 306 | case REL_X64 | R_X86_64_32: 307 | case REL_X64 | R_X86_64_32S: 308 | *(unsigned int *) dst += val; 309 | break; 310 | case REL_X64 | R_X86_64_64: 311 | *dst += val; 312 | break; 313 | case REL_ARM | R_ARM_REL32: 314 | case REL_ARM | R_ARM_PLT32: 315 | case REL_X86 | R_386_PLT32: 316 | case REL_X86 | R_386_PC32: 317 | case REL_X64 | R_X86_64_PC32: 318 | addr = outelf_mapping(oe, other_shdr)->vaddr + 319 | rel->r_offset; 320 | *(unsigned int *) dst += val - addr; 321 | break; 322 | case REL_ARM | R_ARM_PC24: 323 | addr = outelf_mapping(oe, other_shdr)->vaddr + 324 | rel->r_offset; 325 | *dst = (*dst & 0xff000000) | 326 | ((*dst + ((val - addr) >> 2)) & 0x00ffffff); 327 | break; 328 | default: 329 | die("neatld: unknown relocation type!"); 330 | } 331 | } 332 | } 333 | 334 | static void outelf_reloc(struct outelf *oe) 335 | { 336 | int i, j; 337 | for (i = 0; i < oe->nobjs; i++) { 338 | struct obj *obj = &oe->objs[i]; 339 | for (j = 0; j < obj->ehdr->e_shnum; j++) 340 | if (obj->shdr[j].sh_type == SHT_REL_) 341 | outelf_reloc_sec(oe, i, j); 342 | } 343 | } 344 | 345 | static void alloc_bss(struct outelf *oe, Elf_Sym *sym) 346 | { 347 | int n = oe->nbss_syms++; 348 | int off = ALIGN(oe->bss_len, MAX(sym->st_value, 4)); 349 | oe->bss_syms[n].sym = sym; 350 | oe->bss_syms[n].off = off; 351 | oe->bss_len = off + sym->st_size; 352 | } 353 | 354 | static void outelf_bss(struct outelf *oe) 355 | { 356 | int i, j; 357 | for (i = 0; i < oe->nobjs; i++) { 358 | struct obj *obj = &oe->objs[i]; 359 | for (j = 0; j < obj->nsyms; j++) 360 | if (obj->syms[j].st_shndx == SHN_COMMON) 361 | alloc_bss(oe, &obj->syms[j]); 362 | } 363 | } 364 | 365 | #define SEC_CODE(s) ((s)->sh_flags & SHF_EXECINSTR) 366 | #define SEC_BSS(s) ((s)->sh_type == SHT_NOBITS) 367 | #define SEC_DATA(s) (!SEC_CODE(s) && !SEC_BSS(s)) 368 | 369 | static int outelf_str(struct outelf *oe, char *s) 370 | { 371 | int n = oe->nsymstr; 372 | char *d = oe->symstr + oe->nsymstr; 373 | while (*s) 374 | *d++ = *s++; 375 | *d++ = '\0'; 376 | oe->nsymstr = d - oe->symstr; 377 | return n; 378 | } 379 | 380 | static void build_symtab(struct outelf *oe) 381 | { 382 | int i, j; 383 | Elf_Sym *syms = oe->syms; 384 | Elf_Shdr *cs_shdr = &oe->shdr[++oe->nsh]; 385 | Elf_Shdr *ds_shdr = &oe->shdr[++oe->nsh]; 386 | Elf_Shdr *bss_shdr = &oe->shdr[++oe->nsh]; 387 | Elf_Shdr *sym_shdr = &oe->shdr[++oe->nsh]; 388 | Elf_Shdr *str_shdr = &oe->shdr[++oe->nsh]; 389 | int n = 1; 390 | int faddr = oe->shdr_faddr; 391 | oe->nsh++; 392 | outelf_str(oe, ""); 393 | sym_shdr->sh_name = outelf_str(oe, ".symtab"); 394 | str_shdr->sh_name = outelf_str(oe, ".strtab"); 395 | cs_shdr->sh_name = outelf_str(oe, ".text"); 396 | ds_shdr->sh_name = outelf_str(oe, ".data"); 397 | bss_shdr->sh_name = outelf_str(oe, ".bss"); 398 | for (i = 0; i < oe->nobjs; i++) { 399 | struct obj *obj = &oe->objs[i]; 400 | for (j = 0; j < obj->nsyms; j++) { 401 | Elf_Sym *sym = &obj->syms[j]; 402 | int type = ELF_ST_TYPE(sym->st_info); 403 | int bind = ELF_ST_BIND(sym->st_info); 404 | char *name = obj->symstr + sym->st_name; 405 | if (!*name || bind == STB_LOCAL || 406 | sym->st_shndx == SHN_UNDEF) 407 | continue; 408 | syms[n].st_name = outelf_str(oe, name); 409 | syms[n].st_info = ELF_ST_INFO(bind, type); 410 | syms[n].st_value = symval(oe, obj, sym); 411 | syms[n].st_size = sym->st_size; 412 | syms[n].st_shndx = SHN_ABS; 413 | n++; 414 | } 415 | } 416 | oe->nsyms = n; 417 | 418 | oe->shdr_faddr = faddr; 419 | faddr += oe->nsh * sizeof(oe->shdr[0]); 420 | oe->syms_faddr = faddr; 421 | faddr += oe->nsyms * sizeof(oe->syms[0]); 422 | oe->symstr_faddr = faddr; 423 | faddr += oe->nsymstr; 424 | 425 | oe->ehdr.e_shstrndx = str_shdr - oe->shdr; 426 | oe->ehdr.e_shoff = oe->shdr_faddr; 427 | oe->ehdr.e_shnum = oe->nsh; 428 | 429 | str_shdr->sh_type = SHT_STRTAB; 430 | str_shdr->sh_offset = oe->symstr_faddr; 431 | str_shdr->sh_size = oe->nsymstr; 432 | 433 | sym_shdr->sh_type = SHT_SYMTAB; 434 | sym_shdr->sh_entsize = sizeof(oe->syms[0]); 435 | sym_shdr->sh_offset = oe->syms_faddr; 436 | sym_shdr->sh_size = oe->nsyms * sizeof(oe->syms[0]); 437 | sym_shdr->sh_link = str_shdr - oe->shdr; 438 | sym_shdr->sh_info = 0; 439 | 440 | cs_shdr->sh_type = SHT_PROGBITS; 441 | cs_shdr->sh_flags = SHF_ALLOC | SHF_EXECINSTR; 442 | cs_shdr->sh_offset = oe->phdr[I_CS].p_offset; 443 | cs_shdr->sh_addr = oe->phdr[I_CS].p_vaddr; 444 | cs_shdr->sh_addralign = oe->phdr[I_CS].p_align; 445 | cs_shdr->sh_size = oe->phdr[I_CS].p_filesz; 446 | 447 | ds_shdr->sh_type = SHT_PROGBITS; 448 | ds_shdr->sh_flags = SHF_ALLOC | SHF_WRITE; 449 | ds_shdr->sh_offset = oe->phdr[I_DS].p_offset; 450 | ds_shdr->sh_addr = oe->phdr[I_DS].p_vaddr; 451 | ds_shdr->sh_addralign = oe->phdr[I_DS].p_align; 452 | ds_shdr->sh_size = oe->phdr[I_DS].p_filesz; 453 | 454 | bss_shdr->sh_type = SHT_NOBITS; 455 | bss_shdr->sh_flags = SHF_ALLOC | SHF_WRITE; 456 | bss_shdr->sh_offset = oe->phdr[I_BSS].p_offset; 457 | bss_shdr->sh_addr = oe->phdr[I_BSS].p_vaddr; 458 | bss_shdr->sh_addralign = oe->phdr[I_BSS].p_align; 459 | bss_shdr->sh_size = oe->phdr[I_BSS].p_filesz; 460 | } 461 | 462 | static void outelf_write(struct outelf *oe, int fd) 463 | { 464 | int i; 465 | oe->ehdr.e_entry = outelf_addr(oe, entry) - 466 | sec_vaddr[I_CS] + sec_laddr[I_CS]; 467 | if (!nosyms) 468 | build_symtab(oe); 469 | oe->ehdr.e_phnum = oe->nph; 470 | oe->ehdr.e_phoff = sizeof(oe->ehdr); 471 | oe->ehdr.e_machine = e_machine; 472 | oe->ehdr.e_flags = e_flags; 473 | lseek(fd, 0, SEEK_SET); 474 | write(fd, &oe->ehdr, sizeof(oe->ehdr)); 475 | write(fd, &oe->phdr, oe->nph * sizeof(oe->phdr[0])); 476 | for (i = 0; i < oe->nsecs; i++) { 477 | struct secmap *sec = &oe->secs[i]; 478 | char *buf = sec->obj->mem + sec->o_shdr->sh_offset; 479 | int len = sec->o_shdr->sh_size; 480 | if (SEC_BSS(sec->o_shdr)) 481 | continue; 482 | lseek(fd, sec->faddr, SEEK_SET); 483 | write(fd, buf, len); 484 | } 485 | if (!nosyms) { 486 | lseek(fd, oe->shdr_faddr, SEEK_SET); 487 | write(fd, &oe->shdr, oe->nsh * sizeof(oe->shdr[0])); 488 | lseek(fd, oe->syms_faddr, SEEK_SET); 489 | write(fd, &oe->syms, oe->nsyms * sizeof(oe->syms[0])); 490 | lseek(fd, oe->symstr_faddr, SEEK_SET); 491 | write(fd, &oe->symstr, oe->nsymstr); 492 | } 493 | } 494 | 495 | static void outelf_add(struct outelf *oe, char *mem) 496 | { 497 | Elf_Ehdr *ehdr = (void *) mem; 498 | Elf_Shdr *shdr = (void *) (mem + ehdr->e_shoff); 499 | struct obj *obj; 500 | int i; 501 | if (ehdr->e_type != ET_REL) 502 | return; 503 | e_machine = ehdr->e_machine; 504 | e_flags = ehdr->e_flags; 505 | if (oe->nobjs >= MAXOBJS) 506 | die("neatld: MAXOBJS reached!"); 507 | obj = &oe->objs[oe->nobjs++]; 508 | obj_init(obj, mem); 509 | for (i = 0; i < ehdr->e_shnum; i++) { 510 | struct secmap *sec; 511 | if (!(shdr[i].sh_flags & 0x7)) 512 | continue; 513 | if (oe->nsecs >= MAXSECS) 514 | die("neatld: MAXSECS reached!"); 515 | sec = &oe->secs[oe->nsecs++]; 516 | sec->o_shdr = &shdr[i]; 517 | sec->obj = obj; 518 | } 519 | } 520 | 521 | static int link_cs(struct outelf *oe, Elf_Phdr *phdr, unsigned long faddr, 522 | unsigned long vaddr, unsigned long laddr, int len) 523 | { 524 | int i; 525 | for (i = 0; i < oe->nsecs; i++) { 526 | struct secmap *sec = &oe->secs[i]; 527 | int alignment = MAX(sec->o_shdr->sh_addralign, 4); 528 | if (!SEC_CODE(sec->o_shdr)) 529 | continue; 530 | len = ALIGN(vaddr + len, alignment) - vaddr; 531 | sec->vaddr = vaddr + len; 532 | sec->faddr = faddr + len; 533 | len += sec->o_shdr->sh_size; 534 | } 535 | phdr->p_type = PT_LOAD; 536 | phdr->p_flags = PF_R | PF_X; 537 | phdr->p_vaddr = vaddr; 538 | phdr->p_paddr = laddr; 539 | phdr->p_offset = faddr; 540 | phdr->p_filesz = len; 541 | phdr->p_memsz = len; 542 | phdr->p_align = PAGE_SIZE; 543 | return len; 544 | } 545 | 546 | static int link_ds(struct outelf *oe, Elf_Phdr *phdr, unsigned long faddr, 547 | unsigned long vaddr, unsigned long laddr) 548 | { 549 | int len = 0; 550 | int i; 551 | for (i = 0; i < oe->nsecs; i++) { 552 | struct secmap *sec = &oe->secs[i]; 553 | if (!SEC_DATA(sec->o_shdr)) 554 | continue; 555 | sec->vaddr = vaddr + len; 556 | sec->faddr = faddr + len; 557 | len += sec->o_shdr->sh_size; 558 | } 559 | len = ALIGN(len, 4); 560 | phdr->p_type = PT_LOAD; 561 | phdr->p_flags = PF_R | PF_W; 562 | phdr->p_align = PAGE_SIZE; 563 | phdr->p_vaddr = vaddr; 564 | phdr->p_paddr = laddr; 565 | phdr->p_filesz = len; 566 | phdr->p_memsz = len; 567 | phdr->p_offset = faddr; 568 | return len; 569 | } 570 | 571 | static int link_bss(struct outelf *oe, Elf_Phdr *phdr, 572 | unsigned long faddr, unsigned long vaddr, int len) 573 | { 574 | int i; 575 | for (i = 0; i < oe->nsecs; i++) { 576 | struct secmap *sec = &oe->secs[i]; 577 | int alignment = MAX(sec->o_shdr->sh_addralign, 4); 578 | if (!SEC_BSS(sec->o_shdr)) 579 | continue; 580 | len = ALIGN(vaddr + len, alignment) - vaddr; 581 | sec->vaddr = vaddr + len; 582 | sec->faddr = faddr; 583 | len += sec->o_shdr->sh_size; 584 | } 585 | phdr->p_type = PT_LOAD; 586 | phdr->p_flags = PF_R | PF_W; 587 | phdr->p_vaddr = vaddr; 588 | phdr->p_paddr = vaddr; 589 | phdr->p_offset = faddr; 590 | phdr->p_filesz = 0; 591 | phdr->p_memsz = len; 592 | phdr->p_align = PAGE_SIZE; 593 | return len; 594 | } 595 | 596 | static void outelf_link(struct outelf *oe) 597 | { 598 | unsigned long faddr, vaddr, laddr; 599 | int len; 600 | len = ALIGN(sizeof(oe->ehdr) + MAXPHDRS * sizeof(oe->phdr[0]), secalign); 601 | faddr = len & ~PAGE_MASK; 602 | vaddr = sec_vaddr[I_CS]; 603 | laddr = sec_laddr[I_CS]; 604 | len = link_cs(oe, &oe->phdr[0], faddr, vaddr, laddr, len & PAGE_MASK); 605 | 606 | len = ALIGN(faddr + len, secalign) - faddr; 607 | faddr += len; 608 | vaddr = sec_set[I_DS] ? sec_vaddr[I_DS] | (faddr & PAGE_MASK) : 609 | vaddr + PAGE_SIZE + len; 610 | laddr = sec_set[I_DS] ? sec_laddr[I_DS] | (faddr & PAGE_MASK) : 611 | laddr + PAGE_SIZE + len; 612 | len = link_ds(oe, &oe->phdr[1], faddr, vaddr, laddr); 613 | 614 | len = ALIGN(faddr + len, secalign) - faddr; 615 | faddr += len; 616 | vaddr = sec_set[I_BSS] ? sec_vaddr[I_BSS] | (faddr & PAGE_MASK) : 617 | vaddr + PAGE_SIZE + len; 618 | outelf_bss(oe); 619 | oe->bss_vaddr = vaddr; 620 | len = link_bss(oe, &oe->phdr[2], faddr, vaddr, oe->bss_len); 621 | 622 | oe->nph = 3; 623 | outelf_reloc(oe); 624 | oe->shdr_faddr = faddr; 625 | } 626 | 627 | struct arhdr { 628 | char ar_name[16]; 629 | char ar_date[12]; 630 | char ar_uid[6]; 631 | char ar_gid[6]; 632 | char ar_mode[8]; 633 | char ar_size[10]; 634 | char ar_fmag[2]; 635 | }; 636 | 637 | static int get_be32(unsigned char *s) 638 | { 639 | return s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24); 640 | } 641 | 642 | static int sym_undef(struct outelf *oe, char *name) 643 | { 644 | int i, j; 645 | int undef = 0; 646 | for (i = 0; i < oe->nobjs; i++) { 647 | struct obj *obj = &oe->objs[i]; 648 | for (j = 0; j < obj->nsyms; j++) { 649 | Elf_Sym *sym = &obj->syms[j]; 650 | if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) 651 | continue; 652 | if (strcmp(name, obj->symstr + sym->st_name)) 653 | continue; 654 | if (sym->st_shndx != SHN_UNDEF) 655 | return 0; 656 | undef = 1; 657 | } 658 | } 659 | return undef; 660 | } 661 | 662 | static int outelf_ar_link(struct outelf *oe, char *ar, int base) 663 | { 664 | char *ar_index; 665 | char *ar_name; 666 | int nsyms = get_be32((void *) ar); 667 | int added = 0; 668 | int i; 669 | ar_index = ar + 4; 670 | ar_name = ar_index + nsyms * 4; 671 | for (i = 0; i < nsyms; i++) { 672 | int off = get_be32((void *) ar_index + i * 4) + 673 | sizeof(struct arhdr); 674 | if (sym_undef(oe, ar_name)) { 675 | outelf_add(oe, ar - base + off); 676 | added++; 677 | } 678 | ar_name = strchr(ar_name, '\0') + 1; 679 | } 680 | return added; 681 | } 682 | 683 | static void outelf_archive(struct outelf *oe, char *ar) 684 | { 685 | char *beg = ar; 686 | 687 | /* skip magic */ 688 | ar += 8; 689 | for(;;) { 690 | struct arhdr *hdr = (void *) ar; 691 | char *name = hdr->ar_name; 692 | int size; 693 | ar += sizeof(*hdr); 694 | hdr->ar_size[sizeof(hdr->ar_size) - 1] = '\0'; 695 | size = atoi(hdr->ar_size); 696 | size = (size + 1) & ~1; 697 | if (name[0] == '/' && name[1] == ' ') { 698 | while (outelf_ar_link(oe, ar, ar - beg)) 699 | ; 700 | return; 701 | } 702 | if (name[0] == '/' && name[1] == '/' && name[2] == ' ') 703 | outelf_add(oe, ar); 704 | ar += size; 705 | } 706 | } 707 | 708 | static long filesize(int fd) 709 | { 710 | struct stat stat; 711 | fstat(fd, &stat); 712 | return stat.st_size; 713 | } 714 | 715 | static char *fileread(char *path) 716 | { 717 | int fd = open(path, O_RDONLY); 718 | int size = filesize(fd); 719 | char *buf = malloc(size); 720 | read(fd, buf, size); 721 | close(fd); 722 | return buf; 723 | } 724 | 725 | static int is_ar(char *path) 726 | { 727 | int len = strlen(path); 728 | return len > 2 && path[len - 2] == '.' && path[len - 1] == 'a'; 729 | } 730 | 731 | #define LIBDIRS (1 << 5) 732 | #define PATHLEN (1 << 8) 733 | 734 | static char *libdirs[LIBDIRS] = {"/lib"}; 735 | static int nlibdirs = 1; 736 | 737 | static int lib_find(char *path, char *lib) 738 | { 739 | struct stat st; 740 | int i; 741 | for (i = 0; i < nlibdirs; i++) { 742 | sprintf(path, "%s/lib%s.a", libdirs[i], lib); 743 | if (!stat(path, &st)) 744 | return 0; 745 | } 746 | return 1; 747 | } 748 | 749 | static unsigned long hexnum(char *s) 750 | { 751 | unsigned long n = 0; 752 | if (s[0] == '0' && s[1] == 'x') 753 | s += 2; 754 | for (; isdigit(*s) || isalpha(*s); s++) { 755 | n <<= 4; 756 | n |= isdigit(*s) ? *s - '0' : tolower(*s) - 'a' + 10; 757 | } 758 | return n; 759 | } 760 | 761 | static void set_addr(int sec, char *arg) 762 | { 763 | int idx = I_CS; 764 | char *sep = strchr(arg, ':'); 765 | if (sec == 'd') 766 | idx = I_DS; 767 | if (sec == 'b') 768 | idx = I_BSS; 769 | sec_vaddr[idx] = hexnum(arg); 770 | sec_laddr[idx] = sep ? hexnum(sep + 1) : sec_vaddr[idx]; 771 | sec_set[idx] = 1; 772 | } 773 | 774 | static char *obj_add(struct outelf *oe, char *path) 775 | { 776 | char *buf = fileread(path); 777 | if (!buf) 778 | die("neatld: cannot open object!"); 779 | if (is_ar(path)) 780 | outelf_archive(oe, buf); 781 | else 782 | outelf_add(oe, buf); 783 | return buf; 784 | } 785 | 786 | int main(int argc, char **argv) 787 | { 788 | char out[PATHLEN] = "a.out"; 789 | struct outelf oe; 790 | char *mem[MAXFILES]; 791 | int nmem = 0; 792 | int fd; 793 | int i = 0; 794 | if (argc < 2) 795 | die("neatld: no object given!"); 796 | outelf_init(&oe); 797 | 798 | while (++i < argc) { 799 | if (argv[i][0] != '-') { 800 | mem[nmem++] = obj_add(&oe, argv[i]); 801 | continue; 802 | } 803 | if (argv[i][1] == 'l') { 804 | char path[PATHLEN]; 805 | if (lib_find(path, argv[i] + 2)) 806 | die("neatld: cannot find library!"); 807 | mem[nmem++] = obj_add(&oe, path); 808 | continue; 809 | } 810 | if (argv[i][1] == 'L') { 811 | libdirs[nlibdirs++] = argv[i][2] ? argv[i] + 2 : argv[++i]; 812 | continue; 813 | } 814 | if (argv[i][1] == 'o') { 815 | strcpy(out, argv[i][2] ? argv[i] + 2 : argv[++i]); 816 | continue; 817 | } 818 | if (argv[i][1] == 's') { 819 | nosyms = 1; 820 | continue; 821 | } 822 | if (argv[i][1] == 'g') 823 | continue; 824 | if (argv[i][1] == 'm') { 825 | char sec = argv[i][2]; 826 | char *arg = argv[i][3] == '=' ? argv[i] + 4 : argv[++i]; 827 | set_addr(sec, arg); 828 | continue; 829 | } 830 | if (argv[i][1] == 'p') { 831 | secalign = PAGE_SIZE; 832 | continue; 833 | } 834 | if (argv[i][1] == 'e') { 835 | entry = argv[i][2] ? argv[i] + 2 : argv[++i]; 836 | continue; 837 | } 838 | if (argv[i][1] == 'h') { 839 | printf("Usage: neatld [options] objects\n\n"); 840 | printf("Options:\n"); 841 | printf(" -o out set the output file\n"); 842 | printf(" -l lib link with library lib\n"); 843 | printf(" -L dir search dir for libraries\n"); 844 | printf(" -s do not include a symbol table\n"); 845 | printf(" -mXvaddr:laddr section virtual/load address\n"); 846 | printf(" -p page-align sections\n"); 847 | printf(" -e entry point symbol\n"); 848 | return 1; 849 | } 850 | } 851 | outelf_link(&oe); 852 | fd = open(out, O_WRONLY | O_TRUNC | O_CREAT, 0700); 853 | if (fd < 0) 854 | die("neatld: failed to create the output!"); 855 | outelf_write(&oe, fd); 856 | close(fd); 857 | for (i = 0; i < nmem; i++) 858 | free(mem[i]); 859 | return 0; 860 | } 861 | --------------------------------------------------------------------------------