From cdf097bc7c0ccc0c9df80a8c40848e749b516fe9 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Tue, 28 Oct 2025 19:10:13 +0000 Subject: [PATCH] Added ability to create Mach-O executables --- Makefile.unix | 2 +- Makefile.w32 | 2 +- Makefile.wat | 2 +- Makefile.wcd | 2 +- inttypes.h | 24 + ld.c | 244 +++++-- ld.h | 3 + lib.c | 43 +- lib.h | 6 +- link.c | 307 ++++++++- macho.c | 1749 +++++++++++++++++++++++++++++++++++++++++++++++++ macho.h | 353 ++++++++++ map.c | 11 +- reloc.h | 29 +- section.h | 7 +- stdint.h | 50 ++ symbol.c | 25 +- symbol.h | 11 +- 18 files changed, 2797 insertions(+), 73 deletions(-) create mode 100644 inttypes.h create mode 100644 macho.c create mode 100644 macho.h create mode 100644 stdint.h diff --git a/Makefile.unix b/Makefile.unix index 17b1945..02e559f 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -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 diff --git a/Makefile.w32 b/Makefile.w32 index a34c7e2..2068ae7 100644 --- a/Makefile.w32 +++ b/Makefile.w32 @@ -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 diff --git a/Makefile.wat b/Makefile.wat index 5a48e96..a8fed65 100644 --- a/Makefile.wat +++ b/Makefile.wat @@ -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 diff --git a/Makefile.wcd b/Makefile.wcd index c0d53c0..26a85c7 100644 --- a/Makefile.wcd +++ b/Makefile.wcd @@ -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 index 0000000..34d387c --- /dev/null +++ b/inttypes.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * @file inttypes.h + *****************************************************************************/ +#include + +#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 d205a89..1ec61d4 100644 --- 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 82292d0..2d87d8d 100644 --- 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 a91dc5b..855cfed 100644 --- 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 7ee850e..f741eb1 100644 --- 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 d47a9bf..d1885b6 100644 --- a/link.c +++ b/link.c @@ -8,12 +8,274 @@ #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 index 0000000..53e0da4 --- /dev/null +++ b/macho.c @@ -0,0 +1,1749 @@ +/****************************************************************************** + * @file macho.c + *****************************************************************************/ +#include +#include +#include +#include + +#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 = §ion->next; + + } else if (!section->is_bss) { + + *last_data_section_p = section; + last_data_section_p = §ion->next; + + } else { + + *last_bss_section_p = section; + last_bss_section_p = §ion->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 (§ion_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, §ion_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 (§ion_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, §ion_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 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 f3ca848..6f605e0 100644 --- a/map.c +++ b/map.c @@ -1,10 +1,13 @@ /****************************************************************************** * @file map.c *****************************************************************************/ +#include "stdint.h" + #include #include #include +#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 5385dc3..b3c4f4f 100644 --- 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; diff --git a/section.h b/section.h index 1c4f469..4201326 100644 --- 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 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 + +/* 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 */ + diff --git a/symbol.c b/symbol.c index d6598e9..21be523 100644 --- 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); + + } + +} diff --git a/symbol.h b/symbol.h index a7a3195..11edd98 100644 --- 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 */ -- 2.34.1