土法炼钢兴趣小组的算法知识备份

【开源许可与版权工程】GPLv2、GPLv3、LGPL:Linux 内核为什么停在 v2

文章导航

分类入口
architectureopensource
标签入口
#opensource#license#GPL#GPLv2#GPLv3#LGPL#Linux#Tivoization#copyleft#kernel#compliance

目录

GPL 版本演进与主要变化

如果把开源许可证分成两大阵营,一边是「宽松许可证」(Permissive License,代表是 MIT 与 Apache 2.0),另一边就是以 GPL(GNU General Public License)为核心的「著佐权」(Copyleft)阵营。宽松许可证的世界里,大家更关心「我能不能拿走用」;而 Copyleft 的世界里,更关心「我拿走以后,下游的用户还能不能拿回去改」。

GPL 是一套已经跑了近四十年的工程合同。它经历了 v1、v2、v3 三个主要版本,加上 LGPL(Lesser GPL)这一条分支。每一次版本升级都不是简单的措辞修订,而是对一类真实工程事件的回应:AT&T 的 Unix 私有化、Tivo 的硬件签名验证、Microsoft-Novell 的专利交叉许可、DRM(Digital Rights Management,数字版权管理)的泛滥。

然而最有意思的一个工程事实是:作为 Copyleft 世界最重要的「旗舰产品」,Linux 内核至今仍然锁在 GPLv2 上,明确拒绝升级到 GPLv3。这个选择不是惰性,而是一次非常清醒的工程取舍——它直接决定了今天全球几十亿台嵌入式设备、Android 手机、路由器、交换机、车机、智能家电的底层合规边界。

这篇文章会把 GPLv2、GPLv3、LGPL 三份许可证的核心条款放在一起做一次彻底的对照,重点解释 Tivoization(Tivo 化,指通过硬件限制规避 GPL 精神的做法)到底是什么,为什么 Linus Torvalds 公开把它当成内核升级 v3 的主要障碍,以及 LGPL 的「链接例外」在动态链接与静态链接、Java 字节码、Android NDK 这些现代场景下到底怎么落地。最后会用一节专门讨论国内路由器厂商、华为、小米等公司在 GPL 合规上的真实实践。


一、GPL 的哲学起点

1.1 Richard Stallman 与 GNU 宣言

要理解 GPL,必须先理解它背后的人与时代。1983 年,Richard Stallman(RMS,理查德·斯托曼)在 MIT 人工智能实验室发起了 GNU(GNU’s Not Unix,递归缩写)项目,目标是写出一整套与 Unix 兼容、但完全自由的操作系统。1985 年,他发布了《GNU 宣言》(GNU Manifesto),正式把「自由软件」(Free Software)作为一种工程与社会运动提出。

《GNU 宣言》里最核心的一段话,奠定了后续所有 GPL 条款的哲学基础:

“GNU, which stands for Gnu’s Not Unix, is the name for the complete Unix-compatible software system which I am writing so that I can give it away free to everyone who can use it.”

这里的「free」不是价格免费,而是「自由」——后来在 FSF(Free Software Foundation,自由软件基金会)的文档里被明确拆成了「四大自由」(Four Freedoms),并编号为 0 到 3:

这四条自由合在一起的工程含义是:用户不但要能用软件,还要能控制软件。而控制权在工程上等价于源代码获取权 + 修改权 + 再分发权。

1.2 Copyleft(著佐权)的核心思想

Stallman 在推动 GNU 项目时很快遇到一个现实问题:如果他写的工具只是把源代码放出来,任何一家公司都可以拿去改一改,打包成专有产品卖给用户,而用户拿到的版本是没有源代码的。这样「四大自由」在用户端就被剥夺了。

他的回应是一个在法律形式上非常漂亮的反转:既然版权法(Copyright)天然给了作者禁止别人复制修改的权力,那我就用这个权力去强制要求「任何基于我代码的衍生作品,在分发时也必须给予用户同样的自由」。这个思路被称为 Copyleft(著佐权,刻意与 Copyright 对仗),GPL 就是它的法律载体。

从工程角度理解,Copyleft 是一种「传染性约束」:

原始作者 A → GPL → 衍生作者 B → 必须也是 GPL → 衍生作者 C → 必须也是 GPL → ...

任何一个下游节点都无法把许可证改成比 GPL 更严格(比如闭源专有),因为上游作者保留了版权,他以「你要用我代码就必须接受 GPL」作为前提授予你使用权。一旦你违反 GPL(比如不公开修改后的源代码),你对原作者代码的使用权就自动终止,你的产品会立即处于版权侵权状态。

1.3 为什么会有 GPL:打印机驱动与 AT&T Unix 事件

GPL 并不是凭空写出来的。它诞生于两个具体的工程事件:

第一个是「打印机驱动事件」。Stallman 在 MIT 实验室时,实验室收到一台 Xerox 的新打印机,但新机器的驱动源代码不再像老机器那样可以拿到。他希望改一下驱动,让打印机在卡纸时自动给用户发消息——这是一个非常合理的本地修改。但 Xerox 拒绝提供源代码,理由是 NDA(Non-Disclosure Agreement,保密协议)。Stallman 事后在多次演讲中把这个事件描述成他走向自由软件运动的转折点:当软件不再能被用户修改,用户对机器的控制权就被剥夺了。

第二个是 AT&T Unix 的封闭化。Unix 起源于 AT&T 贝尔实验室,早期因为反垄断判决,AT&T 不能把软件作为商品销售,因此 Unix 源代码被以几乎象征性的价格授权给了大学和研究机构。到 1980 年代,AT&T 被拆分后摆脱了反垄断限制,开始把 Unix 商业化,对 BSD(Berkeley Software Distribution,伯克利软件分发版)提起诉讼,源代码许可费暴涨。这直接威胁到了整个 Unix 学术生态。

GNU 项目和 GPL 的答案是:与其寄希望于某家公司愿意开放源代码,不如建立一个法律上自我保护的替代生态——任何一段 GPL 代码,都会以法律形式永远留在公共可控的范围内。

1.4 「病毒式许可证」的事实与神话

商业公司内部,GPL 经常被描述成「病毒式许可证」(viral license)。这个说法有一部分是事实,有一部分是神话。

事实部分:GPL 确实对「衍生作品」(derivative work)具有强制传播性。如果你把 GPL 代码整合到你自己的程序里,形成一个整体,这个整体作为衍生作品在分发时必须也以 GPL 分发。

神话部分

理解这些边界非常重要,因为很多公司内部 OSPO(Open Source Program Office,开源项目办公室)的政策其实是过度保守的,把「不允许使用任何 GPL 代码」当成默认规则,错过了很多合规可用的场景。

1.4.1 Free Software 与 Open Source 的术语分歧

这里还需要澄清一组容易混淆的术语:Free Software(自由软件)与 Open Source Software(开源软件)。

1998 年 2 月,Eric Raymond、Bruce Perens 等人在一次会议上决定用 “Open Source” 这个新术语替代 “Free Software”。原因很直接:Free 在英语里歧义太大,“free beer” 的联想让商业世界对这个运动心存戒备。他们希望用一个中性的、工程色彩更强的词重新定位运动。同年 OSI(Open Source Initiative,开源促进会)成立,制定了 Open Source Definition(OSD),并建立了许可证审核流程。

Stallman 对这个改名强烈反对。他至今坚持用 Free Software 一词,写了专门的文章 “Why Open Source misses the point of Free Software”。他认为 Open Source 把运动从「社会正义运动」降级成了「更好的软件开发方法学」,丢掉了核心的伦理承诺。

工程上两者的外延大致重合:OSI 批准的几乎所有许可证,FSF 也会承认为 Free Software;反之亦然。但在法律文件里两个术语偶尔会精确指向不同。本文大部分地方用「开源」以求通用,讨论 GPL 哲学起源时使用「自由软件」以忠于原文语境。


二、GPLv2(1991)核心条款解读

GPLv2 是 1991 年 6 月发布的,它是 GPL 真正成为工业标准的版本。今天你在绝大多数 Linux 发行版、U-Boot、BusyBox、QEMU 的源代码头部,还能看到它的身影。我们逐节来看它的关键条款。

2.1 Section 1 与 Section 2:分发义务

Section 1 处理的是「逐字复制」(verbatim copies):

You may copy and distribute verbatim copies of the Program’s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

翻译成工程语言:如果你只是原样转发 GPL 代码,你必须保留所有原有的版权声明、许可声明、免责声明,并且把 GPL 许可证本身的副本一并交给下游。这一条看起来很简单,但它解释了为什么每一个 Linux 发行版的安装介质里都必须带一份 /usr/share/common-licenses/GPL-2

Section 2 处理修改后的版本,这才是 Copyleft 的核心:

You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions…

它列出了三个附加条件:

其中 (b) 是整个 Copyleft 的「神之一手」。它使用了一个措辞非常宽泛的短语:「work based on the Program」(基于程序的作品)。GPLv2 对这个概念的定义是:

