bug 518641 - fix the "unify" script to properly handle Java .class files. r=bsmedberg
authorTed Mielczarek <ted.mielczarek@gmail.com>
Tue, 29 Sep 2009 08:31:50 -0400
changeset 33292 5642cd4a7e88fdc016fcd95248cc6109b60be1f8
parent 33291 f2fd71991134ee1bffb583f4a8172cf27fccba88
child 33293 07837dc569c632cc49214aad902a0d7857082f0f
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs518641
milestone1.9.3a1pre
bug 518641 - fix the "unify" script to properly handle Java .class files. r=bsmedberg Java .class files and Mach-O fat binaries use the same 4-byte magic number at the start of the file. 'unify' uses the magic number to detect Mach-O binaries, so it chokes on Java .class files. This change makes us use the same heuristic as file(1), which is to check the second 4 bytes in the file. Java class files put a version number there, and Mach-O fat binaries put the number of contained architectures there. Conveniently, Mach defines only 18 architectures, and Java's lowest shipping version number is 43, so there's no overlap in valid values.
build/Makefile.in
build/macosx/universal/unify
build/unify-test.c
build/unifytest.class
build/unifytest.java
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -96,15 +96,52 @@ libs:: bloaturls.txt
 # of bloaturls.txt.  This is for browsers that can't do -f
 # autocycling of URLs.
 libs:: bloatcycle.html
 	$(INSTALL) $< $(DIST)/bin/res
 
 ifeq ($(OS_ARCH),Darwin)
 libs:: $(topsrcdir)/tools/rb/fix-macosx-stack.pl
 	$(INSTALL) $< $(DIST)/bin
+
+# Basic unit tests for some stuff in the unify script
+check::
+# build ppc/i386 binaries, and unify them
+	rm -f unify-test-ppc unify-test-i386 unify-test-universal
+	$(HOST_CC) -arch ppc $(srcdir)/unify-test.c -o unify-test-ppc
+	$(HOST_CC) -arch i386 $(srcdir)/unify-test.c -o unify-test-i386
+	@if ! $(srcdir)/macosx/universal/unify ./unify-test-ppc ./unify-test-i386 \
+          ./unify-test-universal; then \
+          echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to produce a universal binary!"; \
+          false; \
+        fi
+	@if ! test -f ./unify-test-universal; then \
+          echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to produce a universal binary!"; \
+          false; \
+        fi
+	@if ! file -b ./unify-test-universal | head -n1 | grep -q "^Mach-O universal binary"; then \
+          echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to produce a universal binary!"; \
+          false; \
+        fi
+# try unifying two identical Java class files
+	rm -f unifytesta.class unifytestb.class unifytestc.class
+	cp $(srcdir)/unifytest.class ./unifytesta.class
+	cp $(srcdir)/unifytest.class ./unifytestb.class
+	@if ! $(srcdir)/macosx/universal/unify ./unifytesta.class ./unifytestb.class \
+          ./unifytestc.class; then \
+          echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify a Java class file!"; \
+          false; \
+        fi
+	@if ! test -f ./unifytestc.class; then \
+          echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify a Java class file!"; \
+          false; \
+        fi
+	@if ! diff -q ./unifytesta.class ./unifytestc.class; then \
+          echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify a Java class file!"; \
+          false; \
+        fi
 endif
 
 ifeq ($(OS_ARCH),Linux)
 libs:: $(topsrcdir)/tools/rb/fix-linux-stack.pl
 	$(INSTALL) $< $(DIST)/bin
 endif
-endif
+endif # ENABLE_TESTS
--- a/build/macosx/universal/unify
+++ b/build/macosx/universal/unify
@@ -209,16 +209,17 @@ sub readZipCRCs($);
   sub lIsDir($);
   sub lIsExecutable($);
   sub lIsRegularFile($);
   sub lIsSymLink($);
   sub lstat($);
   sub lstatMode($);
   sub lstatType($);
   sub magic($);
