#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <mach/mach.h>
#include <libc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "libgcc.h"
#include "hack_libgcc.h"
#include "stuff/bool.h"
#include "stuff/allocate.h"
#include "stuff/breakout.h"
#include "stuff/hash_string.h"
#include "stuff/errors.h"
#include "stuff/round.h"
#undef DEBUG
#define NEW_LIBCGG_PREFIX "__libgcc_private_extern"
#define OLD_LIBCGG_PREFIX "__old_libgcc"
__private_extern__
char *progname = NULL;
char *spec_filename = NULL;
static struct arch_flag arch_flag = { 0 };
struct object_info objects = { 0 };
static enum bool input_changed = FALSE;
static enum bool set_private_extern = FALSE;
struct symbol {
char *name;
char *prefixed_name;
struct symbol *next;
};
#define SYMBOL_HASH_SIZE 250
static struct symbol *new_symbol_hash[SYMBOL_HASH_SIZE];
static struct symbol *old_symbol_hash[SYMBOL_HASH_SIZE];
#define INITIAL_STRING_TABLE_SIZE 40960
static struct {
char *strings;
unsigned long size;
unsigned long index;
} string_table;
static void usage(
void);
static void process(
char *filename,
enum bool first_pass,
enum libgcc libgcc);
static void translate_input(
struct arch *archs,
unsigned long narchs,
enum libgcc libgcc);
static void translate_object(
struct arch *arch,
struct member *member,
struct object *object,
enum libgcc libgcc);
static char * lookup_new_symbol(
char *name,
enum bool add);
static char * lookup_old_symbol(
char *name,
enum bool add);
static void start_string_table(
void);
static long add_to_string_table(
char *p);
static void end_string_table(
void);
int
main(
int argc,
char *argv[],
char *envp[])
{
unsigned long i, j, nfiles;
char *p, *filelist, *dirname, *addr;
int fd;
struct stat stat_buf;
enum libgcc libgcc;
kern_return_t r;
progname = argv[0];
libgcc = NEW_LIBGCC;
for(i = 1; i < argc; i++){
if(strcmp(argv[i], "-arch") == 0){
if(arch_flag.name != NULL)
fatal("only one -arch can be specified");
if(i + 1 >= argc){
error("missing argument(s) to %s option", argv[i]);
usage();
}
if(strcmp("all", argv[i+1]) == 0)
fatal("-arch all can't be specified");
if(get_arch_from_flag(argv[i+1], &arch_flag) == 0){
error("unknown architecture specification flag: "
"%s %s", argv[i], argv[i+1]);
arch_usage();
usage();
}
i++;
}
else if(strcmp(argv[i], "-filelist") == 0){
if(spec_filename != NULL){
error("can't specify both -s <specfile> and -filelist "
"arguments");
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
filelist = argv[i + 1];
dirname = strrchr(filelist, ',');
if(dirname != NULL){
*dirname = '\0';
dirname++;
}
else
dirname = "";
if((fd = open(filelist, O_RDONLY, 0)) == -1)
system_fatal("can't open file list file: %s", filelist);
if(fstat(fd, &stat_buf) == -1)
system_fatal("can't stat file list file: %s", filelist);
if(stat_buf.st_size != 0){
if((r = map_fd((int)fd, (vm_offset_t)0,
(vm_offset_t *)&(addr), (boolean_t)TRUE,
(vm_size_t)stat_buf.st_size)) != KERN_SUCCESS)
mach_fatal(r, "can't map file list file: %s",
filelist);
}
else{
fatal("file list file: %s is empty", filelist);
}
if(*dirname != '\0')
dirname[-1] = ',';
close(fd);
nfiles = 0;
for(j = 0; j < stat_buf.st_size; j++){
if(addr[j] == '\n')
nfiles++;
}
if(addr[stat_buf.st_size - 1] != '\n')
nfiles++;
p = allocate((strlen(dirname) + 1) * nfiles +
stat_buf.st_size + 1);
objects.names = reallocate(objects.names,
sizeof(char *) * (objects.nobjects + nfiles));
objects.filelists = reallocate(objects.filelists,
sizeof(char *) * (objects.nobjects + nfiles));
objects.libgcc = reallocate(objects.libgcc,
sizeof(enum libgcc) * (objects.nobjects + nfiles));
objects.names[objects.nobjects] = p;
objects.filelists[objects.nobjects] = filelist;
objects.libgcc[objects.nobjects] = libgcc;
objects.nobjects++;
if(*dirname != '\0'){
strcpy(p, dirname);
p += strlen(dirname);
*p++ = '/';
}
for(j = 0; j < stat_buf.st_size; j++){
if(addr[j] != '\n')
*p++ = addr[j];
else{
*p++ = '\0';
if(j != stat_buf.st_size - 1){
objects.names[objects.nobjects] = p;
objects.filelists[objects.nobjects] = filelist;
objects.libgcc[objects.nobjects] = libgcc;
objects.nobjects++;
if(*dirname != '\0'){
strcpy(p, dirname);
p += strlen(dirname);
*p++ = '/';
}
}
}
}
if(addr[stat_buf.st_size - 1] != '\n')
*p = '\0';
i++;
libgcc = NOT_LIBGCC;
}
else if(strcmp(argv[i], "-s") == 0){
if(objects.nobjects != 0){
error("can't specify both -s <specfile> and -filelist "
"arguments");
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(spec_filename != NULL){
error("only one -s <specfile> argument can be specified");
}
spec_filename = argv[i + 1];
i++;
}
else if(strcmp(argv[i], "-p") == 0){
set_private_extern = TRUE;
}
else{
error("unknown argument: %s", argv[i]);
usage();
}
}
if(spec_filename != NULL){
load_objects_from_specfile();
}
if(arch_flag.name == NULL){
error("-arch must be specified");
usage();
}
if(objects.nobjects == 0){
error("no -filelist arguments or -s <specfile> with #objects "
"specified");
usage();
}
for(i = 0; i < objects.nobjects; i++){
if(objects.libgcc[i] == NEW_LIBGCC ||
objects.libgcc[i] == OLD_LIBGCC)
process(objects.names[i], TRUE, objects.libgcc[i]);
}
for(i = 0; i < objects.nobjects; i++){
if(objects.libgcc[i] == NEW_LIBGCC ||
objects.libgcc[i] == OLD_LIBGCC)
process(objects.names[i], FALSE, objects.libgcc[i]);
}
for(i = 0; i < objects.nobjects; i++){
if(objects.libgcc[i] == NOT_LIBGCC)
process(objects.names[i], FALSE, objects.libgcc[i]);
}
if(errors)
return(EXIT_FAILURE);
else
return(EXIT_SUCCESS);
}
static
void
usage(
void)
{
fprintf(stderr, "Usage: %s [-p] -arch <arch_flag> "
"-filelist filename[,dirname] ... OR\n", progname);
fprintf(stderr, "Usage: %s -arch <arch_flag> -s <specfile>\n",progname);
exit(EXIT_FAILURE);
}
static
void
process(
char *filename,
enum bool first_pass,
enum libgcc libgcc)
{
struct arch *archs;
unsigned long narchs;
struct stat stat_buf;
#ifdef DEBUG
printf("process(%s,",filename);
switch(libgcc){
case NOT_LIBGCC:
printf("NOT_LIBGCC)\n");
break;
case NEW_LIBGCC:
printf("NEW_LIBGCC)\n");
break;
case OLD_LIBGCC:
printf("OLD_LIBGCC)\n");
break;
}
#endif
archs = NULL;
narchs = 0;
input_changed = FALSE;
breakout(filename, &archs, &narchs, FALSE);
if(errors)
exit(EXIT_FAILURE);
checkout(archs, narchs);
translate_input(archs, narchs, libgcc);
if(errors)
exit(EXIT_FAILURE);
if(first_pass == FALSE && input_changed == TRUE){
#ifdef DEBUG
#endif
printf("process(%s) was changed\n", filename);
if(stat(filename, &stat_buf) == -1)
system_error("can't stat file: %s", filename);
writeout(archs, narchs, filename, stat_buf.st_mode & 0777, TRUE,
FALSE, FALSE, NULL);
}
if(errors)
exit(EXIT_FAILURE);
}
static
void
translate_input(
struct arch *archs,
unsigned long narchs,
enum libgcc libgcc)
{
unsigned long i, j, offset, size;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
for(i = 0; i < narchs; i++){
#ifdef DEBUG
printf("translate_input(arch = %lu, %s)\n", i, archs[i].file_name);
#endif
cputype = 0;
cpusubtype = 0;
if(archs[i].type == OFILE_ARCHIVE){
for(j = 0; j < archs[i].nmembers; j++){
if(archs[i].members[j].type == OFILE_Mach_O){
cputype = archs[i].members[j].object->mh->cputype;
cpusubtype = archs[i].members[j].object->mh->cpusubtype;
break;
}
}
}
else if(archs[i].type == OFILE_Mach_O){
cputype = archs[i].object->mh->cputype;
cpusubtype = archs[i].object->mh->cpusubtype;
}
else if(archs[i].fat_arch != NULL){
cputype = archs[i].fat_arch->cputype;
cpusubtype = archs[i].fat_arch->cpusubtype;
}
if(arch_flag.cputype != cputype ||
arch_flag.cpusubtype != cpusubtype)
continue;
if(archs[i].type == OFILE_ARCHIVE){
for(j = 0; j < archs[i].nmembers; j++){
if(archs[i].members[j].type == OFILE_Mach_O){
translate_object(archs + i, archs[i].members + j,
archs[i].members[j].object, libgcc);
}
}
offset = 0;
for(j = 0; j < archs[i].nmembers; j++){
archs[i].members[j].offset = offset;
if(archs[i].members[j].object != NULL){
size = archs[i].members[j].object->object_size
- archs[i].members[j].object->input_sym_info_size
+ archs[i].members[j].object->output_sym_info_size;
sprintf(archs[i].members[j].ar_hdr->ar_size, "%-*ld",
(int)sizeof(archs[i].members[j].ar_hdr->ar_size),
(long)(size));
memcpy(archs[i].members[j].ar_hdr->ar_fmag, ARFMAG,
(int)sizeof(archs[i].members[j].ar_hdr->ar_fmag));
}
else{
size = archs[i].members[j].unknown_size;
}
offset += sizeof(struct ar_hdr) + size;
}
archs[i].library_size = offset;
}
else if(archs[i].type == OFILE_Mach_O){
translate_object(archs + i, NULL, archs[i].object, libgcc);
}
else {
fatal_arch(archs + i, NULL, "can't process non-object and "
"non-archive file: ");
}
}
}
static
void
translate_object(
struct arch *arch,
struct member *member,
struct object *object,
enum libgcc libgcc)
{
unsigned long i;
enum byte_sex host_byte_sex;
struct nlist *symbols, *nlistp;
unsigned long nsyms, offset;
char *strings, *p;
unsigned long strings_size;
#ifdef DEBUG
printf("translate_object(%s)\n", arch->file_name);
#endif
if(object->mh->filetype != MH_OBJECT){
fatal_arch(arch, member,"is not an MH_OBJECT file (invalid input)");
return;
}
host_byte_sex = get_host_byte_sex();
if(object->st == NULL || object->st->nsyms == 0)
return;
symbols = (struct nlist *)(object->object_addr + object->st->symoff);
nsyms = object->st->nsyms;
if(object->object_byte_sex != host_byte_sex)
swap_nlist(symbols, nsyms, host_byte_sex);
strings = object->object_addr + object->st->stroff;
strings_size = object->st->strsize;
object->output_symbols = symbols;
object->output_nsymbols = nsyms;
object->input_sym_info_size = nsyms * sizeof(struct nlist) +
object->st->strsize;
if(object->dyst != NULL){
object->input_sym_info_size +=
object->dyst->nindirectsyms * sizeof(unsigned long);
}
start_string_table();
nlistp = symbols;
for(i = 0; i < nsyms; i++){
if(nlistp->n_type & N_EXT){
if(nlistp->n_un.n_strx){
if(nlistp->n_un.n_strx > 0 &&
nlistp->n_un.n_strx < strings_size){
if(libgcc == OLD_LIBGCC){
p = lookup_old_symbol(strings + nlistp->n_un.n_strx,
((nlistp->n_type & N_TYPE) != N_UNDF));
if(p != NULL){
nlistp->n_un.n_strx = add_to_string_table(p);
}
else{
nlistp->n_un.n_strx = add_to_string_table(
strings + nlistp->n_un.n_strx);
}
}
else{
p = lookup_new_symbol(strings + nlistp->n_un.n_strx,
libgcc == NEW_LIBGCC &&
((nlistp->n_type & N_TYPE) != N_UNDF));
if(p != NULL){
nlistp->n_un.n_strx = add_to_string_table(p);
}
else{
nlistp->n_un.n_strx = add_to_string_table(
strings + nlistp->n_un.n_strx);
}
if(libgcc == NEW_LIBGCC &&
set_private_extern == TRUE &&
(nlistp->n_type & N_TYPE) != N_UNDF &&
(nlistp->n_type & N_PEXT) != N_PEXT){
nlistp->n_type |= N_PEXT;
input_changed = TRUE;
#ifdef DEBUG
printf("translate_object(%s) setting N_PEXT on "
"%s\n", arch->file_name, strings +
nlistp->n_un.n_strx);
#endif
}
}
}
else
fatal_arch(arch, member, "bad string table "
"index in symbol %lu in: ", i);
}
}
else{
if(nlistp->n_un.n_strx){
if(nlistp->n_un.n_strx > 0 && nlistp->n_un.n_strx <
strings_size)
nlistp->n_un.n_strx = add_to_string_table(
strings + nlistp->n_un.n_strx);
else
fatal_arch(arch, member, "bad string table "
"index in symbol %lu in: ", i);
}
}
nlistp++;
}
end_string_table();
object->output_strings = allocate(string_table.index);
memcpy(object->output_strings, string_table.strings,string_table.index);
object->output_strings_size = string_table.index;
object->output_sym_info_size =
nsyms * sizeof(struct nlist) +
string_table.index;
if(object->dyst != NULL){
object->output_sym_info_size +=
object->dyst->nindirectsyms * sizeof(unsigned long);
}
if(object->seg_linkedit != NULL){
object->seg_linkedit->filesize += object->output_sym_info_size -
object->input_sym_info_size;
object->seg_linkedit->vmsize = object->seg_linkedit->filesize;
}
if(object->dyst != NULL){
object->st->nsyms = nsyms;
object->st->strsize = string_table.index;
offset = ULONG_MAX;
if(object->st->nsyms != 0 &&
object->st->symoff < offset)
offset = object->st->symoff;
if(object->dyst->nindirectsyms != 0 &&
object->dyst->indirectsymoff < offset)
offset = object->dyst->indirectsymoff;
if(object->st->strsize != 0 &&
object->st->stroff < offset)
offset = object->st->stroff;
if(object->st->nsyms != 0){
object->st->symoff = offset;
offset += object->st->nsyms * sizeof(struct nlist);
}
else
object->st->symoff = 0;
if(object->dyst->nindirectsyms != 0){
object->output_indirect_symtab = (unsigned long *)
(object->object_addr + object->dyst->indirectsymoff);
object->dyst->indirectsymoff = offset;
offset += object->dyst->nindirectsyms *
sizeof(unsigned long);
}
else
object->dyst->indirectsymoff = 0;;
if(object->st->strsize != 0){
object->st->stroff = offset;
offset += object->st->strsize;
}
else
object->st->stroff = 0;
}
else{
object->st->nsyms = nsyms;
object->st->stroff = object->st->symoff +
nsyms * sizeof(struct nlist);
object->st->strsize = string_table.index;
}
}
static
char *
lookup_new_symbol(
char *name,
enum bool add)
{
char *p, *q;
long hash_key;
struct symbol *sp;
#ifdef DEBUG
printf("lookup_new_symbol(%s, %s)\n", name,
add == TRUE ? "TRUE" : "FALSE");
#endif
if(strncmp(name, NEW_LIBCGG_PREFIX, strlen(NEW_LIBCGG_PREFIX)) == 0)
p = name + strlen(NEW_LIBCGG_PREFIX);
else
p = name;
hash_key = hash_string(p) % SYMBOL_HASH_SIZE;
sp = new_symbol_hash[hash_key];
while(sp != NULL){
if(strcmp(p, sp->name) == 0){
if(p == name)
input_changed = TRUE;
return(sp->prefixed_name);
}
sp = sp->next;
}
if(add == TRUE){
#ifdef DEBUG
printf("lookup_new_symbol adding %s\n", name);
#endif
sp = (struct symbol *)allocate(sizeof(struct symbol));
q = allocate(strlen(NEW_LIBCGG_PREFIX) + strlen(p) + 1);
strcpy(q, NEW_LIBCGG_PREFIX);
strcat(q, p);
sp->name = q + strlen(NEW_LIBCGG_PREFIX);
sp->prefixed_name = q;
sp->next = new_symbol_hash[hash_key];
new_symbol_hash[hash_key] = sp;
if(p == name)
input_changed = TRUE;
return(sp->prefixed_name);
}
return(NULL);
}
static
char *
lookup_old_symbol(
char *name,
enum bool add)
{
char *p, *q;
long hash_key;
struct symbol *sp;
#ifdef DEBUG
printf("lookup_old_symbol(%s, %s)\n", name,
add == TRUE ? "TRUE" : "FALSE");
#endif
if(strncmp(name, OLD_LIBCGG_PREFIX, strlen(OLD_LIBCGG_PREFIX)) == 0)
p = name + strlen(OLD_LIBCGG_PREFIX);
else
p = name;
hash_key = hash_string(p) % SYMBOL_HASH_SIZE;
sp = old_symbol_hash[hash_key];
while(sp != NULL){
if(strcmp(p, sp->name) == 0){
if(p == name)
input_changed = TRUE;
return(sp->prefixed_name);
}
sp = sp->next;
}
if(add == TRUE){
#ifdef DEBUG
printf("lookup_old_symbol adding %s\n", name);
#endif
sp = (struct symbol *)allocate(sizeof(struct symbol));
q = allocate(strlen(OLD_LIBCGG_PREFIX) + strlen(p) + 1);
strcpy(q, OLD_LIBCGG_PREFIX);
strcat(q, p);
sp->name = q + strlen(OLD_LIBCGG_PREFIX);
sp->prefixed_name = q;
sp->next = old_symbol_hash[hash_key];
old_symbol_hash[hash_key] = sp;
if(p == name)
input_changed = TRUE;
return(sp->prefixed_name);
}
return(NULL);
}
static
void
start_string_table()
{
if(string_table.size == 0){
string_table.size = INITIAL_STRING_TABLE_SIZE;
string_table.strings = (char *)allocate(string_table.size);
}
memset(string_table.strings, '\0', sizeof(long));
string_table.index = sizeof(long);
}
static
long
add_to_string_table(
char *p)
{
long len, index;
len = strlen(p) + 1;
if(string_table.size < string_table.index + len){
string_table.strings = (char *)reallocate(string_table.strings,
string_table.size * 2);
string_table.size *= 2;
}
index = string_table.index;
strcpy(string_table.strings + string_table.index, p);
string_table.index += len;
return(index);
}
static
void
end_string_table()
{
unsigned long length;
length = round(string_table.index, sizeof(unsigned long));
memset(string_table.strings + string_table.index, '\0',
length - string_table.index);
string_table.index = length;
}