“The ‘Program’, below, refers to any such program or work, and a ‘work based on the Program’ means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language.”

这里最重要的一点是:GPLv2 把「衍生作品」的判断完全委托给了版权法。这在工程上带来了一个长期的模糊区:静态链接、动态链接、dlopen、RPC、IPC,哪些构成衍生作品?FSF 的立场是「静态链接和动态链接都是衍生作品」,但这个立场并没有经过主流法院的裁决检验。后面的 LGPL 正是在这个模糊区里画了一条更清晰的线。

2.2 Source Code 义务的三种形式

GPLv2 Section 3 规定了当你分发的是可执行二进制时,如何履行源代码义务。它给出了三种可选方式:

Option A:随二进制一起,附带完整机器可读源代码。分发介质必须是
          通常用于软件交换的介质(磁盘、光盘、现在是 U 盘或网盘)。

Option B:随二进制附带一份「书面要约」(written offer),有效期
          至少 3 年,允许任何第三方按不超过实际介质成本的费用索
          取完整的机器可读源代码。

Option C:将你从上游收到的书面要约原样转交给下游。仅允许非商业
          分发者使用此选项。

对嵌入式设备厂商来说,Option B 是实际的主流做法:路由器、机顶盒、监控摄像头的固件里不可能塞进几百兆的源代码,所以厂商会在说明书或官网上提供一个 GPL 源代码下载入口,并承诺 3 年内按成本价邮寄光盘。这也是为什么你在一些老路由器的说明书附页里会看到 “GPL Source Code Offer” 的英文段落。

但这里有两个容易踩坑的点:

2.3 Section 7:Liberty or Death 条款

Section 7 是 GPLv2 里最戏剧性的条款之一,社区常称之为 “Liberty or Death”(不自由毋宁死)条款:

If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all.

翻译过来是:如果有任何外部约束(法院判决、专利授权协议、第三方合同)使你无法同时履行 GPL 和那个外部约束,那么结论是——你就不能分发这个程序。GPL 不会自动给你让路。

这个条款是专门为了应对「专利敲诈」和「选择性授权」设计的。举例:某公司 A 在产品里用了 GPLv2 的 foo 库,然后被公司 B 起诉侵犯了某个专利。B 愿意给 A 一份专利许可,但这份许可只覆盖 A 自己使用,不包括 A 的下游用户。此时 A 如果继续分发 foo,就是「从下游用户那里剥夺了他们自由修改和再分发的专利权」,违反了 GPL。Section 7 告诉 A:对不起,你要么把专利问题解决到可以扩展给所有下游用户,要么就停止分发。

Section 7 是一个典型的「硬规则」(hard rule):它宁可让 GPL 代码被下架,也不允许部分用户享受比其他人更少的自由。

2.3.1 免责声明与责任限制(Sections 11-12)

Section 11 和 12 是 GPLv2 里不太被讨论、但法律上极关键的两段。它们以全大写的方式写着:

11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
    WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
    ...THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND...

12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
    MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
    LIABLE TO YOU FOR DAMAGES...

工程含义:GPL 软件以「AS IS」(按现状)方式提供,作者和再分发者对任何损失都不承担责任,除非当地法律强制规定另有要求或双方另有书面协议。这条是所有开源许可证的标配,也是为什么你不能以「上游 bug 导致我们生产事故」为由直接起诉内核开发者。

但这条在某些大陆法系管辖区(包括中国、德国的消费者保护法)的执行力并不是 100%。德国法院历史上在消费者场景下要求即使是免费软件也有最低限度的瑕疵担保。这也是为什么许多商业 Linux 发行版(Red Hat、SUSE、OpenAnolis 商业版)会在 GPL 之上再叠加一份商业服务合同——GPL 拿走责任,商业合同再把责任部分拿回来以换取订阅费。

2.4 「or later」版本条款的工程含义

GPLv2 里还有一个看起来像脚注、其实影响极深远的条款。FSF 建议所有使用 GPL 的项目在版权声明里加上这样一句话:

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

注意最后那句 “or (at your option) any later version”。它给了最终用户一个选择权:你可以按 GPLv2 使用,也可以按 GPLv3、v4、v5…… 的任何一个未来版本使用。这个措辞在后来被称为「or later clause」或 “GPL-2.0-or-later”。

Linux 内核在这里做了一个非常重要、但很多人没有注意到的选择。你去看内核仓库根目录的 COPYING 文件,Linus 在头部明确写了一段话:

  Also note that the only valid version of the GPL as far as the kernel
  is concerned is _this_ particular version of the license (ie v2, not
  v2.2 or v3.x or whatever), unless explicitly otherwise stated.

这段话用非常口语化的英语做了一件极严肃的事:把内核代码锁死在 GPL v2,而不是 v2 or later。用 SPDX(Software Package Data Exchange)的标准化许可证标识符表达,就是:

Linux 内核是 GPL-2.0-only。这个决定意味着:将来哪怕 FSF 发布了 GPLv4、GPLv5,内核也不会自动升级过去;想要升级必须得到内核历史上所有版权持有人(至少是现存代码的绝大多数版权持有人)的同意,这在工程上几乎是不可能完成的任务。

这一节是整篇文章后面所有讨论的起点。记住它:Linux 内核 = GPL-2.0-only,没有 or later。

2.5 GPLv2 中的「distribute」「copy」「modify」三动词

GPLv2 的文本里反复出现三个动词:copy(复制)、distribute(分发)、modify(修改)。工程师读条文时应该把这三个动词当成状态机的三种转换动作:

       持有                    持有(修改过)
  +----------+   modify   +----------------+
  | original | ─────────> | modified copy  |
  +----------+            +----------------+
       │                           │
       │ copy & distribute         │ copy & distribute
       ▼                           ▼
  +-----------+             +-----------------+
  | 下游用户 A |             | 下游用户 B      |
  +-----------+             +-----------------+

GPLv3 把 distribute 改成了 convey,并在定义中明确「propagation that enables other parties to make or receive copies」才是 convey。内部备份、版本控制、运行程序本身都不是 convey。这个清晰化是 GPLv3 对 GPLv2 的一大语言改进——后者的 distribute 在欧洲某些语言里曾引发过翻译歧义。

记住这个三动词模型,后面所有「我这样做会不会触发 GPL 传染」的问题都能先问一句:「我是否对第三方做了 convey?」如果答案是否,多半没事。

2.6 GPLv2 与商业软件的历史共存

一个常被忽视的事实是:GPLv2 时代的 Linux 商业生态运转得相当成功。Red Hat 1999 年上市,成为第一家上市的开源公司;SUSE、Mandriva、TurboLinux 等发行版在 2000 年代初构成了完整的商业生态;IBM 在 2000 年宣布投入 10 亿美元到 Linux,大量内部工具以 GPLv2 发布。

这些商业成功证明 GPLv2 与商业使用不但不冲突,反而是开源商业化的主力许可证。商业价值从哪里来?

这套经济模型也是 Linus 反对 GPLv3 的一个隐含背景:GPLv2 时代的商业生态已经证明了 Copyleft 与商业化可以共存,他不希望 GPLv3 的更严格条款打破已经运转良好的生态。「If it ain’t broke, don’t fix it」——这是非常工程师的判断方式。


三、GPLv2 到 GPLv3(2007)的变化

GPLv3 是 2007 年 6 月 29 日发布的。从 1991 到 2007,整整 16 年。FSF 为什么等了这么久才升级?因为这 16 年里软件行业发生了太多 GPLv2 当年没有预见的事:TiVo 的硬件签名、Microsoft 的专利收费、DRM 在消费电子里的普及、互联网服务取代盒装软件的商业模式转变。GPLv3 就是对这些新现实的集中回应。

3.1 为什么要写 GPLv3

FSF 与律师 Eben Moglen 在 GPLv3 的公开草案流程中列出了四个主要动机:

  1. Tivoization:TiVo 公司使用 GPLv2 的 Linux 内核构建 DVR(Digital Video Recorder,数字视频录像机),并按 GPLv2 要求公开了源代码,但设备的引导加载器(bootloader)会验证内核签名,如果你用修改后的内核重新编译,哪怕源代码和官方完全一样,只要签名不匹配,设备就拒绝启动。结果是「用户有源代码,但没有修改后运行的自由」。FSF 认为这违反了 Freedom 1 的精神。
  2. 专利问题:2006 年 11 月,Microsoft 与 Novell 签订了一份专利互保协议,大意是 Microsoft 承诺不起诉 Novell 的 SUSE Linux 客户专利侵权。这被社区视为对 GPL 精神的釜底抽薪:Microsoft 实际上是在用「保护部分用户」的方式制造法律歧视。
  3. DRM 与 TPM(Trusted Platform Module,可信平台模块):HDCP、CSS、AACS 这类数字版权管理技术越来越普遍,而 DMCA(Digital Millennium Copyright Act,数字千年版权法)的第 1201 条把「规避 DRM」入罪。FSF 担心 GPL 软件被绑进 DRM 链条后,用户的「修改权」会被一纸刑法冻结。
  4. 国际化:GPLv2 的措辞以美国版权法为默认语境,在欧洲大陆法系国家执行时经常撞到术语差异。GPLv3 显著清理了这类问题。