+  sub magic2($);
   sub path($);
   sub stat($);
   sub statSize($);
 }
 
 %gConfig = (
   'cmd_lipo' => 'lipo',
   'cmd_rm'   => 'rm',
@@ -1048,43 +1049,52 @@ sub readZipCRCs($) {
       $class = $proto;
     }
     $this = {
       'path'        => $path,
       'lstat'       => undef,
       'lstatErrno'  => 0,
       'lstatInit'   => 0,
       'magic'       => undef,
+      'magic2'       => undef,
       'magicErrno'  => 0,
       'magicErrMsg' => undef,
       'magicInit'   => 0,
       'stat'        => undef,
       'statErrno'   => 0,
       'statInit'    => 0,
     };
     bless($this, $class);
     return($this);
   }
 
   # $FileAttrCache->isFat()
   #
   # Returns true if the file is a fat Mach-O file, false if it's not, and
   # undef if an error occurs.  See /usr/include/mach-o/fat.h.
   sub isFat($) {
-    my ($magic, $this);
+    my ($magic, $magic2, $this);
     ($this) = @_;
 
     # magic() caches, there's no separate cache because isFat() doesn't hit
     # the disk other than by calling magic().
 
     if (!defined($magic = $this->magic())) {
       return undef;
     }
+    $magic2 = $this->magic2();
 
-    if ($magic == 0xcafebabe) {
+    # We have to sanity check the second four bytes, because Java class
+    # files use the same magic number as Mach-O fat binaries.
+    # This logic is adapted from file(1), which says that Mach-O uses
+    # these bytes to count the number of architectures within, while
+    # Java uses it for a version number. Conveniently, there are only
+    # 18 labelled Mach-O architectures, and Java's first released
+    # class format used the version 43.0.
+    if ($magic == 0xcafebabe && $magic2 < 20) {
       return 1;
     }
 
     return 0;
   }
 
   # $FileAttrCache->isMachO()
   #
@@ -1100,17 +1110,17 @@ sub readZipCRCs($) {
 
     if (!defined($magic = $this->magic())) {
       return undef;
     }
 
     # Accept Mach-O fat files or Mach-O thin files of either endianness.
     if ($magic == 0xfeedface ||
         $magic == 0xcefaedfe ||
-        $magic == 0xcafebabe) {
+        $this->isFat()) {
       return 1;
     }
 
     return 0;
   }
 
   # $FileAttrCache->isZip()
   #
@@ -1287,39 +1297,64 @@ sub readZipCRCs($) {
       $$this{'magicErrno'} = $!;
       $$this{'magicErrMsg'} = 'open "'.$$this{'path'}.'": '.$!;
       complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:',
                $$this{'path'});
       return undef;
     }
 
     $! = 0;
-    my ($bytes, $magic);
+    my ($bytes, $magic, $bytes2, $magic2);
     if (!defined($bytes = sysread($fh, $magic, 4))) {
       $$this{'magicErrno'} = $!;
       $$this{'magicErrMsg'} = 'read "'.$$this{'path'}.'": '.$!;
       complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:',
                $$this{'path'});
       close($fh);
       return undef;
     }
+    else {
+      $bytes2 = sysread($fh, $magic2, 4);
+    }
 
     close($fh);
 
     if ($bytes != 4) {
       # The file is too short, didn't read a magic number.  This isn't really
       # an error.  Return an unlikely value.
       $$this{'magic'} = -1;
+      $$this{'magic2'} = -1;
       return -1;
     }
+    if ($bytes2 != 4) {
+      # File is too short to read a second 4 bytes.
+      $magic2 = -1;
+    }
 
     $$this{'magic'} = unpack('N', $magic);
+    $$this{'magic2'} = unpack('N', $magic2);
     return $$this{'magic'};
   }
 
+  # $FileAttrCache->magic2()
+  #
+  # Returns the second four bytes of the file as a 32-bit little endian number.
+  # See magic(), above for more info.
+  sub magic2($) {
+    my ($this);
+    ($this) = @_;
+
+    # we do the actual work (and cache it) in magic().
+    if (!$$this{'magicInit'}) {
+      my $magic = $$this->magic();
+    }
+
+    return $$this{'magic2'};
+  }
+
   # $FileAttrCache->path()
   #
   # Returns the file's pathname.
   sub path($) {
     my ($this);
     ($this) = @_;
     return $$this{'path'};
   }
new file mode 100644
--- /dev/null
+++ b/build/unify-test.c
@@ -0,0 +1,4 @@
+int main(int argc, char** argv)
+{
+  return 0;
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7eca557ad5052ad004722bda39d3dcdf7e03c782
GIT binary patch
literal 263
zc${TUy9&ZU5S%s9Xna<Jg<u&i6l=vsunJlzHddE-%88y3FM^+CC0O_Yew4V0poI%F
zy9_hKK413-fIg}&ER-CS9aIRpq0-8X2$t7g672DDEeTa$X*rD#fs7U+*t23Egwlk%
zxAJ%5EP8vPx4pTEl->@KpgTXtQ79)WF;`-(HWwpLrkj{cu#qJczAB;iE13m585*>Z
s;lGl}FgO5yj;|(f;+^f>KvzBp4s$LQxfFTE$J$60{M#&MH*GGy0h(GaUH||9
new file mode 100644
--- /dev/null
+++ b/build/unifytest.java
@@ -0,0 +1,5 @@
+class unifytest {
+    public static void main(String[] args) {
+        return;
+    }
+}