#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "stuff/bool.h"
#include "stuff/ofile.h"
#include "stuff/errors.h"
#include "stuff/reloc.h"
#include "coff/base_relocs.h"
#include "coff/bytesex.h"
#include "mach-o/x86_64/reloc.h"
char *progname = NULL;
static enum bool verbose = FALSE;
static enum byte_sex target_byte_sex;
static enum byte_sex host_byte_sex;
struct base_reloc {
uint64_t addr;
uint32_t type;
};
struct base_reloc *base_relocs = NULL;
uint32_t nbase_reloc = 0;
static void process(
struct ofile *ofile,
char *arch_name,
void *cookie);
static void gather_base_reloc_info(
uint32_t addr,
struct relocation_info *relocs,
uint32_t nreloc,
cpu_type_t cpu_type,
uint32_t length,
int macho_reloc_type,
int base_reloc_type);
static void add_base_reloc(
uint64_t addr,
uint32_t type);
static void output_base_relocs(
char *out);
static int cmp_base_relocs(
struct base_reloc *x1,
struct base_reloc *x2);
static void usage(
void);
extern char apple_version[];
char *version = apple_version;
int
main(
int argc,
char **argv,
char **envp)
{
int i;
char *input, *output;
progname = argv[0];
host_byte_sex = get_host_byte_sex();
input = NULL;
output = NULL;
for(i = 1; i < argc; i++){
if(strcmp(argv[i], "-v") == 0)
verbose = TRUE;
else if(input == NULL)
input = argv[i];
else if(output == NULL)
output = argv[i];
else
usage();
}
if(input == NULL){
warning("no input file specified");
usage();
}
if(output == NULL){
warning("no output file specified");
usage();
}
ofile_process(input, NULL, 0, FALSE, FALSE, FALSE, FALSE,
process, NULL);
if(errors != 0)
return(EXIT_FAILURE);
output_base_relocs(output);
if(errors == 0)
return(EXIT_SUCCESS);
else
return(EXIT_FAILURE);
}
static
void
process(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
uint32_t ncmds, i, j;
uint64_t addr, first_addr;
struct load_command *lc;
struct segment_command *sg;
struct segment_command_64 *sg64;
struct section *s;
struct section_64 *s64;
enum bool swapped;
struct relocation_info *relocs;
struct symtab_command *st;
struct dysymtab_command *dyst;
struct nlist *symbols;
struct nlist_64 *symbols64;
st = NULL;
dyst = NULL;
swapped = host_byte_sex != ofile->object_byte_sex;
target_byte_sex = ofile->object_byte_sex;
if(ofile->mh != NULL)
ncmds = ofile->mh->ncmds;
else
ncmds = ofile->mh64->ncmds;
lc = ofile->load_commands;
for(i = 0; i < ncmds; i++){
if(st == NULL && lc->cmd == LC_SYMTAB){
st = (struct symtab_command *)lc;
}
else if(dyst == NULL && lc->cmd == LC_DYSYMTAB){
dyst = (struct dysymtab_command *)lc;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(ofile->mh != NULL){
symbols = (struct nlist *)(ofile->object_addr + st->symoff);
if(swapped)
swap_nlist(symbols, st->nsyms, host_byte_sex);
symbols64 = NULL;
}
else{
symbols = NULL;
symbols64 = (struct nlist_64 *)(ofile->object_addr +st->symoff);
if(swapped)
swap_nlist_64(symbols64, st->nsyms, host_byte_sex);
}
first_addr = 0;
lc = ofile->load_commands;
for(i = 0; i < ncmds; i++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
if(first_addr == 0)
first_addr = sg->vmaddr;
s = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(j = 0; j < sg->nsects; j++){
relocs = (struct relocation_info *)(ofile->object_addr +
s[j].reloff);
if(swapped)
swap_relocation_info(relocs, s[j].nreloc,
host_byte_sex);
if(ofile->mh_cputype == CPU_TYPE_I386)
gather_base_reloc_info(s[j].addr, relocs, s[j].nreloc,
CPU_TYPE_I386, 2, GENERIC_RELOC_VANILLA,
IMAGE_REL_BASED_HIGHLOW);
else if(ofile->mh_cputype == CPU_TYPE_ARM)
gather_base_reloc_info(s[j].addr, relocs, s[j].nreloc,
CPU_TYPE_ARM, 2, GENERIC_RELOC_VANILLA,
IMAGE_REL_BASED_HIGHLOW);
else
fatal("unsupported cputype %d", ofile->mh_cputype);
if((s[j].flags & SECTION_TYPE) ==
S_NON_LAZY_SYMBOL_POINTERS){
for(addr = s[j].addr;
addr < s[j].addr + s[j].size;
addr += 4) {
add_base_reloc(addr, IMAGE_REL_BASED_HIGHLOW);
}
}
}
}
else if(lc->cmd == LC_SEGMENT_64){
sg64 = (struct segment_command_64 *)lc;
if(first_addr == 0)
first_addr = sg64->vmaddr;
s64 = (struct section_64 *)
((char *)sg64 + sizeof(struct segment_command_64));
for(j = 0; j < sg64->nsects; j++){
relocs = (struct relocation_info *)(ofile->object_addr +
s64[j].reloff);
if(swapped)
swap_relocation_info(relocs, s64[j].nreloc,
host_byte_sex);
if(ofile->mh_cputype == CPU_TYPE_X86_64)
gather_base_reloc_info(s64[j].addr, relocs,
s64[j].nreloc, CPU_TYPE_X86_64, 3,
X86_64_RELOC_UNSIGNED, IMAGE_REL_BASED_DIR64);
else
fatal("unsupported cputype %d", ofile->mh_cputype);
if((s64[j].flags & SECTION_TYPE) ==
S_NON_LAZY_SYMBOL_POINTERS){
for(addr = s64[j].addr;
addr < s64[j].addr + s64[j].size;
addr += 8) {
add_base_reloc(addr, IMAGE_REL_BASED_DIR64);
}
}
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(dyst != NULL && dyst->nlocrel != 0){
relocs = (struct relocation_info *)(ofile->object_addr +
dyst->locreloff);
if(swapped)
swap_relocation_info(relocs, dyst->nlocrel, host_byte_sex);
if(ofile->mh_cputype == CPU_TYPE_I386)
gather_base_reloc_info(first_addr, relocs, dyst->nlocrel,
CPU_TYPE_I386, 2, GENERIC_RELOC_VANILLA,
IMAGE_REL_BASED_HIGHLOW);
if(ofile->mh_cputype == CPU_TYPE_ARM)
gather_base_reloc_info(first_addr, relocs, dyst->nlocrel,
CPU_TYPE_ARM, 2, GENERIC_RELOC_VANILLA,
IMAGE_REL_BASED_HIGHLOW);
else if(ofile->mh_cputype == CPU_TYPE_X86_64)
gather_base_reloc_info(first_addr, relocs, dyst->nlocrel,
CPU_TYPE_X86_64, 3, X86_64_RELOC_UNSIGNED,
IMAGE_REL_BASED_DIR64);
else
fatal("unsupported cputype %d", ofile->mh_cputype);
}
if(dyst != NULL && dyst->nextrel != 0)
fatal("input Mach-O file has external relocation entries");
}
static
void
gather_base_reloc_info(
uint32_t addr,
struct relocation_info *relocs,
uint32_t nreloc,
cpu_type_t cpu_type,
uint32_t length,
int macho_reloc_type,
int base_reloc_type)
{
uint32_t i, r_address, r_pcrel, r_length, r_extern, r_type;
struct scattered_relocation_info *sreloc;
for(i = 0; i < nreloc; i++){
if((relocs[i].r_address & R_SCATTERED) != 0){
sreloc = (struct scattered_relocation_info *)(relocs + i);
r_address = sreloc->r_address;
r_pcrel = sreloc->r_pcrel;
r_length = sreloc->r_length;
r_type = (enum reloc_type_generic)sreloc->r_type;
r_extern = 0;
}
else{
r_address = relocs[i].r_address;
r_pcrel = relocs[i].r_pcrel;
r_length = relocs[i].r_length;
r_extern = relocs[i].r_extern;
r_type = (enum reloc_type_generic)relocs[i].r_type;
}
if(r_extern == 0 && r_pcrel == 0 &&
r_length == length && r_type == macho_reloc_type)
add_base_reloc(addr + r_address, base_reloc_type);
else
;
if((relocs[i].r_address & R_SCATTERED) == 0){
if(reloc_has_pair(cpu_type, relocs[i].r_type))
i++;
}
else{
sreloc = (struct scattered_relocation_info *)relocs + i;
if(reloc_has_pair(cpu_type, sreloc->r_type))
i++;
}
}
}
static
void
add_base_reloc(
uint64_t addr,
uint32_t type)
{
static int max = 0;
struct base_reloc *new_base_relocs;
if(!max){
max = 128;
base_relocs = (struct base_reloc *)
malloc(max * sizeof(struct base_reloc));
}
if(nbase_reloc >= max){
new_base_relocs = malloc(2 * max * sizeof(struct base_reloc));
memcpy(new_base_relocs, base_relocs,
max * sizeof(struct base_reloc));
max *= 2;
free(base_relocs);
base_relocs = new_base_relocs;
}
base_relocs[nbase_reloc].addr = addr;
base_relocs[nbase_reloc].type = type;
nbase_reloc++;
}
static
void
usage(
void)
{
fprintf(stderr, "Usage: %s [-v] input_Mach-O output_relocs\n",
progname);
exit(EXIT_FAILURE);
}
#define MAX_BLOCK_OFFSET 0x1000
#define BLOCK_MASK (MAX_BLOCK_OFFSET-1)
static
void
output_base_relocs(
char *out)
{
int blockcnt;
int i, entries;
uint64_t base;
int size;
char *fb;
struct base_relocation_block_header *h;
struct base_relocation_entry *b;
int f;
uint32_t offset;
enum bool swapped;
blockcnt = 0;
swapped = host_byte_sex != target_byte_sex;
if(nbase_reloc == 0)
goto done;
qsort(base_relocs, nbase_reloc, sizeof(struct base_reloc),
(int (*)(const void *, const void *))cmp_base_relocs);
size = sizeof(struct base_relocation_block_header) +
(nbase_reloc + 1) * sizeof(struct base_relocation_entry);
fb = malloc(size);
f = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if(f == -1){
fatal("open output file");
}
entries = 0;
base = base_relocs[0].addr & ~BLOCK_MASK;
h = (struct base_relocation_block_header *)fb;
b = (struct base_relocation_entry *)
(fb + sizeof(struct base_relocation_block_header));
for(i = 0; i < nbase_reloc; i++){
offset = base_relocs[i].addr - base;
if(offset >= MAX_BLOCK_OFFSET) {
if((entries % 2) != 0){
b[entries].type = IMAGE_REL_BASED_ABSOLUTE;
b[entries].offset = 0;
entries++;
}
h->page_rva = base;
size = sizeof(struct base_relocation_block_header) +
entries * sizeof(struct base_relocation_entry);
h->block_size = size;
if(swapped){
swap_base_relocation_block_header(h,
target_byte_sex);
swap_base_relocation_entry(b, entries,
target_byte_sex);
}
write(f, fb, size);
entries = 0;
blockcnt++;
base = base_relocs[i].addr & ~BLOCK_MASK;
offset = base_relocs[i].addr - base;
}
b[entries].type = base_relocs[i].type;
b[entries].offset = offset;
entries++;
}
if((entries % 2) != 0){
b[entries].type = IMAGE_REL_BASED_ABSOLUTE;
b[entries].offset = 0;
entries++;
}
h->page_rva = base;
size = sizeof(struct base_relocation_block_header) +
entries * sizeof(struct base_relocation_entry);
h->block_size = size;
if(swapped){
swap_base_relocation_block_header(h, target_byte_sex);
swap_base_relocation_entry(b, entries, target_byte_sex);
}
write(f, fb, size);
blockcnt++;
close(f);
done:
printf("wrote %d relocations in %d block%s\n", nbase_reloc, blockcnt,
blockcnt == 1 ? "" : "s");
}
static
int
cmp_base_relocs(
struct base_reloc *x1,
struct base_reloc *x2)
{
if(x1->addr < x2->addr)
return(-1);
if(x1->addr == x2->addr)
return(0);
return(1);
}