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/Patch`` - How to export the library + headers to ``staging_dir`` and rootfs - How 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 .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to create a simple static library (``libfoo.a``) inside the OpenWrt build system? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to patch the library sources using OpenWrt’s automatic ``Build/Patch`` mechanism? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to export the library archive and headers to ``staging_dir`` and into the target root filesystem? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to build a test application that links statically against the patched ``libfoo.a``? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to install and run the patched library and test application on OpenWrt? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Topics in this section, * :ref:`Step 1: Creating a Static Library (libfoo.a) ` * :ref:`Step 1.1: Technical Notes ` * :ref:`Step 2: Test Application for Static Library (foo-test) ` * :ref:`Step 2.1: Technical Notes ` ------------------------------------------------------- 1. Creating a Static Library (libfoo.a) ------------------------------------------------------- A static library in OpenWrt: - uses ``TARGET_CC`` for cross compilation - archives its object files using ``TARGET_AR`` - exports ``.a`` and ``.h`` files to ``staging_dir`` for dependent packages - installs files into the target root filesystem Patching allows modifying the library’s functionality *without* editing the original source tree directly. .. _openwrt_staticlib_step1: .. _openwrt_staticlib_step1_1: .. tab-set:: .. tab-item:: Step 1: Creating a Static Library (libfoo.a) **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`` .. code-block:: c #ifndef FOO_H #define FOO_H int foo_add(int a, int b); #endif ``src/foo.c`` .. code-block:: c #include "foo.h" int foo_add(int a, int b) { return a + b; } **1.3 Patch File** ``patches/0001-change-add-behavior.patch`` .. code-block:: diff --- 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`` .. code-block:: make 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: .. code-block:: bash 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): .. code-block:: bash scp -P root@127.0.0.1:/tmp/ Install on the target: .. code-block:: bash opkg install /tmp/libfoo*.ipk .. tab-item:: Step 1.1: Technical Notes **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_add`` is the public API exported from ``libfoo.a``. - The header file (``foo.h``) is exported to ``staging_dir`` for dependent packages. - The object file (``foo.o``) is archived into a static library via ``TARGET_AR``. **Patch File** - The patch modifies the mathematical behavior of ``foo_add()``. - OpenWrt automatically applies all patches using ``Build/Patch`` during ``Build/Prepare``. - Patch format uses standard kernel-style unified diff (``git format-patch``-style). **Makefile Sections** ``define Package/libfoo`` - Defines metadata for ``menuconfig`` and classification. - ``SECTION`` decides the top-level grouping. - ``CATEGORY`` controls where it appears in package lists. - ``TITLE`` provides 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 ``-lfoo`` will use ``libfoo.a`` from ``staging_dir``. ``define Build/Prepare`` - Performs three essential steps: 1. Creates a clean build directory. 2. Copies sources into ``$(PKG_BUILD_DIR)``. 3. 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 with ``TARGET_CC`` and ``TARGET_CFLAGS``) - ``foo.o`` → ``libfoo.a`` via ``TARGET_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.a`` - These files are present on the OpenWrt target device after installation. **Build + Install** - The resulting ``libfoo`` package is a standard OpenWrt ``.ipk``. - Installation places ``libfoo.a`` inside ``/usr/lib`` on the target. - The header is also installed for optional on-target development or debugging. - The runtime behavior is now patched (``+1`` added to every sum). ------------------------------------------------------- 2. Test Application for Static Library (foo-test) ------------------------------------------------------- This test application: - includes the patched header - links statically with ``libfoo.a`` - demonstrates the patched behavior of ``foo_add()`` .. _openwrt_staticlib_step2: .. _openwrt_staticlib_step2_1: .. tab-set:: .. tab-item:: Step 2: Test Application for Static Library (foo-test) **2.1 Directory Structure** Create the package skeleton: :: package/my/foo-test/ ├── Makefile ├── patches/ │ └── 0001-change-message.patch └── src/ └── main.c ``src/main.c`` .. code-block:: c #include #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`` .. code-block:: diff --- 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`` .. code-block:: make 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): .. code-block:: bash make package/my/libfoo/compile V=s Build the test application: .. code-block:: bash make package/my/foo-test/compile V=s Copy via SCP: .. code-block:: bash scp -P root@127.0.0.1:/tmp/ Install on the OpenWrt target: .. code-block:: bash opkg install /tmp/foo-test*.ipk Run inside OpenWrt: :: # foo-test Expected Output: :: Patched: foo_add(3, 4) = 8 .. tab-item:: Step 2.1: Technical Notes **Directory + Sources** - ``foo_add`` in ``main.c`` resolves 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:=+libfoo`` ensures: - ``libfoo`` is built before ``foo-test``. - ``libfoo`` is installed when you install ``foo-test`` (dependency resolution). - ``-I$(STAGING_DIR)/usr/include`` exposes ``foo.h`` to the compiler. - ``-L$(STAGING_DIR)/usr/lib`` allows ``-lfoo`` to find ``libfoo.a``. - Static linking embeds the code from ``libfoo.a`` into the final binary: - The resulting ``/usr/bin/foo-test`` does **not** require ``libfoo.a`` at 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.a`` is later replaced or removed in the filesystem, the already-installed ``foo-test`` binary continues to use the embedded patched logic. - Rebuilding ``foo-test`` against a *different* version of ``libfoo.a`` will bake the new behavior into a fresh binary, again via static linking. **End-to-End Summary** - ``libfoo`` demonstrates: - Source → patched source → object → static library. - Export to ``staging_dir`` and the target root filesystem. - ``foo-test`` demonstrates: - 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.