Native Client

Building Native Client Modules

Introduction

This document describes how to build Native Client modules. It 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.

When to use a Native Client toolchain

Native Client modules are written in C or C++ and compiled into executable (.nexe) files using a toolchain in the Native Client SDK. Chrome can load .nexe files embedded in web pages and execute the .nexe files as part of a web application.

To build .nexe files, you must use one of the Native Client toolchains included in the SDK. The modified tools in the SDK toolchains use a variety of techniques to ensure that your .nexe files comply with the security constraints of the Native Client sandbox.

During development, you have another choice: You can build modules using a standard GNU toolchain, such as the hosted toolchain on your development machine. The benefit of using a standard GNU toolchain is that you can develop modules in your favorite IDE and use your favorite debugging and profiling tools. The drawback is that modules compiled in this manner can only run as Pepper plugins in Chrome. To publish and distribute Native Client modules as part of a web application, you must eventually use a toolchain in the Native Client SDK to create .nexe files.

Notes:
  • Documentation on how to compile and run modules as Pepper plugins will be published soon.
  • 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 toolchains provided in the SDK.

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 list below shows which .nexe modules run on which target architectures:

32-bit .nexe modules run on:
  • Windows 32-bit
  • Mac
  • Linux 32-bit
  • Linux 64-bit (but only if matched with 32-bit Chrome).
64-bit .nexe modules run on:
  • Windows 64-bit
  • Linux 64-bit (but only if matched with 64-bit Chrome).

In general, you must compile a module for both the 32-bit and 64-bit target architectures, and create a manifest file that specifies which version of the module to load based on the end-user's architecture. The SDK includes a script, create_nmf.py (in the tools/ directory) to generate manifest files. For examples of how to compile modules for multiple target architectures and how to generate manifest files, see the Makefiles included with the SDK examples.

SDK toolchains: newlib and glibc

The Native Client SDK includes two toolchains, differentiated by which C library they use—one toolchain uses newlib and the other uses glibc. The toolchains are located in the following two directories (on Windows, replace forward slashes with back slashes):

  • toolchain/<platform>_newlib/bin
  • toolchain/<platform>_glibc/bin

<platform> is the platform of your development machine, i.e., win_x86, mac_x86, or linux_x86. For example, the tools in the Windows SDK for the newlib toolchain are in toolchain\win_x86_newlib\bin.

Tools

The Native Client toolchains contain modified versions of the tools in the standard GNU toolchain, including the gcc compilers (currently version 4.4) and the linkers and other tools from binutils (currently version 2.20).

There are actually two versions of each tool—one to build Native Client modules for the x86-32 target architecture, and one to build modules for the x86-64 target architecture. Each tool's name is preceded by one of the following prefixes:

i686-nacl-
prefix for tools used to build 32-bit .nexes
x86_64-nacl-
prefix for tools used to build 64-bit .nexes

These prefixes conform to gcc naming standards and make it easy to use tools like autoconf. As an example, you can use i686-nacl-gcc to compile 32-bit .nexes, and x86_64-nacl-gcc to compile 64-bit .nexes. 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 32-bit .nexe.

The SDK toolchains include the following tools:

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

Compiling

To create a .nexe file, use a compiler in one of the Native Client toolchains.

For example, assuming you're developing on a Windows machine and using the newlib toolchain, you can compile a 32-bit .nexe for the hello_world example with the following command:

<NACL_SDK_ROOT>/pepper_20/toolchain/win_x86_newlib/bin/i686-nacl-gcc hello_world.c ^
  -o hello_world_x86_32.nexe -m32 -g -O0 -lppapi

(The carat ^ allows the command to span multiple lines on Windows; to do the same on Mac and Linux use a back slash instead. Or you can simply type the command and all its arguments on one line.)

To compile a 64-bit .nexe, you can run the same command but use -m64 instead of -m32. Alternatively, you could also use the version of the compiler that targets the x86-64 architecture, i.e., x86_64-nacl-gcc.

You should name executable modules with a .nexe filename extension, regardless of what platform you're using.

Compile flags for different development scenarios

To optimize the performance of your .nexe module, you must use the correct set of flags when you compile with nacl-gcc. If you're used to working with an IDE rather than with a command-line compiler like gcc, you may not be familiar with which flags you need to specify. The table below summarizes which flags to specify based on different development scenarios.

Development scenario Flags for nacl-gcc
debug -g -O0
profile [-g] -O{2|3} -msse -mfpmath=sse -ffast-math -fomit-frame-pointer
deploy -s -O{2|3} -msse -mfpmath=sse -ffast-math -fomit-frame-pointer

