Cross-compile V8 for ARM

So you want your own little javascript framework for your favorite ARM device? Us too… OK, here we go.

Getting V8 to compile is not a trivial issue because V8 actually writes its own assembly during operation. Remember, V8 at its core is a JIT compiler for Javascript.

ARM arch? Figure out what you have.

Chances are you are cross-compiling V8 with your target as ARM. If you are, you need to find out what kind of hardware you have; specifically what type of floating point your hardware can do and do you support VFP and VFP3. If you already have Linux up on your hardware, /proc will reveal some good information. Check out the Features line. If you don’t see vfpv3 you don’t want the vfp3 options for V8. And if you don’t see vfp and neon, then you want armeabi=soft (not even softfp, which is using hardware instructions).

root@freescale /proc$ cat /proc/cpuinfo
Processor : ARM926EJ-S rev 5 (v5l)
BogoMIPS : 226.09
Features : swp half thumb fastmult edsp java      NOTE: MISSING: 'vfp'
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 5

Hardware : Freescale MX28EVK board
Revision : 0000
Serial : 0000000000000000

Without Snapshot

First, get V8 cross compiling without snaphost enabled. Snapshots enable faster start of a V8 instance since it will have a pre-compiled set of standard data structures and functions. But its not necessary for functionality.

x86_64: If running uname -f gives you x86_64, your are on 64-bit linux. You need to switch to a 32-bit install to get this to cross compile right now. This is a limitation with V8′s code with tests to make sure you are cross compiling from ia32. I’ll update this if I can figure out how to cross compile on x64.

Here is a cross-compile script I used:

#!/bin/sh
CROSS_BASE=/opt/freescale/usr/local/gcc-4.4.4-glibc-2.11.1-multilib-1.0/arm-fsl-linux-gnueabi
PREFIX_BIN=$CROSS_BASE/bin/arm-fsl-linux-gnueabi
 
export TOOL_PREFIX=$PREFIX_BIN
export CXX=$TOOL_PREFIX-g++
export AR=$TOOL_PREFIX-ar
export RANLIB=$TOOL_PREFIX-ranlib
export CC=$TOOL_PREFIX-gcc
export LD=$TOOL_PREFIX-ld
 
export CCFLAGS="-march=armv5te -mtune=arm926ej-s -mno-thumb-interwork"
# -march=armv5te -mtune=arm926ej-s -mfloat-abi=softfp
 
#export ARM_TARGET_LIB=$CTOOLS_LIB
scons wordsize=32 snapshot=off arch=arm vfp3=off armeabi=soft sample=shell

I did not need to use the ARM_TARGET_LIB var, and frankly could not find anywhere where it was used in the compile. For more information, see Google’s cross-compiling guide for V8. On successful build, you should have an executable called shell. Since we chose not to build V8 as a shared library for now, you can just copy this executable over to the dev hardware and test.

Step by Step

Run the executable…

root@freescale ~$ ./shell
V8 version 3.8.7.1 [sample shell]
> var num=3.3;
> print(num*3.3);
10.889999999999999
> quit();

If it fails, go back turn most options down/off. Then enable them one at time in an effort to find which feature is breaking the execution. Stuff to consider:

  • GCC switches: -march=XXX, -mtune=XXX, -mno-thumb-interwork
  • V8 scons options: vfp3=off/on, armeabi=off/on
  • V8 scons snapshot=off  - Later try to move to snapshot=on

Also, make sure you do a scons –clean (that’s a dash-dash) to cleanup objects when rebuilding.

Errors?

root@freescale ~$ ./shell
Illegal instruction

My first builds didn’t work right as I did not have the floating point switches set correctly. An illegal instruction is likely caused by some assembly that your processor does not support. But let’s use gdb (if your dev hardware has it) to quickly find out.