3.1.1 一次 GPLv3 草案流程的观察

GPLv3 的起草过程本身也值得一提——它是开源许可证历史上第一次大规模的「公开立法」。FSF 和律师 Eben Moglen 从 2006 年 1 月到 2007 年 6 月,发布了四份草案(Discussion Draft 1 到 4),每份草案都配有逐条公开评论界面(gplv3.fsf.org 当时的评注工具)。社区成员可以选中某段文字并发表意见,FSF 收到意见后在下一份草案回应。

这次流程的产物不仅是 GPLv3,还包括 AGPLv3、LGPLv3。它在工程上验证了一件事:法律文本可以像代码一样经历版本控制和 code review。今天 Apache 基金会、CNCF、Linux Foundation 的许多政策文档(包括 DCO、CLA 模板)都借鉴了这种流程。

3.2 Section 6:反 Tivoization 的 Installation Information

GPLv3 最具争议的改动在 Section 6,它引入了「安装信息」(Installation Information)这个新概念:

“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

核心要求:如果你分发的是「用户产品」(User Product,粗略地说就是消费者购买的个人设备),你不仅要给出对应的源代码,还必须给出足以让用户「安装并运行修改后版本」的所有信息——包括签名密钥、解锁方式、烧录流程。

举个例子,假设一家厂商 X 做了一个基于 GPLv3 软件的智能音箱,固件由 bootloader 验证 RSA 签名。X 要合规,就必须:

这个要求在消费电子厂商眼里几乎不可接受:它意味着你不能再用硬件信任链来锁死设备。反过来 FSF 认为:如果你做的是「用户产品」,用户就应该真正拥有它,而不是「拥有一个你赐予的功能集」。

实际案例走查——假设你要做一款 GPLv3 合规的智能灯泡:

硬件:ESP32-S3 + Wi-Fi 模组
软件:
  - GPLv3 MQTT 客户端库
  - 专有语音识别模块(你自己写的)
  - bootloader:基于 U-Boot(GPLv2,不是 v3)

合规动作:
  1. 公开 MQTT 库的源代码(含你的 patch) ← 基本 GPL 义务
  2. 提供 Installation Information:
     a. OTA 签名公钥/私钥对?或者把签名禁用?
     b. 刷机工具、串口协议、分区表文档
     c. 如何把你自己编译的固件烧进设备并启动
  3. 保留书面要约 3 年
  4. 注意:U-Boot 是 GPLv2,Section 6 不适用;
     但 MQTT 库是 GPLv3,其「User Product」级别的要求
     仍然作用于整机(因为组合作品要同时满足所有组件
     许可证的要求)。

这套合规动作做下来,等于你必须把设备彻底「开放」给用户自行刷写。对消费级厂商,这往往是一个难以接受的产品决策,因此大部分商业厂商的做法是——彻底避开 GPLv3 组件,选用 Apache 2.0 或 MIT 的等价库。Mosquitto(MQTT broker)有 EPL 1.0 或 EDL 1.0 双许可就是这种市场需求驱动的产物。

Section 6 对「用户产品」的定义非常谨慎,它只覆盖:

这意味着企业级服务器、路由核心设备、医疗专用设备等不一定落入这个条款。但对智能电视、手机、家用路由器、车机来说,Section 6 是一个硬门槛。

3.3 Section 3:DRM 条款

GPLv3 Section 3 处理 DRM 问题:

No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

这条在法律技巧上非常聪明。它的意思不是「GPL 禁止 DRM」——许可证本身无法禁止技术手段,而是:GPL 软件一旦被纳入 DRM 系统,作者事先声明这段代码不视为 WIPO 版权公约第 11 条(以及 DMCA 第 1201 条)意义上的「有效技术保护措施」。结果是:任何绕过它的人不构成「规避 DRM」,刑事和民事责任被事先排除。

“When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License…”

通俗说:你分发 GPLv3 代码的时候,就同时放弃了用 DMCA 去追究逆向工程者的权利。

3.4 Section 11:专利授权与报复

GPLv3 Section 11 是对 GPLv2 Section 7 的升级。GPLv2 只规定「如果专利阻止你履行 GPL,你就不许分发」,但它没有正面给出专利许可。GPLv3 填上了这一块。

主要内容可以拆成三部分:

  1. 明示专利授权(express patent grant)。每一个贡献者在贡献代码时,自动向所有下游用户授予一份「必要专利」(essential patent claims)的非独占、全球、免版税许可,覆盖该贡献代码及其衍生作品的使用、修改、分发。这与 Apache 2.0 的 Section 3 非常相似。
  2. 专利报复(patent retaliation)。如果你对 GPLv3 软件提起专利诉讼(声称该软件侵犯了你的某项专利),你自己对这份软件的许可自动终止。
  3. Microsoft-Novell 漏洞关闭。Section 11 专门加了一段,说如果你与第三方签订了「歧视性」(discriminatory)专利协议——即协议只保护部分用户而不是全体用户——你就不得分发该程序。这一段在社区里被戏称为 “Novell clause”,因为它直接把 2006 年那笔交易的套路堵死了。

Section 8 和 Section 11 还共同规定了「终止恢复」(termination cure)机制:违反 GPLv3 的人,如果在 30 天内纠正行为并通知版权持有人,原始许可可以被恢复。这是 GPLv2 没有的——GPLv2 里一旦终止就是永久终止,除非版权持有人主动原谅。这个改动被 SFC 多次引用,因为它让「合规事后补救」在 v3 项目里有了法律依据。

3.4.1 专利条款在现实中的触发场景

Section 11 的三部分条款在现实里是怎么被触发的?给几个典型模式:

模式一:贡献者隐藏专利。A 公司把一段代码贡献到某 GPLv3 项目,但 A 公司同时持有一项覆盖这段代码算法的专利。用户 B 按 GPLv3 合法使用该项目,A 公司起诉 B 侵犯专利。后果:

模式二:收购后的专利翻盘。公司 C 收购了开源贡献者 D。D 的专利组合移交给 C,C 想对 D 的历史贡献主张专利。Section 11 的文字把授权绑定在「conveyor」身上,D 贡献代码时作为 conveyor,许可已发出,C 作为受让人受该许可约束。这一条在工程上非常重要,它防止了「收购式专利围猎」。

模式三:标准必要专利(SEP)。某视频编解码算法被 MPEG-LA 列为 SEP,同时某 GPLv3 项目实现了该算法。实现方如果自己持有相关专利,授权发出没问题;但项目里可能间接使用到其他第三方的 SEP,这部分 Section 11 覆盖不到。这是 FFmpeg 等项目长期采用 LGPL 而非 GPL 的原因之一:LGPL 的宽松链接允许下游按需自行选择编解码组件和专利策略。

3.5 Section 7:附加权限与许可证兼容性

GPLv3 Section 7 引入了「附加权限」(Additional Permissions)机制:

“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions.

它允许项目作者在不修改 GPL 正文的前提下,给下游再附加一些例外。最典型的例子是 GCC 的 Runtime Library Exception:GCC 运行时库(libgcc、libstdc++)本体是 GPLv3,但带一个附加例外,允许你用 GCC 编译出的程序以任意许可证发布,而不需要连带把 libgcc 一并 GPL。这是让 GCC 能作为通用工具链的关键设计。

Section 7 对 Apache 2.0 兼容性的贡献也很大。Apache 2.0 里有专利终止条款和专利授权条款,这些条款与 GPLv2 不兼容(GPLv2 不允许额外施加不在 GPL 里的条件)。但 GPLv3 Section 7 专门把「专利报复」「免责声明」这些额外条款列为允许的附加条件。结果是:GPLv3 + Apache 2.0 可以单向兼容(Apache 代码可以整合进 GPLv3 项目),而 GPLv2 + Apache 2.0 则不行。

3.6 其他变化

除了上述几个重磅条款,GPLv3 还做了一系列工程上有意义的调整:

这些变化加在一起,使 GPLv3 从一份朴素的 Copyleft 契约变成了一份覆盖硬件、专利、DRM、国际法的综合性工程合同。问题是:这份合同要求的东西,对某一类重要使用者来说太多了。那个使用者就是——Linux 内核。


四、Linus 拒绝升级到 v3 的原因

Linus Torvalds 对 GPLv3 的态度在 2006 年草案发布后迅速明朗,而且从未软化。他在多次 LKML(Linux Kernel Mailing List)邮件、公开演讲和媒体访谈里反复说过同一件事:Linux 内核将永远留在 GPLv2,理由不是 v3 写得不好,而是 v3 不适合内核想做的事。

4.1 Linus 的公开立场

Linus 在 2007 年接受访谈时有一段被反复引用的话:

“I think version 3 is a fine license for the FSF’s goals. But it’s not the license I want for my kernel.”

