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.
--- 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 0000000000000000000000000000000000000000..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;
+ }
+}