Native Client

Compiling Native Client Modules

This chapter describes how to compile a Native Client module (that is, a .nexe file).

Audience

This chapter is intended for developers who have experience writing, compiling, and linking C and C++ code.

If you haven't read the Native Client Technical Overview and Tutorial, we recommend starting with those.

Available tools

The Native Client SDK includes modified versions of the standard GNU toolchain: the gcc compilers (currently version 4.4) and the linkers and other tools from binutils (currently version 2.20).

To compile a Native Client module, you must use the Native Client versions of the GNU tools. These versions use a variety of techniques to ensure that your .nexe doesn't violate Native Client's security constraints.

Note: In the future, additional tools will be available to compile Native Client modules written in other programming languages, such as C#. But this document covers only compiling C and C++ code, using the modified GNU toolchain provided with the SDK.

The tools are located in toolchain/platform/bin, where platform is the platform on which you are developing (either linux_x86, mac_x86, or win_x86). For example, the tools in the Macintosh version of the SDK are in toolchain/mac_x86/bin.

The SDK includes the following tools:

  • addr2line
  • ar
  • as
  • c++
  • c++filt
  • cpp
  • g++
  • gcc
  • gcc-4.4.3
  • gccbug
  • gcov
  • gprof
  • ld
  • nm
  • objcopy
  • objdump
  • ranlib
  • readelf
  • size
  • strings
  • strip

The bin directory contains two versions of each tool – one to compile Native Client modules for the x86-32 platform, and one to compile modules for the x86-64 platform. (For additional information about target architectures, see the section immediately below.) Each tool's name is preceded by "i686-nacl-" for the version used to compile for the x86-32 platform, and by "x86_64-nacl-" for the version used to compile for the x86-64 platform (these prefixes conform to gcc naming standards and make it easier to use tools like autoconf). For example, you can use i686-nacl-gcc to compile .nexe files for the x86-32 platform, and x86_64-nacl-gcc to compile .nexe files for the x86-64 platform. Note that you can typically override a tool's default target architecture with command line flags, e.g., you can specify x86_64-nacl-gcc -m32 ... to compile a .nexe for the x86-32 architecture.

Target architectures

As explained in the Technical Overview, Native Client modules are operating-system-independent, not processor-independent. Therefore, you must compile separate versions of your Native Client module for different processors on end-user machines, such as x86-32 and x86-64. The table below lists which .nexe modules run on which target architectures:

End-user OS/architecture ... can only run these .nexe modules
Windows 32-bit 32-bit .nexe modules
Windows 64-bit 64-bit .nexe modules
Mac 32-bit .nexe modules
Linux 32-bit 32-bit .nexe modules
Linux 64-bit both 32-bit and 64-bit .nexe modules, but only if matched with 32-bit or 64-bit Chrome, respectively

In general, you'll want to compile your modules for both 32-bit and 64-bit target architectures, and create a manifest file that specifies which version of your module to load based on the end-user's architecture. The SDK includes a custom SCons Builder to generate manifest files (see the function GenerateNmf() in build_tools/nacl_sdk_scons/nacl_utils.py). For an example of how to compile modules for multiple architectures and generate manifest files, see the commands that are executed by the SCons build tool for any of the examples in the SDK.

How to compile

To create a .nexe file, run the Native Client version of the appropriate compiler.

For example, to compile the x86-32 version of hello_world (on any of the supported platforms), you could use this series of command lines:

i686-nacl-g++ -o opt_x86_32/hello_world.o -Wall -c -Wno-long-long -pthread -O2 -m32 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/include -I ~/src/native_client_sdk_0_4_894 \
  hello_world.cc
i686-nacl-g++ -o opt_x86_32/helper_functions.o -Wall -c -Wno-long-long -pthread -O2 -m32 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/include -I ~/src/native_client_sdk_0_4_894 \
  helper_functions.cc
i686-nacl-g++ -o hello_world_x86_32.nexe -melf_nacl -m32 \
  opt_x86_32/hello_world.o opt_x86_32/helper_functions.o -lppapi -lppapi_cpp

When you're compiling, be sure to link the libraries in the order shown above. For more information about the libraries, see Libraries and header files provided with the SDK.