“for my kernel” 四个字是关键。Linus 从不反对 FSF 的哲学本身,他承认 GPLv3 对消费者自由的保护很完备;但他认为内核的定位不是「消费者自由的载体」,而是「任何人——包括希望锁住硬件的公司——都可以放心使用的基础设施」。

在另一封更长的邮件里,Linus 把反 Tivoization 称为他的「主要反对点」(main objection):

“I think it is insane to require people to make their private signing keys available, for example. I wouldn’t do it. So I don’t think the GPLv3 is as good a license as v2.”

他认为:硬件制造商在自己卖的硬件上用签名防止用户刷机,这是硬件制造商的正当商业决定;用软件许可证去强制他们放弃这项技术手段,是把软件自由的边界推到了硬件之上,超出了许可证该干的事。

Linus 还提过一个非常工程师气质的论点:GPLv2 的「tit for tat」(投桃报李)规则已经足够好——你用我的代码,你就分享你的修改,至于你拿修改后的代码做什么硬件限制,那是硬件和用户之间的事,与代码许可无关。

4.1.1 Tivo 的签名机制技术细节

为了理解 Linus 的反对意见,需要先看看 TiVo 当年具体在技术上做了什么。TiVo 的 DVR 设备(以 TiVo Series 2 为代表)使用一块基于 PowerPC 的 SoC,内部是一颗定制的 Broadcom 芯片。启动流程大致是:

Power on
    ↓
Boot ROM(芯片内掩膜,不可修改)
    ↓
读取闪存里的 stage-1 bootloader,校验签名
    ↓
stage-1 加载 kernel + initrd,再次校验签名
    ↓
kernel 启动,自检自身签名(双保险)
    ↓
如果任何一步签名失败 → 硬件进入安全模式,拒绝启动

TiVo 用的内核就是 GPLv2 的 Linux。TiVo 按规矩公开了完整源代码——包括所有 patch、.config、构建脚本。从 GPLv2 字面义务看,TiVo 完全合规。

但 FSF 的观点是:TiVo 做了一件「符合文字、违反精神」的事。你可以拿到源代码,也可以修改编译,但你把编译结果烧进 TiVo 设备 → 启动失败,因为签名不是 TiVo 的。Freedom 1(修改权)在代码层面存在,在设备层面消失。

Linus 的反驳:设备是 TiVo 的硬件,TiVo 有权决定它运行什么。GPL 的目标是保护代码不被圈地,不是保护「硬件所有权」这件事。如果你不满意 TiVo 设备的限制,别买。他反对把软件许可的权限扩张到硬件供应链上。

这场争论没有对错,它是两种不同的「软件自由边界」哲学。GPLv3 站在 FSF 一侧,Linux 内核站在 Linus 一侧。

4.2 嵌入式生态的工程影响

Linus 的立场背后有巨大的现实动力:嵌入式领域。把 GPLv3 套到嵌入式生态上,会发生什么?

看一下全球路由器市场的主流厂商:TP-Link、Netgear、ASUS、D-Link、华为、小米、Ubiquiti。他们几乎全部基于 Linux 内核 + BusyBox + 各家自定义用户态组件。他们的固件链条大致是这样:

[SoC 厂商 BSP(Board Support Package)]
       ↓
[Linux 内核 + 板级驱动 patch]
       ↓
[路由器厂商用户态(配置工具、Web UI、OTA 升级代理)]
       ↓
[签名与分区布局]
       ↓
[设备 bootloader 验证 → 启动]

在 GPLv2 下,这个链条是合规的:厂商只需要公开 Linux 内核(含 patch)、BusyBox、其他 GPL 组件的源代码;签名密钥、Web UI 这些专有组件可以不开放。产品照常出货。

如果切换到 GPLv3,Section 6 的 Installation Information 要求会直接摧毁签名机制:厂商必须交出能让用户刷入任意修改固件的所有信息,包括私钥或等价的解锁方法。这意味着:

这种冲突不是理论推演。CISCO、TP-Link、Ubiquiti 这些公司的某些产品线,在 2016 年后确实在 OpenWrt 等第三方固件的支持上变得更保守,原因之一就是 FCC 规则的紧缩——而这整张网,如果 Linux 切到 v3,会彻底断裂。

Linus 在某封邮件里用了一个更直白的表达:

“I don’t think anybody has the right to tell me that I can’t prevent people from using my code in a cell phone that is locked down.”

翻译:我不觉得有谁有权告诉我,我不能阻止别人在锁死的手机里用我的代码。这句话的精神内核是:他把内核当成「工具」,工具不应对使用方式做道德裁决。

4.3 GPL-2.0-only 的法律含义

如前所述,内核根目录的 COPYING 明确写着 v2 only。让我们看一下它的工程含义。

首先,内核是一个巨大的版权聚合体。linux/CREDITSMAINTAINERSscripts/get_maintainer.pl 可以帮助追踪哪些文件由谁维护,但「维护者」不等于「版权持有人」。内核每个文件的版权归属非常分散——既有个人开发者,也有 IBM、Intel、Red Hat、Google、Huawei、Samsung 等几百家公司。自 2005 年起,内核大规模使用 Signed-off-by 约定(Developer Certificate of Origin,DCO),要求每个提交者声明自己有权以内核当前许可证贡献,但这份声明针对的就是 GPL-2.0,不是 v2 or later。

其次,根据 FSF 自己的说法,GPL 项目升级到新版本有两条路:

  1. 项目一开始就写了 "v2 or any later version"。这种项目任何单个用户都可以选择以 v3 使用,项目维护者无需额外做什么。
  2. 项目写的是「仅 v2」或没有 or later。此时要升级到 v3,必须得到所有现存代码版权持有人的同意,或者把不同意的人的代码全部移除重写。

Linux 内核是第二种。它的版权持有人数以千计(很多已经过世或失联),代码量数千万行。让所有人同意升级到 v3,在工程上等同于重写一次内核。Linus 在公开发言里多次表示他不会发起这件事。

可以把这个状态写成一个推论:

(COPYING 写 v2 only)
    ∧
(版权持有人分散,无法统一同意)
    ⇒
(Linux 内核事实上永远停在 GPLv2)

这不是一个「还没升级」的问题,而是一个结构性稳态。任何时候讨论「Linux 能不能改成 v3」,都应该先把这个推论说清楚。

SPDX 上的内核许可清理工作(由 Thomas Gleixner 等人在 2017–2018 年推动)最终结果是:内核绝大部分文件头被标注为 // SPDX-License-Identifier: GPL-2.0GPL-2.0 WITH Linux-syscall-note。注意这里的 GPL-2.0 在 SPDX 2.0 之前是 “GPL-2.0-only” 的别名。大约 98% 以上的文件是 v2 only。

4.4 内核模块与「污染」旗标

内核 GPL 的边界上还有一个工程师每天都会碰到的话题:内核模块的许可证。内核里有一套宏:

MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL v2");
MODULE_LICENSE("GPL and additional rights");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_LICENSE("Dual MPL/GPL");
MODULE_LICENSE("Proprietary");

当一个模块用 "Proprietary" 加载时,内核会设置 TAINT_PROPRIETARY_MODULE 标志位,并在 dmesg 里打印:

foo: loading out-of-tree module taints kernel.
foo: module license 'Proprietary' taints kernel.
Disabling lock debugging due to kernel taint

这就是「污染旗标」(taint flag)。它不是阻止你加载专有模块,而是告诉任何 bug 报告的接收者:这个内核状态包含了非 GPL 模块,社区不承诺为此提供支持。/proc/sys/kernel/tainted 按位记录污染来源,每一位都有语义(P=proprietaryF=forcedC=staging crap…)。

历史上最典型的例子是 NVIDIA 的 nvidia.ko,它是专有模块,长期设置 Proprietary 旗标。AMD 的 amdgpu 最终走了反方向,主线化并以 GPL/MIT 双许可发布,避免了污染。而 NVIDIA 直到 2022 年才发布了 open-gpu-kernel-modules(GPLv2/MIT 双许可),但仅覆盖 Turing 及更新架构,且大量固件仍为闭源 blob。

这里衍生出一个长期争议:专有内核模块是否构成内核的衍生作品?FSF 的官方立场一贯是「是」——任何链接到内核的模块,哪怕是运行时动态加载,都属于衍生作品,必须是 GPL。但 Linus 和很多内核开发者的立场更温和:只要模块是一个可以在其他系统上同样工作的、使用内核稳定接口的「独立作品」,它就不一定构成衍生作品。这个立场的体现是内核里存在两套符号导出:

EXPORT_SYMBOL(foo);        /* 所有模块可用 */
EXPORT_SYMBOL_GPL(bar);    /* 只有 MODULE_LICENSE 为 GPL 兼容时可用 */

当一个 Proprietary 模块尝试解析 EXPORT_SYMBOL_GPL 的符号时,模块加载器会拒绝:

foo: Unknown symbol bar (err -22)
foo: disagrees about version of symbol bar

