SpiderMonkey 编译

0x01. Why

道路千万条,编译第一条!

在 Windows 上编译开源软件,总是有各种各样的坑等着去填。

0x02. Steps

2.1 工具

  • Git
  • Mozilla-Build
  • CMake
  • Visual Studio 2017

2.2 步骤

使用 Git 拉取最新的源代码,如果对历史 commit 不感兴趣,可以选择 --depth 1 来加快拉取速度:

git clone --depth 1 https://github.com/mozilla/gecko-dev.git

安装 Mozilla-Build ,这会配置一个基本的 MinGW32 环境;安装完毕之后,打开 Mozilla-Build :

‪C:\mozilla-build\start-shell.bat

切换到 SpiderMonkey 所在的路径,并执行如下命令(参考 Introduction to SpiderMonkey Exploitation ):

autoconf-2.13
mkdir build.asserts
cd build.asserts
../configure --host=x86_64-pc-mingw32 --target=x86_64-pc-mingw32 --enable-debug

提示找不到 C 编译器:

checking for the target C compiler... not found
DEBUG: _cc: Trying clang-cl
DEBUG: _cc: Trying gcc
DEBUG: _cc: Trying clang
ERROR: Cannot find the target C compiler

此时,不要急着去下载安装 LLVM ,因为官方预编译版本都是没有 llvm-config 组件的,照样无法编译:

checking for llvm-config... c:/Users/User\.mozbuild\clang\bin\llvm-config
not found
Traceback (most recent call last):
File "../../../configure.py", line 132, in <module>
sys.exit(main(sys.argv))
File "../../../configure.py", line 38, in main
sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure'))
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 481, in run
func(*args)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 525, in _value_for
return self._value_for_depends(obj)
File "d:\gecko-dev\python\mozbuild\mozbuild\util.py", line 947, in method_call
cache[args] = self.func(instance, *args)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 534, in _value_for_depends
value = obj.result()
File "d:\gecko-dev\python\mozbuild\mozbuild\util.py", line 947, in method_call
cache[args] = self.func(instance, *args)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 151, in result
return self._func(*resolved_args)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/bindgen.configure", line 322, in basic_bindgen_cflags
info = check_compiler([clang_path], 'C++', target)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/toolchain.configure", line 508, in check_compiler
info = get_compiler_info(compiler, language)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/toolchain.configure", line 455, in get_compiler_info
result = try_preprocess(compiler, language, check)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/toolchain.configure", line 371, in try_preprocess
return try_invoke_compiler(compiler, language, source, ['-E'])
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/util.configure", line 246, in try_invoke_compiler
return check_cmd_output(*cmd, **kwargs)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/util.configure", line 69, in check_cmd_output
retcode, stdout, stderr = get_cmd_output(*args, **kwargs)
File "d:\gecko-dev\python\mozbuild\mozbuild\configure\__init__.py", line 1097, in wrapped
return new_func(*args, **kwargs)
File "d:/gecko-dev/build/moz.configure/util.configure", line 46, in get_cmd_output
log.debug('Executing: `%s`', quote(*args))
File "d:\gecko-dev\python\mozbuild\mozbuild\shellutil.py", line 206, in quote
return ' '.join(_quote(s) for s in strings)
File "d:\gecko-dev\python\mozbuild\mozbuild\shellutil.py", line 206, in <genexpr>
return ' '.join(_quote(s) for s in strings)
File "d:\gecko-dev\python\mozbuild\mozbuild\shellutil.py", line 194, in _quote
return t("'%s'") % s.replace(t("'"), t("'\\''"))
TypeError: cannot create 'NoneType' instances

没办法,只能自己编译一个 LLVM (参考 Building and Running Clang ,记得先安装 CMake ):

git clone --depth 1 https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS=clang -G "Visual Studio 15 2017" -A x64 -Thost=x64 ../llvm

然后用 Visual Studio 2017 打开 LLVM.sln 编译 Release 版本。

Build the “clang” project for just the compiler driver and front end, or the “ALL_BUILD” project to build everything, including tools.

现在,可以回到编译 SpiderMonkey 的步骤了,开始下一步之前,先把 LLVM 的路径添加到 PATH 环境变量:

