summaryrefslogtreecommitdiffstats
path: root/OSX/enable_gc.c
diff options
context:
space:
mode:
Diffstat (limited to 'OSX/enable_gc.c')
-rw-r--r--OSX/enable_gc.c368
1 files changed, 0 insertions, 368 deletions
diff --git a/OSX/enable_gc.c b/OSX/enable_gc.c
deleted file mode 100644
index 699c9a6..0000000
--- a/OSX/enable_gc.c
+++ /dev/null
@@ -1,368 +0,0 @@
-/* enable_gc.c, Copyright (c) 2014 Dave Odell <dmo2118@gmail.com>
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- *
- * The problem:
- *
- * - OSX 10.5 and earlier require .saver bundles to not use GC.
- * - OSX 10.6 and 10.7 require .saver bundles to use GC.
- * - OSX 10.8 and later require .saver bundles to not use GC.
- *
- * So the way to build a portable .saver is to build it with "GC optional",
- * via "-fobjc-gc" on the x86-64 architecture.
- *
- * But XCode 5.0.2 was the last version of XCode to support building
- * executables that support GC, even optionally. So there's no way to make
- * the XCode that ships with OSX 10.9 create a .saver bundle that will work
- * on OSX 10.6 and 10.7. Though it will work on 10.5!
- *
- * The fix: after compiling, hand-hack the generated binary to tag the
- * x86-64 arch with the OBJC_IMAGE_SUPPORTS_GC flag.
- *
- * Specifically, OR the __DATA,__objc_imageinfo section with
- * "00 00 00 00 02 00 00 00"; normally this section is all zeros.
- * The __objc_imageinfo section corresponds to struct objc_image_info in:
- * http://www.opensource.apple.com/source/objc4/objc4-551.1/runtime/objc-private.h
- * You can use "otool -o Interference.saver/Contents/MacOS/Interference"
- * or "otool -s __DATA __objc_imageinfo Interference" to look at the
- * section.
- *
- * This means that the binary is marked as supporting GC, but there
- * are no actual GC-supporting write barriers compiled in! So does it
- * actually ever GC? Yes, apparently it does. Apparently what's
- * going on is that incremental-GCs are doing nothing, but full-GCs
- * still collect ObjC objects properly.
- *
- * Mad Science!
- *
- * In the xscreensaver build process, the "enable_gc" target is a
- * dependency of "libjwxyz" (so that it gets built first) and is
- * invoked by "update-info-plist.pl" (so that it gets run on every
- * saver).
- *
- *
- * UPDATE, 2-Jun-2014:
- *
- * Actually, this seems not to be working. We're seeing intermittent
- * crashes in malloc/calloc/free on 10.6 64 bit. When compiled with
- * legit -fobjc-gc, those crashes don't occur.
- */
-
-#include <assert.h>
-#include <CoreFoundation/CFByteOrder.h>
-#include <fcntl.h>
-#include <mach-o/fat.h>
-#include <mach-o/loader.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#define BOUNDS_CHECK(ptr, end) \
- ((const void *)((ptr) + 1) <= (const void *)(end))
-
-#define BOUNDS_CHECK_PRINT(ptr, end) \
- (BOUNDS_CHECK(ptr, end) ? 1 : (_got_eof(), 0))
-
-/*
- This part is lifted from objc-private.h, because it's not present on
- most OS X systems.
- http://www.opensource.apple.com/source/objc4/objc4-551.1/runtime/objc-private.h
- */
-
-typedef struct {
- uint32_t version; // currently 0
- uint32_t flags;
-} objc_image_info;
-
-// masks for objc_image_info.flags
-#define OBJC_IMAGE_IS_REPLACEMENT (1<<0)
-#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
-#define OBJC_IMAGE_REQUIRES_GC (1<<2)
-#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
-#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) // might be re-assignable
-
-/* End objc-private.h excerpt. */
-
-static void
-_got_eof()
-{
- fputs("Error: Unexpected EOF\n", stderr);
-}
-
-/* This will probably only ever run on OS X, so CoreFoundation is used here. */
-
-static inline uint32_t
-_be_u32(uint32_t x) /* Big Endian _ Unsigned int 32-bit */
-{
- return (uint32_t)CFSwapInt32BigToHost(x);
-}
-
-static inline uint32_t
-_le_u32(uint32_t x) /* Little Endian _ Unsigned int 32-bit */
-{
- return (uint32_t)CFSwapInt32LittleToHost(x);
-}
-
-static inline uint32_t
-_le_u64(uint64_t x) /* Little Endian _ Unsigned int 64-bit */
-{
- return (uint32_t)CFSwapInt64LittleToHost(x);
-}
-
-static int
-_handle_x86_64(void *exec, void *exec_end)
-{
- const uint32_t *magic = exec;
-
- if(!BOUNDS_CHECK_PRINT(magic, exec_end))
- return EXIT_FAILURE;
-
- if(*magic != _le_u32(MH_MAGIC_64))
- {
- fputs("Error: Unknown magic number on Mach header.\n", stderr);
- return EXIT_FAILURE;
- }
-
- /* Mach headers can be little-endian or big-endian. */
-
- const struct mach_header_64 *hdr = (const struct mach_header_64 *)magic;
- if(!BOUNDS_CHECK_PRINT(hdr, exec_end))
- return EXIT_FAILURE;
-
- if(hdr->cputype != _le_u32(CPU_TYPE_X86_64))
- {
- fputs("Error: Unexpected CPU type on Mach header.\n", stderr);
- return EXIT_FAILURE;
- }
-
- /* I may have missed a few _le_u32 calls, so watch out on PowerPC (heh). */
-
- if((const uint8_t *)hdr + _le_u32(hdr->sizeofcmds) >
- (const uint8_t *)exec_end)
- {
- _got_eof();
- return EXIT_FAILURE;
- }
-
- const struct load_command *load_cmd = (const struct load_command *)(hdr + 1);
- const void *cmds_end = (const uint8_t *)load_cmd + hdr->sizeofcmds;
-
- for(unsigned i = 0; i != _le_u32(hdr->ncmds); ++i)
- {
- if(!BOUNDS_CHECK_PRINT(load_cmd, cmds_end))
- return EXIT_FAILURE;
-
- const struct load_command *next_load_cmd =
- (const struct load_command *)((const uint8_t *)load_cmd +
- _le_u32(load_cmd->cmdsize));
-
- if(load_cmd->cmd == _le_u32(LC_SEGMENT_64))
- {
- const struct segment_command_64 *seg_cmd =
- (const struct segment_command_64 *)load_cmd;
- if(!BOUNDS_CHECK_PRINT(seg_cmd, cmds_end))
- return EXIT_FAILURE;
-
- if(!strcmp(seg_cmd->segname, "__DATA"))
- {
- const struct section_64 *sect =
- (const struct section_64 *)(seg_cmd + 1);
- for(unsigned j = 0; j != _le_u32(seg_cmd->nsects); ++j)
- {
- if(!BOUNDS_CHECK_PRINT(&sect[j], next_load_cmd))
- return EXIT_FAILURE;
-
- if(strcmp(sect[j].segname, "__DATA"))
- fprintf(stderr,
- "Warning: segment name mismatch in __DATA,%.16s\n",
- sect[j].sectname);
-
- if(!memcmp(sect[j].sectname, "__objc_imageinfo", 16))
- { /* No null-terminator here. */
- if(_le_u64(sect[j].size) < sizeof(objc_image_info))
- {
- fputs("__DATA,__objc_imageinfo too small.\n",
- stderr);
- return EXIT_FAILURE;
- }
-
- /*
- Not checked:
- - Overlapping segments.
- - Segments overlapping the load commands.
- */
-
- objc_image_info *img_info = (objc_image_info *)
- ((uint8_t *)exec + _le_u64(sect[j].offset));
-
- if(!BOUNDS_CHECK_PRINT(img_info, exec_end))
- return EXIT_FAILURE;
-
- if(img_info->version != 0)
- {
- fprintf(
- stderr,
- "Error: Unexpected version for "
- "__DATA,__objc_imageinfo section. "
- "Expected 0, got %d\n",
- _le_u32(img_info->version));
- return EXIT_FAILURE;
- }
-
- if(img_info->flags &
- _le_u32(OBJC_IMAGE_REQUIRES_GC |
- OBJC_IMAGE_SUPPORTS_GC))
- {
- fputs("Warning: Image already supports GC.\n",
- stderr);
- return EXIT_SUCCESS;
- }
-
- /* Finally, do the work. */
- img_info->flags |= _le_u32(OBJC_IMAGE_SUPPORTS_GC);
- return EXIT_SUCCESS;
- }
- }
- }
- }
-
- load_cmd = next_load_cmd;
- }
-
- if((const void *)load_cmd > cmds_end)
- {
- _got_eof();
- return EXIT_FAILURE;
- }
-
- assert(load_cmd == cmds_end);
-
- fputs("Error: __DATA,__objc_imageinfo not found.\n", stderr);
- return EXIT_FAILURE;
-}
-
-int
-main(int argc, const char **argv)
-{
- if(argc != 2)
- {
- fprintf(stderr, "Usage: %s executable\n", argv[0]);
- return EXIT_FAILURE;
- }
-
- const char *exec_path = argv[1];
-
- int fd = open(exec_path, O_RDWR | O_EXLOCK);
-
- if(fd < 0)
- {
- perror(exec_path);
- return EXIT_FAILURE;
- }
-
- int result = EXIT_FAILURE;
-
- struct stat exec_stat;
- if(fstat(fd, &exec_stat) < 0)
- {
- perror("fstat");
- exit (1);
- }
- else
- {
- if(!(exec_stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
- {
- fprintf(stderr, "Warning: %s is not executable.\n", exec_path);
- exit (1);
- }
-
- assert(exec_stat.st_size >= 0);
-
- /*
- TODO (technically): mmap(2) can throw signals if somebody unplugs
- the file system. In such situations, a signal handler
- should be used to ensure sensible recovery.
- */
-
- void *exec = NULL;
-
- if(exec_stat.st_size)
- {
- exec = mmap(NULL, exec_stat.st_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if(!exec)
- perror("mmap");
- }
-
- if(exec || !exec_stat.st_size)
- {
- const void *exec_end = (const char *)exec + exec_stat.st_size;
-
- const uint32_t *magic = exec;
-
- if(BOUNDS_CHECK_PRINT(magic, exec_end))
- {
- if(*magic == _be_u32(FAT_MAGIC))
- {
- struct fat_header *hdr = (struct fat_header *)magic;
- if(BOUNDS_CHECK_PRINT(hdr, exec_end))
- {
- uint32_t nfat_arch = _be_u32(hdr->nfat_arch);
- const struct fat_arch *arch =
- (const struct fat_arch *)(hdr + 1);
-
- unsigned i = 0;
- for(;;)
- {
- if(i == nfat_arch)
- {
- /* This could be done for other architectures. */
- fputs("Error: x86_64 architecture not found.\n",
- stderr);
- exit (1);
- break;
- }
-
- if(!BOUNDS_CHECK_PRINT(&arch[i], exec_end))
- break;
-
- if(arch[i].cputype == _be_u32(CPU_TYPE_X86_64))
- {
- uint8_t *obj_begin =
- (uint8_t *)exec + _be_u32(arch[i].offset);
- result = _handle_x86_64(obj_begin,
- obj_begin +
- _be_u32(arch[i].size));
- break;
- }
-
- ++i;
- }
- }
- }
- else
- {
- fprintf(stderr,
- "Error: %s is not a recognized Mach binary format.\n",
- exec_path);
- exit (1);
- }
- }
-
- munmap(exec, exec_stat.st_size);
- }
- }
-
- close(fd);
-
- return result;
-}