这个机制是 Linus 给出的一种工程妥协:内核接受专有模块存在的事实,但把「内部、GPL 哲学敏感的接口」单独圈出来禁止专有模块使用。哪些接口用哪个宏,不是统一标准,而是维护者的判断。

对企业工程师来说,实操上的关键点是:

4.4.1 发行版中的 GPL-only 模块冲突:ZFS on Linux

一个至今未解的著名案例是 ZFS。ZFS 原本是 Sun 为 Solaris 开发的文件系统,2005 年随 OpenSolaris 以 CDDL(Common Development and Distribution License,MPL 衍生的一种弱 Copyleft)授权开源。OpenZFS 项目后来把它移植到 Linux 作为内核模块。

矛盾来了:CDDL 与 GPLv2 被广泛认为不兼容。不是因为 CDDL 的 Copyleft 比 GPL 弱(弱是不冲突的),而是因为 CDDL 和 GPL 都对「修改后必须用自己那套许可证」有要求。两套不重叠的要求合在一起就会自相矛盾——你不能同时满足 CDDL 要求「文件级 Copyleft」和 GPL 要求「整体 Copyleft」。

后果:

Ubuntu 在 2016 年做了一个更大胆的选择:把 zfs.ko 作为二进制模块包放进官方仓库。Canonical 的法律分析认为这不违反 GPL,因为内核与 ZFS 分别作为独立软件分发,只在运行时加载——这个立场被 SFC 公开批评,但至今没有诉讼。

这个悬而未决的状态很好地说明:GPL 的传染边界不是一个黑白答案,而是一个「法律分析 + 风险承受度」的工程决策。

4.4.2 内核的 symbol namespace 与 CONFIG_MODULE_SIG

近年内核引入了另两套机制间接强化许可证控制:

这些机制的综合效果是:Linux 内核在坚守 GPLv2 only 的同时,还通过技术手段把 GPL 的边界守得更紧。这与 Linus 的立场并不矛盾——他反对的是「软件许可强加于硬件」,不反对「软件许可在软件内部被严格执行」。


五、LGPL:宽松版 GPL

GPL 本身不区分「程序」和「库」:只要是 GPL 代码,被链接进来的程序就整体被视为衍生作品。这对系统工具、应用程序来说问题不大,但对底层库(C 运行时、数学库、编解码库、GUI 工具包)来说是灾难性的——它意味着任何使用这个库的专有软件都无法存在。1990 年代初,GNU 项目在写 glibc、libstdc++ 时马上撞上了这堵墙。

LGPL(Lesser GPL)的使命就是在 GPL 和 BSD 之间开出一条中间道路。

5.1 LGPL v2.1:从 Library GPL 到 Lesser GPL

LGPL 最早叫 “Library General Public License”(LGPL 2.0,1991)。1999 年 Stallman 写了一篇长文章,把它改名为 “Lesser General Public License”(LGPL 2.1),并解释了改名的原因:他希望开发者默认不要用 LGPL,而是把 GPL 作为首选,LGPL 只在「战略上必要」时才使用——也就是当你写的库和某个专有库有直接竞争,不允许专有程序链接意味着你的库根本没有用户。glibc 和 libstdc++ 就是典型例子:如果它们不允许专有程序链接,Linux 就不会有商业应用,Linux 生态就起不来。

改名 “Lesser” 是故意的贬义措辞:FSF 希望告诉开发者「这是一个较弱的 Copyleft,用它意味着你放弃了一部分自由保护」。

5.2 链接例外的工程含义

LGPL v2.1 的核心是 Section 5 和 Section 6。它区分两种使用模式:

Section 6 给出了「使用」模式下的条件清单(择要翻译):

读完这些条件,可以得到 LGPL 链接例外的工程精髓:你可以静态链接 LGPL 库到闭源程序里,但必须给下游用户「替换这个库」的能力。

在动态链接场景下这很自然:你程序里调用 glibc,glibc 是共享库,用户换一个版本的 glibc 你的程序自动用新的。这时 (d2) 天然满足。

在静态链接场景下麻烦得多:静态链接把库代码编进了你的二进制,用户怎么「替换」?LGPL 的答案是——你必须提供你程序的「可重链接对象文件」(relocatable object files),让用户可以把新版本的库链接回去。具体做法是提供 .o 文件或 .a 文件(不含 LGPL 库代码的你自己的部分),加上 Makefile/脚本,让用户在本地完成一次链接操作。

这个要求对 Android NDK 里的 LGPL 库使用就是一个真实的坑:如果你在 apk 里静态链接了一个 LGPL 库,你理论上必须在合规材料中附带你 native 组件的 .o 文件和链接脚本,以便用户自己换库。绝大多数 Android 应用根本做不到这一点。这是为什么 Android 社区里对 LGPL 库采取非常审慎的态度,常见建议是:

5.2.1 LGPL 链接边界的一个具体推演

假设你做一款闭源 PDF 阅读器,想用 Poppler(LGPL v2.1+)作为 PDF 渲染内核。按 LGPL v2.1 Section 6 的要求,工程动作可以有几种合规路径:

路径 A:动态链接(推荐)

你的 app 二进制 ← dlopen → libpoppler.so
                                ↑
                        用户可自由替换该 .so

合规清单:
  - 在「关于」页列出 Poppler 名称、版本、LGPL 链接
  - 官网提供 Poppler 源代码下载(可直接链上游)
  - 保证 .so 与 app 在独立文件(不要把 .so 合并进 app 可执行)
  - 无须公开 app 的任何代码

路径 B:静态链接 + 可重链接对象

你的 app 二进制 = app.o + libpoppler.a 静态合并

合规清单(比 A 多出来的部分):
  - 发布 app.o(你的可重链接对象文件)+ 链接脚本
    用户取得新版本 libpoppler.a 后,能自行
    执行:ld app.o libpoppler_new.a -o app_new
  - 或者,直接公开 app 源代码

路径 C:违规做法(错误示范)

把 libpoppler 源代码拷一份到自己仓库,改成内部
类名,直接静态编译进 app.exe,不公开任何东西。

这在 LGPL 下是清楚的违规:修改库本身的代码必须以
LGPL 发布;静态链接没提供可重链接路径。

路径 A 对现代桌面、服务器都容易满足。问题出在 iOS、Web Assembly、某些单文件分发场景:iOS 禁止动态加载第三方代码(App Store 规则),WASM 把所有依赖打进一个 .wasm 文件,这两种场景天然只能走静态链接,LGPL 的路径 B 在实操上非常笨重。因此 iOS App 社区对 LGPL 库的接受度很低——在 CocoaPods 上你会看到许多库的作者主动提供「商业许可证选项」来绕过这个问题。

5.3 LGPL v3:继承 GPLv3 所有新条款

LGPL v3(2007)的结构设计非常简洁——它不再是一份独立的完整许可证,而是写成「GPLv3 + 一组附加权限」。因此:

LGPLv3 引入了一个新概念:「对应应用代码」(Corresponding Application Code):

“The Corresponding Application Code for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.”

简单说:如果你把 LGPLv3 库组合进一个「结合作品」(Combined Work),你必须把你应用部分的「对象代码或源代码」作为合规材料的一部分——让用户能把它和新版本的库组合回去。这与 LGPL v2.1 Section 6 (d1) 的要求类似,但在 v3 里被用更工整的术语描述了。

5.4 LGPL 在 Java、Android 上的特殊问题

LGPL 最早是为 C/C++ 生态设计的,链接模型是链接器把对象文件合并进二进制。在 Java、Kotlin、Swift、Go 这些后出现的语言里,「链接」的含义完全不同,LGPL 的条文变得模糊甚至不可执行。

Java 的情况:Java 程序通过 .class / .jar 分发,运行时由 JVM 加载并 JIT 编译。一个程序是否「链接」了一个库?在字节码层面它只是在 import 时引用类名,在运行时由 classloader 动态解析。FSF 对此写了一份专门的 FAQ「LGPL and Java」,立场是:Java 的类链接被视为 LGPL 意义上的动态链接,因此静态 include 的问题不存在,只要你满足「让用户能替换库」的条件就行。但这份 FAQ 没有法律效力,只是 FSF 的解读。

Android 的情况:Android 平台自己几乎不用 LGPL 代码。Google 的核心策略是用 Apache 2.0 重写基础库(Android Framework、Bionic C 库、Skia),唯一的 GPL 组件是 Linux 内核,唯一的 LGPL 组件集中在少数本地库(早期 FFmpeg、某些编解码)。Android 明确避开了 GNU libc(glibc)而选择 Bionic,避开 GNU GCC 的 libstdc++ 而选择 libc++(LLVM),避开 GNU Classpath 而选择 Apache Harmony(早期)和后来基于 OpenJDK 重写。这条路线的代价是大量工程重复劳动,但回报是 Android 的用户空间(不含内核)可以以 Apache 2.0 作为默认授权进入到几乎所有 OEM 厂商的专有定制里。

