Added ability to create Mach-O executables master
authorRobert Pengelly <robertapengelly@hotmail.com>
Tue, 28 Oct 2025 19:10:13 +0000 (19:10 +0000)
committerRobert Pengelly <robertapengelly@hotmail.com>
Tue, 28 Oct 2025 19:10:13 +0000 (19:10 +0000)
18 files changed:
Makefile.unix
Makefile.w32
Makefile.wat
Makefile.wcd
inttypes.h [new file with mode: 0644]
ld.c
ld.h
lib.c
lib.h
link.c
macho.c [new file with mode: 0644]
macho.h [new file with mode: 0644]
map.c
reloc.h
section.h
stdint.h [new file with mode: 0644]
symbol.c
symbol.h

index 17b1945c948fe369d72d8d0b56fd40e5ca07a916..02e559fb0779044147b359b8cf66102da516c7dc 100644 (file)
@@ -7,7 +7,7 @@ VPATH               :=  $(SRCDIR)
 CC                  :=  gcc
 CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90
 
-CSRC                :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
+CSRC                :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
 
 ifeq ($(OS), Windows_NT)
 all: slink.exe
index a34c7e284245fdd8b77948da44c43533a95919a0..2068ae71b65a5becdd3c4c5317a10582bfe0a295 100644 (file)
@@ -7,7 +7,7 @@ VPATH               :=  $(SRCDIR)
 CC                  :=  gcc
 CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90
 
-CSRC                :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
+CSRC                :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
 
 all: slink.exe
 
index 5a48e961602e6e8a4259308c74b7012039a3951f..a8fed6522c7f73237bdf86eef8a00377fb16d140 100644 (file)
@@ -4,7 +4,7 @@
 SRCDIR              ?=  $(CURDIR)
 VPATH               :=  $(SRCDIR)
 
-SRC                 :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
+SRC                 :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
 
 all: slink.exe
 
index c0d53c030132fdf933263bc938915d5c79c43272..26a85c7199c9bf2332f1bc440ba60c652319014f 100644 (file)
@@ -9,7 +9,7 @@ COPTS=-oneatx -ecc -zp1 -q -w -c -ml -zl -D__MSDOS__ -D__PDOS__ -fpi87 -s -zdp -
 all: clean slink.exe
 
 slink.exe: aout.obj coff.obj elks.obj hashtab.obj ld.obj \
-     lib.obj link.obj map.obj mz.obj omf.obj pe.obj report.obj \
+     lib.obj link.obj macho.obj map.obj mz.obj omf.obj pe.obj report.obj \
      section.obj symbol.obj vector.obj write7x.obj
   wlink File ..\pdos\pdpclib\dosstart.obj,ld.obj Name slink.exe Form dos Library temp.lib,..\pdos\pdpclib\watcom.lib Option quiet,nod,stack=8192,start=___asmstart,map,verbose,dosseg
 
diff --git a/inttypes.h b/inttypes.h
new file mode 100644 (file)
index 0000000..34d387c
--- /dev/null
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * @file            inttypes.h
+ *****************************************************************************/
+#include    <limits.h>
+
+#if     defined (NO_LONG_LONG)
+# define    I64_FMT                     "l"
+#elif   defined (_MSVC_)
+# define    I64_FMT                     "I64"
+#elif   defined (__PRI_64_LENGTH_MODIFIER__)                                                        /* Mac */
+# define    I64_FMT                     __PRI_64_LENGTH_MODIFIER__
+#elif   (defined (SIZEOF_LONG) && SIZEOF_LONG >= 8) || ULONG_MAX > 4294967295UL
+# define    I64_FMT                     "l"
+#else
+# define    I64_FMT                     "ll"
+#endif
+
+#define     PRId64                      I64_FMT"d"
+#define     PRIi64                      I64_FMT"i"
+#define     PRIo64                      I64_FMT"o"
+#define     PRIu64                      I64_FMT"u"
+#define     PRIx64                      I64_FMT"x"
+#define     PRIX64                      I64_FMT"X"
+
diff --git a/ld.c b/ld.c
index d205a89a1010d86be4ac42bc06582014ed40699b..1ec61d4ff1fb2bf23381c5e51abd5a9de8ca2c61 100644 (file)
--- a/ld.c
+++ b/ld.c
@@ -11,6 +11,7 @@
 #include    "elks.h"
 #include    "ld.h"
 #include    "lib.h"
+#include    "macho.h"
 #include    "mz.h"
 #include    "omf.h"
 #include    "pe.h"