export LLVMDIR=/D/llvm-project/build/Release
export PATH=$PATH:$LLVMDIR/bin

继续配置编译 SpiderMonkey 相关的参数:

../configure --host=x86_64-pc-mingw32 --target=x86_64-pc-mingw32 --enable-debug

提示找不到链接器:

checking for mt... C:/PROGRA~2/WI3CF2~1/10/bin/100177~1.0/x64/mt.exe
checking whether MT is really Microsoft Manifest Tool... yes
checking for linker... not found
DEBUG: linker: Trying lld-link
ERROR: Cannot find linker

通过设置 LINKER 环境变量解决:

export LINKER="/C/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Tools/MSVC/14.15.26726/bin/Hostx64/x64/link.exe"

继续运行 configure ,会提示找不到 host_linker

checking for linker... c:/PROGRA~2/MICROS~3/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/bin/Hostx64/x64/link.exe
checking for host_linker... not found
DEBUG: host_linker: Trying lld-link
ERROR: Cannot find host_linker

通过设置 HOST_LINKER 环境变量解决:

export HOST_LINKER=$LINKER

一切正常,终于可以开始编译了(为了加快编译速度,可以指定 -j 参数):

mozmake -j4

0x03. JavaScript Shell

等 SpiderMonkey 编译完之后,在路径 build.asserts\dist\bin 下可以找到相关的二进制文件,其中 JavaScript Shell 为 js.exe

如果不想折腾编译,也可以直接从 Firefox Nightly Builds 直接下载已经编译好的 JavaScript Shell (这是 Release 版本,功能比 Debug 版本少一些)。

3.1 SpiderMonkey

js.exe 支持参数启动,具体可以参考 js.exe --help

JavaScript Shell 提供了许多的内置函数可供调用,具体可以参考 help() 的执行结果( Debug 版本比 Release 提供了更多的内置函数);几个比较常用的函数:

  1. objectAddress ,打印 JavaScript 对象的内存地址
  2. dumpObject ,打印 JavaScript 对象的结构
  3. dis ,打印函数的字节码
  4. quit ,退出 JavaScript Shell

SpiderMonkey JavaScript Shell

3.2 IonMonkey

SpiderMonkey 的 JavaScript Shell 同时支持设置其 JIT 编译引擎 IonMonkey 的相关参数,可以将环境变量 IONFLAGS 设置为 help 来查看详细的帮助信息:

set IONFLAGS=help

js.exe
found tag: help

usage: IONFLAGS=option,option,option,... where options can be:

aborts Compilation abort messages
scripts Compiled scripts
mir MIR information
prune Prune unused branches
escape Escape analysis
alias Alias analysis
alias-sum Alias analysis: shows summaries for every block
gvn Global Value Numbering
licm Loop invariant code motion
flac Fold linear arithmetic constants
eaa Effective address analysis
sincos Replace sin/cos by sincos
sink Sink transformation
regalloc Register allocation
inline Inlining
snapshots Snapshot information
codegen Native code generation
bailouts Bailouts
caches Inline caches
osi Invalidation
safepoints Safepoints
pools Literal Pools (ARM only for now)
cacheflush Instruction Cache flushes (ARM only for now)
range Range Analysis
logs JSON visualization logging
logs-sync Same as logs, but flushes between each pass (sync. compiled functions only).
profiling Profiling-related information
trackopts Optimization tracking information gathered by the Gecko profiler. (Note: call enableGeckoProfiling() in your script to enable it).
trackopts-ext Encoding information about optimization tracking
dump-mir-expr Dump the MIR expressions
cfg Control flow graph generation
all Everything

bl-aborts Baseline compiler abort messages
bl-scripts Baseline script-compilation
bl-op Baseline compiler detailed op-specific messages
bl-ic Baseline inline-cache messages
bl-ic-fb Baseline IC fallback stub messages
bl-osr Baseline IC OSR messages
bl-bails Baseline bailouts
bl-dbg-osr Baseline debug mode on stack recompile messages
bl-all All baseline spew

See also SPEW=help for information on the Structured Spewer.
请作者喝杯咖啡☕