GNU Classpath 与 Android OpenJDK 之间还有一个半公开的故事。2010 年 Oracle 起诉 Google,指控 Android 的 Java 实现侵犯 Oracle 的专利和 Java API 版权。官司打了将近十年,最终 2021 年美国最高法院判决 Google 使用 Java API 构成合理使用(fair use)。这期间 Android 从 Apache Harmony 迁到了 OpenJDK 基础。OpenJDK 的许可证是 GPLv2 with Classpath Exception——这里的 Classpath Exception 就是一种「类似 LGPL 链接例外」的附加权限,允许任意许可证的程序链接 OpenJDK 的类库。这让 Android 在法律上能直接使用 OpenJDK 实现而不污染 app 的许可证。

5.5 LGPL 在嵌入式系统里的两难

LGPL 在桌面/服务器环境里几乎是完美设计:动态链接天然满足 Section 6 (d2)。但在嵌入式与移动设备里,LGPL 的「你必须能替换库」要求与「设备的完整性、安全启动、FCC 合规」之间的冲突比 GPLv3 更早出现。

以一款智能门锁为例:

厂商的普遍应对:

这一节对比 GPLv3 的反 Tivoization 条款,可以看出:FSF 在 LGPL v2.1 时代其实已经预见到了硬件锁定问题,但当时的措辞是温和的(「让用户能重新链接」),没有上升到「必须给签名密钥」的程度。GPLv3 的反 Tivoization 本质上是把 LGPL v2.1 里那个「能替换库」的精神从库层面推广到了整个程序层面——也正是这一步让 Linus 觉得走过头了。


六、GPL 兼容性矩阵

讨论许可证兼容性时,最容易混乱的是「A + B 能不能合并成一个作品」。下面按常见情况整理一张表。这里「合并」的含义是做成一个单一作品或紧耦合的衍生作品;纯粹的聚合(同一光盘里两个独立程序)不受这张表约束。

A  B Public Domain MIT/BSD-2 BSD-3 Apache 2.0 LGPL v2.1 LGPL v3 GPL v2 only GPL v2 or later GPL v3
Public Domain
MIT / BSD-2
BSD-3
Apache 2.0 ✓*
LGPL v2.1 ✓* ✓*
LGPL v3 ✓*
GPL v2 only
GPL v2 or later ✓*
GPL v3 ✓*

注释:

表里最反直觉的两个单元格是:

这张表直接解释了为什么 Linux 内核没法直接吸收某些 Apache 2.0 的代码:如果某个子系统想从 Apache 项目引入实现,要么上游的作者愿意以 MIT/BSD 重新授权给内核,要么干脆重写。

6.1 许可证合并后的「优势者原则」

在上面的矩阵里,合并后需要按较严格一侧的许可证分发。这个规则常被称为「优势者原则」或「最强 Copyleft 决定整体许可」:

MIT ⊕ MIT   = MIT
MIT ⊕ BSD-3 = BSD-3(或 MIT,取决于哪边有更多条件)
MIT ⊕ GPLv2 = GPLv2(GPL 胜出)
Apache 2.0 ⊕ GPLv3 = GPLv3
LGPLv3 ⊕ GPLv3 = GPLv3(LGPL 在组合作品里退化为 GPL)

理解这条原则的一个关键是:Copyleft 许可证是「入口许可」而不是「出口许可」。GPL 并不是说「我所有代码一定要出到 GPL」,而是说「任何基于我代码的衍生作品,在下游分发时必须至少给予用户 GPL 等价的自由」。结果是下游作品的许可证必须兼容 GPL,最简单的做法就是整体用 GPL。

6.2 SPDX License Expression

现代工程里许可证的精确表达已经标准化为 SPDX Expression 语法:

简单:          MIT
合取(全部):  GPL-2.0-only AND MIT
析取(选其一):GPL-2.0-or-later OR MIT
附加例外:      GPL-2.0-only WITH Linux-syscall-note
                GPL-3.0-or-later WITH Classpath-exception-2.0

Linux 内核顶部的 SPDX 行非常典型:

// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note

Linux-syscall-note 是内核的一个附加例外,它说:通过系统调用从用户态程序「使用」内核,不视为衍生作品。这解决了一个古老的困惑——每一个 Linux 上跑的程序都在调 syscall,难道都是内核的衍生作品?显然不是,这个 note 把它写死。

SPDX 还允许自定义例外,GCC Runtime Library Exception、Autoconf 例外、Bison 例外都有对应的标准 SPDX 表达。企业内部做 SBOM 时应当一律使用 SPDX 表达式,避免自创文字描述。


七、工程坑点

7.1 内核模块的 GPL 符号导出

上面第四节已经介绍了 EXPORT_SYMBOLEXPORT_SYMBOL_GPL 的区别。这里补充一些工程实践细节。

内核里有多少 GPL-only 符号?截至近几年的 Linux 主线,EXPORT_SYMBOL_GPL 的数量已经接近甚至超过 EXPORT_SYMBOL。一个粗略的估算:

# 在内核源代码树执行
grep -r "EXPORT_SYMBOL(" --include='*.c' | wc -l
grep -r "EXPORT_SYMBOL_GPL(" --include='*.c' | wc -l

结果会显示两者都是数万级的量。哪些符号是 GPL-only?有一些通用模式:

一个真实的工程陷阱:某些闭源厂商会通过「shim 模块」绕过符号限制。Shim 的做法是写一个 GPL 的「桥接模块」,把 GPL-only 符号中转成普通 EXPORT_SYMBOL 给自己的闭源模块用。这种做法在社区被严厉批评——它在法律上形成了一个明显的「用 GPL 许可证作伪装」的结构,FSF 和 SFC 都将其视为违规。然而它在技术上是能工作的,内核并没有机制阻止。工程师应当警惕任何代码审查中出现的 shim 模块模式。

7.2 动态链接 vs 静态链接下的 LGPL 实践

实务上企业工程师最常问的问题之一是:「我动态链接 LGPL 库,需要做什么?」

最小合规清单(以 LGPL v2.1 为例):

1. 在产品文档 / 「关于」页面列出使用的 LGPL 库名称、版本、
   许可证链接。
2. 在发行介质或官网提供完整的库源代码(可以转发上游 tarball)。
   如果打了本地 patch,patch 也要给出。
3. 如果产品做过自动更新,确保更新后替换库的能力仍然存在
   (不要把动态链接偷偷改成静态)。
4. 保留书面要约 3 年(对实体产品尤其重要)。

静态链接的增量义务:

5. 提供让用户能把新版本库重新链接回你的程序的材料。两种常见
   做法:
   (a) 发布你程序的可重链接对象文件(.o / .a),加上 Makefile
       和链接脚本;
   (b) 发布你程序的完整源代码(很多厂商嫌麻烦,干脆把程序改成
       开源)。
6. 在隐私页/版权页显著标注:「本产品包含 XX LGPL 库的静态链
   接版本,如需替换库请访问 …」。

另一个被忽视的坑:LGPL 对库的修改。如果你对 LGPL 库本身打了 patch,该 patch 必须以 LGPL 发布。这一点对使用定制版 FFmpeg、定制版 OpenSSL(OpenSSL 不是 LGPL,但逻辑相同)的团队是常见合规问题。实操建议是把 patch 放在一个独立的、公开的 git 仓库里,每次发布版本时打 tag,URL 放进产品合规页。

7.3 GPL 传染的「组合作品」判断

GPLv2 Section 2 最后一段有一个常被引用的措辞:

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

「单纯聚合」(mere aggregation)不触发传染。一张光盘里放了一个 GPL 程序和一个闭源程序,两者互不依赖,光盘本身不因此全部 GPL。

问题在于:什么算「单纯聚合」,什么算「组合成一个作品」?边界从来没有被法院清晰划定。历史上有几个著名的论战:

FSF 自己的 FAQ 给出的近似判据是:「如果两个程序在同一地址空间通过函数调用交互,或者共享数据结构,它们更可能是一个作品;如果通过管道、socket、D-Bus、文件、网络调用交互,它们更可能是独立作品。」这个判据在工程上是合理的,但没有司法判决背书。

7.3.1 一个真实的组合边界争议:glibc 链接问题

glibc 是 LGPL(v2.1+)。你写的专有程序 foo 通过动态链接 glibc,这显然合规。但有一个细节:glibc 头文件(<stdio.h><stdlib.h>)里包含大量宏、inline 函数、静态 inline 定义。当你 #include <stdio.h> 并编译时,这些宏和 inline 会被直接内联到你的 .o 里,成为你二进制的一部分。

FSF 对此的立场写在 “LGPL-and-Java” FAQ 与 glibc 自己的授权说明里:glibc 头文件中的 inline 内容被明确视为「接口定义」而非「库内容」,通过它们引入的代码不触发 LGPL 的 Copyleft。否则任何 Linux 应用都会因为 printf 的 inline 定义而被 LGPL「感染」,这显然不是设计意图。

这个例子说明:Copyleft 的边界在现实里不是纯粹的法律问题,而是法律 + 工程惯例 + 上游作者意图 + 社区共识 的综合产物。阅读许可证文本只是入门,理解项目的实际合规惯例同样重要。