root@freescal ~$ gdb ./shell
(gdb) layout split
(gdb) r
(gdb) bt
lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
 x x
 x x
 x x
 x [ No Source Available ] x
 x x
 x x
 mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
 >x0x78680 <_ZN2v88internal4HeapC1Ev+216> vldr d7, [pc, #424] ; 0x78830 <_x
 x0x78684 <_ZN2v88internal4HeapC1Ev+220> mov r3, #5242880 ; 0x500000 x
 x0x78688 <_ZN2v88internal4HeapC1Ev+224> add r8, r4, #1392 ; 0x570 x
 x0x7868c <_ZN2v88internal4HeapC1Ev+228> add r8, r8, #4 ; 0x4 x
 x0x78690 <_ZN2v88internal4HeapC1Ev+232> str r3, [r4, #392] x
 x0x78694 <_ZN2v88internal4HeapC1Ev+236> mvn r3, #-2147483648 ; 0xx
 mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
multi-thre Thread 1073867 In: v8::internal::Heap::Heap Line: ?? PC: 0x78680 
#1 0x000b26fc in v8::internal::Isolate::Isolate ()
#2 0x000b2c10 in v8::internal::Isolate::EnsureDefaultIsolate ()
#3 0x000b2c8c in global constructors keyed to _ZN2v88internal8ThreadId18highest
_thread_id_E ()
#4 0x002831c4 in __libc_csu_init ()
#5 0x401bf430 in __libc_start_main () from /lib/libc.so.6
#6 0x0000a254 in _start ()
(gdb)

The top assembly instruction is the culprit. vldr is an ARM assembly instruction for floating point. My hardware does not support this at all, so it needs complete soft-float, which is the armeabi=soft option handed to scons for V8. You can do a similar test if necessary to determine the cause of the crash.

Snapshots

Snapshots will make initial startup of V8′s instance faster. You will notice you can simply add snapshot=on to you scons command. But doing that in your cross-compile script will results in:

...
obj/release/version.o obj/release/zone.o obj/release/snapshot-empty.o -lpthread
obj/release/mksnapshot obj/release/snapshot.cc --logfile "/home/ed/v8/v8-src/obj/release/snapshot.log" --log-snapshot-positions
/bin/bash: obj/release/mksnapshot: cannot execute binary file
scons: *** [obj/release/snapshot.cc] Error 126
scons: building terminated because of errors.

This is because mksnapshot is a ARM binary, and that’s not what we need. Instead you need to build V8 once with an ARM simulator which will generate ARM code, with a x86 binary. Then this will be used to generate the snapshot to build the ARM binary with snapshot enabled.

Below is a full script which does both. This is built off Google’s information located here, but with a few minor changes.

Notice that CCFLAGS now has the -lstdc++ library tacked on. For some reason, during this two stage build, with two different build directories, libstdc++ was not being linked in. Not sure why, and your mileage will likely vary. My result was an error such as:

... undefined reference to `__cxa_pure_virtual'

Which indicates you don’t have the libstdc++ lib. Also, make sure you set the vpf3= and armeabi= options correctly when building the simulator as well, or otherwise your startup will see an Illegal Instruction again. Without snapshot the startup code is compiled when V8 is run on the target, and with snapshots its pre-stored from simulator’s output. So those options handed to scons for the simulator must be essentially the same as when cross-compiling with out snapshots.

The full script:

#!/bin/bash
V8DIR=..
function print_usage() {
    echo "$0 [ with-snapshot ]"
    exit
}
# some ugly option processing...
if [ $# -gt 1 ]; then
    print_usage
fi
if [ $# -gt 0 ]; then
    echo "Here"
    if [ "$1" == "with-snapshot" ]; then
	SNAPSHOT="1"
    else
	print_usage
    fi
fi

if [ ! -z $SNAPSHOT ]; then
    if [ ! -d "host" ]; then
	echo "Making ./host"
	mkdir host
    fi
    if [ ! -d "target" ]; then
	echo "Making ./target"
	mkdir target
    
    fi
    echo "Building simulator..."
    cd host
    scons -Y$V8DIR simulator=arm vfp3=off armeabi=soft snapshot=on
    mv obj/release/snapshot.cc $V8DIR/src/snapshot.cc
    cd ..
fi

CROSS_BASE=/opt/freescale/usr/local/gcc-4.4.4-glibc-2.11.1-multilib-1.0/arm-fsl-linux-gnueabi
PREFIX_BIN=$CROSS_BASE/bin/arm-fsl-linux-gnueabi

CSTOOLS_LIB=/opt/ltib/rootfs/lib
CSTOOLS_USR_LIB=/opt/ltib/rootfs/usr/lib

export TOOL_PREFIX=$PREFIX_BIN
export CXX=$TOOL_PREFIX-g++
export AR=$TOOL_PREFIX-ar
export RANLIB=$TOOL_PREFIX-ranlib
export CC=$TOOL_PREFIX-gcc
export LD=$TOOL_PREFIX-ld

export CCFLAGS="-march=armv5te -mtune=arm926ej-s -mno-thumb-interwork -lstdc++"
# -march=armv5te -mtune=arm926ej-s -mfloat-abi=softfp

export ARM_TARGET_LIB=$CTOOLS_LIB

if [ ! -z $SNAPSHOT ]; then
    cd target
    echo "Building for target..."
    scons -Y$V8DIR wordsize=32 snapshot=nobuild arch=arm vfp3=off armeabi=soft sample=shell
    rm $V8DIR/src/snapshot.cc
    cd ..
else
    scons wordsize=32 snapshot=off arch=arm vfp3=off armeabi=soft sample=shell
fi

Run as

./setup-cross.sh with-snapshot

for building with a snapshot. Your finished executable is in ./target

Related posts:

  • Gibbon1

    I just want to thank you. I’ve been off and on trying to compile google V8 and shell for about a week, and this post put it together. This script built it using the compiler from timesys.

    #!/bin/sh

    export TOOL_PREFIX=/armdev/G20/factory/build_armv5l-timesys-linux-uclibcgnueabi/toolchain/bin/armv5l-timesys-linux-uclibcgnueabi
    export CXX=$TOOL_PREFIX-g++
    export AR=$TOOL_PREFIX-ar
    export RANLIB=$TOOL_PREFIX-ranlib
    export CC=$TOOL_PREFIX-gcc
    export LD=$TOOL_PREFIX-ld

    export CCFLAGS=”-march=armv5te -mtune=arm926ej-s”
    scons wordsize=32 snapshot=off arch=arm vfp3=off armeabi=soft sample=shell

    Couple of issues I had.

    Target is an Atmel (AT91SAM9G20) running Linux. Which means no hardware fpu, so the floating point emulation has to be software only.

    Target libraries are uclibc, which has no support for backtrace. You get execinfo.h not found in platform-linux.cc.

    • Ed

      Great.. thanks for the feedback!

  • http://www.it-wars.com/ Vincent RABAH

    Hi,

    I’m trying to make it, but running into issues : even manually I got a -m32 error ? See logs … Any ideas ?

    root@cross-ia32:~/cross/node-v0.6.11/deps/v8/host# scons -Y$V8DIR simulator=arm vfp3=off armeabi=soft snapshot=on
    scons: Reading SConscript files …

    scons: warning: Ignoring missing SConscript ‘obj/test/release/SConscript’
    File “/root/cross/node-v0.6.11/deps/v8/SConstruct”, line 1475, in BuildSpecific
    scons: done reading SConscript files.
    scons: Building targets …
    /root/arm-2009q1/bin/arm-none-linux-gnueabi-g++ -o obj/release/accessors.o -c -fno-rtti -fno-exceptions -fvisibility=hidden -Wall -W -Wno-unused-parameter -Wnon-virtual-dtor -Wno-abi -pedantic -O3 -fomit-frame-pointer -fdata-sections -ffunction-sections -ansi -m32 -DUSE_EABI_HARDFLOAT=0 -DV8_TARGET_ARCH_ARM -DENABLE_DEBUGGER_SUPPORT -I/root/cross/node-v0.6.11/deps/v8/src /root/cross/node-v0.6.11/deps/v8/src/accessors.cc
    cc1plus: error: unrecognized command line option “-m32″
    scons: *** [obj/release/accessors.o] Error 1
    scons: building terminated because of errors.

    • Ed

      Hi Vincent,
      Sorry to not see your comment earlier. It looks like your toolchain does not support the -m32 option. This was put in recently with the move to GYP build system in V8 (which this post did not cover). I think they did this to force ia32 builds when building the simulator. I can’t find the issue, but it was in the V8 bug tracker list.

      Try overloading the CXXFLAGS… so take the above script, and just add an export CXXFLAGS in with your options. Your build is failing on g++.

      http://code.google.com/p/v8/wiki/CrossCompilingForARM
      http://code.google.com/p/v8/wiki/BuildingWithGYP

  • Jon

    Excellent tutorial here, I’m in a situation here where I want a custom v8 shell in an armv5te environment, with a few extensions, but nothing as extensive as node.js.

    However with the move to GYP I have been unable to find any clear instructions or documentation on using that to build v8 standalone instead of using scons. Any recommendations?