cctools-port/cctools/misc/seg_addr_table.c

2235 lines
73 KiB
C

/*
* Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include "stuff/ofile.h"
#include "stuff/allocate.h"
#include "stuff/rnd.h"
#include "stuff/errors.h"
#include "stuff/seg_addr_table.h"
#include "stuff/guess_short_name.h"
#include "stuff/dylib_table.h"
#include "stuff/dylib_roots.h"
#include "stuff/macosx_deployment_target.h"
/*
* These are the default addresses use when re-laying out the images. These
* changed in Mac OS X in the 10.2 release.
*/
#define DEFAULT_SEG1ADDR_X10_1 0x41300000 /* low to high allocation */
#define DEFAULT_READ_ONLY_ADDR_X10_1 0x70000000
#define DEFAULT_READ_WRITE_ADDR_X10_1 0x80000000
#define DEFAULT_SEG1ADDR_X10_2 0x8fe00000 /* high to low allocation */
#define DEFAULT_READ_ONLY_ADDR_X10_2 0x90000000
#define DEFAULT_READ_WRITE_ADDR_X10_2 0xa0000000
/*
* Starting in 10.3, we will put all the debug and profile libraries together,
* low in the flat region (and assigned in descending order)
*/
#define DEFAULT_DEBUG_ADDR_X10_3 0x40000000
/*
* The 256meg regions can only have at most 128meg allocated from them leaving
* half of them for the "alternate" area. So if any library starts at an
* address and ends at an address with these top bits different then we have
* overflowed the lower 128meg part that is to be allocated.
*/
#define SPLIT_OVERFLOW_MASK_X10_3 0xf8000000
#define SPLIT_OVERFLOW_MASK_X10_4 0xf0000000
/*
* For intel the kernel limits the address space to the lower 3 gig so we don't
* want to put any addresses above this.
*/
#define MAX_ADDR 0xc0000000
/*
* These are just used in the layout info structs created in the
* sorted_flat_layout_info array so that next_flat_seg1addr() will step over
* them.
*/
#define READ_ONLY_SEGMENT_NAME "<< read-only segment for split libraries >>"
#define READ_WRITE_SEGMENT_NAME "<< read-write segment for split libraries >>"
/*
* These are the default factor (power of 2) to multiply the sizes of the
* segments of of an image and what to round that size up to to allow the
* image grow without overlapping the next image.
*
* The two 256meg regions for split libraries will overflow when the doing
* a -relayout of the Gonzo1H libraries and a DEFAULT_FACTOR of more than
* 1 or trying to used the SYMROOT files. So the DEFAULT_FACTOR is not used for
* split libraries and only the DEFAULT_ROUND.
*/
#define DEFAULT_FACTOR 0x1 /* power of 2, so this is 1<<1 = 2 */
#define DEFAULT_ROUND_X10_3 0x10000
#define DEFAULT_ROUND_X10_4 0x1000
/* used by error routines as the name of the program */
char *progname = NULL;
/*
* This structure is passed to the routine max_size to accumulate the maximum
* size of the segments a given image for the architectures being considered.
*/
struct max_sizes {
uint32_t all;
uint32_t read_only;
uint32_t read_write;
};
/*
* This structure is used when re-laying out of the table. Dynamic libraries
* which are laid out flat that have the same name but with differ only in
* suffix (like no suffix, _profile, _debug, etc) are given the same address.
* The base name must be the same to say that it is the same dynamic library.
* That is /lib/libx.A.dylib is not the same as /usr/local/libx_profile.A.dylib.
* For dynamic libraries which are laid out split each one is given a different
* address.
*/
struct layout_info {
char *install_name;
char *image_file_name;
struct max_sizes max_sizes;
enum bool split;
enum bool use_debug_region;
/* used only for flat layouts */
enum bool assigned;
char *short_name;
uint32_t seg1addr;
/* used only for split layouts */
uint32_t segs_read_only_addr;
uint32_t segs_read_write_addr;
/* the current entry when calling sizes_and_addresses() */
struct seg_addr_table *current_entry;
};
/*
* This structure has the info needed to re-layout or update the
* seg_addr_table.
*/
struct info {
/* the file name of the input seg_addr_table */
char *seg_addr_table_name;
/* the parsed seg_addr_table */
struct seg_addr_table *seg_addr_table;
uint32_t table_size;
/* the array of pointers to info for layout for each entry */
struct layout_info **layout_info;
/* the arrays of pointers to sorted layout info for flat/split entries */
struct layout_info **sorted_flat_layout_info;
uint32_t nsorted_flat;
struct layout_info **sorted_split_read_only_layout_info;
struct layout_info **sorted_split_read_write_layout_info;
uint32_t nsorted_split;
/* The first segment address for non-split images */
uint32_t seg1addr;
uint32_t next_flat_line;
enum bool allocate_flat_increasing;
/* Address in the flat region where debug/profile libs will be placed */
uint32_t debug_seg1addr;
/* read-only and read-write segment addresses for split images*/
uint32_t start_segs_read_only_addr;
uint32_t start_segs_read_write_addr;
uint32_t segs_read_only_addr;
uint32_t segs_read_write_addr;
uint32_t next_split_line;
uint32_t default_read_only_addr;
uint32_t default_read_write_addr;
/* the specified -arch flags */
struct arch_flag *arch_flags;
uint32_t narch_flags;
enum bool all_archs;
/* the file name of the output seg_addr_table */
char *output_file_name;
/* the factor and amount to round applied to the sizes for layout */
uint32_t factor;
uint32_t round;
/* the -release argument for translating to SYMROOT files */
char *release_name;
/* the -disablewarnings flag */
enum bool disablewarnings;
/* Mask for determining size of the split region */
uint32_t overflow_mask;
/* The MACOSX_DEPLOYMENT_TARGET */
struct macosx_deployment_target macosx_deployment_target;
};
static void usage(
void);
static FILE * create_output_file(
char *output_file_name);
static int qsort_flat(
const struct layout_info **p1,
const struct layout_info **p2);
static int qsort_split_read_only(
const struct layout_info **p1,
const struct layout_info **p2);
static int qsort_split_read_write(
const struct layout_info **p1,
const struct layout_info **p2);
static void flat_overlap_error(
struct info *info,
uint32_t i1,
uint32_t i2,
enum bool next_address);
static void split_overlap_error(
struct info *info,
uint32_t i1,
uint32_t i2,
struct layout_info **sorted_layout_info,
char *segment,
enum bool next_address);
static struct seg_addr_table * search_seg_addr_table_for_fixed(
struct seg_addr_table *seg_addr_table,
uint32_t seg1addr,
uint32_t size);
static uint32_t next_flat_seg1addr(
struct info *info,
uint32_t size);
static uint32_t next_debug_seg1addr(
struct info *info,
uint32_t size);
static char * get_image_file_name(
struct info *info,
char *install_name,
enum bool try_symroot,
enum bool no_error_if_missing);
static void new_table_processor(
struct seg_addr_table *entry,
FILE *out_fp,
void *cookie);
static void sizes_and_addresses(
struct ofile *ofile,
char *arch_name,
void *cookie);
static void dylib_table_processor(
struct seg_addr_table *entry,
FILE *out_fp,
void *cookie);
static void get_seg1addr(
struct ofile *ofile,
char *arch_name,
void *cookie);
/* apple_version is created by the libstuff/Makefile */
extern char apple_version[];
char *version = apple_version;
/*
* The seg_addr_table program. It takes a file which contains the starting
* segment address of images and can update or re-layout the table based on
* the images it finds on the machine it is running on.
*/
int
main(
int argc,
char **argv,
char **envp)
{
int a;
uint32_t i, j, used, max, size, seg1addr;
char *endp, *user, *dylib_table_name, *base_name, *short_name,
*image_file_name;
struct dylib_table *dylib_table;
FILE *out_fp;
time_t tloc;
struct info info;
struct seg_addr_table *entry;
struct layout_info *layout_info;
enum bool seg1addr_specified, segs_read_only_addr_specified,
segs_read_write_addr_specified, relayout, update, create,
checkonly, update_overlaps, allocate_flat_specified,
relayout_nonsplit;
enum bool found, is_framework, next_flat, next_split, next_debug;
enum bool operation_specified, from_dylib_table, create_dylib_table;
char *install_name, *has_suffix;
struct stat stat_buf;
struct macosx_deployment_target macosx_deployment_target;
progname = argv[0];
dylib_table_name = NULL;
dylib_table = NULL;
info.seg_addr_table = NULL;
info.seg_addr_table_name = NULL;
info.table_size = 0;
info.layout_info = NULL;
info.arch_flags = NULL;
info.narch_flags = 0;
info.all_archs = FALSE;
info.disablewarnings = FALSE;
operation_specified = FALSE;
relayout = FALSE;
update = FALSE;
checkonly = FALSE;
update_overlaps = FALSE;
create = FALSE;
from_dylib_table = FALSE;
create_dylib_table = FALSE;
relayout_nonsplit = FALSE;
info.output_file_name = NULL;
out_fp = NULL;
seg1addr_specified = FALSE;
allocate_flat_specified = FALSE;
segs_read_only_addr_specified = FALSE;
segs_read_write_addr_specified = FALSE;
info.factor = DEFAULT_FACTOR;
/*
* Pick up the Mac OS X deployment target and set the defaults based
* on it.
*/
get_macosx_deployment_target(&macosx_deployment_target);
/*
* In 10.4 and later we now only round to the nearest page and
* allow the entire 256 mb split region to be used.
*/
if(macosx_deployment_target.major >= 4) {
info.round = DEFAULT_ROUND_X10_4;
info.overflow_mask = SPLIT_OVERFLOW_MASK_X10_4;
} else {
info.round = DEFAULT_ROUND_X10_3;
info.overflow_mask = SPLIT_OVERFLOW_MASK_X10_3;
}
if(macosx_deployment_target.major >= 2){
info.allocate_flat_increasing = FALSE;
info.seg1addr = DEFAULT_SEG1ADDR_X10_2;
info.segs_read_only_addr = DEFAULT_READ_ONLY_ADDR_X10_2;
info.segs_read_write_addr = DEFAULT_READ_WRITE_ADDR_X10_2;
}
else{
info.allocate_flat_increasing = TRUE;
info.seg1addr = DEFAULT_SEG1ADDR_X10_1;
info.segs_read_only_addr = DEFAULT_READ_ONLY_ADDR_X10_1;
info.segs_read_write_addr = DEFAULT_READ_WRITE_ADDR_X10_1;
}
info.default_read_only_addr = info.segs_read_only_addr;
info.default_read_write_addr = info.segs_read_write_addr;
info.start_segs_read_only_addr = info.segs_read_only_addr;
info.start_segs_read_write_addr = info.segs_read_write_addr;
info.macosx_deployment_target = macosx_deployment_target;
info.debug_seg1addr = DEFAULT_DEBUG_ADDR_X10_3;
info.release_name = NULL;
for(a = 1; a < argc; a++){
if(argv[a][0] == '-'){
if(strcmp(argv[a], "-relayout") == 0){
if(operation_specified == TRUE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
relayout = TRUE;
}
else if(strcmp(argv[a], "-update") == 0){
if(operation_specified == TRUE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
update = TRUE;
}
else if(strcmp(argv[a], "-checkonly") == 0){
if(operation_specified == TRUE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
checkonly = TRUE;
}
else if(strcmp(argv[a], "-update_overlaps") == 0){
if(operation_specified == TRUE &&
relayout_nonsplit == FALSE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
update_overlaps = TRUE;
}
else if(strcmp(argv[a], "-create") == 0){
if(operation_specified == TRUE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
create = TRUE;
}
else if(strcmp(argv[a], "-relayout_nonsplit") == 0){
if(operation_specified == TRUE && update_overlaps == FALSE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
relayout_nonsplit = TRUE;
}
else if(strcmp(argv[a], "-from_dylib_table") == 0){
if(operation_specified == TRUE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
from_dylib_table = TRUE;
}
else if(strcmp(argv[a], "-create_dylib_table") == 0){
if(operation_specified == TRUE){
error("more than one operation specified");
usage();
}
operation_specified = TRUE;
create_dylib_table = TRUE;
}
else if(strcmp(argv[a], "-seg_addr_table") == 0){
if(a + 1 == argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
if(info.seg_addr_table != NULL){
error("more than one: %s option", argv[a]);
usage();
}
info.seg_addr_table_name = argv[a+1];
info.seg_addr_table = parse_seg_addr_table(argv[a+1],
argv[a], argv[a+1], &info.table_size);
a++;
}
else if(strcmp(argv[a], "-dylib_table") == 0){
if(a + 1 == argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
if(dylib_table_name != NULL){
error("more than one: %s option", argv[a]);
usage();
}
dylib_table_name = argv[a+1];
dylib_table = parse_dylib_table(argv[a+1], argv[a],
argv[a+1]);
a++;
}
/* specify the address (in hex) of the first segment
-seg1addr <address> */
else if(strcmp(argv[a], "-seg1addr") == 0){
if(a + 1 >= argc){
error("%s: argument missing", argv[a]);
usage();
}
if(seg1addr_specified == TRUE){
error("more than one: %s option", argv[a]);
usage();
}
info.seg1addr = strtoul(argv[a+1], &endp, 16);
if(*endp != '\0')
fatal("address for %s %s not a proper "
"hexadecimal number", argv[a], argv[a+1]);
seg1addr_specified = TRUE;
a++;
}
/* specify which way to allocate flat libraries
-allocate_flat increasing
or
-allocate_flat decreasing */
else if(strcmp(argv[a], "-allocate_flat") == 0){
if(a + 1 >= argc){
error("%s: argument missing", argv[a]);
usage();
}
if(allocate_flat_specified == TRUE){
error("more than one: %s option", argv[a]);
usage();
}
if(strcmp(argv[a+1], "increasing") == 0)
info.allocate_flat_increasing = TRUE;
else if(strcmp(argv[a+1], "decreasing") == 0)
info.allocate_flat_increasing = FALSE;
else
fatal("argument: %s for %s not valid (can be either "
"increasing or decreasing)", argv[a+1], argv[a]);
allocate_flat_specified = TRUE;
a++;
}
/* specify the address (in hex) of the read-only segments
-segs_read_only_addr <address> */
else if(strcmp(argv[a], "-segs_read_only_addr") == 0){
if(a + 1 >= argc){
error("%s: argument missing", argv[a]);
usage();
}
if(segs_read_only_addr_specified == TRUE){
error("more than one: %s option", argv[a]);
usage();
}
info.segs_read_only_addr =
strtoul(argv[a+1], &endp, 16);
info.start_segs_read_only_addr = info.segs_read_only_addr;
if(*endp != '\0')
fatal("address for %s %s not a proper "
"hexadecimal number", argv[a], argv[a+1]);
segs_read_only_addr_specified = TRUE;
a++;
}
/* specify the address (in hex) of the read-write segments
-segs_read_write_addr <address> */
else if(strcmp(argv[a], "-segs_read_write_addr") == 0){
if(a + 1 >= argc){
error("%s: argument missing", argv[a]);
usage();
}
if(segs_read_write_addr_specified == TRUE){
error("more than one: %s option", argv[a]);
usage();
}
info.segs_read_write_addr =
strtoul(argv[a+1], &endp, 16);
info.start_segs_read_write_addr = info.segs_read_write_addr;
if(*endp != '\0')
fatal("address for %s %s not a proper "
"hexadecimal number", argv[a], argv[a+1]);
segs_read_write_addr_specified = TRUE;
a++;
}
else if(strcmp(argv[a], "-arch") == 0){
if(a + 1 == argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
if(strcmp("all", argv[a+1]) == 0){
info.all_archs = TRUE;
}
else{
info.arch_flags = reallocate(info.arch_flags,
(info.narch_flags + 1) *
sizeof(struct arch_flag));
if(get_arch_from_flag(argv[a+1],
info.arch_flags + info.narch_flags) == 0){
error("unknown architecture specification flag: "
"%s %s", argv[a], argv[a+1]);
arch_usage();
usage();
}
info.narch_flags++;
}
a++;
}
else if(strcmp(argv[a], "-o") == 0){
if(a + 1 == argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
if(info.output_file_name != NULL){
error("more than one: %s %s option", argv[a],argv[a+1]);
usage();
}
info.output_file_name = argv[a+1];
a++;
}
else if(strcmp(argv[a], "-release") == 0){
if(a + 1 == argc){
error("missing argument(s) to %s option", argv[a]);
usage();
}
if(info.release_name != NULL){
error("more than one: %s option", argv[a]);
usage();
}
info.release_name = argv[a+1];
a++;
}
else if(strcmp(argv[a], "-disablewarnings") == 0){
info.disablewarnings = TRUE;
}
else{
error("unknown option: %s\n", argv[a]);
usage();
}
}
else{
error("unknown argument: %s\n", argv[a]);
usage();
}
}
/* check the sizes of all architectures by default */
if(info.narch_flags == 0)
info.all_archs = TRUE;
if(operation_specified == FALSE){
error("no operation specified");
usage();
}
if(checkonly == FALSE && info.output_file_name == NULL){
error("must specify an output with the -o option");
usage();
}
if((checkonly == TRUE || relayout == TRUE) &&
info.disablewarnings == TRUE){
warning("-disablewarnings has no effect with -checkonly or "
"-relayout");
info.disablewarnings = FALSE;
}
if(update == TRUE || update_overlaps == TRUE){
if(seg1addr_specified == TRUE)
fatal("can't specify -seg1addr option when -update or "
"-update_overlaps is used");
if(segs_read_only_addr_specified == TRUE)
fatal("can't specify -segs_read_only_addr option when -update "
"or -update_overlaps is used");
if(segs_read_write_addr_specified == TRUE)
fatal("can't specify -segs_read_write_addr option when -update "
"or -update_overlaps is used");
}
/*
* If there is no seg_addr_table open the default table and use it.
*/
if(info.seg_addr_table == NULL && from_dylib_table == FALSE)
info.seg_addr_table = parse_default_seg_addr_table(
&info.seg_addr_table_name, &info.table_size);
#ifdef DEBUG
if(dylib_table == NULL)
dylib_table = parse_default_dylib_table(&dylib_table_name);
if(dylib_table != NULL){
printf("dylib_table %s\n", dylib_table_name);
for(i = 0; dylib_table[i].name != NULL; i++){
printf(" %d\n", i);
printf("\tname = %s\n", dylib_table[i].name);
printf("\tseg1addr = 0x%x\n",
(unsigned int)dylib_table[i].seg1addr);
install_name = guess_dylib_install_name(dylib_table[i].name);
if(install_name != NULL)
printf("\tinstall_name = %s\n", install_name);
}
}
if(info.seg_addr_table != NULL){
printf("seg_addr_table %s\n", info.seg_addr_table_name);
for(i = 0; info.seg_addr_table[i].install_name != NULL; i++){
printf(" %d\n", i);
printf("\tinstall_name = %s\n",
info.seg_addr_table[i].install_name);
printf("\tsplit = %s\n", info.seg_addr_table[i].split == TRUE ?
"TRUE" : "FALSE");
if(info.seg_addr_table[i].split == TRUE){
printf("\tsegs_read_only_addr = 0x%x\n",
(unsigned int)info.seg_addr_table[i].segs_read_only_addr);
printf("\tsegs_read_write_addr = 0x%x\n",
(unsigned int)info.seg_addr_table[i].segs_read_write_addr);
}
else{
printf("\tseg1addr = 0x%x\n",
(unsigned int)info.seg_addr_table[i].seg1addr);
}
short_name = guess_short_name(
info.seg_addr_table[i].install_name,
&is_framework, &has_suffix);
if(short_name != NULL){
printf("\tshort_name = %s\n", short_name);
printf("\tis_framework = %s\n", is_framework == TRUE ?
"TRUE" : "FALSE");
printf("\thas_suffix = %s\n", has_suffix == NULL ?
"NULL" : has_suffix);
if(search_dylib_table(dylib_table, short_name) == NULL)
printf("\tNOT in dylib_table\n");
else
printf("\thas entry in dylib_table\n");
}
}
}
#endif /* DEBUG */
/*
* If trying to create a segment address table from a dylib table then
* guess at all the install names.
*/
if(from_dylib_table == TRUE){
if(dylib_table == NULL)
dylib_table = parse_default_dylib_table(&dylib_table_name);
for(i = 0; dylib_table[i].name != NULL; i++){
install_name = guess_dylib_install_name(dylib_table[i].name);
if(install_name == NULL)
error("can't guess install name for: %s",
dylib_table[i].name);
else{
if(out_fp == NULL)
out_fp = create_output_file(info.output_file_name);
fprintf(out_fp, "0x%x\t%s\n",
(unsigned int)dylib_table[i].seg1addr,
install_name);
}
}
}
/*
* If trying to create a dylib table from a segment address table then
* process the segment address table to determine the addresses and
* guess at the short name of the library.
*/
if(create_dylib_table == TRUE){
out_fp = create_output_file(info.output_file_name);
process_seg_addr_table(info.seg_addr_table_name, out_fp, progname,
dylib_table_processor, &info);
}
/*
* If the -update, -update_overlap or -checkonly options are
* specified then pick up the next addresses
* to assign. The addresses are assigned starting at the
* NEXT_FLAT_ADDRESS_TO_ASSIGN, NEXT_SPLIT_ADDRESS_TO_ASSIGN and
* NEXT_DEBUG_ADDRESS_TO_ASSIGN.
*/
if(update == TRUE || checkonly == TRUE || update_overlaps == TRUE){
next_flat = FALSE;
next_debug = FALSE;
next_split = FALSE;
for(i = 0 ; i < info.table_size; i++){
entry = info.seg_addr_table + i;
if(strcmp(entry->install_name,
NEXT_FLAT_ADDRESS_TO_ASSIGN) == 0){
if(next_flat == TRUE)
fatal("segment address table: %s has more than one "
"entry for %s", info.seg_addr_table_name,
NEXT_FLAT_ADDRESS_TO_ASSIGN);
if(entry->split == TRUE)
fatal("segment address table: %s entry for %s is not "
"a single address", info.seg_addr_table_name,
NEXT_FLAT_ADDRESS_TO_ASSIGN);
next_flat = TRUE;
if(relayout_nonsplit == FALSE)
info.seg1addr = entry->seg1addr;
info.next_flat_line = entry->line;
}
else if(strcmp(entry->install_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN) == 0){
if(next_split == TRUE)
fatal("segment address table: %s has more than one "
"entry for %s", info.seg_addr_table_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
if(entry->split == FALSE)
fatal("segment address table: %s entry for %s is "
"a single address", info.seg_addr_table_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
next_split = TRUE;
info.segs_read_only_addr = entry->segs_read_only_addr;
info.segs_read_write_addr = entry->segs_read_write_addr;
info.next_split_line = entry->line;
}
else if(strcmp(entry->install_name,
NEXT_DEBUG_ADDRESS_TO_ASSIGN) == 0){
if(next_debug == TRUE)
fatal("segment address table: %s has more than one "
"entry for %s", info.seg_addr_table_name,
NEXT_DEBUG_ADDRESS_TO_ASSIGN);
if(entry->split == TRUE)
fatal("segment address table: %s entry for %s is not "
"a single address", info.seg_addr_table_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
next_debug = TRUE;
if(relayout_nonsplit == FALSE)
info.debug_seg1addr = entry->seg1addr;
}
}
if(next_flat == FALSE && macosx_deployment_target.major < 4)
error("segment address table: %s does not have an entry for %s",
info.seg_addr_table_name, NEXT_FLAT_ADDRESS_TO_ASSIGN);
if(next_split == FALSE)
error("segment address table: %s does not have an entry for %s",
info.seg_addr_table_name, NEXT_SPLIT_ADDRESS_TO_ASSIGN);
if(next_debug == FALSE && macosx_deployment_target.major < 4)
error("segment address table: %s does not have an entry for %s",
info.seg_addr_table_name, NEXT_DEBUG_ADDRESS_TO_ASSIGN);
if(errors != 0)
exit(EXIT_FAILURE);
}
/*
* For the -relayout, -update, -checkonly, -update_overlaps
* and -create options make a pass through all the entries in
* in the seg_addr_table and determine the short names for each
* of the flat entries and determine the maximum size and their
* seg1addr. For the split libraries just determine
* their maximum size and their segs_read_only_addr and
* segs_read_write_addr.
*/
if(relayout == TRUE || update == TRUE || checkonly == TRUE ||
create == TRUE || update_overlaps == TRUE ||
relayout_nonsplit == TRUE){
layout_info = allocate(sizeof(struct layout_info) *
(info.table_size + 2));
memset(layout_info, '\0', sizeof(struct layout_info) *
(info.table_size + 2));
used = 0;
info.layout_info =
allocate(sizeof(struct layout_info *) *
(info.table_size + 2));
memset(info.layout_info, '\0', sizeof(struct layout_info *) *
(info.table_size + 2));
info.sorted_flat_layout_info =
allocate(sizeof(struct layout_info *) * (info.table_size + 2));
info.nsorted_flat = 0;
info.sorted_split_read_only_layout_info =
allocate(sizeof(struct layout_info *) * info.table_size);
info.sorted_split_read_write_layout_info =
allocate(sizeof(struct layout_info *) * info.table_size);
info.nsorted_split = 0;
/*
* Force in two layout info structs into the
* sorted_flat_layout_info array so that next_flat_seg1addr() will
* step over them.
*/
layout_info[used].install_name = READ_ONLY_SEGMENT_NAME;
layout_info[used].image_file_name = READ_ONLY_SEGMENT_NAME;
layout_info[used].short_name = READ_ONLY_SEGMENT_NAME;
layout_info[used].split = FALSE;
layout_info[used].seg1addr = info.default_read_only_addr;
layout_info[used].max_sizes.all = 0x10000000;
info.sorted_flat_layout_info[info.nsorted_flat++] =
layout_info + used;
/*
* Create a bogus current_entry to keep all current_entry
* pointers valid.
*/
layout_info[used].current_entry =
allocate(sizeof(struct seg_addr_table));
layout_info[used].current_entry->install_name =
READ_ONLY_SEGMENT_NAME;
layout_info[used].current_entry->split = FALSE;
layout_info[used].current_entry->seg1addr =
info.default_read_only_addr;
used++;
layout_info[used].install_name = READ_WRITE_SEGMENT_NAME;
layout_info[used].image_file_name = READ_WRITE_SEGMENT_NAME;
layout_info[used].short_name = READ_WRITE_SEGMENT_NAME;
layout_info[used].split = FALSE;
layout_info[used].seg1addr = info.default_read_write_addr;
layout_info[used].max_sizes.all = 0x10000000;
info.sorted_flat_layout_info[info.nsorted_flat++] =
layout_info + used;
/*
* Create a bogus current_entry to keep all current_entry
* pointers valid.
*/
layout_info[used].current_entry =
allocate(sizeof(struct seg_addr_table));
layout_info[used].current_entry->install_name =
READ_WRITE_SEGMENT_NAME;
layout_info[used].current_entry->split = FALSE;
layout_info[used].current_entry->seg1addr =
info.default_read_write_addr;
used++;
for(i = 0 ; i < info.table_size; i++){
entry = info.seg_addr_table + i;
/*
* Just skip the entries for the address used for updating.
*/
if(strcmp(entry->install_name,
NEXT_FLAT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name,
NEXT_DEBUG_ADDRESS_TO_ASSIGN) == 0)
continue;
/*
* Convert fixed address and size regions to flat entries using
* the segs_read_only_addr value as the address and the
* segs_read_write_addr value as the size.
*/
if(strcmp(entry->install_name, FIXED_ADDRESS_AND_SIZE) == 0){
if(entry->split != TRUE)
error("entry in seg_addr_table: %s line: %u for fixed "
"address and size not to be allocated does not "
"have both address and size values",
info.seg_addr_table_name, entry->line);
else{
entry->split = FALSE;
entry->seg1addr = entry->segs_read_only_addr;
layout_info[used].install_name = entry->install_name;
layout_info[used].image_file_name = entry->install_name;
layout_info[used].short_name = entry->install_name;
layout_info[used].split = FALSE;
layout_info[used].seg1addr = entry->seg1addr;
layout_info[used].max_sizes.all =
entry->segs_read_write_addr;
/*
* Create a current_entry pointer
* for the fixed addresses
*/
layout_info[used].current_entry = entry;
info.layout_info[i] = layout_info + used;
info.sorted_flat_layout_info
[info.nsorted_flat++] = layout_info + used;
used++;
}
continue;
}
/*
* Get the DSTROOT file for this install
* name if we are given a -release option.
* No longer are we giving enough room for the SYMROOT.
*/
image_file_name = get_image_file_name(&info,
entry->install_name, FALSE,
update == TRUE || update_overlaps == TRUE);
if(image_file_name == NULL){
if(info.disablewarnings == FALSE &&
update == FALSE && update_overlaps == FALSE)
error("from seg_addr_table: %s line: %u can't find "
"file for install name: %s in -release %s",
info.seg_addr_table_name, entry->line,
entry->install_name, info.release_name);
continue;
}
if(stat(image_file_name, &stat_buf) == -1){
if(info.disablewarnings == FALSE &&
update == FALSE && update_overlaps == FALSE)
error("from seg_addr_table: %s line: %u can't open "
"file: %s", info.seg_addr_table_name, entry->line,
image_file_name);
continue;
}
if(entry->split == FALSE){
base_name = strrchr(entry->install_name, '/');
if(base_name == NULL || base_name[1] == '\0')
base_name = entry->install_name;
else
base_name = base_name + 1;
short_name = guess_short_name(entry->install_name,
&is_framework,
&has_suffix);
if(short_name == NULL)
short_name = base_name;
if((has_suffix != NULL) &&
(strcmp(has_suffix, "_debug") == 0 ||
strcmp(has_suffix, "_profile") == 0))
layout_info[used].use_debug_region = TRUE;
else
layout_info[used].use_debug_region = FALSE;
found = FALSE;
for(j = 0; j < used; j++){
if(layout_info[j].short_name != NULL &&
(layout_info[used].use_debug_region ==
layout_info[j].use_debug_region) &&
strncmp(layout_info[j].install_name,
entry->install_name,
base_name - entry->install_name) == 0 &&
strcmp(layout_info[j].short_name, short_name) == 0){
found = TRUE;
info.layout_info[i] = layout_info + j;
}
}
if(found == FALSE){
layout_info[used].install_name = entry->install_name;
layout_info[used].image_file_name = image_file_name;
layout_info[used].short_name = short_name;
info.layout_info[i] = layout_info + used;
if((update == TRUE || checkonly == TRUE ||
update_overlaps == TRUE) &&
relayout_nonsplit == FALSE &&
entry->seg1addr != 0)
info.sorted_flat_layout_info
[info.nsorted_flat++] = layout_info + used;
used++;
}
}
else{
layout_info[used].install_name = entry->install_name;
layout_info[used].image_file_name = image_file_name;
layout_info[used].short_name = NULL;
info.layout_info[i] = layout_info + used;
if((update == TRUE || checkonly == TRUE ||
update_overlaps == TRUE) &&
entry->segs_read_only_addr != 0){
info.sorted_split_read_only_layout_info
[info.nsorted_split] = layout_info + used;
info.sorted_split_read_write_layout_info
[info.nsorted_split] = layout_info + used;
info.nsorted_split++;
}
used++;
}
info.layout_info[i]->current_entry = entry;
ofile_process(image_file_name, info.arch_flags,
info.narch_flags, info.all_archs, FALSE,
TRUE, FALSE, sizes_and_addresses,
info.layout_info[i]);
}
/*
* Now with all the sizes and addresses known create the sorted
* list of flat and split libraries for those things not to be
* reassigned addresses and check for overlaps.
*/
qsort(info.sorted_flat_layout_info, info.nsorted_flat,
sizeof(struct layout_info *),
(int (*)(const void *, const void *))qsort_flat);
#ifdef DEBUG
for(i = 0 ; i < info.nsorted_flat; i++){
printf("0x%08x %s\n",
(unsigned int)info.sorted_flat_layout_info[i]->seg1addr,
info.sorted_flat_layout_info[i]->image_file_name);
}
#endif /* DEBUG */
qsort(info.sorted_split_read_only_layout_info, info.nsorted_split,
sizeof(struct layout_info *),
(int (*)(const void *, const void *))qsort_split_read_only);
qsort(info.sorted_split_read_write_layout_info, info.nsorted_split,
sizeof(struct layout_info *),
(int (*)(const void *, const void *))qsort_split_read_write);
/* check the sorted flat libraries for overlaps */
for(i = 0 ;
i < info.nsorted_flat && relayout_nonsplit == FALSE;
i++){
for(j = i + 1; j < info.nsorted_flat; j++){
if(info.sorted_flat_layout_info[i]->current_entry->
seg1addr +
info.sorted_flat_layout_info[i]->max_sizes.all >
info.sorted_flat_layout_info[j]->current_entry->
seg1addr){
if(update_overlaps == TRUE)
/* Zero out the address for the overlap */
info.sorted_flat_layout_info[i]->
current_entry->seg1addr = 0;
else
flat_overlap_error(&info, i, j, FALSE);
}
}
/*
* If the operation is update check the last assigned address
* (excluding fixed regions) so that it does not overlap with
* the next address to be assigned. If
* info.allocate_flat_increasing is TRUE we need to also check.
*/
if(info.sorted_flat_layout_info[i]->use_debug_region == FALSE)
seg1addr = info.seg1addr;
else
seg1addr = info.debug_seg1addr;
if((info.allocate_flat_increasing == FALSE) &&
(update == TRUE || checkonly == TRUE ||
update_overlaps == TRUE) &&
strcmp(info.sorted_flat_layout_info[i]->install_name,
FIXED_ADDRESS_AND_SIZE) != 0 &&
info.sorted_flat_layout_info[i]->current_entry->seg1addr -
info.sorted_flat_layout_info[i]->max_sizes.all <
seg1addr &&
info.sorted_flat_layout_info[i]->seg1addr < seg1addr){
if(update_overlaps == TRUE)
/* Zero out the address for the overlap */
info.sorted_flat_layout_info[i]->
current_entry->seg1addr = 0;
else
flat_overlap_error(&info, i, UINT_MAX, TRUE);
} else if((info.allocate_flat_increasing == TRUE) &&
(update == TRUE || checkonly == TRUE ||
update_overlaps == TRUE) &&
strcmp(info.sorted_flat_layout_info[i]->install_name,
FIXED_ADDRESS_AND_SIZE) != 0 &&
info.sorted_flat_layout_info[i]->current_entry->seg1addr +
info.sorted_flat_layout_info[i]->max_sizes.all >
info.seg1addr &&
info.sorted_flat_layout_info[i]->seg1addr < info.seg1addr){
if(update_overlaps == TRUE)
/* Zero out the address for the overlap */
info.sorted_flat_layout_info[i]->
current_entry->seg1addr = 0;
else
flat_overlap_error(&info, i, UINT_MAX, TRUE);
}
}
/*
* Check the sorted split libraries read-only segments for
* overlaps.
*/
for(i = 0 ; i < info.nsorted_split; i++){
for(j = i + 1 ; j < info.nsorted_split; j++){
if(info.sorted_split_read_only_layout_info[i]->
current_entry->segs_read_only_addr +
info.sorted_split_read_only_layout_info[i]->
max_sizes.read_only >
info.sorted_split_read_only_layout_info[j]->
current_entry->segs_read_only_addr){
if(update_overlaps == TRUE)
/* Zero out the address for the overlap */
info.sorted_split_read_only_layout_info[i]->
current_entry->segs_read_only_addr = 0;
else
split_overlap_error(&info, i, j,
info.sorted_split_read_only_layout_info,
"read-only", FALSE);
}
}
/*
* If the operation is update check the last assigned address
* so that it does not overlap with the next address to be
* assigned.
*/
if((update == TRUE || checkonly == TRUE ||
update_overlaps == TRUE) &&
info.sorted_split_read_only_layout_info[i]->
current_entry->segs_read_only_addr +
info.sorted_split_read_only_layout_info[i]->
max_sizes.read_only >
info.segs_read_only_addr){
if(update_overlaps == TRUE)
/* Zero out the address for the overlap */
info.sorted_split_read_only_layout_info[i]->
current_entry->segs_read_only_addr = 0;
else
split_overlap_error(&info, i, UINT_MAX,
info.sorted_split_read_only_layout_info,
"read-only", TRUE);
}
}
/*
* Check the sorted split libraries read-write segments for
* overlaps.
*/
for(i = 0 ; i < info.nsorted_split; i++){
for(j = i + 1 ; j < info.nsorted_split; j++){
if(info.sorted_split_read_write_layout_info[i]->
current_entry->segs_read_write_addr +
info.sorted_split_read_write_layout_info[i]->
max_sizes.read_write >
info.sorted_split_read_write_layout_info[j]->
current_entry->segs_read_write_addr){
if(update_overlaps == TRUE)
info.sorted_split_read_write_layout_info[i]->
current_entry->segs_read_write_addr = 0;
else
split_overlap_error(&info, i, j,
info.sorted_split_read_write_layout_info,
"read-write", FALSE);
}
}
/*
* If the operation is update check the last assigned address
* so that it does not overlap with the next address to be
* assigned.
*/
if((update == TRUE || checkonly == TRUE ||
update_overlaps == TRUE) &&
info.sorted_split_read_write_layout_info[i]->
current_entry->segs_read_write_addr +
info.sorted_split_read_write_layout_info[i]->
max_sizes.read_write >
info.segs_read_write_addr){
if(update_overlaps == TRUE)
info.sorted_split_read_write_layout_info[i]->
current_entry->segs_read_write_addr = 0;
else
split_overlap_error(&info, i, UINT_MAX,
info.sorted_split_read_write_layout_info,
"read-write", TRUE);
}
}
}
/*
* If the -create option is specified then just used the addresses
* picked up from the libraries them selves and write out the table.
*/
if(create == TRUE){
out_fp = create_output_file(info.output_file_name);
fprintf(out_fp, "#%s -create", progname);
user = getenv("USER");
if(user != NULL)
fprintf(out_fp, " DEVELOPER:%s", user);
if(time(&tloc) != -1)
fprintf(out_fp, " BUILT:%s", ctime(&tloc));
else
fprintf(out_fp, "\n");
process_seg_addr_table(info.seg_addr_table_name, out_fp, progname,
new_table_processor, &info);
}
/*
* If the -relayout option is specified then re-layout all images in
* the seg_addr_table.
*/
if(relayout == TRUE || relayout_nonsplit == TRUE){
/*
* Since we are relaying out all libraries and not just updating
* the table for unassigned addresses clear the assigned feild
* that sizes_and_addresses() set.
*/
for(i = 0 ; i < info.table_size; i++){
entry = info.seg_addr_table + i;
if(info.layout_info[i] == NULL)
continue;
if(entry->split == FALSE){
info.layout_info[i]->assigned = FALSE;
}
}
/*
* Now with all the maximum sizes known for the libraries assign
* them addresses.
*/
for(i = 0 ; i < info.table_size; i++){
entry = info.seg_addr_table + i;
if(info.layout_info[i] == NULL ||
strcmp(entry->install_name, FIXED_ADDRESS_AND_SIZE) == 0)
continue;
if(entry->split == FALSE){
if(info.layout_info[i]->assigned == FALSE){
size = rnd(info.layout_info[i]->max_sizes.all <<
info.factor, info.round);
if(info.layout_info[i]->use_debug_region == FALSE){
info.seg1addr = next_flat_seg1addr(&info, size);
info.layout_info[i]->seg1addr = info.seg1addr;
if(info.allocate_flat_increasing == TRUE){
info.seg1addr += size;
}
}
else{
info.debug_seg1addr =
next_debug_seg1addr(&info, size);
info.layout_info[i]->seg1addr = info.debug_seg1addr;
}
info.layout_info[i]->assigned = TRUE;
if(info.seg1addr > MAX_ADDR)
error("address assignment: 0x%x plus size 0x%x for "
"%s greater maximum address 0x%x",
(unsigned int)info.layout_info[i]->seg1addr,
(unsigned int)size,
entry->install_name, (unsigned int)MAX_ADDR);
}
}
else if (relayout_nonsplit == FALSE){
info.layout_info[i]->segs_read_only_addr =
info.segs_read_only_addr;
info.layout_info[i]->segs_read_write_addr =
info.segs_read_write_addr;
if(info.layout_info[i]->max_sizes.read_only >
info.layout_info[i]->max_sizes.read_write)
max = info.layout_info[i]->max_sizes.read_only;
else
max = info.layout_info[i]->max_sizes.read_write;
size = rnd(max + info.round, info.round);
info.segs_read_only_addr += size;
info.segs_read_write_addr += size;
if((info.layout_info[i]->segs_read_only_addr &
info.overflow_mask) !=
(info.start_segs_read_only_addr & info.overflow_mask))
error("read-only address assignment: 0x%x plus size "
"0x%x for %s overflows area to be allocated",
(unsigned int)
info.layout_info[i]->segs_read_only_addr,
(unsigned int)size,
entry->install_name);
if((info.layout_info[i]->segs_read_write_addr &
info.overflow_mask) !=
(info.start_segs_read_write_addr & info.overflow_mask))
error("read-write address assignment: 0x%x plus size "
"0x%x for %s overflows area to be allocated",
(unsigned int)
info.layout_info[i]->segs_read_write_addr,
(unsigned int)size,
entry->install_name);
}
}
if(update_overlaps == FALSE && relayout == FALSE){
next_split = FALSE;
for(i = 0 ; i < info.table_size; i++){
entry = info.seg_addr_table + i;
if(strcmp(entry->install_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN) == 0){
if(next_split == TRUE)
fatal("segment address table: %s has more than one "
"entry for %s", info.seg_addr_table_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
if(entry->split == FALSE)
fatal("segment address table: %s entry for %s is "
"a single address", info.seg_addr_table_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
next_split = TRUE;
info.segs_read_only_addr = entry->segs_read_only_addr;
info.segs_read_write_addr = entry->segs_read_write_addr;
info.next_split_line = entry->line;
}
}
}
/*
* Now with the addresses assigned write out the new table.
*/
if(update_overlaps == FALSE){
out_fp = create_output_file(info.output_file_name);
fprintf(out_fp, "#%s -relayout", progname);
user = getenv("USER");
if(user != NULL)
fprintf(out_fp, " DEVELOPER:%s", user);
if(time(&tloc) != -1)
fprintf(out_fp, " BUILT:%s", ctime(&tloc));
else
fprintf(out_fp, "\n");
process_seg_addr_table(info.seg_addr_table_name, out_fp,
progname, new_table_processor, &info);
fprintf(out_fp, "#%s: Do not remove the following line, "
"it is used by the %s tool\n", progname, progname);
fprintf(out_fp, "0x%08x\t0x%08x\t%s\n",
(unsigned int)info.segs_read_only_addr,
(unsigned int)info.segs_read_write_addr,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
/*
* If MACOSX_DEPLOYMENT_TARGET is greater then 10.4.
* Then we need to:
* 1. Avoid outputting the NEXT_FLAT_ADDRESS_TO_ASSIGN and
* NEXT_DEBUG_ADDRESS_TO_ASSIGN.
* 2. Check if the we've overflowed the 256 mb region
* instead of the 128mb region
*/
if(macosx_deployment_target.major >= 4) {
if(info.segs_read_only_addr >
info.start_segs_read_only_addr + 0x10000000)
error("segs_read_only_addr over flow (more than 256meg's"
" of address space assigned, 0x%08x - 0x%08x)",
(unsigned int)info.start_segs_read_only_addr,
(unsigned int)info.segs_read_only_addr);
if (info.segs_read_write_addr >
info.start_segs_read_write_addr + 0x10000000)
error("segs_read_write_addr over flow (more than "
"256meg's of address space assigned, "
"0x%08x - 0x%08x)",
(unsigned int)info.start_segs_read_write_addr,
(unsigned int)info.segs_read_write_addr);
} else {
if(info.segs_read_only_addr >
info.start_segs_read_only_addr + 0x08000000)
error("segs_read_only_addr over flow (more than 128meg's"
" of address space assigned, 0x%08x - 0x%08x)",
(unsigned int)info.start_segs_read_only_addr,
(unsigned int)info.segs_read_only_addr);
if(info.segs_read_write_addr >
info.start_segs_read_write_addr + 0x08000000)
error("segs_read_write_addr over flow (more than"
"128meg's of address space assigned, "
" 0x%08x - 0x%08x)",
(unsigned int)info.start_segs_read_write_addr,
(unsigned int)info.segs_read_write_addr);
fprintf(out_fp, "#%s: Do not remove the following line, "
"it is used by the %s tool\n", progname, progname);
fprintf(out_fp, "0x%08x\t%s\n",
(unsigned int)info.seg1addr,
NEXT_FLAT_ADDRESS_TO_ASSIGN);
fprintf(out_fp, "#%s: Do not remove the following line, "
"it is used by the %s tool\n", progname, progname);
fprintf(out_fp, "0x%08x\t%s\n",
(unsigned int)info.debug_seg1addr,
NEXT_DEBUG_ADDRESS_TO_ASSIGN);
}
}
}
/*
* If the -update or the -update_overlaps option is specified
* then only layout the images in
* the seg_addr_table which had an address of zero. The addresses are
* assigned starting at the NEXT_FLAT_ADDRESS_TO_ASSIGN,
* NEXT_DEBUG_ADDRESS_TO_ASSIGN and NEXT_SPLIT_ADDRESS_TO_ASSIGN and
* were picked up above.
*/
if(update == TRUE || update_overlaps == TRUE){
/*
* Now with all the maximum sizes known for the libraries assign
* them addresses.
*/
for(i = 0 ; i < info.table_size; i++){
entry = info.seg_addr_table + i;
/*
* Just skip the entries for the address used for updating and
* fixed address and size regions.
*/
if(strcmp(entry->install_name,
NEXT_FLAT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name,
NEXT_SPLIT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name,
NEXT_DEBUG_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name,
FIXED_ADDRESS_AND_SIZE) == 0)
continue;
if(info.layout_info[i] == NULL)
continue;
if(entry->split == FALSE && entry->seg1addr == 0 &&
relayout_nonsplit == FALSE){
if(info.layout_info[i]->assigned == FALSE){
size = rnd(info.layout_info[i]->max_sizes.all <<
info.factor, info.round);
if(info.layout_info[i]->use_debug_region == FALSE){
info.seg1addr = next_flat_seg1addr(&info, size);
info.layout_info[i]->seg1addr = info.seg1addr;
if(info.allocate_flat_increasing == TRUE){
info.seg1addr += size;
}
}
else{
info.debug_seg1addr =
next_debug_seg1addr(&info, size);
info.layout_info[i]->seg1addr = info.debug_seg1addr;
}
info.layout_info[i]->assigned = TRUE;
if(info.seg1addr > MAX_ADDR)
error("address assignment: 0x%x plus size 0x%x for "
"%s greater maximum address 0x%x",
(unsigned int)info.layout_info[i]->seg1addr,
(unsigned int)size,
entry->install_name, (unsigned int)MAX_ADDR);
}
}
/*
* If the flat address is not zero then it is possible that
* the layout info will have the address the library was last
* built at and not the address in the table entry. So to make
* sure the table is written out correctly in an -update mode
* copy the table entries that are non-zero into the layout
* info.
*/
if(entry->split == FALSE && entry->seg1addr != 0 &&
relayout_nonsplit == FALSE){
info.layout_info[i]->seg1addr = entry->seg1addr;
}
else if(entry->split == TRUE &&
(entry->segs_read_only_addr == 0 ||
entry->segs_read_write_addr == 0)){
info.layout_info[i]->segs_read_only_addr =
info.segs_read_only_addr;
info.layout_info[i]->segs_read_write_addr =
info.segs_read_write_addr;
if(info.layout_info[i]->max_sizes.read_only >
info.layout_info[i]->max_sizes.read_write)
max = info.layout_info[i]->max_sizes.read_only;
else
max = info.layout_info[i]->max_sizes.read_write;
size = rnd(max+info.round, info.round);
info.segs_read_only_addr += size;
info.segs_read_write_addr += size;
if((info.layout_info[i]->segs_read_only_addr &
info.overflow_mask) !=
(info.start_segs_read_only_addr & info.overflow_mask))
error("read-only address assignment: 0x%x plus size "
"0x%x for %s overflows area to be allocated",
(unsigned int)
info.layout_info[i]->segs_read_only_addr,
(unsigned int)size,
entry->install_name);
if((info.layout_info[i]->segs_read_write_addr &
info.overflow_mask) !=
(info.start_segs_read_write_addr & info.overflow_mask))
error("read-write address assignment: 0x%x plus size "
"0x%x for %s overflows area to be allocated",
(unsigned int)
info.layout_info[i]->segs_read_write_addr,
(unsigned int)size,
entry->install_name);
}
/*
* If the split address is not zero then it is possible that
* the layout info will have the address the library was last
* built at and not the address in the table entry. So to make
* sure the table is written out correctly in an -update mode
* copy the table entries that are non-zero into the layout
* info.
*/
else if(entry->split == TRUE &&
entry->segs_read_only_addr != 0){
info.layout_info[i]->segs_read_only_addr =
entry->segs_read_only_addr;
info.layout_info[i]->segs_read_write_addr =
entry->segs_read_write_addr;
}
}
/*
* Now with the addresses assigned write out the updated table.
*/
out_fp = create_output_file(info.output_file_name);
process_seg_addr_table(info.seg_addr_table_name, out_fp, progname,
new_table_processor, &info);
fprintf(out_fp, "#%s: Do not remove the following line, "
"it is used by the %s tool\n", progname, progname);
fprintf(out_fp, "0x%08x\t0x%08x\t%s\n",
(unsigned int)info.segs_read_only_addr,
(unsigned int)info.segs_read_write_addr,
NEXT_SPLIT_ADDRESS_TO_ASSIGN);
/*
* Output the next flat and next debug addresses to assign only
* if the deployment target is less than 10.4.
*/
if(macosx_deployment_target.major < 4) {
fprintf(out_fp, "#%s: Do not remove the following line, "
"it is used by the %s tool\n", progname, progname);
fprintf(out_fp, "0x%08x\t%s\n",
(unsigned int)info.seg1addr,
NEXT_FLAT_ADDRESS_TO_ASSIGN);
fprintf(out_fp, "#%s: Do not remove the following line, "
"it is used by the %s tool\n", progname, progname);
fprintf(out_fp, "0x%08x\t%s\n",
(unsigned int)info.debug_seg1addr,
NEXT_DEBUG_ADDRESS_TO_ASSIGN);
}
}
if(out_fp != NULL){
if(fclose(out_fp) != 0)
system_fatal("can't close output file: %s\n",
info.output_file_name);
}
if(errors != 0)
return(EXIT_FAILURE);
else
return(EXIT_SUCCESS);
}
/*
* usage() prints the current usage message and exits indicating failure.
*/
static
void
usage(
void)
{
fprintf(stderr, "Usage: %s [[[-relayout | -update "
"| [-update_overlaps [-relayout_nonsplit]] -o output_file] | "
"-checkonly ] [-seg_addr_table input_file] "
"[-disablewarnings] [-seg1addr hex_address] "
"[-allocate_flat increasing | decreasing]] "
"[-segs_read_only_addr hex_address] "
"[-segs_read_write_addr hex_address] "
"[-release <release_name> ] [[-arch <arch_flag>] ...]\n",
progname);
exit(EXIT_FAILURE);
}
/*
* create_output_file() creates the output file for the new table.
*/
static
FILE *
create_output_file(
char *output_file_name)
{
FILE *out_fp;
out_fp = fopen(output_file_name, "w");
if(out_fp == NULL)
system_fatal("can't create output file: %s\n", output_file_name);
return(out_fp);
}
/*
* qsort_flat() is used by qsort() to sort the list of flat libraries that have
* addresses assigned to them my their seg1addr.
*/
static
int
qsort_flat(
const struct layout_info **p1,
const struct layout_info **p2)
{
if((*p1)->current_entry->seg1addr == (*p2)->current_entry->seg1addr)
return(0);
if((*p1)->current_entry->seg1addr < (*p2)->current_entry->seg1addr)
return(-1);
return(1);
}
/*
* qsort_split_read_only() is used by qsort() to sort the list of split
* libraries that have addresses assigned to them my their segs_read_only_addr.
*/
static
int
qsort_split_read_only(
const struct layout_info **p1,
const struct layout_info **p2)
{
if((*p1)->current_entry->segs_read_only_addr ==
(*p2)->current_entry->segs_read_only_addr)
return(0);
if((*p1)->current_entry->segs_read_only_addr <
(*p2)->current_entry->segs_read_only_addr)
return(-1);
return(1);
}
/*
* qsort_split_read_write() is used by qsort() to sort the list of split
* libraries that have addresses assigned to them my their segs_read_write_addr.
*/
static
int
qsort_split_read_write(
const struct layout_info **p1,
const struct layout_info **p2)
{
if((*p1)->current_entry->segs_read_write_addr ==
(*p2)->current_entry->segs_read_write_addr)
return(0);
if((*p1)->current_entry->segs_read_write_addr <
(*p2)->current_entry->segs_read_write_addr)
return(-1);
return(1);
}
/*
* flat_overlap_error() is called to print an error message indicating that two
* flat libraries overlap. The libraries are indicated as indexes into the
* array of sorted flat library layout info in the struct info. If next_address
* is TRUE then the overlap is with the next address to assign and not the
* second index.
*/
static
void
flat_overlap_error(
struct info *info,
uint32_t i1,
uint32_t i2,
enum bool next_address)
{
struct layout_info *p1, *p2;
struct seg_addr_table *f1, *f2, fake;
fake.line = 0;
p1 = info->sorted_flat_layout_info[i1];
if(strcmp(p1->install_name, READ_ONLY_SEGMENT_NAME) == 0 ||
strcmp(p1->install_name, READ_WRITE_SEGMENT_NAME) == 0)
f1 = &fake;
else if(strcmp(p1->install_name, FIXED_ADDRESS_AND_SIZE) == 0)
f1 = search_seg_addr_table_for_fixed(info->seg_addr_table,
p1->seg1addr,
p1->max_sizes.all);
else
f1 = search_seg_addr_table(info->seg_addr_table, p1->install_name);
if(f1 == NULL)
fatal("internal error (can't find entry in info->seg_addr_table "
"for: %s\n", p1->install_name);
if(next_address == FALSE){
p2 = info->sorted_flat_layout_info[i2];
if(strcmp(p2->install_name, READ_ONLY_SEGMENT_NAME) == 0 ||
strcmp(p2->install_name, READ_WRITE_SEGMENT_NAME) == 0)
f2 = &fake;
else if(strcmp(p2->install_name, FIXED_ADDRESS_AND_SIZE) == 0)
f2 = search_seg_addr_table_for_fixed(info->seg_addr_table,
p2->seg1addr,
p2->max_sizes.all);
else
f2 = search_seg_addr_table(info->seg_addr_table,
p2->install_name);
if(f2 == NULL)
fatal("internal error (can't find entry in info->"
"seg_addr_table for: %s\n", p2->install_name);
if(info->disablewarnings == FALSE)
error("address space of: %s for seg_addr_table: %s entry on "
"line: %u overlaps with: %s for entry on line: %u",
p1->image_file_name, info->seg_addr_table_name, f1->line,
p2->image_file_name, f2->line);
}
else{
if(info->disablewarnings == FALSE)
error("address space of: %s for seg_addr_table: %s entry on "
"line: %u overlaps with: %s for entry on line: %u",
p1->image_file_name, info->seg_addr_table_name, f1->line,
NEXT_FLAT_ADDRESS_TO_ASSIGN, info->next_flat_line);
}
}
/*
* split_overlap_error() is called to print an error message indicating that two
* split libraries overlap. The string segment is either "read-only" or
* "read-write" and sorted_layout_info is a pointer to the sorted array of
* layout info structs into which the indexes i1 and i2 refer which are the
* libraries that overlap. If next_address is TRUE then the overlap is with
* the next address to assign and not the second index.
*/
static
void
split_overlap_error(
struct info *info,
uint32_t i1,
uint32_t i2,
struct layout_info **sorted_layout_info,
char *segment,
enum bool next_address)
{
struct layout_info *p1, *p2;
struct seg_addr_table *f1, *f2;
p1 = sorted_layout_info[i1];
f1 = search_seg_addr_table(info->seg_addr_table, p1->install_name);
if(f1 == NULL)
fatal("internal error (can't find entry in info->seg_addr_table "
"for: %s\n", p1->install_name);
if(next_address == FALSE){
p2 = sorted_layout_info[i2];
f2 = search_seg_addr_table(info->seg_addr_table, p2->install_name);
if(f2 == NULL)
fatal("internal error (can't find entry in info->"
"seg_addr_table for: %s\n", p2->install_name);
if(info->disablewarnings == FALSE)
error("%s segments of: %s for seg_addr_table: %s entry on line:"
" %u overlaps with: %s for entry on line: %u", segment,
p1->image_file_name, info->seg_addr_table_name, f1->line,
p2->image_file_name, f2->line);
}
else{
if(info->disablewarnings == FALSE)
error("%s segments of: %s for seg_addr_table: %s entry on line:"
" %u overlaps with: %s for entry on line: %u", segment,
p1->image_file_name, info->seg_addr_table_name, f1->line,
NEXT_SPLIT_ADDRESS_TO_ASSIGN, info->next_split_line);
}
}
/*
* search_seg_addr_table() searches the specified segment address table for
* a fixed entry with the specified seg1addr and size and returns the entry to
* it if it is found. If it is not found NULL is returned.
*/
static
struct seg_addr_table *
search_seg_addr_table_for_fixed(
struct seg_addr_table *seg_addr_table,
uint32_t seg1addr,
uint32_t size)
{
struct seg_addr_table *p;
if(seg_addr_table == NULL)
return(NULL);
for(p = seg_addr_table; p->install_name != NULL; p++){
if(strcmp(p->install_name, FIXED_ADDRESS_AND_SIZE) == 0 &&
p->segs_read_only_addr == seg1addr &&
p->segs_read_write_addr == size)
return(p);
}
return(NULL);
}
/*
* next_flat_seg1addr() uses the info->seg1addr as the starting point
* to try to find the next address of a flat library to assign for 'size'. It
* goes through the sorted libraries and finds the next place big enough to put
* 'size' if info->seg1addr of 'size' overlaps a library.
*/
static
uint32_t
next_flat_seg1addr(
struct info *info,
uint32_t size)
{
uint32_t seg1addr, start, end;
uint32_t i;
int32_t j;
if(info->allocate_flat_increasing == TRUE){
seg1addr = info->seg1addr;
/*
* Go through the flat libraries sorted by address to see if
* seg1addr for size overlaps with anything. If so move seg1addr
* past that region. When all regions have been checked then
* return the seg1addr to be used.
*/
for(i = 0 ; i < info->nsorted_flat; i++){
start = info->sorted_flat_layout_info[i]->seg1addr;
end = start + info->sorted_flat_layout_info[i]->max_sizes.all;
if((seg1addr <= start && seg1addr + size > start) ||
(seg1addr < end && seg1addr + size > end) ||
(seg1addr >= start && seg1addr + size <= end)){
#ifdef DEBUG
printf("next_flat_seg1addr() stepping over region start = 0x%x end = 0x%x\n",
(unsigned int)start, (unsigned int)end);
#endif
seg1addr = end;
}
}
}
else{ /* allocate in decreasing order */
seg1addr = info->seg1addr - size;
/*
* Go through the flat libraries sorted by address to see if
* seg1addr for size overlaps with anything. If so move seg1addr
* past that region. When all regions have been checked then
* return the seg1addr to be used.
*/
for(j = info->nsorted_flat-1 ; j >= 0; j--){
start = info->sorted_flat_layout_info[j]->seg1addr;
end = start + info->sorted_flat_layout_info[j]->max_sizes.all;
if((seg1addr <= start && seg1addr + size > start) ||
(seg1addr < end && seg1addr + size > end) ||
(seg1addr >= start && seg1addr + size <= end)){
#ifdef DEBUG
printf("next_flat_seg1addr() stepping back over region start = 0x%x "
"end = 0x%x\n", (unsigned int)start, (unsigned int)end);
#endif
seg1addr = start;
}
}
}
#ifdef DEBUG
printf("next_flat_seg1addr() returning seg1addr = 0x%x\n",
(unsigned int)seg1addr);
#endif
return(seg1addr);
}
/*
* next_debug_seg1addr() uses the info->debug_seg1addr as the starting point
* to try to find the next address of a flat library to assign for 'size'. It
* goes through the sorted libraries and finds the next place big enough to put
* 'size' if info->debug_seg1addr of 'size' overlaps a library.
*/
static
uint32_t
next_debug_seg1addr(
struct info *info,
uint32_t size)
{
uint32_t seg1addr, start, end;
int32_t j;
seg1addr = info->debug_seg1addr - size;
/*
* Go through the flat libraries sorted by address to see if
* seg1addr for size overlaps with anything. If so move seg1addr
* past that region. When all regions have been checked then
* return the seg1addr to be used.
*/
for(j = info->nsorted_flat-1 ; j >= 0; j--){
start = info->sorted_flat_layout_info[j]->seg1addr;
end = start + info->sorted_flat_layout_info[j]->max_sizes.all;
if((seg1addr <= start && seg1addr + size > start) ||
(seg1addr < end && seg1addr + size > end) ||
(seg1addr >= start && seg1addr + size <= end)){
#ifdef DEBUG
printf("next_debug_seg1addr() stepping back over region "
"start = 0x%x end = 0x%x\n",
(unsigned int)start, (unsigned int)end);
#endif
seg1addr = start;
}
}
#ifdef DEBUG
printf("next_debug_seg1addr() returning seg1addr = 0x%x\n",
(unsigned int)seg1addr);
#endif
return(seg1addr);
}
/*
* get_image_file_name() gets the SYMROOT file or the DSTROOT file for this
* install name if we were given a -release option. If a -release option is
* given an the install_name can't be found in the SYMROOT or the DSTROOT this
* routine returns NULL. If a -release option is not given this routine simply
* returns install_name.
*/
static
char *
get_image_file_name(
struct info *info,
char *install_name,
enum bool try_symroot,
enum bool no_error_if_missing)
{
char *image_file_name;
enum bool found_project;
/*
* Get the SYMROOT file or the DSTROOT file for this install
* name if we are given a -release option.
*/
image_file_name = NULL;
if(info->release_name != NULL){
/*
* There is not enough room in the shared regions to use the full
* debugging SYMROOT files. And we only want to us the DSTROOT when
* update_overlaps. So only look there if the caller wants to.
*/
if(try_symroot == TRUE)
image_file_name = get_symfile_for_dylib(
install_name,
info->release_name,
&found_project,
info->disablewarnings,
no_error_if_missing);
else
found_project = TRUE;
if(image_file_name == NULL && found_project == TRUE){
image_file_name = get_dstfile_for_dylib(
install_name,
info->release_name,
&found_project,
info->disablewarnings,
no_error_if_missing);
}
}
else{
image_file_name = install_name;
}
#ifdef DEBUG
printf("using %s for %s\n",
image_file_name == NULL ? "NULL" : image_file_name,
install_name);
#endif
return(image_file_name);
}
/*
* new_table_processor() is passed an entry in the seg_addr_table and writes
* the new entry into the file pointer out_fp. The cookie is a info struct
* with all the info for the new entry.
*/
static
void
new_table_processor(
struct seg_addr_table *entry,
FILE *out_fp,
void *cookie)
{
struct info *info;
char *image_file_name;
struct stat stat_buf;
struct seg_addr_table *f;
/*
* Just skip the entries for the address used for updating as that
* will be set last in the routine that create the output file.
*/
if(strcmp(entry->install_name, NEXT_FLAT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name, NEXT_SPLIT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name, NEXT_DEBUG_ADDRESS_TO_ASSIGN) == 0)
return;
if(strcmp(entry->install_name, FIXED_ADDRESS_AND_SIZE) == 0){
fprintf(out_fp, "0x%08x\t0x%08x\t%s\n",
(unsigned int)entry->segs_read_only_addr,
(unsigned int)entry->segs_read_write_addr,
entry->install_name);
return;
}
info = (struct info *)cookie;
/*
* Skip non-split entries when MACOSX_DEPLOYMENT_TARGET is 10.4 or
* greater.
*/
if(info->macosx_deployment_target.major >= 4 && entry->split != TRUE)
return;
/*
* If the file exist then print out its previously assigned address.
*/
image_file_name = get_image_file_name(info, entry->install_name,
FALSE,
TRUE);
if(image_file_name == NULL)
return;
if(stat(image_file_name, &stat_buf) != -1){
/*
* Since new_table_processor() is called to re-walk the table so to
* re-layout it in the order it came in, then entries passed to it
* are not the same as the ones stored way in the first pass used to
* layout the addresses. So match up this entry with the previous
* copy of the entry by its name.
*/
f = search_seg_addr_table(info->seg_addr_table,
entry->install_name);
if(f == NULL)
fatal("internal error (can't find entry in info->"
"seg_addr_table for: %s\n", entry->install_name);
if(entry->split == TRUE){
if(info->layout_info[f - info->seg_addr_table]->split != TRUE &&
info->macosx_deployment_target.major >= 4)
return;
fprintf(out_fp, "0x%08x\t0x%08x\t%s\n",
(unsigned int)info->layout_info[
f - info->seg_addr_table]->segs_read_only_addr,
(unsigned int)info->layout_info[
f - info->seg_addr_table]->segs_read_write_addr,
entry->install_name);
}
else{
fprintf(out_fp, "0x%08x\t%s\n",
(unsigned int)info->layout_info[
f - info->seg_addr_table]->seg1addr,
entry->install_name);
}
}
}
/*
* sizes_and_addresses() is the routine that gets called by ofile_process() to
* process single object files. If sums up the vmsizes of all the segments, the
* read_only segments and the read_write segments. Then if any of these are
* larger the the previous sizes in the max_sizes struct passed in cookie the
* fields in the max_sizes struct are updated with the larger sizes.
* It also picks up the seg1addr or the pair of segs_read_only_addr and
* seg_read_write_addr's.
*/
static
void
sizes_and_addresses(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
uint32_t i, all, read_only, read_write,
segs_read_only_addr, segs_read_write_addr;
struct load_command *lc;
struct segment_command *sg, *first;
struct layout_info *layout_info;
enum bool split;
if(ofile->mh == NULL)
return;
#ifdef DEBUG
printf("In max_size() ofile->file_name = %s", ofile->file_name);
if(arch_name != NULL)
printf(" arch_name = %s\n", arch_name);
else
printf("\n");
#endif /* DEBUG */
layout_info = (struct layout_info *)cookie;
split = (ofile->mh->flags & MH_SPLIT_SEGS) == MH_SPLIT_SEGS;
layout_info->split = split;
lc = ofile->load_commands;
first = NULL;
all = 0;
read_only = 0;
read_write = 0;
segs_read_only_addr = UINT_MAX;
segs_read_write_addr = UINT_MAX;
for(i = 0; i < ofile->mh->ncmds; i++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
if(first == NULL){
first = sg;
if(split == FALSE && first->vmaddr != 0){
if(layout_info->seg1addr == 0 ||
layout_info->seg1addr == first->vmaddr){
layout_info->seg1addr = first->vmaddr;
}
}
}
if((sg->initprot & VM_PROT_WRITE) == 0){
read_only += sg->vmsize;
if(split == TRUE && sg->vmaddr < segs_read_only_addr)
segs_read_only_addr = sg->vmaddr;
}
else{
read_write += sg->vmsize;
if(split == TRUE && sg->vmaddr < segs_read_write_addr)
segs_read_write_addr = sg->vmaddr;
}
all += sg->vmsize;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(all > layout_info->max_sizes.all)
layout_info->max_sizes.all = all;
if(read_only > layout_info->max_sizes.read_only)
layout_info->max_sizes.read_only = read_only;
if(read_write > layout_info->max_sizes.read_write)
layout_info->max_sizes.read_write = read_write;
if(split == TRUE){
layout_info->segs_read_only_addr = segs_read_only_addr;
layout_info->segs_read_write_addr = segs_read_write_addr;
}
}
/*
* dylib_table_processor() is passed an entry in the seg_addr_table and writes
* a dylib table entry into the file pointer out_fp. The cookie is a info
* struct with all the info (but not yet used here).
*/
static
void
dylib_table_processor(
struct seg_addr_table *entry,
FILE *out_fp,
void *cookie)
{
struct info *info;
char *image_file_name, *short_name, *has_suffix;
struct stat stat_buf;
uint32_t seg1addr;
enum bool is_framework;
/*
* Just skip the entries for the address used for updating as that
* will be set last in the routine that create the output file.
*/
if(strcmp(entry->install_name, NEXT_FLAT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name, NEXT_SPLIT_ADDRESS_TO_ASSIGN) == 0 ||
strcmp(entry->install_name, NEXT_DEBUG_ADDRESS_TO_ASSIGN) == 0)
return;
info = (struct info *)cookie;
/*
* This may change to using the DSTROOT file for this install name.
*/
image_file_name = entry->install_name;
/*
* Determine if the file exist and if so check to see all the
* architectures to be considered have the same seg1addr.
*/
if(stat(image_file_name, &stat_buf) != -1){
seg1addr = 0;
ofile_process(image_file_name, info->arch_flags, info->narch_flags,
info->all_archs, FALSE, TRUE, FALSE,
get_seg1addr,&seg1addr);
short_name = guess_short_name(entry->install_name, &is_framework,
&has_suffix);
if(short_name == NULL){
short_name = strrchr(entry->install_name, '/');
if(short_name == NULL || short_name[1] == '\0')
short_name = entry->install_name;
}
fprintf(out_fp, "0x%08x\t%s\n", (unsigned int)seg1addr, short_name);
}
else{
error("from seg_addr_table: %s line: %u can't open file: "
"%s \n", info->seg_addr_table_name, entry->line,
image_file_name);
}
}
/*
* get_seg1addr() is the routine that gets called by ofile_process() to process
* single object files. It determines the seg1addr of the ofile passed to it
* and checks that it is the same as what is passed in the cookie (it that is
* not zero). Then it sets the seg1addr into the cookie is it was zero.
*/
static
void
get_seg1addr(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
uint32_t i;
struct load_command *lc;
struct segment_command *sg;
uint32_t *seg1addr, first_addr;
if(ofile->mh == NULL)
return;
#ifdef DEBUG
printf("In get_seg1addr() ofile->file_name = %s", ofile->file_name);
if(arch_name != NULL)
printf(" arch_name = %s\n", arch_name);
else
printf("\n");
#endif /* DEBUG */
first_addr = 0;
seg1addr = (uint32_t *)cookie;
lc = ofile->load_commands;
for(i = 0; i < ofile->mh->ncmds; i++){
if(lc->cmd == LC_SEGMENT){
sg = (struct segment_command *)lc;
first_addr = sg->vmaddr;
break;
}
lc = (struct load_command *)((char *)lc + lc->cmdsize);
}
if(*seg1addr != 0 && *seg1addr != first_addr)
error("seg1addr of: %s not the same in all architectures",
ofile->file_name);
*seg1addr = first_addr;
}