7.4 源代码发布义务的时间节点

GPL 一个常被误解的维度是「什么时候必须提供源代码」。答案是:在你分发可执行文件的那一刻,源代码义务就已经开始

具体形态:

历史上 SFC 打过的几桩有名的合规官司(在此不展开具体文号,只说公开事实):

工程上的合规提醒:

7.5 一次真实合规排查的工程日志(虚构但典型)

把本节的各种原则串起来,看一个现场化的场景。你是某物联网初创的技术负责人,产品即将出海欧盟。法务发来一封邮件:「我们要过 CE 认证,合规部门要求出示所有开源许可证的合规证据。你有一周时间。」

Day 1:盘点依赖。你让团队跑一遍 SBOM 生成:

# Yocto 项目可直接生成 SPDX
bitbake -c create_spdx <image>

# 对手写固件
pip install cyclonedx-bom
cyclonedx-py -o sbom.json

产出一份 JSON,里面 387 个组件,按许可证分布:

MIT          : 142
Apache-2.0   :  88
BSD-3-Clause :  41
GPL-2.0-only :  56  ← 主要是 Linux 内核 + BusyBox + dropbear
LGPL-2.1-or-later : 18
GPL-3.0-or-later : 12  ← 这是危险信号
MPL-2.0      :   9
CDDL-1.0     :   1  ← 不兼容警报
Proprietary  :  20

Day 2:消除危险组件

Day 3:准备 GPL 源代码包