Compiling the x86-64 version is the same, except that you use -m64 instead of -m32:

x86_64-nacl-g++ -o opt_x86_64/hello_world.o -Wall -c -Wno-long-long -pthread -O2 -m64 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/nacl64/include -I ~/src/native_client_sdk_0_4_894 \
  hello_world.cc
x86_64-nacl-g++ -o opt_x86_64/helper_functions.o -Wall -c -Wno-long-long -pthread -O2 -m64 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/nacl64/include -I ~/src/native_client_sdk_0_4_894 \
  helper_functions.cc
x86_64-nacl-g++ -o hello_world_x86_64.nexe -melf64_nacl -m64 \
  opt_x86_64/hello_world.o opt_x86_64/helper_functions.o -lppapi -lppapi_cpp

The executable module is always named with the filename extension .nexe, regardless of what platform you're using.

Note: To see how the example Native Client modules in the SDK are compiled, run ../scons from any of the example subdirectories (e.g., examples/hello_world). On Windows, run ..\scons.bat. The SCons build tool displays the command it executes for each step of the build process. You may have to run ../scons -c first to clean (delete) previously compiled files, so that SCons won't skip any steps. The compile commands executed by SCons are slightly different than the commands shown above; in particular they include optional flags to turn on various compiler warnings, and canonical paths to the include file directories.

For more information about command-line options, see the options summary for the GNU tools, especially gcc.

Using SCons

Starting with version 0.4, the Native Client SDK uses the SCons build tool in place of Make. For SCons documentation, see the SCons man page and the SCons user guide.

The SDK includes the following SCons-related files:

examples/scons
Shell driver script that executes the scons command. On Windows this script is named scons.bat.
examples/*/build.scons
examples/build.scons
SCons configuration files that contain the build rules; these are the equivalent of Makefiles. There is a build.scons file in each of the example subdirectories (e.g., examples/hello_world/build.scons). There is also a build.scons file in the top-level examples directory, which you can use to build all the examples.
build_tools/nacl_sdk_scons/make_nacl_env.py
Script that creates a SCons construction environment that uses the Native Client toolchain instead of the platform-native toolchain (e.g., nacl-g++ instead of g++). This script also sets variables like CCFLAGS and CPPPATH with base options used to compile .nexes.
build_tools/nacl_sdk_scons/site_tools/nacl_tools.py
Script that appends options to CCFLAGS for debug and optimized build variants, and that extends the SCons construction environment with NaCl-specific builders.

There are a number of additional SDK-specific SCons utilities in the nacl_sdk_scons directory.

Note: You must have Python installed in order to use SCons. See the Download page for instructions on installing Python.

To build the examples in the SDK, go to any of the example subdirectories and run the SCons driver script:

cd examples/hello_world
../scons 

Note the "../" before the scons command. On Windows, run ..\scons.bat.

If you don't specify any arguments, the SCons driver script and configuration files for the examples will execute the following actions:

  • build optimized versions of a .nexe for both 32- and 64-bit architectures (e.g., hello_world_x86_32.nexe and hello_world_x86_64.nexe)
  • build debug versions of a .nexe for both 32- and 64-bit architectures (e.g., hello_world_x86_32_dbg.nexe and hello_world_x86_64_dbg.nexe)
  • generate optizimized and debug versions of a manifest file to load either the optimized or debug .nexes (e.g., hello_world.nmf and hello_world_dbg.nmf)
  • build test versions of a .nexe for both 32- and 64-bit architectures (e.g., hello_world_test_x86_32_test32.nexe and hello_world_test_x86_64_test64.nexe); you can use nacl-sel_ldr (described below) to load and run these .nexe files from the command line

Here are a few additional SCons commands:

  • To build a specific target, run scons and specify that target on the command line, e.g., ../scons hello_world_x86_32_dbg.nexe or ../scons hello_world_dbg.nmf.
  • To build all of the examples in the SDK, go to the examples directory and run ./scons.
  • To delete a specific target and its build artifacts, use the -c option, e.g., ../scons -c hello_world_x86_32.nexe.
  • To delete all previously compiled files and build artifacts, run ../scons -c without any targets. This is equivalent to make clean.
  • To add a compile flag for your project, append the flag to the appropriate variable in your project's build.scons file. For example, to compile with the ‑Werror flag, add the following line to build.scons:
    nacl_env.Append(CCFLAGS=['-Werror'])
    The call to the Append() method typically goes right after the nacl_env variable is created, near the top of the build.scons file. For an example of how to add compile flags, see the SCons configuration file for the hello_world example in the SDK (examples/hello_world/build.scons).
  • To add an include directory for your project, make sure your project's build.scons file includes the line "import os" near the top, and then, to add a path like path/to/include, add the following line to build.scons:
    nacl_env.Append(CPPPATH=[os.path.join('path', 'to', 'include')
    The call to the Append() method typically goes right after the nacl_env variable is created, near the top of the build.scons file. See examples/hello_world/build.scons for an example.
  • To see a list of SCons options, run ../scons --help.

For additional information on SCons, see the SCons man page and the SCons user guide.

If you use the Python script init_project.py to set up template files for a project, the project directory will include both the scons driver script and the build.scons configuration file for your project. To build your project simply run ./scons in the project directory. For additional information on setting up a project see the description of project template files in the C++ Tutorial.

Libraries and header files provided with the SDK

The Native Client SDK includes modified versions of standard toolchain-support libraries, such as iberty, nosys, pthread, and valgrind, plus the relevant header files.

The libraries are located in toolchain/platform/x86_64-nacl/lib32 (x86-32) and toolchain/platform/x86_64-nacl/lib64 (x86-64). For example, the x86-64 libraries in the Macintosh version of the SDK are in toolchain/mac_x86/x86_64-nacl/lib64.

The standard gcc libraries are also available, in toolchain/platform/lib.

The header files are in toolchain/platform/x86_64-nacl/include.

Note: The Native Client toolchain has its own default library and header search paths. Thus if you use a specific third-party library in your non-Native-Client development, the Native Client tools won't find that library. If you want to use the library for Native Client development, then look for it in naclports, or port it yourself.

The toolchain intentionally leaves out some standard libraries and header files; in particular, for sandboxing reasons, the SDK doesn't support some POSIX-specified items. For example, open(2) isn't included, and close(2) doesn't precisely match the POSIX version.

Many other libraries have been ported for use with Native Client; for more information, see the naclports project. If you port an open-source library for your own use, we recommend adding it to naclports.

Here are descriptions of some of the Native Client-specific libraries provided in the SDK:

libgio.a
Is used to implement Native Client logging and some other features in nacl_platform.
libimc.a
Implementations of the intermodule communications layer (IMC), which is used to implement SRPC, the Native Client RPC library.
libplatform.a
Provides some platform abstractions, and is used to implement some other Native Client libraries.
libppapi_cpp.a
Implementation of the Pepper (PPAPI) C++ interface.
libppruntime.a
Implementation of the Pepper (PPAPI) C interface; needed for all applications that use Pepper.
libpthread.a
Implementation of the Native Client pthread interface.
libsrpc.a
Implementation of the Native Client RPC layer, and is used to implement the Pepper C layer.

Debugging

Use of a debugger is not yet supported in Native Client. We're actively working on better debugging support.

However, there are some alternative approaches to debugging that expert gdb users may be able to use.

Another approach that may help you debug is to examine the JavaScript console in the browser for error messages.

You can also use various environment variables to output logging information. In particular, setting the NACL_PPAPI_PROXY_DEBUG=1 and NACL_PLUGIN_DEBUG=1 environment variables before starting the browser will give you a lot of logging information from the browser on Linux or Macintosh (those variables have no effect on Windows). Setting the NACL_SRPC_DEBUG=1 environment variable provides a huge amount of output.

Also, you may be able to use printf() to display information.

If you want to compile a debug version of a module, retaining symbol information, you can use the standard -g flag:

i686-nacl-g++ -o opt_x86_32/hello_world.o -Wall -c -Wno-long-long -pthread -g -m32 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/include -I ~/src/native_client_sdk_0_4_894 \
  hello_world.cc
i686-nacl-g++ -o opt_x86_32/helper_functions.o -Wall -c -Wno-long-long -pthread -g -m32 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/include -I ~/src/native_client_sdk_0_4_894 \
  helper_functions.cc
i686-nacl-g++ -o hello_world_x86_32.nexe -melf_nacl -m32 \
  opt_x86_32/hello_world.o opt_x86_32/helper_functions.o -lppapi -lppapi_cpp

Or for x86-64 debug versions:

x86_64-nacl-g++ -o opt_x86_64/hello_world.o -Wall -c -Wno-long-long -pthread -g -m64 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/nacl64/include -I ~/src/native_client_sdk_0_4_894 \
  hello_world.cc
x86_64-nacl-g++ -o opt_x86_64/helper_functions.o -Wall -c -Wno-long-long -pthread -g -m64 \
  -I ~/src/native_client_sdk_0_4_894/toolchain/linux_x86/nacl64/include -I ~/src/native_client_sdk_0_4_894 \
  helper_functions.cc
x86_64-nacl-g++ -o hello_world_x86_64.nexe -melf64_nacl -m64 \
  opt_x86_64/hello_world.o opt_x86_64/helper_functions.o -lppapi -lppapi_cpp

Testing

The SDK includes two tools to help you test .nexe modules:

  • ncval_x86_32 and sel_ldr_x86_32 (if your development machine is an x86-32 machine)
  • ncval_x86_64 and sel_ldr_x86_64 (if your development machine is an x86-64 machine)

These tools are located in toolchain/platform/bin, where platform is the platform on which you are developing (either linux_x86, mac_x86, or win_x86). You can use ncval to verify that a module meets Native Client's security constraints (address alignment, no restricted instructions, etc.). You can use sel_ldr to load and run a module from the command line. For an example of a unit test that you can run with sel_ldr, see examples/hello_world/test_helper_functions.cc.

Reducing code size

When you're ready to release, you'll probably want to make your .nexe file as small as possible, to improve download speed.

To reduce the size of your final .nexe files, compile your modules with the standard -O2 flag, and then use the i686-nacl-strip (or x86_64-nacl-strip) utility:

x86_64-nacl-strip hello_world_x86_32.nexe -o hello_world_x86_32.nexe
x86_64-nacl-strip hello_world_x86_64.nexe -o hello_world_x86_64.nexe

nacl-strip is based on the standard strip utility; for information on how to use it, see the man page for strip(1).

Note: The -Os flag (to optimize for size without loss of speed) has not been thoroughly tested. The example SCons configuration files use the -O2 flag. If you run into problems with optimization options, please file a bug.

Troubleshooting

Some common problems, and how to fix them:

SCons commands don't work
Be sure to have Python installed (see the Download page for instructions), and to invoke the SCons driver script from the appropriate directory.
"Undefined reference" errors

An "undefined reference" error may indicate incorrect link order and/or missing libraries. For example, if you leave out -lppapi when compiling the hello_world example, you'll see a series of undefined reference errors.

One common type of "undefined reference" error is with respect to certain system calls, e.g., "undefined reference to 'mkdir'". For security reasons, Native Client does not support a number of system calls. Depending on how your code uses such system calls, you have a few options:

  1. Link with the -lnosys flag to provide empty/always-fail versions of unsupported system calls. This will at least get you past the link stage.
  2. Find and remove use of the unsupported system calls.
  3. Create your own implementation of the unsupported system calls to do something useful for your application.

If your code uses mkdir or other file system calls, you might find nacl-mounts useful. Nacl-mounts essentially does option 3 for you: It lets your code use POSIX-like file system calls, and implements the calls using various technologies (e.g., App Engine or an in-memory filesystem).

Module doesn't load in browser
There can be various reasons for this problem:
  • You must use Chrome 14 or greater.
  • You must enable the Native Client flag in Chrome, and relaunch your browser (all Chrome windows will restart).
  • You may also need to enable the Native Client plugin in Chrome.
  • The .nexe file must be served from a web server (e.g., the local mini-server included with the SDK).
  • The .nexe file must be compiled for your machine's architecture (x86-32 or x86-64).

For instructions on how to resolve these issues, see the .nexe files never finish loading question in the FAQ.

Can't find libraries containing necessary symbols
One way to find the appropriate library for a given symbol: nm -o toolchain/platform/x86_64-nacl/lib64/*.a | grep MySymbolName

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.