From e185a5c2b698fce97bb5ef765e96f60710af7ef8 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Wed, 25 Apr 2012 17:42:31 +0200 Subject: [PATCH] Improve package-linux-dynamic.sh to create a quasi-static phantomjs. The script now copies all required shared libraries found via ldd to the deployment folder. Furthermore, a run-script is created that runs the phantomjs binary through the copied build-host's ld-linux.so to ensure all symbols are properly resolved. Last but not least, we now include a very slightly modified brandelf.c from FreeBSD. This version can be compiled on Linux machines and is used to change the ELF ABI of the binary and libs in the deployment folder, such that they all use the old SYSV OS ABI for compatibility. Usage: $ package-linux-dynamic.sh $ scp phantomjs-$version.tar.bz2 yourhost $ ssh yourhost $ tar -xf phantomjs-$version.tar.bz2 $ ./phantomjs-$version/phantomjs.sh CC ISSUE: 413 (http://code.google.com/p/phantomjs/issues/detail?id=413) (proper static build would still be desirable eventually if possible) --- deploy/brandelf.c | 213 ++++++++++++++++++++++++++++++++ deploy/package-linux-dynamic.sh | 96 ++++++++++++-- 2 files changed, 300 insertions(+), 9 deletions(-) create mode 100644 deploy/brandelf.c diff --git a/deploy/brandelf.c b/deploy/brandelf.c new file mode 100644 index 00000000..75be5071 --- /dev/null +++ b/deploy/brandelf.c @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 2000, 2001 David O'Brien + * Copyright (c) 1996 Søren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +//NOTE: commented out to make it compile on linux +// __FBSDID("$FreeBSD: src/usr.bin/brandelf/brandelf.c,v 1.25.22.2 2012/03/16 03:22:37 eadler Exp $"); + +#include +//NOTE: changed path to make it compile on linux +#include +#include +#include +#include +#include +#include +#include +#include + +static int elftype(const char *); +static const char *iselftype(int); +static void printelftypes(void); +static void usage(void); + +struct ELFtypes { + const char *str; + int value; +}; +/* XXX - any more types? */ +static struct ELFtypes elftypes[] = { + { "FreeBSD", ELFOSABI_FREEBSD }, + { "Linux", ELFOSABI_LINUX }, + { "Solaris", ELFOSABI_SOLARIS }, + { "SVR4", ELFOSABI_SYSV } +}; + +int +main(int argc, char **argv) +{ + + const char *strtype = "FreeBSD"; + int type = ELFOSABI_FREEBSD; + int retval = 0; + int ch, change = 0, force = 0, listed = 0; + + while ((ch = getopt(argc, argv, "f:lt:v")) != -1) + switch (ch) { + case 'f': + if (change) + errx(1, "f option incompatible with t option"); + force = 1; + type = atoi(optarg); + if (errno == ERANGE || type < 0 || type > 255) { + warnx("invalid argument to option f: %s", + optarg); + usage(); + } + break; + case 'l': + printelftypes(); + listed = 1; + break; + case 'v': + /* does nothing */ + break; + case 't': + if (force) + errx(1, "t option incompatible with f option"); + change = 1; + strtype = optarg; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (!argc) { + if (listed) + exit(0); + else { + warnx("no file(s) specified"); + usage(); + } + } + + if (!force && (type = elftype(strtype)) == -1) { + warnx("invalid ELF type '%s'", strtype); + printelftypes(); + usage(); + } + + while (argc) { + int fd; + char buffer[EI_NIDENT]; + + if ((fd = open(argv[0], change || force ? O_RDWR : O_RDONLY, 0)) < 0) { + warn("error opening file %s", argv[0]); + retval = 1; + goto fail; + } + if (read(fd, buffer, EI_NIDENT) < EI_NIDENT) { + warnx("file '%s' too short", argv[0]); + retval = 1; + goto fail; + } + if (buffer[0] != ELFMAG0 || buffer[1] != ELFMAG1 || + buffer[2] != ELFMAG2 || buffer[3] != ELFMAG3) { + warnx("file '%s' is not ELF format", argv[0]); + retval = 1; + goto fail; + } + if (!change && !force) { + fprintf(stdout, + "File '%s' is of brand '%s' (%u).\n", + argv[0], iselftype(buffer[EI_OSABI]), + buffer[EI_OSABI]); + if (!iselftype(type)) { + warnx("ELF ABI Brand '%u' is unknown", + type); + printelftypes(); + } + } + else { + buffer[EI_OSABI] = type; + lseek(fd, 0, SEEK_SET); + if (write(fd, buffer, EI_NIDENT) != EI_NIDENT) { + warn("error writing %s %d", argv[0], fd); + retval = 1; + goto fail; + } + } +fail: + close(fd); + argc--; + argv++; + } + + return retval; +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: brandelf [-lv] [-f ELF_ABI_number] [-t string] file ...\n"); + exit(1); +} + +static const char * +iselftype(int etype) +{ + size_t elfwalk; + + for (elfwalk = 0; + elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); + elfwalk++) + if (etype == elftypes[elfwalk].value) + return elftypes[elfwalk].str; + return 0; +} + +static int +elftype(const char *elfstrtype) +{ + size_t elfwalk; + + for (elfwalk = 0; + elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); + elfwalk++) + if (strcasecmp(elfstrtype, elftypes[elfwalk].str) == 0) + return elftypes[elfwalk].value; + return -1; +} + +static void +printelftypes(void) +{ + size_t elfwalk; + + fprintf(stderr, "known ELF types are: "); + for (elfwalk = 0; + elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); + elfwalk++) + fprintf(stderr, "%s(%u) ", elftypes[elfwalk].str, + elftypes[elfwalk].value); + fprintf(stderr, "\n"); +} + diff --git a/deploy/package-linux-dynamic.sh b/deploy/package-linux-dynamic.sh index 1c37c988..51e9a3cc 100755 --- a/deploy/package-linux-dynamic.sh +++ b/deploy/package-linux-dynamic.sh @@ -1,14 +1,92 @@ #!/bin/bash -cd `dirname $0`/.. +# +# usage: just run this script (after having run build.sh) +# and deploy the created tarball to your target machine. +# +# It creates a phantomjs-$version folder and copies the binary, +# example, license etc. together with all shared library dependencies +# to that folder. Furthermore brandelf is used to make the lib +# and binary compatible with older unix/linux machines that don't +# know the new Linux ELF ABI. +# -mkdir phantomjs -cp -r examples ChangeLog LICENSE.BSD README.md bin phantomjs +cd $(dirname $0) -QT_LIB=src/qt/lib -mkdir phantomjs/lib -cp -r $QT_LIB/libQtCore.* $QT_LIB/libQtGui.* $QT_LIB/libQtNetwork.* $QT_LIB/libQtWebKit.* phantomjs/lib +if [[ ! -f ../bin/phantomjs ]]; then + echo "phantomjs was not built yet, please run build.sh first" + exit 1 +fi -chrpath -r \$ORIGIN/../lib phantomjs/bin/phantomjs -tar -czf phantomjs.tar.gz phantomjs -rm -r phantomjs/ +# get version +version=$(../bin/phantomjs --version | sed 's/ /-/' | sed 's/[()]//g') + +echo "creating quasi-static deployable phantomjs $version" + +if [[ ! -f brandelf ]]; then + echo + echo "brandelf executable not found in current dir" + echo -n "compiling it now..." + g++ brandelf.c -o brandelf || exit 1 + echo "done" +fi + +src=.. +dest=phantomjs-$version + +rm -Rf $dest{.tar.bz2,} &> /dev/null +mkdir -p $dest/bin $dest/lib + +echo + +echo -n "copying files..." +cp $src/bin/phantomjs $dest/bin +cp -r $src/{ChangeLog,examples,LICENSE.BSD,README.md} $dest/ +echo "done" + +echo + +echo -n "copying shared libs..." +libld= +for l in $(ldd $dest/bin/phantomjs | egrep -o "/[^ ]+ "); do + if [[ "$l" != "" ]]; then + ll=$(basename $l) + cp $l $dest/lib/$ll + # ensure OS ABI compatibility + ./brandelf -t SVR4 $dest/lib/$ll + if [[ "$l" == *"ld-linux"* ]]; then + libld=$ll + fi + fi +done +echo "done" + +echo + +# strip to reduce file size +echo -n "stripping binary and libs..." +strip -s $dest/lib/* $dest/bin/* +echo "done" + +echo + +echo -n "writing run script..." +# write run scripts +run=$dest/phantomjs.sh +echo '#!/bin/sh' >> $run +echo 'path=$(dirname $(readlink -f $0))' >> $run +echo 'export LD_LIBRARY_PATH=$path/lib' >> $run +echo '$path/lib/'$libld' $path/bin/phantomjs $@' >> $run +chmod +x $run +echo "done" + +echo + +echo -n "creating tarball..." +tar -cjf $dest{.tar.bz2,} +echo "done" + +echo + +echo "you can now deploy $dest or $dest.tar.bz2" +echo "run phantomjs on the target via $dest/phantomjs.sh"