@@ -95,7 +96,7 @@ static int read_object_file (const char *filename, unsigned char *data, unsigned
     
     if (data[0] == 0 && data[1] == 0) {
     
-        if (data[2] == 0xff && data[3] == 0xff) {
+        if (data[2] == 0xFF && data[3] == 0xFF) {
         
             read_pe_import_library (filename, data);
             return 0;
@@ -104,6 +105,13 @@ static int read_object_file (const char *filename, unsigned char *data, unsigned
     
     }
     
+    if (data[0] == 0xCF && data[1] == 0xFA && data[2] == 0xED && data[3] == 0xFE) {
+    
+        read_macho_object (filename, data, data_size);
+        return 0;
+    
+    }
+    
     if (data[0] == RECORD_TYPE_THEADR) {
     
         read_omf_object (filename, data, data_size);
@@ -140,32 +148,98 @@ static int read_archive_memeber (unsigned char *pos, const char *filename) {
     
     hdr = (struct ar_header *) pos;
     
-    temp = xstrndup (hdr->name, sizeof (hdr->name));
-    strip_trailing_spaces (temp);
+    name = xstrndup (hdr->name, sizeof (hdr->name));
+    strip_trailing_spaces (name);
     
-    if (temp) {
+    if (strncmp (name, "#1/", 3) == 0) {
     
-        unsigned long member_name_len = strlen (temp);
+        unsigned long name_size, hdr_size;
+        char *temp, *p;
+        
+        name_size = strtoul (name + 3, &p, 10);
+        
+        if (*p) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: invalid archive member longname '%s'", filename, name);
+            
+            free (name);
+            return -1;
         
-        if (member_name_len && temp[member_name_len - 1] == '/' && temp[0] != '/') {
-            temp[member_name_len - 1] = '\0';
         }
+        
+        temp = xstrndup (hdr->size, sizeof (hdr->size));
+        strip_trailing_spaces (temp);
+        
+        hdr_size = strtoul (temp, NULL, 10);
+        free (temp);
+        
+        if (name_size > hdr_size) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: invalid archive member longname size %lu", filename, name_size);
+            
+            free (name);
+            return -1;
+        
+        }
+        
+        temp = xstrndup (hdr->size, sizeof (hdr->size));
+        strip_trailing_spaces (temp);
+        
+        data_size = strtoul (temp, NULL, 10);
+        free (temp);
+        
+        hdr = (struct ar_header *) (pos += sizeof (*hdr));
+        free (name);
+        
+        temp = xstrndup (hdr->name, name_size);
+        
+        if (temp) {
+        
+            unsigned long member_name_len = strlen (temp);
+            
+            if (member_name_len && temp[member_name_len - 1] == '/' && temp[0] != '/') {
+                temp[member_name_len - 1] = '\0';
+            }
+        
+        }
+        
+        name = xmalloc (strlen (filename) + 1 + strlen (temp) + 1 + 1);
+        sprintf (name, "%s(%s)", filename, temp);
+        
+        free (temp);
+        
+        ret = read_object_file (name, pos + name_size, data_size);
+        free (name);
     
-    }
-    
-    name = xmalloc (strlen (filename) + 1 + strlen (temp) + 1 + 1);
-    sprintf (name, "%s(%s)", filename, temp);
-    
-    free (temp);
-    
-    temp = xstrndup (hdr->size, sizeof (hdr->size));
-    strip_trailing_spaces (temp);
+    } else {
     
-    data_size = strtoul (temp, NULL, 10);
-    free (temp);
+        temp = name;
+        
+        if (temp) {
+        
+            unsigned long member_name_len = strlen (temp);
+            
+            if (member_name_len && temp[member_name_len - 1] == '/' && temp[0] != '/') {
+                temp[member_name_len - 1] = '\0';
+            }
+        
+        }
+        
+        name = xmalloc (strlen (filename) + 1 + strlen (temp) + 1 + 1);
+        sprintf (name, "%s(%s)", filename, temp);
+        
+        free (temp);
+        
+        temp = xstrndup (hdr->size, sizeof (hdr->size));
+        strip_trailing_spaces (temp);
+        
+        data_size = strtoul (temp, NULL, 10);
+        free (temp);
+        
+        ret = read_object_file (name, pos + sizeof (*hdr), data_size);
+        free (name);
     
-    ret = read_object_file (name, pos + sizeof (*hdr), data_size);
-    free (name);
+    }
     
     memcpy (pos, ALREADY_READ, sizeof (ALREADY_READ));
     return ret;
@@ -180,7 +254,7 @@ static void read_archive (const char *filename, unsigned char *data) {
     struct offset_name_table *offset_name_table;
     
     char *string_table_pos, *name;
-    unsigned long no_symbols, i;
+    unsigned long num_symbols, i;
     
     unsigned long start_header_object_offset = 0;
     unsigned long end_header_object_offset = 0;
@@ -188,37 +262,121 @@ static void read_archive (const char *filename, unsigned char *data) {
     name = xstrndup (hdr->name, sizeof (hdr->name));
     strip_trailing_spaces (name);
     
-    if (strcmp (name, "/") != 0) {
+    if (strcmp (name, "/") == 0) {
     
-        report_at (program_name, 0, REPORT_ERROR, "%s: unable to add symbols: archive has no index; run ranlib to add one", filename);
+        pos += sizeof (*hdr);
+        
+        num_symbols = array_to_integer (pos, 4, 1);
+        pos += 4;
+        
+        offset_name_table = xmalloc (sizeof (*offset_name_table) * num_symbols);
+        string_table_pos = (char *) pos + (num_symbols * 4);
+        
+        for (i = 0; i < num_symbols; i++) {
+        
+            offset_name_table[i].offset = array_to_integer (pos, 4, 1);
+            pos += 4;
+            
+            offset_name_table[i].name = (char *) string_table_pos;
+            string_table_pos += strlen (offset_name_table[i].name) + 1;
+        
+        }
         
         free (name);
-        return;
-    
-    }
-    
-    free (name);
-    pos += sizeof (*hdr);
     
-    no_symbols = array_to_integer (pos, 4, 1);
-    pos += 4;
+    } else if (strncmp (name, "#1/", 3) == 0) {
     
-    offset_name_table = xmalloc (sizeof (*offset_name_table) * no_symbols);
-    string_table_pos = (char *) pos + (no_symbols * 4);
+        unsigned long name_size, hdr_size;
+        char *temp, *p;
+        
+        name_size = strtoul (name + 3, &p, 10);
+        
+        if (*p) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: invalid archive member longname '%s'", filename, name);
+            
+            free (name);
+            return;
+        
+        }
+        
+        temp = xstrndup (hdr->size, sizeof (hdr->size));
+        strip_trailing_spaces (temp);
+        
+        hdr_size = strtoul (temp, NULL, 10);
+        free (temp);
+        
+        if (name_size > hdr_size) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: invalid archive member longname size %lu", filename, name_size);
+            
+            free (name);
+            return;
+        
+        }
+        
+        hdr = (struct ar_header *) (pos += sizeof (*hdr));
+        free (name);
+        
+        name = xstrndup (hdr->name, name_size);
+        
+        if (strcmp (name, "__.SYMDEF SORTED") == 0) {
+        
+            unsigned long num_bytes, str_offset;
+            pos += name_size;
+            
+            num_bytes = array_to_integer (pos, 4, 0);
+            pos += 4;
+            
+            if (num_bytes % 8) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "%s: __.SYMDEF SORTED first field value not divisible by 8", filename);
+                
+                free (name);
+                return;
+            
+            }
+            
+            num_symbols = num_bytes / 8;
+            
+            offset_name_table = xmalloc (sizeof (*offset_name_table) * num_symbols);
+            string_table_pos = (char *) pos + num_symbols * 8 + 4;
+            
+            for (i = 0; i < num_symbols; i++) {
+            
+                str_offset = array_to_integer (pos, 4, 0);
+                pos += 4;
+                
+                offset_name_table[i].offset = array_to_integer (pos, 4, 0);
+                pos += 4;
+                
+                offset_name_table[i].name = (char *) string_table_pos + str_offset;
+            
+            }
+            
+            free (name);
+        
+        } else {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: unable to add symbols: archive has no index; run ranlib to add one", filename);
+            
+            free (name);
+            return;
+        
+        }
     
-    for (i = 0; i < no_symbols; i++) {
+    } else  {
     
-        offset_name_table[i].offset = array_to_integer (pos, 4, 1);
-        offset_name_table[i].name = (char *) string_table_pos;
+        report_at (program_name, 0, REPORT_ERROR, "%s: unable to add symbols: archive has no index; run ranlib to add one", filename);
         
-        string_table_pos += strlen (offset_name_table[i].name) + 1;
-        pos += 4;
+        free (name);
+        return;
     
     }
     
     if (state->format == LD_FORMAT_I386_PE) {
     
-        for (i = 0; i < no_symbols && (!start_header_object_offset || !end_header_object_offset); i++) {
+        for (i = 0; i < num_symbols && (!start_header_object_offset || !end_header_object_offset); i++) {
         
             char *symbol_name = offset_name_table[i].name;
             
@@ -241,7 +399,7 @@ static void read_archive (const char *filename, unsigned char *data) {
         struct symbol *symbol;
         int change = 0;
         
-        for (i = 0; i < no_symbols; i++) {
+        for (i = 0; i < num_symbols; i++) {
         
             if (!(symbol = symbol_find (offset_name_table[i].name)) || !symbol_is_undefined (symbol)) {
                 continue;
@@ -359,6 +517,8 @@ int main (int argc, char **argv) {
                 state->base_address = 0x00400000;
             }
         
+        } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
+            state->base_address = 0x100000000;
         }
     
     }
@@ -371,6 +531,8 @@ int main (int argc, char **argv) {
         elks_before_link ();
     } else if (state->format == LD_FORMAT_I386_PE) {
         pe_before_link ();
+    } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
+        macho_before_link ();
     }
     
     link ();
@@ -438,6 +600,8 @@ int main (int argc, char **argv) {
         coff_after_link ();
         coff_write (state->output_filename);
     
+    } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
+        macho_write (state->output_filename);
     }
     
     if (state->output_map_filename) {
diff --git a/ld.h b/ld.h
index 82292d0f4de667def19dfd75f5b2917ba28e1022..2d87d8dbbe22fd02bf6b2d8aaa23081a85d05917 100644 (file)
--- a/ld.h
+++ b/ld.h
@@ -51,6 +51,9 @@ struct ld_state {
 
 #define     LD_FORMAT_I386_PE           0x07
 
+#define     LD_FORMAT_AMD64_MACHO       0x08
+#define     LD_FORMAT_AARCH64_MACHO     0x09
+
 
 extern struct ld_state *state;
 extern const char *program_name;
diff --git a/lib.c b/lib.c
index a91dc5bc0c404ab574cec3076e0012890f1fc4f7..855cfed6c39dc9ea0ea231d3112fa66faec462e0 100644 (file)
--- a/lib.c
+++ b/lib.c
@@ -13,6 +13,7 @@
 #include    "mz.h"
 #include    "pe.h"
 #include    "report.h"
+#include    "stdint.h"
 
 struct options_with_use {
 
@@ -301,6 +302,30 @@ static void use_option (const char *cmd_arg, int idx, const char *optarg) {
             
             }
             
+            if (xstrcasecmp (optarg, "macho-amd64") == 0) {
+            
+                state->format = LD_FORMAT_AMD64_MACHO;
+                
+                /*if (state->emulation == LD_EMULATION_NONE) {
+                    state->emulation = LD_EMULATION_AND64_MACHO;
+                }*/
+                
+                break;
+            
+            }
+            
+            if (xstrcasecmp (optarg, "macho-aarch64") == 0) {
+            
+                state->format = LD_FORMAT_AARCH64_MACHO;
+                
+                /*if (state->emulation == LD_EMULATION_NONE) {
+                    state->emulation = LD_EMULATION_AND64_MACHO;
+                }*/
+                
+                break;
+            
+            }
+            
             report_at (program_name, 0, REPORT_ERROR, "unrecognised output format '%s'", optarg);
             exit (EXIT_FAILURE);
         
@@ -388,23 +413,27 @@ static void use_option (const char *cmd_arg, int idx, const char *optarg) {
 
 }
 
-unsigned long array_to_integer (unsigned char *arr, int size, int big_endian) {
+uint64_t array_to_integer (unsigned char *arr, int size, int big_endian) {
 
-    unsigned long value = 0;
+    uint64_t value = 0;
     int i;
     
+#if     defined (NO_LONG_LONG) && ULONG_MAX <= 4294967295UL
+    if (size == 8) { size = 4; }
+#endif
+    
     if (big_endian) {
     
         int j;
         
         for (i = size, j = 0; i > 0; i--, j++) {
-            value |= (unsigned long) arr[j] << (CHAR_BIT * (i - 1));
+            value |= (uint64_t) arr[j] << (CHAR_BIT * (i - 1));
         }
     
     } else {
     
         for (i = 0; i < size; i++) {
-            value |= (unsigned long) arr[i] << (CHAR_BIT * i);
+            value |= (uint64_t) arr[i] << (CHAR_BIT * i);
         }
     
     }
@@ -413,8 +442,12 @@ unsigned long array_to_integer (unsigned char *arr, int size, int big_endian) {
 
 }
 
-void integer_to_array (unsigned long value, unsigned char *dest, int size, int big_endian) {
+void integer_to_array (uint64_t value, unsigned char *dest, int size, int big_endian) {
 
+#if     defined (NO_LONG_LONG) && ULONG_MAX <= 4294967295UL
+    if (size == 8) { size = 4; }
+#endif
+    
     if (big_endian) {
     
         int i, j;
diff --git a/lib.h b/lib.h
index 7ee850ec40de4c420282cc52fb71515406657469..f741eb1649723ef90614d677665e01774d81d0d1 100644 (file)
--- a/lib.h
+++ b/lib.h
@@ -19,8 +19,10 @@ struct ld_option {
 #define     LD_OPTION_NO_ARG            0
 #define     LD_OPTION_HAS_ARG           1
 
-unsigned long array_to_integer (unsigned char *arr, int size, int big_endian);
-void integer_to_array (unsigned long value, unsigned char *dest, int size, int big_endian);
+#include    "stdint.h"
+
+uint64_t array_to_integer (unsigned char *arr, int size, int big_endian);
+void integer_to_array (uint64_t value, unsigned char *dest, int size, int big_endian);
 
 int xstrcasecmp (const char *__s1, const char *__s2);
 
diff --git a/link.c b/link.c
index d47a9bfb1920faa7db6d73456b5fc03870cc0cc7..d1885b619e303ff1c1d90ccbcec3f40693a73405 100644 (file)
--- a/link.c
+++ b/link.c
 
 #include    "ld.h"
 #include    "lib.h"
+#include    "macho.h"
 #include    "pe.h"
 #include    "reloc.h"
 #include    "report.h"
 #include    "section.h"
+#include    "stdint.h"
 #include    "symbol.h"
 
+static uint64_t generic_read (struct section_part *part, struct reloc_entry *rel) {
+
+    uint64_t result;
+    int endianess = 0;
+    
+    switch (rel->howto->size) {
+    
+        case 8:
+        
+            result = array_to_integer (part->content + rel->offset, 8, endianess);
+            break;
+        
+        case 4:
+        
+            result = array_to_integer (part->content + rel->offset, 4, endianess);
+            break;
+        
+        case 3:
+        
+            result = array_to_integer (part->content + rel->offset, 3, endianess);
+            break;
+        
+        case 2:
+        
+            result = array_to_integer (part->content + rel->offset, 2, endianess);
+            break;
+        
+        case 1:
+        
+            result = array_to_integer (part->content + rel->offset, 1, endianess);
+            break;
+        
+        default:
+        
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "invalid relocation size");
+            exit (EXIT_FAILURE);
+    
+    }
+    
+    return result;
+
+}
+
+static void generic_write (struct section_part *part, struct reloc_entry *rel, uint64_t result) {
+
+    int endianess = 0;
+    
+    switch (rel->howto->size) {
+    
+        case 8:
+        
+            integer_to_array (result, part->content + rel->offset, 8, endianess);
+            break;
+        
+        case 4:
+        
+            integer_to_array (result, part->content + rel->offset, 4, endianess);
+            break;
+        
+        case 3:
+        
+            integer_to_array (result, part->content + rel->offset, 3, endianess);
+            break;
+        
+        case 2:
+        
+            integer_to_array (result, part->content + rel->offset, 2, endianess);
+            break;
+        
+        case 1:
+        
+            integer_to_array (result, part->content + rel->offset, 1, endianess);
+            break;
+        
+        default:
+        
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "invalid relocation size");
+            exit (EXIT_FAILURE);
+    
+    }
+
+}
+
+static void reloc_generic_add (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol) {
+
+    uint64_t result, saved;
+    
+    saved = generic_read (part, rel);
+    result = rel->addend;
+    
+    if (rel->howto->pc_rel || rel->howto->no_base) {
+        result += symbol_get_value_no_base (symbol);
+    } else {
+        result += symbol_get_value_with_base (symbol);
+    }
+    
+    if (rel->howto->pc_rel) {
+    
+        result -= part->rva + rel->offset;
+        result -= rel->howto->size;
+    
+    }
+    
+    if (rel->howto->size < (int) sizeof (result)) {
+    
+        uint64_t mask = (((uint64_t) 1) << (CHAR_BIT * rel->howto->size)) - 1;
+        result &= mask;
+    
+    }
+    
+    if (rel->howto->final_right_shift) {
+    
+        /* If the result was negative, it should remain negative even after the final right shift. */
+        uint64_t sign_bit = result & (((uint64_t) 1) << (rel->howto->size * 8 - 1));
+        
+        result >>= rel->howto->final_right_shift;
+        result |= sign_bit;
+    
+    }
+    
+    generic_write (part, rel, saved + result);
+
+}
+
+static void reloc_generic_subtract (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol) {
+
+    uint64_t result, saved;
+    
+    saved = generic_read (part, rel);
+    result = rel->addend;
+    
+    if (rel->howto->pc_rel || rel->howto->no_base) {
+        result += symbol_get_value_no_base (symbol);
+    } else {
+        result += symbol_get_value_with_base (symbol);
+    }
+    
+    if (rel->howto->pc_rel) {
+    
+        result -= part->rva + rel->offset;
+        result -= rel->howto->size;
+    
+    }
+    
+    if (rel->howto->size < (int) sizeof (result)) {
+    
+        uint64_t mask = (((uint64_t) 1) << (CHAR_BIT * rel->howto->size)) - 1;
+        result &= mask;
+    
+    }
+    
+    if (rel->howto->final_right_shift) {
+    
+        /* If the result was negative, it should remain negative even after the final right shift. */
+        uint64_t sign_bit = result & (((uint64_t) 1) << (rel->howto->size * 8 - 1));
+        
+        result >>= rel->howto->final_right_shift;
+        result |= sign_bit;
+    
+    }
+    
+    generic_write (part, rel, saved - result);
+
+}
+
+static void reloc_aarch64_hi21_page_pcrel (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol) {
+
+    uint64_t bottom_2_bits = 0, result;
+    unsigned long field;
+    
+    result = (symbol_get_value_with_base (symbol) + rel->addend) & ~(uint64_t) 0xfff;
+    result -= (state->base_address + part->rva + rel->offset) & ~(uint64_t) 0xfff;
+    
+    if ((bottom_2_bits = result & 0x3000)) {
+    
+        result &= ~bottom_2_bits;
+        bottom_2_bits >>= 12;
+    
+    }
+    
+    result >>= 9;
+    
+    /**
+     * If the result is negative, those 2 bits are already set,
+     * so they must be cleared before putting the real bottom 2 bits there.
+     */
+    result &= ~(((uint64_t) 0x03) << 29);
+    result |= bottom_2_bits << 29;
+    
+    field = array_to_integer (part->content + rel->offset, 4, 0);
+    field = ((field & ~(uint64_t) rel->howto->dst_mask) | (((field & rel->howto->dst_mask) + result) & rel->howto->dst_mask));
+    
+    integer_to_array (field, part->content + rel->offset, 4, 0);
+
+}
+
+static void reloc_aarch64_generic (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol) {
+
+    uint64_t result = rel->addend, field;
+    result += symbol_get_value_with_base (symbol);
+    
+    if (rel->howto->pc_rel) {
+        result -= state->base_address + part->rva + rel->offset;
+    }
+    
+    result >>= rel->howto->final_right_shift;
+    result <<= rel->howto->final_left_shift;
+    
+    switch (rel->howto->size) {
+    
+        case 8:
+        
+            field = array_to_integer (part->content + rel->offset, 8, 0);
+            break;
+        
+        case 4:
+        
+            field = array_to_integer (part->content + rel->offset, 4, 0);
+            break;
+        
+        case 2:
+        
+            field = array_to_integer (part->content + rel->offset, 2, 0);
+            break;
+        
+        default:
+        
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "invalid relocation size");
+            exit (EXIT_FAILURE);
+    
+    }
+
+    field = ((field & ~(uint64_t) rel->howto->dst_mask) | (((field & rel->howto->dst_mask) + result) & rel->howto->dst_mask));
+    
+    switch (rel->howto->size) {
+    
+        case 8:
+        
+            integer_to_array (field, part->content + rel->offset, 8, 0);
+            break;
+        
+        case 4:
+        
+            integer_to_array (field, part->content + rel->offset, 4, 0);
+            break;
+        
+        case 2:
+        
+            integer_to_array (field, part->content + rel->offset, 2, 0);
+            break;
+        
+        default:
+        
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "invalid relocation size");
+            exit (EXIT_FAILURE);
+    
+    }
+
+}
+
 struct reloc_howto reloc_howtos[RELOC_TYPE_END] = {
 
     {   0,  0,  0,  0,  0,  "RELOC_TYPE_IGNORED",       0,  0   },
@@ -28,7 +290,29 @@ struct reloc_howto reloc_howtos[RELOC_TYPE_END] = {
     {   2,  1,  0,  0,  0,  "RELOC_TYPE_PC16",          0,  0   },
     {   1,  1,  0,  0,  0,  "RELOC_TYPE_PC8",           0,  0   },
     
-    {   4,  0,  1,  0,  0,  "RELOC_TYPE_32_NO_BASE",    0,  0   }
+    {   4,  0,  1,  0,  0,  "RELOC_TYPE_32_NO_BASE",    0,  0   },
+    
+    {   8,  0,  0,  0,  &reloc_generic_add,     "RELOC_TYPE_64_ADD",    0,  0   },
+    {   4,  0,  0,  0,  &reloc_generic_add,     "RELOC_TYPE_32_ADD",    0,  0   },
+    {   3,  0,  0,  0,  &reloc_generic_add,     "RELOC_TYPE_24_ADD",    0,  0   },
+    {   2,  0,  0,  0,  &reloc_generic_add,     "RELOC_TYPE_16_ADD",    0,  0   },
+    {   1,  0,  0,  0,  &reloc_generic_add,     "RELOC_TYPE_8_ADD",     0,  0   },
+
+    {   8,  0,  0,  0,  &reloc_generic_subtract,    "RELOC_TYPE_64_SUB",    0,  0   },
+    {   4,  0,  0,  0,  &reloc_generic_subtract,    "RELOC_TYPE_32_SUB",    0,  0   },
+    {   3,  0,  0,  0,  &reloc_generic_subtract,    "RELOC_TYPE_24_SUB",    0,  0   },
+    {   2,  0,  0,  0,  &reloc_generic_subtract,    "RELOC_TYPE_16_SUB",    0,  0   },
+    {   1,  0,  0,  0,  &reloc_generic_subtract,    "RELOC_TYPE_8_SUB",     0,  0   },
+    
+    {   4,  1,  0,  9,  &reloc_aarch64_hi21_page_pcrel,     "RELOC_TYPE_AARCH64_ADR_PREL_PG_HI21",      0x60ffffe0,     0   },
+    {   4,  0,  0,  0,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_ADD_ABS_LO12_NC",       0x3ffc00,       10  },
+    {   4,  0,  0,  0,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_LDST8_ABS_LO12_NC",     0x3ffc00,       10  },
+    {   4,  0,  0,  1,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_LDST16_ABS_LO12_NC",    0x1ffc00,       10  },
+    {   4,  0,  0,  2,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_LDST32_ABS_LO12_NC",    0xffc00,        10  },
+    {   4,  0,  0,  3,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_LDST64_ABS_LO12_NC",    0x7fc00,        10  },
+    {   4,  0,  0,  4,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_LDST128_ABS_LO12_NC",   0x3fc00,        10  },
+    {   4,  1,  0,  2,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_JUMP26",                0x3ffffff,      0   },
+    {   4,  1,  0,  2,  &reloc_aarch64_generic,             "RELOC_TYPE_AARCH64_CALL26",                0x3ffffff,      0   }
 
 };
 
@@ -252,10 +536,12 @@ static void calculate_section_sizes_and_rvas (void) {
     struct section *section;
     struct section_part *part;
     
-    unsigned long rva = 0;
+    uint64_t rva = 0;
     
     if (state->format == LD_FORMAT_I386_PE) {
         rva = pe_get_first_section_rva ();
+    } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
+        rva = macho_get_first_section_rva ();
     }
     
     for (section = all_sections; section; section = section->next) {
@@ -269,7 +555,7 @@ static void calculate_section_sizes_and_rvas (void) {
         
             if (part->next && part->next->alignment > 1) {
             
-                unsigned long new_rva = ALIGN (rva + part->content_size, part->next->alignment);
+                uint64_t new_rva = ALIGN (rva + part->content_size, part->next->alignment);
                 
                 if (new_rva != rva + part->content_size) {
                 
@@ -296,7 +582,7 @@ static void calculate_section_sizes_and_rvas (void) {
 static void reloc_generic (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol) {
 
     unsigned char opcode = (part->content + rel->offset - 1)[0];
-    unsigned long result = 0, size = rel->howto->size, offset = rel->offset;
+    uint64_t result = 0, size = rel->howto->size, offset = rel->offset;
     
     switch (size) {
     
@@ -413,14 +699,19 @@ static void reloc_generic (struct section_part *part, struct reloc_entry *rel, s
     
     if ((unsigned long) size < sizeof (result)) {
     
-        unsigned long mask = (((unsigned long) 1) << (CHAR_BIT * size)) - 1;
+        unsigned long mask = (((uint64_t) 1) << (CHAR_BIT * size)) - 1;
         result &= mask;
     
     }
     
-    result >>= rel->howto->final_right_shift;
+    if (rel->howto->final_right_shift) {
+    
+        uint64_t sign_bit = result & (((uint64_t) 1) << (rel->howto->size * 8 - 1));
+        result >>= rel->howto->final_right_shift;
+        
+        result |= sign_bit;
     
-    /*printf ("%lx\n", n_type);*/
+    }
     
     if (state->emulation == LD_EMULATION_IA16_ELKS || state->emulation == LD_EMULATION_IA16_MZ) {
     
@@ -666,7 +957,7 @@ static void calculate_entry_point (void) {
     
     if (!state->entry_symbol_name) {
     
-        if (state->format == LD_FORMAT_I386_AOUT || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_IA16_ELKS) {
+        if (state->format == LD_FORMAT_I386_AOUT || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
         
             state->entry_symbol_name = xstrdup ("_start");
             
diff --git a/macho.c b/macho.c
new file mode 100644 (file)
index 0000000..53e0da4
--- /dev/null
+++ b/macho.c
@@ -0,0 +1,1749 @@
+/******************************************************************************
+ * @file            macho.c
+ *****************************************************************************/
+#include    <limits.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "ld.h"
+#include    "lib.h"
+#include    "macho.h"
+#include    "report.h"
+#include    "section.h"
+
+struct part_reloc {
+
+    struct section_part *part;
+    struct reloc_entry *reloc_entry;
+
+};
+
+static struct part_reloc addend_part_reloc = { 0 };
+
+static void apply_addend_reloc (struct reloc_entry *reloc, struct section_part *part) {
+
+    if (!addend_part_reloc.reloc_entry) return;
+    
+    if (part != addend_part_reloc.part) {
+        report_at (program_name, 0, REPORT_WARNING, "%s: ARM64_RELOC_ADDEND is in different" " section than relocation it applies to", addend_part_reloc.part->of->filename);
+    }
+    
+    if (reloc->offset != addend_part_reloc.reloc_entry->offset) {
+        report_at (program_name, 0, REPORT_WARNING, "%s: ARM64_RELOC_ADDEND has different offset than relocation it applies to", addend_part_reloc.part->of->filename);
+    }
+    
+    reloc->addend = addend_part_reloc.reloc_entry->addend;
+    addend_part_reloc.reloc_entry = 0;
+
+}
+
+#define     CHECK_READ(memory_position, size_to_read)                           \
+    do                                                                          \
+        if (((memory_position) - data + (size_to_read) > data_size) ||          \
+             (memory_position) < data) {                                        \
+            report_at (program_name, 0, REPORT_FATAL_ERROR,                     \
+                "%s: corrupt input file", filename);                            \
+            exit (EXIT_FAILURE);                                                \
+        }                                                                       \
+    while (0)
+
+static void translate_relocation (struct reloc_entry *reloc, struct macho_relocation_info *input_reloc, struct section_part *part) {
+
+    unsigned long r_symbolnum = array_to_integer (input_reloc->r_symbolnum, 4, 0);
+    
+    unsigned long field;
+    unsigned int size, pcrel, type;
+    
+    if ((r_symbolnum >> 27) & 1) {
+        reloc->symbol = part->of->symbol_arr + (r_symbolnum & 0xffffff);
+    } else {
+    
+        if (r_symbolnum == 0) {
+        
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "local input_reloc->r_symbolnum %#lx is not yet supported", r_symbolnum & 0xffffff);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        reloc->symbol = part->of->symbol_arr + part->of->symbol_cnt - (r_symbolnum & 0xffffff);
+    
+    }
+    
+    reloc->offset = array_to_integer (input_reloc->r_address, 4, 0);
+    reloc->r_symbolnum = r_symbolnum;
+    
+    size = 1U << ((r_symbolnum >> 25) & 3);
+    
+    pcrel = (r_symbolnum >> 24) & 1;
+    type = r_symbolnum >> 28;
+    
+    if (state->format == LD_FORMAT_AMD64_MACHO) {
+    
+        switch (size) {
+        
+            case 8:
+            
+                if (!pcrel && type == AMD64_RELOC_UNSIGNED) {
+                    reloc->howto = &reloc_howtos[RELOC_TYPE_64];
+                } else {
+                    goto unsupported;
+                }
+                
+                break;
+            
+            case 4:
+            
+                if (pcrel) {
+                
+                    if (type == AMD64_RELOC_SIGNED) {
+                    
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_PC32];
+                        
+                        if (reloc->symbol->flags & SYMBOL_FLAG_SECTION_SYMBOL) {
+                        
+                            field = array_to_integer (part->content + reloc->offset, 4, 0);
+                            
+                            field -= reloc->symbol->part->rva - reloc->offset - 4;
+                            integer_to_array (field, part->content + reloc->offset, 4, 0);
+                        
+                        }
+                    
+                    } else if (type == AMD64_RELOC_BRANCH) {
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_PC32];
+                    } else if (type == AMD64_RELOC_GOT_LOAD) {
+                    
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_PC32];
+                        
+                        if (part->content[reloc->offset - 2] != 0x8b) {
+                        
+                            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: not yet supported x64 GOT instruction", part->of->filename);
+                            exit (EXIT_FAILURE);
+                        
+                        }
+                        
+                        part->content[reloc->offset - 2] = 0x8d;
+                    
+                    } else if (type == AMD64_RELOC_GOT) {
+                    
+                        uint64_t addend, helper_offset;
+                        
+                        if (part->content[reloc->offset - 1] != 0x05 || part->content[reloc->offset - 2] != 0x03 || part->content[reloc->offset - 3] != 0x48) {
+                        
+                            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "%s: not yet supported x64 GOT instruction", part->of->filename);
+                            exit (EXIT_FAILURE);
+                        
+                        }
+                        
+                        part->content[reloc->offset - 3] = 0x90;
+                        part->content[reloc->offset - 2] = 0x90;
+                        part->content[reloc->offset - 1] = 0xe8;
+                        
+                        field = array_to_integer (part->content + reloc->offset, 4, 0);
+                        addend = field;
+                        
+                        field = part->content_size - reloc->offset - 4;
+                        integer_to_array (field, part->content + reloc->offset, 4, 0);
+                        
+                        helper_offset = part->content_size;
+                        part->content_size += 13;
+                        
+                        part->content = xrealloc (part->content, part->content_size);
+                        memcpy (part->content + helper_offset,
+                            "\x51"
+                            "\x48\x8D\x0D\x00\x00\x00\x00"
+                            "\x48\x01\xC8"
+                            "\x59"
+                            "\xC3",
+                        13);
+                        
+                        reloc->offset = helper_offset + 4;
+                        integer_to_array (addend, part->content + reloc->offset, 4, 0);
+                        
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_PC32];
+                    
+                    } else if (type == AMD64_RELOC_SIGNED_1 || type == AMD64_RELOC_SIGNED_2 || type == AMD64_RELOC_SIGNED_4) {
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_PC32];
+                    } else {
+                        goto unsupported;
+                    }
+                
+                } else {
+                
+                    if (type == AMD64_RELOC_UNSIGNED) {
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_32];
+                    } else if (type == AMD64_RELOC_SUBTRACTOR) {
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_32_SUB];
+                    } else {
+                        goto unsupported;
+                    }
+                
+                }
+                
+                break;
+            
+            default:
+            
+                goto unsupported;
+        
+        }
+        
+        return;
+    
+    }
+    
+    if (state->format == LD_FORMAT_AARCH64_MACHO) {
+    
+        switch (size) {
+        
+            case 8:
+            
+                if (!pcrel && type == ARM64_RELOC_UNSIGNED) {
+                    reloc->howto = &reloc_howtos[RELOC_TYPE_64];
+                } else {
+                    goto unsupported;
+                }
+                
+                break;
+            
+            case 4:
+            
+                if (pcrel) {
+                
+                    if (type == ARM64_RELOC_BRANCH26) {
+                    
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_CALL26];
+                        apply_addend_reloc (reloc, part);
+                    
+                    } else if (type == ARM64_RELOC_PAGE21) {
+                    
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_ADR_PREL_PG_HI21];
+                        apply_addend_reloc (reloc, part);
+                    
+                    } else if (type == ARM64_RELOC_GOT_LOAD_PAGE21) {
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_ADR_PREL_PG_HI21];
+                    } else {
+                        goto unsupported;
+                    }
+                } else {
+                
+                    if (type == ARM64_RELOC_PAGEOFF12) {
+                    
+                        if (part->content[reloc->offset + 3] == 0x39) {
+                            reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_LDST8_ABS_LO12_NC];
+                        } else if (part->content[reloc->offset + 3] == 0x79) {
+                            reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_LDST16_ABS_LO12_NC]; /*strh*/
+                        } else if (part->content[reloc->offset + 3] == 0xB9) {
+                            reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_LDST32_ABS_LO12_NC];
+                        } else if (part->content[reloc->offset + 3] == 0xF9 || part->content[reloc->offset + 3] == 0xFD) {
+                            reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_LDST64_ABS_LO12_NC];
+                        } else if (part->content[reloc->offset + 3] == 0x3D) {
+                            reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_LDST128_ABS_LO12_NC];
+                        } else {
+                            reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_ADD_ABS_LO12_NC];
+                        }
+                        
+                        apply_addend_reloc (reloc, part);
+                    
+                    } else if (type == ARM64_RELOC_GOT_LOAD_PAGEOFF12) {
+                    
+                        field  = array_to_integer (part->content + reloc->offset, 4, 0);
+                        field &= ~0x68400000;
+                        
+                        integer_to_array (field, part->content + reloc->offset, 4, 0);
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_AARCH64_ADD_ABS_LO12_NC];
+                    
+                    } else if (type == ARM64_RELOC_ADDEND) {
+                    
+                        reloc->symbol = NULL;
+                        reloc->addend = r_symbolnum & 0xffffff;
+                        reloc->howto = &reloc_howtos[RELOC_TYPE_IGNORED];
+                        
+                        addend_part_reloc.reloc_entry = reloc;
+                        addend_part_reloc.part = part;
+                    
+                    } else {
+                        goto unsupported;
+                    }
+                
+                }
+                
+                break;
+            
+            default:
+            
+                goto unsupported;
+        
+        }
+        
+        return;
+    
+    }
+    
+unsupported:
+    
+    report_at (program_name, 0, REPORT_WARNING, "%s: ignoring not yet supported relocation with size %u, pcrel %u and type %#x", part->of->filename, size, pcrel, type);
+    reloc->howto = &reloc_howtos[RELOC_TYPE_IGNORED];
+
+}
+
+#define     UNNAMED_SYMBOL_NAME         "(unnamed)"
+
+void read_macho_object (const char *filename, unsigned char *data, unsigned long data_size) {
+
+    unsigned char *pos = (unsigned char *) data;
+    struct macho_header_64 *header;
+    
+    struct section_part **part_p_array = 0, *bss_part;
+    struct object_file *of = 0;
+    
+    struct segment_command_64 *segment_cmd;
+    struct section_64 *section_64;
+    struct symtab_command *symtab_cmd;
+    
+    struct section_part *part;
+    struct section *section;
+    
+    struct section *bss_section = 0;
+    long bss_section_number = 0;
+    
+    char *section_name, *segment_name, *string_table;
+    unsigned long num_symbols = 0, num_sections = 0;
+    
+    unsigned long sizeof_cmds, cmd_size, i, j, k;
+    struct load_command *load_command;
+    
+    unsigned long num_cmds, num_sects, num_syms, n_strx, cpu_type;
+    unsigned char *sym_pos;
+    
+    struct nlist_64 *nlist_64;
+    struct symbol *old_symbol, *symbol;
+    
+    struct macho_relocation_info *reloc_info;
+    unsigned char *rel_pos;
+    
+    CHECK_READ (pos, sizeof (*header));
+    
+    header = (struct macho_header_64 *) pos;
+    pos += sizeof (*header);
+    
+    cpu_type = array_to_integer (header->cpu_type, 4, 0);
+    
+    if (cpu_type != MH_CPU_TYPE_AMD64 && cpu_type != MH_CPU_TYPE_ARM64) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: unrecognized cputype %#lx", filename, cpu_type);
+        return;
+    
+    }
+    
+    sizeof_cmds = array_to_integer (header->sizeof_cmds, 4, 0);
+    
+    if (array_to_integer (header->file_type, 4, 0) != MH_OBJECT) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: file type is not MH_OBJECT", filename);
+        return;
+    
+    }
+    
+    num_cmds = array_to_integer (header->num_cmds, 4, 0);
+    CHECK_READ (pos, num_cmds);
+    
+    for (i = 0; i < num_cmds; i++) {
+    
+        load_command = (struct load_command *) pos;
+        
+        if (pos - data + sizeof (*load_command) > sizeof (*header) + sizeof_cmds) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: invalid header size of commands / number of commands", filename);
+            return;
+        
+        }
+        
+        cmd_size = array_to_integer (load_command->command_size, 4, 0);
+        
+        if (pos - data + cmd_size > sizeof (*header) + sizeof_cmds) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "%s: invalid load command command size", filename);
+            return;
+        
+        }
+        
+        if (array_to_integer (load_command->command, 4, 0) == LC_SYMTAB) {
+        
+            symtab_cmd = (struct symtab_command *) pos;
+            
+            if (sizeof (*symtab_cmd) > array_to_integer (symtab_cmd->command_size, 4, 0)) {
+            
+                report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: symtab command command size too small", filename);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            if (num_symbols && array_to_integer (symtab_cmd->num_symbols, 4, 0)) {
+            
+                report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: more than 1 non-empty symbol table per object file", filename);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            num_symbols = array_to_integer (symtab_cmd->num_symbols, 4, 0);
+        
+        } else if (array_to_integer (load_command->command, 4, 0) == LC_SEGMENT_64) {
+        
+            segment_cmd = (struct segment_command_64 *) pos;
+            
+            if (num_sections && array_to_integer (segment_cmd->num_sects, 4, 0)) {
+            
+                report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: more than 1 non-empty segment command per object file", filename);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            num_sections = array_to_integer (segment_cmd->num_sects, 4, 0);
+        
+        }
+        
+        pos += cmd_size;
+    
+    }
+    
+    of = object_file_make (filename, num_symbols + num_sections);
+    pos = (unsigned char *) (data + sizeof (*header));
+    
+    for (i = 0; i < num_cmds; i++) {
+    
+        load_command = (struct load_command *) pos;
+        
+        if (array_to_integer (load_command->command, 4, 0) == LC_SEGMENT_64) {
+        
+            segment_cmd = (struct segment_command_64 *) pos;
+            
+            if (!(num_sects = array_to_integer (segment_cmd->num_sects, 4, 0))) {
+            
+                pos += array_to_integer (load_command->command_size, 4, 0);
+                continue;
+            
+            }
+            
+            part_p_array = xmalloc ((num_sects + 1) * sizeof (*part_p_array));
+            
+            if (sizeof (*segment_cmd) + num_sects * sizeof (*section_64) > array_to_integer (segment_cmd->command_size, 4, 0)) {
+            
+                report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: segment command command size and number of sections mismatch", filename);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            for (j = 0; j < num_sects; j++) {
+            
+                section_64 = (struct section_64 *) (pos + sizeof (*segment_cmd) + j * sizeof (*section_64));
+                
+                section_name = xstrndup (section_64->sect_name, sizeof (section_64->sect_name));
+                segment_name = xstrndup (section_64->seg_name, sizeof (section_64->seg_name));
+                
+                section = section_find_or_make (section_name);
+                free (section_name);
+                
+                if (1LU << array_to_integer (section_64->align, 4, 0) > section->section_alignment) {
+                    section->section_alignment = 1LU << array_to_integer (section_64->align, 4, 0);
+                }
+                
+                if (strcmp (segment_name, "__TEXT") == 0) {
+                
+                    unsigned long flags = array_to_integer (section_64->flags, 4, 0);
+                    section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_READONLY;
+                    
+                    if ((flags & S_REGULAR)) {
+                        section->flags |= SECTION_FLAG_CODE;
+                    }
+                
+                } else if (strcmp (segment_name, "__DATA") == 0) {
+                
+                    unsigned long flags = array_to_integer (section_64->flags, 4, 0);
+                    
+                    if (strcmp (section->name, "__const") == 0) {
+                        section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_READONLY;
+                    } else if ((flags & 0xff) == S_ZEROFILL) {
+                    
+                        section->flags = SECTION_FLAG_ALLOC;
+                        section->is_bss = 1;
+                        
+                        bss_section_number = j + 1;
+                        bss_section = section;
+                    
+                    } else {
+                        section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_DATA;
+                    }
+                
+                } else {
+                    section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_READONLY;
+                }
+                
+                free (segment_name);
+                
+                part = section_part_new (section, of);
+                part->rva = array_to_integer (section_64->addr, 8, 0);
+                
+                part->content_size = array_to_integer (section_64->size, 8, 0);
+                part->alignment = 1LU << array_to_integer (section_64->align, 4, 0);
+                
+                if (!section->is_bss) {
+                
+                    unsigned char *content_pos = (unsigned char *) data + array_to_integer (section_64->offset, 4, 0);
+                    
+                    part->content = xmalloc (part->content_size);
+                    memcpy (part->content, content_pos, part->content_size);
+                
+                }
+                
+                section_append_section_part (section, part);
+                part_p_array[j + 1] = part;
+            
+            }
+        
+        }
+        
+        pos += array_to_integer (load_command->command_size, 4, 0);
+    
+    }
+    
+    pos = (unsigned char *) (data + sizeof (*header));
+    
+    for (i = 0; i < num_cmds; i++) {
+    
+        load_command = (struct load_command *) pos;
+        
+        if (array_to_integer (load_command->command, 4, 0) == LC_SYMTAB) {
+        
+            symtab_cmd = (struct symtab_command *) pos;
+            
+            if (!(num_syms = array_to_integer (symtab_cmd->num_symbols, 4, 0))) {
+            
+                pos += array_to_integer (load_command->command_size, 4, 0);
+                continue;
+            
+            }
+            
+            string_table = (char *) data + array_to_integer (symtab_cmd->string_offset, 4, 0);
+            CHECK_READ (data + array_to_integer (symtab_cmd->string_offset, 4, 0), array_to_integer (symtab_cmd->string_size, 4, 0));
+            
+            sym_pos = (unsigned char *) data + array_to_integer (symtab_cmd->symbol_offset, 4, 0);
+            CHECK_READ (sym_pos, sizeof (*nlist_64) * num_syms);
+            
+            for (j = 0; j < num_syms; j++) {
+            
+                symbol = of->symbol_arr + j;
+                nlist_64 = (struct nlist_64 *) (sym_pos + j * sizeof (*nlist_64));
+                
+                if ((n_strx = array_to_integer (nlist_64->n_strx, 4, 0)) < array_to_integer (symtab_cmd->string_size, 4, 0)) {
+                
+                    if (string_table[n_strx] == '\0') {
+                        symbol->name = xstrdup (UNNAMED_SYMBOL_NAME);
+                    } else {
+                        symbol->name = xstrdup (string_table + n_strx);
+                    }
+                
+                } else {
+                
+                    report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid index into string table", filename);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                symbol->value = array_to_integer (nlist_64->n_value, 8, 0);
+                
+                if ((nlist_64->n_type[0] & MACHO_N_STAB) || (nlist_64->n_type[0] & MACHO_N_PEXT)) {
+                
+                    report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "+++%s: not yet supported symbol n_type: %#x", filename, nlist_64->n_type[0]);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                if ((nlist_64->n_type[0] & MACHO_N_TYPE) == MACHO_N_UNDF) {
+                
+                    if (symbol->value) {
+                    
+                        old_symbol = symbol_find (symbol->name);
+                        
+                        if (!old_symbol || symbol_is_undefined (old_symbol)) {
+                        
+                            if (!bss_section) {
+                            
+                                bss_section = section_find_or_make ("__bss");
+                                
+                                bss_section->flags = SECTION_FLAG_ALLOC;
+                                bss_section->is_bss = 1;
+                                
+                                bss_section_number = num_sections ? num_sections : 1;
+                            
+                            }
+                            
+                            bss_part = section_part_new (bss_section, of);
+                            section_append_section_part (bss_section, bss_part);
+                            
+                            bss_part->content_size = symbol->size = symbol->value;
+                            
+                            symbol->part = bss_part;
+                            symbol->value = 0;
+                            symbol->section_number = bss_section_number;
+                        
+                        } else {
+                        
+                            if (symbol->value > old_symbol->size) {
+                                old_symbol->part->content_size = old_symbol->size = symbol->value;
+                            }
+                            
+                            symbol->value = 0;
+                            symbol->part = 0;
+                        
+                        }
+                    
+                    } else {
+                    
+                        symbol->section_number = UNDEFINED_SECTION_NUMBER;
+                        symbol->part = 0;
+                    
+                    }
+                
+                } else if ((nlist_64->n_type[0] & MACHO_N_TYPE) == MACHO_N_SECT) {
+                
+                    if (nlist_64->n_sect[0] > num_sections) {
+                    
+                        report_at (__FILE__, __LINE__, REPORT_ERROR, "%s: invalid symbol n_sect: %u", filename, nlist_64->n_sect[0]);
+                        symbol->part = 0;
+                    
+                    } else {
+                        symbol->value -= part_p_array[nlist_64->n_sect[0]]->rva;
+                    }
+                    
+                    symbol->section_number = nlist_64->n_sect[0];
+                    symbol->part = part_p_array[nlist_64->n_sect[0]];
+                
+                } else {
+                
+                    report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "+++%s: not yet supported symbol n_type: %#x", filename, nlist_64->n_type[0]);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                if (nlist_64->n_type[0] & MACHO_N_EXT) {
+                    symbol_record_external_symbol (symbol);
+                }
+            
+            }
+            
+            break;
+        
+        }
+        
+        pos += array_to_integer (load_command->command_size, 4, 0);
+    
+    }
+    
+    for (i = 1; i < num_sections + 1; i++) {
+    
+        symbol = of->symbol_arr + of->symbol_cnt - i;
+        
+        part = part_p_array[i];
+        symbol->name = xstrdup (part->section->name);
+        
+        symbol->flags |= SYMBOL_FLAG_SECTION_SYMBOL;
+        symbol->value = 0;
+        symbol->size = 0;
+        symbol->part = part;
+        symbol->section_number = i;
+    
+    }
+    
+    pos = (unsigned char *) (data + sizeof (*header));
+    
+    for (i = 0; i < num_cmds; i++) {
+    
+        load_command = (struct load_command *) pos;
+        
+        if (array_to_integer (load_command->command, 4, 0) == LC_SEGMENT_64) {
+        
+            segment_cmd = (struct segment_command_64 *) pos;
+            
+            if (!(num_sects = array_to_integer (segment_cmd->num_sects, 4, 0))) {
+            
+                pos += array_to_integer (load_command->command_size, 4, 0);
+                continue;
+            
+            }
+            
+            for (j = 0; j < num_sects; j++) {
+            
+                section_64 = (struct section_64 *) (pos + sizeof (*segment_cmd) + j * sizeof (*section_64));
+                
+                {
+                
+                    part = part_p_array[j + 1];
+                    
+                    if ((part->reloc_cnt = array_to_integer (section_64->num_reloc, 4, 0))) {
+                    
+                        rel_pos = data + array_to_integer (section_64->rel_off, 4, 0);
+                        CHECK_READ (rel_pos, part->reloc_cnt * sizeof (*reloc_info));
+                        
+                        part->reloc_arr = xmalloc (part->reloc_cnt * sizeof (*part->reloc_arr));
+                        
+                        for (k = 0; k < part->reloc_cnt; k++) {
+                        
+                            reloc_info = (struct macho_relocation_info *) (rel_pos + sizeof (*reloc_info) * k);
+                            translate_relocation (part->reloc_arr + k, reloc_info, part);
+                        
+                        }
+                    
+                    }
+                
+                }
+            
+            }
+        
+        }
+        
+        pos += array_to_integer (load_command->command_size, 4, 0);
+    
+    }
+    
+    free (part_p_array);
+    
+    if (addend_part_reloc.reloc_entry) {
+        report_at (program_name, 0, REPORT_WARNING, "%s: ARM64_RELOC_ADDEND without following relocation", filename);
+    }
+
+}
+
+#define     section_in_text_seg(section)                    \
+    ((section)->flags & SECTION_FLAG_CODE)
+
+#define     section_in_data_seg(section)                    \
+    (!((section)->flags & SECTION_FLAG_CODE))
+
+#define     PAGE_SIZE                   0x4000
+
+static struct section *first_data_section;
+static uint64_t first_data_section_alignment;
+
+void macho_before_link (void) {
+
+    struct section *section, *next_section;
+    
+    struct section *text_sections = 0;
+    struct section **last_text_section_p = &text_sections;
+    
+    struct section *data_sections = 0;
+    struct section **last_data_section_p = &data_sections;
+    
+    struct section *bss_sections = 0;
+    struct section **last_bss_section_p = &bss_sections;
+    
+    for (section = all_sections; section; section = next_section) {
+    
+        next_section = section->next;
+        section->next = 0;
+        
+        if (section_in_text_seg (section)) {
+        
+            *last_text_section_p = section;
+            last_text_section_p = &section->next;
+        
+        } else if (!section->is_bss) {
+        
+            *last_data_section_p = section;
+            last_data_section_p = &section->next;
+        
+        } else {
+        
+            *last_bss_section_p = section;
+            last_bss_section_p = &section->next;
+        
+        }
+    
+    }
+    
+    *last_text_section_p = data_sections;
+    
+    if (data_sections) {
+        *last_data_section_p = bss_sections;
+    } else {
+        *last_text_section_p = bss_sections;
+    }
+    
+    all_sections = text_sections;
+    
+    if ((first_data_section = *last_text_section_p)) {
+    
+        first_data_section_alignment = first_data_section->section_alignment;
+        first_data_section->section_alignment = PAGE_SIZE;
+    
+    }
+
+}
+
+uint64_t macho_get_first_section_rva (void) {
+    return PAGE_SIZE;
+}
+
+#define     FLOOR_TO(to_floor, floor)                       \
+    ((to_floor) / (floor) * (floor))
+
+static int part_reloc_compare (const void *a, const void *b) {
+
+    const struct part_reloc *apr, *bpr;
+    uint64_t arva, brva;
+    
+    apr = a;
+    bpr = b;
+    
+    arva = apr->part->rva + apr->reloc_entry->offset;
+    brva = bpr->part->rva + bpr->reloc_entry->offset;
+    
+    if (arva < brva) {
+        return -1;
+    }
+    
+    if (arva > brva) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+#define     NUM_SEGS                    4
+
+struct chained_fixup_starts {
+
+    unsigned short *page_starts;
+    unsigned short page_count;
+
+};
+
+unsigned long minos_version = 0x000B0000LU;                 /* "11.0.0" */
+
+static void calculate_chained_fixups (struct chained_fixup_starts *cfs, int doing_data) {
+
+    struct section *section;
+    struct section_part *part;
+    
+    struct part_reloc *part_rels = 0, *p;
+    unsigned long num_relocs = 0, i;
+    
+    uint64_t seg_rva = 0;
+    
+    if (doing_data) {
+    
+        for (section = all_sections; section; section = section->next) {
+        
+            if (section_in_data_seg (section)) {
+            
+                seg_rva = section->rva;
+                break;
+            
+            }
+        
+        }
+    
+    }
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if ((doing_data && !section_in_data_seg (section)) || (!doing_data && !section_in_text_seg (section))) {
+            continue;
+        }
+        
+        for (part = section->first_part; part; part = part->next) {
+        
+            struct reloc_entry *relocs = part->reloc_arr;
+            
+            for (i = 0; i < part->reloc_cnt; i++) {
+            
+                if (relocs[i].howto != &reloc_howtos[RELOC_TYPE_64]) {
+                    continue;
+                }
+                
+                num_relocs++;
+            
+            }
+        
+        }
+    
+    }
+    
+    if (!num_relocs) {
+        return;
+    }
+    
+    p = part_rels = xmalloc (sizeof (*part_rels) * num_relocs);
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if ((doing_data && !section_in_data_seg (section)) || (!doing_data && !section_in_text_seg (section))) {
+            continue;
+        }
+        
+        for (part = section->first_part; part; part = part->next) {
+        
+            struct reloc_entry *relocs = part->reloc_arr;
+            
+            for (i = 0; i < part->reloc_cnt; i++) {
+            
+                if (relocs[i].howto != &reloc_howtos[RELOC_TYPE_64]) {
+                    continue;
+                }
+                
+                p->reloc_entry = &relocs[i];
+                p->part = part;
+                
+                p++;
+            
+            }
+        
+        }
+    
+    }
+    
+    qsort (part_rels, num_relocs, sizeof (*part_rels), &part_reloc_compare);
+    
+    {
+        uint64_t max_rva = part_rels[num_relocs - 1].part->rva + part_rels[num_relocs - 1].reloc_entry->offset;
+        
+        max_rva = ALIGN (max_rva, PAGE_SIZE);
+        cfs->page_count = (max_rva - seg_rva) / PAGE_SIZE;
+    }
+    
+    cfs->page_starts = xmalloc (sizeof *(cfs->page_starts) * cfs->page_count);
+    
+    for (i = 0, p = part_rels; (i < cfs->page_count) && (p < part_rels + num_relocs); i++) {
+    
+        uint64_t p_rva = p->part->rva + p->reloc_entry->offset;
+        
+        if (seg_rva + (i + 1) * PAGE_SIZE <= p_rva) {
+        
+            cfs->page_starts[i] = DYLD_CHAINED_PTR_START_NONE;
+            continue;
+        
+        }
+        
+        cfs->page_starts[i] = p_rva & (PAGE_SIZE - 1);
+        
+        for (; p < part_rels + num_relocs; p++) {
+        
+            uint64_t result = array_to_integer (p->part->content + p->reloc_entry->offset, 8, 0);
+            result -= state->base_address;
+            
+#if     !defined (NO_LONG_LONG) || ULONG_MAX > 4294967295UL
+
+            result &= 0xfffffffff;
+            
+            if (p + 1 != part_rels + num_relocs) {
+            
+                uint64_t rva1, rva2;
+                
+                rva1 = p->part->rva + p->reloc_entry->offset;
+                rva2 = p[1].part->rva + p[1].reloc_entry->offset;
+                
+                if (FLOOR_TO (rva1, PAGE_SIZE) != FLOOR_TO (rva2, PAGE_SIZE)) {
+                
+                    integer_to_array (result, p->part->content + p->reloc_entry->offset, 8, 0);
+                    
+                    p++;
+                    break;
+                
+                }
+                
+                result |= (((rva2 - rva1) / 4) & 0xfff) << 51;
+            
+            }
+
+#endif
+            
+            integer_to_array (result, p->part->content + p->reloc_entry->offset, 8, 0);
+        
+        }
+    
+    }
+    
+    free (part_rels);
+
+}
+
+static unsigned long num_function_starts_symbols = 0;
+static uint64_t *function_starts_addresses_p = 0;
+
+static void function_starts_symbol_callback (struct symbol *symbol) {
+
+    if (!symbol->part || !(symbol->part->section->flags & SECTION_FLAG_CODE)) {
+        return;
+    }
+    
+    num_function_starts_symbols++;
+
+}
+
+static void function_starts_symbol_callback2 (struct symbol *symbol) {
+
+    if (!symbol->part || !(symbol->part->section->flags & SECTION_FLAG_CODE)) {
+        return;
+    }
+    
+    *function_starts_addresses_p = symbol_get_value_no_base (symbol);
+    function_starts_addresses_p++;
+
+}
+
+static int function_starts_address_compare (const void *a, const void *b) {
+
+    if (*(const uint64_t *) a < *(const uint64_t *) b) {
+        return -1;
+    }
+    
+    if (*(const uint64_t *) a > *(const uint64_t *) b) {
+        return 1;
+    }
+    
+    return 0;
+
+}
+
+#define     PROT_READ                   0x01
+#define     PROT_WRITE                  0x02
+#define     PROT_EXECUTE                0x04
+
+static int log_base2 (uint64_t val) {
+
+    int ret = 0;
+    
+    if (!val) {
+        return ret;
+    }
+    
+    for (val >>= 1; val; val >>= 1) {
+        ret++;
+    }
+    
+    return ret;
+
+}
+
+#define     SDK_VERSION                 0x000D0300LU        /* "13.3.0" */
+
+void macho_write (const char *filename) {
+
+    struct chained_fixup_starts chained_fixups[NUM_SEGS] = { { 0 } };
+    struct macho_header_64 header = { 0 };
+    
+    FILE *fp;
+    
+    unsigned long data_size = 0, content_offset = 0, sizeof_commands = 0, num_commands = 0, file_offset = 0;
+    unsigned char *saved_pos, *content, *data, *pos;
+    
+    struct segment_command_64 segment_cmd = { 0 };
+    struct section *section;
+    struct section_64 section_64 = { 0 };
+    struct symtab_command symtab_cmd = { 0 };
+    
+    struct function_starts function_starts_command = { 0 };
+    struct nlist_64 nlist_64 = { 0 };
+    struct main_command main_command = { 0 };
+    struct dylib_command dylib_command = { 0 };
+    
+    struct version_min version_min = { 0 };
+    struct data_in_code data_in_code = { 0 };
+    struct source_version source_version = { 0 };
+    
+    struct dysymtab_command dysymtab = { 0 };
+    struct dylinker_command dylinker = { 0 };
+    
+    struct dyld_exports_trie dyld_exports_trie = { 0 };
+    struct dyld_chained_fixups_header dyld_chained_fixups_header = { 0 };
+    struct dyld_chained_fixups_command dyld_chained_fixups_command = { 0 };
+    struct dyld_chained_starts_in_image dyld_chained_starts_in_image = { 0 };
+    struct dyld_chained_starts_in_segment dyld_chained_starts_in_segment = { 0 };
+    
+    unsigned long function_starts_size = 0, command_size, num_sects, vm_addr, vm_size;
+    unsigned char *function_starts = 0;
+    
+    struct unixthread_command unixthread = { 0 };
+    struct amd64_thread_state64 thread_state64 = { 0 };
+    
+    uint64_t data_segment_offset = 0, symbol_offset = 0;
+    
+    if (!(fp = fopen (filename, "wb"))) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", filename);
+        return;
+    
+    }
+    
+    data_size += sizeof (header);
+    
+    if (first_data_section) {
+        first_data_section->section_alignment = first_data_section_alignment;
+    }
+    
+    integer_to_array (MH_MAGIC_64, header.magic, 4, 0);
+    integer_to_array (MH_EXECUTE, header.file_type, 4, 0);
+    
+    if (state->format == LD_FORMAT_AMD64_MACHO) {
+    
+        integer_to_array (MH_CPU_TYPE_AMD64, header.cpu_type, 4, 0);
+        integer_to_array (MH_CPU_SUBTYPE_I386_ALL, header.cpu_subtype, 4, 0);
+    
+    } else if (state->format == LD_FORMAT_AARCH64_MACHO) {
+        integer_to_array (MH_CPU_TYPE_ARM64, header.cpu_type, 4, 0);
+    }
+    
+    if (state->format == LD_FORMAT_AARCH64_MACHO) {
+        integer_to_array (MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL | MH_PIE, header.flags, 4, 0);
+    } else {
+        integer_to_array (MH_NOUNDEFS, header.flags, 4, 0);
+    }
+    
+    sizeof_commands += sizeof (segment_cmd);
+    num_commands++;
+    
+    sizeof_commands += sizeof (segment_cmd);
+    num_commands++;
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if (section_in_text_seg (section)) {
+            sizeof_commands += sizeof (section_64);
+        }
+    
+    }
+    
+    sizeof_commands += sizeof (segment_cmd);
+    num_commands++;
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if (section_in_data_seg (section)) {
+            sizeof_commands += sizeof (section_64);
+        }
+    
+    }
+    
+    sizeof_commands += sizeof (segment_cmd);
+    num_commands++;
+    
+    if (state->format == LD_FORMAT_AARCH64_MACHO) {
+    
+        sizeof_commands += sizeof (dyld_chained_fixups_command);
+        num_commands++;
+        
+        sizeof_commands += sizeof (dyld_exports_trie);
+        num_commands++;
+        
+        sizeof_commands += sizeof (dysymtab);
+        num_commands++;
+        
+#define     DYLINKER_STRING             "/usr/lib/dyld"
+        
+        sizeof_commands += ALIGN (sizeof (dylinker) + sizeof (DYLINKER_STRING), 8);
+        num_commands++;
+        
+        sizeof_commands += sizeof (function_starts_command);
+        num_commands++;
+        
+        sizeof_commands += sizeof (symtab_cmd);
+        num_commands++;
+        
+#define     DYLIB_STRING                "/usr/lib/libSystem.B.dylib"
+        
+        sizeof_commands += ALIGN (sizeof (dylib_command) + sizeof (DYLIB_STRING), 8);
+        num_commands++;
+        
+        sizeof_commands += sizeof (version_min);
+        num_commands++;
+        
+        sizeof_commands += sizeof (source_version);
+        num_commands++;
+        
+        sizeof_commands += sizeof (data_in_code);
+        num_commands++;
+        
+        sizeof_commands += sizeof (main_command);
+        num_commands++;
+    
+    } else if (state->format == LD_FORMAT_AMD64_MACHO) {
+    
+        sizeof_commands += sizeof (symtab_cmd);
+        num_commands++;
+        
+        sizeof_commands += sizeof (unixthread) + sizeof (thread_state64);
+        num_commands++;
+    
+    }
+    
+    integer_to_array (num_commands, header.num_cmds, 4, 0);
+    integer_to_array (sizeof_commands, header.sizeof_cmds, 4, 0);
+    
+    data_size = all_sections ? all_sections->rva : PAGE_SIZE;
+    content_offset = data_size;
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if (section_in_text_seg (section)) {
+        
+            if (!section->is_bss) {
+            
+                data_size  = ALIGN (data_size, section->section_alignment);
+                data_size += section->total_size;
+                
+            }
+            
+        }
+        
+    }
+    
+    data_size = ALIGN (data_size, PAGE_SIZE);
+    
+    for (section = all_sections; section; section = section->next) {
+        
+        if (section_in_data_seg (section)) {
+            
+            if (!section->is_bss) {
+            
+                data_size  = ALIGN (data_size, section->section_alignment);
+                data_size += section->total_size;
+            
+            }
+        
+        }
+    
+    }
+    
+    data_size = ALIGN (data_size, PAGE_SIZE);
+    
+    if (state->format == LD_FORMAT_AARCH64_MACHO) {
+    
+        data_size += sizeof (dyld_chained_fixups_header);
+        data_size += sizeof (dyld_chained_starts_in_image) + (NUM_SEGS - 1) * 4 + 4;
+        
+        calculate_chained_fixups (&chained_fixups[2], 1);
+        
+        if (chained_fixups[2].page_count) {
+        
+            data_size += sizeof (dyld_chained_starts_in_segment);
+            data_size += chained_fixups[2].page_count * 2 - 2;
+            
+            data_size = ALIGN (data_size, 4);
+        
+        }
+        
+        data_size += sizeof (dyld_chained_starts_in_image);
+        data_size += 0x980;
+        
+        num_function_starts_symbols = 0;
+        symbols_for_each_global (&function_starts_symbol_callback);
+        
+        {
+        
+            unsigned char *p;
+            
+            uint64_t *addresses, prev_rva;
+            unsigned long i;
+            
+            addresses = xmalloc (sizeof (*addresses) * num_function_starts_symbols);
+            
+            function_starts_addresses_p = addresses;
+            symbols_for_each_global (&function_starts_symbol_callback2);
+            
+            qsort (addresses, num_function_starts_symbols, sizeof (*addresses), &function_starts_address_compare);
+            function_starts = xmalloc (num_function_starts_symbols * (sizeof (uint64_t) * CHAR_BIT / 7 + 1));
+            
+            prev_rva = 0;
+            p = function_starts;
+            
+            for (i = 0; i < num_function_starts_symbols; i++) {
+            
+                uint64_t diff = addresses[i] - prev_rva;
+                
+                while (diff) {
+                
+                    *p = diff & 0x7f;
+                
+                    if ((diff >>= 7)) {
+                        *p |= 0x80;
+                    }
+                    
+                    p++;
+                
+                }
+                
+                prev_rva = addresses[i];
+            
+            }
+            
+            data_size += function_starts_size = p - function_starts;
+            free (addresses);
+        
+        }
+        
+#define     MH_HEADER_SYMBOL_NAME       "__mh_execute_header"
+        
+        data_size += sizeof (nlist_64);
+        data_size += 4 + sizeof (MH_HEADER_SYMBOL_NAME);
+    
+    }
+    
+    pos = (data = xmalloc (data_size));
+    content = pos + content_offset;
+    
+    memcpy (pos, &header, sizeof (header));
+    pos += sizeof (header);
+    
+    memset (&segment_cmd, 0, sizeof (segment_cmd));
+    
+    integer_to_array (LC_SEGMENT_64, segment_cmd.command, 4, 0);
+    integer_to_array (sizeof (segment_cmd), segment_cmd.command_size, 4, 0);
+    
+    strcpy (segment_cmd.seg_name, "__PAGEZERO");
+    integer_to_array (state->base_address, segment_cmd.vm_size, 8, 0);
+    
+    memcpy (pos, &segment_cmd, sizeof (segment_cmd));
+    pos += sizeof (segment_cmd);
+    
+    vm_addr = 0;
+    vm_size = 0;
+    
+    memset (&segment_cmd, 0, sizeof (segment_cmd));
+    integer_to_array (LC_SEGMENT_64, segment_cmd.command, 4, 0);
+    
+    command_size = sizeof (segment_cmd);
+    num_sects = 0;
+    
+    strcpy (segment_cmd.seg_name, "__TEXT");
+    integer_to_array (state->base_address, segment_cmd.vm_addr, 8, 0);
+    
+    integer_to_array (PROT_READ | PROT_EXECUTE, segment_cmd.max_prot, 4, 0);
+    integer_to_array (PROT_READ | PROT_EXECUTE, segment_cmd.init_prot, 4, 0);
+    
+    saved_pos = pos;
+    pos += sizeof (segment_cmd);
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        memset (&section_64, 0, sizeof (section_64));
+        
+        if (section_in_text_seg (section)) {
+        
+            command_size += sizeof (section_64);
+            num_sects++;
+            
+            memcpy (section_64.sect_name, section->name, 
+                (strlen (section->name) >= sizeof (section_64.sect_name)) ?
+                    sizeof (section_64.seg_name) : strlen (section->name));
+            
+            if (section_64.sect_name[0] == '.') {
+            
+                memmove (section_64.sect_name + 2, section_64.sect_name + 1, sizeof (section_64.sect_name) - 2);
+                section_64.sect_name[0] = section_64.sect_name[1] = '_';
+            
+            }
+            
+            strcpy (section_64.seg_name, "__TEXT");
+            
+            integer_to_array (state->base_address + section->rva, section_64.addr, 8, 0);
+            integer_to_array (section->total_size, section_64.size, 8, 0);
+            integer_to_array (log_base2 (section->section_alignment), section_64.align, 4, 0);
+            
+            if (section->flags & SECTION_FLAG_CODE) {
+                integer_to_array ((0x80000000 | S_REGULAR), section_64.flags, 4, 0);
+            }
+            
+            vm_addr = array_to_integer (segment_cmd.vm_addr, 8, 0);
+            vm_size = state->base_address + section->rva + section->total_size - vm_addr;
+            
+            if (!section->is_bss) {
+            
+                content = data + ALIGN (content - data, section->section_alignment);
+                integer_to_array (content - data, section_64.offset, 4, 0);
+                
+                section_write (section, content);
+                content += section->total_size;
+            
+            }
+            
+            memcpy (pos, &section_64, sizeof (section_64));
+            pos += sizeof (section_64);
+        
+        }
+    
+    }
+    
+    integer_to_array (command_size, segment_cmd.command_size, 4, 0);
+    integer_to_array (num_sects, segment_cmd.num_sects, 4, 0);
+    integer_to_array (ALIGN (vm_size, PAGE_SIZE), segment_cmd.vm_size, 8, 0);
+    
+    content = data + ALIGN (content - data, PAGE_SIZE);
+    
+    file_offset = array_to_integer (segment_cmd.file_off, 8, 0);
+    integer_to_array (content - data - file_offset, segment_cmd.file_size, 8, 0);
+    
+    memcpy (saved_pos, &segment_cmd, sizeof (segment_cmd));
+    
+    vm_addr = 0;
+    vm_size = 0;
+    
+    memset (&segment_cmd, 0, sizeof (segment_cmd));
+    integer_to_array (LC_SEGMENT_64, segment_cmd.command, 4, 0);
+    
+    command_size = sizeof (segment_cmd);
+    num_sects = 0;
+    
+    strcpy (segment_cmd.seg_name, "__DATA");
+    integer_to_array (content - data, segment_cmd.file_off, 8, 0);
+    
+    integer_to_array (PROT_READ | PROT_WRITE, segment_cmd.max_prot, 4, 0);
+    integer_to_array (PROT_READ | PROT_WRITE, segment_cmd.init_prot, 4, 0);
+    
+    saved_pos = pos;
+    pos += sizeof (segment_cmd);
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        memset (&section_64, 0, sizeof (section_64));
+        
+        if (section_in_data_seg (section)) {
+        
+            if (!(vm_addr = array_to_integer (segment_cmd.vm_addr, 8, 0))) {
+            
+                vm_addr = state->base_address + section->rva;
+                integer_to_array (vm_addr, segment_cmd.vm_addr, 8, 0);
+            
+            }
+            
+            command_size += sizeof (section_64);
+            num_sects++;
+            
+            memcpy (section_64.sect_name, section->name, 
+                (strlen (section->name) >= sizeof (section_64.sect_name)) ?
+                    sizeof (section_64.seg_name) : strlen (section->name));
+            
+            if (section_64.sect_name[0] == '.') {
+            
+                memmove (section_64.sect_name + 2, section_64.sect_name + 1, sizeof (section_64.sect_name) - 2);
+                section_64.sect_name[0] = section_64.sect_name[1] = '_';
+            
+            }
+            
+            strcpy (section_64.seg_name, "__DATA");
+            
+            integer_to_array (state->base_address + section->rva, section_64.addr, 8, 0);
+            integer_to_array (section->total_size, section_64.size, 8, 0);
+            integer_to_array (log_base2 (section->section_alignment), section_64.align, 4, 0);
+            
+            if (!(section->flags & SECTION_FLAG_LOAD)) {
+                integer_to_array (S_ZEROFILL, section_64.flags, 4, 0);
+            }
+            
+            vm_size = state->base_address + section->rva + section->total_size - vm_addr;
+            
+            if (!section->is_bss) {
+            
+                content = data + ALIGN (content - data, section->section_alignment);
+                integer_to_array (content - data, section_64.offset, 4, 0);
+                
+                section_write (section, content);
+                content += section->total_size;
+            
+            }
+            
+            memcpy (pos, &section_64, sizeof (section_64));
+            pos += sizeof (section_64);
+        
+        }
+    
+    }
+    
+    integer_to_array (command_size, segment_cmd.command_size, 4, 0);
+    integer_to_array (num_sects, segment_cmd.num_sects, 4, 0);
+    integer_to_array (ALIGN (vm_size, PAGE_SIZE), segment_cmd.vm_size, 8, 0);
+    
+    content = data + ALIGN (content - data, PAGE_SIZE);
+    
+    file_offset = array_to_integer (segment_cmd.file_off, 8, 0);
+    integer_to_array (content - data - file_offset, segment_cmd.file_size, 8, 0);
+    
+    memcpy (saved_pos, &segment_cmd, sizeof (segment_cmd));
+    data_segment_offset = array_to_integer (segment_cmd.file_off, 8, 0);
+    
+    vm_addr = array_to_integer (segment_cmd.vm_addr, 8, 0);
+    vm_size = array_to_integer (segment_cmd.vm_size, 8, 0);
+    
+    memset (&segment_cmd, 0, sizeof (segment_cmd));
+    
+    integer_to_array (LC_SEGMENT_64, segment_cmd.command, 4, 0);
+    integer_to_array (sizeof (segment_cmd), segment_cmd.command_size, 4, 0);
+    
+    strcpy (segment_cmd.seg_name, "__LINKEDIT");
+    
+    vm_addr += vm_size;
+    integer_to_array (vm_addr, segment_cmd.vm_addr, 8, 0);
+    
+    vm_size = data_size - (content - data);
+    integer_to_array (ALIGN (vm_size, PAGE_SIZE), segment_cmd.vm_size, 8, 0);
+    
+    integer_to_array (PROT_READ, segment_cmd.max_prot, 4, 0);
+    integer_to_array (PROT_READ, segment_cmd.init_prot, 4, 0);
+    
+    file_offset = content - data;
+    
+    integer_to_array (file_offset, segment_cmd.file_off, 8, 0);
+    integer_to_array (data_size - file_offset, segment_cmd.file_size, 8, 0);
+        
+    memcpy (pos, &segment_cmd, sizeof (segment_cmd));
+    pos += sizeof (segment_cmd);
+    
+    if (state->format == LD_FORMAT_AARCH64_MACHO) {
+    
+        integer_to_array (LC_DYLD_CHAINED_FIXUPS, dyld_chained_fixups_command.command, 4, 0);
+        integer_to_array (sizeof (dyld_chained_fixups_command), dyld_chained_fixups_command.command_size, 4, 0);
+        integer_to_array (content - data, dyld_chained_fixups_command.data_offset, 4, 0);
+        
+        {
+        
+            unsigned char *saved = content, *starts;
+            
+            memset (&dyld_chained_fixups_header, 0, sizeof (dyld_chained_fixups_header));
+            integer_to_array (DYLD_CHAINED_IMPORT, dyld_chained_fixups_header.imports_format, 4, 0);
+            
+            content += sizeof (dyld_chained_fixups_header);
+            integer_to_array (content - saved, dyld_chained_fixups_header.starts_offset, 4, 0);
+            
+            starts = content;
+            
+            integer_to_array (NUM_SEGS, content, 4, 0);
+            integer_to_array (0, content + 4, 4, 0);
+            integer_to_array (0, content + 8, 4, 0);
+            integer_to_array (0, content + 12, 4, 0);
+            integer_to_array (0, content + 16, 4, 0);
+            integer_to_array (0, content + 20, 4, 0);
+            
+            content += 24;
+            
+            if (chained_fixups[2].page_count) {
+            
+                struct chained_fixup_starts *cfs = &chained_fixups[2];
+                
+                unsigned long count, size;
+                int i;
+                
+                integer_to_array (content - starts, starts + 12, 4, 0);
+                
+                integer_to_array (sizeof (dyld_chained_starts_in_segment), dyld_chained_starts_in_segment.size, 4, 0);
+                integer_to_array (PAGE_SIZE, dyld_chained_starts_in_segment.page_size, 2, 0);
+                integer_to_array (6, dyld_chained_starts_in_segment.pointer_format, 2, 0);
+                integer_to_array (data_segment_offset, dyld_chained_starts_in_segment.segment_offset, 8, 0);
+                integer_to_array (0, dyld_chained_starts_in_segment.max_valid_pointer, 4, 0);
+                integer_to_array (cfs->page_count, dyld_chained_starts_in_segment.page_count, 2, 0);
+                integer_to_array (cfs->page_starts[0], dyld_chained_starts_in_segment.page_start, 2, 0);
+                
+                count = array_to_integer (dyld_chained_starts_in_segment.page_count, 2, 0);
+                
+                size  = array_to_integer (dyld_chained_starts_in_segment.size, 2, 0);
+                size += count * 2 - 2;
+                
+                integer_to_array (ALIGN (size, 4), dyld_chained_starts_in_segment.size, 4, 0);
+                
+                memcpy (content, &dyld_chained_starts_in_segment, sizeof (dyld_chained_starts_in_segment));
+                content += sizeof (dyld_chained_starts_in_segment);
+                
+                for (i = 1; i < cfs->page_count; i++) {
+                
+                    integer_to_array (cfs->page_starts[i], content, 2, 0);
+                    content += 2;
+                
+                }
+                
+                content = data + ALIGN (content - data, 4);
+                free (cfs->page_starts);
+            
+            }
+            
+            integer_to_array (content - saved, dyld_chained_fixups_header.imports_offset, 4, 0);
+            integer_to_array (content - saved, dyld_chained_fixups_header.symbols_offset, 4, 0);
+            
+            integer_to_array (0, content, 4, 0);
+            content += 4;
+            
+            integer_to_array (0, content, 4, 0);
+            content += 4;
+            
+            memcpy (saved, &dyld_chained_fixups_header, sizeof (dyld_chained_fixups_header));
+        
+        }
+        
+        integer_to_array (content - data - array_to_integer (dyld_chained_fixups_command.data_offset, 4, 0), dyld_chained_fixups_command.data_size, 4, 0);
+        
+        memcpy (pos, &dyld_chained_fixups_command, sizeof (dyld_chained_fixups_command));
+        pos += array_to_integer (dyld_chained_fixups_command.command_size, 4, 0);
+        
+        integer_to_array (LC_DYLD_EXPORTS_TRIE, dyld_exports_trie.command, 4, 0);
+        integer_to_array (sizeof (dyld_exports_trie), dyld_exports_trie.command_size, 4, 0);
+        integer_to_array (content - data, dyld_exports_trie.data_offset, 4, 0);
+        
+        {
+        
+            unsigned char *saved = content;
+            
+            content[0] = 0;
+            content[1] = 1;
+            content += 2;
+            
+            strcpy ((char *) content, MH_HEADER_SYMBOL_NAME);
+            content += sizeof (MH_HEADER_SYMBOL_NAME);
+            
+            content[0] = content - saved + 1;
+            content[1] = 2;
+            
+            content = saved + 0x980;
+        
+        }
+        
+        integer_to_array (content - data - array_to_integer (dyld_exports_trie.data_offset, 4, 0), dyld_exports_trie.data_size, 4, 0);
+        
+        memcpy (pos, &dyld_exports_trie, sizeof (dyld_exports_trie));
+        pos += array_to_integer (dyld_exports_trie.command_size, 4, 0);
+        
+        integer_to_array (LC_LOAD_DYLINKER, dylinker.command, 4, 0);
+        integer_to_array (ALIGN (sizeof (dylinker) + sizeof (DYLINKER_STRING), 8), dylinker.command_size, 4, 0);
+        integer_to_array (sizeof (dylinker), dylinker.name_offset, 4, 0);
+        
+        memcpy (pos, &dylinker, sizeof (dylinker));
+        
+        strcpy ((char *) pos + sizeof (dylinker), DYLINKER_STRING);
+        pos += array_to_integer (dylinker.command_size, 4, 0);
+        
+        integer_to_array (LC_FUNCTION_STARTS, function_starts_command.command, 4, 0);
+        integer_to_array (sizeof (function_starts_command), function_starts_command.command_size, 4, 0);
+        integer_to_array (content - data, function_starts_command.data_offset, 4, 0);
+        
+        memcpy (content, function_starts, function_starts_size);
+        
+        content += function_starts_size;
+        free (function_starts);
+        
+        integer_to_array (content - data - array_to_integer (function_starts_command.data_offset, 4, 0), function_starts_command.data_size, 4, 0);
+        
+        memcpy (pos, &function_starts_command, sizeof (function_starts_command));
+        pos += array_to_integer (function_starts_command.command_size, 4, 0);
+        
+        integer_to_array (LC_SYMTAB, symtab_cmd.command, 4, 0);
+        integer_to_array (sizeof (symtab_cmd), symtab_cmd.command_size, 4, 0);
+        
+        symbol_offset = content - data;
+        
+        integer_to_array (symbol_offset, symtab_cmd.symbol_offset, 4, 0);
+        integer_to_array (0, symtab_cmd.num_symbols, 4, 0);
+        
+        integer_to_array (4, nlist_64.n_strx, 4, 0);
+        
+        nlist_64.n_type[0] = MACHO_N_SECT | MACHO_N_EXT;
+        nlist_64.n_sect[0] = 1;
+        
+        integer_to_array (0x10, nlist_64.n_desc, 2, 0);
+        integer_to_array (state->base_address, nlist_64.n_value, 8, 0);
+        
+        memcpy (content, &nlist_64, sizeof (nlist_64));
+        content += sizeof (nlist_64);
+        
+        integer_to_array (array_to_integer (symtab_cmd.num_symbols, 4, 0) + 1, symtab_cmd.num_symbols, 4, 0);
+        integer_to_array (content - data, symtab_cmd.string_offset, 4, 0);
+        
+        memset (content, '\0', 4);
+        content += 4;
+        
+        strcpy ((char *) content, MH_HEADER_SYMBOL_NAME);
+        content += sizeof (MH_HEADER_SYMBOL_NAME);
+        
+        integer_to_array (content - data - array_to_integer (symtab_cmd.string_offset, 4, 0), symtab_cmd.string_size, 4, 0);
+        
+        memcpy (pos, &symtab_cmd, sizeof (symtab_cmd));
+        pos += array_to_integer (symtab_cmd.command_size, 4, 0);
+        
+        integer_to_array (LC_DYSYMTAB, dysymtab.cmd, 4, 0);
+        integer_to_array (sizeof (dysymtab), dysymtab.cmdsize, 4, 0);
+        integer_to_array (1, dysymtab.nextdefsym, 4, 0);
+        integer_to_array (1, dysymtab.iundefsym, 4, 0);
+        
+        memcpy (pos, &dysymtab, sizeof (dysymtab));
+        pos += array_to_integer (dysymtab.cmdsize, 4, 0);
+        
+        integer_to_array (LC_VERSION_MIN, version_min.command, 4, 0);
+        integer_to_array (sizeof (version_min), version_min.command_size, 4, 0);
+        integer_to_array (minos_version, version_min.version, 4, 0);
+        integer_to_array (SDK_VERSION, version_min.sdk, 4, 0);
+        
+        memcpy (pos, &version_min, sizeof (version_min));
+        pos += array_to_integer (version_min.command_size, 4, 0);
+        
+        integer_to_array (LC_LOAD_DYLIB, dylib_command.command, 4, 0);
+        integer_to_array (ALIGN (sizeof (dylib_command) + sizeof (DYLIB_STRING), 8), dylib_command.command_size, 4, 0);
+        integer_to_array (sizeof (dylib_command), dylib_command.name_offset, 4, 0);
+        integer_to_array (2, dylib_command.timestamp, 4, 0);
+        integer_to_array (0x05276403, dylib_command.current_version, 4, 0);
+        integer_to_array (0x00010000, dylib_command.compatibility_version, 4, 0);
+        
+        memcpy (pos, &dylib_command, sizeof (dylib_command));
+        strcpy ((char *) pos + sizeof (dylib_command), DYLIB_STRING);
+        
+        pos += array_to_integer (dylib_command.command_size, 4, 0);
+        
+        integer_to_array (LC_SOURCE_VERSION, source_version.command, 4, 0);
+        integer_to_array (sizeof (source_version), source_version.command_size, 4, 0);
+        integer_to_array (0, source_version.version, 8, 0);
+        
+        memcpy (pos, &source_version, sizeof (source_version));
+        pos += array_to_integer (source_version.command_size, 4, 0);
+        
+        integer_to_array (LC_DATA_IN_CODE, data_in_code.command, 4, 0);
+        integer_to_array (sizeof (data_in_code), data_in_code.command_size, 4, 0);
+        integer_to_array (symbol_offset, data_in_code.data_offset, 4, 0);
+        integer_to_array (0, data_in_code.data_size, 4, 0);
+        
+        memcpy (pos, &data_in_code, sizeof (data_in_code));
+        pos += array_to_integer (data_in_code.command_size, 4, 0);
+        
+        integer_to_array (LC_MAIN, main_command.command, 4, 0);
+        integer_to_array (sizeof (main_command), main_command.command_size, 4, 0);
+        integer_to_array (state->entry_point, main_command.entry_offset, 8, 0);
+        integer_to_array (0, main_command.stack_size, 8, 0);
+        
+        memcpy (pos, &main_command, sizeof (main_command));
+        pos += array_to_integer (main_command.command_size, 4, 0);
+    
+    } else if (state->format == LD_FORMAT_AMD64_MACHO) {
+    
+        integer_to_array (LC_SYMTAB, symtab_cmd.command, 4, 0);
+        integer_to_array (sizeof (symtab_cmd), symtab_cmd.command_size, 4, 0);
+        
+        memcpy (pos, &symtab_cmd, sizeof (symtab_cmd));
+        pos += array_to_integer (symtab_cmd.command_size, 4, 0);
+        
+        integer_to_array (LC_UNIXTHREAD, unixthread.command, 4, 0);
+        integer_to_array (sizeof (unixthread) + sizeof (thread_state64), unixthread.command_size, 4, 0);
+        integer_to_array (0x04, unixthread.flavor, 4, 0);
+        integer_to_array (0x2A, unixthread.count, 4, 0);
+        
+        memcpy (pos, &unixthread, sizeof (unixthread));
+        
+        integer_to_array (state->base_address + state->entry_point, thread_state64.rip, 8, 0);
+        memcpy (pos + sizeof (unixthread), &thread_state64, sizeof (thread_state64));
+        
+        pos += array_to_integer (unixthread.command_size, 4, 0);
+    
+    }
+    
+    if (fwrite (data, data_size, 1, fp) != 1) {
+        report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", filename);
+    }
+    
+    free (data);
+    fclose (fp);
+
+}
diff --git a/macho.h b/macho.h
new file mode 100644 (file)
index 0000000..6d5b5bc
--- /dev/null
+++ b/macho.h
@@ -0,0 +1,353 @@
+/******************************************************************************
+ * @file            macho.h
+ *****************************************************************************/
+#ifndef     _MACHO_H
+#define     _MACHO_H
+
+struct macho_header_64 {
+
+    unsigned char magic[4];
+    unsigned char cpu_type[4];
+    unsigned char cpu_subtype[4];
+    unsigned char file_type[4];
+    unsigned char num_cmds[4];
+    unsigned char sizeof_cmds[4];
+    unsigned char flags[4];
+    unsigned char reserved[4];
+
+};
+
+#define     MH_MAGIC_64                 0xFEEDFACF
+
+#define     MH_CPU_TYPE_AMD64           0x01000007
+#define     MH_CPU_TYPE_ARM64           0x0100000C
+
+#define     MH_CPU_SUBTYPE_I386_ALL     3
+
+#define     MH_OBJECT                   1
+#define     MH_EXECUTE                  2
+
+#define     MH_NOUNDEFS                 0x000001
+#define     MH_DYLDLINK                 0x000004
+#define     MH_TWOLEVEL                 0x000080
+#define     MH_PIE                      0x200000
+
+struct load_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+
+};
+
+#define     LC_SEGMENT_64               0x19
+#define     LC_SYMTAB                   0x02
+#define     LC_UNIXTHREAD               0x05
+#define     LC_DYSYMTAB                 0x0B
+#define     LC_LOAD_DYLIB               0x0C
+#define     LC_LOAD_DYLINKER            0x0E
+#define     LC_VERSION_MIN              0x24
+#define     LC_FUNCTION_STARTS          0x26
+#define     LC_DATA_IN_CODE             0x29
+#define     LC_SOURCE_VERSION           0x2A
+
+#define     LC_MAIN                     (0x80000000 | 0x28)
+#define     LC_DYLD_EXPORTS_TRIE        (0x80000000 | 0x33)
+#define     LC_DYLD_CHAINED_FIXUPS      (0x80000000 | 0x34)
+
+struct segment_command_64 {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    char seg_name[16];
+    
+    unsigned char vm_addr[8];
+    unsigned char vm_size[8];
+    unsigned char file_off[8];
+    unsigned char file_size[8];
+    
+    unsigned char max_prot[4];
+    unsigned char init_prot[4];
+    unsigned char num_sects[4];
+    unsigned char flags[4];
+
+};
+
+struct section_64 {
+
+    char sect_name[16];
+    char seg_name[16];
+    
+    unsigned char addr[8];
+    unsigned char size[8];
+    
+    unsigned char offset[4];
+    unsigned char align[4];
+    unsigned char rel_off[4];
+    unsigned char num_reloc[4];
+    
+    unsigned char flags[4];
+    
+    unsigned char reserved1[4];
+    unsigned char reserved2[4];
+    unsigned char reserved3[4];
+
+};
+
+#define     S_REGULAR                   0x0400
+#define     S_ZEROFILL                  0x0001
+
+struct nlist_64 {
+
+    unsigned char n_strx[4];
+    unsigned char n_type[1];
+    unsigned char n_sect[1];
+    unsigned char n_desc[2];
+    unsigned char n_value[8];
+
+};
+
+#define     MACHO_N_STAB                0xE0
+#define     MACHO_N_PEXT                0x10
+
+#define     MACHO_N_TYPE                0x0E
+#define     MACHO_N_EXT                 0x01
+
+#define     MACHO_N_UNDF                0x00
+#define     MACHO_N_SECT                0x0E
+
+struct unixthread_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    unsigned char flavor[4];
+    unsigned char count[4];
+
+};
+
+struct amd64_thread_state64 {
+
+    unsigned char rax[8];
+    unsigned char rbx[8];
+    unsigned char rcx[8];
+    unsigned char rdx[8];
+    unsigned char rdi[8];
+    unsigned char rsi[8];
+    unsigned char rbp[8];
+    unsigned char rsp[8];
+    unsigned char r8[8];
+    unsigned char r9[8];
+    unsigned char r10[8];
+    unsigned char r11[8];
+    unsigned char r12[8];
+    unsigned char r13[8];
+    unsigned char r14[8];
+    unsigned char r15[8];
+    unsigned char rip[8];
+    unsigned char rflags[8];
+    unsigned char cs[8];
+    unsigned char fs[8];
+    unsigned char gs[8];
+
+};
+
+struct macho_relocation_info {
+
+    unsigned char r_address[4];
+    unsigned char r_symbolnum[4];
+
+};
+
+#define     ARM64_RELOC_UNSIGNED                            0
+#define     ARM64_RELOC_SUBTRACTOR                          1
+#define     ARM64_RELOC_BRANCH26                            2
+#define     ARM64_RELOC_PAGE21                              3
+#define     ARM64_RELOC_PAGEOFF12                           4
+#define     ARM64_RELOC_GOT_LOAD_PAGE21                     5
+#define     ARM64_RELOC_GOT_LOAD_PAGEOFF12                  6
+#define     ARM64_RELOC_POINTER_TO_GOT                      7
+#define     ARM64_RELOC_TLVP_LOAD_PAGE21                    8
+#define     ARM64_RELOC_TLVP_LOAD_PAGEOFF12                 9
+#define     ARM64_RELOC_ADDEND                              10
+
+#define     AMD64_RELOC_UNSIGNED        0
+#define     AMD64_RELOC_SIGNED          1
+#define     AMD64_RELOC_BRANCH          2
+#define     AMD64_RELOC_GOT_LOAD        3
+#define     AMD64_RELOC_GOT             4
+#define     AMD64_RELOC_SUBTRACTOR      5
+#define     AMD64_RELOC_SIGNED_1        6
+#define     AMD64_RELOC_SIGNED_2        7
+#define     AMD64_RELOC_SIGNED_4        8
+#define     AMD64_RELOC_TLV             9
+
+struct symtab_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    unsigned char symbol_offset[4];
+    unsigned char num_symbols[4];
+    unsigned char string_offset[4];
+    unsigned char string_size[4];
+
+};
+
+struct dyld_chained_fixups_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    unsigned char data_offset[4];
+    unsigned char data_size[4];
+
+};
+
+struct dyld_exports_trie {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    unsigned char data_offset[4];
+    unsigned char data_size[4];
+
+};
+
+struct dylib_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    unsigned char name_offset[4];
+    unsigned char timestamp[4];
+    unsigned char current_version[4];
+    unsigned char compatibility_version[4];
+
+};
+
+struct function_starts {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    unsigned char data_offset[4];
+    unsigned char data_size[4];
+
+};
+
+struct data_in_code {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    unsigned char data_offset[4];
+    unsigned char data_size[4];
+
+};
+
+struct version_min {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    
+    unsigned char version[4];
+    unsigned char sdk[4];
+
+};
+
+struct dylinker_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    unsigned char name_offset[4];
+
+};
+
+struct main_command {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    unsigned char entry_offset[8];
+    unsigned char stack_size[8];
+
+};
+
+struct source_version {
+
+    unsigned char command[4];
+    unsigned char command_size[4];
+    unsigned char version[8];
+
+};
+
+struct dysymtab_command {
+
+    unsigned char cmd[4];
+    unsigned char cmdsize[4];
+    unsigned char ilocalsym[4];
+    unsigned char nlocalsym[4];
+    unsigned char iextdefsym[4];
+    unsigned char nextdefsym[4];
+    unsigned char iundefsym[4];
+    unsigned char nundefsym[4];
+    unsigned char tocoff[4];
+    unsigned char ntoc[4];
+    unsigned char modtaboff[4];
+    unsigned char nmodtab[4];
+    unsigned char extrefsymoff[4];
+    unsigned char nextrefsyms[4];
+    unsigned char indirectsymoff[4];
+    unsigned char nindirectsyms[4];
+    unsigned char extreloff[4];
+    unsigned char nextrel[4];
+    unsigned char locreloff[4];
+    unsigned char nlocrel[4];
+
+};
+
+struct dyld_chained_fixups_header {
+
+    unsigned char fixups_version[4];
+    unsigned char starts_offset[4];
+    unsigned char imports_offset[4];
+    unsigned char symbols_offset[4];
+    unsigned char imports_counts[4];
+    unsigned char imports_format[4];
+    unsigned char symbols_format[4];
+    unsigned char padding[4];
+
+};
+
+#define     DYLD_CHAINED_IMPORT                             1
+#define     DYLD_CHAINED_IMPORT_ADDEND                      2
+#define     DYLD_CHAINED_IMPORT_ADDEND64                    3
+
+struct dyld_chained_starts_in_image {
+
+    unsigned char seg_count[4];
+    unsigned char seg_into_offset[4][1];
+
+};
+
+struct dyld_chained_starts_in_segment {
+
+    unsigned char size[4];
+    unsigned char page_size[2];
+    unsigned char pointer_format[2];
+    unsigned char segment_offset[8];
+    unsigned char max_valid_pointer[4];
+    unsigned char page_count[2];
+    unsigned char page_start[2];
+
+};
+
+#define     DYLD_CHAINED_PTR_START_NONE                     0xFFFF
+#define     DYLD_CHAINED_PTR_START_MULTI                    0x8000
+#define     DYLD_CHAINED_PTR_START_LAST                     0x8000
+
+void read_macho_object (const char *filename, unsigned char *data, unsigned long data_size);
+
+uint64_t macho_get_first_section_rva (void);
+void macho_before_link (void);
+void macho_write (const char *filename);
+
+#endif      /* _MACHO_H */
diff --git a/map.c b/map.c
index f3ca848ab373d9f000584635db62bde8bf622813..6f605e0c34e86ad703837acdf0daa708615cef50 100644 (file)
--- a/map.c
+++ b/map.c
@@ -1,10 +1,13 @@
 /******************************************************************************
  * @file            map.c
  *****************************************************************************/
