[
  {
    "path": ".gitignore",
    "content": "build\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 AlexDenisov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "CC=$(shell which clang)\n\nBUILD_DIR=build\nSUBJECT_DIR=subject\n\nSOURCE_FILES=main.c macho_retriever.c macho_reader.c macho_util.c\n\nRETRIEVER_BIN=$(BUILD_DIR)/bitcode_retriever\n\nall: $(BUILD_DIR)\n\t$(CC) $(SOURCE_FILES) -o $(RETRIEVER_BIN) -I/usr/include/libxml2 -lxar -lxml2\n\n### Testing\n\nOBJECT_FILES=i386.o x86_64.o\n\nsubject: $(BUILD_DIR) fat.o\n\t@true\n\nfat.o: $(OBJECT_FILES)\n\tcd $(BUILD_DIR) && lipo -create $^ -output $@\n\n%.o:\n\t$(CC) -fembed-bitcode -c -arch $* $(SUBJECT_DIR)/main.c -o $(BUILD_DIR)/main.o\n\t$(CC) -fembed-bitcode -c -arch $* $(SUBJECT_DIR)/power2.c -o $(BUILD_DIR)/power2.o\n\t$(CC) -fembed-bitcode -arch $* $(BUILD_DIR)/main.o $(BUILD_DIR)/power2.o -o $(BUILD_DIR)/$@\n\n$(BUILD_DIR):\n\tmkdir $@\n\nclean:\n\trm -rf $(BUILD_DIR)\n\n"
  },
  {
    "path": "README.md",
    "content": "## Bitcode Retriever\n\nRetrieves Bitcode from Mach-O binaries\n\n### About\n\n[Bitcode](http://llvm.org/docs/BitCodeFormat.html) stores as an [xar](https://en.wikipedia.org/wiki/Xar_(archiver)) archive inside of a Mach-O binary.\n\nThis tool extracts the archive and puts it near the binary, so it can be easily discovered using `xar` and `llvm-dis`\n\n### Build\n\nClone the repo and run `make`, built binary lays inside of `build` directory\n\n```bash\n$ git clone git@github.com:AlexDenisov/bitcode_retriever.git\n$ cd bitcode_retriever\n$ make\n```\n\n### Usage\n\n_Note: currently is does not work with static libraries, there is an opened issue #1, if you need this feature please a comment there, it will bump prioity of this project at my personal todo-list._\n\nTo use `bitcode_retriever` simple feed him your binary and it'll produce archive with bitcode.\n\nIt accepts both fat and non-fat binaries. For fat binaries it produces separate archive for each slice, e.g.:\n\n```bash\n$ bitcode_retriever fat_app\ni386.xar\nx86_64.xar\narm64.xar\n```\n\nfor non-fat binaries it produces just one archive with the bitcode:\n\n```bash\n$ bitcode_retriever non_fat_app\ni386.xar\n```\n\nTo skip the xar archive and obtain the bitcode immediately, pass the `-e` argument.\n\n```bash\n$ bitcode_retriever -e fat_app\ni386.1.bc\ni386.2.bc\nx86_64.1.bc\nx86_64.2.bc\n```\n\nThe project provides a sample binaries, you can play a bit with them:\n\n```bash\n$ make subject\n$ cd build\n$ ./bitcode_retriever i386.o\n# or\n$ ./bitcode_retriever fat.o\n```\n\nThe xar archive stores set of files with bitcode:\n\n```bash\n$ xar -xf i386.o\n$ ls\ni386.o 1 2\n```\n\nYou can dump LLVM IR from each file (`1`, `2`) using [`llvm-dis`](http://llvm.org/docs/CommandGuide/llvm-dis.html)\n\n```bash\n$ llvm-dis 1\n$ llvm-dis 2\n$ ls\n1 2 1.ll 2.ll\n```\n\n### Bugs and issues\n\nIf you have any problems or found some bug - feel free to open an issue and/or send a pull request\n\n"
  },
  {
    "path": "macho_reader.c",
    "content": "#include \"macho_reader.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <mach-o/loader.h>\n#include <mach-o/fat.h>\n#include <mach-o/swap.h>\n#include <mach/machine.h>\n\nconst static int uint32_size = sizeof(uint32_t);\n\nconst static int fat_header_size = sizeof(struct fat_header);\nconst static int fat_arch_size = sizeof(struct fat_arch);\n\nconst static int mach_header_size = sizeof(struct mach_header);\nconst static int mach_header_64_size = sizeof(struct mach_header_64);\n\nconst static int load_command_size = sizeof(struct load_command);\n\nconst static int segment_command_size = sizeof(struct segment_command);\nconst static int segment_command_64_size = sizeof(struct segment_command_64);\n\nstruct _cpu_type_names {\n  cpu_type_t cputype;\n  const char *cpu_name;\n};\n\nstatic struct _cpu_type_names cpu_type_names[] = {\n  { CPU_TYPE_I386, \"i386\" },\n  { CPU_TYPE_X86_64, \"x86_64\" },\n  { CPU_TYPE_ARM, \"arm\" },\n  { CPU_TYPE_ARM64, \"arm64\" }\n};\n\nint get_cpu_type_count() {\n  return (int)(sizeof(cpu_type_names) / sizeof(cpu_type_names[0]));\n}\n\nstatic const char *cpu_type_name(cpu_type_t cpu_type) {\n  static int cpu_type_names_size = sizeof(cpu_type_names) / sizeof(struct _cpu_type_names);\n  for (int i = 0; i < cpu_type_names_size; i++ ) {\n    if (cpu_type == cpu_type_names[i].cputype) {\n      return cpu_type_names[i].cpu_name;\n    }\n  }\n\n  return \"unknown\";\n}\n\nconst char *get_cpu_type_name(struct mach_header *header) {\n  return cpu_type_name(header->cputype);\n}\n\nconst char *get_cpu_type_name_64(struct mach_header_64 *header) {\n  return cpu_type_name(header->cputype);\n}\n\nuint32_t get_magic(FILE *stream, int offset) {\n  uint32_t magic = 0;\n  fseek(stream, offset, SEEK_SET);\n  fread(&magic, uint32_size, 1, stream);\n  rewind(stream);\n  return magic;\n}\n\nint is_magic_macho(const uint32_t magic) {\n  return magic == MH_MAGIC_64\n      || magic == MH_CIGAM_64\n      || magic == MH_MAGIC\n      || magic == MH_CIGAM\n      || magic == FAT_MAGIC\n      || magic == FAT_CIGAM;\n}\n\nint is_magic_64(const uint32_t magic) {\n  return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;\n}\n\nint is_should_swap_bytes(const uint32_t magic) {\n  return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM;\n}\n\nint is_fat(const uint32_t magic) {\n  return magic == FAT_MAGIC || magic == FAT_CIGAM;\n}\n\nstruct fat_header *load_fat_header(FILE *stream, const int swap_bytes) {\n  struct fat_header *header = malloc(fat_header_size);\n  fread(header, fat_header_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_fat_header(header, 0);\n  }\n\n  return header;\n}\n\nstruct fat_arch *load_fat_arch(FILE *stream, const int offset, const int swap_bytes) {\n  struct fat_arch *arch = malloc(fat_arch_size);\n  fseek(stream, offset, SEEK_SET);\n  fread(arch, fat_arch_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_fat_arch(arch, 1, 0);\n  }\n\n  return arch;\n}\n\nuint32_t offset_for_arch(FILE *stream, const int index, const int swap_bytes) {\n  int offset = fat_header_size + fat_arch_size * index;\n  struct fat_arch *arch = load_fat_arch(stream, offset, swap_bytes);\n  uint32_t arch_offset = arch->offset;\n  free(arch);\n  return arch_offset;\n}\n\nstruct mach_header *load_mach_header(FILE *stream, const int offset, const int swap_bytes) {\n  struct mach_header *header = malloc(mach_header_size);\n  fseek(stream, offset, SEEK_SET);\n  fread(header, mach_header_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_mach_header(header, 0);\n  }\n\n  return header;\n}\n\nstruct mach_header_64 *load_mach_header_64(FILE *stream, const int offset, const int swap_bytes) {\n  struct mach_header_64 *header = malloc(mach_header_64_size);\n  fseek(stream, offset, SEEK_SET);\n  fread(header, mach_header_64_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_mach_header_64(header, 0);\n  }\n\n  return header;\n}\n\nstruct load_command *load_load_command(FILE *stream, const int offset, const int swap_bytes) {\n  struct load_command *command = malloc(load_command_size);\n  fseek(stream, offset, SEEK_SET);\n  fread(command, load_command_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_load_command(command, 0);\n  }\n\n  return command;\n}\n\nstruct segment_command *load_segment_command(FILE *stream, const int offset, const int swap_bytes) {\n  struct segment_command *command = malloc(segment_command_size);\n  fseek(stream, offset, SEEK_SET);\n  fread(command, segment_command_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_segment_command(command, 0);\n  }\n\n  return command;\n}\n\nstruct segment_command_64 *load_segment_command_64(FILE *stream, const int offset, const int swap_bytes) {\n  struct segment_command_64 *command = malloc(segment_command_64_size);\n  fseek(stream, offset, SEEK_SET);\n  fread(command, segment_command_64_size, 1, stream);\n  rewind(stream);\n\n  if (swap_bytes) {\n    swap_segment_command_64(command, 0);\n  }\n\n  return command;\n}\n\nstruct segment_command *load_llvm_segment_command(FILE *stream, struct mach_header *header, const int offset, const int swap_bytes) {\n  int cmd_offset = offset + mach_header_size;\n  for (int i = 0; i < header->ncmds; i++) {\n    struct load_command *cmd = load_load_command(stream, cmd_offset, swap_bytes);\n    if (cmd->cmd == LC_SEGMENT) {\n      struct segment_command *segment = load_segment_command(stream, cmd_offset, swap_bytes);\n      if (!strncmp(\"__LLVM\", segment->segname, 7)) {\n        return segment;\n      }\n      free(segment);\n    }\n    cmd_offset += cmd->cmdsize;\n    free(cmd);\n  }\n\n  return 0;\n}\n\nstruct segment_command_64 *load_llvm_segment_command_64(FILE *stream, struct mach_header_64 *header, const int offset, const int swap_bytes) {\n\n  int cmd_offset = offset + mach_header_64_size;\n  for (int i = 0; i < header->ncmds; i++) {\n    struct load_command *cmd = load_load_command(stream, cmd_offset, swap_bytes);\n    if (cmd->cmd == LC_SEGMENT_64) {\n      struct segment_command_64 *segment = load_segment_command_64(stream, cmd_offset, swap_bytes);\n      if (!strncmp(\"__LLVM\", segment->segname, 7)) {\n        return segment;\n      }\n      free(segment);\n    }\n    cmd_offset += cmd->cmdsize;\n    free(cmd);\n  }\n\n  return 0;\n}\n\n"
  },
  {
    "path": "macho_reader.h",
    "content": "#ifndef MACHO_READER_H\n#define MACHO_READER_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\nstruct mach_header;\nstruct mach_header_64;\nstruct fat_header;\n\nint get_cpu_type_count();\nconst char *get_cpu_type_name(struct mach_header *header);\nconst char *get_cpu_type_name_64(struct mach_header_64 *header);\n\nuint32_t get_magic(FILE *stream, const int offset);\n\nint is_magic_macho(const uint32_t magic);\nint is_magic_64(const uint32_t magic);\nint is_fat(const uint32_t magic);\nint is_should_swap_bytes(const uint32_t magic);\n\nstruct fat_header *load_fat_header(FILE *stream, const int swap_bytes);\nuint32_t offset_for_arch(FILE *stream, const int index, const int swap_bytes);\n\nstruct mach_header *load_mach_header(FILE *stream, const int offset, const int swap_bytes);\nstruct mach_header_64 *load_mach_header_64(FILE *stream, const int offset, const int swap_bytes);\n\nstruct segment_command *load_llvm_segment_command(FILE *stream, struct mach_header *header, const int offset,\n                                                  const int swap_bytes);\nstruct segment_command_64 *load_llvm_segment_command_64(FILE *stream, struct mach_header_64 *header, const int offset,\n                                                        const int swap_bytes);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "macho_retriever.c",
    "content": "#include \"macho_reader.h\"\n#include \"macho_retriever.h\"\n\n#include <mach-o/fat.h>\n#include <mach-o/loader.h>\n\n#include <string.h>\n\nint max_number_of_archives() { return get_cpu_type_count(); }\n\nstruct bitcode_archive *make_bitcode(FILE *stream, const char *cpuname, const uint64_t offset, const uint64_t size) {\n  struct bitcode_archive *bitcode = malloc(sizeof(struct bitcode_archive));\n  bitcode->size = size;\n\n  bitcode->buffer = malloc(sizeof(char) * size);\n  fseek(stream, offset, SEEK_SET);\n  fread(bitcode->buffer, sizeof(char), size, stream);\n\n  bitcode->cpu = cpuname;\n  return bitcode;\n}\n\nstruct bitcode_archive *extract_bitcode(FILE *stream, const int offset, const int swap_bytes) {\n  struct mach_header *header = load_mach_header(stream, offset, swap_bytes);\n  const char *cpu_name = get_cpu_type_name(header);\n  struct segment_command *segment = load_llvm_segment_command(stream, header, offset, swap_bytes);\n  if (!segment) {\n    free(header);\n    return NULL;\n  }\n\n  struct bitcode_archive *bitcode = make_bitcode(stream, cpu_name, offset + segment->fileoff, segment->filesize);\n  free(segment);\n  free(header);\n  return bitcode;\n}\n\nstruct bitcode_archive *extract_bitcode_64(FILE *stream, const int offset, const int swap_bytes) {\n  struct mach_header_64 *header = load_mach_header_64(stream, offset, swap_bytes);\n  const char *cpu_name = get_cpu_type_name_64(header);\n  struct segment_command_64 *segment = load_llvm_segment_command_64(stream, header, offset, swap_bytes);\n  if (!segment) {\n    free(header);\n    return NULL;\n  }\n\n  struct bitcode_archive *bitcode = make_bitcode(stream, cpu_name, offset + segment->fileoff, segment->filesize);\n  free(segment);\n  free(header);\n  return bitcode;\n}\n\nstruct bitcode_archive *retrieve_bitcode_from_nonfat(FILE *stream, const uint32_t offset) {\n  uint32_t magic = get_magic(stream, offset);\n  int is64 = is_magic_64(magic);\n  int swap_bytes = is_should_swap_bytes(magic);\n\n  if (is64) {\n    return extract_bitcode_64(stream, offset, swap_bytes);\n  } else {\n    return extract_bitcode(stream, offset, swap_bytes);\n  }\n}\n\nint is_macho(FILE *stream) {\n  uint32_t magic = get_magic(stream, 0);\n  return is_magic_macho(magic);\n}\n\nvoid retrieve_bitcode(FILE *stream, struct bitcode_archive *bitcodes[], int *count) {\n  uint32_t magic = get_magic(stream, 0);\n  if (is_fat(magic)) {\n    int swap_bytes = is_should_swap_bytes(magic);\n    struct fat_header *header = load_fat_header(stream, swap_bytes);\n    *count = header->nfat_arch;\n    for (int i = 0; i < *count; i++) {\n      uint32_t offset = offset_for_arch(stream, i, swap_bytes);\n      bitcodes[i] = retrieve_bitcode_from_nonfat(stream, offset);\n    }\n    free(header);\n  } else {\n    bitcodes[0] = retrieve_bitcode_from_nonfat(stream, 0);\n    *count = 1;\n  }\n}\n"
  },
  {
    "path": "macho_retriever.h",
    "content": "#ifndef MACHO_RETRIEVER_H\n#define MACHO_RETRIEVER_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdio.h>\n#include <stdlib.h>\n\nstruct bitcode_archive {\n  uint64_t size;\n  char* buffer;\n  const char* cpu;\n};\n\nstruct bitcode_archive* make_bitcode(FILE* stream, const char* cpuname, const uint64_t offset, const uint64_t size);\nstruct bitcode_archive* extract_bitcode(FILE* stream, const int offset, const int swap_bytes);\nstruct bitcode_archive* extract_bitcode_64(FILE* stream, const int offset, const int swap_bytes);\n\nstruct bitcode_archive* retrieve_bitcode_from_nonfat(FILE* stream, const uint32_t offset);\n\nint max_number_of_archives();\nint is_macho(FILE* stream);\n\nvoid retrieve_bitcode(FILE* stream, struct bitcode_archive* bitcodes[], int* count);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "macho_util.c",
    "content": "#include \"macho_reader.h\"\n#include \"macho_retriever.h\"\n#include \"macho_util.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <xar/xar.h>\n\n#include <libxml/parser.h>\n\nchar *fname(const char *name, const char *ext) {\n  const char *delimiter = \".\";\n  int length = strlen(name) + strlen(delimiter) + strlen(ext) + 1;\n  char *filename = calloc(sizeof(char), length);\n  strcpy(filename, name);\n  strcat(filename, delimiter);\n  strcat(filename, ext);\n  return filename;\n}\n\nchar *write_to_xar(struct bitcode_archive *bitcode) {\n  char *filename = fname(bitcode->cpu, \"xar\");\n  FILE *output = fopen(filename, \"wb\");\n\n  if (!output) {\n    fprintf(stderr, \"Cannot open '%s' for writing\\n\", filename);\n    free(filename);\n    return NULL;\n  }\n\n  fwrite(bitcode->buffer, sizeof(char), bitcode->size, output);\n  fclose(output);\n\n  return filename;\n}\n\nint extract_xar(const char *path, const char *cpu, char *files[], int *count) {\n  xar_t x;\n  xar_iter_t xi;\n  xar_file_t xf;\n  xar_stream xs;\n  char buffer[8192];\n\n  x = xar_open(path, READ);\n  if (!x) {\n    fprintf(stderr, \"Error opening archive\\n\");\n    return 1;\n  }\n\n  xi = xar_iter_new();\n  if (!xi) {\n    fprintf(stderr, \"Error creating xar iterator\\n\");\n    return 2;\n  }\n\n  *count = 0;\n  for (xf = xar_file_first(x, xi); xf; xf = xar_file_next(xi)) {\n    char *path = xar_get_path(xf);\n    const char *type;\n    xar_prop_get(xf, \"type\", &type);\n    if (!type) {\n      fprintf(stderr, \"File has no type %s\\n\", path);\n      free(path);\n      continue;\n    }\n    if (strcmp(type, \"file\") != 0) {\n      fprintf(stderr, \"Skipping %s\\n\", path);\n      free(path);\n      continue;\n    }\n\n    if (xar_extract_tostream_init(x, xf, &xs) != XAR_STREAM_OK) {\n      fprintf(stderr, \"Error initializing stream %s\\n\", path);\n      free(path);\n      continue;\n    }\n\n    char *prefix = fname(cpu, path);\n    char *output_path = fname(prefix, \"bc\");\n    free(path);\n    free(prefix);\n\n    FILE *output = fopen(output_path, \"wb\");\n    if (!output) {\n      fprintf(stderr, \"Error opening output file %s\\n\", output_path);\n      free(output_path);\n      continue;\n    }\n\n    xs.avail_out = sizeof(buffer);\n    xs.next_out = buffer;\n\n    int32_t ret;\n    while ((ret = xar_extract_tostream(&xs)) != XAR_STREAM_END) {\n      if (ret == XAR_STREAM_ERR) {\n        fprintf(stderr, \"Error extracting stream %s\\n\", output_path);\n        free(output_path);\n        return 3;\n      }\n\n      fwrite(buffer, sizeof(char), sizeof(buffer) - xs.avail_out, output);\n\n      xs.avail_out = sizeof(buffer);\n      xs.next_out = buffer;\n    }\n\n    if (xar_extract_tostream_end(&xs) != XAR_STREAM_OK) {\n      fprintf(stderr, \"Error ending stream %s\\n\", output_path);\n    }\n\n    fclose(output);\n    files[(*count)++] = output_path;\n  }\n  return 0;\n}\n\nint write_to_bitcode(struct bitcode_archive *bitcode, char *files[], int *count) {\n  char *xar_file = write_to_xar(bitcode);\n  int extracted = extract_xar(xar_file, bitcode->cpu, files, count);\n  if (extracted != 0) {\n    fprintf(stderr, \"Error extracting xar file %s\\n\", xar_file);\n    free(xar_file);\n    return 1;\n  }\n\n  int removed = remove(xar_file);\n  if (removed != 0) {\n    fprintf(stderr, \"Error removing xar file %s\\n\", xar_file);\n    free(xar_file);\n    return 2;\n  }\n\n  free(xar_file);\n  return 0;\n}\n\nint get_options(xmlNode *option_parent, char *options[], int *size) {\n  xmlNode *cur_node = NULL;\n  *size = 0;\n  for (cur_node = option_parent; cur_node; cur_node = cur_node->next) {\n    if (cur_node->type == XML_ELEMENT_NODE && strcmp((const char *)cur_node->name, \"option\") == 0) {\n      char *content = (char *)xmlNodeGetContent(cur_node);\n      options[(*size)++] = content;\n    }\n  }\n  return 0;\n}\n\nint get_linker_options(xmlNode *a_node, char *options[], int *size) {\n  xmlNode *cur_node = NULL;\n  for (cur_node = a_node; cur_node; cur_node = cur_node->next) {\n    if (cur_node->type == XML_ELEMENT_NODE) {\n      if (strcmp((const char *)cur_node->name, \"link-options\") == 0) {\n        return get_options(cur_node->children, options, size);\n      } else {\n        get_linker_options(cur_node->children, options, size);\n      }\n    }\n  }\n  return 1;\n}\n\nint retrieve_toc(const char *xar_path, const char *toc_path) {\n  xar_t x;\n  x = xar_open(xar_path, READ);\n  if (!x) {\n    fprintf(stderr, \"Error opening xar archive %s\\n\", xar_path);\n    return 1;\n  }\n  xar_serialize(x, toc_path);\n  return 0;\n}\n\nint retrieve_linker_options(const char *xar_path, char *options[], int *size) {\n  const char *toc_file = \"toc.temp\";\n  retrieve_toc(xar_path, toc_file);\n\n  xmlDoc *doc = NULL;\n  doc = xmlReadFile(toc_file, NULL, 0);\n  if (doc == NULL) {\n    fprintf(stderr, \"Cannot parse TOC %s\\n\", toc_file);\n    remove(toc_file);\n    return 1;\n  }\n\n  get_linker_options(xmlDocGetRootElement(doc), options, size);\n\n  xmlFreeDoc(doc);\n  xmlCleanupParser();\n\n  remove(toc_file);\n\n  return 0;\n}\n"
  },
  {
    "path": "macho_util.h",
    "content": "#ifndef MACHO_UTIL_H\n#define MACHO_UTIL_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <libxml/tree.h>\n#include \"macho_retriever.h\"\n\nchar *fname(const char *name, const char *ext);\nchar *write_to_xar(struct bitcode_archive *bitcode);\n\nint extract_xar(const char *path, const char *cpu, char *files[], int *count);\nint write_to_bitcode(struct bitcode_archive *bitcode, char *files[], int *count);\n\nint get_options(xmlNode *option_parent, char *options[], int *size);\nint get_linker_options(xmlNode *a_node, char *options[], int *size);\nint retrieve_toc(const char *xar_path, const char *toc_path);\nint retrieve_linker_options(const char *xar_path, char *options[], int *size);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "main.c",
    "content": "#include \"macho_retriever.h\"\n#include \"macho_util.h\"\n\n#include <string.h>\n#include <unistd.h>\n\nint main(int argc, char *argv[]) {\n  int extract = 0;\n  int linker_options = 0;\n\n  int c;\n  while ((c = getopt(argc, argv, \"el\")) != -1) {\n    switch (c) {\n      case 'e':\n        extract = 1;\n        break;\n      case 'l':\n        linker_options = 1;\n        break;\n      default:\n        fprintf(stderr, \"Unknown option `-%c'.\\n\", optopt);\n        exit(1);\n    }\n  }\n\n  if(optind >= argc) {\n    fprintf(stderr, \"No file provided.\\n\");\n  }\n\n  char *filename = argv[optind];\n\n  FILE *stream = fopen(filename, \"rb\");\n\n  struct bitcode_archive *archives[max_number_of_archives()];\n  int archive_count;\n  retrieve_bitcode(stream, archives, &archive_count);\n\n  for (int i = 0; i < archive_count; i++) {\n    if (archives[i]) {\n\n      if (extract) {\n        char *bitcode_files[1024];\n        int bitcode_count;\n        write_to_bitcode(archives[i], bitcode_files, &bitcode_count);\n        for (int j = 0; j < bitcode_count; j++) {\n          printf(\"%s\\n\", bitcode_files[j]);\n          free(bitcode_files[j]);\n        }\n      }\n\n      if(!extract || linker_options) {\n        char *xar_name = write_to_xar(archives[i]);\n        printf(\"%s\\n\", xar_name);\n\n        if (linker_options) {\n          char *options[128];\n          int size = 0;\n          retrieve_linker_options(xar_name, options, &size);\n          printf(\"Linker options: \");\n          for (int i = 0; i < size; i++) {\n            printf(\"%s \", options[i]);\n            free(options[i]);\n          }\n          printf(\"\\n\");\n        }\n\n        free(xar_name);\n      }\n      free(archives[i]->buffer);\n      free(archives[i]);\n    }\n  }\n\n  fclose(stream);\n  return 0;\n}\n"
  },
  {
    "path": "subject/main.c",
    "content": "#include <stdio.h>\n\nextern int power2(int x);\n\nint main() {\n  printf(\"%d\\n\", power2(4));\n  return 0;\n}\n\n"
  },
  {
    "path": "subject/power2.c",
    "content": "int power2(int x) {\n  return x * x;\n}\n\n"
  }
]