blob: c19fa8244a5653d960945b21d3ad19718fedb2c8 [file] [log] [blame]
######################################
Dynamic Linking and Loading with glibc
######################################
.. contents::
:local:
:backlinks: none
:depth: 2
Introduction
============
This document describes how to create and deploy dynamically linked and loaded
applications with the glibc library in the Native Client SDK. Before reading
this document, we recommend reading :doc:`Building Native Client Modules
<building>`
.. _c_libraries:
C standard libraries: glibc and newlib
--------------------------------------
The Native Client SDK comes with two C standard libraries --- glibc and
newlib. These libraries are described in the table below.
+--------+----------+-------------+--------------------------------------------+
| Library| Linking | License | Description |
+========+==========+=============+============================================+
| glibc | dynamic | GNU Lesser | glibc is the GNU implementation of the |
| | or static| General | POSIX_ standard runtime library for the C |
| | | Public | programming language. Designed for |
| | | License | portability and performance, glibc is one |
| | | (LGPL) | of the most popular implementations of the |
| | | | C library. It is comprised of a set of |
| | | | interdependent libraries including libc, |
| | | | libpthreads, libdl, and others. For |
| | | | documentation, FAQs, and additional |
| | | | information about glibc, see GLIBC_ |
+--------+----------+-------------+--------------------------------------------+
| newlib | static | Berkeley | newlib is a C library intended for use in |
| | | Software | embedded systems. Like glibc, newlib is a |
| | | Distribution| conglomeration of several library parts. |
| | | (BSD) type | It is available for use under BSD-type free|
| | | free | software licenses, which generally makes it|
| | | software | more suitable to link statically in |
| | | licenses | commercial, closed-source applications. For|
| | | | documentation, FAQs, and additional |
| | | | information about newlib, see the `newlib |
| | | | documentation`__. |
+--------+----------+-------------+--------------------------------------------+
.. _GLIBC: http://www.gnu.org/software/libc/index.html
.. _POSIX: http://en.wikipedia.org/wiki/POSIX
__ http://sourceware.org/newlib
For proprietary (closed-source) applications, your options are to either
statically link to newlib, or dynamically link to glibc. We recommend
dynamically linking to glibc, for a couple of reasons:
* The glibc library is widely distributed (it's included in Linux
distributions), and as such it's mature, hardened, and feature-rich. Your
code is more likely to compile out-of-the-box with glibc.
* Dynamic loading can provide a big performance benefit for your application if
you can structure the application to defer loading of code that's not needed
for initial interaction with the user. It takes some work to put such code in
shared libraries and to load the libraries at runtime, but the payoff is
usually worth it. In future releases, Chrome may also support caching of
common dynamically linked libraries such as libc.so between applications.
This could significantly reduce download size and provide a further potential
performance benefit (for example, the hello_world example would only require
downloading a .nexe file that's on the order of 30KB, rather than a .nexe
file and several libraries, which are on the order of 1.5MB).
Native Client support for dynamic linking and loading is based on glibc. Thus,
**if your Native Client application must dynamically link and load code (e.g.,
due to licensing considerations), we recommend that you use the glibc
library.**
.. Note::
:class: note
**Notes:**
* **None of the above constitutes legal advice, or a description of the legal
obligations you need to fulfill in order to be compliant with the LGPL or
newlib licenses. The above description is only a technical explanation of
the differences between newlib and glibc, and the choice you must make
between the two libraries.**
* Static linking with glibc is rarely used. Use this feature with caution.
* The standard C++ runtime in Native Client is provided by libstdc++; this
library is independent from and layered on top of glibc. Because of
licensing restrictions, libstdc++ must be statically linked for commercial
uses, even if the rest of an application is dynamically linked.
SDK toolchains
--------------
The Native Client SDK contains multiple toolchains, which are differentiated by
:ref:`target architecture <target_architectures>` and C library:
=================== ========= ===============================
Target architecture C library Toolchain directory
=================== ========= ===============================
x86 newlib toolchain/<platform>_x86_newlib
x86 glibc toolchain/<platform>_x86_glibc
ARM newlib toolchain/<platform>_arm_newlib
PNaCl newlib toolchain/<platform>_pnacl
=================== ========= ===============================
In the directories listed above, <platform> is the platform of your development
machine (i.e., win, mac, or linux). For example, in the Windows SDK, the x86
toolchain that uses glibc is in ``toolchain/win_x86_glibc``.
.. Note::
:class: note
**Note:** The ARM and PNaCl toolchains are currently restricted to newlib.
To use the glibc library and dynamic linking in your application, you **must**
use a glibc toolchain. (Currently the only glibc toolchain is
``<platform>_x86_glibc``.) Note that you must build all code in your application
with one toolchain. Code from multiple toolchains cannot be mixed.
Specifying and delivering shared libraries
------------------------------------------
One significant difference between newlib and glibc applications is that glibc
applications must explicitly list and deploy the shared libraries that they
use.
In a desktop environment, when the user launches a dynamically linked
application, the operating system's program loader determines the set of
libraries the application requires by reading explicit inter-module
dependencies from executable file headers, and loads the required libraries
into the address space of the application process. Typically the required
libraries will have been installed on the system as a part of the application's
installation process. Often the desktop application developer doesn't know or
think about the libraries that are required by an application, as those details
are taken care of by the user's operating system.
In the Native Client sandbox, dynamic linking can't rely in the same way on the
operating system or the local file system. Instead, the application developer
must identify the set of libraries that are required by an application, list
those libraries in a Native Client :ref:`manifest file <manifest_file>`, and
deploy the libraries along with the application. Instructions for how to build
a dynamically linked Native Client application, generate a Native Client
manifest (.nmf) file, and deploy an application are provided below.
Building a dynamically linked application
=========================================
A dynamically linked application typically includes one Native Client module
and one or more shared libraries. (How to allocate code between Native Client
modules and shared libraries is a question of application design that is beyond
the scope of this document.) Each Native Client module and shared library must
be compiled for at least the x86 32-bit and 64-bit architectures.
The dlopen example in the SDK
-----------------------------
The Native Client SDK includes an example that demonstrates how to build a
shared library, and how to use the ``dlopen()`` interface to load that library
at runtime (after the application is already running). Many applications load
and link shared libraries at launch rather than at runtime, and hence do not
use the ``dlopen()`` interface. The SDK example is nevertheless instructive, as
it demonstrates how to build Native Client modules (.nexe files) and shared
libraries (.so files) with the x86 glibc toolchain, and how to generate a
Native Client manifest file for glibc applications.
The SDK example, located in the directory examples/dlopen, includes two C++
files:
eightball.cc
This file implements the function ``Magic8Ball()``, which is used to provide
whimsical answers to user questions. The file is compiled into a shared
library, ``libeightball.so``.
dlopen.cc
This file implements the Native Client module, which loads
``libeightball.so``, receives messages from JavaScript (sent in response to
user input), calls ``Magic8Ball()`` to generate answers, and sends messages
back to JavaScript with the generated answers. The file is compiled into a
.nexe file.
.. TODO(sbc): also mention reverse.{cc,h} files
Run ``make`` in the dlopen directory to see the commands the Makefile executes
to build x86 32-bit and 64-bit .nexe and .so files, and to generate a .nmf
file. These commands are described below.
.. Note::
:class: note
**Note:** The Makefiles for most of the examples in the SDK build the
examples using multiple toolchains (x86 newlib, x86 glibc, ARM, and PNaCl).
With a few exceptions (listed in the :ref:`Release Notes
<sdk-release-notes>`), running "make" in each example's directory builds
multiple versions of the example using the SDK toolchains. The dlopen example
is one of those exceptions – it is only built with the x86 glibc toolchain,
as that is currently the only toolchain that supports glibc and thus dynamic
linking and loading. Take a look at the example Makefiles and the generated
.nmf files for details on how to build dynamically linked applications.
Building a Native Client module (.nexe file)
--------------------------------------------
.. TODO(sbc): there is a lot of redundant detail here. Also the Makefile
structure has changed significantly.
The Makefile in the dlopen example builds ``dlopen.cc`` into a .nexe file using
the two commands shown below. (For simplicity, the full path to the
compiler/linker is not shown; the tool is located in the bin directory in the
x86 glibc toolchain, e.g. toolchain/win_x86_glibc/bin.)
To compile dlopen.cc into dlopen_x86_32.o::
i686-nacl-g++ -o dlopen_x86_32.o -c dlopen.cc -m32 -g -O0 -pthread -std=gnu++98 -Wno-long-long -Wall
To link dlopen_x86_32.o into dlopen_x86_32.nexe::
i686-nacl-g++ -o dlopen_x86_32.nexe dlopen_x86_32.o -m32 -g -ldl -lppapi_cpp -lppapi
A few of the flags in these commands are described below:
``-o`` *file*
put the output in *file*
``-c``
compile the source file, but do not link it
``-m32``
produce 32-bit code (i.e., code for the x86-32 target architecture)
``-g``
produce debugging information
``-O0``
use a base optimization level that minimizes compile time
``-pthread``
support multithreading with the pthread library
``-W`` *warning*
request or supress the specified warning
``-l`` *library*
use the specified *library* when linking (per C library naming conventions,
the linker uses the file lib*library*.so, or if that file is not available,
lib*library*.a; e.g., -ldl corresponds to libdl.so or libdl.a)
Many of these flags are optional; you need not use all of them to compile and
link your application. For example, you only need to use -ldl if your
application uses the dlopen() interface to open a library at runtime. The
toolchains in the Native Client SDK are based on the gcc compiler; see `gcc
command options <http://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html>`_ for a
full description of the gcc flags. For flags that are recommended with Native
Client, see :ref:`compile flags for different development scenarios
<compile_flags>`.
Note that you can combine the compile and link steps to build a .nexe file
using one command. Simply run i686-nacl-g++ once and use the appropriate
combination of flags (omit the -c flag and include the -l flag with the
required libraries)::
i686-nacl-g++ -o dlopen_x86_32.nexe dlopen.cc ^ -m32 -g -O0 -pthread -std=gnu++98 -Wno-long-long -Wall -ldl -lppapi_cpp -lppapi
(The carat ``^`` allows the command to span multiple lines on Windows; to do
the same on Mac and Linux use a backslash instead. Or you can simply type the
command and all its arguments on one line.)
The commands above build a 32-bit .nexe. To build a 64-bit .nexe, run the same
commands but with the **-m64** flag instead of -m32, and of course specify
different output file names. Check the Makefile in the dlopen example to see
the set of commands that is used to generate 32-bit and 64-bit .nexes.
Building a shared library (.so file)
------------------------------------
The Makefile in the dlopen example builds eightball.cc into a .so file using
the two commands shown below.
To compile eightball.cc into eightball_x86_32.o::
i686-nacl-g++ -o eightball_x86_32.o -c eightball.cc -m32 -g -O0 -pthread -std=gnu++98 -Wno-long-long -Wall -fPIC
To link eightball_x86_32.o into eightball_x86_32.so::
i686-nacl-g++ -o libeightball.so eightball_x86_32.o -m32 -g -ldl -lppapi_cpp -lppapi -shared
A couple of the important flags in these commands are described below:
``-fPIC``
generate position-independent code (PIC) suitable for use in a shared library
(this flag is required for all x86 64-bit modules and for 32-bit shared
libraries)
``-shared``
produce a shared object that can be linked with other objects to form an
executable (this flag is required for .so files)
As when building a .nexe, you can combine compiling and linking into one step
by running i686-nacl-g++ once with the appropriate combination of flags.
As with .nexes, you need to generate both 32-bit and 64-bit versions of a
shared object -- see the dlopen example for an illustration. In the dlopen
example, the shared objects are put into the subdirectories ``lib32`` and
``lib64``. These directories are used to collect all the shared libraries
needed by the application, as discussed below.
Generating a Native Client manifest file for a dynamically linked application
=============================================================================
The Native Client manifest file must specify the full list of executable files
needed by an application, including the recursive closure of shared library
dependencies. Take a look at the manifest file in the dlopen example to see how
a glibc-style manifest file is structured. (Run make in the dlopen directory to
generate the manifest file if you haven't done so already.) Here is an excerpt
from ``dlopen.nmf``::
{
"files": {
"libeightball.so": {
"x86-64": {
"url": "lib64/libeightball.so"
},
"x86-32": {
"url": "lib32/libeightball.so"
}
},
"libstdc++.so.6": {
"x86-64": {
"url": "lib64/libstdc++.so.6"
},
"x86-32": {
"url": "lib32/libstdc++.so.6"
}
},
"libppapi_cpp.so": {
"x86-64": {
"url": "lib64/libppapi_cpp.so"
},
"x86-32": {
"url": "lib32/libppapi_cpp.so"
}
},
... etc.
In most cases, you can use the ``create_nmf.py`` script in the SDK to generate
a manifest file for your application. The script is located in the tools
directory (e.g., pepper_28/tools).
.. TODO(sbc): running create_nmf.py is much simpler now.
The Makefile in the dlopen example generates the manifest file ``dlopen.nmf``
by running the following command::
python <NACL_SDK_ROOT>/tools/create_nmf.py ^
-D <NACL_SDK_ROOT>/toolchain/win_x86_glibc/x86_64-nacl/bin/objdump ^
-o dlopen.nmf ^
-s . ^
dlopen_x86_32.nexe dlopen_x86_64.nexe lib32/libeightball.so lib64/libeightball.so ^
-L <NACL_SDK_ROOT>/toolchain/win_x86_glibc/x86_64-nacl/lib32 ^
-L <NACL_SDK_ROOT>/toolchain/win_x86_glibc/x86_64-nacl/lib64
(The carat ``^`` allows the command to span multiple lines on Windows; to do the
same on Mac and Linux use a backslash instead, or you can simply type the
command and all its arguments on one line. *<NACL_SDK_ROOT>* represents the path
to the top-level directory of the bundle you are using, e.g.,
*<location-where-you-installed-the-SDK>*/pepper_28.)
Run python ``create_nmf.py --help`` to see a description of the command-line
flags. A few of the important flags are described below.
.. TODO(sbc): remove -D option which is now deprecated.
``-D`` *tool*
use *tool* to read information about a file and determine shared library
dependencies (the tool must be a version of the `objdump
<http://en.wikipedia.org/wiki/Objdump>`_ utility)
``-s`` *directory*
use *directory* to stage libraries (libraries are added to ``lib32`` and
``lib64`` subfolders)
``-L`` *directory*
add *directory* to the library search path
.. Note::
:class: note
**Caution:** The ``create_nmf.py`` script only recognizes explicit shared
library dependencies (for example, dependencies specified with the -l flag
for the compiler/linker). The manifest file generated by create_nmf.py will
be incorrect in the following situations:
* You run ``create_nmf.py`` without listing as arguments all the libraries
that your application opens with ``dlopen()``.
* After you run ``create_nmf.py``, you subsequently add a library dependency
that is not mentioned in the manifest file.
* After you run ``create_nmf.py``, you subsequently change the directory
structure on your server or in your Chrome Web Store manifest file, such
that the needed libraries are no longer in the location specified in the
.nmf file
To handle the above situations correctly, you must re-run ``create_nmf.py``
(for example, if you added a new library dependency in your application or
changed the application directory structure), and make sure to list all the
libraries that your application opens at runtime with ``dlopen()`` (e.g.,
libeighball.so in the dlopen example).
.. TODO(sbc): We probably don't want/need this next section in the docs at all.
As an alternative to using ``create_nmf.py``, you can also chase down the full
list of shared library dependencies manually and add those to your .nmf file.
To do so, start by running the Native Client version of the objdump utility on
your .nexe file, as shown below. (The objdump utility is located in the same
directory as the glibc toolchain, e.g., toolchain/win_x86_glibc/bin.)
::
i686-nacl-objdump -p dlopen_x86_32.nexe
A .nexe file contains compiled machine code, as well as headers that describe
the contents of the file and information about how to use the file. The objdump
utility lets you examine the file's headers, including the "Dynamic Section,"
which specifies shared library dependencies, as in this example output from the
dlopen example::
Dynamic Section:
NEEDED libdl.so.32d9fc17
NEEDED libppapi_cpp.so
NEEDED libpthread.so.32d9fc17
NEEDED libstdc++.so.6
NEEDED libm.so.32d9fc17
NEEDED libgcc_s.so.1
NEEDED libc.so.32d9fc17
INIT 0x01000140
FINI 0x01002560
HASH 0x110025fc
...
All the files that are identified as NEEDED in the "Dynamic Section" portion of
the objdump output are files that you need to list in your Native Client
manifest file and distribute with your application. (The numbers listed at the
end of the file names are version numbers, and you must list and distribute
those exact versions.) Once you've identified the shared libraries that are
needed by your .nexe file, you must repeat the process recursively: Run objdump
on each of the NEEDED files, and add the newly-identified NEEDED files to your
manifest file and to your distribution directories. To get the full list of
libraries for an application, repeat the process until you've identified the
recursive closure of dependencies.
Deploying a dynamically linked application
==========================================
As described above, an application's manifest file must explicitly list all the
executable code modules that the application requires, including modules from
the application itself (.nexe and .so files), modules from the Native Client
SDK (e.g., libppapi_cpp.so), and perhaps also modules from `naclports
<http://code.google.com/p/naclports>`_ or from :doc:`middleware systems
<../../community/middleware>` that the application uses. You must provide all of
those modules as part of the application deployment process.
As explained in :doc:`Distributing Your Application
<../distributing>`, there are two basic ways to deploy an application:
* **hosted application:** all modules are hosted together on a web server of
your choice
* **packaged application:** all modules are packaged into one file, hosted in
the Chrome Web Store, and downloaded to the user's machine
You must deploy all the modules listed in your application's manifest file for
either the hosted application or the packaged application case. For hosted
applications, you must upload the modules to your web server. For packaged
applications, you must include the modules in the application's Chrome Web
Store .crx file. Modules should use URLs/names that are consistent with those
in the Native Client manifest file, and be named relative to the location of
the manifest file. Remember that some of the libraries named in the manifest
file may be located in directories you specified with the -L option to
``create_nmf.py``. You are free to rename/rearrange files and directories
referenced by the Native Client manifest file, so long as the modules are
available in the locations indicated by the manifest file. If you move or
rename modules, it may be easier to re-run create_nmf.py to generate a new
manifest file rather than edit the original manifest file. For hosted
applications, you can check for name mismatches during testing by watching the
request log of the web server hosting your test deployment.
Opening a shared library at runtime
===================================
Native Client supports a version of the POSIX standard ``dlopen()`` interface
for opening libraries explicitly, after an application is already running.
Calling ``dlopen()`` may cause a library download to occur, and automatically
loads all libraries that are required by the named library.
.. Note::
:class: note
**Caution:** Since ``dlopen()`` can potentially block, you must initially
call ``dlopen()`` off your application's main thread. Initial calls to
``dlopen()`` from the main thread will always fail in the current
implementation of Native Client.
The best practice for opening libraries with ``dlopen()`` is to use a worker
thread to pre-load libraries asynchronously during initialization of your
application, so that the libraries are available when they're needed. You can
call ``dlopen()`` a second time when you need to use a library -- per the
specification, subsequent calls to ``dlopen()`` return a handle to the
previously loaded library. Note that you should only call dlclose() to close a
library when you no longer need the library; otherwise, subsequent calls to
``dlopen()`` could cause the library to be fetched again.
The dlopen example in the SDK demonstrates how to open a shared library,
magiceightball.so, at runtime. To reiterate, the example includes two C++
files:
.. TODO(sbc): mention the third .cc file which is now part of this example.
* eightball.cc: this is the shared library that implements the function
``Magic8Ball()`` (this file is compiled into libeightball.so)
* dlopen.cc: this is the Native Client module that loads ``libeightball.so``
and calls ``Magic8Ball()`` to generate answers (this file is compiled into
dlopen_x86_{32,64}.nexe)
When the Native Client module starts, it kicks off a worker thread that calls
``dlopen()`` to load magiceightball.so. When the download of
``libeightball.so`` completes, the worker thread schedules a callback function
on the main thread. The callback function calls ``dlopen()`` for
``magiceightball.so`` a second time; this second call obtains a proper handle
to the library. Once the module has a handle to the library, it grabs the entry
point in libeightball.so for the ``Magic8Ball()`` function. When a user types
in a query and clicks the 'ASK!' button, the module calls ``Magic8Ball()`` to
generate an answer, and returns the result to the user.
The sequence of calls in the dlopen module is illustrated by the pseudo-code in
the table below:
+------------------------------------------------+------------------------------------------+
| Worker Thread | Main Thread |
+================================================+==========================================+
| :: | :: |
| | |
| pthread_create(.., LoadLibrariesOnWorker, ..)| - |
| - | LoadLibrariesOnWorker() |
| - | LoadLibrary() |
| - | dlopen("libeightball.so",...) |
| - | CallOnMainThread(.., LoadDone, ..) |
| LoadDone() | - |
| UseLibrary() | - |
| dlopen("libeightball.so", ...) | - |
| offset = dlsym(..., "Magic8Ball") | - |
| HandleMessage() | - |
| _eightball = (TYPE_eightball) offset; | - |
| PostMessage() | - |
+------------------------------------------------+------------------------------------------+
Troubleshooting
===============
If your .nexe isn't loading, the best place to look for information that can
help you troubleshoot the problem is stdout and nacllog. See the Debugging page
for instructions about how to access those streams.
Here are a few common error messages and explanations of what they mean:
**/main.nexe: error while loading shared libraries: /main.nexe: failed to allocate code and data space for executable**
The .nexe may not have been compiled correctly (e.g., the .nexe may be
statically linked). Try cleaning and recompiling with the glibc toolchain.
**/main.nexe: error while loading shared libraries: libpthread.so.xxxx: cannot open shared object file: Permission denied**
(xxxx is a version number, for example, 5055067a.) This error can result from
having the wrong path in the .nmf file. Double-check that the path in the
.nmf file is correct.
**/main.nexe: error while loading shared libraries: /main.nexe: cannot open shared object file: No such file or directory**
If there are no obvious problems with your main.nexe entry in the .nmf file,
check where main.nexe is being requested from. Use Chrome's Developer Tools:
Click the menu icon |menu-icon|, select Tools > Developer Tools, click the
Network tab, and look at the path in the Name column.
**NaCl module load failed: ELF executable text/rodata segment has wrong starting address**
This error happens when using a newlib-style .nmf file instead of a
glibc-style .nmf file. Make sure you build your application with the glic
toolchain, and use the create_nmf.py script to generate your .nmf file.
**NativeClient: NaCl module load failed: Nexe crashed during startup**
This error message indicates that a module crashed while being loaded. You
can determine which module crashed by looking at the Network tab in Chrome's
Developer Tools (see above). The module that crashed will be the last one
that was loaded.
**/lib/main.nexe: error while loading shared libraries: /lib/main.nexe: only ET_DYN and ET_EXEC can be loaded**
This error message indicates that there is an error with the .so files listed
in the .nmf file -- either the files are the wrong type or kind, or an
expected library is missing.
**undefined reference to 'dlopen' collect2: ld returned 1 exit status**
This is a linker ordering problem that usually results from improper ordering
of command line flags when linking. Reconfigure your command line string to
list libraries after the -o flag.
.. |menu-icon| image:: /images/menu-icon.png