+#include    "stdint.h"
+
 #include    <stdio.h>
 #include    <stdlib.h>
 #include    <string.h>
 
+#include    "inttypes.h"
 #include    "ld.h"
 #include    "report.h"
 #include    "section.h"
@@ -75,7 +78,7 @@ void map_write (const char *filename) {
             fprintf (fp, "\n%-16s", "");
         }
         
-        fprintf (fp, "0x%08lx  %12lu\n\n", state->base_address + section->rva, section->total_size);
+        fprintf (fp, "0x%08"PRIx64"  %12"PRIu64"\n\n", state->base_address + section->rva, section->total_size);
         
         for (part = section->first_part; part; part = part->next) {
         
@@ -83,7 +86,7 @@ void map_write (const char *filename) {
                 fprintf (fp, "\n%-16s", "");
             }
             
-            fprintf (fp, "0x%08lx  %12lu %s\n", state->base_address + part->rva, part->content_size, part->of->filename);
+            fprintf (fp, "0x%08"PRIx64"  %12"PRIu64" %s\n", state->base_address + part->rva, part->content_size, part->of->filename);
             
             for (symbol = part->of->symbol_arr; symbol < part->of->symbol_arr + part->of->symbol_cnt; symbol++) {
             
@@ -95,7 +98,7 @@ void map_write (const char *filename) {
                     continue;
                 }
                 
-                fprintf (fp, "%-16s %12lu %10s %s\n", "", symbol_get_value_with_base (symbol), "", symbol->name);
+                fprintf (fp, "%-16s %12"PRIu64" %10s %s\n", "", symbol_get_value_with_base (symbol), "", symbol->name);
             
             }
         
@@ -115,7 +118,7 @@ void map_write (const char *filename) {
             last_section = section;
         }
         
-        fprintf (fp, "Sizeof Module: %lu bytes\n", last_section ? (last_section->rva + last_section->total_size) : 0);
+        fprintf (fp, "Sizeof Module: %"PRIu64" bytes\n", last_section ? (last_section->rva + last_section->total_size) : 0);
     
     }
     
