
LGPL Static Linking: Ship Object Files, Keep Your Source
Ship your application's object files, not your source. That is the whole trick to statically linking an LGPL library into a closed program and staying legal. You hand over your .o files or a .a archive, the link instructions, and the LGPL library's own source with any changes you made. From those three pieces a user can rebuild the library, relink your program, and run it. Skip any one and static linking becomes a violation, no matter how a vendor's "compliance" page words it.
Last updated: 2026-07-21
What does the LGPL actually require for static linking?
One rule, and it is short: a user must be able to swap in a modified version of the LGPL library and still build a working program. The Free Software Foundation calls this the relink requirement. Your own code stays closed. You just owe enough that someone else could replace the library and get a running binary.
Three things satisfy it. First, the object files or static library of your proprietary parts, so they can be relinked. Second, the build and link instructions to do it. Third, the LGPL library's source, including your edits. Miss one and the user cannot relink, which was the entire point.
Want the license text and history? Start with the GNU Lesser General Public License itself, not a summary blog.
Providing object files satisfies the relink requirement
Object code plus link instructions meets the rule without exposing your source. Teams get this wrong in both directions. Some think static linking forces them to publish everything, so they dodge the license. Others ship nothing and hope no one checks.
The FSF accepts object files as a deliverable. Your .o files or .a archive hold machine code, not readable source, so handing them over leaks almost nothing about your logic. What they let a user do is drop in a rebuilt libfoo.a, run your documented link step, and get a program that runs against their version.
So the minimum bundle is your object files, the LGPL library source, and a script or README with the link command. That is all. If you were about to zip up your whole source tree, stop.
What the FSF FAQ says about static versus dynamic
Read the FSF FAQ before you trust a paraphrase. Here is the line most summaries drop: in the FSF's analysis, dynamic versus static linking never makes any difference to the outcome. Either way, the combined work must let the user relink against a modified library. Dynamic linking usually satisfies that for free, because swapping a shared object is trivial. Static linking does not, so you owe the extra materials.
People read "static is more work" and assume it is forbidden or riskier by nature. It is not. It just puts the burden on you to reproduce, by hand, the substitution that dynamic linking hands users automatically. The obligation is identical. Only the plumbing changes.
Rely on secondhand summaries and you end up defending a rule you never read. The GNU licenses FAQ is the primary text worth bookmarking.
Can you ship closed-source software with a static LGPL library?
Yes, and this is the whole reason weak copyleft exists. A proprietary application can statically link an LGPL library and stay closed, as long as you provide the relink materials above. Your program's license does not change. Your source stays private.
What you cannot do is treat the library like it came from a permissive license. MIT and BSD let you absorb code and walk away. LGPL keeps a boundary around the library: its source stays open, your modifications get shared, and the user must be able to replace it. Cross that boundary and your closed application is out of compliance.
For where LGPL sits between permissive and strong copyleft, see this walkthrough of the GNU Lesser General Public License.
What you hand over: object files, not your source
The minimum deliverable is your application's object files or static libraries, plus build and link instructions, plus the library source. Nothing more. Overcompliance and undercompliance are both common, and both cost you.
Undercompliance is shipping the final binary and calling it done. A stripped, fully linked executable gives the user no way to relink, so it fails outright. Overcompliance is publishing your entire proprietary source because someone told you LGPL "is basically GPL for statically linked code." It is not. You gave away trade secrets to satisfy a rule that object files already covered.
The trade-offs between the two linking models are worth keeping in front of your compliance team:
| Aspect | Dynamic linking | Static linking |
|---|---|---|
| Compliance effort | Lower: provide library source and notices | Higher: provide rebuild info and object files |
| Update model | Independent library updates | Tied to program rebuilds |
| Practical benefit | Simpler audits and upgrades | More control, more paperwork |
Use that split to audit fast. Lean on plain checks and real examples, not license lawyering.
Can you use LGPL for device drivers this way?
For user-space driver code, yes, the same static-linking rules apply and work well. The catch is the kernel side. The Linux kernel ships under GPL-2.0 with an explicit syscall exception, which clarifies that user-space programs using kernel services through normal system calls are not derived works and do not fall under GPL requirements. That boundary is what lets a user-space driver link an LGPL library without dragging your whole stack into the GPL.
Inside the kernel it is a different story. A loadable module's MODULE_LICENSE string and the symbols it can reach are governed by kernel rules, not the LGPL alone. So if your driver lives partly in the kernel, plan the licensing at design time, not at ship time. The mechanics of that string are covered in MODULE_LICENSE kernel strings.
The practical split: LGPL earns its place in the user-space parts of a driver stack, where relinkable object files satisfy the license cleanly.
Test the relink before you ship
Do not assume compliance from reading the license. Prove it. Do exactly what a user would do: modify the library, rebuild it into a fresh libfoo.a, then link your program against it using only the files and instructions you plan to ship. Run the binary. Confirm it picked up your changed library, not the original.
Run this test from a clean directory that holds nothing but the shipping bundle: your object files, your link instructions, and the library source. If the link step fails because an object file is missing, that tells you exactly what to add. If the binary still loads the old library, your instructions point at the wrong path.
Watch the build output, not your assumptions. A link that succeeds only because a stray .o was sitting in your working tree is a link that will fail for the customer who has just your bundle. Ship only after a clean-room relink works from the files you actually distribute. That is the difference between "I think this complies" and "I watched it comply."
Why dynamic and static linking get treated differently
Ease of substitution, nothing deeper. Dynamic linking already hands the user a replaceable component: they drop a new .so in place and your program loads it. The relink requirement is met by design, so the FSF asks little beyond the library source and notices.
Static linking bakes the library into your binary. Once it is compiled in, the user cannot swap it without your object files and a way to relink. So the license asks you to reproduce, on purpose, the freedom that dynamic linking gives away for free.
That is why the rule reads as "more work for static" even though the obligation is identical. The goal never changes: the user keeps the ability to run a modified library. How you preserve that ability shifts with how you linked.
The practical risks of getting it wrong
The failure is rarely a lawsuit on day one. It is a compliance audit, an acquisition, or a customer's legal team asking for your relink materials years later, when the build system that made them is gone. If you cannot reproduce the object files and link steps, you cannot comply, and rebuilding them after the fact is miserable.
Common failure points I would watch:
- Shipping a stripped final binary with no object files, so no relink is possible.
- Losing the exact build and link instructions, so even you cannot reproduce the bundle.
- Ambiguous or missing license headers, which turn a simple audit into archaeology.
- Trusting a vendor's "LGPL compliant" claim; verify the relink yourself.
The fix for all of these is one discipline: archive the relink bundle with the release, tag files with clear SPDX identifiers, and run the substitution test once per release. For teams already tracking kernel-side rules, pair this with your process to keep kernel code GPL-compliant. Compliance you can reproduce on demand is the only kind that survives an audit.
FAQ
Does providing object files really replace the source code requirement for static linking?
For your proprietary code, yes. You hand over object files or a static library so users can relink, and your source stays private. What you still owe is the LGPL library's own source, including any edits you made. Those are two separate duties, and people conflate them constantly.
Can I ship just the final executable if I also publish the library source?
No. A fully linked executable gives the user nothing to relink against. Publishing the library source alone does not let them substitute a modified library into your binary. You still need your object files and the link instructions so the swap actually works.
Does the LGPL version I pick change the static-linking rules?
The relink requirement lives in both v2.1 and v3. The main difference that touches static linking is v3's installation information rule for consumer devices, which can force you to provide what a user needs to install a modified build on locked-down hardware. Read the exact clause for the version you chose.
How is this different from just using an MIT or BSD library?
Permissive licenses let you absorb the code with no reciprocal duties. LGPL keeps a boundary: the library source stays open, your changes to it get shared, and the user must be able to replace it. Static linking is where that boundary turns into real work, in the form of object files and link instructions.
What is the fastest way to know my build is compliant?
Run the relink test from your shipping bundle alone. Rebuild the library with a small change, link your object files against it using only your documented instructions, and run the binary. If it builds and loads the modified library, you comply. If it does not, the link error names the missing file.