A few of these flags are described below:

-g
Produce debugging information.
-On
Optimize the executable for both performance and code size. A higher n increases the level of optimization. Use -O0 when debugging, and -O2 or -O3 for profiling and deployment.

The main difference between -O2 and -O3 is whether the compiler performs optimizations that involve a space-speed tradeoff. It could be the case that these optimizations are not desirable due to .nexe download size; you should make your own performance measurements to determine which level of optimization is right for you. When looking at code size, note that what you generally care about is not the size of the .nexe produced by nacl-gcc, but the size of the compressed .nexe that you upload to the Chrome Web Store. Optimizations that increase the size of a .nexe may not increase the size of the compressed .nexe that much.

For additional information about optimizations, see the gcc optimization options. Note that the -Os option (optimize for size) is not currently supported.
-s
Strip the .nexe, i.e., remove all symbol table and relocation information from the executable.

As an alternative to using the -s option, you can store a copy of the non-stripped .nexe somewhere so that you can extract symbol information from it if necessary, and use the nacl-strip tool in the SDK to strip symbol information from the .nexe that you deploy.

Note: To see how the examples in the SDK are built, run make in any of the example subdirectories (e.g., examples/hello_world). The make tool displays the full command lines it runs for each step of the build process (compiling, linking, and generating Native Client manifest files).

For additional information about compiler options, see gcc command options.

Using make

This document doesn't cover how to use make, but if you want to use make to build your Native Client module, you can base your Makefile on the ones in the SDK examples.

Starting with the pepper_21 bundle, the Makefiles for the SDK examples build the examples using both the newlib and glibc toolchains. With a few exceptions (tumbler, debugging, and dlopen), running make in each example's directory does the following:

  • creates a subdirectory called newlib;
  • builds 32- and 64-bit .nexes using the newlib toolchain;
  • generates a Native Client manifest (.nmf) file for the newlib version of the example;
  • creates a subdirectory called glibc;
  • builds 32- and 64-bit .nexes using the glibc toolchain; and
  • generates a Native Client manifest (.nmf) file for the glibc version of the example.

Your Makefile can be simpler since you will probably want to build your project using only one toolchain. The example Makefiles define numerous variables near the top (e.g., NACL_CCFLAGS) that make it easy to customize the commands that are executed for your project and the options for each command.

In addition to building .nexe files, the example Makefiles also generate Native Client manifest (.nmf) files, which your application points to from the src attribute of an <embed> tag in its HTML file. For information about Native Client manifest files, see the Technical Overview. The SDK includes a script called create_nmf.py (in the tools/ directory) that you can use to generate .nmf files. Run "python create_nmf.py --help" to see the script's command-line options, and look at the Makefiles in the SDK examples to see how to use the script to generate a manifest file for modules compiled with either toolchain.

For details on how to use make, see the GNU 'make' Manual.

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>_<library>/x86_64-nacl/lib32 and /lib64 (for the 32-bit and 64-bit target architectures, respectively). For example, on Windows, the libraries for the x86-64 architecture in the newlib toolchain are in toolchain\win_x86_newlib\x86_64-nacl/lib64.

The standard gcc libraries are also available, in toolchain/<platform>_<library>/lib.

The header files are in toolchain/<platform>_<library>/x86_64-nacl/include.

The toolchains intentionally leave 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:

libppapi.a
Implements the Pepper (PPAPI) C interface (needed for all applications that use Pepper).
libppapi_cpp.a
Implements the Pepper (PPAPI) C++ interface.
libpthread.a
Implements the Native Client pthread interface.
libsrpc.a
Implements the Native Client RPC layer, and is used to implement the Pepper C layer.
libimc.a
Implements the intermodule communications layer (IMC), which is used to implement SRPC, the Native Client RPC library.
libgio.a
Used to implement Native Client logging and some other features in nacl_platform.
libplatform.a
Provides some platform abstractions, and is used to implement some other Native Client libraries.
Notes:
  • Since the Native Client toolchains use their own library and header search paths, the tools won't find third-party libraries you use in your non-Native-Client development. If you want to use a specific third-party library for Native Client development, look for it in naclports, or port the library yourself.
  • The order in which you list libraries in your build commands is important, since the linker searches and processes libraries in the order in which they are specified. See the NACL_LDFLAGS and <PROJECT>_LDFLAGS variables in the Makefiles of the SDK examples for the order in which specific libraries should be listed.

Troubleshooting

Some common problems, and how to fix them:

"Undefined reference" error

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).

Can't find libraries containing necessary symbols
Here is one way to find the appropriate library for a given symbol: nm -o toolchain/<platform>_<library>/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.