diff --git a/reloc.h b/reloc.h
index 5385dc3fddadf26867b60ad641130fd9efce3ea0..b3c4f4f7015f3e992f5122b33272027a93f4ba29 100644 (file)
--- a/reloc.h
+++ b/reloc.h
@@ -19,6 +19,29 @@ enum {
     RELOC_TYPE_PC8,
     
     RELOC_TYPE_32_NO_BASE,
+    
+    RELOC_TYPE_64_ADD,
+    RELOC_TYPE_32_ADD,
+    RELOC_TYPE_24_ADD,
+    RELOC_TYPE_16_ADD,
+    RELOC_TYPE_8_ADD,
+    
+    RELOC_TYPE_64_SUB,
+    RELOC_TYPE_32_SUB,
+    RELOC_TYPE_24_SUB,
+    RELOC_TYPE_16_SUB,
+    RELOC_TYPE_8_SUB,
+    
+    RELOC_TYPE_AARCH64_ADR_PREL_PG_HI21,
+    RELOC_TYPE_AARCH64_ADD_ABS_LO12_NC,
+    RELOC_TYPE_AARCH64_LDST8_ABS_LO12_NC,
+    RELOC_TYPE_AARCH64_LDST16_ABS_LO12_NC,
+    RELOC_TYPE_AARCH64_LDST32_ABS_LO12_NC,
+    RELOC_TYPE_AARCH64_LDST64_ABS_LO12_NC,
+    RELOC_TYPE_AARCH64_LDST128_ABS_LO12_NC,
+    RELOC_TYPE_AARCH64_JUMP26,
+    RELOC_TYPE_AARCH64_CALL26,
+    
     RELOC_TYPE_END
 
 };
