#include <mach/mach.h>
#include "stuff/openstep_mach.h"
#include <libc.h>
#ifndef __OPENSTEP__
#include <utime.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ar.h>
#include <mach-o/ranlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "stuff/bool.h"
#include "stuff/ofile.h"
#include "stuff/rnd.h"
#include "stuff/errors.h"
#include "stuff/allocate.h"
#include "stuff/execute.h"
#include "stuff/version_number.h"
#include "stuff/unix_standard_mode.h"
#ifdef LTO_SUPPORT
#include "stuff/lto.h"
#endif
#include <mach/mach_init.h>
#if defined(__OPENSTEP__) || defined(__GONZO_BUNSEN_BEAKER__)
#include <servers/netname.h>
#else
#include <servers/bootstrap.h>
#endif
struct toc {
char *name;
int32_t index1;
};
char *progname = NULL;
static enum byte_sex host_byte_sex = UNKNOWN_BYTE_SEX;
static time_t toc_time = 0;
static enum bool zero_ar_date = FALSE;
static u_short toc_mode = 0;
struct cmd_flags {
char **files;
uint32_t
nfiles;
char **filelist;
enum bool
no_files_ok;
enum bool ranlib;
enum bool s;
enum bool a;
enum bool c;
enum bool t;
enum bool f;
enum bool q;
char *output;
enum bool final_output_specified;
enum bool dynamic;
char *compatibility;
char *current;
char *install_name;
char *seg1addr;
char *segs_read_only_addr;
char *segs_read_write_addr;
char *seg_addr_table;
char *seg_addr_table_filename;
char **Ldirs;
uint32_t
nLdirs;
char **ldflags;
uint32_t
nldflags;
enum bool verbose;
struct arch_flag
arch_only_flag;
enum bool
prebinding_flag_specified;
enum bool
prebinding;
enum bool
all_load_flag_specified;
enum bool
all_load;
enum bool
use_long_names;
enum bool L_or_T_specified;
enum bool
ld_trace_archives;
const char *
trace_file_path;
enum bool
search_paths_first;
enum bool noflush;
uint32_t debug;
};
static struct cmd_flags cmd_flags = { 0 };
static char *next_root = NULL;
char *standard_dirs[] = {
"/lib/",
"/usr/lib/",
"/usr/local/lib/",
NULL
};
static struct arch *archs = NULL;
static uint32_t narchs = 0;
struct arch {
struct arch_flag arch_flag;
uint32_t size;
uint32_t toc_size;
struct ar_hdr toc_ar_hdr;
enum bool toc_long_name;
char *toc_name;
uint32_t toc_name_size;
struct toc *tocs;
struct ranlib *toc_ranlibs;
uint32_t toc_nranlibs;
char *toc_strings;
uint32_t toc_strsize;
struct member *members;
uint32_t nmembers;
};
struct member {
uint32_t offset;
struct ar_hdr ar_hdr;
char null_byte;
char *object_addr;
uint32_t object_size;
enum byte_sex object_byte_sex;
struct mach_header *mh;
struct mach_header_64 *mh64;
struct load_command
*load_commands;
struct symtab_command *st;
struct section **sections;
struct section_64 **sections64;
#ifdef LTO_SUPPORT
void *lto;
#endif
char *member_name;
uint32_t member_name_size;
enum bool output_long_name;
char *input_file_name;
char *input_base_name;
uint32_t input_base_name_size;
struct ar_hdr *input_ar_hdr;
};
static void usage(
void);
static void process(
void);
static char *file_name_from_l_flag(
char *l_flag);
static char *search_for_file(
char *base_name);
static char * search_paths_for_lname(
const char *lname_argument);
static char * search_path_for_lname(
const char *dir,
const char *lname_argument);
static void add_member(
struct ofile *ofile);
static void free_archs(
void);
static void create_library(
char *output);
static void create_dynamic_shared_library(
char *output);
static void create_dynamic_shared_library_cleanup(
int sig);
static void make_table_of_contents(
struct arch *arch,
char *output);
static int toc_name_qsort(
const struct toc *toc1,
const struct toc *toc2);
static int toc_index1_qsort(
const struct toc *toc1,
const struct toc *toc2);
static enum bool toc_symbol(
struct nlist *symbol,
struct section **sections);
static enum bool toc_symbol_64(
struct nlist_64 *symbol64,
struct section_64 **sections64);
static enum bool toc(
uint32_t n_strx,
uint8_t n_type,
uint64_t n_value,
enum bool attr_no_toc);
static enum bool check_sort_tocs(
struct arch *arch,
char *output,
enum bool library_warnings);
static void warn_duplicate_member_names(
void);
static int member_name_qsort(
const struct member *member1,
const struct member *member2);
static int member_offset_qsort(
const struct member *member1,
const struct member *member2);
static void warn_member(
struct arch *arch,
struct member *member,
const char *format, ...) __attribute__ ((format (printf, 3, 4)));
static void ld_trace(
const char *format, ...) __attribute__ ((format (printf, 1, 2)));
static struct block {
uint32_t offset;
uint32_t size;
uint32_t written_offset;
uint32_t written_size;
struct block *next;
} *output_blocks;
static void output_flush(
char *library,
uint32_t library_size,
int fd,
uint32_t offset,
uint32_t size);
static void final_output_flush(
char *library,
int fd);
#ifdef DEBUG
static void print_block_list(void);
#endif
static struct block *get_block(void);
static void remove_block(
struct block *block);
static uint32_t trnc(
uint32_t v,
uint32_t r);
extern char apple_version[];
int
main(
int argc,
char **argv,
char **envp)
{
char *p, *endp, *filelist, *dirname, *addr;
int fd, i;
struct stat stat_buf;
uint32_t j, nfiles, maxfiles;
uint32_t temp;
int oumask, numask;
enum bool lflags_seen, bad_flag_seen, Vflag;
lflags_seen = FALSE;
Vflag = FALSE;
progname = argv[0];
host_byte_sex = get_host_byte_sex();
if(getenv("ZERO_AR_DATE") == NULL)
zero_ar_date = FALSE;
else
zero_ar_date = TRUE;
if(zero_ar_date == FALSE)
toc_time = time(0);
else
toc_time = 0;
numask = 0;
oumask = umask(numask);
toc_mode = S_IFREG | (0666 & ~oumask);
(void)umask(oumask);
p = strrchr(argv[0], '/');
if(p != NULL)
p++;
else
p = argv[0];
if(strncmp(p, "ranlib", sizeof("ranlib") - 1) == 0)
cmd_flags.ranlib = TRUE;
cmd_flags.use_long_names = TRUE;
maxfiles = argc;
cmd_flags.files = allocate(sizeof(char *) * maxfiles);
cmd_flags.filelist = allocate(sizeof(char *) * maxfiles);
memset(cmd_flags.filelist, '\0', sizeof(char *) * maxfiles);
for(i = 1; i < argc; i++){
if(argv[i][0] == '-'){
if(argv[i][1] == '\0'){
for(i += 1 ; i < argc; i++)
cmd_flags.files[cmd_flags.nfiles++] = argv[i];
break;
}
if(strcmp(argv[i], "-o") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.output != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
cmd_flags.output = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-arch_only") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to %s option", argv[i]);
usage();
}
if(cmd_flags.arch_only_flag.name != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
else{
if(get_arch_from_flag(argv[i+1],
&cmd_flags.arch_only_flag) == 0){
error("unknown architecture specification flag: "
"%s %s", argv[i], argv[i+1]);
arch_usage();
usage();
}
}
i++;
}
else if(strcmp(argv[i], "-dynamic") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
cmd_flags.dynamic = TRUE;
}
else if(strcmp(argv[i], "-static") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
cmd_flags.dynamic = FALSE;
}
else if(strcmp(argv[i], "-filelist") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
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);
addr = NULL;
if(stat_buf.st_size != 0){
addr = mmap(0, stat_buf.st_size, PROT_READ|PROT_WRITE,
MAP_FILE|MAP_PRIVATE, fd, 0);
if((intptr_t)addr == -1)
system_error("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);
cmd_flags.files = reallocate(cmd_flags.files,
sizeof(char *) * (maxfiles + nfiles));
cmd_flags.filelist = reallocate(cmd_flags.filelist,
sizeof(char *) * (maxfiles + nfiles));
memset(cmd_flags.filelist + maxfiles, '\0',
sizeof(char *) * nfiles);
maxfiles += nfiles;
cmd_flags.files[cmd_flags.nfiles] = p;
cmd_flags.filelist[cmd_flags.nfiles] = filelist;
cmd_flags.nfiles++;
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){
cmd_flags.files[cmd_flags.nfiles] = p;
cmd_flags.filelist[cmd_flags.nfiles] =argv[i+1];
cmd_flags.nfiles++;
if(*dirname != '\0'){
strcpy(p, dirname);
p += strlen(dirname);
*p++ = '/';
}
}
}
}
if(addr[stat_buf.st_size - 1] != '\n')
*p = '\0';
i++;
}
else if(strcmp(argv[i], "-compatibility_version") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.compatibility != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
if(get_version_number(argv[i], argv[i+1], &temp) == FALSE){
usage();
}
cmd_flags.compatibility = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-current_version") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.current != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
if(get_version_number(argv[i], argv[i+1], &temp) == FALSE){
usage();
}
cmd_flags.current = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-install_name") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.install_name != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
cmd_flags.install_name = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-seg1addr") == 0 ||
strcmp(argv[i], "-image_base") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.seg1addr != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
temp = strtoul(argv[i + 1], &endp, 16);
if(*endp != '\0'){
error("address for -seg1addr %s not a proper "
"hexadecimal number", argv[i+1]);
usage();
}
cmd_flags.seg1addr = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-segs_read_only_addr") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.segs_read_only_addr != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
temp = strtoul(argv[i + 1], &endp, 16);
if(*endp != '\0'){
error("address for -segs_read_only_addr %s not a "
"proper hexadecimal number", argv[i+1]);
usage();
}
cmd_flags.segs_read_only_addr = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-segs_read_write_addr") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.segs_read_write_addr != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
temp = strtoul(argv[i + 1], &endp, 16);
if(*endp != '\0'){
error("address for -segs_read_write_addr %s not a "
"proper hexadecimal number", argv[i+1]);
usage();
}
cmd_flags.segs_read_write_addr = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-seg_addr_table") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.seg_addr_table != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
cmd_flags.seg_addr_table = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-seg_addr_table_filename") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(cmd_flags.seg_addr_table_filename != NULL){
error("more than one: %s option specified", argv[i]);
usage();
}
cmd_flags.seg_addr_table_filename = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-syslibroot") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 == argc){
error("missing argument to: %s option", argv[i]);
usage();
}
if(next_root != NULL && strcmp(next_root, argv[i+1]) != 0){
error("more than one: %s option specified", argv[i]);
usage();
}
next_root = argv[i+1];
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 2));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i+1];
i++;
}
else if(strcmp(argv[i], "-sectcreate") == 0 ||
strcmp(argv[i], "-segcreate") == 0 ||
strcmp(argv[i], "-sectorder") == 0 ||
strcmp(argv[i], "-sectalign") == 0 ||
strcmp(argv[i], "-segprot") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 3 >= argc){
error("not enough arguments follow %s", argv[i]);
usage();
}
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 4));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i+1];
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i+2];
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i+3];
if(strcmp(argv[i], "-sectcreate") == 0 ||
strcmp(argv[i], "-segcreate") == 0)
cmd_flags.no_files_ok = TRUE;
i += 3;
}
else if(strcmp(argv[i], "-segalign") == 0 ||
strcmp(argv[i], "-undefined") == 0 ||
strcmp(argv[i], "-macosx_version_min") == 0 ||
strcmp(argv[i], "-multiply_defined") == 0 ||
strcmp(argv[i], "-multiply_defined_unused") == 0 ||
strcmp(argv[i], "-umbrella") == 0 ||
strcmp(argv[i], "-sub_umbrella") == 0 ||
strcmp(argv[i], "-sub_library") == 0 ||
strcmp(argv[i], "-allowable_client") == 0 ||
strcmp(argv[i], "-read_only_relocs") == 0 ||
strcmp(argv[i], "-init") == 0 ||
strcmp(argv[i], "-U") == 0 ||
strcmp(argv[i], "-Y") == 0 ||
strcmp(argv[i], "-dylib_file") == 0 ||
strcmp(argv[i], "-final_output") == 0 ||
strcmp(argv[i], "-headerpad") == 0 ||
strcmp(argv[i], "-weak_reference_mismatches") == 0 ||
strcmp(argv[i], "-u") == 0 ||
strcmp(argv[i], "-exported_symbols_list") == 0 ||
strcmp(argv[i], "-unexported_symbols_list") == 0 ||
strcmp(argv[i], "-executable_path") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 >= argc){
error("not enough arguments follow %s", argv[i]);
usage();
}
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 2));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i+1];
if(strcmp(argv[i], "-final_output") == 0)
cmd_flags.final_output_specified = TRUE;
i += 1;
}
else if(strcmp(argv[i], "-sectorder_detail") == 0 ||
strcmp(argv[i], "-Sn") == 0 ||
strcmp(argv[i], "-Si") == 0 ||
strcmp(argv[i], "-Sp") == 0 ||
strcmp(argv[i], "-S") == 0 ||
strcmp(argv[i], "-X") == 0 ||
strcmp(argv[i], "-x") == 0 ||
strcmp(argv[i], "-whatsloaded") == 0 ||
strcmp(argv[i], "-whyload") == 0 ||
strcmp(argv[i], "-arch_errors_fatal") == 0 ||
strcmp(argv[i], "-run_init_lazily") == 0 ||
strcmp(argv[i], "-twolevel_namespace") == 0 ||
strcmp(argv[i], "-twolevel_namespace_hints") == 0 ||
strcmp(argv[i], "-flat_namespace") == 0 ||
strcmp(argv[i], "-nomultidefs") == 0 ||
strcmp(argv[i], "-headerpad_max_install_names") == 0 ||
strcmp(argv[i], "-prebind_all_twolevel_modules") == 0 ||
strcmp(argv[i], "-prebind_allow_overlap") == 0 ||
strcmp(argv[i], "-ObjC") == 0 ||
strcmp(argv[i], "-M") == 0 ||
strcmp(argv[i], "-t") == 0 ||
strcmp(argv[i], "-single_module") == 0 ||
strcmp(argv[i], "-multi_module") == 0 ||
strcmp(argv[i], "-m") == 0 ||
strcmp(argv[i], "-dead_strip") == 0 ||
strcmp(argv[i], "-no_uuid") == 0 ||
strcmp(argv[i], "-no_dead_strip_inits_and_terms") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 1));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
}
else if(strcmp(argv[i], "-no_arch_warnings") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
}
else if(strcmp(argv[i], "-prebind") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(cmd_flags.prebinding_flag_specified == TRUE &&
cmd_flags.prebinding == FALSE){
error("both -prebind and -noprebind can't be "
"specified");
usage();
}
cmd_flags.prebinding_flag_specified = TRUE;
cmd_flags.prebinding = TRUE;
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 1));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
}
else if(strcmp(argv[i], "-noprebind") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(cmd_flags.prebinding_flag_specified == TRUE &&
cmd_flags.prebinding == TRUE){
error("both -prebind and -noprebind can't be "
"specified");
usage();
}
cmd_flags.prebinding_flag_specified = TRUE;
cmd_flags.prebinding = FALSE;
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 1));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
}
else if(strcmp(argv[i], "-all_load") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(cmd_flags.all_load_flag_specified == TRUE &&
cmd_flags.all_load == FALSE){
error("both -all_load and -noall_load can't be "
"specified");
usage();
}
cmd_flags.all_load_flag_specified = TRUE;
cmd_flags.all_load = TRUE;
}
else if(strcmp(argv[i], "-noall_load") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(cmd_flags.all_load_flag_specified == TRUE &&
cmd_flags.all_load == TRUE){
error("both -all_load and -noall_load can't be "
"specified");
usage();
}
cmd_flags.all_load_flag_specified = TRUE;
cmd_flags.all_load = FALSE;
}
else if(strncmp(argv[i], "-y", 2) == 0 ||
strncmp(argv[i], "-i", 2) == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(strncmp(argv[i], "-i", 2) == 0)
cmd_flags.no_files_ok = TRUE;
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 1));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
}
else if(argv[i][1] == 'l'){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(argv[i][2] == '\0'){
error("-l: name missing");
usage();
}
cmd_flags.files[cmd_flags.nfiles++] = argv[i];
lflags_seen = TRUE;
}
else if(strncmp(argv[i], "-weak-l", 7) == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(argv[i][7] == '\0'){
error("-weak-l: name missing");
usage();
}
cmd_flags.files[cmd_flags.nfiles++] = argv[i];
lflags_seen = TRUE;
}
else if(strcmp(argv[i], "-framework") == 0 ||
strcmp(argv[i], "-weak_framework") == 0 ||
strcmp(argv[i], "-weak_library") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
if(i + 1 >= argc){
error("not enough arguments follow %s", argv[i]);
usage();
}
cmd_flags.files[cmd_flags.nfiles++] = argv[i];
cmd_flags.files[cmd_flags.nfiles++] = argv[i+1];
lflags_seen = TRUE;
i += 1;
}
else if(strcmp(argv[i], "-T") == 0){
if(cmd_flags.L_or_T_specified == TRUE){
error("both -T and -L can't be specified");
usage();
}
cmd_flags.L_or_T_specified = TRUE;
cmd_flags.use_long_names = FALSE;
}
else if(argv[i][1] == 'L' || argv[i][1] == 'F'){
if(argv[i][1] == 'L' && argv[i][2] == '\0'){
if(cmd_flags.L_or_T_specified == TRUE){
error("both -T and -L can't be specified");
usage();
}
cmd_flags.L_or_T_specified = TRUE;
cmd_flags.use_long_names = TRUE;
}
else{
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
cmd_flags.Ldirs = realloc(cmd_flags.Ldirs,
sizeof(char *) * (cmd_flags.nLdirs + 1));
cmd_flags.Ldirs[cmd_flags.nLdirs++] = argv[i];
}
}
else if(argv[i][1] == 'g'){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
;
}
else if(strcmp(argv[i], "-pg") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
;
}
else if(strcmp(argv[i], "-search_paths_first") == 0){
if(cmd_flags.ranlib == TRUE){
error("unknown option: %s", argv[i]);
usage();
}
cmd_flags.search_paths_first = TRUE;
cmd_flags.ldflags = reallocate(cmd_flags.ldflags,
sizeof(char *) * (cmd_flags.nldflags + 1));
cmd_flags.ldflags[cmd_flags.nldflags++] = argv[i];
}
else if(strcmp(argv[i], "-noflush") == 0){
cmd_flags.noflush = TRUE;
}
#ifdef DEBUG
else if(strcmp(argv[i], "-debug") == 0){
if(i + 1 >= argc){
error("not enough arguments follow %s", argv[i]);
usage();
}
i++;
cmd_flags.debug |= 1 << strtoul(argv[i], &endp, 10);
if(*endp != '\0' || strtoul(argv[i], &endp, 10) > 32)
fatal("argument for -debug %s not a proper "
"decimal number less than 32", argv[i]);
}
#endif
else{
for(j = 1; argv[i][j] != '\0'; j++){
switch(argv[i][j]){
case 's':
cmd_flags.s = TRUE;
break;
case 'a':
cmd_flags.a = TRUE;
break;
case 'c':
cmd_flags.c = TRUE;
break;
case 'v':
if(cmd_flags.ranlib == TRUE){
error("unknown option character `%c' in: %s",
argv[i][j], argv[i]);
usage();
}
cmd_flags.verbose= TRUE;
break;
case 'V':
printf("Apple Computer, Inc. version %s\n",
apple_version);
Vflag = TRUE;
break;
case 't':
if(cmd_flags.ranlib == TRUE){
warning("touch option (`%c' in: %s) ignored "
"(table of contents rebuilt anyway)",
argv[i][j], argv[i]);
cmd_flags.t = TRUE;
break;
}
else {
error("unknown option character `%c' in: %s",
argv[i][j], argv[i]);
usage();
}
case 'f':
if(cmd_flags.ranlib == TRUE){
cmd_flags.f = TRUE;
break;
}
else {
error("unknown option character `%c' in: %s",
argv[i][j], argv[i]);
usage();
}
case 'q':
if(cmd_flags.ranlib == TRUE){
cmd_flags.q = TRUE;
break;
}
else {
error("unknown option character `%c' in: %s",
argv[i][j], argv[i]);
usage();
}
default:
error("unknown option character `%c' in: %s",
argv[i][j], argv[i]);
usage();
}
}
}
}
else
cmd_flags.files[cmd_flags.nfiles++] = argv[i];
}
if((getenv("RC_TRACE_ARCHIVES") != NULL) ||
(getenv("LD_TRACE_ARCHIVES") != NULL)) {
cmd_flags.ld_trace_archives = TRUE;
cmd_flags.trace_file_path = getenv("LD_TRACE_FILE");
}
if(next_root != NULL){
if(getenv("NEXT_ROOT") != NULL)
warning("NEXT_ROOT environment variable ignored because "
"-syslibroot specified");
}
else{
next_root = getenv("NEXT_ROOT");
}
if(next_root != NULL){
for(i = 0; standard_dirs[i] != NULL; i++){
p = allocate(strlen(next_root) +
strlen(standard_dirs[i]) + 1);
strcpy(p, next_root);
strcat(p, standard_dirs[i]);
standard_dirs[i] = p;
}
for(i = 0; i < cmd_flags.nLdirs ; i++){
if(cmd_flags.Ldirs[i][1] != 'L')
continue;
if(cmd_flags.Ldirs[i][2] == '/'){
p = makestr(next_root, cmd_flags.Ldirs[i] + 2, NULL);
if(access(p, F_OK) != -1){
free(p);
p = makestr("-L", next_root, cmd_flags.Ldirs[i] + 2,
NULL);
cmd_flags.Ldirs[i] = p;
}
else{
free(p);
}
}
}
}
if(cmd_flags.ranlib == FALSE && cmd_flags.dynamic == TRUE){
if(cmd_flags.s == TRUE){
warning("-static not specified, -s invalid");
}
if(cmd_flags.a == TRUE){
warning("-static not specified, -a invalid");
}
if(cmd_flags.c == TRUE){
warning("-static not specified, -c invalid");
}
if(cmd_flags.L_or_T_specified == TRUE){
if(cmd_flags.use_long_names == TRUE)
warning("-static not specified, -L invalid");
else
warning("-static not specified, -T invalid");
}
}
if(cmd_flags.s == TRUE && cmd_flags.a == TRUE){
error("only one of -s or -a can be specified");
usage();
}
if(cmd_flags.ranlib == FALSE && cmd_flags.output == NULL){
if(Vflag == TRUE)
exit(EXIT_SUCCESS);
error("no output file specified (specify with -o output)");
usage();
}
if(cmd_flags.dynamic == FALSE){
if(cmd_flags.compatibility != NULL){
warning("-dynamic not specified, -compatibility_version %s "
"invalid", cmd_flags.compatibility);
}
if(cmd_flags.current != NULL){
warning("-dynamic not specified, -current_version %s invalid",
cmd_flags.current);
}
if(cmd_flags.install_name != NULL){
warning("-dynamic not specified, -install_name %s invalid",
cmd_flags.install_name);
}
if(cmd_flags.seg1addr != NULL){
warning("-dynamic not specified, -seg1addr %s invalid",
cmd_flags.seg1addr);
}
if(cmd_flags.segs_read_only_addr != NULL){
warning("-dynamic not specified, -segs_read_only_addr %s "
"invalid", cmd_flags.segs_read_only_addr);
}
if(cmd_flags.segs_read_write_addr != NULL){
warning("-dynamic not specified, -segs_read_write_addr %s "
"invalid", cmd_flags.segs_read_write_addr);
}
if(cmd_flags.seg_addr_table != NULL){
warning("-dynamic not specified, -seg_addr_table %s "
"invalid", cmd_flags.seg_addr_table);
}
if(cmd_flags.seg_addr_table_filename != NULL){
warning("-dynamic not specified, -seg_addr_table_filename %s "
"invalid", cmd_flags.seg_addr_table_filename);
}
if(cmd_flags.all_load_flag_specified == TRUE){
if(cmd_flags.all_load == TRUE)
warning("-dynamic not specified, -all_load invalid");
else
warning("-dynamic not specified, -noall_load invalid");
}
if(cmd_flags.nldflags != 0){
bad_flag_seen = FALSE;
for(j = 0; j < cmd_flags.nldflags; j++){
if(strcmp(cmd_flags.ldflags[j], "-syslibroot") == 0){
j++;
continue;
}
if(bad_flag_seen == FALSE){
fprintf(stderr, "%s: -dynamic not specified the "
"following flags are invalid: ", progname);
bad_flag_seen = TRUE;
}
fprintf(stderr, "%s ", cmd_flags.ldflags[j]);
}
if(bad_flag_seen == TRUE)
fprintf(stderr, "\n");
}
if(cmd_flags.nLdirs != 0){
bad_flag_seen = FALSE;
for(j = 0; j < cmd_flags.nLdirs; j++){
if(strncmp(cmd_flags.Ldirs[j], "-L", 2) == 0)
continue;
if(bad_flag_seen == FALSE){
fprintf(stderr, "%s: -dynamic not specified the "
"following flags are invalid: ", progname);
bad_flag_seen = TRUE;
}
fprintf(stderr, "%s ", cmd_flags.Ldirs[j]);
}
if(bad_flag_seen == TRUE)
fprintf(stderr, "\n");
}
}
else{
if(getenv("LD_PREBIND") != NULL){
if(cmd_flags.prebinding_flag_specified == TRUE &&
cmd_flags.prebinding == FALSE){
warning("LD_PREBIND environment variable ignored because "
"-noprebind specified");
}
else{
cmd_flags.prebinding_flag_specified = TRUE;
cmd_flags.prebinding = TRUE;
}
}
}
if(cmd_flags.nfiles == 0){
if(cmd_flags.ranlib == TRUE){
error("no archives specified");
usage();
}
else{
if(cmd_flags.dynamic == TRUE && cmd_flags.no_files_ok == TRUE)
warning("warning no files specified");
else{
error("no files specified");
usage();
}
}
}
if(cmd_flags.a == FALSE)
cmd_flags.s = TRUE;
process();
if(errors == 0)
return(EXIT_SUCCESS);
else
return(EXIT_FAILURE);
}
static
void
usage(
void)
{
if(cmd_flags.ranlib)
fprintf(stderr, "Usage: %s [-sactfqLT] [-] archive [...]\n",
progname);
else{
fprintf(stderr, "Usage: %s -static [-] file [...] "
"[-filelist listfile[,dirname]] [-arch_only arch] "
"[-sacLT]\n", progname);
fprintf(stderr, "Usage: %s -dynamic [-] file [...] "
"[-filelist listfile[,dirname]] [-arch_only arch] "
"[-o output] [-install_name name] "
"[-compatibility_version #] [-current_version #] "
"[-seg1addr 0x#] [-segs_read_only_addr 0x#] "
"[-segs_read_write_addr 0x#] [-seg_addr_table <filename>] "
"[-seg_addr_table_filename <file_system_path>] "
"[-all_load] [-noall_load]\n",
progname);
}
exit(EXIT_FAILURE);
}
static
void
process(
void)
{
uint32_t i, j, k, previous_errors;
struct ofile *ofiles;
char *file_name;
enum bool flag, ld_trace_archive_printed;
ofiles = allocate(sizeof(struct ofile) * cmd_flags.nfiles);
for(i = 0; i < cmd_flags.nfiles; i++){
if(strncmp(cmd_flags.files[i], "-l", 2) == 0 ||
strncmp(cmd_flags.files[i], "-weak-l", 7) == 0){
file_name = file_name_from_l_flag(cmd_flags.files[i]);
if(file_name != NULL)
if(ofile_map(file_name, NULL, NULL, ofiles + i, TRUE) ==
FALSE)
continue;
}
else if(strcmp(cmd_flags.files[i], "-framework") == 0 ||
strcmp(cmd_flags.files[i], "-weak_framework") == 0 ||
strcmp(cmd_flags.files[i], "-weak_library") == 0){
i++;
continue;
}
else{
if(ofile_map(cmd_flags.files[i], NULL, NULL, ofiles + i,
TRUE) == FALSE)
continue;
}
previous_errors = errors;
errors = 0;
ld_trace_archive_printed = FALSE;
if(ofiles[i].file_type == OFILE_FAT){
(void)ofile_first_arch(ofiles + i);
do{
if(ofiles[i].arch_type == OFILE_ARCHIVE){
if(cmd_flags.ld_trace_archives == TRUE &&
cmd_flags.dynamic == FALSE &&
ld_trace_archive_printed == FALSE){
char resolvedname[MAXPATHLEN];
if(realpath(ofiles[i].file_name, resolvedname) !=
NULL)
ld_trace("[Logging for XBS] Used static archive: "
"%s\n", resolvedname);
else
ld_trace("[Logging for XBS] Used static archive: "
"%s\n", ofiles[i].file_name);
ld_trace_archive_printed = TRUE;
}
if((flag = ofile_first_member(ofiles + i)) == TRUE){
if(ofiles[i].member_ar_hdr != NULL &&
strncmp(ofiles[i].member_name, SYMDEF,
sizeof(SYMDEF) - 1) == 0)
flag = ofile_next_member(ofiles + i);
while(flag == TRUE){
if(ofiles[i].mh != NULL ||
ofiles[i].mh64 != NULL ||
ofiles[i].lto != NULL ||
cmd_flags.ranlib == TRUE)
add_member(ofiles + i);
else{
error("for architecture: %s file: %s(%.*s) "
"is not an object file (not allowed "
"in a library)",
ofiles[i].arch_flag.name,
cmd_flags.files[i],
(int)ofiles[i].member_name_size,
ofiles[i].member_name);
}
flag = ofile_next_member(ofiles + i);
}
}
}
else if(ofiles[i].arch_type == OFILE_Mach_O){
if(cmd_flags.ranlib == TRUE){
error("for architecture: %s file: %s is not an "
"archive (no processing done on this file)",
ofiles[i].arch_flag.name, cmd_flags.files[i]);
goto ranlib_fat_error;
}
else
add_member(ofiles + i);
}
else if(ofiles[i].arch_type == OFILE_UNKNOWN){
if(cmd_flags.ranlib == TRUE){
error("for architecture: %s file: %s is not an "
"archive (no processing done on this file)",
ofiles[i].arch_flag.name, cmd_flags.files[i]);
goto ranlib_fat_error;
}
else{
error("for architecture: %s file: %s is not an "
"object file (not allowed in a library)",
ofiles[i].arch_flag.name, cmd_flags.files[i]);
}
}
}while(ofile_next_arch(ofiles + i) == TRUE);
}
else if(ofiles[i].file_type == OFILE_ARCHIVE){
if(cmd_flags.ld_trace_archives == TRUE &&
cmd_flags.dynamic == FALSE &&
ld_trace_archive_printed == FALSE){
char resolvedname[MAXPATHLEN];
if(realpath(ofiles[i].file_name, resolvedname) != NULL)
ld_trace("[Logging for XBS] Used static archive: "
"%s\n", resolvedname);
else
ld_trace("[Logging for XBS] Used static archive: "
"%s\n", ofiles[i].file_name);
ld_trace_archive_printed = TRUE;
}
if((flag = ofile_first_member(ofiles + i)) == TRUE){
if(ofiles[i].member_ar_hdr != NULL &&
strncmp(ofiles[i].member_name, SYMDEF,
sizeof(SYMDEF) - 1) == 0)
flag = ofile_next_member(ofiles + i);
while(flag == TRUE){
if(ofiles[i].member_type == OFILE_FAT){
(void)ofile_first_arch(ofiles + i);
do{
if(ofiles[i].mh != NULL ||
ofiles[i].mh64 != NULL ||
ofiles[i].lto != NULL ||
cmd_flags.ranlib == TRUE){
add_member(ofiles + i);
}
else{
error("file: %s(%.*s) for architecture: %s "
"is not an object file (not allowed in "
"a library)", cmd_flags.files[i],
(int)ofiles[i].member_name_size,
ofiles[i].member_name,
ofiles[i].arch_flag.name);
}
}while(ofile_next_arch(ofiles + i) == TRUE);
}
else if(ofiles[i].mh != NULL ||
ofiles[i].mh64 != NULL ||
#ifdef LTO_SUPPORT
ofiles[i].lto != NULL ||
#endif
cmd_flags.ranlib == TRUE){
add_member(ofiles + i);
}
else{
error("file: %s(%.*s) is not an object file (not "
"allowed in a library)", cmd_flags.files[i],
(int)ofiles[i].member_name_size,
ofiles[i].member_name);
}
flag = ofile_next_member(ofiles + i);
}
}
}
else if(ofiles[i].file_type == OFILE_Mach_O){
if(cmd_flags.ranlib == TRUE){
error("file: %s is not an archive", cmd_flags.files[i]);
continue;
}
add_member(ofiles + i);
}
#ifdef LTO_SUPPORT
else if(ofiles[i].file_type == OFILE_LLVM_BITCODE){
if(cmd_flags.ranlib == TRUE){
error("file: %s is not an archive", cmd_flags.files[i]);
continue;
}
add_member(ofiles + i);
}
#endif
else{
if(cmd_flags.ranlib == TRUE){
error("file: %s is not an archive", cmd_flags.files[i]);
continue;
}
else{
error("file: %s is not an object file (not allowed in a "
"library)", cmd_flags.files[i]);
}
}
if(cmd_flags.ranlib == TRUE){
if(narchs > 1){
for(j = 0; j < narchs; j++){
for(k = 0; k < archs[j].nmembers; k++){
if(archs[j].members[k].mh == NULL &&
#ifdef LTO_SUPPORT
archs[j].members[k].lto == NULL &&
#endif
archs[j].members[k].mh64 == NULL){
error("library member: %s(%.*s) is not an "
"object file (not allowed in a library "
"with multiple architectures)",
cmd_flags.files[i],
(int)archs[j].members[k].
input_base_name_size,
archs[j].members[k].input_base_name);
}
}
}
}
if(errors == 0)
create_library(cmd_flags.files[i]);
if(cmd_flags.nfiles > 1){
ranlib_fat_error:
free_archs();
ofile_unmap(ofiles + i);
}
}
errors += previous_errors;
}
if(cmd_flags.ranlib == FALSE && errors == 0)
create_library(cmd_flags.output);
}
static
char *
file_name_from_l_flag(
char *l_flag)
{
char *file_name, *p, *start;
if(strncmp(l_flag, "-weak-l", 7) == 0)
start = &l_flag[7];
else
start = &l_flag[2];
p = strrchr(start, '.');
if(p != NULL && strcmp(p, ".o") == 0){
p = start;
file_name = search_for_file(p);
}
else{
file_name = NULL;
if(cmd_flags.dynamic == TRUE){
if(cmd_flags.search_paths_first == TRUE){
file_name = search_paths_for_lname(start);
}
else{
p = makestr("lib", start, ".dylib", NULL);
file_name = search_for_file(p);
free(p);
if(file_name == NULL){
p = makestr("lib", start, ".a", NULL);
file_name = search_for_file(p);
free(p);
}
}
}
else{
p = makestr("lib", start, ".a", NULL);
file_name = search_for_file(p);
free(p);
}
}
if(file_name == NULL)
error("can't locate file for: %s", l_flag);
return(file_name);
}
static
char *
search_for_file(
char *base_name)
{
uint32_t i;
char *file_name;
for(i = 0; i < cmd_flags.nLdirs ; i++){
if(cmd_flags.Ldirs[i][1] != 'L')
continue;
file_name = makestr(cmd_flags.Ldirs[i] + 2, "/", base_name, NULL);
if(access(file_name, R_OK) != -1)
return(file_name);
free(file_name);
}
for(i = 0; standard_dirs[i] != NULL ; i++){
file_name = makestr(standard_dirs[i], base_name, NULL);
if(access(file_name, R_OK) != -1)
return(file_name);
free(file_name);
}
return(NULL);
}
static
char *
search_paths_for_lname(
const char *lname_argument)
{
uint32_t i;
char *file_name, *dir;
for(i = 0; i < cmd_flags.nLdirs ; i++){
if(cmd_flags.Ldirs[i][1] != 'L')
continue;
dir = makestr(cmd_flags.Ldirs[i] + 2, "/", NULL);
file_name = search_path_for_lname(dir, lname_argument);
free(dir);
if(file_name != NULL)
return(file_name);
}
for(i = 0; standard_dirs[i] != NULL ; i++){
file_name = search_path_for_lname(standard_dirs[i], lname_argument);
if(file_name != NULL)
return(file_name);
}
return(NULL);
}
static
char *
search_path_for_lname(
const char *dir,
const char *lname_argument)
{
char *file_name;
file_name = makestr(dir, "/", "lib", lname_argument, ".dylib", NULL);
if(access(file_name, R_OK) != -1)
return(file_name);
free(file_name);
file_name = makestr(dir, "/", "lib", lname_argument, ".a", NULL);
if(access(file_name, R_OK) != -1)
return(file_name);
free(file_name);
return(NULL);
}
static
void
add_member(
struct ofile *ofile)
{
uint32_t i, j, size, ar_name_size;
struct arch *arch;
struct member *member;
struct stat stat_buf;
char *p, c, ar_name_buf[sizeof(ofile->member_ar_hdr->ar_name) + 1];
char ar_size_buf[sizeof(ofile->member_ar_hdr->ar_size) + 1];
const struct arch_flag *family_arch_flag;
if(ofile->member_ar_hdr == NULL){
if(stat(ofile->file_name, &stat_buf) == -1){
system_error("can't stat file: %s", ofile->file_name);
return;
}
}
if(ofile->mh != NULL || ofile->mh64 != NULL)
size = rnd(ofile->object_size, 8);
#ifdef LTO_SUPPORT
else if(ofile->lto != NULL && ofile->file_type == OFILE_LLVM_BITCODE)
size = rnd(ofile->file_size, 8);
#endif
else
size = rnd(ofile->member_size, 8);
i = 0;
if(ofile->mh != NULL ||
ofile->mh64 != NULL){
if(ofile->mh_cputype == 0){
if(ofile->member_ar_hdr != NULL){
error("file: %s(%.*s) cputype is zero (a reserved value)",
ofile->file_name, (int)ofile->member_name_size,
ofile->member_name);
}
else
error("file: %s cputype is zero (a reserved value)",
ofile->file_name);
return;
}
if(ofile->mh_filetype == MH_DYLIB ||
ofile->mh_filetype == MH_DYLIB_STUB){
if(cmd_flags.dynamic != TRUE){
if(ofile->member_ar_hdr != NULL){
warning("file: %s(%.*s) is a dynamic library, not "
"added to the static library",
ofile->file_name, (int)ofile->member_name_size,
ofile->member_name);
}
else
warning("file: %s is a dynamic library, not added to "
"the static library", ofile->file_name);
}
return;
}
if(cmd_flags.arch_only_flag.name != NULL){
if(cmd_flags.arch_only_flag.cputype != ofile->mh_cputype)
return;
if(cmd_flags.arch_only_flag.cputype == CPU_TYPE_ARM){
if(cmd_flags.arch_only_flag.cpusubtype !=
ofile->mh_cpusubtype)
return;
}
}
for( ; i < narchs; i++){
if(archs[i].arch_flag.cputype == ofile->mh_cputype){
if(archs[i].arch_flag.cputype == CPU_TYPE_ARM &&
archs[i].arch_flag.cpusubtype != ofile->mh_cpusubtype)
continue;
break;
}
}
}
#ifdef LTO_SUPPORT
else if(ofile->lto != NULL){
if(cmd_flags.arch_only_flag.name != NULL &&
cmd_flags.arch_only_flag.cputype != ofile->lto_cputype)
return;
for( ; i < narchs; i++){
if(archs[i].arch_flag.cputype == ofile->lto_cputype){
if(archs[i].arch_flag.cputype == CPU_TYPE_ARM &&
archs[i].arch_flag.cpusubtype != ofile->lto_cpusubtype)
continue;
break;
}
}
}
#endif
if(narchs == 1 && archs[0].arch_flag.cputype == 0){
i = 0;
}
else if(i == narchs){
archs = reallocate(archs, sizeof(struct arch) * (narchs+1));
memset(archs + narchs, '\0', sizeof(struct arch));
if(ofile->mh != NULL ||
ofile->mh64 != NULL){
if(ofile->mh_cputype == CPU_TYPE_ARM){
archs[narchs].arch_flag.name = (char *)
get_arch_name_from_types(
ofile->mh_cputype, ofile->mh_cpusubtype);
archs[narchs].arch_flag.cputype = ofile->mh_cputype;
archs[narchs].arch_flag.cpusubtype = ofile->mh_cpusubtype;
}
else{
family_arch_flag =
get_arch_family_from_cputype(ofile->mh_cputype);
if(family_arch_flag != NULL)
archs[narchs].arch_flag = *family_arch_flag;
}
}
#ifdef LTO_SUPPORT
else if(ofile->lto != NULL){
if(ofile->lto_cputype == CPU_TYPE_ARM){
archs[narchs].arch_flag.name = (char *)
get_arch_name_from_types(
ofile->lto_cputype, ofile->lto_cpusubtype);
archs[narchs].arch_flag.cputype = ofile->lto_cputype;
archs[narchs].arch_flag.cpusubtype = ofile->lto_cpusubtype;
}
else{
family_arch_flag =
get_arch_family_from_cputype(ofile->lto_cputype);
if(family_arch_flag != NULL)
archs[narchs].arch_flag = *family_arch_flag;
}
}
#endif
else
archs[narchs].arch_flag.name = "unknown";
narchs++;
}
arch = archs + i;
if(arch->arch_flag.cputype == 0 &&
(ofile->mh != NULL || ofile->mh64 != NULL)){
family_arch_flag = get_arch_family_from_cputype(ofile->mh_cputype);
if(family_arch_flag != NULL){
arch->arch_flag = *family_arch_flag;
}
else{
arch->arch_flag.name =
savestr("cputype 1234567890 cpusubtype 1234567890");
if(arch->arch_flag.name != NULL)
sprintf(arch->arch_flag.name, "cputype %u cpusubtype %u",
ofile->mh_cputype, ofile->mh_cpusubtype &
~CPU_SUBTYPE_MASK);
arch->arch_flag.cputype = ofile->mh_cputype;
arch->arch_flag.cpusubtype = ofile->mh_cpusubtype;
}
}
#ifdef LTO_SUPPORT
if(arch->arch_flag.cputype == 0 &&
(ofile->lto != NULL)){
family_arch_flag = get_arch_family_from_cputype(ofile->lto_cputype);
if(family_arch_flag != NULL){
arch->arch_flag = *family_arch_flag;
}
else{
arch->arch_flag.name =
savestr("cputype 1234567890 cpusubtype 1234567890");
if(arch->arch_flag.name != NULL)
sprintf(arch->arch_flag.name, "cputype %u cpusubtype %u",
ofile->lto_cputype, ofile->lto_cpusubtype &
~CPU_SUBTYPE_MASK);
arch->arch_flag.cputype = ofile->lto_cputype;
arch->arch_flag.cpusubtype = ofile->lto_cpusubtype;
}
}
#endif
arch->members = reallocate(arch->members, sizeof(struct member) *
(arch->nmembers + 1));
member = arch->members + arch->nmembers;
memset(member, '\0', sizeof(struct member));
arch->nmembers++;
member->input_file_name = ofile->file_name;
if(ofile->member_ar_hdr == NULL){
p = strrchr(ofile->file_name, '/');
if(p != NULL)
p++;
else
p = ofile->file_name;
member->input_base_name = p;
member->input_base_name_size = strlen(p);
member->member_name = member->input_base_name;
if(cmd_flags.use_long_names == TRUE){
member->output_long_name = TRUE;
member->member_name_size = member->input_base_name_size;
ar_name_size = rnd(member->input_base_name_size, 8) +
(rnd(sizeof(struct ar_hdr), 8) -
sizeof(struct ar_hdr));
sprintf(ar_name_buf, "%s%-*lu", AR_EFMT1,
(int)(sizeof(member->ar_hdr.ar_name) -
(sizeof(AR_EFMT1) - 1)),
(long unsigned int)ar_name_size);
memcpy(member->ar_hdr.ar_name, ar_name_buf,
sizeof(member->ar_hdr.ar_name));
}
else{
ar_name_size = 0;
member->output_long_name = FALSE;
c = '\0';
if(strlen(p) > sizeof(member->ar_hdr.ar_name)){
c = p[sizeof(member->ar_hdr.ar_name)];
p[sizeof(member->ar_hdr.ar_name)] = '\0';
}
sprintf((char *)(&member->ar_hdr), "%-*s",
(int)sizeof(member->ar_hdr.ar_name), p);
if(c != '\0')
p[sizeof(member->ar_hdr.ar_name)] = c;
member->member_name_size = size_ar_name(&member->ar_hdr);
}
if(zero_ar_date == TRUE)
stat_buf.st_mtime = 0;
sprintf((char *)(&member->ar_hdr) + sizeof(member->ar_hdr.ar_name),
"%-*ld%-*u%-*u%-*o%-*ld%-*s",
(int)sizeof(member->ar_hdr.ar_date),
(long int)stat_buf.st_mtime,
(int)sizeof(member->ar_hdr.ar_uid),
(unsigned short)stat_buf.st_uid,
(int)sizeof(member->ar_hdr.ar_gid),
(unsigned short)stat_buf.st_gid,
(int)sizeof(member->ar_hdr.ar_mode),
(unsigned int)stat_buf.st_mode,
(int)sizeof(member->ar_hdr.ar_size),
(long)size + ar_name_size,
(int)sizeof(member->ar_hdr.ar_fmag),
ARFMAG);
}
else{
member->input_ar_hdr = ofile->member_ar_hdr;
member->input_base_name = ofile->member_name;
member->input_base_name_size = ofile->member_name_size;
member->ar_hdr = *(ofile->member_ar_hdr);
member->member_name = ofile->member_name;
if(cmd_flags.use_long_names == TRUE){
if(ofile->member_name != ofile->member_ar_hdr->ar_name){
member->output_long_name = TRUE;
member->member_name_size = ofile->member_name_size;
for(ar_name_size = member->member_name_size;
ar_name_size > 1 ;
ar_name_size--){
if(ofile->member_name[ar_name_size - 1] != '\0')
break;
}
member->member_name_size = ar_name_size;
ar_name_size = rnd(ar_name_size, 8) +
(rnd(sizeof(struct ar_hdr), 8) -
sizeof(struct ar_hdr));
sprintf(ar_name_buf, "%s%-*lu", AR_EFMT1,
(int)(sizeof(member->ar_hdr.ar_name) -
(sizeof(AR_EFMT1) - 1)),
(long unsigned int)ar_name_size);
memcpy(member->ar_hdr.ar_name, ar_name_buf,
sizeof(member->ar_hdr.ar_name));
}
else{
member->member_name_size = size_ar_name(&member->ar_hdr);
ar_name_size = rnd(ofile->member_name_size, 8) +
(rnd(sizeof(struct ar_hdr), 8) -
sizeof(struct ar_hdr));
member->output_long_name = TRUE;
sprintf(ar_name_buf, "%s%-*lu", AR_EFMT1,
(int)(sizeof(member->ar_hdr.ar_name) -
(sizeof(AR_EFMT1) - 1)),
(long unsigned int)ar_name_size);
memcpy(member->ar_hdr.ar_name, ar_name_buf,
sizeof(member->ar_hdr.ar_name));
}
}
else{
ar_name_size = 0;
member->output_long_name = FALSE;
if(ofile->member_name != ofile->member_ar_hdr->ar_name){
for(j = 0; j < sizeof(member->ar_hdr.ar_name) &&
j < ofile->member_name_size &&
ofile->member_name[j] != '\0'; j++)
member->ar_hdr.ar_name[j] = ofile->member_name[j];
for( ; j < sizeof(member->ar_hdr.ar_name); j++)
member->ar_hdr.ar_name[j] = ' ';
}
member->member_name_size = size_ar_name(&member->ar_hdr);
}
sprintf(ar_size_buf, "%-*ld",
(int)sizeof(member->ar_hdr.ar_size),
(long)size + ar_name_size);
memcpy(member->ar_hdr.ar_size, ar_size_buf,
sizeof(member->ar_hdr.ar_size));
}
member->offset = arch->size;
arch->size += sizeof(struct ar_hdr) + size + ar_name_size;
if(ofile->mh != NULL ||
ofile->mh64 != NULL){
member->object_addr = ofile->object_addr;
member->object_size = ofile->object_size;
member->object_byte_sex = ofile->object_byte_sex;
member->mh = ofile->mh;
member->mh64 = ofile->mh64;
member->load_commands = ofile->load_commands;
}
#ifdef LTO_SUPPORT
else if(ofile->file_type == OFILE_LLVM_BITCODE){
member->object_addr = ofile->file_addr;
member->object_size = ofile->file_size;
member->lto = ofile->lto;
member->object_byte_sex = get_byte_sex_from_flag(&arch->arch_flag);
}
#endif
else{
member->object_addr = ofile->member_addr;
member->object_size = ofile->member_size;
#ifdef LTO_SUPPORT
if(ofile->lto != NULL){
member->lto = ofile->lto;
member->object_byte_sex = get_byte_sex_from_flag(
&arch->arch_flag);
}
#endif
}
}
static
void
free_archs(
void)
{
uint32_t i;
for(i = 0 ; i < narchs; i++){
if(archs[i].tocs != NULL)
free(archs[i].tocs);
if(archs[i].toc_ranlibs != NULL)
free(archs[i].toc_ranlibs);
if(archs[i].toc_strings != NULL)
free(archs[i].toc_strings);
if(archs[i].members != NULL)
free(archs[i].members);
}
if(archs != NULL)
free(archs);
archs = NULL;
narchs = 0;
}
static
void
create_library(
char *output)
{
uint32_t i, j, k, l, library_size, offset, pad, *time_offsets;
enum byte_sex target_byte_sex;
char *library, *p, *flush_start;
kern_return_t r;
struct arch *arch;
struct fat_header *fat_header;
struct fat_arch *fat_arch;
int fd;
#ifndef __OPENSTEP__
struct utimbuf timep;
#else
time_t timep[2];
#endif
struct stat stat_buf;
struct ar_hdr toc_ar_hdr;
enum bool some_tocs;
if(narchs == 0){
if(cmd_flags.ranlib == TRUE){
if(cmd_flags.q == FALSE)
warning("empty library: %s (no table of contents added)",
output);
return;
}
else{
if(cmd_flags.dynamic == FALSE ||
cmd_flags.no_files_ok == FALSE){
if(cmd_flags.arch_only_flag.name != NULL)
error("no library created (no object files in input "
"files matching -arch_only %s)",
cmd_flags.arch_only_flag.name);
else
error("no library created (no object files in input "
"files)");
return;
}
}
}
if(cmd_flags.dynamic == TRUE){
create_dynamic_shared_library(output);
return;
}
if(cmd_flags.ranlib == FALSE)
warn_duplicate_member_names();
if(narchs > 1){
library_size = sizeof(struct fat_header) +
sizeof(struct fat_arch) * narchs;
if(cmd_flags.q == TRUE)
exit(EXIT_SUCCESS);
if(cmd_flags.f == TRUE)
warning("archive library: %s will be fat and ar(1) will not "
"be able to operate on it", output);
}
else
library_size = 0;
some_tocs = FALSE;
for(i = 0; i < narchs; i++){
make_table_of_contents(archs + i, output);
if(errors != 0)
return;
if(archs[i].toc_nranlibs != 0)
some_tocs = TRUE;
archs[i].size += SARMAG + archs[i].toc_size;
library_size += archs[i].size;
}
if(cmd_flags.q == TRUE && some_tocs == FALSE)
exit(EXIT_SUCCESS);
if((r = vm_allocate(mach_task_self(), (vm_address_t *)&library,
library_size, TRUE)) != KERN_SUCCESS)
mach_fatal(r, "can't vm_allocate() buffer for output file: %s of "
"size %u", output, library_size);
if(cmd_flags.ranlib == TRUE &&
get_unix_standard_mode() == TRUE &&
access(output, W_OK) == -1){
system_error("file: %s is not writable", output);
return;
}
(void)unlink(output);
if((fd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1){
system_error("can't create output file: %s", output);
return;
}
#ifdef F_NOCACHE
(void)fcntl(fd, F_NOCACHE, 1);
#endif
if(narchs > 1){
fat_header = (struct fat_header *)library;
fat_header->magic = FAT_MAGIC;
fat_header->nfat_arch = narchs;
offset = sizeof(struct fat_header) +
sizeof(struct fat_arch) * narchs;
fat_arch = (struct fat_arch *)(library + sizeof(struct fat_header));
for(i = 0; i < narchs; i++){
fat_arch[i].cputype = archs[i].arch_flag.cputype;
fat_arch[i].cpusubtype = archs[i].arch_flag.cpusubtype;
fat_arch[i].offset = offset;
fat_arch[i].size = archs[i].size;
fat_arch[i].align = 2;
offset += archs[i].size;
}
#ifdef __LITTLE_ENDIAN__
swap_fat_header(fat_header, BIG_ENDIAN_BYTE_SEX);
swap_fat_arch(fat_arch, narchs, BIG_ENDIAN_BYTE_SEX);
#endif
offset = sizeof(struct fat_header) +
sizeof(struct fat_arch) * narchs;
}
else
offset = 0;
output_flush(library, library_size, fd, 0, offset);
time_offsets = allocate(narchs * sizeof(uint32_t));
for(i = 0; i < narchs; i++){
p = library + offset;
flush_start = p;
arch = archs + i;
memcpy(p, ARMAG, SARMAG);
p += SARMAG;
if(arch->toc_nranlibs == 0 && cmd_flags.q == FALSE){
if(narchs > 1)
warning("warning for library: %s for architecture: %s the "
"table of contents is empty (no object file members"
" in the library define global symbols)", output,
arch->arch_flag.name);
else
warning("warning for library: %s the table of contents is "
"empty (no object file members in the library "
"define global symbols)", output);
}
target_byte_sex = UNKNOWN_BYTE_SEX;
for(j = 0;
j < arch->nmembers && target_byte_sex == UNKNOWN_BYTE_SEX;
j++){
target_byte_sex = arch->members[j].object_byte_sex;
}
if(target_byte_sex == UNKNOWN_BYTE_SEX)
target_byte_sex = host_byte_sex;
time_offsets[i] =
(p - library) +
((char *)&toc_ar_hdr.ar_date - (char *)&toc_ar_hdr);
memcpy(p, (char *)&arch->toc_ar_hdr, sizeof(struct ar_hdr));
p += sizeof(struct ar_hdr);
if(arch->toc_long_name == TRUE){
memcpy(p, arch->toc_name, arch->toc_name_size);
p += arch->toc_name_size +
(rnd(sizeof(struct ar_hdr), 8) -
sizeof(struct ar_hdr));
}
l = arch->toc_nranlibs * sizeof(struct ranlib);
if(target_byte_sex != host_byte_sex)
l = SWAP_INT(l);
memcpy(p, (char *)&l, sizeof(uint32_t));
p += sizeof(uint32_t);
if(target_byte_sex != host_byte_sex)
swap_ranlib(arch->toc_ranlibs, arch->toc_nranlibs,
target_byte_sex);
memcpy(p, (char *)arch->toc_ranlibs,
arch->toc_nranlibs * sizeof(struct ranlib));
p += arch->toc_nranlibs * sizeof(struct ranlib);
l = arch->toc_strsize;
if(target_byte_sex != host_byte_sex)
l = SWAP_INT(l);
memcpy(p, (char *)&l, sizeof(uint32_t));
p += sizeof(uint32_t);
memcpy(p, (char *)arch->toc_strings, arch->toc_strsize);
p += arch->toc_strsize;
output_flush(library, library_size, fd, flush_start - library,
p - flush_start);
for(j = 0; j < arch->nmembers; j++){
flush_start = p;
memcpy(p, (char *)&(arch->members[j].ar_hdr),
sizeof(struct ar_hdr));
p += sizeof(struct ar_hdr);
if(arch->members[j].output_long_name == TRUE){
strncpy(p, arch->members[j].member_name,
arch->members[j].member_name_size);
p += rnd(arch->members[j].member_name_size, 8) +
(rnd(sizeof(struct ar_hdr), 8) -
sizeof(struct ar_hdr));
}
if(arch->members[j].mh != NULL &&
arch->members[j].object_byte_sex != host_byte_sex){
if(swap_object_headers(arch->members[j].mh,
arch->members[j].load_commands) == FALSE)
fatal("internal error: swap_object_headers() failed");
}
else if(arch->members[j].mh64 != NULL &&
arch->members[j].object_byte_sex != host_byte_sex){
if(swap_object_headers(arch->members[j].mh64,
arch->members[j].load_commands) == FALSE)
fatal("internal error: swap_object_headers() failed");
}
memcpy(p, arch->members[j].object_addr,
arch->members[j].object_size);
#ifdef VM_SYNC_DEACTIVATE
vm_msync(mach_task_self(),
(vm_address_t)arch->members[j].object_addr,
(vm_size_t)arch->members[j].object_size,
VM_SYNC_DEACTIVATE);
#endif
p += arch->members[j].object_size;
pad = rnd(arch->members[j].object_size, 8) -
arch->members[j].object_size;
for(k = 0; k < pad; k++)
*p++ = '\n';
output_flush(library, library_size, fd, flush_start - library,
p - flush_start);
}
offset += arch->size;
}
if(cmd_flags.noflush == TRUE){
if(write(fd, library, library_size) != (int)library_size){
system_error("can't write output file: %s", output);
return;
}
}
else{
final_output_flush(library, fd);
}
if(close(fd) == -1){
system_fatal("can't close output file: %s", output);
return;
}
if(stat(output, &stat_buf) == -1){
system_fatal("can't stat file output file: %s", output);
return;
}
if((fd = open(output, O_WRONLY, 0)) == -1){
system_error("can't open output file: %s", output);
return;
}
if(zero_ar_date == TRUE)
stat_buf.st_mtime = 0;
sprintf((char *)(&toc_ar_hdr), "%-*s%-*ld",
(int)sizeof(toc_ar_hdr.ar_name),
SYMDEF,
(int)sizeof(toc_ar_hdr.ar_date),
(long int)stat_buf.st_mtime + 5);
for(i = 0; i < narchs; i++){
if(lseek(fd, time_offsets[i], L_SET) == -1){
system_error("can't lseek in output file: %s", output);
return;
}
if(write(fd, &toc_ar_hdr.ar_date, sizeof(toc_ar_hdr.ar_date)) !=
sizeof(toc_ar_hdr.ar_date)){
system_error("can't write to output file: %s", output);
return;
}
}
if(close(fd) == -1){
system_fatal("can't close output file: %s", output);
return;
}
#ifndef __OPENSTEP__
timep.actime = stat_buf.st_mtime;
timep.modtime = stat_buf.st_mtime;
if(utime(output, &timep) == -1)
#else
timep[0] = stat_buf.st_mtime;
timep[1] = stat_buf.st_mtime;
if(utime(output, timep) == -1)
#endif
{
system_fatal("can't set the modifiy times in output file: %s",
output);
return;
}
if((r = vm_deallocate(mach_task_self(), (vm_address_t)library,
library_size)) != KERN_SUCCESS){
my_mach_error(r, "can't vm_deallocate() buffer for output file");
return;
}
}
static
void
output_flush(
char *library,
uint32_t library_size,
int fd,
uint32_t offset,
uint32_t size)
{
uint32_t write_offset, write_size, host_pagesize;
struct block **p, *block, *before, *after;
kern_return_t r;
host_pagesize = 0x2000;
if(cmd_flags.noflush == TRUE)
return;
if(offset + size > library_size)
fatal("internal error: output_flush(offset = %u, size = %u) out "
"of range for library_size = %u", offset, size, library_size);
#ifdef DEBUG
if(cmd_flags.debug & (1 << 2))
print_block_list();
if(cmd_flags.debug & (1 << 1))
printf("output_flush(offset = %lu, size %lu)", offset, size);
#endif
if(size == 0){
#ifdef DEBUG
if(cmd_flags.debug & (1 << 1))
printf("\n");
#endif
return;
}
before = NULL;
after = NULL;
p = &(output_blocks);
while(*p){
block = *p;
if(offset < block->offset){
after = block;
break;
}
else{
before = block;
}
p = &(block->next);
}
if(before != NULL){
if(before->offset + before->size > offset){
warning("internal error: output_flush(offset = %u, size = %u)"
" overlaps with flushed block(offset = %u, size = %u)",
offset, size, before->offset, before->size);
printf("calling abort()\n");
abort();
}
}
if(after != NULL){
if(offset + size > after->offset){
warning("internal error: output_flush(offset = %u, size = %u)"
" overlaps with flushed block(offset = %u, size = %u)",
offset, size, after->offset, after->size);
printf("calling abort()\n");
abort();
}
}
if(before != NULL && before->offset + before->size == offset){
if(after != NULL && offset + size == after->offset){
if(before->offset == 0 && before->written_size == 0){
write_offset = 0;
before->written_offset = 0;
}
else
write_offset =before->written_offset + before->written_size;
if(after->written_size == 0)
write_size = trnc(after->offset + after->size -
write_offset, host_pagesize);
else
write_size = trnc(after->written_offset - write_offset,
host_pagesize);
if(write_size != 0){
before->written_size += write_size;
}
if(after->written_size != 0)
before->written_size += after->written_size;
before->size += size + after->size;
before->next = after->next;
remove_block(after);
}
else{
write_offset = before->written_offset + before->written_size;
write_size = trnc(offset + size - write_offset, host_pagesize);
if(write_size != 0)
before->written_size += write_size;
before->size += size;
}
}
else if(after != NULL && offset + size == after->offset){
write_offset = rnd(offset, host_pagesize);
if(after->written_size == 0)
write_size = trnc(after->offset + after->size - write_offset,
host_pagesize);
else
write_size = trnc(after->written_offset - write_offset,
host_pagesize);
if(write_size != 0){
after->written_offset = write_offset;
after->written_size += write_size;
}
else if(write_offset != after->written_offset){
after->written_offset = write_offset;
}
after->offset = offset;
after->size += size;
}
else{
write_offset = rnd(offset, host_pagesize);
write_size = trnc(offset + size - write_offset, host_pagesize);
block = get_block();
block->offset = offset;
block->size = size;
block->written_offset = write_offset;
block->written_size = write_size;
if(before != NULL){
block->next = before->next;
before->next = block;
}
else{
block->next = output_blocks;
output_blocks = block;
}
}
if(write_size != 0){
#ifdef DEBUG
if((cmd_flags.debug & (1 << 1)) || (cmd_flags.debug & (1 << 0)))
printf(" writing (write_offset = %lu write_size = %lu)\n",
write_offset, write_size);
#endif
lseek(fd, write_offset, L_SET);
if(write(fd, library + write_offset, write_size) !=
(int)write_size)
system_fatal("can't write to output file");
if((r = vm_deallocate(mach_task_self(), (vm_address_t)(library +
write_offset), write_size)) != KERN_SUCCESS)
mach_fatal(r, "can't vm_deallocate() buffer for output file");
}
#ifdef DEBUG
else{
if(cmd_flags.debug & (1 << 1))
printf(" no write\n");
}
#endif
}
static
void
final_output_flush(
char *library,
int fd)
{
struct block *block;
uint32_t write_offset, write_size;
kern_return_t r;
#ifdef DEBUG
write_offset = 0;
if((cmd_flags.debug & (1 << 1)) || (cmd_flags.debug & (1 << 0))){
printf("final_output_flush block_list:\n");
print_block_list();
}
#endif
write_size = 0;
block = output_blocks;
if(block != NULL){
if(block->offset != 0)
fatal("internal error: first block not at offset 0");
if(block->written_size != 0){
if(block->written_offset != 0)
fatal("internal error: first block written_offset not 0");
write_offset = block->written_size;
write_size = block->size - block->written_size;
}
else{
write_offset = block->offset;
write_size = block->size;
}
if(block->next != NULL)
fatal("internal error: more than one block in final list");
}
if(write_size != 0){
#ifdef DEBUG
if((cmd_flags.debug & (1 << 1)) || (cmd_flags.debug & (1 << 1)))
printf(" writing (write_offset = %lu write_size = %lu)\n",
write_offset, write_size);
#endif
lseek(fd, write_offset, L_SET);
if(write(fd, library + write_offset, write_size) !=
(int)write_size)
system_fatal("can't write to output file");
if((r = vm_deallocate(mach_task_self(), (vm_address_t)(library +
write_offset), write_size)) != KERN_SUCCESS)
mach_fatal(r, "can't vm_deallocate() buffer for output file");
}
output_blocks = NULL;
}
#ifdef DEBUG
static
void
print_block_list(void)
{
struct block **p, *block;
p = &(output_blocks);
if(*p == NULL)
printf("Empty block list\n");
while(*p){
block = *p;
printf("block 0x%x\n", (unsigned int)block);
printf(" offset %lu\n", block->offset);
printf(" size %lu\n", block->size);
printf(" written_offset %lu\n", block->written_offset);
printf(" written_size %lu\n", block->written_size);
printf(" next 0x%x\n", (unsigned int)(block->next));
p = &(block->next);
}
}
#endif
static
struct block *
get_block(void)
{
struct block *block;
block = allocate(sizeof(struct block));
return(block);
}
static
void
remove_block(
struct block *block)
{
free(block);
}
static
uint32_t
trnc(
uint32_t v,
uint32_t r)
{
if(((int32_t)v) < 0)
return(0);
return(v & ~(r - 1));
}
static
void
create_dynamic_shared_library(
char *output)
{
uint32_t i, j;
char *p, *filelist;
struct stat stat_buf;
enum bool use_force_cpusubtype_ALL;
const struct arch_flag *family_arch_flag;
if(narchs > 1)
signal(SIGINT, create_dynamic_shared_library_cleanup);
use_force_cpusubtype_ALL = TRUE;
if(cmd_flags.arch_only_flag.name != NULL){
family_arch_flag = get_arch_family_from_cputype(
cmd_flags.arch_only_flag.cputype);
if(family_arch_flag != NULL){
if((family_arch_flag->cpusubtype & ~CPU_SUBTYPE_MASK) !=
(cmd_flags.arch_only_flag.cpusubtype & ~CPU_SUBTYPE_MASK))
use_force_cpusubtype_ALL = FALSE;
}
}
for(i = 0; i < narchs || (i == 0 && narchs == 0); i++){
reset_execute_list();
add_execute_list_with_prefix("ld");
if(narchs != 0 && cmd_flags.arch_only_flag.name == NULL)
add_execute_list("-arch_multiple");
if(archs != NULL){
add_execute_list("-arch");
if(use_force_cpusubtype_ALL == TRUE)
add_execute_list(archs[i].arch_flag.name);
else
add_execute_list(cmd_flags.arch_only_flag.name);
}
add_execute_list("-dylib");
add_execute_list("-dynamic");
if(cmd_flags.all_load_flag_specified == FALSE ||
cmd_flags.all_load == TRUE)
add_execute_list("-all_load");
if(use_force_cpusubtype_ALL == TRUE)
add_execute_list("-force_cpusubtype_ALL");
add_execute_list("-no_arch_warnings");
if(cmd_flags.seg1addr != NULL){
add_execute_list("-seg1addr");
add_execute_list(cmd_flags.seg1addr);
}
if(cmd_flags.segs_read_only_addr != NULL){
add_execute_list("-segs_read_only_addr");
add_execute_list(cmd_flags.segs_read_only_addr);
}
if(cmd_flags.segs_read_write_addr != NULL){
add_execute_list("-segs_read_write_addr");
add_execute_list(cmd_flags.segs_read_write_addr);
}
if(cmd_flags.seg_addr_table != NULL){
add_execute_list("-seg_addr_table");
add_execute_list(cmd_flags.seg_addr_table);
}
if(cmd_flags.seg_addr_table_filename != NULL){
add_execute_list("-seg_addr_table_filename");
add_execute_list(cmd_flags.seg_addr_table_filename);
}
if(cmd_flags.compatibility != NULL){
add_execute_list("-dylib_compatibility_version");
add_execute_list(cmd_flags.compatibility);
}
if(cmd_flags.current != NULL){
add_execute_list("-dylib_current_version");
add_execute_list(cmd_flags.current);
}
if(cmd_flags.install_name != NULL){
add_execute_list("-dylib_install_name");
add_execute_list(cmd_flags.install_name);
}
else{
if(narchs > 1){
add_execute_list("-dylib_install_name");
add_execute_list(cmd_flags.output);
}
}
for(j = 0; j < cmd_flags.nldflags; j++)
add_execute_list(cmd_flags.ldflags[j]);
for(j = 0; j < cmd_flags.nLdirs; j++)
add_execute_list(cmd_flags.Ldirs[j]);
add_execute_list("-ldylib1.o");
filelist = NULL;
for(j = 0; j < cmd_flags.nfiles; j++){
if(cmd_flags.filelist[j] == NULL){
add_execute_list(cmd_flags.files[j]);
}
else{
if(cmd_flags.filelist[j] != filelist){
add_execute_list("-filelist");
add_execute_list(cmd_flags.filelist[j]);
filelist = cmd_flags.filelist[j];
}
}
}
if(narchs <= 1){
add_execute_list("-o");
add_execute_list(cmd_flags.output);
}
else{
add_execute_list("-o");
add_execute_list(makestr(cmd_flags.output, ".libtool.",
archs[i].arch_flag.name, NULL));
if(cmd_flags.final_output_specified == FALSE){
add_execute_list("-final_output");
add_execute_list(cmd_flags.output);
}
}
if(execute_list(cmd_flags.verbose) == 0)
fatal("internal link edit command failed");
}
if(narchs > 1){
reset_execute_list();
add_execute_list_with_prefix("lipo");
add_execute_list("-create");
add_execute_list("-output");
add_execute_list(cmd_flags.output);
for(i = 0; i < narchs; i++){
add_execute_list(makestr(cmd_flags.output, ".libtool.",
archs[i].arch_flag.name, NULL));
}
if(execute_list(cmd_flags.verbose) == 0)
fatal("internal lipo command failed");
for(i = 0; i < narchs; i++){
p = makestr(cmd_flags.output, ".libtool.",
archs[i].arch_flag.name, NULL);
if(unlink(p) == -1){
error("can't remove temporary file: %s", p);
}
}
}
if(cmd_flags.prebinding == TRUE){
if(stat("/usr/bin/objcunique", &stat_buf) != -1){
reset_execute_list();
add_execute_list_with_prefix("objcunique");
add_execute_list(cmd_flags.output);
add_execute_list("-prebind");
for(j = 0; j < cmd_flags.nLdirs; j++)
add_execute_list(cmd_flags.Ldirs[j]);
if(execute_list(cmd_flags.verbose) == 0)
fatal("internal objcunique command failed");
}
}
}
static
void
create_dynamic_shared_library_cleanup(
int sig)
{
uint32_t i;
for(i = 0; i < narchs; i++){
(void)unlink(makestr(cmd_flags.output, ".libtool.",
archs[i].arch_flag.name, NULL));
}
exit(EXIT_FAILURE);
}
static
void
make_table_of_contents(
struct arch *arch,
char *output)
{
uint32_t i, j, k, r, s, nsects, ncmds, n_strx;
struct member *member;
struct load_command *lc;
struct segment_command *sg;
struct segment_command_64 *sg64;
struct nlist *symbols;
struct nlist_64 *symbols64;
char *strings;
enum bool sorted, is_toc_symbol;
char *ar_name;
struct section *section;
struct section_64 *section64;
uint8_t n_type, n_sect;
#ifdef LTO_SUPPORT
uint32_t nsyms;
#endif
symbols = NULL;
symbols64 = NULL;
for(i = 0; i < arch->nmembers; i++){
member = arch->members + i;
if(member->mh != NULL || member->mh64 != NULL){
nsects = 0;
lc = member->load_commands;
if(member->mh != NULL)
ncmds = member->mh->ncmds;
else
ncmds = member->mh64->ncmds;
for(j = 0; j < ncmds; j++){
if(lc->cmd == LC_SYMTAB){
if(member->st == NULL)
member->st = (struct symtab_command *)lc;
}
else if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
nsects += sg->nsects;
}
else if(lc->cmd == LC_SEGMENT_64){
sg64 = (struct segment_command_64 *)lc;
nsects += sg64->nsects;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(member->mh != NULL)
member->sections = allocate(nsects *
sizeof(struct section *));
else
member->sections64 = allocate(nsects *
sizeof(struct section_64 *));
nsects = 0;
lc = member->load_commands;
for(j = 0; j < ncmds; j++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
section = (struct section *)
((char *)sg + sizeof(struct segment_command));
for(k = 0; k < sg->nsects; k++){
member->sections[nsects++] = section++;
}
}
else if(lc->cmd == LC_SEGMENT_64){
sg64 = (struct segment_command_64 *)lc;
section64 = (struct section_64 *)
((char *)sg64 + sizeof(struct segment_command_64));
for(k = 0; k < sg64->nsects; k++){
member->sections64[nsects++] = section64++;
}
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(member->st != NULL && member->st->nsyms != 0){
if(member->mh != NULL){
symbols = (struct nlist *)(member->object_addr +
member->st->symoff);
if(member->object_byte_sex != get_host_byte_sex())
swap_nlist(symbols, member->st->nsyms,
get_host_byte_sex());
}
else{
symbols64 = (struct nlist_64 *)(member->object_addr +
member->st->symoff);
if(member->object_byte_sex != get_host_byte_sex())
swap_nlist_64(symbols64, member->st->nsyms,
get_host_byte_sex());
}
strings = member->object_addr + member->st->stroff;
for(j = 0; j < member->st->nsyms; j++){
if(member->mh != NULL){
n_strx = symbols[j].n_un.n_strx;
n_type = symbols[j].n_type;
n_sect = symbols[j].n_sect;
}
else{
n_strx = symbols64[j].n_un.n_strx;
n_type = symbols64[j].n_type;
n_sect = symbols64[j].n_sect;
}
if(n_strx > member->st->strsize){
warn_member(arch, member, "malformed object "
"(symbol %u n_strx field extends past the "
"end of the string table)", j);
errors++;
continue;
}
if((n_type & N_TYPE) == N_SECT){
if(n_sect == NO_SECT){
warn_member(arch, member, "malformed object "
"(symbol %u must not have NO_SECT for its "
"n_sect field given its type (N_SECT))", j);
errors++;
continue;
}
if(n_sect > nsects){
warn_member(arch, member, "malformed object "
"(symbol %u n_sect field greater than the "
"number of sections in the file)", j);
errors++;
continue;
}
}
if(member->mh != NULL)
is_toc_symbol = toc_symbol(symbols + j,
member->sections);
else
is_toc_symbol = toc_symbol_64(symbols64 + j,
member->sections64);
if(is_toc_symbol == TRUE){
arch->toc_nranlibs++;
arch->toc_strsize += strlen(strings + n_strx) + 1;
}
}
}
else
warn_member(arch, member, "has no symbols");
}
#ifdef LTO_SUPPORT
else if(member->lto != NULL){
nsyms = lto_get_nsyms(member->lto);
for(j = 0; j < nsyms; j++){
if(lto_toc_symbol(member->lto, j, cmd_flags.c) == TRUE){
arch->toc_nranlibs++;
arch->toc_strsize +=
strlen(lto_symbol_name(member->lto, j)) + 1;
}
}
}
#endif
else{
if(cmd_flags.ranlib == FALSE){
warn_member(arch, member, "is not an object file");
errors++;
}
}
}
if(errors != 0)
return;
arch->toc_ranlibs = allocate(sizeof(struct ranlib) *arch->toc_nranlibs);
arch->tocs = allocate(sizeof(struct toc) * arch->toc_nranlibs);
arch->toc_strsize = rnd(arch->toc_strsize, 8);
arch->toc_strings = allocate(arch->toc_strsize);
r = 0;
s = 0;
for(i = 0; i < arch->nmembers; i++){
member = arch->members + i;
if(member->mh != NULL || member->mh64 != NULL){
if(member->st != NULL && member->st->nsyms != 0){
if(member->mh != NULL)
symbols = (struct nlist *)(member->object_addr +
member->st->symoff);
else
symbols64 = (struct nlist_64 *)(member->object_addr +
member->st->symoff);
strings = member->object_addr + member->st->stroff;
for(j = 0; j < member->st->nsyms; j++){
if(member->mh != NULL)
n_strx = symbols[j].n_un.n_strx;
else
n_strx = symbols64[j].n_un.n_strx;
if(n_strx > member->st->strsize)
continue;
if(member->mh != NULL)
is_toc_symbol = toc_symbol(symbols + j,
member->sections);
else
is_toc_symbol = toc_symbol_64(symbols64 + j,
member->sections64);
if(is_toc_symbol == TRUE){
strcpy(arch->toc_strings + s,
strings + n_strx);
arch->tocs[r].name = arch->toc_strings + s;
arch->tocs[r].index1 = i + 1;
r++;
s += strlen(strings + n_strx) + 1;
}
}
if(member->object_byte_sex != get_host_byte_sex()){
if(member->mh != NULL)
swap_nlist(symbols, member->st->nsyms,
member->object_byte_sex);
else
swap_nlist_64(symbols64, member->st->nsyms,
member->object_byte_sex);
}
}
}
#ifdef LTO_SUPPORT
else if(member->lto != NULL){
nsyms = lto_get_nsyms(member->lto);
for(j = 0; j < nsyms; j++){
if(lto_toc_symbol(member->lto, j, cmd_flags.c) == TRUE){
strcpy(arch->toc_strings + s,
lto_symbol_name(member->lto, j));
arch->tocs[r].name = arch->toc_strings + s;
arch->tocs[r].index1 = i + 1;
r++;
s += strlen(lto_symbol_name(member->lto, j)) + 1;
}
}
}
#endif
}
if(cmd_flags.s == TRUE){
qsort(arch->tocs, arch->toc_nranlibs, sizeof(struct toc),
(int (*)(const void *, const void *))toc_name_qsort);
sorted = check_sort_tocs(arch, output, FALSE);
if(sorted == FALSE){
qsort(arch->tocs, arch->toc_nranlibs, sizeof(struct toc),
(int (*)(const void *, const void *))toc_index1_qsort);
arch->toc_name = SYMDEF;
arch->toc_name_size = sizeof(SYMDEF) - 1;
if(cmd_flags.use_long_names == TRUE){
arch->toc_long_name = TRUE;
ar_name = AR_EFMT1 "20";
arch->toc_name_size = 16;
arch->toc_name = SYMDEF "\0\0\0\0\0\0\0";
}
else{
arch->toc_long_name = FALSE;
ar_name = arch->toc_name;
}
}
else{
arch->toc_name = SYMDEF_SORTED;
arch->toc_name_size = sizeof(SYMDEF_SORTED) - 1;
if(cmd_flags.use_long_names == TRUE){
arch->toc_long_name = TRUE;
ar_name = AR_EFMT1 "20";
}
else{
arch->toc_long_name = FALSE;
ar_name = arch->toc_name;
}
}
}
else{
sorted = FALSE;
arch->toc_name = SYMDEF;
arch->toc_name_size = sizeof(SYMDEF) - 1;
if(cmd_flags.use_long_names == TRUE){
arch->toc_long_name = TRUE;
ar_name = AR_EFMT1 "20";
arch->toc_name_size = 16;
arch->toc_name = SYMDEF "\0\0\0\0\0\0\0";
}
else{
arch->toc_long_name = FALSE;
ar_name = arch->toc_name;
}
}
arch->toc_size = sizeof(struct ar_hdr) +
sizeof(uint32_t) +
arch->toc_nranlibs * sizeof(struct ranlib) +
sizeof(uint32_t) +
arch->toc_strsize;
if(arch->toc_long_name == TRUE)
arch->toc_size += arch->toc_name_size +
(rnd(sizeof(struct ar_hdr), 8) -
sizeof(struct ar_hdr));
for(i = 0; i < arch->nmembers; i++)
arch->members[i].offset += SARMAG + arch->toc_size;
for(i = 0; i < arch->toc_nranlibs; i++){
arch->toc_ranlibs[i].ran_un.ran_strx =
arch->tocs[i].name - arch->toc_strings;
arch->toc_ranlibs[i].ran_off =
arch->members[arch->tocs[i].index1 - 1].offset;
}
sprintf((char *)(&arch->toc_ar_hdr), "%-*s%-*ld%-*u%-*u%-*o%-*ld",
(int)sizeof(arch->toc_ar_hdr.ar_name),
ar_name,
(int)sizeof(arch->toc_ar_hdr.ar_date),
toc_time,
(int)sizeof(arch->toc_ar_hdr.ar_uid),
(unsigned short)getuid(),
(int)sizeof(arch->toc_ar_hdr.ar_gid),
(unsigned short)getgid(),
(int)sizeof(arch->toc_ar_hdr.ar_mode),
(unsigned int)toc_mode,
(int)sizeof(arch->toc_ar_hdr.ar_size),
(long)(arch->toc_size - sizeof(struct ar_hdr)));
memcpy(arch->toc_ar_hdr.ar_fmag, ARFMAG,
(int)sizeof(arch->toc_ar_hdr.ar_fmag));
}
static
int
toc_name_qsort(
const struct toc *toc1,
const struct toc *toc2)
{
return(strcmp(toc1->name, toc2->name));
}
static
int
toc_index1_qsort(
const struct toc *toc1,
const struct toc *toc2)
{
if(toc1->index1 < toc2->index1)
return(-1);
if(toc1->index1 > toc2->index1)
return(1);
return(0);
}
static
enum bool
toc_symbol(
struct nlist *symbol,
struct section **sections)
{
return(toc(symbol->n_un.n_strx,
symbol->n_type,
symbol->n_value,
(symbol->n_type & N_TYPE) == N_SECT &&
sections[symbol->n_sect - 1]->flags & S_ATTR_NO_TOC));
}
static
enum bool
toc_symbol_64(
struct nlist_64 *symbol64,
struct section_64 **sections64)
{
return(toc(symbol64->n_un.n_strx,
symbol64->n_type,
symbol64->n_value,
(symbol64->n_type & N_TYPE) == N_SECT &&
sections64[symbol64->n_sect-1]->flags & S_ATTR_NO_TOC));
}
static
enum bool
toc(
uint32_t n_strx,
uint8_t n_type,
uint64_t n_value,
enum bool attr_no_toc)
{
if(n_strx == 0)
return(FALSE);
if((n_type & N_EXT) == 0)
return(FALSE);
if((n_type & N_TYPE) == N_UNDF && n_value == 0)
return(FALSE);
if((n_type & N_TYPE) == N_UNDF && n_value != 0 &&
cmd_flags.c == FALSE)
return(FALSE);
if(attr_no_toc != 0)
return(FALSE);
return(TRUE);
}
static
enum bool
check_sort_tocs(
struct arch *arch,
char *output,
enum bool library_warnings)
{
uint32_t i;
enum bool multiple_defs;
struct member *member;
if(arch->toc_nranlibs == 0)
return(TRUE);
multiple_defs = FALSE;
for(i = 0; i < arch->toc_nranlibs - 1; i++){
if(strcmp(arch->tocs[i].name, arch->tocs[i+1].name) == 0){
if(multiple_defs == FALSE){
if(library_warnings == FALSE)
return(FALSE);
fprintf(stderr, "%s: same symbol defined in more than one "
"member ", progname);
if(narchs > 1)
fprintf(stderr, "for architecture: %s ",
arch->arch_flag.name);
fprintf(stderr, "in: %s (table of contents will not be "
"sorted)\n", output);
multiple_defs = TRUE;
}
if((int)(arch->tocs[i].index1) > 0){
member = arch->members + arch->tocs[i].index1 - 1;
warn_member(arch, member, "defines symbol: %s",
arch->tocs[i].name);
arch->tocs[i].index1 =
-(arch->tocs[i].index1);
}
if((int)(arch->tocs[i+1].index1) > 0){
member = arch->members + arch->tocs[i+1].index1 - 1;
warn_member(arch, member, "defines symbol: %s",
arch->tocs[i+1].name);
arch->tocs[i+1].index1 =
-(arch->tocs[i+1].index1);
}
}
}
if(multiple_defs == FALSE)
return(TRUE);
else{
for(i = 0; i < arch->toc_nranlibs; i++)
if(((int)arch->tocs[i].index1) < 0)
arch->tocs[i].index1 =
-(arch->tocs[i].index1);
return(FALSE);
}
}
static
void
warn_duplicate_member_names(
void)
{
uint32_t i, j, len, len1, len2;
for(i = 0; i < narchs; i++){
qsort(archs[i].members, archs[i].nmembers, sizeof(struct member),
(int (*)(const void *, const void *))member_name_qsort);
for(j = 0; j < archs[i].nmembers - 1; j++){
len1 = archs[i].members[j].member_name_size;
len2 = archs[i].members[j+1].member_name_size;
len = len1 > len2 ? len1 : len2;
if(strncmp(archs[i].members[j].member_name,
archs[i].members[j+1].member_name,
len) == 0){
fprintf(stderr, "%s: warning ", progname);
if(narchs > 1)
fprintf(stderr, "for architecture: %s ",
archs[i].arch_flag.name);
fprintf(stderr, "same member name (%.*s) in output file "
"used for input files: ", (int)len1,
archs[i].members[j].member_name);
if(archs[i].members[j].input_ar_hdr != NULL){
len = archs[i].members[j].input_base_name_size;
fprintf(stderr, "%s(%.*s) and: ",
archs[i].members[j].input_file_name, (int)len,
archs[i].members[j].input_base_name);
}
else
fprintf(stderr, "%s and: ",
archs[i].members[j].input_file_name);
if(archs[i].members[j+1].input_ar_hdr != NULL){
len = archs[i].members[j+1].input_base_name_size;
fprintf(stderr, "%s(%.*s) due to use of basename, "
"truncation and blank padding\n",
archs[i].members[j+1].input_file_name, (int)len,
archs[i].members[j+1].input_base_name);
}
else
fprintf(stderr, "%s (due to use of basename, truncation"
", blank padding or duplicate input files)\n",
archs[i].members[j+1].input_file_name);
}
}
qsort(archs[i].members, archs[i].nmembers, sizeof(struct member),
(int (*)(const void *, const void *))member_offset_qsort);
}
}
static
int
member_name_qsort(
const struct member *member1,
const struct member *member2)
{
uint32_t len, len1, len2;
len1 = member1->member_name_size;
len2 = member2->member_name_size;
len = len1 > len2 ? len1 : len2;
return(strncmp(member1->member_name, member2->member_name, len));
}
static
int
member_offset_qsort(
const struct member *member1,
const struct member *member2)
{
if(member1->offset < member2->offset)
return(-1);
if(member1->offset > member2->offset)
return(1);
return(0);
}
static
void
warn_member(
struct arch *arch,
struct member *member,
const char *format, ...)
{
va_list ap;
fprintf(stderr, "%s: ", progname);
if(narchs > 1)
fprintf(stderr, "for architecture: %s ", arch->arch_flag.name);
if(member->input_ar_hdr != NULL){
fprintf(stderr, "file: %s(%.*s) ", member->input_file_name,
(int)member->input_base_name_size, member->input_base_name);
}
else
fprintf(stderr, "file: %s ", member->input_file_name);
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static
void
ld_trace(
const char *format, ...)
{
static int trace_file = -1;
char trace_buffer[MAXPATHLEN * 2];
char *buffer_ptr;
int length;
ssize_t amount_written;
if(trace_file == -1){
if(cmd_flags.trace_file_path != NULL){
trace_file = open(cmd_flags.trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666);
if(trace_file == -1)
error("Could not open or create trace file: %s\n", cmd_flags.trace_file_path);
}
else{
trace_file = fileno(stderr);
}
}
va_list ap;
va_start(ap, format);
length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap);
va_end(ap);
buffer_ptr = trace_buffer;
while(length > 0){
amount_written = write(trace_file, buffer_ptr, length);
if(amount_written == -1)
return;
buffer_ptr += amount_written;
length -= amount_written;
}
}