原文链接:https://developer.chrome.com/native-client/devguide/devcycle/building
注意:已针对ChromeOS以外的平台公布了此处所述技术的弃用。
请访问我们的 迁移指南 了解详情。
构建
介绍
本文档介绍了如何构建Native Client模块。它适用于具有编写,编译和链接C和C ++代码经验的开发人员。如果您还没有阅读Native Client 技术概述和教程,我们建议您从这些开始。
目标架构
Portable Native Client(PNaCl)模块使用C或C ++编写,并使用Native Client SDK 中的PNaCl工具链编译为以.pexe扩展名结尾的可执行文件。Chrome可以加载嵌入在网页中的pexe文件,并将其作为Web应用程序的一部分执行。
如技术概述中所述,PNaCl模块独立于操作系统且与处理器无关。同样的pexe 将在Windows,Mac OS X,Linux和ChromeOS上运行,它将在x86-32,x86-64,ARM和MIPS处理器上运行。
Native Client还支持特定于体系结构的nexe文件。这些nexe文件也与操作系统无关,但它们不依赖于处理器。要支持各种设备,必须为最终用户计算机上的不同处理器编译单独版本的Native Client模块。然后, 清单文件将根据最终用户的体系结构指定要加载的模块版本。SDK包含一个用于生成名为的清单文件的脚本create_nmf.py
。这个脚本位于pepper_<version>/tools/
目录,意思是你安装的胡椒包下。有关如何为多个目标体系结构编译模块以及如何生成清单文件的示例,请参阅SDK示例中包含的Makefile。
本节将主要介绍PNaCl,还介绍了如何构建 nexe应用程序。
C库
所述PNaCl工具链使用newlib C库,并且可以用于建立便携式pexe文件(使用pnacl-clang
)或nexe文件(使用,例如,x86_64-nacl-clang
)。Native Client SDK还有一个基于GCC的工具链,用于构建使用glibc C库的nexe文件。有关这些库的信息,请参阅使用glibc动态链接和加载,包括帮助您确定使用哪些库的因素。
C ++标准库
PNaCl SDK可以使用LLVM的libc ++ (当前默认值)或GCC的libstdc ++(不建议使用)。该 -stdlib=[libc++|libstdc++]
命令行参数可用于选择要使用的标准库。
基于GCC的工具链仅支持GCC的libstdc ++。
C ++ 11库支持仅在libc ++中完成,但无论使用哪个标准库,其他非库语言功能都应该有效。该 -std=gnu++11
命令行参数可用于指示要使用的C ++语言标准(-std=c++11
往往是因为newlib依赖于一些GNU扩展不能很好地工作)。
SDK工具链
Native Client SDK包含多个工具链。它有一个PNaCl工具链,它有多个基于GCC的工具链,这些工具链由目标体系结构和C库区分。单个PNaCl工具链位于名为的目录中pepper_<version>/toolchain/<OS_platform>_pnacl
,基于GCC的工具链位于名为的目录中pepper_<version>/toolchain/<OS_platform>_<architecture>_<c_library>
。
编译器,链接器和其他工具位于bin/
每个工具链的子目录中。例如,Windows SDK for PNaCl中的工具中有一个C ++编译器toolchain/win_pnacl/bin/pnacl-clang++
。
SDK工具链与托管工具链
要构建NaCl模块,必须使用SDK中包含的一个Native Client工具链。SDK工具链使用各种技术来确保您的NaCl模块符合Native Client沙箱的安全限制。
在开发过程中,您还有另一种选择:您可以使用标准构建模块 工具链,例如开发计算机上托管的工具链。这可以是开发计算机上Visual Studio的标准编译器,XCode,LLVM或基于GNU的编译器。这些标准工具链不会生成符合Native Client沙箱安全性约束的可执行文件。它们也不能跨操作系统移植,也不能跨不同处理器移植。但是,使用标准工具链可以在您喜欢的IDE中开发模块,并使用您喜欢的调试和分析工具。缺点是以这种方式编译的模块只能在Chrome中作为Pepper(PPAPI)插件运行。要将Native Client模块作为Web应用程序的一部分发布和分发,最终必须在Native Client SDK中使用工具链。
将来,还可以使用其他工具来编译用其他编程语言编写的Native Client模块,例如C#。但是本文档仅涉及使用SDK中提供的工具链编译C和C ++代码。
PNaCl工具链
PNaCl工具链包含LLVM工具链中工具的修改版本,以及来自binutils的链接器和其他工具。要确定工具所基于的LLVM或binutils版本,请使用--version
命令行标志运行该工具。这些工具用于编译应用程序并将其链接到.pexe文件。工具链还包含一个工具,用于将pexe文件转换为特定于体系结构的.nexe(例如,用于调试目的)。
一些有用的工具包括:
pnacl-abicheck
检查pexe是否遵循PNaCl ABI规则。
pnacl-ar
创建档案(即静态库)
pnacl-bcdis
PNaCl bitcode文件的对象转储程序。
pnacl-clang
C编译器和编译驱动程序
pnacl-clang++
C ++编译器和编译器驱动程序
pnacl-compress
压缩最终的pexe文件以进行部署。
pnacl-dis
反汇编两个pexe文件和nexe文件
pnacl-finalize
完成pexe文件的部署
pnacl-ld
Bitcode链接器
pnacl-nm
列出bitcode文件,本机代码和库中的符号
pnacl-ranlib
为档案生成符号表(即静态库)
pnacl-translate
将pexe转换为浏览器之外的本机架构
有关完整工具列表,请参阅该 pepper_<version>/toolchain/<platform>_pnacl/bin
目录。
使用PNaCl工具进行编译,链接,调试和部署
要使用PNaCl SDK工具链构建应用程序,必须编译代码,链接代码,测试和调试代码,然后进行部署。本节将介绍如何使用这些工具的一些示例。
编
要编译由一个简单的应用程序file1.cc
,并file2.cc
为 hello_world.pexe
使用pnacl-clang++
工具
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-clang++
file1.cc file2.cc -Inacl_sdk/pepper_<version>/include
-Lnacl_sdk/pepper_<version>/lib/pnacl/Release -o hello_world.pexe
-g -O2 -lppapi_cpp -lppapi
典型的应用程序包含许多文件。在这种情况下,可以单独编译每个文件,以便只需要重新编译受更改影响的文件。要从应用程序编译单个文件,必须使用pnacl-clang
C编译器或pnacl-clang++
C ++编译器。编译器生成单独的bitcode文件。例如:
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-clang++
hello_world.cc -Inacl_sdk/pepper_<version>/include -c
-o hello_world.o -g -O0
有关每个命令行标志的说明,请运行pnacl-clang --help
。为方便起见,下面是对示例中使用的一些标志的描述。
-c
表示pnacl-clang++
应该只编译单个文件,而不是继续构建过程并将整个应用程序链接在一起。
-o <output_file>
表示输出文件名。
-g
告诉编译器在结果中包含调试信息。可以在开发期间使用此调试信息,然后 在实际部署应用程序之前将其剥离,以使应用程序的下载量保持较小。
-On
将优化级别设置为n。使用-O0
时调试,-O2
或 -O3
进行部署。
-O2
和之间的主要区别在于-O3
编译器是否执行涉及空速 - 权衡的优化。-O3
由于pexe 下载量增加,可能会出现不希望优化的情况; 您应该进行自己的性能测量,以确定哪种优化级别适合您。当代码大小看,请注意您通常关心的是不是大小pexe所生产 pnacl-clang
,但压缩的大小pexe你上传到服务器或Chrome网上应用店。优化,提高未压缩的大小pexe可能不会增加压缩的大小pexe 非常。您还应该验证优化级别如何影响设备上的转换时间,这可以在本地进行测试pnacl-translate
。
-I<directory>
将目录添加到包含文件的搜索路径中。SDK具有Pepper(PPAPI)标头nacl_sdk/pepper_<version>/ include
,因此在编译时添加该目录以便能够包含标头。
-mllvm -inline-threshold=n
更改LLVM执行的内联多少(默认值为225,较小的值将导致执行的内联更少)。选择正确的数字是特定于应用程序的,因此您需要尝试传递的值:您将通过pexe大小和设备上的翻译速度来衡量潜在的性能 。
创建一个静态库
该pnacl-ar
和pnacl-ranlib
工具允许您创建一个 静态从一组位码文件,以后可以链接到完整的应用程序库。
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-ar cr
libfoo.a foo1.o foo2.o foo3.o
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-ranlib libfoo.a
链接应用程序
该pnacl-clang++
工具用于编译应用程序,但它也可以用于将编译的bitcode和库链接到一个完整的应用程序中。
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-clang++
-o hello_world.pexe hello_world.o -Lnacl_sdk/pepper_<version>/lib/pnacl/Debug
-lfoo -lppapi_cpp -lppapi
这将hello world bitcode与foo
示例中的库以及位于其中的Pepper库的Debug版本相链接nacl_sdk/pepper_<version>/lib/pnacl/Debug
。如果您希望链接Pepper文件库的Release版本,请更改 -Lnacl_sdk/pepper_<version>/lib/pnacl/Debug
为 -Lnacl_sdk/pepper_<version>/lib/pnacl/Release
。
在发布版本中,您需要传递-O2
给编译器以及链接器以启用链接时优化。这样可以减小尺寸并提高最终pexe的性能,从而加快下载速度和设备上的翻译速度。
默认情况下,链接步骤会将所有C ++异常转换为调用,abort()
以减少最终pexe的大小,并使其转换和运行更快。如果要使用C ++异常,则应使用 --pnacl-exceptions=sjlj
链接器标志,如C ++语言支持参考的异常处理部分所述。
完成pexe的部署
通常,您将运行应用程序来测试它,并在部署之前根据需要进行调试。请参阅运行文档以了解如何运行PNaCl应用程序,并查看调试技术和工作流程的调试文档。测试PNaCl应用程序后,您必须 完成它。该pnacl-finalize
工具处理此问题。
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-finalize
hello_world.pexe -o hello_world.final.pexe
在最终确定之前,应用程序pexe以二进制格式存储,可能会发生变化。完成后,应用程序 pexe被重写为一种稳定的不同二进制格式, 并且将由未来版本的PNaCl支持。最终确定步骤还有助于通过剥离调试信息和其他元数据来最小化应用程序的大小。
完成应用程序后,请确保在部署之前调整清单文件以引用应用程序的最终版本。该create_nmf.py
工具有助于生成.nmf
文件,但.nmf
文件也可以手动编写。
压缩pexe进行部署
大小压缩是部署的可选步骤,并且减小了必须通过线路传输的pexe文件的大小 ,从而提高了下载速度。该工具pnacl-compress
应用已构建到pexe 应用程序的稳定二进制格式的压缩策略。因此,压缩的pexe文件不需要任何额外的时间在客户端解压缩。致电时,所有费用都是预付的 。pnacl-compress
目前,该工具将压缩pexe文件约25%。但是,它有点慢(在大型应用程序上可能需要几秒到几分钟)。因此,此步骤是可选的。
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-compress
hello_world.final.pexe
pnacl-compress
必须在pexe文件完成部署(通过pnacl-finalize
)后调用。或者,您可以通过将--compress
标志添加到pnacl-finalize
命令行来 将此步骤作为最终步骤的一部分应用。
此压缩步骤不会替换为HTTP压缩配置的gzip压缩执行的Web服务器:两个压缩都是互补的。你要配置你的Web服务器gzip压缩pexe文件:压缩的gzip压缩版本pexe文件比相应的未压缩的更小 pexe 7.5%的文件到10%。
对象转储PNaCl bitcode文件
有时您可能会对PNaCl bitcode文件的内容感兴趣。工具pnacl-bcdis
对象转储PNaCl bitcode文件的内容。有关此工具生成的输出的说明,请参阅 PNaCl Bitcode文件的内容。
nacl_sdk/pepper_<version>/toolchain/win_pnacl/bin/pnacl-bcdis
hello_world.final.pexe
输出是给定pexe的相应内容。
基于GNU的工具链
除了PNaCl工具链之外,Native Client SDK还包括标准GNU工具链中工具的修改版本,包括GCC编译器和来自binutils的链接器和其他工具。这些工具仅支持构建nexe文件。使用--version
命令行标志运行该工具以确定工具的当前版本。
工具链中的每个工具都以目标体系结构的名称为前缀。在ARM目标体系结构的工具链中,每个工具的名称前面都有前缀“arm-nacl-”。在x86目标体系结构的工具链中,每个工具实际上有两个版本 - 一个用于为x86-32目标体系结构构建Native Client模块,另一个用于为x86-64目标体系结构构建模块。“i686-nacl-”是用于构建32位.nexes的工具的前缀,“x86_64-nacl-”是用于构建64位.nexes的工具的前缀。
这些前缀符合gcc命名标准,可以轻松使用autoconf等工具。例如,您可以使用i686-nacl-gcc
编译32位 .nexes,并x86_64-nacl-gcc
编译64位.nexes。请注意,您通常可以使用命令行标志覆盖工具的默认目标体系结构,例如,您可以指定x86_64-nacl-gcc -m32
编译32位 .nexe。
基于GNU的SDK工具链包括以下工具:
- <前缀> addr2line
- <前缀> AR
- <前缀>如
- <前缀> C ++
- <前缀> C ++ FILT
- <前缀> CPP
- <前缀>克++
- <前缀> GCC
- <前缀> GCC-4.4.3
- <前缀> gccbug
- <前缀>的gcov
- <前缀> gprof的
- <前缀> LD
- <前缀>纳米
- <前缀> objcopy把
- <前缀> objdump的
- <前缀> ranlib的
- <前缀> readelf
- <前缀>尺寸
- <前缀>串
- <前缀>条
编译
使用基于GNU的工具链编译文件类似于使用基于PNaCl的工具链编译文件,除了输出是特定于体系结构的。
例如,假设您正在Windows机器上进行开发,以x86架构为目标,您可以使用以下命令为hello_world示例编译32位.nexe:
nacl_sdk/pepper_<version>/toolchain/win_x86_glibc/bin/i686-nacl-gcc
hello_world.c -Inacl_sdk/pepper_<version>/include
-Lnacl_sdk/pepper_<version>/lib/glibc/Release -o hello_world_x86_32.nexe
-m32 -g -O2 -lppapi
要编译64位.nexe,可以运行相同的命令,但使用-m64而不是-m32。或者,您也可以使用针对x86-64体系结构的编译器版本,即x86_64-nacl-gcc
。
无论您使用什么平台,都应该命名具有.nexe文件扩展名的可执行模块。
创建库和链接
创建库并与基于GNU的工具链链接类似于使用PNaCl工具链进行相同操作。用于创建静态库的相关工具是<prefix>ar
和<prefix>ranlib
。链接可以完成<prefix>g++
。有关 如何创建共享库的信息,请参阅 动态链接和加载glibc部分。
最后确定nexe部署
与PNaCl工具链不同,nexe文件不需要单独的完成步骤。该nexe文件常常是一个稳定的格式。但是,nexe文件可能包含调试信息和符号信息,这可能使nexe文件大于分发所需的文件。要最小化分布式文件的大小,可以运行该<prefix>strip
工具以去除调试信息。
使用make
本文档未介绍如何使用make
,但如果要用于 make
构建Native Client模块,则可以将Makefile基于SDK示例中的Makefile。
SDK示例的Makefiles构建了多个配置中的大多数示例(使用PNaCl与NaCl,使用不同的C库,针对不同的体系结构,并使用不同的优化级别)。以选择特定的工具链中,设置环境变量 TOOLCHAIN
要么pnacl
,clang-newlib
,glibc
,或host
。要选择最优化的特定级别设置环境变量 CONFIG
要么Debug
,或Release
。make
在每个示例的目录中运行 会执行以下操作之一,具体取决于环境变量的设置。
-
如果
TOOLCHAIN=pnacl
创建一个名为的子目录pnacl
;- 使用newlib库构建.pexe(与架构无关的Native Client可执行文件)
- 为示例的pnacl版本生成Native Client清单(.nmf)文件
-
如果
TOOLCHAIN=clang-newlib
创建一个名为的子目录clang-newlib
;- 使用nacl-clang工具链和newlib C库为x86-32,x86-64和ARM体系结构构建.nexes
- 为示例的clang-newlib版本生成Native Client清单(.nmf)文件
-
如果
TOOLCHAIN=glibc
创建一个名为的子目录glibc
;- 使用glibc库为x86-32,x86-64和ARM体系结构构建.nexes
- 为示例的glibc版本生成Native Client清单(.nmf)文件
-
如果
TOOLCHAIN=host
创建一个名为windows
,,linux
或mac
(取决于您的开发机器)的子目录;- 使用开发机器上的托管工具链构建一个Pepper插件(.dll用于Windows,.so用于Linux / Mac)
- 为示例的主机Pepper插件版本生成Native Client清单(.nmf)文件
glibc库尚不适用于ARM和PNaCl工具链。
以下是如何在Windows上以发布模式使用PNaCl构建示例。生成的文件examples/api/audio
将在其中examples/api/audio/pnacl/Release
,并且目录布局与其他示例类似。
set TOOLCHAIN=pnacl
set CONFIG=Release
make
您的Makefile可以更简单,因为您可能不希望构建模块的这么多不同配置。Makefiles示例在顶部(例如CFLAGS
)附近定义了许多变量,这使得可以轻松自定义为项目执行的命令以及每个命令的选项。
有关如何使用make的详细信息,请参阅GNU'make'手册。
随SDK提供的库和头文件
Native Client SDK包括标准工具链支持库的修改版本,例如libpthread和libc,以及相关的头文件。标准库位于/pepper_<version>
以下位置的目录下:
- PNaCl工具链:
toolchain/<platform>_pnacl/usr/lib
- x86工具链:
toolchain/<platform>_x86_<c_library>/x86_64-nacl/lib32
和/lib64
(分别用于32位和64位目标体系结构) - ARM工具链:
toolchain/<platform>_arm_<c_library>/arm-nacl/lib
例如,在Windows上,glibc工具链中的x86-64体系结构的库位于toolchain/win_x86_glibc/x86_64-nacl/lib64
。
头文件位于:
- PNaCl工具链:
toolchain/<platform>_pnacl/le32-nacl/include
- clang newlib工具链:
toolchain/<platform>_pnacl/<arch>-nacl/include
- x86 glibc工具链:
toolchain/<platform>_x86_glibc/x86_64-nacl/include
- ARM glibc工具链:
toolchain/<platform>_arm_glibc/arm-nacl/include
许多其他库已移植到Native Client使用; 有关更多信息,请参阅webports 项目。如果您移植开源库供自己使用,我们建议您将其添加到Webports。
除标准库外,SDK还包含Pepper库。PNaCl Pepper库位于nacl_sdk/pepper_<version>/lib/pnacl/<Release or Debug>
目录中。基于GNU工具链有辣椒图书馆nacl_sdk/pepper_<version>/lib/glibc_<arch>/<Release or Debug>
和nacl_sdk/pepper_<version>/lib/clang-newlib_<arch>/<Release or Debug>
。SDK提供的库允许应用程序使用Pepper以及便捷库来简化移植使用POSIX功能的应用程序。以下是SDK中提供的Pepper库的说明。
libppapi.a
实现Pepper(PPAPI)C接口。所有使用Pepper(甚至是C ++应用程序)的应用程序都需要。
libppapi_cpp.a
实现Pepper(PPAPI)C ++接口。需要使用Pepper的C ++应用程序。
libppapi_gles2.a
实现Pepper(PPAPI)GLES接口。需要使用3D图形API的应用程序。
libnacl_io.a
为NaCl提供POSIX层。特别是,该库提供了一个虚拟文件系统并支持套接字。虚拟文件系统允许模块“挂载”给定的目录树。一旦一个模块安装在一个文件系统,它可以使用标准C库文件操作: fopen
,fread
,fwrite
,fseek
,和fclose
。有关更多详细信息,请参阅标题include/nacl_io/nacl_io.h
。有关如何使用nacl_io的示例,请参阅examples/demo/nacl_io_demo
。
libppapi_simple.a
通过让模块具有简单的main()
入口点来提供熟悉的C编程环境。入口点类似于标准C main()
函数,带有argc
和argv[]
参数。详情见include/ppapi_simple/ps.h
。有关如何使用ppapi_simple的示例,see examples/tutorial/using_ppapi_simple
。
- 由于Native Client工具链使用自己的库和头搜索路径,因此工具将找不到您在非Native-Client开发中使用的第三方库。如果要使用特定的第三方库进行Native Client开发,请在Webports中查找,或自行移植库。
- 在构建命令中列出库的顺序非常重要,因为链接器按照指定的顺序搜索和处理库。有关
*_LDFLAGS
应列出特定库的顺序,请参阅SDK示例的Makefile中的变量。
故障排除
一些常见问题,以及如何解决它们:
“未定义的引用”错误
“未定义的引用”错误可能表示链接顺序不正确和/或缺少库。例如,如果-lppapi
在编译Pepper应用程序时遗漏,您将看到一系列未定义的引用错误。
一种常见类型的“未定义引用”错误是针对某些系统调用,例如“未定义引用'mkdir'”。出于安全原因,Native Client不支持许多系统调用。根据代码使用此类系统调用的方式,您有以下几种选择:
- 与
-lnosys
标志链接以提供不受支持的系统调用的空/始终失败版本。这至少会让你超越链接阶段。 - 查找并删除不受支持的系统调用的使用。
- 创建自己的不受支持的系统调用实现,以便为您的应用程序执行一些有用的操作。
如果您的代码使用mkdir或其他文件系统调用,您可能会发现 nacl_io库很有用。nacl_io库基本上为您做了选项(3):它允许您的代码使用类似POSIX的文件系统调用,并使用各种技术实现调用(例如,HTML5文件系统,使用URL加载器的只读文件系统,或者-memory filesystem)。
找不到包含必要符号的库
以下是为给定符号查找适当库的一种方法:
nacl_sdk/pepper_<version>/toolchain/<platform>_pnacl/bin/pnacl-nm -o
nacl_sdk/pepper_<version>toolchain/<platform>_pnacl/usr/lib/*.a |
grep <MySymbolName>
PNaCl ABI验证错误
PNaCl对bitcode支持的内容有限制。在翻译并在浏览器中运行之前,有一个bitcode ABI验证程序可以检查应用程序是否符合ABI限制。但是,最好避免用户的运行时错误,因此验证程序也会在链接时在开发人员的计算机上运行。
例如,以下使用128位整数的程序将使用NaCl GCC编译x86-64目标。但是,它不可移植,不能用NaCl GCC编译i686目标。使用PNaCl,它将无法通过ABI验证程序:
typedef unsigned int uint128_t __attribute__((mode(TI)));
uint128_t foo(uint128_t x) {
return x;
}
使用PNaCl,您将在链接时收到以下错误:
Function foo has disallowed type: i128 (i128)
LLVM ERROR: PNaCl ABI verification failed
遇到PNaCl ABI验证错误时,请检查PNaCl不支持的功能列表。如果您遇到的问题未列为限制,请 告知我们!
CC-By 3.0许可下提供的内容