@@ -29,7 +52,11 @@ struct reloc_entry;
 
 struct reloc_howto {
 
-    int size, pc_rel, no_base, final_right_shift;
+    int size;
+    int pc_rel;
+    int no_base;
+    int final_right_shift;
+    
     void (*special_function) (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol);
     
     const char *name;
index 1c4f4698da8755cdab52f0dc1217ba2f1ccbec24..42013264f7df8f591496e9e8c71a5d2bbb2d6a0e 100644 (file)
--- a/section.h
+++ b/section.h
@@ -5,6 +5,7 @@
 #define     _SECTION_H
 
 #include    "reloc.h"
+#include    "stdint.h"
 #include    "symbol.h"
 
 struct object_file {
@@ -29,7 +30,7 @@ struct section_part {
     struct reloc_entry *reloc_arr;
     unsigned long reloc_cnt;
     
-    unsigned long rva;
+    uint64_t rva;
     struct section_part *next;
 
 };
@@ -58,10 +59,10 @@ struct section {
     
     struct subsection *all_subsections;
     
-    unsigned long total_size;
+    uint64_t total_size;
     int is_bss;
     
-    unsigned long rva;
+    uint64_t rva;
     unsigned long section_alignment;
     
     int target_index;
diff --git a/stdint.h b/stdint.h
new file mode 100644 (file)
index 0000000..fc8eb0a
--- /dev/null
+++ b/stdint.h
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * @file            stdint.h
+ *****************************************************************************/
+#ifndef     _STDINT_H_INCLUDED
+#ifndef     _STDINT_H
+#ifndef     _STDINT_H_
+
+#define     _STDINT_H_INCLUDED
+#define     _STDINT_H
+#define     _STDINT_H_
+
+#include    <limits.h>
+
+/* Add all data types (even though we don't use them) as the project seems to fail to build on some systems. */
+typedef     signed char                 int8_t;
+typedef     unsigned char               uint8_t;
+
+typedef     signed short                int16_t;
+typedef     unsigned short              uint16_t;
+
+#if     INT_MAX > 32767
+typedef     signed int                  int32_t;
+typedef     unsigned int                uint32_t;
+#else
+typedef     signed long                 int32_t;
+typedef     unsigned long               uint32_t;
+#endif
+
+#ifndef     _INT64_T
+#define     _INT64_T
+#if     defined (NO_LONG_LONG) || ULONG_MAX > 4294967295UL
+typedef     signed long                 int64_t;
+#else
+typedef     signed long long            int64_t;
+#endif
+#endif      /* _INT64_T */
+
+#ifndef     _UINT64_T
+#define     _UINT64_T
+#if     defined (NO_LONG_LONG) || ULONG_MAX > 4294967295UL
+typedef     unsigned long               uint64_t;
+#else
+typedef     unsigned long long          uint64_t;
+#endif
+#endif      /* _UINT64_T */
+
+#endif      /* _STDINT_H_ */
+#endif      /* _STDINT_H */
+#endif      /* _STDINT_H_INCLUDED */
+
index d6598e9c95e47dda046c55c0640426c0ba1a8c7b..21be523cbf325d8c2c270163b876438d06ce87b0 100644 (file)
--- a/symbol.c
+++ b/symbol.c
@@ -84,7 +84,7 @@ void symbol_remove_from_hashtab (struct symbol *symbol) {
 
 }
 
-unsigned long symbol_get_value_with_base (const struct symbol *symbol) {
+uint64_t symbol_get_value_with_base (const struct symbol *symbol) {
 
     if (symbol->part) {
         return state->base_address + symbol->part->rva + symbol->value;
@@ -94,7 +94,7 @@ unsigned long symbol_get_value_with_base (const struct symbol *symbol) {
 
 }
 
-unsigned long symbol_get_value_no_base (const struct symbol *symbol) {
+uint64_t symbol_get_value_no_base (const struct symbol *symbol) {
 
     if (symbol->part) {
         return symbol->part->rva + symbol->value;
@@ -103,3 +103,24 @@ unsigned long symbol_get_value_no_base (const struct symbol *symbol) {
     return (symbol->value - state->base_address);
 
 }
+
+void symbols_for_each_global (void (*symbol_callback) (struct symbol *)) {
+
+    struct hashtab_entry *entry;
+    int i;
+    
+    for (i = 0; i < symbol_hashtab.capacity; i++) {
+    
+        if (!(entry = &symbol_hashtab.entries[i])) {
+            continue;
+        }
+        
+        if (!entry->key || !entry->value) {
+            continue;
+        }
+        
+        symbol_callback (entry->value);
+    
+    }
+
+}
index a7a3195cf39af5339120e248fd31b4e1bee8401d..11edd985320afcd145f68f01b0dffb2eacf4a509 100644 (file)
--- a/symbol.h
+++ b/symbol.h
@@ -5,14 +5,15 @@
 #define     _SYMBOL_H
 
 #include    "section.h"
+#include    "stdint.h"
 
 struct symbol {
 
     char *name;
     int flags, n_type;
     
-    unsigned long value;
-    unsigned long size;
+    uint64_t value;
+    uint64_t size;
     
     long section_number;                /* 1-based, 0 means undefined, negative numbers have special meaning. */
     int auxiliary;                      /* such symbol should be ignored and is only a filter. */
@@ -32,7 +33,9 @@ int symbol_is_undefined (const struct symbol *symbol);
 void symbol_add_to_hashtab (struct symbol *symbol);
 void symbol_remove_from_hashtab (struct symbol *symbol);
 
-unsigned long symbol_get_value_with_base (const struct symbol *symbol);
-unsigned long symbol_get_value_no_base (const struct symbol *symbol);
+uint64_t symbol_get_value_with_base (const struct symbol *symbol);
+uint64_t symbol_get_value_no_base (const struct symbol *symbol);
+
+void symbols_for_each_global (void (*symbol_callback) (struct symbol *));
 
 #endif      /* _SYMBOL_H */