1/* 2 * Copyright (c) 2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <fcntl.h> 27#include <sys/types.h> 28#include <sys/uio.h> 29#include <unistd.h> 30#include "stuff/bool.h" 31#include "stuff/ofile.h" 32#include "stuff/errors.h" 33#include "stuff/reloc.h" 34#include "coff/base_relocs.h" 35#include "coff/bytesex.h" 36#include "mach-o/x86_64/reloc.h" 37 38/* used by error routines as the name of this program */ 39char *progname = NULL; 40 41/* used for debugging this program */ 42static enum bool verbose = FALSE; 43 44/* the bytesex of our target object file and of this host machine */ 45static enum byte_sex target_byte_sex; 46static enum byte_sex host_byte_sex; 47 48/* 49 * This is the internal structure that we gather the base relocation in from 50 * the Mach-O relocation entries. 51 */ 52struct base_reloc { 53 uint64_t addr; 54 uint32_t type; 55}; 56struct base_reloc *base_relocs = NULL; 57uint32_t nbase_reloc = 0; 58 59static void process( 60 struct ofile *ofile, 61 char *arch_name, 62 void *cookie); 63static void gather_base_reloc_info( 64 uint32_t addr, 65 struct relocation_info *relocs, 66 uint32_t nreloc, 67 cpu_type_t cpu_type, 68 uint32_t length, 69 int macho_reloc_type, 70 int base_reloc_type); 71static void add_base_reloc( 72 uint64_t addr, 73 uint32_t type); 74static void output_base_relocs( 75 char *out); 76static int cmp_base_relocs( 77 struct base_reloc *x1, 78 struct base_reloc *x2); 79static void usage( 80 void); 81 82/* apple_version is created by the libstuff/Makefile */ 83extern char apple_version[]; 84char *version = apple_version; 85 86/* 87 * The makerelocs(1) tool makes a file of PECOFF base relocation entries from a 88 * fully linked Mach-O file compiled with dynamic code gen and relocation 89 * entries saved (linked with -r). The file of PECOFF base relocation entries 90 * then are put in a Mach-O section called .reloc and is then used with 91 * objcopy(1) to convert that Mach-O file into a PECOFF file. The makerelocs(1) 92 * program has the current usage: 93 * 94 * makerelocs [-v] input_Mach-O output_relocs 95 * 96 * Where the -v flag provides verbose output used to debug the this programs 97 * creation of the PECOFF base relocation entries. 98 * 99 * TODO: This code can be used for the basis to replace the makerelocs(1) 100 * program from the efitools project. Its current state is that it works for 101 * building the efiboot project and the Bluetooth.efi hardware diag. 102 */ 103int 104main( 105int argc, 106char **argv, 107char **envp) 108{ 109 int i; 110 char *input, *output; 111 112 progname = argv[0]; 113 host_byte_sex = get_host_byte_sex(); 114 115 input = NULL; 116 output = NULL; 117 118 for(i = 1; i < argc; i++){ 119 if(strcmp(argv[i], "-v") == 0) 120 verbose = TRUE; 121 else if(input == NULL) 122 input = argv[i]; 123 else if(output == NULL) 124 output = argv[i]; 125 else 126 usage(); 127 } 128 if(input == NULL){ 129 warning("no input file specified"); 130 usage(); 131 } 132 if(output == NULL){ 133 warning("no output file specified"); 134 usage(); 135 } 136 137 ofile_process(input, NULL, 0, FALSE, FALSE, FALSE, FALSE, 138 process, NULL); 139 if(errors != 0) 140 return(EXIT_FAILURE); 141 142 /* create the output file */ 143 output_base_relocs(output); 144 145 if(errors == 0) 146 return(EXIT_SUCCESS); 147 else 148 return(EXIT_FAILURE); 149} 150 151/* 152 * process() is the routine that gets called by ofile_process() to process the 153 * object file and gather the info to create the base relocation entries. 154 */ 155static 156void 157process( 158struct ofile *ofile, 159char *arch_name, 160void *cookie) 161{ 162 uint32_t ncmds, i, j; 163 uint64_t addr, first_addr; 164 struct load_command *lc; 165 struct segment_command *sg; 166 struct segment_command_64 *sg64; 167 struct section *s; 168 struct section_64 *s64; 169 enum bool swapped; 170 struct relocation_info *relocs; 171 172 struct symtab_command *st; 173 struct dysymtab_command *dyst; 174 struct nlist *symbols; 175 struct nlist_64 *symbols64; 176 177 st = NULL; 178 dyst = NULL; 179 swapped = host_byte_sex != ofile->object_byte_sex; 180 target_byte_sex = ofile->object_byte_sex; 181 if(ofile->mh != NULL) 182 ncmds = ofile->mh->ncmds; 183 else 184 ncmds = ofile->mh64->ncmds; 185 186 lc = ofile->load_commands; 187 for(i = 0; i < ncmds; i++){ 188 if(st == NULL && lc->cmd == LC_SYMTAB){ 189 st = (struct symtab_command *)lc; 190 } 191 else if(dyst == NULL && lc->cmd == LC_DYSYMTAB){ 192 dyst = (struct dysymtab_command *)lc; 193 } 194 lc = (struct load_command *)((char *)lc + lc->cmdsize); 195 } 196 /* TODO this would be need to to do checking for undefined symbols */ 197 if(ofile->mh != NULL){ 198 symbols = (struct nlist *)(ofile->object_addr + st->symoff); 199 if(swapped) 200 swap_nlist(symbols, st->nsyms, host_byte_sex); 201 symbols64 = NULL; 202 } 203 else{ 204 symbols = NULL; 205 symbols64 = (struct nlist_64 *)(ofile->object_addr +st->symoff); 206 if(swapped) 207 swap_nlist_64(symbols64, st->nsyms, host_byte_sex); 208 } 209 210 first_addr = 0; 211 lc = ofile->load_commands; 212 for(i = 0; i < ncmds; i++){ 213 if(lc->cmd == LC_SEGMENT){ 214 sg = (struct segment_command *)lc; 215 if(first_addr == 0) 216 first_addr = sg->vmaddr; 217 s = (struct section *) 218 ((char *)sg + sizeof(struct segment_command)); 219 for(j = 0; j < sg->nsects; j++){ 220 relocs = (struct relocation_info *)(ofile->object_addr + 221 s[j].reloff); 222 if(swapped) 223 swap_relocation_info(relocs, s[j].nreloc, 224 host_byte_sex); 225 if(ofile->mh_cputype == CPU_TYPE_I386) 226 gather_base_reloc_info(s[j].addr, relocs, s[j].nreloc, 227 CPU_TYPE_I386, 2, GENERIC_RELOC_VANILLA, 228 IMAGE_REL_BASED_HIGHLOW); 229 else if(ofile->mh_cputype == CPU_TYPE_ARM) 230 gather_base_reloc_info(s[j].addr, relocs, s[j].nreloc, 231 CPU_TYPE_ARM, 2, GENERIC_RELOC_VANILLA, 232 IMAGE_REL_BASED_HIGHLOW); 233 else 234 fatal("unsupported cputype %d", ofile->mh_cputype); 235 if((s[j].flags & SECTION_TYPE) == 236 S_NON_LAZY_SYMBOL_POINTERS){ 237 for(addr = s[j].addr; 238 addr < s[j].addr + s[j].size; 239 addr += 4) { 240 add_base_reloc(addr, IMAGE_REL_BASED_HIGHLOW); 241 } 242 } 243 } 244 } 245 else if(lc->cmd == LC_SEGMENT_64){ 246 sg64 = (struct segment_command_64 *)lc; 247 if(first_addr == 0) 248 first_addr = sg64->vmaddr; 249 s64 = (struct section_64 *) 250 ((char *)sg64 + sizeof(struct segment_command_64)); 251 for(j = 0; j < sg64->nsects; j++){ 252 relocs = (struct relocation_info *)(ofile->object_addr + 253 s64[j].reloff); 254 if(swapped) 255 swap_relocation_info(relocs, s64[j].nreloc, 256 host_byte_sex); 257 if(ofile->mh_cputype == CPU_TYPE_X86_64) 258 gather_base_reloc_info(s64[j].addr, relocs, 259 s64[j].nreloc, CPU_TYPE_X86_64, 3, 260 X86_64_RELOC_UNSIGNED, IMAGE_REL_BASED_DIR64); 261 else 262 fatal("unsupported cputype %d", ofile->mh_cputype); 263 if((s64[j].flags & SECTION_TYPE) == 264 S_NON_LAZY_SYMBOL_POINTERS){ 265 for(addr = s64[j].addr; 266 addr < s64[j].addr + s64[j].size; 267 addr += 8) { 268 add_base_reloc(addr, IMAGE_REL_BASED_DIR64); 269 } 270 } 271 } 272 } 273 lc = (struct load_command *)((char *)lc + lc->cmdsize); 274 } 275 if(dyst != NULL && dyst->nlocrel != 0){ 276 relocs = (struct relocation_info *)(ofile->object_addr + 277 dyst->locreloff); 278 if(swapped) 279 swap_relocation_info(relocs, dyst->nlocrel, host_byte_sex); 280 if(ofile->mh_cputype == CPU_TYPE_I386) 281 gather_base_reloc_info(first_addr, relocs, dyst->nlocrel, 282 CPU_TYPE_I386, 2, GENERIC_RELOC_VANILLA, 283 IMAGE_REL_BASED_HIGHLOW); 284 if(ofile->mh_cputype == CPU_TYPE_ARM) 285 gather_base_reloc_info(first_addr, relocs, dyst->nlocrel, 286 CPU_TYPE_ARM, 2, GENERIC_RELOC_VANILLA, 287 IMAGE_REL_BASED_HIGHLOW); 288 else if(ofile->mh_cputype == CPU_TYPE_X86_64) 289 gather_base_reloc_info(first_addr, relocs, dyst->nlocrel, 290 CPU_TYPE_X86_64, 3, X86_64_RELOC_UNSIGNED, 291 IMAGE_REL_BASED_DIR64); 292 else 293 fatal("unsupported cputype %d", ofile->mh_cputype); 294 } 295 if(dyst != NULL && dyst->nextrel != 0) 296 fatal("input Mach-O file has external relocation entries"); 297} 298 299/* 300 * gather_base_reloc_info() is passed the base address for the set of Mach-O 301 * relocation entries. And is passed the cpu_type, length and macho_reloc_type 302 * to look for and the base_reloc_type to create if found. 303 */ 304static 305void 306gather_base_reloc_info( 307uint32_t addr, 308struct relocation_info *relocs, 309uint32_t nreloc, 310cpu_type_t cpu_type, 311uint32_t length, 312int macho_reloc_type, 313int base_reloc_type) 314{ 315 uint32_t i, r_address, r_pcrel, r_length, r_extern, r_type; 316 struct scattered_relocation_info *sreloc; 317 318 for(i = 0; i < nreloc; i++){ 319 if((relocs[i].r_address & R_SCATTERED) != 0){ 320 sreloc = (struct scattered_relocation_info *)(relocs + i); 321 r_address = sreloc->r_address; 322 r_pcrel = sreloc->r_pcrel; 323 r_length = sreloc->r_length; 324 r_type = (enum reloc_type_generic)sreloc->r_type; 325 r_extern = 0; 326 } 327 else{ 328 r_address = relocs[i].r_address; 329 r_pcrel = relocs[i].r_pcrel; 330 r_length = relocs[i].r_length; 331 r_extern = relocs[i].r_extern; 332 r_type = (enum reloc_type_generic)relocs[i].r_type; 333 } 334 335 if(r_extern == 0 && r_pcrel == 0 && 336 r_length == length && r_type == macho_reloc_type) 337 add_base_reloc(addr + r_address, base_reloc_type); 338 else 339 ; /* TODO add checking and error messages here */ 340 341 if((relocs[i].r_address & R_SCATTERED) == 0){ 342 if(reloc_has_pair(cpu_type, relocs[i].r_type)) 343 i++; 344 } 345 else{ 346 sreloc = (struct scattered_relocation_info *)relocs + i; 347 if(reloc_has_pair(cpu_type, sreloc->r_type)) 348 i++; 349 } 350 } 351} 352 353/* 354 * add_base_reloc() is passed a addr and a type for a base relocation entry to 355 * add to the list. 356 */ 357static 358void 359add_base_reloc( 360uint64_t addr, 361uint32_t type) 362{ 363 static int max = 0; 364 struct base_reloc *new_base_relocs; 365 366 if(!max){ 367 max = 128; 368 base_relocs = (struct base_reloc *) 369 malloc(max * sizeof(struct base_reloc)); 370 } 371 if(nbase_reloc >= max){ 372 new_base_relocs = malloc(2 * max * sizeof(struct base_reloc)); 373 memcpy(new_base_relocs, base_relocs, 374 max * sizeof(struct base_reloc)); 375 max *= 2; 376 free(base_relocs); 377 base_relocs = new_base_relocs; 378 } 379 base_relocs[nbase_reloc].addr = addr; 380 base_relocs[nbase_reloc].type = type; 381 nbase_reloc++; 382} 383 384/* 385 * usage() prints the current usage message and exits indicating failure. 386 */ 387static 388void 389usage( 390void) 391{ 392 fprintf(stderr, "Usage: %s [-v] input_Mach-O output_relocs\n", 393 progname); 394 exit(EXIT_FAILURE); 395} 396 397/* 398 * The base relocation table in a PECOFF file is divided into blocks. Each 399 * block represents the base relocations for a 4K page. Each block must start 400 * on a 32-bit boundary. Which is why one "nop" base relocation entry may be 401 * be added as padding in a block. 402 */ 403#define MAX_BLOCK_OFFSET 0x1000 404#define BLOCK_MASK (MAX_BLOCK_OFFSET-1) 405 406/* 407 * output_base_relocs() takes the info for the base relocation entries gathered 408 * and outputs the fixup blocks as they would be in a PECOFF file in to the 409 * specified file name. 410 */ 411static 412void 413output_base_relocs( 414char *out) 415{ 416 int blockcnt; 417 int i, entries; 418 uint64_t base; 419 int size; 420 char *fb; 421 struct base_relocation_block_header *h; 422 struct base_relocation_entry *b; 423 int f; 424 uint32_t offset; 425 enum bool swapped; 426 427 blockcnt = 0; 428 swapped = host_byte_sex != target_byte_sex; 429 430 if(nbase_reloc == 0) 431 goto done; 432 433 qsort(base_relocs, nbase_reloc, sizeof(struct base_reloc), 434 (int (*)(const void *, const void *))cmp_base_relocs); 435 436 /* 437 * The size of the base relocation tables must be a multiple of 4 bytes. 438 * so we may need to add one relocation entry as padding. We make this 439 * fixup block large enought to hold all the base relocation entries. 440 * But it will be broken up for the base relocation entries for each 441 * each group that refers to the same 4K page. 442 */ 443 size = sizeof(struct base_relocation_block_header) + 444 (nbase_reloc + 1) * sizeof(struct base_relocation_entry); 445 fb = malloc(size); 446 447 f = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); 448 if(f == -1){ 449 fatal("open output file"); 450 } 451 452 entries = 0; 453 base = base_relocs[0].addr & ~BLOCK_MASK; 454 h = (struct base_relocation_block_header *)fb; 455 b = (struct base_relocation_entry *) 456 (fb + sizeof(struct base_relocation_block_header)); 457 for(i = 0; i < nbase_reloc; i++){ 458 offset = base_relocs[i].addr - base; 459 if(offset >= MAX_BLOCK_OFFSET) { 460 /* add padding if needed */ 461 if((entries % 2) != 0){ 462 b[entries].type = IMAGE_REL_BASED_ABSOLUTE; 463 b[entries].offset = 0; 464 entries++; 465 } 466 h->page_rva = base; 467 size = sizeof(struct base_relocation_block_header) + 468 entries * sizeof(struct base_relocation_entry); 469 h->block_size = size; 470 if(swapped){ 471 swap_base_relocation_block_header(h, 472 target_byte_sex); 473 swap_base_relocation_entry(b, entries, 474 target_byte_sex); 475 } 476 // write out the block then start a new one 477 write(f, fb, size); 478 479 entries = 0; 480 blockcnt++; 481 base = base_relocs[i].addr & ~BLOCK_MASK; 482 offset = base_relocs[i].addr - base; 483 } 484 b[entries].type = base_relocs[i].type; 485 b[entries].offset = offset; 486 entries++; 487 } 488 489 /* add padding if needed */ 490 if((entries % 2) != 0){ 491 b[entries].type = IMAGE_REL_BASED_ABSOLUTE; 492 b[entries].offset = 0; 493 entries++; 494 } 495 h->page_rva = base; 496 size = sizeof(struct base_relocation_block_header) + 497 entries * sizeof(struct base_relocation_entry); 498 h->block_size = size; 499 if(swapped){ 500 swap_base_relocation_block_header(h, target_byte_sex); 501 swap_base_relocation_entry(b, entries, target_byte_sex); 502 } 503 /* write out the last block */ 504 write(f, fb, size); 505 506 blockcnt++; 507 close(f); 508done: 509 printf("wrote %d relocations in %d block%s\n", nbase_reloc, blockcnt, 510 blockcnt == 1 ? "" : "s"); 511} 512 513static 514int 515cmp_base_relocs( 516struct base_reloc *x1, 517struct base_reloc *x2) 518{ 519 if(x1->addr < x2->addr) 520 return(-1); 521 if(x1->addr == x2->addr) 522 return(0); 523 /* x1->addr > x2->addr */ 524 return(1); 525} 526