OpenWrt: Adding Patches to a Static Library
This document explains:
How to build a static library (
libfoo.a)How to patch the library source before compilation
How OpenWrt applies patches automatically using
Build/PatchHow to export the library + headers to
staging_dirand rootfsHow to build a test application linking with the patched library
How to install and run the patched system on OpenWrt
This mirrors OpenWrt’s standard build architecture for static libraries.
In this section, you are going to learn
How to create a simple static library (libfoo.a) inside the OpenWrt build system?
How to patch the library sources using OpenWrt’s automatic Build/Patch mechanism?
How to export the library archive and headers to staging_dir and into the target root filesystem?
How to build a test application that links statically against the patched libfoo.a?
How to install and run the patched library and test application on OpenWrt?
1. Creating a Static Library (libfoo.a)
A static library in OpenWrt:
uses
TARGET_CCfor cross compilationarchives its object files using
TARGET_ARexports
.aand.hfiles tostaging_dirfor dependent packagesinstalls files into the target root filesystem
Patching allows modifying the library’s functionality without editing the original source tree directly.
1.1 Directory Layout
Create the package skeleton:
package/my/libfoo/
├── Makefile
├── patches/
│ └── 0001-change-add-behavior.patch
└── src/
├── foo.c
└── foo.h
A helper command (not part of OpenWrt build):
$ mkdir -p libfoo/patches libfoo/src
1.2 Source Files
src/foo.h
#ifndef FOO_H
#define FOO_H
int foo_add(int a, int b);
#endif
src/foo.c
#include "foo.h"
int foo_add(int a, int b) {
return a + b;
}
1.3 Patch File
patches/0001-change-add-behavior.patch
--- a/foo.c
+++ b/foo.c
@@ -1,5 +1,5 @@
#include "foo.h"
int foo_add(int a, int b) {
- return a + b;
+ return a + b + 1; /* patched behavior */
}
1.4 Makefile (Static Library)
package/my/libfoo/Makefile
include $(TOPDIR)/rules.mk
PKG_NAME:=libfoo
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/libfoo
SECTION:=libs
CATEGORY:=Libraries
TITLE:=Simple static foo library
endef
define Build/InstallDev
$(INSTALL_DIR) $(STAGING_DIR)/usr/include
$(INSTALL_DATA) $(PKG_BUILD_DIR)/foo.h $(STAGING_DIR)/usr/include/
$(INSTALL_DIR) $(STAGING_DIR)/usr/lib
$(INSTALL_DATA) $(PKG_BUILD_DIR)/libfoo.a $(STAGING_DIR)/usr/lib/
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
$(call Build/Patch,$(PKG_BUILD_DIR))
endef
define Build/Compile
$(TARGET_CC) $(TARGET_CFLAGS) -c $(PKG_BUILD_DIR)/foo.c -o $(PKG_BUILD_DIR)/foo.o
$(TARGET_AR) rcs $(PKG_BUILD_DIR)/libfoo.a $(PKG_BUILD_DIR)/foo.o
endef
define Package/libfoo/install
$(INSTALL_DIR) $(1)/usr/include
$(INSTALL_DATA) $(PKG_BUILD_DIR)/foo.h $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib
$(INSTALL_DATA) $(PKG_BUILD_DIR)/libfoo.a $(1)/usr/lib/
endef
$(eval $(call BuildPackage,libfoo))
1.5 Build
Build the package inside the OpenWrt tree:
make package/my/libfoo/compile V=s
Resulting package is located at (path may vary by target):
bin/targets/x86/64/packages/libfoo*.ipk
1.6 Copy + Install
Copy the resulting package to the OpenWrt target (for example, a QEMU VM):
scp -P <QEMU_PORT> <path_to_ipk> root@127.0.0.1:/tmp/
Install on the target:
opkg install /tmp/libfoo*.ipk
Directory Layout
patches/contains all source modifications applied by OpenWrt.src/holds the original upstream source.The library is built entirely inside
$(PKG_BUILD_DIR)to maintain isolation from your version-controlled source tree.
Source Files
foo_addis the public API exported fromlibfoo.a.The header file (
foo.h) is exported tostaging_dirfor dependent packages.The object file (
foo.o) is archived into a static library viaTARGET_AR.
Patch File
The patch modifies the mathematical behavior of
foo_add().OpenWrt automatically applies all patches using
Build/PatchduringBuild/Prepare.Patch format uses standard kernel-style unified diff (
git format-patch-style).
Makefile Sections
define Package/libfoo
Defines metadata for
menuconfigand classification.SECTIONdecides the top-level grouping.CATEGORYcontrols where it appears in package lists.TITLEprovides a human-readable description.It does not affect compilation directly.
define Build/InstallDev
Installs headers and library into:
$(STAGING_DIR)/usr/include$(STAGING_DIR)/usr/lib
This is critical because:
Compilation of downstream packages requires these files.
Static linking with
-lfoowill uselibfoo.afromstaging_dir.
define Build/Prepare
Performs three essential steps:
Creates a clean build directory.
Copies sources into
$(PKG_BUILD_DIR).Applies patches using:
$(call Build/Patch,$(PKG_BUILD_DIR))
This ensures patched sources are compiled, not upstream originals.
define Build/Compile
Builds the static library:
foo.c→foo.o(compiled withTARGET_CCandTARGET_CFLAGS)foo.o→libfoo.aviaTARGET_AR
Static libraries contain compiled code but do not support dynamic symbol lookup (unlike shared libraries).
define Package/libfoo/install
Installs the library and header into the final root filesystem: -
/usr/include/foo.h-/usr/lib/libfoo.aThese files are present on the OpenWrt target device after installation.
Build + Install
The resulting
libfoopackage is a standard OpenWrt.ipk.Installation places
libfoo.ainside/usr/libon the target.The header is also installed for optional on-target development or debugging.
The runtime behavior is now patched (
+1added to every sum).
2. Test Application for Static Library (foo-test)
This test application:
includes the patched header
links statically with
libfoo.ademonstrates the patched behavior of
foo_add()
2.1 Directory Structure
Create the package skeleton:
package/my/foo-test/
├── Makefile
├── patches/
│ └── 0001-change-message.patch
└── src/
└── main.c
src/main.c
#include <stdio.h>
#include "foo.h"
int main(void) {
int result = foo_add(3, 4);
printf("foo_add(3, 4) returned %d\n", result);
return 0;
}
2.2 Patch File
patches/0001-change-message.patch
--- a/main.c
+++ b/main.c
@@ -4,6 +4,6 @@
int main(void) {
int result = foo_add(3, 4);
- printf("foo_add(3, 4) returned %d\n", result);
+ printf("Patched: foo_add(3, 4) = %d\n", result);
return 0;
}
2.3 Makefile (foo-test)
package/my/foo-test/Makefile
include $(TOPDIR)/rules.mk
PKG_NAME:=foo-test
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/foo-test
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Test program for libfoo (static library)
DEPENDS:=+libfoo
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
$(call Build/Patch,$(PKG_BUILD_DIR))
endef
define Build/Compile
$(TARGET_CC) $(TARGET_CFLAGS) -I$(STAGING_DIR)/usr/include -L$(STAGING_DIR)/usr/lib $(PKG_BUILD_DIR)/main.c -lfoo -o $(PKG_BUILD_DIR)/foo-test
endef
define Package/foo-test/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/foo-test $(1)/usr/bin/
endef
$(eval $(call BuildPackage,foo-test))
2.4 Build + Install
Build the static library (if not already built):
make package/my/libfoo/compile V=s
Build the test application:
make package/my/foo-test/compile V=s
Copy via SCP:
scp -P <QEMU_PORT> <path_to_ipk> root@127.0.0.1:/tmp/
Install on the OpenWrt target:
opkg install /tmp/foo-test*.ipk
Run inside OpenWrt:
# foo-test
Expected Output:
Patched: foo_add(3, 4) = 8
Directory + Sources
foo_addinmain.cresolves to the static library’s patched version.The entire library code is embedded directly into the final binary (static linking).
The
patches/directory demonstrates that you can patch consumer applications in OpenWrt in the same way you patch libraries.
Patch File
The patch modifies only the printed output, not the calculation logic.
This illustrates that behavior changes can live either in the library or in the consuming application—or both.
Makefile
DEPENDS:=+libfooensures:libfoois built beforefoo-test.libfoois installed when you installfoo-test(dependency resolution).
-I$(STAGING_DIR)/usr/includeexposesfoo.hto the compiler.-L$(STAGING_DIR)/usr/liballows-lfooto findlibfoo.a.Static linking embeds the code from
libfoo.ainto the final binary:The resulting
/usr/bin/foo-testdoes not requirelibfoo.aat runtime to execute the logic (though the package dependency still brings it in).
Runtime Behavior
Unpatched logic:
foo_add(3, 4) = 7
Patched library logic:
Internal computation becomes
a + b + 1, so:foo_add(3, 4) = 8
Because of static linking:
Even if
libfoo.ais later replaced or removed in the filesystem, the already-installedfoo-testbinary continues to use the embedded patched logic.Rebuilding
foo-testagainst a different version oflibfoo.awill bake the new behavior into a fresh binary, again via static linking.
End-to-End Summary
libfoodemonstrates:Source → patched source → object → static library.
Export to
staging_dirand the target root filesystem.
foo-testdemonstrates:A consumer package that picks up the patched library via
Build/Patch+staging_dir.How OpenWrt’s build system composes libraries and applications with clear, reproducible patching and dependency management.