# 收集所有 GPL/LGPL 组件的源代码 tarball + patch
mkdir -p gpl_release/kernel gpl_release/busybox ...
cp $YOCTO_DL/linux-5.15.123.tar.xz gpl_release/kernel/
cp $BUILD/tmp/work/.../linux-5.15.123/.config gpl_release/kernel/
cp -r $BUILD/tmp/work/.../linux-5.15.123/*.patch gpl_release/kernel/

打一个总的 gpl_source_v1.0.0.tar.xz,放到合规站点。

Day 4:文档化。写一份 THIRD_PARTY_LICENSES.md

# Third-Party Licenses
This product includes software from the following open-source projects.

## GPL v2 Components
- Linux Kernel 5.15.123 (GPL-2.0-only)
  Source: https://compliance.example.com/fw-1.0.0/kernel.tar.xz
- BusyBox 1.36.1 (GPL-2.0-only)
  ...

## LGPL v2.1 Components
- zlib ...  # 实际是 zlib license,校对!
- glibc ...

## Written Offer
For a period of 3 years from product shipment, you may request
complete corresponding source code by emailing compliance@example.com.

Day 5:固件印刻。把 THIRD_PARTY_LICENSES.md 做成产品「关于 → 开源声明」菜单中的静态页,OTA 更新同步更新。产品说明书附加 “Written Offer” 段落。

Day 6:法务复核。列几个检查项:

Day 7:提交认证

这个流程走下来,如果之前没做工作,通常要 1–2 个月;一旦建立了自动化 SBOM + 合规发布管道,每次版本发布的增量工作只有几十分钟。


八、中国案例:路由器厂商与 Android 厂商的 GPL 合规

下面这节基于公开可查的事实(厂商的公开源代码门户、SFC 的公开披露、媒体报道)。不涉及任何未公开的内部文件或未经证实的诉讼号。

TP-Link(普联技术)维护了一个公开的 GPL 源代码门户,历史域名是 www.tp-link.com/en/support/gpl-code/ 以及专门的代码仓库 gpl.tp-link.com(站点可用性随时间变化,近年部分页面整合进全球支持中心)。用户可以按产品型号和固件版本号下载对应的源代码压缩包。

TP-Link 的典型合规材料包含:

历史上 TP-Link 与 OpenWrt 社区、GPL 执法者之间经历过一段摩擦:早期某些型号的合规压缩包不包含完整的交叉编译脚本和 kernel config,社区需要逆向工程来补齐构建能力。随着社区的持续压力以及 OpenWrt 社区与部分型号的深度合作,TP-Link 的合规质量在近年有明显提升。国内研究者做过的一类典型验证动作是:下载合规包、抽取内核 .config、与官方固件解包后的 /proc/config.gz(如果开启)对比,检查两者是否一致。

8.2 华为与 openEuler / HarmonyOS 的合规链条

华为作为硬件厂商的 GPL 合规同样成熟。它公开的开源门户之一是 consumer.huawei.com/en/opensource/(消费者终端产品的 GPL 源代码下载)以及 openEuleropenHarmony 两个社区。

HarmonyOS / openHarmony 的一个长期讨论点是:如果一款终端设备的底层是 Linux 内核(GPL),上层是 OpenHarmony 框架(Apache 2.0),那么整机的合规材料应该覆盖哪些部分?答案是分层处理——GPL 部分按 GPL 发布源代码,Apache 部分按 Apache 的 NOTICE 要求发布。两者共存不冲突,正是 GPL 兼容性矩阵里「Apache ✓ GPL v2(通过独立程序边界)」的实际体现。

8.3 小米的 Android 内核合规

小米有一个公开的 GitHub 组织 MiCode,其中包含了大量机型的内核源代码仓库,例如 MiCode/Xiaomi_Kernel_OpenSource 以及按机型命名的分支(如 mayfly-q-oss 代表某代小米 12 的 Q 版本内核)。每次 MIUI 或 HyperOS 主要版本发布后,相应机型的内核源代码会在一段时间窗口内推送到 GitHub。

小米的合规质量随机型不同存在差异:

LineageOS、PixelExperience 等第三方 ROM 社区的生态,直接依赖这类合规源代码——没有内核源代码就无法为新机型做二次开发。社区与厂商之间形成了一种松散但稳定的关系:社区通过公开压力推动合规,厂商通过合规换取对第三方开发者生态的部分支持(解锁 bootloader 的官方流程就是这种关系的技术体现)。

值得强调的是:GPL 义务的触发点是「分发」,不是「上市销售」。所以理论上只要机器开卖,同时就需要合规材料到位。国内部分中低端机型长期处于延迟合规状态,这在 GPL 合规社区眼里是一个真实但较少被追责的灰色地带——既没有版权持有人主动起诉,也没有监管机关介入,于是维持了事实上的弹性。

8.4 国内 Android 刷机生态与 AOSP / 内核边界

Android 的特殊分层对 GPL 合规影响深远:

 [应用层 / Apache 2.0]
     ↓
 [Android Framework / Apache 2.0]
     ↓  (JNI)
 [Native libs / Apache 2.0 + 少量 BSD / 少量 LGPL]
     ↓
 [Linux kernel / GPL-2.0-only]

这套分层设计的意图之一是:让 OEM 厂商对上层 Android 做任何闭源修改都不会触发 GPL 传染。厂商的所有 UI 定制、AI 模型、人脸解锁、支付沙箱都可以是专有二进制,不需要公开源代码。只有内核及其补丁必须公开。

这也是为什么对国内 Android 厂商的 GPL 合规监督几乎完全聚焦在内核部分:社区不会问你 MIUI 桌面的代码,但会反复要你 kernel_*.tar.gz

刷机生态的工程细节上还有一个隐蔽的合规问题:Device Tree Overlay(DTBO)与 vendor 分区。现代 Android 设备通常把内核与硬件描述分开:内核主体在 boot.img,硬件特定的 dtb/dtbo 在 vendor_boot。GPL 要求的「完整对应源代码」应该包括用于生成 dtbo 的设备树源文件(.dts.dtsi)。一些厂商只发布 boot.img 对应的内核树,但遗漏 vendor 的设备树——这是常见合规缺陷。

8.5 国内合规成熟度对比:从「被动回应」到「主动发布」

把几家国内主要硬件厂商放在同一坐标系下比较,可以看到明显的阶段性分化:

阶段 1:被动回应(2010 年前)
  公司压根没有 GPL 合规意识,社区投诉后才手工打包一份源代码
  发给投诉者,不公开。

阶段 2:门户化(2010–2015)
  建立对外 GPL 代码门户,按机型发布源代码压缩包。但质量参差:
  缺 .config、缺编译脚本、patch 与实际固件不对应。

阶段 3:工程化(2015–2020)
  合规纳入发布流程。CI 在固件构建时自动打出「GPL bundle」,
  含内核源、.config、工具链版本说明、patch 序列。
  代表:华为 consumer 门户、小米 MiCode 后期仓库。

阶段 4:社区化(2020 之后)
  部分厂商把 GPL 代码直接以 Git 仓库形式在 GitHub / Gitee
  上维护,允许社区以 issue 形式反馈问题。
  代表:openEuler、openHarmony、小米部分旗舰机型。

从阶段 2 跳到阶段 3 是国内厂商过去十年最大的合规进步。驱动力不是监管压力(国内监管对 GPL 合规几乎没有直接执法路径),而是两股力量:一是出海产品被 SFC、FSFE(Free Software Foundation Europe)这类欧美组织盯上,触发跨境法律风险;二是第三方开发者社区(OpenWrt、LineageOS)对合规材料的工程质量直接构成产品口碑压力。

这也给一个对国内所有做出海硬件产品的公司的实用提醒:GPL 合规是产品出海的基础门槛,不是法务的加分项。SFC 一类组织每年会对主要市场上的消费电子做合规审计,未合规产品被发出「cease and desist」后续由代理销售渠道自动下架,影响比预期严重。


九、选型建议

把前面所有讨论收敛到一个工程决策框架,给出实际选型建议。

场景 A:你写的是一个通用库,希望被最大范围采用,包括商业闭源场景。

选择:MIT 或 Apache 2.0。不要选 GPL、LGPL。原因:GPL 会把大量潜在用户挡在门外;LGPL 虽然允许专有软件链接,但它对「静态链接」的义务在移动端和容器化时代越来越难履行,用户会倾向于找替代品。Apache 2.0 的额外好处是提供专利授权与专利报复,对企业法务更友好。

场景 B:你写的是一个底层基础库,在某个领域存在专有竞争对手,你希望用 Copyleft 强迫专有产品也贡献回来,但不希望断绝商业合作。

选择:LGPL v2.1。glibc、FFmpeg(LGPL 核心 + 可选 GPL 组件)、早期 Qt 都是这个选择。它保留了「修改库本身必须开源」的硬约束,同时允许专有程序通过动态链接合法使用。

场景 C:你写的是一个应用程序、开发工具、命令行工具、系统服务,你希望它永远留在开源世界。

选择:GPL v2 或 GPL v3。具体选哪个,看你是否关心 Tivoization、专利、DRM:

场景 D:你做嵌入式固件,希望深度集成 Linux 内核并发布商业产品。

选择:内核必然是 GPL-2.0-only(你无法改)。你自己的驱动、用户态组件可以在兼容性允许的范围内自选: - 板级配置、非关键用户态:MIT/Apache 都可以; - 内核模块:推荐 GPL v2(避免 Proprietary taint,允许使用 GPL-only 符号); - 如果你写了想单独卖的工具,走专有许可;GPL 边界清晰即可。

场景 E:你的库/框架主要服务于云端 SaaS 场景,担心云厂商白嫖。

选择:考虑 AGPL v3。它把 GPL 的 convey 定义扩展到了「通过网络对外提供服务」。相关讨论将在本系列下一篇展开——这里仅作提示。

场景 F:你在做一个工具链或运行时(编译器、解释器、标准库实现)。

选择:看看 GCC 的做法。GCC 本身是 GPLv3,但运行时库(libgcc、libstdc++)带 GCC Runtime Library Exception,允许用 GCC 编译出的目标程序按任意许可证发布。这种「Core Copyleft + Runtime Exception」的组合是经过长期实战检验的模板。

一条反复被验证的经验法则:选许可证永远应该围绕「我希望谁能使用我的代码 / 在什么条件下使用」这个问题,不要围绕「我觉得哪个许可证的哲学最对」。GPL 本身不是错误,但放错位置就是错误。

9.1 企业内部的 GPL 合规流程建议

把选型落到工程组织里,需要一套轻量但执行到位的流程。参考形态:

[新依赖引入] ——审批——> [OSPO / 法务检查许可证]
                               │
         ┌─────────────────────┼─────────────────────┐
         ▼                     ▼                     ▼
   [宽松许可证]           [LGPL]                 [GPL/AGPL]
     直接放行          评估静态/动态链接          高度关注
                       是否能满足替换要求        默认禁止进入
                                                闭源产品线;
                                                独立 GPL 产品
                                                线单独审批

落地到工具链:

9.2 许可证选择的决策清单(Checklist)

给一个便于背的清单,在团队内部讨论许可证选型时可以按序过:

1. 这个项目的目标用户是谁?(个人开发者 / 企业 / 商业产品 /
   消费硬件)

2. 你希望收到的最大贡献者规模是?(几十 / 几千 / 几万)
   许可证越严格,贡献者心理门槛越高。

3. 你是否在意专利?(是 → Apache 2.0 / GPLv3 / AGPLv3)

4. 你是否在意「云厂商拿去做闭源 SaaS」?(是 → AGPLv3 / SSPL / BSL)

5. 你是否在意「嵌入设备里被签名锁死」?(是 → GPLv3;
   否 → GPLv2 or later;不关心 → MIT/Apache 2.0)

6. 你写的是库还是应用?库优先考虑 LGPL 或 Apache;
   应用可以 GPL。

7. 这个项目将来可能合并进哪些上游?如果你希望能被
   Linux 内核吸纳 → GPL-2.0 only(与内核一致)。

8. 你团队的法务/商务对 GPL 的态度?如果极度敏感,
   选 Apache 2.0 能省掉大量沟通成本。

这张清单跑一遍,许可证选型基本不会错。

9.3 企业里最常见的三个错误

实战中看到的错误模式高度集中,列一下帮助读者自检:

9.4 常见 FAQ

把客户、同事、法务反复问过的问题集中回答一次:

Q1:我们只在服务器内部用 GPL 软件,需要公开源代码吗?

不需要——GPL 义务只在「convey」时触发,纯内部使用不触发。但需要注意「内部」的边界:如果你把软件部署到客户侧服务器,或者打包进交付物,就是 convey。云服务场景下,服务自己运行 GPL 程序而不分发二进制给最终用户,GPL 不触发(AGPL 才触发,下一篇讨论)。

Q2:我能不能买一份 GPL 代码的「商业许可」绕过公开源代码?

如果代码的所有版权持有人都愿意给你出「商业许可」,可以。这叫双许可(dual licensing)。但 Linux 内核由数千位贡献者组成,要获得全部版权许可是不可能的。典型的成功双许可项目:MySQL(Oracle 单一版权人,可以发商业许可)、Qt(Qt Company 持有大部分版权,早期提供商业许可)。

Q3:我改了 Linux 内核做了一个驱动,我发布的产品必须公开这个驱动吗?

是。内核驱动通常被视为内核的衍生作品,必须 GPL v2。一个特例是:如果你使用严格稳定的内核接口、驱动独立编译、不依赖 GPL-only 符号,你可以在法律灰色地带发布闭源模块,但风险自担,也会产生 TAINT_PROPRIETARY_MODULE。主流实践建议永远 GPL。

Q4:我只是链接 glibc 做系统调用,我的专有程序算 LGPL 吗?

不算。glibc 是 LGPL,但系统调用层面和 glibc 头文件的 inline 定义都不触发传染(按 glibc 本身的 COPYING 与 FSF FAQ)。你的专有程序可以自由授权。

Q5:GPL 软件的 bug 导致我们生产事故,能向上游追责吗?

不能。GPLv2/v3 都有明确的「AS IS」免责声明和责任限制条款。除非上游作者出具了独立的书面承诺或商业合同,否则开源上游不承担任何责任。这就是为什么企业付费购买 Red Hat、SUSE 支持——不是为了代码,是为了责任承担方。

Q6:我写的开源库被一家公司违反 GPL 拿去做专有产品,我能怎么办?

步骤:

  1. 收集证据——被违反产品的二进制、证据表明其中包含你的代码(反编译、字符串匹配、内核模块符号等);
  2. 向对方发送合规通知(非起诉函),要求其在合理期限内履行义务;
  3. 联系 SFC 或当地类似组织寻求代理执法;
  4. 如对方持续不配合,以版权侵权提起诉讼。

GPLv3 有 30 天「cure period」给被违反方一个纠正窗口;GPLv2 没有,原则上一旦违反许可立即终止,但实务中法院通常接受善意补救。

Q7:MIT 代码合并进 GPL 项目以后还能拿出来单独用吗?

MIT 原始代码在它的原始仓库里仍然是 MIT,随时可以拿出来单独使用。你从 GPL 项目里取出的那份「被合并、被修改」的代码是 GPL 的,不能当成 MIT 用。这是「入口许可」原则的又一体现。

Q8:AGPL 适合我的项目吗?

如果你的项目主要是被部署为网络服务,并且你担心云厂商拿去改闭源运营,AGPL 值得考虑。但要知道:AGPL 会让 80% 的商业用户直接放弃你——很多公司的 OSPO 把 AGPL 与 SSPL 列入黑名单。具体权衡本系列下一篇详细讨论。


本文为工程参考,不构成法律意见。涉及具体法律风险请咨询专业法律顾问。

十、参考资料

以下为本文涉及的主要公开材料索引。所有链接为撰写时可访问的公开页面,具体内容以官方最新版本为准。


上一篇MIT、BSD、Apache 2.0:宽松许可证的真实区别

下一篇AGPL、SSPL、BSL:云厂商时代的”反云”许可证

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-22 · architecture / opensource

开源许可与版权工程

面向中国工程团队的开源许可、版权与合规系列。从 GPL、AGPL、Apache、木兰协议到中国真实案例、SCA/SBOM 工具链与出海合规,讲清楚开源在工程落地中的坑与方法。


By .