From e89d3dee76bc7f2d4b967ce9238f7f22e0a7c147 Mon Sep 17 00:00:00 2001 From: Tristan Gingold Date: Fri, 11 Apr 2014 17:10:03 +0200 Subject: [PATCH] Mach-O: Add objdump -P dyld_info to dump dyld data. include/mach-o/ * loader.h: Add macros for rebase, bind and export constants. binutils/ * od-macho.c (OPT_DYLD_INFO): New macro. (options): Add entry for dyld_info. (mach_o_help): Likewise. (load_and_dump, dump_dyld_info_rebase, dump_dyld_info_bind) (dump_dyld_info_export_1, dump_dyld_info_export): New functions. (bfd_mach_o_dyld_rebase_type_name): New array. (export_info_data): New struct. (dump_dyld_info): Add verbose argument. Dump rebase, bind and exports data. (dump_load_command): Adjust dump_dyld_info call. (mach_o_dump): Handle dyld_info. --- binutils/ChangeLog | 14 ++ binutils/od-macho.c | 359 +++++++++++++++++++++++++++++++++++++++++++++-- include/mach-o/ChangeLog | 4 + include/mach-o/loader.h | 57 ++++++++ 4 files changed, 426 insertions(+), 8 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 02931b7..ddf7293 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,5 +1,19 @@ 2014-04-16 Tristan Gingold + * od-macho.c (OPT_DYLD_INFO): New macro. + (options): Add entry for dyld_info. + (mach_o_help): Likewise. + (load_and_dump, dump_dyld_info_rebase, dump_dyld_info_bind) + (dump_dyld_info_export_1, dump_dyld_info_export): New functions. + (bfd_mach_o_dyld_rebase_type_name): New array. + (export_info_data): New struct. + (dump_dyld_info): Add verbose argument. Dump rebase, bind and + exports data. + (dump_load_command): Adjust dump_dyld_info call. + (mach_o_dump): Handle dyld_info. + +2014-04-16 Tristan Gingold + * od-macho.c (dump_header): Display sizeofcmds in decimal too. (dump_segment): Reformat output. (dump_dyld_info): Also display end offsets. diff --git a/binutils/od-macho.c b/binutils/od-macho.c index 92d6a73..bf90f09 100644 --- a/binutils/od-macho.c +++ b/binutils/od-macho.c @@ -45,6 +45,7 @@ #define OPT_FUNCTION_STARTS 8 #define OPT_DATA_IN_CODE 9 #define OPT_TWOLEVEL_HINTS 10 +#define OPT_DYLD_INFO 11 /* List of actions. */ static struct objdump_private_option options[] = @@ -60,6 +61,7 @@ static struct objdump_private_option options[] = { "function_starts", 0 }, { "data_in_code", 0 }, { "twolevel_hints", 0 }, + { "dyld_info", 0 }, { NULL, 0 } }; @@ -81,6 +83,7 @@ For Mach-O files:\n\ function_starts Display start address of functions\n\ data_in_code Display data in code entries\n\ twolevel_hints Display the two-level namespace lookup hints table\n\ + dyld_info Display dyld information\n\ ")); } @@ -622,8 +625,319 @@ dump_dysymtab (bfd *abfd, bfd_mach_o_load_command *cmd, bfd_boolean verbose) } +static bfd_boolean +load_and_dump (bfd *abfd, ufile_ptr off, unsigned int len, + void (*dump)(bfd *abfd, unsigned char *buf, unsigned int len, + ufile_ptr off)) +{ + unsigned char *buf; + + if (len == 0) + return TRUE; + + buf = xmalloc (len); + + if (bfd_seek (abfd, off, SEEK_SET) == 0 + && bfd_bread (buf, len, abfd) == len) + dump (abfd, buf, len, off); + else + return FALSE; + + free (buf); + return TRUE; +} + +static const bfd_mach_o_xlat_name bfd_mach_o_dyld_rebase_type_name[] = +{ + { "pointer", BFD_MACH_O_REBASE_TYPE_POINTER }, + { "text_abs32", BFD_MACH_O_REBASE_TYPE_TEXT_ABSOLUTE32 }, + { "text_pcrel32", BFD_MACH_O_REBASE_TYPE_TEXT_PCREL32 }, + { NULL, 0 } +}; + +static void +dump_dyld_info_rebase (bfd *abfd, unsigned char *buf, unsigned int len, + ufile_ptr off ATTRIBUTE_UNUSED) +{ + unsigned int i; + bfd_mach_o_data_struct *mdata = bfd_mach_o_get_data (abfd); + unsigned int ptrsize = mdata->header.version == 2 ? 8 : 4; + + for (i = 0; i < len; ) + { + unsigned char b = buf[i++]; + unsigned char imm = b & BFD_MACH_O_REBASE_IMMEDIATE_MASK; + bfd_vma leb; + unsigned int leblen; + + printf (" [0x%04x] 0x%02x: ", i, b); + switch (b & BFD_MACH_O_REBASE_OPCODE_MASK) + { + case BFD_MACH_O_REBASE_OPCODE_DONE: + printf ("done\n"); + return; + case BFD_MACH_O_REBASE_OPCODE_SET_TYPE_IMM: + printf ("set_type %s\n", + bfd_mach_o_get_name (bfd_mach_o_dyld_rebase_type_name, imm)); + break; + case BFD_MACH_O_REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("set segment: %u and offset: 0x%08x\n", + imm, (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_REBASE_OPCODE_ADD_ADDR_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("add addr uleb: 0x%08x\n", (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + printf ("add addr imm scaled: %u\n", imm * ptrsize); + break; + case BFD_MACH_O_REBASE_OPCODE_DO_REBASE_IMM_TIMES: + printf ("rebase imm times: %u\n", imm); + break; + case BFD_MACH_O_REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("rebase uleb times: %u\n", (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("rebase add addr uleb: %u\n", (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("rebase uleb times (%u)", (unsigned) leb); + i += leblen; + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf (" skipping uleb (%u)\n", (unsigned) leb); + i += leblen; + break; + default: + printf ("unknown\n"); + return; + } + } + printf (" rebase commands without end!\n"); +} + +static void +dump_dyld_info_bind (bfd *abfd, unsigned char *buf, unsigned int len, + ufile_ptr off ATTRIBUTE_UNUSED) +{ + unsigned int i; + bfd_mach_o_data_struct *mdata = bfd_mach_o_get_data (abfd); + unsigned int ptrsize = mdata->header.version == 2 ? 8 : 4; + + for (i = 0; i < len; ) + { + unsigned char b = buf[i++]; + unsigned char imm = b & BFD_MACH_O_BIND_IMMEDIATE_MASK; + bfd_vma leb; + unsigned int leblen; + + printf (" [0x%04x] 0x%02x: ", i, b); + switch (b & BFD_MACH_O_BIND_OPCODE_MASK) + { + case BFD_MACH_O_BIND_OPCODE_DONE: + printf ("done\n"); + return; + case BFD_MACH_O_BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + printf ("set dylib ordinal imm: %u\n", imm); + break; + case BFD_MACH_O_BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("set dylib ordinal uleb: %u\n", imm); + i += leblen; + break; + case BFD_MACH_O_BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + imm = (imm != 0) ? imm | BFD_MACH_O_BIND_OPCODE_MASK : imm; + printf ("set dylib special imm: %d\n", imm); + break; + case BFD_MACH_O_BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + printf ("set symbol trailing flags imm: 0x%02x, ", imm); + for (; i < len && buf[i] != 0; i++) + putchar (buf[i] >= ' ' && buf[i] < 0x7f ? buf[i] : '?'); + putchar ('\n'); + i++; + break; + case BFD_MACH_O_BIND_OPCODE_SET_TYPE_IMM: + /* Kludge: use the same table as rebase type. */ + printf ("set_type %s\n", + bfd_mach_o_get_name (bfd_mach_o_dyld_rebase_type_name, imm)); + break; + case BFD_MACH_O_BIND_OPCODE_SET_ADDEND_SLEB: + { + bfd_signed_vma svma; + svma = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("set addend sleb: 0x%08x\n", (unsigned) svma); + i += leblen; + } + break; + case BFD_MACH_O_BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("set segment: %u and offset: 0x%08x\n", + imm, (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_BIND_OPCODE_ADD_ADDR_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("add addr uleb: 0x%08x\n", (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_BIND_OPCODE_DO_BIND: + printf ("do bind\n"); + break; + case BFD_MACH_O_BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("do bind add addr uleb: 0x%08x\n", (unsigned) leb); + i += leblen; + break; + case BFD_MACH_O_BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + printf ("do bind add addr imm scaled: %u\n", imm * ptrsize); + break; + case BFD_MACH_O_BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf ("do bind uleb times (%u)", (unsigned) leb); + i += leblen; + leb = read_unsigned_leb128 (abfd, buf + i, &leblen); + printf (" skipping uleb (%u)\n", (unsigned) leb); + i += leblen; + break; + default: + printf ("unknown\n"); + return; + } + } + printf (" bind commands without end!\n"); +} + +struct export_info_data +{ + const unsigned char *name; + struct export_info_data *next; +}; + static void -dump_dyld_info (bfd *abfd ATTRIBUTE_UNUSED, bfd_mach_o_load_command *cmd) +dump_dyld_info_export_1 (bfd *abfd, unsigned char *buf, unsigned int len, + unsigned int off, struct export_info_data *parent, + struct export_info_data *base) +{ + bfd_vma size; + unsigned int leblen; + unsigned int child_count; + unsigned int i; + + size = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + + if (size != 0) + { + bfd_vma flags; + struct export_info_data *d; + + flags = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + + fputs (" ", stdout); + switch (flags & BFD_MACH_O_EXPORT_SYMBOL_FLAGS_KIND_MASK) + { + case BFD_MACH_O_EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + putchar ('-'); + break; + case BFD_MACH_O_EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + putchar ('T'); + break; + default: + putchar ('?'); + break; + } + putchar ((flags & BFD_MACH_O_EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) ? + 'W' : '-'); + + if (flags & BFD_MACH_O_EXPORT_SYMBOL_FLAGS_REEXPORT) + { + bfd_vma lib; + + lib = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + + fputs (" [reexport] ", stdout); + for (d = base; d != NULL; d = d->next) + printf ("%s", d->name); + + fputs (" (", stdout); + if (buf[off] != 0) + { + fputs ((const char *)buf + off, stdout); + putchar (' '); + off += strlen ((const char *)buf + off); + } + printf ("from dylib %u)\n", (unsigned) lib); + off++; + } + else + { + bfd_vma offset; + bfd_vma resolv = 0; + + offset = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + + if (flags & BFD_MACH_O_EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) + { + resolv = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + } + + printf (" 0x%08x ", (unsigned) offset); + for (d = base; d != NULL; d = d->next) + printf ("%s", d->name); + if (flags & BFD_MACH_O_EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) + printf (" [resolv: 0x%08x]", (unsigned) resolv); + printf ("\n"); + } + } + + child_count = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + + for (i = 0; i < child_count; i++) + { + struct export_info_data sub_data; + bfd_vma sub_off; + + sub_data.name = buf + off; + sub_data.next = NULL; + parent->next = &sub_data; + + off += strlen ((const char *)buf + off) + 1; + + sub_off = read_unsigned_leb128 (abfd, buf + off, &leblen); + off += leblen; + + dump_dyld_info_export_1 (abfd, buf, len, sub_off, &sub_data, base); + } +} + +static void +dump_dyld_info_export (bfd *abfd, unsigned char *buf, unsigned int len, + ufile_ptr off ATTRIBUTE_UNUSED) +{ + struct export_info_data data; + + data.name = (const unsigned char *) ""; + data.next = NULL; + + printf (" fl offset sym (Flags: Tls Weak)\n"); + dump_dyld_info_export_1 (abfd, buf, len, 0, &data, &data); +} + +static void +dump_dyld_info (bfd *abfd, bfd_mach_o_load_command *cmd, + bfd_boolean verbose) { bfd_mach_o_dyld_info_command *info = &cmd->command.dyld_info; @@ -642,6 +956,34 @@ dump_dyld_info (bfd *abfd ATTRIBUTE_UNUSED, bfd_mach_o_load_command *cmd) printf (" export: off: 0x%08x size: %-8u (endoff: 0x%08x)\n", info->export_off, info->export_size, info->export_off + info->export_size); + + if (!verbose) + return; + + printf (" rebase:\n"); + if (!load_and_dump (abfd, info->rebase_off, info->rebase_size, + dump_dyld_info_rebase)) + non_fatal (_("cannot read rebase dyld info")); + + printf (" bind:\n"); + if (!load_and_dump (abfd, info->bind_off, info->bind_size, + dump_dyld_info_bind)) + non_fatal (_("cannot read bind dyld info")); + + printf (" weak bind:\n"); + if (!load_and_dump (abfd, info->weak_bind_off, info->weak_bind_size, + dump_dyld_info_bind)) + non_fatal (_("cannot read weak bind dyld info")); + + printf (" lazy bind:\n"); + if (!load_and_dump (abfd, info->lazy_bind_off, info->lazy_bind_size, + dump_dyld_info_bind)) + non_fatal (_("cannot read lazy bind dyld info")); + + printf (" exported symbols:\n"); + if (!load_and_dump (abfd, info->export_off, info->export_size, + dump_dyld_info_export)) + non_fatal (_("cannot read export symbols dyld info")); } static void @@ -1219,16 +1561,15 @@ dump_load_command (bfd *abfd, bfd_mach_o_load_command *cmd, { bfd_mach_o_encryption_info_command *cryp = &cmd->command.encryption_info; - printf - (" cryptoff: 0x%08x cryptsize: 0x%08x (endoff 0x%08x)" - " cryptid: %u\n", - cryp->cryptoff, cryp->cryptsize, - cryp->cryptoff + cryp->cryptsize, - cryp->cryptid); + printf (" cryptoff: 0x%08x cryptsize: 0x%08x (endoff 0x%08x)" + " cryptid: %u\n", + cryp->cryptoff, cryp->cryptsize, + cryp->cryptoff + cryp->cryptsize, + cryp->cryptid); } break; case BFD_MACH_O_LC_DYLD_INFO: - dump_dyld_info (abfd, cmd); + dump_dyld_info (abfd, cmd, verbose); break; case BFD_MACH_O_LC_VERSION_MIN_MACOSX: case BFD_MACH_O_LC_VERSION_MIN_IPHONEOS: @@ -1791,6 +2132,8 @@ mach_o_dump (bfd *abfd) dump_section_content (abfd, "__TEXT", "__unwind_info", dump_exe_compact_unwind); } + if (options[OPT_DYLD_INFO].selected) + dump_load_commands (abfd, BFD_MACH_O_LC_DYLD_INFO, 0); } /* Vector for Mach-O. */ diff --git a/include/mach-o/ChangeLog b/include/mach-o/ChangeLog index 0f2721a..7adbc49 100644 --- a/include/mach-o/ChangeLog +++ b/include/mach-o/ChangeLog @@ -1,5 +1,9 @@ 2014-04-16 Tristan Gingold + * loader.h: Add macros for rebase, bind and export constants. + +2014-04-16 Tristan Gingold + * loader.h (BFD_MACH_O_CPU_ARCH_MASK, BFD_MACH_O_CPU_ARCH_ABI64) (BFD_MACH_O_CPU_SUBTYPE_MASK, BFD_MACH_O_CPU_SUBTYPE_LIB64): Define. diff --git a/include/mach-o/loader.h b/include/mach-o/loader.h index 20cfe42..ff794a3 100644 --- a/include/mach-o/loader.h +++ b/include/mach-o/loader.h @@ -342,6 +342,63 @@ bfd_mach_o_section_attribute; #define BFD_MACH_O_INDIRECT_SYM_LOCAL 0x80000000 #define BFD_MACH_O_INDIRECT_SYM_ABS 0x40000000 +/* Constants for dyld info rebase. */ +#define BFD_MACH_O_REBASE_OPCODE_MASK 0xf0 +#define BFD_MACH_O_REBASE_IMMEDIATE_MASK 0x0f + +/* The rebase opcodes. */ +#define BFD_MACH_O_REBASE_OPCODE_DONE 0x00 +#define BFD_MACH_O_REBASE_OPCODE_SET_TYPE_IMM 0x10 +#define BFD_MACH_O_REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20 +#define BFD_MACH_O_REBASE_OPCODE_ADD_ADDR_ULEB 0x30 +#define BFD_MACH_O_REBASE_OPCODE_ADD_ADDR_IMM_SCALED 0x40 +#define BFD_MACH_O_REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50 +#define BFD_MACH_O_REBASE_OPCODE_DO_REBASE_ULEB_TIMES 0x60 +#define BFD_MACH_O_REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 0x70 +#define BFD_MACH_O_REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 0x80 + +/* The rebase type. */ +#define BFD_MACH_O_REBASE_TYPE_POINTER 1 +#define BFD_MACH_O_REBASE_TYPE_TEXT_ABSOLUTE32 2 +#define BFD_MACH_O_REBASE_TYPE_TEXT_PCREL32 3 + +/* Constants for dyld info bind. */ +#define BFD_MACH_O_BIND_OPCODE_MASK 0xf0 +#define BFD_MACH_O_BIND_IMMEDIATE_MASK 0x0f + +/* The bind opcodes. */ +#define BFD_MACH_O_BIND_OPCODE_DONE 0x00 +#define BFD_MACH_O_BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10 +#define BFD_MACH_O_BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20 +#define BFD_MACH_O_BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30 +#define BFD_MACH_O_BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40 +#define BFD_MACH_O_BIND_OPCODE_SET_TYPE_IMM 0x50 +#define BFD_MACH_O_BIND_OPCODE_SET_ADDEND_SLEB 0x60 +#define BFD_MACH_O_BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70 +#define BFD_MACH_O_BIND_OPCODE_ADD_ADDR_ULEB 0x80 +#define BFD_MACH_O_BIND_OPCODE_DO_BIND 0x90 +#define BFD_MACH_O_BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xa0 +#define BFD_MACH_O_BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xb0 +#define BFD_MACH_O_BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xc0 + +/* The bind types. */ +#define BFD_MACH_O_BIND_TYPE_POINTER 1 +#define BFD_MACH_O_BIND_TYPE_TEXT_ABSOLUTE32 2 +#define BFD_MACH_O_BIND_TYPE_TEXT_PCREL32 3 + +/* The special dylib. */ +#define BFD_MACH_O_BIND_SPECIAL_DYLIB_SELF 0 +#define BFD_MACH_O_BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE -1 +#define BFD_MACH_O_BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2 + +/* Constants for dyld info export. */ +#define BFD_MACH_O_EXPORT_SYMBOL_FLAGS_KIND_MASK 0x03 +#define BFD_MACH_O_EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00 +#define BFD_MACH_O_EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 0x01 +#define BFD_MACH_O_EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04 +#define BFD_MACH_O_EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#define BFD_MACH_O_EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 + /* Constants for DATA_IN_CODE entries. */ typedef enum bfd_mach_o_data_in_code_entry_kind { -- 2.7.4