diff --git a/0001-fix-kernel-patch-ACCEPTED-change-to-DEACTIVED-after-.patch b/0001-fix-kernel-patch-ACCEPTED-change-to-DEACTIVED-after-.patch index 6870278c75898bc3f7657a89d4e38c17598aefbc..0247673b90e641fe2780e6f09b778bbe56cc3974 100644 --- a/0001-fix-kernel-patch-ACCEPTED-change-to-DEACTIVED-after-.patch +++ b/0001-fix-kernel-patch-ACCEPTED-change-to-DEACTIVED-after-.patch @@ -22,5 +22,5 @@ index 22efa93..fd5160c 100644 .map_err(|e| anyhow!("Kpatch: {}", std::io::Error::from(e))) } -- -2.34.1 +2.43.0 diff --git a/0002-fix-some-clean-code-problem.patch b/0002-fix-some-clean-code-problem.patch index 7f14c6071b177d7242b4128a4c39c60bc9f41834..3532244f8d5dc7a69f8d2c0c6d51023272a3e077 100644 --- a/0002-fix-some-clean-code-problem.patch +++ b/0002-fix-some-clean-code-problem.patch @@ -93,5 +93,5 @@ index d9a356b..7880eed 100644 + (unsigned long)insn->kaddr); +} -- -2.34.1 +2.43.0 diff --git a/0003-syscared-fix-active-accepted-patch-failure-issue.patch b/0003-syscared-fix-active-accepted-patch-failure-issue.patch index 21beac2375feb2f2269d3ae81e4c396a8ca5e503..5061b530cb85101e7c00cb3fb548f303fbcb4727 100644 --- a/0003-syscared-fix-active-accepted-patch-failure-issue.patch +++ b/0003-syscared-fix-active-accepted-patch-failure-issue.patch @@ -45,5 +45,5 @@ index 3a724db..a17703c 100644 impl Drop for PatchManager { -- -2.34.1 +2.43.0 diff --git a/0004-upatch-helper-fix-object-upatch-id-duplicated-issue.patch b/0004-upatch-helper-fix-object-upatch-id-duplicated-issue.patch index e491876bc4a5098d56ef4855febbbc058719b595..2eaf1eff5e9fe3c759fa0d299c0d16f9d4d9dffc 100644 --- a/0004-upatch-helper-fix-object-upatch-id-duplicated-issue.patch +++ b/0004-upatch-helper-fix-object-upatch-id-duplicated-issue.patch @@ -46,5 +46,5 @@ index d98b167..303088c 100644 } -- -2.34.1 +2.43.0 diff --git a/0005-syscare-build-update-README.md.patch b/0005-syscare-build-update-README.md.patch index 54860be1723ab05207490851f649bf07db708c2a..b6089157882e959d5e4716a8bb643c8ab7cf3725 100644 --- a/0005-syscare-build-update-README.md.patch +++ b/0005-syscare-build-update-README.md.patch @@ -166,5 +166,5 @@ index e0cf66e..1f05c74 100644 -rw-r--r--. 1 dev dev 186M Nov 12 00:00 kernel-5.10.0-60.80.0.104.oe2203-HP001-1-1.x86_64.src.rpm -rw-r--r--. 1 dev dev 11K Nov 12 00:00 patch-kernel-5.10.0-60.80.0.104.oe2203-HP001-1-1.x86_64.rpm -- -2.34.1 +2.43.0 diff --git a/0006-syscared-support-saving-restoring-patch-status-by-op.patch b/0006-syscared-support-saving-restoring-patch-status-by-op.patch index 9699cd80093ffc95f39362fcd81971f1243417ed..98a29a064c5304c229880e18da92fe7501326fc0 100644 --- a/0006-syscared-support-saving-restoring-patch-status-by-op.patch +++ b/0006-syscared-support-saving-restoring-patch-status-by-op.patch @@ -158,5 +158,5 @@ index a17703c..48c583a 100644 } -- -2.34.1 +2.43.0 diff --git a/0007-project-update-Cargo.lock.patch b/0007-project-update-Cargo.lock.patch index 4649e6cac8cd04475e758827ec6c705344ad7af1..ab73a334fa853852e4e7f8bdf8d9088b13693702 100644 --- a/0007-project-update-Cargo.lock.patch +++ b/0007-project-update-Cargo.lock.patch @@ -30,5 +30,5 @@ index 48ca061..30f2015 100644 [[package]] -- -2.34.1 +2.43.0 diff --git a/0008-all-remove-signal-handler.patch b/0008-all-remove-signal-handler.patch index d5980eacdf00d4ad9cb2a3c19648e51cc07c7c70..1ee216106a938fea2619f46e25cd8cdb6652e1b7 100644 --- a/0008-all-remove-signal-handler.patch +++ b/0008-all-remove-signal-handler.patch @@ -1,4 +1,4 @@ -From 5daf18084f8d2865db24b6627d2d28d9234bcf5c Mon Sep 17 00:00:00 2001 +From f41f2c12673a1a5a25fd1f149220836c93d3b677 Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 10 Feb 2025 10:49:49 +0800 Subject: [PATCH] all: remove signal handler @@ -70,5 +70,5 @@ index 77b256a..09f907b 100644 args, logger, -- -2.34.1 +2.43.0 diff --git a/0009-project-update-Cargo.lock.patch b/0009-project-update-Cargo.lock.patch index d1bcc1844d402762201fcb7df04cf26c4c47d53e..eb1abaf613c26fe6431d2ee5b322f5ba2b690524 100644 --- a/0009-project-update-Cargo.lock.patch +++ b/0009-project-update-Cargo.lock.patch @@ -1,4 +1,4 @@ -From bed35b84e7cbeaafd16c9c01650243ae548a9c14 Mon Sep 17 00:00:00 2001 +From 276ea96f44628f4ccc8dc43962502d414a24e80b Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 10 Feb 2025 11:49:40 +0800 Subject: [PATCH] project: update Cargo.lock @@ -62,5 +62,5 @@ index 30f2015..6ae9f36 100644 name = "windows-targets" version = "0.48.5" -- -2.34.1 +2.43.0 diff --git a/0010-syscare-remove-working-directory-check.patch b/0010-syscare-remove-working-directory-check.patch index a4a9e1a056864bc895c98bae8bd0859fb55b1ab2..978a0339e248049942bcb8670caa9b863a18ac12 100644 --- a/0010-syscare-remove-working-directory-check.patch +++ b/0010-syscare-remove-working-directory-check.patch @@ -1,4 +1,4 @@ -From 707475a7361b6f16ca2b9e62f222b1982a57e506 Mon Sep 17 00:00:00 2001 +From 1f34ba0818560a0da448e9034876082e9a30a129 Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 10 Feb 2025 14:21:29 +0800 Subject: [PATCH] syscare: remove working directory check @@ -48,5 +48,5 @@ index 4f034a0..5e7299d 100644 impl std::fmt::Display for Arguments { -- -2.34.1 +2.43.0 diff --git a/0011-all-fix-cargo-clippy-warnings.patch b/0011-all-fix-cargo-clippy-warnings.patch index 3f8ef8e36611a31784b056dbb5ed28b65d10c344..c3893dc03f44e3e27e6a4f0100f9c730cb4a7fd6 100644 --- a/0011-all-fix-cargo-clippy-warnings.patch +++ b/0011-all-fix-cargo-clippy-warnings.patch @@ -1,4 +1,4 @@ -From 2af03514afcc797cac3125c7b04ee0f5eae43aa0 Mon Sep 17 00:00:00 2001 +From d0cc1a3324f2f82f08297b4dcea4a3d9387b203c Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 10 Feb 2025 10:29:00 +0800 Subject: [PATCH] all: fix cargo clippy warnings @@ -158,5 +158,5 @@ index e8c7cdf..a6e8fb9 100644 type Offset = R::Offset; -- -2.34.1 +2.43.0 diff --git a/0012-upatch-diff-format-code.patch b/0012-upatch-diff-format-code.patch index 5620e49910ab966000ed07e12dcb53c9ceddeecc..b7f88a246c505a687657e9f1ba616fb8cf9cf545 100644 --- a/0012-upatch-diff-format-code.patch +++ b/0012-upatch-diff-format-code.patch @@ -1,4 +1,4 @@ -From 47ac3ae5e1e6cb790df0f24433e2d8071ef7541f Mon Sep 17 00:00:00 2001 +From 5ed5d301e6cb5b0c6df8071e988fda7e0deb90df Mon Sep 17 00:00:00 2001 From: renoseven Date: Sat, 15 Feb 2025 17:05:06 +0800 Subject: [PATCH] upatch-diff: format code @@ -8353,5 +8353,5 @@ index 5e2abce..ec0148f 100644 \ No newline at end of file +#endif /* __UPATCH_PATCH_H_ */ -- -2.34.1 +2.43.0 diff --git a/0013-upatch-manage-format-code.patch b/0013-upatch-manage-format-code.patch index f915b9c6cdc157ae36af7bab4b75d5c02d493eee..827436dd9594b97aa3c6355db060b9fb656b27ae 100644 --- a/0013-upatch-manage-format-code.patch +++ b/0013-upatch-manage-format-code.patch @@ -1,4 +1,4 @@ -From 9356dfedc5a242df73febf40eccb428347c3a7ce Mon Sep 17 00:00:00 2001 +From d608ecb58f076aacf3c6da115f6933a48a2b3925 Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 17 Feb 2025 15:41:50 +0800 Subject: [PATCH] upatch-manage: format code @@ -8176,5 +8176,5 @@ index 96b8a51..49be6e3 100644 + upatch_action_t action); +#endif -- -2.34.1 +2.43.0 diff --git a/0014-upatch-manage-remove-build-id-check.patch b/0014-upatch-manage-remove-build-id-check.patch index ce549f7846421175dd791a7c1418c2fe311d108e..f0f922fe080fbbce068d9670610007796f3efa9d 100644 --- a/0014-upatch-manage-remove-build-id-check.patch +++ b/0014-upatch-manage-remove-build-id-check.patch @@ -1,4 +1,4 @@ -From a54cbc3202a1409c040e988043310d4d09d91bc8 Mon Sep 17 00:00:00 2001 +From 5dbcd7bfb65297cf9f49a6a6f1eed5e8ad282cfe Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 18 Feb 2025 15:48:28 +0800 Subject: [PATCH] upatch-manage: remove build id check @@ -84,5 +84,5 @@ index 18e0d2c..2193f9a 100644 bool is_note_section(GElf_Word); -- -2.34.1 +2.43.0 diff --git a/0015-upatch-diff-print-detail-changes.patch b/0015-upatch-diff-print-detail-changes.patch index 92e76e4525caa2e760e69f9134ab54380bde7104..363a37749e28fc3f5b86d0b3636586ecd33c2cff 100644 --- a/0015-upatch-diff-print-detail-changes.patch +++ b/0015-upatch-diff-print-detail-changes.patch @@ -1,4 +1,4 @@ -From b923bc814e0863fe61456fe6a40bd8b2be2be993 Mon Sep 17 00:00:00 2001 +From 1b46259d34d45135b86712699d59d54e662a067b Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 18 Feb 2025 16:03:26 +0800 Subject: [PATCH] upatch-diff: print detail changes @@ -92,5 +92,5 @@ index 677b8bc..9d2641a 100644 void upatch_dump_kelf(struct upatch_elf *uelf) -- -2.34.1 +2.43.0 diff --git a/0016-upatch-diff-ignore-useless-sections.patch b/0016-upatch-diff-ignore-useless-sections.patch index e46b30968f9457c0dfba0153086a39e762cdeb9a..88dc5ee852f4b263a83791ee7c6c60ec939fc9b1 100644 --- a/0016-upatch-diff-ignore-useless-sections.patch +++ b/0016-upatch-diff-ignore-useless-sections.patch @@ -1,4 +1,4 @@ -From c3df3930682334433437ee58a4e8dce46d956929 Mon Sep 17 00:00:00 2001 +From e7bcedcfd2d7f5578de82b426e52cb307596ec98 Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 18 Feb 2025 17:04:46 +0800 Subject: [PATCH] upatch-diff: ignore useless sections @@ -78,5 +78,5 @@ index f5a48da..bc2c1ac 100644 do { \ (_new) = calloc(1, sizeof(*(_new))); \ -- -2.34.1 +2.43.0 diff --git a/0017-upatch-manage-enrich-log-output.patch b/0017-upatch-manage-enrich-log-output.patch index 4772eba31dea19484fc7958e84cf5fc61d6cd194..4b803420ae1d9f82c898b4856e04ac0768d6a0b0 100644 --- a/0017-upatch-manage-enrich-log-output.patch +++ b/0017-upatch-manage-enrich-log-output.patch @@ -1,4 +1,4 @@ -From 425450509c8903d86b6f0c17acdbd52cb46c99fe Mon Sep 17 00:00:00 2001 +From 1dc077014395da0bba4bb05573c7a1f96f44c6a5 Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 18 Feb 2025 17:50:34 +0800 Subject: [PATCH] upatch-manage: enrich log output @@ -336,5 +336,5 @@ index 8cc4867..474d857 100644 for (size_t i = 0; i < stack_size / sizeof(*stack); i++) { -- -2.34.1 +2.43.0 diff --git a/0018-project-change-build-options.patch b/0018-project-change-build-options.patch index f186c2a715cec7723ea8a953d4dfe63c08c415a4..b3e1871918da2631588333afac42fd32c3c815ff 100644 --- a/0018-project-change-build-options.patch +++ b/0018-project-change-build-options.patch @@ -1,4 +1,4 @@ -From 553e54f017d502da712c8d0e04bf8b40d634beac Mon Sep 17 00:00:00 2001 +From 6838a888ae87a78b9c0fc95edaee4ae1e93ce72a Mon Sep 17 00:00:00 2001 From: renoseven Date: Sat, 22 Feb 2025 17:38:12 +0800 Subject: [PATCH] project: change build options @@ -54,5 +54,5 @@ index ff219b9..37da26d 100644 message("-- Build flags: ${PROJECT_C_BUILD_FLAGS}") message("-- Link flags: ${PROJECT_C_LINK_FLAGS}") -- -2.34.1 +2.43.0 diff --git a/0019-upatch-manage-increase-jump-table-size-to-4096.patch b/0019-upatch-manage-increase-jump-table-size-to-4096.patch index 125e6842ebf58d1f1da729257c219468dd260645..10bc8efac4c15776617e211a957a271baa924ad7 100644 --- a/0019-upatch-manage-increase-jump-table-size-to-4096.patch +++ b/0019-upatch-manage-increase-jump-table-size-to-4096.patch @@ -1,4 +1,4 @@ -From c9b05a6f5cb8d55c6836a310542e133a0c0d1fb2 Mon Sep 17 00:00:00 2001 +From 2058c41ed76846943809a7b3f3d25c70e0b7a646 Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 21 Feb 2025 14:37:37 +0800 Subject: [PATCH] upatch-manage: increase jump table size to 4096 @@ -22,5 +22,5 @@ index 2193f9a..6f62c7d 100644 #define UPATCH_HEADER_LEN 6 #define UPATCH_ID_LEN 40 -- -2.34.1 +2.43.0 diff --git a/0020-upatch-manage-fix-failed-to-attach-non-exist-process.patch b/0020-upatch-manage-fix-failed-to-attach-non-exist-process.patch index 2af16d82566b2bfc16826bbe5708877d1e397aef..2bcd4bd004b38e640d9f7119eb7b398141c453b1 100644 --- a/0020-upatch-manage-fix-failed-to-attach-non-exist-process.patch +++ b/0020-upatch-manage-fix-failed-to-attach-non-exist-process.patch @@ -1,4 +1,4 @@ -From 18a08f1f324974535bb00c7eed79f36fe5b8a4b7 Mon Sep 17 00:00:00 2001 +From c20f78f14932e89ee59b5c84feb03acf7bcdf0f6 Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 21 Feb 2025 16:29:44 +0800 Subject: [PATCH] upatch-manage: fix 'failed to attach non-exist process' issue @@ -63,5 +63,5 @@ index 2d0845c..ab21891 100644 } while (1); -- -2.34.1 +2.43.0 diff --git a/0021-upatch-manage-fix-failed-to-alloc-patch-memory-issue.patch b/0021-upatch-manage-fix-failed-to-alloc-patch-memory-issue.patch index 734871e75fa8902135acef535a79d50f90998ffb..f6e8c3d284e91242dc2d50dea1fb87ad57579071 100644 --- a/0021-upatch-manage-fix-failed-to-alloc-patch-memory-issue.patch +++ b/0021-upatch-manage-fix-failed-to-alloc-patch-memory-issue.patch @@ -1,4 +1,4 @@ -From b41af7298a34f3f53d611f54aa1f93d1a8bc3e99 Mon Sep 17 00:00:00 2001 +From 175fd2d549806ae979bb64699d20ee55bcbf28a8 Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 21 Feb 2025 15:15:31 +0800 Subject: [PATCH] upatch-manage: fix 'failed to alloc patch memory' issue @@ -522,5 +522,5 @@ index 23cbced..e8d5dee 100644 #endif -- -2.34.1 +2.43.0 diff --git a/0022-upatch-manage-fix-misresolve-.rela.plt-symbol-issue.patch b/0022-upatch-manage-fix-misresolve-.rela.plt-symbol-issue.patch index d1037ec9472a622e7df26388433a356a71407bfd..3b5e2406a50ad768d8f879bb2ce233fc1f23318b 100644 --- a/0022-upatch-manage-fix-misresolve-.rela.plt-symbol-issue.patch +++ b/0022-upatch-manage-fix-misresolve-.rela.plt-symbol-issue.patch @@ -1,4 +1,4 @@ -From b70bc8123941d7e7cf103c6c6731423297943935 Mon Sep 17 00:00:00 2001 +From 978eb28fe3cf5d13a404ce9fd51908513c62acdc Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 21 Feb 2025 11:13:00 +0800 Subject: [PATCH] upatch-manage: fix 'misresolve .rela.plt symbol' issue @@ -25,5 +25,5 @@ index b564578..53debe6 100644 } -- -2.34.1 +2.43.0 diff --git a/0023-upatch-manage-fix-misresolve-std-cout-symbol-issue.patch b/0023-upatch-manage-fix-misresolve-std-cout-symbol-issue.patch index d8a6e2df0c556aa4675f29ea6b40797595ae365a..20b611172f7f5b9a7dcf8a39bf0000a32572d451 100644 --- a/0023-upatch-manage-fix-misresolve-std-cout-symbol-issue.patch +++ b/0023-upatch-manage-fix-misresolve-std-cout-symbol-issue.patch @@ -1,4 +1,4 @@ -From 13faaebfb43c8c374c14fdd049881c4558b497f0 Mon Sep 17 00:00:00 2001 +From c420f30ac116fd189fcc3c580046925c245902e5 Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 21 Feb 2025 15:24:19 +0800 Subject: [PATCH] upatch-manage: fix 'misresolve std::cout symbol' issue @@ -25,5 +25,5 @@ index 5e0ddea..ed33925 100644 jmp_addr, tls_addr); -- -2.34.1 +2.43.0 diff --git a/0024-upatch-manage-fix-access-global-variable-coredump-is.patch b/0024-upatch-manage-fix-access-global-variable-coredump-is.patch index f6bdd4d92a5302c5d805b0284b513b2805644182..e58c3190631522b045fba6953c86152b1cb0ea22 100644 --- a/0024-upatch-manage-fix-access-global-variable-coredump-is.patch +++ b/0024-upatch-manage-fix-access-global-variable-coredump-is.patch @@ -1,4 +1,4 @@ -From daef225331b197ad5f047f36989f7eddeac63eec Mon Sep 17 00:00:00 2001 +From fea9b28ef8bfd4b54429023a05a9725ff8ef582e Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 21 Feb 2025 14:34:09 +0800 Subject: [PATCH] upatch-manage: fix 'access global variable coredump' issue @@ -89,5 +89,5 @@ index 863bcc5..62efc13 100644 unsigned long, unsigned long); -- -2.34.1 +2.43.0 diff --git a/0025-project-update-CMakeLists.txt.patch b/0025-project-update-CMakeLists.txt.patch index a81c9405151015ea73368aa0f35c5d28ca374124..cff9fce2768a9729a47207f1506ca9f265195213 100644 --- a/0025-project-update-CMakeLists.txt.patch +++ b/0025-project-update-CMakeLists.txt.patch @@ -1,4 +1,4 @@ -From 63b964867143e5715e597e39a8209dec163e7de0 Mon Sep 17 00:00:00 2001 +From 43bc48384958e408f571e69c253e1d2c1d5b14c0 Mon Sep 17 00:00:00 2001 From: renoseven Date: Sat, 22 Feb 2025 18:24:36 +0800 Subject: [PATCH] project: update CMakeLists.txt @@ -32,5 +32,5 @@ index 37da26d..ca9fdfe 100644 message("-- Build flags: ${PROJECT_C_BUILD_FLAGS}") message("-- Link flags: ${PROJECT_C_LINK_FLAGS}") -- -2.34.1 +2.43.0 diff --git a/0026-upatch-manage-fix-compile-issue.patch b/0026-upatch-manage-fix-compile-issue.patch index 96a9cd143a41700ccdda938d34975b26518d2efd..014dee3d1afc801f958d6051993a50ebad8a070e 100644 --- a/0026-upatch-manage-fix-compile-issue.patch +++ b/0026-upatch-manage-fix-compile-issue.patch @@ -1,4 +1,4 @@ -From cd6381cf0634fb68b1f2b387adf3c559e8a923ba Mon Sep 17 00:00:00 2001 +From e20166661ed9e22fd64c5e20c840e672b8fc3a3e Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 24 Feb 2025 10:07:17 +0800 Subject: [PATCH] upatch-manage: fix compile issue @@ -26,5 +26,5 @@ index 123ecfe..c3b07ac 100644 if (new_start > hole->start) { struct vm_hole *left = NULL; -- -2.34.1 +2.43.0 diff --git a/0027-syscare-build-supress-rpm-signature-warning.patch b/0027-syscare-build-supress-rpm-signature-warning.patch index d8d7b9058d176758d3e6e79056e88bf4db9f0359..346ae6825185b7336d3f31199aa25bbad7b2c1f5 100644 --- a/0027-syscare-build-supress-rpm-signature-warning.patch +++ b/0027-syscare-build-supress-rpm-signature-warning.patch @@ -1,4 +1,4 @@ -From 8a84b063e8e0d8261118cbe51b7dde68ffe0edf8 Mon Sep 17 00:00:00 2001 +From 7fe8dd23ff85c40074fb2e52d15523b9d653cd6d Mon Sep 17 00:00:00 2001 From: renoseven Date: Sat, 1 Mar 2025 14:47:37 +0800 Subject: [PATCH] syscare-build: supress rpm signature warning @@ -29,5 +29,5 @@ index 4054f67..ee7f19f 100644 .arg(pkg_path) .run_with_output()?; -- -2.34.1 +2.43.0 diff --git a/0028-syscare-build-support-custom-kernel-configuration.patch b/0028-syscare-build-support-custom-kernel-configuration.patch index 4570893d4c17ae0bd64d532b6eaa17a83b20551e..2b543dac79d72cfb8fa14b261dcdb8beeabd3418 100644 --- a/0028-syscare-build-support-custom-kernel-configuration.patch +++ b/0028-syscare-build-support-custom-kernel-configuration.patch @@ -1,4 +1,4 @@ -From 986c8ff3fcc4af577a90569dba5fe1585649c685 Mon Sep 17 00:00:00 2001 +From 3ce548a787062061335c9fc55f3b247e67ec25a2 Mon Sep 17 00:00:00 2001 From: renoseven Date: Fri, 28 Feb 2025 11:30:33 +0800 Subject: [PATCH] syscare-build: support custom kernel configuration @@ -204,5 +204,5 @@ index 8cbe9b3..f109b64 100644 fs::find_file( directory, -- -2.34.1 +2.43.0 diff --git a/0029-common-add-unit-test-for-process.patch b/0029-common-add-unit-test-for-process.patch index be0217b505f7d686ac6caae9fb0751605df5dff3..646e5b17f66093966930057f717b441e229de06a 100644 --- a/0029-common-add-unit-test-for-process.patch +++ b/0029-common-add-unit-test-for-process.patch @@ -1,4 +1,4 @@ -From 32df34f1fe208e9f99e58022f872ba68feeb14b8 Mon Sep 17 00:00:00 2001 +From b5343541223f1e41cec0c9406452965a09547036 Mon Sep 17 00:00:00 2001 From: xuxiaojuan1 Date: Tue, 7 May 2024 17:33:38 +0800 Subject: [PATCH] common: add unit test for process @@ -57,5 +57,5 @@ index d992372..4569587 100644 + } +} -- -2.34.1 +2.43.0 diff --git a/0030-syscare-build-add-unit-test-for-spec-writer.patch b/0030-syscare-build-add-unit-test-for-spec-writer.patch index 4e14b5980efbe29e33a10691d23beae8fc33624e..b644388be3db928080d61912898979431467d2fe 100644 --- a/0030-syscare-build-add-unit-test-for-spec-writer.patch +++ b/0030-syscare-build-add-unit-test-for-spec-writer.patch @@ -1,4 +1,4 @@ -From 001ddc89ebd935a556e0676d2aba244824570f03 Mon Sep 17 00:00:00 2001 +From 9bd4b7be1c266ffe51b526334588a75e0669fc50 Mon Sep 17 00:00:00 2001 From: xuxiaojuan1 Date: Wed, 8 May 2024 17:56:03 +0800 Subject: [PATCH] syscare-build: add unit test for spec writer @@ -46,5 +46,5 @@ index dc33459..97218c2 100644 + } +} -- -2.34.1 +2.43.0 diff --git a/0031-syscare-remove-root-permission-check.patch b/0031-syscare-remove-root-permission-check.patch index 11bb73c33d9dba11bc6abf12ef268edf8dc1db41..b5c4174c7ed4bb8057fc18d109a9863935fd7d32 100644 --- a/0031-syscare-remove-root-permission-check.patch +++ b/0031-syscare-remove-root-permission-check.patch @@ -1,4 +1,4 @@ -From b0d436bf14fc724a7820ebddda32da58114de8ff Mon Sep 17 00:00:00 2001 +From 87f0004364d811f74a444126052448bad1b8b49e Mon Sep 17 00:00:00 2001 From: renoseven Date: Wed, 12 Feb 2025 11:24:40 +0800 Subject: [PATCH] syscare: remove root permission check @@ -53,5 +53,5 @@ index 98e719d..b9606f0 100644 SubCommand::Info { identifiers } => { let mut patch_list = vec![]; -- -2.34.1 +2.43.0 diff --git a/0032-syscared-remove-upatch-failure-process-blacklist.patch b/0032-syscared-remove-upatch-failure-process-blacklist.patch index 28ab928f84727a6ebb5643a91659d5a981ebb649..fdbfd4fab39172af218796f8eed32c8ad4b5405c 100644 --- a/0032-syscared-remove-upatch-failure-process-blacklist.patch +++ b/0032-syscared-remove-upatch-failure-process-blacklist.patch @@ -1,4 +1,4 @@ -From fa19bb309e2cce60eba73a08408d228d9ef45a72 Mon Sep 17 00:00:00 2001 +From 025bbee900f0221ef318d10e9352579f3fdece0b Mon Sep 17 00:00:00 2001 From: renoseven Date: Wed, 12 Feb 2025 11:31:15 +0800 Subject: [PATCH] syscared: remove upatch failure process blacklist @@ -103,5 +103,5 @@ index 73ef94e..a1e9002 100644 if result.is_ok() { patch_entity.remove_process(pid) -- -2.34.1 +2.43.0 diff --git a/0033-syscare-optimize-syscare-info-syscare-target-output.patch b/0033-syscare-optimize-syscare-info-syscare-target-output.patch index 6579b2831e8c46bed6baa37bb47831f2c179ea3b..fbc6fa46db9cd1c63156e029e3993ea34d474882 100644 --- a/0033-syscare-optimize-syscare-info-syscare-target-output.patch +++ b/0033-syscare-optimize-syscare-info-syscare-target-output.patch @@ -1,4 +1,4 @@ -From 2d71a1c6c368392359869b3b641e2592db5bb859 Mon Sep 17 00:00:00 2001 +From a5e69b1d3c96c91b19aa7c99788991ef30cc0654 Mon Sep 17 00:00:00 2001 From: renoseven Date: Thu, 13 Feb 2025 09:40:23 +0800 Subject: [PATCH] syscare: optimize 'syscare info' & 'syscare target' output @@ -49,5 +49,5 @@ index b9606f0..5ad9cc2 100644 fn show_patch_status(status_list: impl IntoIterator) { -- -2.34.1 +2.43.0 diff --git a/0034-syscared-impl-syscare-rescan-command.patch b/0034-syscared-impl-syscare-rescan-command.patch index 73985ce731da4f51e9c8a5d0d984529cdfe8aea4..1ec86c8e25780f7b8980bdbf7f1c2815781c4db1 100644 --- a/0034-syscared-impl-syscare-rescan-command.patch +++ b/0034-syscared-impl-syscare-rescan-command.patch @@ -1,4 +1,4 @@ -From b0553998fe9b9947ead6d880d36b61721155bfbb Mon Sep 17 00:00:00 2001 +From eefbace91270aa2eea679df00fd626d4b3158b34 Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 11 Feb 2025 17:29:13 +0800 Subject: [PATCH] syscared: impl 'syscare rescan' command @@ -40,5 +40,5 @@ index 8216428..38c9f37 100644 + } } -- -2.34.1 +2.43.0 diff --git a/0035-syscare-impl-syscare-rescan-command.patch b/0035-syscare-impl-syscare-rescan-command.patch index b09dda4a3e3ecacdd1a86acdccb44532849e141a..6829f6d53e44f7809245603e752c0516203837ba 100644 --- a/0035-syscare-impl-syscare-rescan-command.patch +++ b/0035-syscare-impl-syscare-rescan-command.patch @@ -1,4 +1,4 @@ -From 5636956c349b431b3467cfa993caf4469df37f8d Mon Sep 17 00:00:00 2001 +From 4960d37c168ebdf50ec6f1a402f375c0f9abf6e5 Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 11 Feb 2025 17:38:41 +0800 Subject: [PATCH] syscare: impl 'syscare rescan' command @@ -55,5 +55,5 @@ index bb01a67..d328398 100644 + } } -- -2.34.1 +2.43.0 diff --git a/0036-syscared-impl-log-config.patch b/0036-syscared-impl-log-config.patch index 6e000433918a2a3c3f4f7e4292e9f586dfe77879..2b999d995ce0f67bfb32091ed89aa2cdab80e361 100644 --- a/0036-syscared-impl-log-config.patch +++ b/0036-syscared-impl-log-config.patch @@ -1,4 +1,4 @@ -From 90dfc6929f4e1bd170f034093bef8aea2b92ad89 Mon Sep 17 00:00:00 2001 +From 6777e5a3fb5fad1c5d5c14f0522c71e1876abb3f Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 11 Feb 2025 18:10:38 +0800 Subject: [PATCH] syscared: impl log config @@ -137,5 +137,5 @@ index 42e9564..9ddfbe8 100644 fs::set_permissions( -- -2.34.1 +2.43.0 diff --git a/0037-syscared-impl-patch-blacklist.patch b/0037-syscared-impl-patch-blacklist.patch index e2d79cf5d7ba2cc298755cb383cebad5b81dc004..7b4094f457fee60632d2b044b36a0a2be344f0ce 100644 --- a/0037-syscared-impl-patch-blacklist.patch +++ b/0037-syscared-impl-patch-blacklist.patch @@ -1,4 +1,4 @@ -From f2c7cfcda4c6dbd09cbf5419a33d5dc646bc84d6 Mon Sep 17 00:00:00 2001 +From f1a905f95611481325b505412725d2e99ae14e9c Mon Sep 17 00:00:00 2001 From: renoseven Date: Wed, 12 Feb 2025 10:27:50 +0800 Subject: [PATCH] syscared: impl patch blacklist @@ -406,5 +406,5 @@ index 863f65b..4ec35f4 100644 patch_file, sys_file, -- -2.34.1 +2.43.0 diff --git a/0041-syscared-fix-patch-target-is-blocked-issue.patch b/0038-syscared-fix-patch-target-is-blocked-issue.patch similarity index 92% rename from 0041-syscared-fix-patch-target-is-blocked-issue.patch rename to 0038-syscared-fix-patch-target-is-blocked-issue.patch index ea4d93f30caad73ca9499e9194b4b8b075037f83..6d9eec8242f96579a7670e46e52a1ca53104e0c3 100644 --- a/0041-syscared-fix-patch-target-is-blocked-issue.patch +++ b/0038-syscared-fix-patch-target-is-blocked-issue.patch @@ -1,4 +1,4 @@ -From d8d7101dd519bbbd145c0735309bd0c3b5c7f253 Mon Sep 17 00:00:00 2001 +From 92dfc8490e548b0d6b687e39aa00d0185e874d84 Mon Sep 17 00:00:00 2001 From: renoseven Date: Mon, 3 Mar 2025 17:22:10 +0800 Subject: [PATCH] syscared: fix 'patch target is blocked' issue @@ -22,5 +22,5 @@ index 349d495..bdcdd4c 100644 patch.target_name.to_string_lossy(), ); -- -2.34.1 +2.43.0 diff --git a/0043-syscare-build-fix-detect-incorrect-kernel-source-dir.patch b/0039-syscare-build-fix-detect-incorrect-kernel-source-dir.patch similarity index 96% rename from 0043-syscare-build-fix-detect-incorrect-kernel-source-dir.patch rename to 0039-syscare-build-fix-detect-incorrect-kernel-source-dir.patch index 3d5bb4de31a56277669954dd9ec3eb68a7c175c5..3dc603d762eeee7ce7c9afeeca5f54cca3c236e9 100644 --- a/0043-syscare-build-fix-detect-incorrect-kernel-source-dir.patch +++ b/0039-syscare-build-fix-detect-incorrect-kernel-source-dir.patch @@ -1,4 +1,4 @@ -From 0d2a4fc0e9748a06c55d1962c5c794988f488338 Mon Sep 17 00:00:00 2001 +From 74500953bc32852cd53796306290c4508d123ec7 Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 4 Mar 2025 11:32:31 +0800 Subject: [PATCH] syscare-build: fix 'detect incorrect kernel source directory' @@ -40,5 +40,5 @@ index accad0a..07d4a85 100644 let oot_source_dir = oot_module_entry.map(|build_entry| build_entry.build_source.clone()); -- -2.34.1 +2.43.0 diff --git a/0044-upatch-manage-fix-compile-failure.patch b/0040-upatch-manage-fix-compile-failure.patch similarity index 91% rename from 0044-upatch-manage-fix-compile-failure.patch rename to 0040-upatch-manage-fix-compile-failure.patch index 0ac19b7ac920646b5b342cec483e1a1d99063a3e..7c9240b1f3f7f2378e4b28ea41b51355c968aa82 100644 --- a/0044-upatch-manage-fix-compile-failure.patch +++ b/0040-upatch-manage-fix-compile-failure.patch @@ -1,4 +1,4 @@ -From c8ee23919b11e0a0d3ed469af4213d692035d2f7 Mon Sep 17 00:00:00 2001 +From 689cb97db736a05d676f1a492910f6d96960cc5f Mon Sep 17 00:00:00 2001 From: renoseven Date: Wed, 5 Mar 2025 15:22:45 +0800 Subject: [PATCH] upatch-manage: fix compile failure @@ -22,5 +22,5 @@ index 80a817d..7dd82eb 100644 GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; -- -2.34.1 +2.43.0 diff --git a/0045-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch b/0041-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch similarity index 97% rename from 0045-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch rename to 0041-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch index 00125ce9038cff2c4a296e03b129d18e0e1c77d9..6317ae8a674a50726be5565efe048dd899aa9666 100644 --- a/0045-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch +++ b/0041-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch @@ -1,4 +1,4 @@ -From 48fc62552f397d56c87f1d1879f5a88a9fee9835 Mon Sep 17 00:00:00 2001 +From 872947a5300e276f234944b44dcb169d3bf918a5 Mon Sep 17 00:00:00 2001 From: renoseven Date: Thu, 6 Mar 2025 10:37:17 +0800 Subject: [PATCH] syscared: fix 'kmod patch conflicts with itself' issue @@ -74,5 +74,5 @@ index beab803..49f1185 100644 .filter(|patch_function| !patch_function.is_same_function(uuid, function)) }) -- -2.34.1 +2.43.0 diff --git a/0046-common-fix-failed-to-acquire-flock-issue.patch b/0042-common-fix-failed-to-acquire-flock-issue.patch similarity index 98% rename from 0046-common-fix-failed-to-acquire-flock-issue.patch rename to 0042-common-fix-failed-to-acquire-flock-issue.patch index 0e2fccd887c6bfbe79f274242cb140b154d25429..99e061fe4dd4f6f0bb8995011f3b59a6a3a8a2d1 100644 --- a/0046-common-fix-failed-to-acquire-flock-issue.patch +++ b/0042-common-fix-failed-to-acquire-flock-issue.patch @@ -1,4 +1,4 @@ -From fef1fc6d7bc4a9e8805553a202ffde05ed570f23 Mon Sep 17 00:00:00 2001 +From 9234af9c63fee5f33f403b1aa5cffba1f6195c9e Mon Sep 17 00:00:00 2001 From: renoseven Date: Tue, 11 Mar 2025 12:01:48 +0800 Subject: [PATCH] common: fix 'failed to acquire flock' issue @@ -173,5 +173,5 @@ index bfd39d7..29bff55 100644 drop(shared_lock1); drop(shared_lock2); -- -2.34.1 +2.43.0 diff --git a/0047-syscare-rewrite-syscare-cli.patch b/0043-syscare-rewrite-syscare-cli.patch similarity index 99% rename from 0047-syscare-rewrite-syscare-cli.patch rename to 0043-syscare-rewrite-syscare-cli.patch index cae0376603781d422519ba658804a6c20d228cdf..d4f1713457507f56f2e4fc83bc1e7dac7c1b3a66 100644 --- a/0047-syscare-rewrite-syscare-cli.patch +++ b/0043-syscare-rewrite-syscare-cli.patch @@ -1,4 +1,4 @@ -From 2cebc4d7a83c1b7837b62ef5f9723a0b93ea18c4 Mon Sep 17 00:00:00 2001 +From 460621cf824ee7f38e7261ef43d3560d6c5e3c17 Mon Sep 17 00:00:00 2001 From: renoseven Date: Thu, 6 Mar 2025 16:48:10 +0800 Subject: [PATCH] syscare: rewrite syscare cli @@ -1348,5 +1348,5 @@ index f2c9244..0000000 - } -} -- -2.34.1 +2.43.0 diff --git a/0044-upatch-diff-fix-static-local-var-correlate.patch b/0044-upatch-diff-fix-static-local-var-correlate.patch new file mode 100644 index 0000000000000000000000000000000000000000..12f38e4ef65534823f06f77bbae551df440632c7 --- /dev/null +++ b/0044-upatch-diff-fix-static-local-var-correlate.patch @@ -0,0 +1,26 @@ +From 03b965c1147379eebad0547b618edb7e60949c87 Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Thu, 27 Mar 2025 12:03:23 +0800 +Subject: [PATCH] upatch-diff: fix static local var correlate + +Signed-off-by: Jvle +--- + upatch-diff/elf-correlate.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 979453b..35039b2 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -432,7 +432,7 @@ void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + if (bundled != patched_bundled) { + ERROR("bundle mismatch for symbol %s", sym->name); + } +- if (!bundled && sym->sec->twin != patched_sym->sec) { ++ if (!bundled && sym->sec->twin && sym->sec->twin != patched_sym->sec) { + ERROR("sections %s and %s aren't correlated for symbol %s", + sym->sec->name, patched_sym->sec->name, sym->name); + } +-- +2.43.0 + diff --git a/0045-upatch-manage-fix-first-object-cannot-find-mem-hole.patch b/0045-upatch-manage-fix-first-object-cannot-find-mem-hole.patch new file mode 100644 index 0000000000000000000000000000000000000000..635bc54bc37b309782dfda280e9c7ba5b1a83286 --- /dev/null +++ b/0045-upatch-manage-fix-first-object-cannot-find-mem-hole.patch @@ -0,0 +1,33 @@ +From 8f4374b1ae8a20f6c40ff355968b8e2d2f305bc2 Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Wed, 26 Mar 2025 15:45:43 +0800 +Subject: [PATCH] upatch-manage: fix first object cannot find mem hole + +Signed-off-by: Jvle +--- + upatch-manage/upatch-process.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/upatch-manage/upatch-process.c b/upatch-manage/upatch-process.c +index c3b07ac..8e1882f 100644 +--- a/upatch-manage/upatch-process.c ++++ b/upatch-manage/upatch-process.c +@@ -889,7 +889,14 @@ struct vm_hole *find_patch_region(struct object_file *obj, size_t len) + struct obj_vm_area *vma = NULL; + list_for_each_entry(vma, &obj->vma, list) { + struct vm_hole *left_hole = obj->prev_hole; +- struct vm_hole *right_hole = next_hole(obj->prev_hole, vma_holes); ++ struct vm_hole *right_hole = NULL; ++ if (left_hole) { ++ right_hole = next_hole(left_hole, vma_holes); ++ } else { ++ if (!list_empty(vma_holes)) { ++ right_hole = list_first_entry(vma_holes, struct vm_hole, list); ++ } ++ } + + while ((left_hole != NULL) || (right_hole != NULL)) { + if (left_hole != NULL) { +-- +2.43.0 + diff --git a/0046-syscare-fix-request-is-unrecognized-issue.patch b/0046-syscare-fix-request-is-unrecognized-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..c675b27a9b56423a7e840fcdf670a5e8ce681f4b --- /dev/null +++ b/0046-syscare-fix-request-is-unrecognized-issue.patch @@ -0,0 +1,26 @@ +From da051ca015e9eec0e2108458fbb5af4d6f43a9b3 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 29 Mar 2025 17:43:59 +0800 +Subject: [PATCH] syscare: fix 'request is unrecognized' issue + +Signed-off-by: renoseven +--- + syscare/src/rpc/client.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/syscare/src/rpc/client.rs b/syscare/src/rpc/client.rs +index c609849..3428984 100644 +--- a/syscare/src/rpc/client.rs ++++ b/syscare/src/rpc/client.rs +@@ -137,7 +137,7 @@ impl RpcClient { + match error { + jsonrpc::Error::Transport(err) => self.parse_transport_error(err), + jsonrpc::Error::Json(_) => anyhow!("Response format error"), +- jsonrpc::Error::Rpc(_) => anyhow!("Request is unrecognized"), ++ jsonrpc::Error::Rpc(e) => anyhow!(e.message), + _ => anyhow!("Unknown response error"), + } + } +-- +2.43.0 + diff --git a/0047-upatch-manage-fix-alloc-patch-memory-permission-deni.patch b/0047-upatch-manage-fix-alloc-patch-memory-permission-deni.patch new file mode 100644 index 0000000000000000000000000000000000000000..f5203463635f0bddab63409fffc4ff4b20d59218 --- /dev/null +++ b/0047-upatch-manage-fix-alloc-patch-memory-permission-deni.patch @@ -0,0 +1,28 @@ +From 4686876bff5f70a2287e174c2bedb308b8e107cf Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 1 Apr 2025 18:41:37 +0800 +Subject: [PATCH] upatch-manage: fix 'alloc patch memory permission denied' + issue + +Signed-off-by: renoseven +--- + upatch-manage/upatch-patch.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/upatch-manage/upatch-patch.c b/upatch-manage/upatch-patch.c +index 4a1e568..4b1b1be 100644 +--- a/upatch-manage/upatch-patch.c ++++ b/upatch-manage/upatch-patch.c +@@ -278,8 +278,7 @@ static void *upatch_alloc(struct object_file *obj, size_t len) + log_debug("Found patch region at 0x%lx, size=0x%lx\n", addr, len); + + addr = upatch_mmap_remote(pctx, addr, len, +- PROT_READ | PROT_WRITE | PROT_EXEC, +- MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, ++ PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + (unsigned long)-1, 0); + if (addr == 0) { + log_error("Failed to map patch region, ret=%d\n", errno); +-- +2.43.0 + diff --git a/0048-upatch-diff-remove-line-macro-related-functions.patch b/0048-upatch-diff-remove-line-macro-related-functions.patch new file mode 100644 index 0000000000000000000000000000000000000000..e9690c19fbd82d8e24c698145233629e60870a5e --- /dev/null +++ b/0048-upatch-diff-remove-line-macro-related-functions.patch @@ -0,0 +1,239 @@ +From e8fc9b5b01a4e7df5d8dd6689c3d99f0844a71ab Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 1 Apr 2025 18:51:57 +0800 +Subject: [PATCH] upatch-diff: remove line macro related functions + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 208 -------------------------------------- + 1 file changed, 208 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 7ca6845..fe97528 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -166,208 +166,6 @@ out: + return 0; + } + +-bool upatch_handle_redis_line(const char *symname) +-{ +- if (!strncmp(symname, "_serverPanic", 12) || +- !strncmp(symname, "_serverAssert", 13) || +- !strncmp(symname, "_serverAssertWithInfo", 21) || +- !strncmp(symname, "rdbReportError", 14) || +- !strncmp(symname, "RedisModule__Assert", 19)) { +- return true; +- } +- +- return false; +-} +- +-/* TODO: let user support this list or generate by the compiler ? */ +-bool check_line_func(const char *symname) +-{ +- if (!strncmp(basename(g_relf_name), "redis-server", 12)) { +- return upatch_handle_redis_line(symname); +- } +- +- return false; +-} +- +-/* Determine if a section has changed only due to a __LINE__ bumber change. +- * For example, a WARN() or might_sleep() macro's embedding of the line number into an +- * instruction operand. +- */ +-static bool line_macro_change_only_x86_64(struct upatch_elf *uelf, +- struct section *sec) +-{ +- unsigned long offset; +- unsigned long insn1_len; +- unsigned long insn2_len; +- +- void *data1; +- void *data2; +- void *insn1; +- void *insn2; +- +- struct rela *rela = NULL; +- bool found_any = false; +- bool found = false; +- +- if (sec->status != CHANGED || +- is_rela_section(sec) || +- !is_text_section(sec) || +- sec->sh.sh_size != sec->twin->sh.sh_size || +- !sec->rela || +- sec->rela->status != SAME) { +- return false; +- } +- +- data1 = sec->twin->data->d_buf; +- data2 = sec->data->d_buf; +- for (offset = 0; offset < sec->sh.sh_size; offset += insn1_len) { +- insn1 = data1 + offset; +- insn2 = data2 + offset; +- +- insn1_len = insn_length(uelf, insn1); +- insn2_len = insn_length(uelf, insn2); +- if (!insn1_len || !insn2_len) { +- ERROR("decode instruction in section %s at offset 0x%lx failed", +- sec->name, offset); +- } +- +- if (insn1_len != insn2_len) { +- return false; +- } +- +- /* if insn are same, continue */ +- if (!memcmp(insn1, insn2, insn1_len)) { +- continue; +- } +- log_debug("check list for %s at 0x%lx\n", sec->name, offset); +- +- /* +- * Here we found a differece between two instructions of the +- * same length. Only ignore the change if: +- * +- * 1) the instruction match a known pattern of a '__LINE__' +- * macro immediate value which was embedded in the instruction. +- * +- * 2) the instructions are followed by certain expected relocations. +- * (white-list) +- */ +- if (!insn_is_load_immediate(uelf, insn1) || +- !insn_is_load_immediate(uelf, insn2)) { +- return false; +- } +- +- found = false; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- if (rela->offset < offset + insn1_len) { +- continue; +- } +- if (rela->string) { +- continue; +- } +- /* TODO: we may need black list ? */ +- if (check_line_func(rela->sym->name)) { +- found = true; +- break; +- } +- return false; +- } +- if (!found) { +- return false; +- } +- +- found_any = true; +- } +- +- if (!found_any) { +- ERROR("no instruction changes detected for changed section %s", +- sec->name); +- } +- +- return true; +-} +- +-static bool line_macro_change_only_aarch64(struct upatch_elf *uelf, +- struct section *sec) +-{ +- unsigned long start1; +- unsigned long start2; +- unsigned long size; +- unsigned long offset; +- +- struct rela *rela = NULL; +- bool found_any = false; +- bool found = false; +- +- unsigned int mov_imm_mask = ((1<<16) - 1)<<5; +- unsigned long insn_len = insn_length(uelf, NULL); +- +- if (sec->status != CHANGED || +- is_rela_section(sec) || +- !is_text_section(sec) || +- sec->sh.sh_size != sec->twin->sh.sh_size || +- !sec->rela || +- sec->rela->status != SAME) { +- return false; +- } +- +- start1 = (unsigned long)sec->twin->data->d_buf; +- start2 = (unsigned long)sec->data->d_buf; +- size = sec->sh.sh_size; +- for (offset = 0; offset < size; offset += insn_len) { +- if (!memcmp((void *)start1 + offset, (void *)start2 + offset, +- insn_len)) { +- continue; +- } +- +- /* verify it's a mov immediate to w1 */ +- if ((*(unsigned int *)(start1 + offset) & ~mov_imm_mask) != +- (*(unsigned int *)(start2 + offset) & ~mov_imm_mask)) { +- return false; +- } +- +- found = false; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- if (rela->offset < offset + insn_len) { +- continue; +- } +- if (rela->string) { +- continue; +- } +- /* TODO: we may need black list ? */ +- if (check_line_func(rela->sym->name)) { +- found = true; +- break; +- } +- return false; +- } +- if (!found) { +- return false; +- } +- +- found_any = true; +- } +- +- if (!found_any) { +- ERROR("no instruction changes detected for changed section %s", +- sec->name); +- } +- +- return true; +-} +- +-static bool line_macro_change_only(struct upatch_elf *uelf, struct section *sec) +-{ +- switch (uelf->arch) { +- case AARCH64: +- return line_macro_change_only_aarch64(uelf, sec); +- case X86_64: +- return line_macro_change_only_x86_64(uelf, sec); +- default: +- ERROR("unsupported arch"); +- } +- return false; +-} +- + static void update_section_status(struct section *sec, enum status status) + { + if (sec == NULL) { +@@ -397,12 +195,6 @@ void upatch_compare_sections(struct upatch_elf *uelf) + } else { + compare_correlated_section(sec, sec->twin); + } +- /* exclude WARN-only, might_sleep changes */ +- if (line_macro_change_only(uelf, sec)) { +- log_debug("reverting line number section %s status to SAME\n", +- sec->name); +- sec->status = SAME; +- } + /* sync status */ + update_section_status(sec, sec->status); + update_section_status(sec->twin, sec->status); +-- +2.43.0 + diff --git a/0049-upatch-build-support-override-line-macros.patch b/0049-upatch-build-support-override-line-macros.patch new file mode 100644 index 0000000000000000000000000000000000000000..58f8078ed6861ba97d1bf4d3c055d9d5e2fe172f --- /dev/null +++ b/0049-upatch-build-support-override-line-macros.patch @@ -0,0 +1,113 @@ +From e217de3518df8b292409524ded68f4a8c8736081 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 31 Mar 2025 10:12:49 +0800 +Subject: [PATCH] upatch-build: support override line macros + +1. add "--keep-line-macros" argument +2. replace all "__LINE__" macros in source files to "0" + +Signed-off-by: renoseven +--- + upatch-build/src/args.rs | 4 ++++ + upatch-build/src/main.rs | 14 ++++++++++++++ + upatch-build/src/project.rs | 23 +++++++++++++++++++++-- + 3 files changed, 39 insertions(+), 2 deletions(-) + +diff --git a/upatch-build/src/args.rs b/upatch-build/src/args.rs +index 7462856..cb9bbef 100644 +--- a/upatch-build/src/args.rs ++++ b/upatch-build/src/args.rs +@@ -90,6 +90,10 @@ pub struct Arguments { + #[clap(short, long, default_value = DEFAULT_OUTPUT_DIR)] + pub output_dir: PathBuf, + ++ /// Keep line macro unchanged ++ #[clap(long)] ++ pub keep_line_macros: bool, ++ + /// Skip compiler version check (not recommended) + #[clap(long)] + pub skip_compiler_check: bool, +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 09f907b..51956d8 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -281,6 +281,13 @@ impl UpatchBuild { + .prepare() + .with_context(|| format!("Failed to prepare {}", project))?; + ++ if !self.args.keep_line_macros { ++ info!("Overriding line macros"); ++ project ++ .override_line_macros() ++ .context("Failed to override line macros")?; ++ } ++ + info!("Building '{}'", project); + project + .build() +@@ -300,6 +307,13 @@ impl UpatchBuild { + .apply_patches() + .with_context(|| format!("Failed to patch {}", project))?; + ++ if !self.args.keep_line_macros { ++ info!("Overriding line macros"); ++ project ++ .override_line_macros() ++ .context("Failed to override line macros")?; ++ } ++ + info!("Building '{}'", project); + project + .build() +diff --git a/upatch-build/src/project.rs b/upatch-build/src/project.rs +index 0caa0a2..636d00f 100644 +--- a/upatch-build/src/project.rs ++++ b/upatch-build/src/project.rs +@@ -17,7 +17,7 @@ use std::{ + ffi::{OsStr, OsString}, + fs::File, + io::Write, +- os::unix::ffi::OsStrExt, ++ os::unix::ffi::{OsStrExt as _, OsStringExt}, + path::{Path, PathBuf}, + }; + +@@ -27,7 +27,7 @@ use log::{debug, Level}; + use which::which; + + use crate::{args::Arguments, build_root::BuildRoot, compiler::CompilerInfo, dwarf::ProducerType}; +-use syscare_common::{concat_os, fs, process::Command}; ++use syscare_common::{concat_os, ffi::OsStrExt, fs, process::Command}; + + const PATCH_BIN: &str = "patch"; + const UPATCH_HELPER_BIN: &str = "upatch-helper"; +@@ -175,6 +175,25 @@ impl Project<'_> { + Ok(()) + } + ++ pub fn override_line_macros(&self) -> Result<()> { ++ const LINE_MACRO_NAME: &str = "__LINE__"; ++ const LINE_MACRO_VALUE: &str = "0"; ++ ++ let file_list = fs::list_files(self.source_dir, fs::TraverseOptions { recursive: true })?; ++ for file_path in file_list { ++ let old_contents = fs::read(&file_path)?; ++ let new_contents = OsStr::from_bytes(&old_contents) ++ .replace(LINE_MACRO_NAME, LINE_MACRO_VALUE) ++ .into_vec(); ++ if old_contents != new_contents { ++ debug!("* {}", file_path.display()); ++ fs::write(&file_path, new_contents)?; ++ } ++ } ++ ++ Ok(()) ++ } ++ + pub fn prepare(&self) -> Result<()> { + self.exec_command(PREPARE_SCRIPT_NAME, self.prepare_cmd) + } +-- +2.43.0 + diff --git a/0050-syscare-build-add-keep-line-macros-argument.patch b/0050-syscare-build-add-keep-line-macros-argument.patch new file mode 100644 index 0000000000000000000000000000000000000000..9f0d388275e3413ba606e99944b5f1cf8b6a7ccd --- /dev/null +++ b/0050-syscare-build-add-keep-line-macros-argument.patch @@ -0,0 +1,85 @@ +From 831ebfc34ef12e84fb0dcd27d1dc1f876b2a3090 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 31 Mar 2025 16:19:26 +0800 +Subject: [PATCH] syscare-build: add '--keep-line-macros' argument + +Signed-off-by: renoseven +--- + syscare-build/src/args.rs | 4 ++++ + syscare-build/src/build_params.rs | 1 + + syscare-build/src/main.rs | 1 + + syscare-build/src/patch/user_patch/upatch_builder.rs | 5 +++++ + 4 files changed, 11 insertions(+) + +diff --git a/syscare-build/src/args.rs b/syscare-build/src/args.rs +index a90b77a..4a51715 100644 +--- a/syscare-build/src/args.rs ++++ b/syscare-build/src/args.rs +@@ -97,6 +97,10 @@ pub struct Arguments { + #[clap(short, long, default_value = &DEFAULT_BUILD_JOBS)] + pub jobs: usize, + ++ /// Keep line macro unchanged (userspace patch only) ++ #[clap(long)] ++ pub keep_line_macros: bool, ++ + /// Skip compiler version check (not recommended) + #[clap(long)] + pub skip_compiler_check: bool, +diff --git a/syscare-build/src/build_params.rs b/syscare-build/src/build_params.rs +index f0e391c..29dd960 100644 +--- a/syscare-build/src/build_params.rs ++++ b/syscare-build/src/build_params.rs +@@ -39,6 +39,7 @@ pub struct BuildParameters { + pub patch_description: String, + pub patch_files: Vec, + pub jobs: usize, ++ pub keep_line_macros: bool, + pub skip_compiler_check: bool, + pub skip_cleanup: bool, + pub verbose: bool, +diff --git a/syscare-build/src/main.rs b/syscare-build/src/main.rs +index dec67b2..51f9050 100644 +--- a/syscare-build/src/main.rs ++++ b/syscare-build/src/main.rs +@@ -305,6 +305,7 @@ impl SyscareBuild { + patch_type, + patch_files, + jobs: self.args.jobs, ++ keep_line_macros: self.args.keep_line_macros, + skip_compiler_check: self.args.skip_compiler_check, + skip_cleanup: self.args.skip_cleanup, + verbose: self.args.verbose, +diff --git a/syscare-build/src/patch/user_patch/upatch_builder.rs b/syscare-build/src/patch/user_patch/upatch_builder.rs +index 9165380..a69373c 100644 +--- a/syscare-build/src/patch/user_patch/upatch_builder.rs ++++ b/syscare-build/src/patch/user_patch/upatch_builder.rs +@@ -54,6 +54,7 @@ struct UBuildParameters { + patch_target: PackageInfo, + patch_description: String, + patch_files: Vec, ++ keep_line_macros: bool, + skip_compiler_check: bool, + verbose: bool, + } +@@ -181,6 +182,7 @@ impl UserPatchBuilder { + patch_target: build_params.build_entry.target_pkg.to_owned(), + patch_description: build_params.patch_description.to_owned(), + patch_files: build_params.patch_files.to_owned(), ++ keep_line_macros: build_params.keep_line_macros, + skip_compiler_check: build_params.skip_compiler_check, + verbose: build_params.verbose, + }; +@@ -223,6 +225,9 @@ impl UserPatchBuilder { + .arg("--output-dir") + .arg(&ubuild_params.patch_output_dir); + ++ if ubuild_params.keep_line_macros { ++ cmd_args.arg("--keep-line-macros"); ++ } + if ubuild_params.skip_compiler_check { + cmd_args.arg("--skip-compiler-check"); + } +-- +2.43.0 + diff --git a/0051-common-fs-glob-wildcard-path-includes-current-direct.patch b/0051-common-fs-glob-wildcard-path-includes-current-direct.patch new file mode 100644 index 0000000000000000000000000000000000000000..befd5a30b952d28762b40d5c39af5f657661dce3 --- /dev/null +++ b/0051-common-fs-glob-wildcard-path-includes-current-direct.patch @@ -0,0 +1,25 @@ +From b5cc04bed8d3bd87bf507990af3f2ef10413d6eb Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 3 Apr 2025 15:47:44 +0800 +Subject: [PATCH] common: fs::glob wildcard path includes current directory + +Signed-off-by: renoseven +--- + syscare-common/src/fs/fs_impl.rs | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/syscare-common/src/fs/fs_impl.rs b/syscare-common/src/fs/fs_impl.rs +index 30c11b7..58f97c3 100644 +--- a/syscare-common/src/fs/fs_impl.rs ++++ b/syscare-common/src/fs/fs_impl.rs +@@ -620,6 +620,7 @@ pub fn glob>(path: P) -> io::Result> { + if let Component::Normal(pattern) = components[comp_idx] { + if pattern == WILDCARD_RECURSIVE { + if let Ok(read_dir) = self::read_dir(&curr_dir) { ++ stack.push((curr_dir.clone(), comp_idx + 1)); + for dir_entry in read_dir.flatten() { + let next_path = dir_entry.path(); + if next_path.is_dir() { +-- +2.43.0 + diff --git a/0052-upatch-build-only-replace-line-macros-for-source-fil.patch b/0052-upatch-build-only-replace-line-macros-for-source-fil.patch new file mode 100644 index 0000000000000000000000000000000000000000..61429442930b2213f4e8e1e519695c20877af7bd --- /dev/null +++ b/0052-upatch-build-only-replace-line-macros-for-source-fil.patch @@ -0,0 +1,72 @@ +From e8f8c432b08dcdfdfcb93fc6d131929145052ce2 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 11 Apr 2025 15:30:36 +0800 +Subject: [PATCH] upatch-build: only replace line macros for source files + +1. add '--source-ext' argument +2. check extension whitelist before replacing '__LINE__' macros + +Signed-off-by: renoseven +--- + upatch-build/src/args.rs | 5 +++++ + upatch-build/src/project.rs | 7 +++++++ + 2 files changed, 12 insertions(+) + +diff --git a/upatch-build/src/args.rs b/upatch-build/src/args.rs +index cb9bbef..5871f82 100644 +--- a/upatch-build/src/args.rs ++++ b/upatch-build/src/args.rs +@@ -23,6 +23,7 @@ use which::which; + use super::{CLI_ABOUT, CLI_NAME, CLI_VERSION}; + + const DEFAULT_EMPTY_VALUE: &str = ""; ++const DEFAULT_SOURCE_EXT: [&str; 8] = ["h", "hpp", "hxx", "c", "cpp", "cxx", "in", "inc"]; + const DEFAULT_COMPILERS: [&str; 2] = ["gcc", "g++"]; + const DEFAULT_BUILD_ROOT: &str = "."; + const DEFAULT_OUTPUT_DIR: &str = "."; +@@ -50,6 +51,10 @@ pub struct Arguments { + #[clap(short, long)] + pub source_dir: PathBuf, + ++ /// Source file extension(s) ++ #[clap(long, multiple = true, default_values = &DEFAULT_SOURCE_EXT)] ++ pub source_ext: Vec, ++ + /// Build compiler(s) + #[clap(long, multiple = true, default_values = &DEFAULT_COMPILERS)] + pub compiler: Vec, +diff --git a/upatch-build/src/project.rs b/upatch-build/src/project.rs +index 636d00f..d563c02 100644 +--- a/upatch-build/src/project.rs ++++ b/upatch-build/src/project.rs +@@ -49,6 +49,7 @@ pub struct Project<'a> { + name: OsString, + build_root: &'a BuildRoot, + source_dir: &'a Path, ++ source_ext: &'a [OsString], + prepare_cmd: &'a OsStr, + build_cmd: &'a OsStr, + clean_cmd: &'a OsStr, +@@ -92,6 +93,7 @@ impl<'a> Project<'a> { + .to_os_string(), + build_root, + source_dir: args.source_dir.as_path(), ++ source_ext: args.source_ext.as_slice(), + prepare_cmd: args.prepare_cmd.as_os_str(), + build_cmd: args.build_cmd.as_os_str(), + clean_cmd: args.clean_cmd.as_os_str(), +@@ -181,6 +183,11 @@ impl Project<'_> { + + let file_list = fs::list_files(self.source_dir, fs::TraverseOptions { recursive: true })?; + for file_path in file_list { ++ let file_ext = file_path.extension().unwrap_or_default(); ++ if self.source_ext.iter().all(|ext| ext != file_ext) { ++ continue; ++ } ++ + let old_contents = fs::read(&file_path)?; + let new_contents = OsStr::from_bytes(&old_contents) + .replace(LINE_MACRO_NAME, LINE_MACRO_VALUE) +-- +2.43.0 + diff --git a/0053-upatch-manage-container_of-use-void-instead-of-char.patch b/0053-upatch-manage-container_of-use-void-instead-of-char.patch new file mode 100644 index 0000000000000000000000000000000000000000..83b4bf584f609c8bad52ce921f0ef39fa50fa12c --- /dev/null +++ b/0053-upatch-manage-container_of-use-void-instead-of-char.patch @@ -0,0 +1,26 @@ +From 4e8f082c4c5552fea89da476b43c55303fdf18f0 Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Wed, 23 Apr 2025 16:52:11 +0800 +Subject: [PATCH] upatch-manage: container_of: use void* instead of char* + +Signed-off-by: Jvle +--- + upatch-manage/list.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upatch-manage/list.h b/upatch-manage/list.h +index f175c29..9e55a75 100644 +--- a/upatch-manage/list.h ++++ b/upatch-manage/list.h +@@ -34,7 +34,7 @@ + */ + + #define container_of(ptr, type, member) \ +- ((type *)(((char *)(ptr)) - offsetof(type, member))) ++ ((type *)(((void *)(ptr)) - offsetof(type, member))) + + struct list_head { + struct list_head *next, *prev; +-- +2.43.0 + diff --git a/0054-CMake-move-EXECUTE_PROCESS-from-upatch-manage-to-top.patch b/0054-CMake-move-EXECUTE_PROCESS-from-upatch-manage-to-top.patch new file mode 100644 index 0000000000000000000000000000000000000000..d2bb84a895f64d1a43beb57231c604395218691b --- /dev/null +++ b/0054-CMake-move-EXECUTE_PROCESS-from-upatch-manage-to-top.patch @@ -0,0 +1,46 @@ +From 5b4ea24bb263e667c48a96be261244e0021e2dee Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Wed, 23 Apr 2025 17:00:17 +0800 +Subject: [PATCH] CMake: move EXECUTE_PROCESS from upatch-manage to top CMake + file + +Signed-off-by: Jvle +--- + CMakeLists.txt | 5 +++++ + upatch-manage/CMakeLists.txt | 4 ---- + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index ca9fdfe..d8f3258 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -4,6 +4,11 @@ cmake_minimum_required(VERSION 3.14) + + project(syscare) + ++# Arch ++EXECUTE_PROCESS(COMMAND uname -m ++ OUTPUT_VARIABLE ARCH ++ OUTPUT_STRIP_TRAILING_WHITESPACE) ++ + # Includes + include(GNUInstallDirs) + find_package(Git QUIET) +diff --git a/upatch-manage/CMakeLists.txt b/upatch-manage/CMakeLists.txt +index e09aa9c..1f7ecd0 100644 +--- a/upatch-manage/CMakeLists.txt ++++ b/upatch-manage/CMakeLists.txt +@@ -2,10 +2,6 @@ + + set(UPATCH_MANAGE "upatch-manage") + +-EXECUTE_PROCESS(COMMAND uname -m +- OUTPUT_VARIABLE ARCH +- OUTPUT_STRIP_TRAILING_WHITESPACE) +- + set(ARCH_PATH arch/${ARCH}) + + include_directories( +-- +2.43.0 + diff --git a/0055-upatch-diff-impl-section-type-checking.patch b/0055-upatch-diff-impl-section-type-checking.patch new file mode 100644 index 0000000000000000000000000000000000000000..a2a91c132a2e5a613af48d337a7aa319f781fdcf --- /dev/null +++ b/0055-upatch-diff-impl-section-type-checking.patch @@ -0,0 +1,127 @@ +From 8a872ea234403dcee20b0f7af4db54b8fc3a1699 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Apr 2025 10:12:09 +0800 +Subject: [PATCH] upatch-diff: impl section type checking + +Signed-off-by: renoseven +--- + upatch-diff/elf-common.h | 78 +++++++++++++++++++++++++++++----------- + 1 file changed, 57 insertions(+), 21 deletions(-) + +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index bc2c1ac..5e63b09 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -49,31 +49,40 @@ + } \ + } while (0) + +-static inline bool is_rela_section(struct section *sec) ++static inline bool is_text_section(struct section *sec) + { +- /* +- * An architecture usually only accepts one type. +- * And, X86_64 only uses RELA +- */ +- return (sec->sh.sh_type == SHT_RELA); ++ return (sec != NULL) && ++ (sec->sh.sh_type == SHT_PROGBITS) && ++ (sec->sh.sh_flags & SHF_EXECINSTR); + } + ++static inline bool is_data_section(struct section *sec) ++{ ++ return (sec != NULL) && ++ (sec->sh.sh_type == SHT_PROGBITS) && ++ (sec->sh.sh_flags & SHF_WRITE); ++} + +-static inline bool is_text_section(struct section *sec) ++static inline bool is_rodata_section(struct section *sec) + { +- return (sec->sh.sh_type == SHT_PROGBITS && +- (sec->sh.sh_flags & SHF_EXECINSTR)); ++ return (sec != NULL) && ++ (sec->sh.sh_type == SHT_PROGBITS) && ++ ((sec->sh.sh_flags & SHF_WRITE) == 0); + } + +-static inline bool is_string_section(struct section *sec) ++static inline bool is_symtab_section(struct section *sec) + { +- return sec->sh.sh_flags & SHF_STRINGS; ++ return (sec != NULL) && (sec->sh.sh_type == SHT_SYMTAB); + } + +-/* used for c++ exception handle */ +-static inline bool is_except_section(struct section *sec) ++static inline bool is_strtab_section(struct section *sec) + { +- return !strncmp(sec->name, ".gcc_except_table", 17); ++ return (sec != NULL) && (sec->sh.sh_type == SHT_STRTAB); ++} ++ ++static inline bool is_rela_section(struct section *sec) ++{ ++ return (sec != NULL) && (sec->sh.sh_type == SHT_RELA); + } + + static inline bool is_note_section(struct section *sec) +@@ -81,7 +90,40 @@ static inline bool is_note_section(struct section *sec) + if (is_rela_section(sec)) { + sec = sec->base; + } +- return sec->sh.sh_type == SHT_NOTE; ++ return (sec->sh.sh_type == SHT_NOTE); ++} ++ ++static inline bool is_bss_section(struct section *sec) ++{ ++ return (sec != NULL) && (sec->sh.sh_type == SHT_NOBITS); ++} ++ ++static inline bool is_group_section(struct section *sec) ++{ ++ return (sec != NULL) && (sec->sh.sh_type == SHT_GROUP); ++} ++ ++static inline bool is_read_only_section(struct section *sec) ++{ ++ return (sec != NULL) && ((sec->sh.sh_flags & SHF_WRITE) == 0); ++} ++ ++static inline bool is_string_section(struct section *sec) ++{ ++ return (sec != NULL) && (sec->sh.sh_flags & SHF_STRINGS); ++} ++ ++static inline bool is_string_literal_section(struct section *sec) ++{ ++ return (sec != NULL) && ++ (sec->sh.sh_flags & SHF_STRINGS) && ++ ((sec->sh.sh_flags & SHF_WRITE) == 0); ++} ++ ++/* used for c++ exception handle */ ++static inline bool is_except_section(struct section *sec) ++{ ++ return !strncmp(sec->name, ".gcc_except_table", 17); + } + + static inline bool is_eh_frame(struct section *sec) +@@ -160,12 +202,6 @@ static inline struct section *find_section_by_name(struct list_head *list, const + return NULL; + } + +-// section like .rodata.str1. and .rodata.__func__. +-static inline bool is_string_literal_section(struct section *sec) +-{ +- return !strncmp(sec->name, ".rodata.", 8) && (strstr(sec->name, ".str") || strstr(sec->name, "__func__")); +-} +- + static inline bool has_digit_tail(char *tail) + { + if (*tail != '.') { +-- +2.43.0 + diff --git a/0056-upatch-diff-allow-string-literal-section-being-inclu.patch b/0056-upatch-diff-allow-string-literal-section-being-inclu.patch new file mode 100644 index 0000000000000000000000000000000000000000..f93a161e2befc015544e4862570033b9fce922af --- /dev/null +++ b/0056-upatch-diff-allow-string-literal-section-being-inclu.patch @@ -0,0 +1,81 @@ +From 8c5cdf3bde909616963f441decd189fedcb6b374 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Apr 2025 15:45:38 +0800 +Subject: [PATCH] upatch-diff: allow string literal section being included + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 53 +++++++++++++++++--------------- + 1 file changed, 29 insertions(+), 24 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 1c14b7a..c792f16 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -910,34 +910,39 @@ static bool has_tls_included(struct upatch_elf *uelf) + + static void verify_patchability(struct upatch_elf *uelf) + { +- struct section *sec; ++ struct section *sec = NULL; ++ struct rela *rela = NULL; + int errs = 0; + + list_for_each_entry(sec, &uelf->sections, list) { +- if (sec->status == CHANGED && !sec->include && !is_rela_section(sec)) { +- log_normal("Section '%s' changed, but not included\n", sec->name); +- errs++; +- } +- +- if (sec->status != SAME && sec->grouped) { +- log_normal("Section '%s' is changed, but it is in section group\n", +- sec->name); +- errs++; +- } +- +- if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) { +- log_normal("Section '%s' is new, but it is not supported\n", +- sec->name); +- errs++; ++ if (sec->status != SAME) { // NEW or CHANGED ++ if ((sec->include == 0) && !is_rela_section(sec)) { ++ log_warn("Section '%s' is changed, but it is not included\n", ++ sec->name); ++ errs++; ++ } ++ if ((sec->grouped != 0) || is_group_section(sec)) { ++ log_warn("Section '%s' is changed, but it is in a group\n", ++ sec->name); ++ errs++; ++ } + } +- +- if ((sec->include && sec->status != NEW) && +- (!strncmp(sec->name, ".data", 5) || +- !strncmp(sec->name, ".bss", 4)) && +- (strcmp(sec->name, ".data.unlikely") && +- strcmp(sec->name, ".data.once"))) { +- log_normal("Data section '%s' is included", sec->name); +- errs++; ++ if (sec->status != NEW) { // SAME or CHANGED ++ if (sec->rela == NULL) { ++ continue; ++ } ++ if (!is_data_section(sec) && !is_bss_section(sec)) { ++ continue; ++ } ++ list_for_each_entry(rela, &sec->rela->relas, list) { ++ if ((rela->sym == NULL) || (rela->sym->status == NEW)) { ++ continue; ++ } ++ if (!is_string_literal_section(rela->sym->sec)) { ++ log_warn("Data section '%s' is changed\n", sec->name); ++ errs++; ++ } ++ } + } + } + +-- +2.43.0 + diff --git a/0057-upatch-diff-fix-code-logic-when-drop-rela.patch b/0057-upatch-diff-fix-code-logic-when-drop-rela.patch new file mode 100644 index 0000000000000000000000000000000000000000..edc5bff629e77fcac332f3f37fefcba42a2985a4 --- /dev/null +++ b/0057-upatch-diff-fix-code-logic-when-drop-rela.patch @@ -0,0 +1,30 @@ +From 3d90b7d23c8f02792d0df6a8296cc6ecf1b714fe Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Wed, 23 Apr 2025 16:55:26 +0800 +Subject: [PATCH] upatch-diff: fix code logic when drop rela + +Signed-off-by: Jvle +--- + upatch-diff/create-diff-object.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index c792f16..8a4089d 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -978,8 +978,10 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, + list_add_tail(&sec->list, &uelf_out->sections); + sec->index = 0; + +- if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include) { +- sec->secsym = NULL; // break link to non-included section symbol ++ if (!is_rela_section(sec)) { ++ if (sec->secsym && !sec->secsym->include) { ++ sec->secsym = NULL; // break link to non-included section symbol ++ } + } + } + +-- +2.43.0 + diff --git a/0058-upatch-diff-fix-cannot-detect-some-changes-issue.patch b/0058-upatch-diff-fix-cannot-detect-some-changes-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..e1756fec63ae3758ed7e29892efd527ecd710269 --- /dev/null +++ b/0058-upatch-diff-fix-cannot-detect-some-changes-issue.patch @@ -0,0 +1,112 @@ +From 24e2572e478ac32fb0bb908b11b9bf6bcfe07bb5 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Apr 2025 18:06:32 +0800 +Subject: [PATCH] upatch-diff: fix cannot detect some changes issue + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 53 ++++++++++++++++---------------- + 1 file changed, 26 insertions(+), 27 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 8a4089d..731bbdb 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -763,24 +763,24 @@ static void include_symbol(struct symbol *sym) + + static void include_section(struct section *sec) + { +- struct rela *rela; ++ struct rela *rela = NULL; + +- if (sec->include) { ++ if (sec->include != 0) { + return; + } + + sec->include = 1; +- if (sec->secsym) { +- sec->secsym->include = 1; +- } + +- if (!sec->rela) { +- return; +- } +- +- sec->rela->include = 1; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- include_symbol(rela->sym); ++ if (!is_rela_section(sec)) { ++ if (sec->secsym != NULL) { ++ sec->secsym->include = 1; ++ } ++ if (sec->rela != NULL) { ++ sec->rela->include = 1; ++ list_for_each_entry(rela, &sec->rela->relas, list) { ++ include_symbol(rela->sym); ++ } ++ } + } + } + +@@ -808,27 +808,26 @@ static void include_standard_elements(struct upatch_elf *uelf) + list_entry(uelf->symbols.next, struct symbol, list)->include = 1; + } + +-static int include_changed_functions(struct upatch_elf *uelf) ++static int include_changes(struct upatch_elf *uelf) + { + struct symbol *sym; + int changed_nr = 0; + + list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->status == CHANGED && +- sym->type == STT_FUNC) { +- changed_nr++; +- include_symbol(sym); ++ if (sym->status != CHANGED) { ++ continue; + } +- /* exception handler is a special function */ +- if (sym->status == CHANGED && +- sym->type == STT_SECTION && +- sym->sec && is_except_section(sym->sec)) { +- log_warn("Exception section '%s' is changed\n", sym->sec->name); ++ ++ if (sym->type == STT_FUNC) { + changed_nr++; + include_symbol(sym); +- } +- if (sym->type == STT_FILE) { +- sym->include = 1; ++ } else if (sym->type == STT_SECTION) { ++ if (is_string_literal_section(sym->sec) || is_except_section(sym->sec)) { ++ changed_nr++; ++ include_symbol(sym); ++ } ++ } else if (sym->type == STT_FILE) { ++ include_symbol(sym); + } + } + +@@ -935,7 +934,7 @@ static void verify_patchability(struct upatch_elf *uelf) + continue; + } + list_for_each_entry(rela, &sec->rela->relas, list) { +- if ((rela->sym == NULL) || (rela->sym->status == NEW)) { ++ if ((rela->sym == NULL) || (rela->sym->status != CHANGED)) { + continue; + } + if (!is_string_literal_section(rela->sym->sec)) { +@@ -1064,7 +1063,7 @@ int main(int argc, char*argv[]) + + include_standard_elements(&uelf_patched); + +- num_changed = include_changed_functions(&uelf_patched); ++ num_changed = include_changes(&uelf_patched); + new_globals_exist = include_new_globals(&uelf_patched); + if (!num_changed && !new_globals_exist) { + log_normal("No functional changes\n"); +-- +2.43.0 + diff --git a/0059-upatch-diff-fix-.text-section-change-does-not-includ.patch b/0059-upatch-diff-fix-.text-section-change-does-not-includ.patch new file mode 100644 index 0000000000000000000000000000000000000000..fce488727914afc45d076364099726bb838f1ab6 --- /dev/null +++ b/0059-upatch-diff-fix-.text-section-change-does-not-includ.patch @@ -0,0 +1,66 @@ +From 66edfa93f7587ddd20166516f6d8e827030b0318 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Apr 2025 10:25:11 +0800 +Subject: [PATCH] upatch-diff: fix .text section change does not include symbol + issue + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 37 +++++++++++++++++++++++-------------- + 1 file changed, 23 insertions(+), 14 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index fe97528..6443544 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -29,24 +29,33 @@ + #include "elf-compare.h" + #include "elf-insn.h" + +-static void compare_correlated_symbol(struct symbol *sym, +- struct symbol *symtwin) ++static int compare_correlated_section(struct section *sec, struct section *twin); ++ ++static void compare_correlated_symbol(struct symbol *sym, struct symbol *twin) + { +- // compare bind and type info +- if (sym->sym.st_info != symtwin->sym.st_info || +- (sym->sec && !symtwin->sec) || +- (symtwin->sec && !sym->sec)) { +- ERROR("symbol info mismatch: %s", sym->name); ++ // symbol type & binding cannot be changed ++ if (sym->type != twin->type) { ++ ERROR("symbol '%s' type mismatched", sym->name); + } +- // check if correlated symbols have correlated sections +- if (sym->sec && symtwin->sec && sym->sec->twin != symtwin->sec) { +- ERROR("symbol changed sections: %s", sym->name); ++ if (sym->sym.st_info != twin->sym.st_info) { ++ ERROR("symbol '%s' binding mismatched", sym->name); + } +- // data object can't change size +- if (sym->type == STT_OBJECT && sym->sym.st_size != symtwin->sym.st_size) { +- ERROR("object size mismatch: %s", sym->name); ++ // object symbol size cannot be changed ++ if ((sym->type == STT_OBJECT) && (sym->sym.st_size != twin->sym.st_size)) { ++ ERROR("symbol '%s' object size mismatched", sym->name); + } +- if (sym->sym.st_shndx == SHN_UNDEF || sym->sym.st_shndx == SHN_ABS) { ++ ++ // compare symbol sections ++ if ((sym->sec != NULL) && (twin->sec != NULL)) { ++ // symbol section correlation cannot be changed ++ if (sym->sec->twin != twin->sec) { ++ ERROR("symbol '%s' section mismatched", sym->name); ++ } ++ compare_correlated_section(sym->sec, twin->sec); ++ sym->status = sym->sec->status; ++ } ++ ++ if ((sym->sym.st_shndx == SHN_UNDEF) || (sym->sym.st_shndx == SHN_ABS)) { + sym->status = SAME; + } + /* +-- +2.43.0 + diff --git a/0060-upatch-diff-fix-.data.rel-section-is-changed-issue.patch b/0060-upatch-diff-fix-.data.rel-section-is-changed-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..8560f85c0b3bed6d64dcf83473611d3e3d2b2877 --- /dev/null +++ b/0060-upatch-diff-fix-.data.rel-section-is-changed-issue.patch @@ -0,0 +1,33 @@ +From a25609c99493b219a43afa8146ba5ebdf00744eb Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Apr 2025 18:13:55 +0800 +Subject: [PATCH] upatch-diff: fix .data.rel section is changed issue + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 731bbdb..a5bc1ab 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -937,10 +937,12 @@ static void verify_patchability(struct upatch_elf *uelf) + if ((rela->sym == NULL) || (rela->sym->status != CHANGED)) { + continue; + } +- if (!is_string_literal_section(rela->sym->sec)) { +- log_warn("Data section '%s' is changed\n", sec->name); +- errs++; ++ if (is_read_only_section(sec) || ++ is_string_literal_section(rela->sym->sec)) { ++ continue; + } ++ log_warn("Data section '%s' is changed\n", sec->name); ++ errs++; + } + } + } +-- +2.43.0 + diff --git a/0061-upatch-diff-rewrite-patch-verification-logic.patch b/0061-upatch-diff-rewrite-patch-verification-logic.patch new file mode 100644 index 0000000000000000000000000000000000000000..c6f39a1643c958c913a35bdd386a4ea0617b1f61 --- /dev/null +++ b/0061-upatch-diff-rewrite-patch-verification-logic.patch @@ -0,0 +1,144 @@ +From 9d90dc4bc5b36ab2f3578f6888c874bcbb9123ff Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Apr 2025 10:14:54 +0800 +Subject: [PATCH] upatch-diff: rewrite patch verification logic + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 102 +++++++++++++++++++------------ + 1 file changed, 62 insertions(+), 40 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index a5bc1ab..980c111 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -894,64 +894,86 @@ static void include_debug_sections(struct upatch_elf *uelf) + /* currently, there si no special section need to be handled */ + static void process_special_sections(void) {} + +-static bool has_tls_included(struct upatch_elf *uelf) ++static int verify_symbol_patchability(struct upatch_elf *uelf) + { +- struct symbol *sym; ++ int err_count = 0; + ++ struct symbol *sym = NULL; + list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->include == 1 && sym->type == STT_TLS) { +- log_normal("TLS symbol '%s' is not supported", sym->name); +- return true; ++ if (sym->include == 0) { ++ continue; ++ } ++ if (sym->type == STT_TLS) { ++ log_warn("Symbol '%s' is included, but TLS is not supported\n", ++ sym->name); ++ err_count++; ++ } ++ if (sym->type == STT_GNU_IFUNC) { ++ log_warn("Symbol '%s' is included, but IFUNC is not supported\n", ++ sym->name); ++ err_count++; + } + } +- return false; ++ ++ return err_count; + } + +-static void verify_patchability(struct upatch_elf *uelf) ++static int verify_section_patchability(struct upatch_elf *uelf) + { +- struct section *sec = NULL; +- struct rela *rela = NULL; +- int errs = 0; ++ int err_count = 0; + ++ struct section *sec = NULL; + list_for_each_entry(sec, &uelf->sections, list) { +- if (sec->status != SAME) { // NEW or CHANGED +- if ((sec->include == 0) && !is_rela_section(sec)) { +- log_warn("Section '%s' is changed, but it is not included\n", +- sec->name); +- errs++; +- } +- if ((sec->grouped != 0) || is_group_section(sec)) { +- log_warn("Section '%s' is changed, but it is in a group\n", +- sec->name); +- errs++; +- } +- } +- if (sec->status != NEW) { // SAME or CHANGED +- if (sec->rela == NULL) { ++ if ((sec->status == NEW) && (sec->include == 0)) { ++ // new sections should be included ++ log_warn("Section '%s' is %s, but it is not included\n", ++ sec->name, status_str(sec->status)); ++ err_count++; ++ } else if ((sec->status == CHANGED) && (sec->include == 0)) { ++ // changed sections should be included, except rela & debug section ++ if (is_rela_section(sec) || is_debug_section(sec)) { + continue; + } +- if (!is_data_section(sec) && !is_bss_section(sec)) { +- continue; ++ log_warn("Section '%s' is %s, but it is not included\n", ++ sec->name, status_str(sec->status)); ++ err_count++; ++ } else if ((sec->status == CHANGED) && (sec->include != 0)) { ++ // changed group section cannot be included ++ if (is_group_section(sec) || (sec->grouped != 0)) { ++ log_warn("Section '%s' is %s, but it is not supported\n", ++ sec->name, status_str(sec->status)); ++ err_count++; + } +- list_for_each_entry(rela, &sec->rela->relas, list) { +- if ((rela->sym == NULL) || (rela->sym->status != CHANGED)) { +- continue; +- } +- if (is_read_only_section(sec) || +- is_string_literal_section(rela->sym->sec)) { +- continue; ++ // changed .data & .bss section cannot be included ++ if (is_data_section(sec) || is_bss_section(sec)) { ++ struct rela *rela = NULL; ++ list_for_each_entry(rela, &sec->rela->relas, list) { ++ if ((rela->sym == NULL) || (rela->sym->status != CHANGED)) { ++ continue; ++ } ++ if (is_read_only_section(rela->sym->sec) || ++ is_string_literal_section(rela->sym->sec)) { ++ continue; ++ } ++ log_warn("Section '%s' is %s, but it is not supported\n", ++ sec->name, status_str(sec->status)); ++ err_count++; + } +- log_warn("Data section '%s' is changed\n", sec->name); +- errs++; + } + } + } + +- if (errs) { +- ERROR("%d, Unsupported section changes", errs); +- } +- if (has_tls_included(uelf)) { +- ERROR("Unsupported symbol included"); ++ return err_count; ++} ++ ++static void verify_patchability(struct upatch_elf *uelf) ++{ ++ int err_count = 0; ++ ++ err_count += verify_symbol_patchability(uelf); ++ err_count += verify_section_patchability(uelf); ++ if (err_count != 0) { ++ ERROR("Found %d unexpected changes", err_count); + } + } + +-- +2.43.0 + diff --git a/0062-upatch-diff-rewrite-symbol-include-logic.patch b/0062-upatch-diff-rewrite-symbol-include-logic.patch new file mode 100644 index 0000000000000000000000000000000000000000..fd73e60a57d998c48c69cb3aac9be987495b0f05 --- /dev/null +++ b/0062-upatch-diff-rewrite-symbol-include-logic.patch @@ -0,0 +1,370 @@ +From f0204e122efa867de74b7db5573958fe5ff2919a Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 11:37:46 +0800 +Subject: [PATCH] upatch-diff: rewrite symbol include logic + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 142 ++++++++----------------------- + upatch-diff/elf-debug.c | 107 ----------------------- + upatch-diff/elf-debug.h | 2 - + 3 files changed, 34 insertions(+), 217 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 980c111..9c3bc7e 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -739,7 +739,7 @@ static void include_special_local_section(struct upatch_elf *uelf) { + static void include_section(struct section *sec); + static void include_symbol(struct symbol *sym) + { +- if (sym->include) { ++ if ((sym == NULL) || (sym->include != 0)) { + return; + } + /* +@@ -747,6 +747,8 @@ static void include_symbol(struct symbol *sym) + * might be needed: either permanently for a rela, or temporarily for + * the later creation of a dynrela. + */ ++ log_debug("Include symbol '%s', status: %s\n", ++ sym->name, status_str(sym->status)); + sym->include = 1; + /* + * For a function/object symbol, if it has a section, we only need to +@@ -756,144 +758,76 @@ static void include_symbol(struct symbol *sym) + * For section symbols, we always include the section because + * references to them can't otherwise be resolved externally. + */ +- if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME)) { ++ if ((sym->status != SAME) || (sym->type == STT_SECTION)) { + include_section(sym->sec); + } + } + + static void include_section(struct section *sec) + { +- struct rela *rela = NULL; +- +- if (sec->include != 0) { ++ if ((sec == NULL) || (sec->include != 0)) { + return; + } + ++ log_debug("Include section '%s', status: %s\n", ++ sec->name, status_str(sec->status)); + sec->include = 1; + +- if (!is_rela_section(sec)) { +- if (sec->secsym != NULL) { +- sec->secsym->include = 1; +- } +- if (sec->rela != NULL) { +- sec->rela->include = 1; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- include_symbol(rela->sym); +- } ++ if (is_rela_section(sec)) { ++ struct rela *rela = NULL; ++ list_for_each_entry(rela, &sec->relas, list) { ++ include_symbol(rela->sym); + } ++ return; ++ } else { ++ include_symbol(sec->secsym); ++ include_section(sec->rela); + } + } + + static void include_standard_elements(struct upatch_elf *uelf) + { +- struct section *sec; +- struct symbol *sym; ++ struct section *sec = NULL; + + list_for_each_entry(sec, &uelf->sections, list) { +- if (!strcmp(sec->name, ".shstrtab") || +- !strcmp(sec->name, ".strtab") || +- !strcmp(sec->name, ".symtab") || +- !strcmp(sec->name, ".rodata") || +- is_string_literal_section(sec)) { ++ if (is_symtab_section(sec) || is_strtab_section(sec)) { + include_section(sec); + } + } +- +- list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->sec && is_string_literal_section(sym->sec)) { +- sym->include = 1; +- } +- } + /* include the NULL symbol */ +- list_entry(uelf->symbols.next, struct symbol, list)->include = 1; ++ include_symbol(list_entry(uelf->symbols.next, struct symbol, list)); + } + + static int include_changes(struct upatch_elf *uelf) + { +- struct symbol *sym; +- int changed_nr = 0; ++ int count = 0; + ++ struct symbol *sym = NULL; + list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->status != CHANGED) { ++ if (sym->status == SAME) { + continue; + } + +- if (sym->type == STT_FUNC) { +- changed_nr++; ++ if ((sym->type == STT_OBJECT) || ++ (sym->type == STT_FUNC) || ++ (sym->type == STT_COMMON) || ++ (sym->type == STT_TLS) || ++ (sym->type == STT_GNU_IFUNC)) { + include_symbol(sym); ++ count++; + } else if (sym->type == STT_SECTION) { +- if (is_string_literal_section(sym->sec) || is_except_section(sym->sec)) { +- changed_nr++; +- include_symbol(sym); ++ if ((sym->sec != NULL) && ++ (is_rela_section(sym->sec) || is_debug_section(sym->sec))) { ++ continue; + } +- } else if (sym->type == STT_FILE) { +- include_symbol(sym); +- } +- } +- +- return changed_nr; +-} +- +-static int include_new_globals(struct upatch_elf *uelf) +-{ +- struct symbol *sym; +- int nr = 0; +- +- list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->bind == STB_GLOBAL && sym->sec && sym->status == NEW) { + include_symbol(sym); +- nr++; ++ count++; + } + } + +- return nr; ++ return count; + } + +-static void include_debug_sections(struct upatch_elf *uelf) +-{ +- struct rela *rela; +- struct rela *saferela; +- struct section *sec = NULL; +- struct section *eh_sec = NULL; +- +- /* include all .debug_* sections */ +- list_for_each_entry(sec, &uelf->sections, list) { +- if (is_debug_section(sec)) { +- sec->include = 1; +- if (!is_rela_section(sec) && sec->secsym) { +- sec->secsym->include = 1; +- } +- if (!is_rela_section(sec) && is_eh_frame(sec)) { +- eh_sec = sec; +- } +- } +- } +- +- /* +- * modify relocation entry here +- * remove unincluded symbol in debug relocation section +- * for eh_frame section, sync the FDE at the same time +- */ +- list_for_each_entry(sec, &uelf->sections, list) { +- if (!is_rela_section(sec) || !is_debug_section(sec)) { +- continue; +- } +- list_for_each_entry_safe(rela, saferela, &sec->relas, list) { +- // The shndex of symbol is SHN_COMMON, there is no related section +- if (rela->sym && !rela->sym->include) { +- list_del(&rela->list); +- free(rela); +- } +- } +- } +- if (eh_sec) { +- upatch_rebuild_eh_frame(eh_sec); +- } +-} +- +-/* currently, there si no special section need to be handled */ +-static void process_special_sections(void) {} +- + static int verify_symbol_patchability(struct upatch_elf *uelf) + { + int err_count = 0; +@@ -1033,9 +967,6 @@ int main(int argc, char*argv[]) + struct upatch_elf uelf_out; + struct running_elf relf; + +- int num_changed; +- int new_globals_exist; +- + memset(&arguments, 0, sizeof(arguments)); + argp_parse(&argp, argc, argv, 0, NULL, &arguments); + +@@ -1087,9 +1018,8 @@ int main(int argc, char*argv[]) + + include_standard_elements(&uelf_patched); + +- num_changed = include_changes(&uelf_patched); +- new_globals_exist = include_new_globals(&uelf_patched); +- if (!num_changed && !new_globals_exist) { ++ int change_count = include_changes(&uelf_patched); ++ if (change_count == 0) { + log_normal("No functional changes\n"); + upatch_elf_destroy(&uelf_source); + upatch_elf_destroy(&uelf_patched); +@@ -1101,10 +1031,6 @@ int main(int argc, char*argv[]) + return 0; + } + +- include_debug_sections(&uelf_patched); +- +- process_special_sections(); +- + upatch_print_changes(&uelf_patched); + + upatch_dump_kelf(&uelf_patched); +diff --git a/upatch-diff/elf-debug.c b/upatch-diff/elf-debug.c +index 9d2641a..983bf03 100644 +--- a/upatch-diff/elf-debug.c ++++ b/upatch-diff/elf-debug.c +@@ -160,110 +160,3 @@ static inline bool skip_bytes(unsigned char **iter, unsigned char *end, + *iter += len; + return true; + } +- +-void upatch_rebuild_eh_frame(struct section *sec) +-{ +- void *eh_frame; +- unsigned long long frame_size; +- +- struct rela *rela; +- unsigned char *data; +- unsigned char *data_end; +- +- unsigned int hdr_length; +- unsigned int hdr_id; +- +- unsigned long current_offset; +- unsigned int count = 0; +- +- /* sanity check */ +- if (!is_eh_frame(sec) || is_rela_section(sec) || !sec->rela) { +- return; +- } +- +- list_for_each_entry(rela, &sec->rela->relas, list) { +- count++; +- } +- +- /* currently, only delete is possible */ +- if (sec->rela->sh.sh_entsize != 0 && +- count == sec->rela->sh.sh_size / sec->rela->sh.sh_entsize) { +- return; +- } +- +- log_debug("sync modification for eh_frame\n"); +- data = sec->data->d_buf; +- data_end = sec->data->d_buf + sec->data->d_size; +- +- /* in this time, some relcation entries may have been deleted */ +- frame_size = 0; +- eh_frame = calloc(1, sec->data->d_size); +- if (!eh_frame) { +- ERROR("malloc eh_frame failed\n"); +- } +- +- /* 8 is the offset of PC begin */ +- current_offset = 8; +- list_for_each_entry(rela, &sec->rela->relas, list) { +- unsigned long offset = rela->offset; +- bool found_rela = false; +- log_debug("handle relocaton offset at 0x%lx\n", offset); +- while (data != data_end) { +- void *__src = data; +- +- log_debug("current handle offset is 0x%lx\n", current_offset); +- +- REQUIRE(skip_bytes(&data, data_end, 4), "no length to be read"); +- hdr_length = *(unsigned int *)(data - 4); +- +- REQUIRE(hdr_length != 0xffffffff, +- "64 bit .eh_frame is not supported"); +- /* if it is 0, we reach the end. */ +- if (hdr_length == 0) { +- break; +- } +- +- REQUIRE(skip_bytes(&data, data_end, 4), "no length to be read"); +- hdr_id = *(unsigned int *)(data - 4); +- +- REQUIRE(skip_bytes(&data, data_end, hdr_length - 4), +- "no length to be read"); +- if (current_offset == offset) { +- found_rela = true; +- } +- +- /* CIE or relocation releated FDE */ +- if (hdr_id == 0 || found_rela) { +- memcpy(eh_frame + frame_size, __src, hdr_length + 4); +- /* update rela offset to point to new offset, and also hdr_id */ +- if (found_rela) { +- /* 4 is the offset of hdr_id and 8 is the offset of PC begin */ +- *(unsigned long *)(eh_frame + frame_size + 4) = frame_size + 4; +- rela->offset = frame_size + 8; +- } +- +- frame_size += (hdr_length + 4); +- } else { +- log_debug("remove FDE at 0x%lx\n", current_offset); +- } +- /* hdr_length(value) + hdr_length(body) */ +- current_offset += (4 + hdr_length); +- +- if (found_rela) { +- break; +- } +- } +- } +- +- /* +- * FIXME: data may not reach the data_end, since we have found +- * all FDE for relocation entries, the only problem here is +- * we may miss the CIE, but CIE is always in the beginning ? +- */ +- +- sec->data->d_buf = eh_frame; +- sec->data->d_size = frame_size; +- sec->dbuf_source = DATA_SOURCE_ALLOC; +- +- sec->sh.sh_size = frame_size; +-} +diff --git a/upatch-diff/elf-debug.h b/upatch-diff/elf-debug.h +index 0852c94..7c8db40 100644 +--- a/upatch-diff/elf-debug.h ++++ b/upatch-diff/elf-debug.h +@@ -31,6 +31,4 @@ void upatch_print_changes(struct upatch_elf *); + + void upatch_dump_kelf(struct upatch_elf *); + +-void upatch_rebuild_eh_frame(struct section *); +- + #endif /* __UPATCH_ELF_DEBUG_H_ */ +-- +2.43.0 + diff --git a/0063-upatch-diff-allow-.rodata-section-sh_addralign-chang.patch b/0063-upatch-diff-allow-.rodata-section-sh_addralign-chang.patch new file mode 100644 index 0000000000000000000000000000000000000000..9ee6d81fef8d82af428cc292904889dd56942c69 --- /dev/null +++ b/0063-upatch-diff-allow-.rodata-section-sh_addralign-chang.patch @@ -0,0 +1,48 @@ +From 260ceee3a124b9f569b73b265cf46fbff6837f10 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 11:50:33 +0800 +Subject: [PATCH] upatch-diff: allow .rodata section sh_addralign changes + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 6443544..c31dbb9 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -131,17 +131,20 @@ static void compare_correlated_nonrela_section(struct section *sec, + static int compare_correlated_section(struct section *sec, + struct section *sectwin) + { +- /* TODO: addr align of rodata has changed. after strlen(str) >= 30, align 8 exists */ + /* compare section headers */ +- if (sec->sh.sh_type != sectwin->sh.sh_type || +- sec->sh.sh_flags != sectwin->sh.sh_flags || +- sec->sh.sh_entsize != sectwin->sh.sh_entsize || +- (sec->sh.sh_addralign != sectwin->sh.sh_addralign && +- !is_text_section(sec) && !is_string_section(sec))) { +- ERROR("%s section header details differ from %s", +- sec->name, sectwin->name); +- return -1; +- } ++ if ((sec->sh.sh_type != sectwin->sh.sh_type) || ++ (sec->sh.sh_flags != sectwin->sh.sh_flags) || ++ (sec->sh.sh_entsize != sectwin->sh.sh_entsize) || ++ ((sec->sh.sh_addralign != sectwin->sh.sh_addralign) && ++ !is_text_section(sec) && ++ !is_string_section(sec) && ++ !is_rodata_section(sec))) { ++ /* shaddralign of .rodata may be changed from 0 to 8 bytes ++ once string length is over 30 */ ++ ERROR("%s section header details differ from %s", ++ sec->name, sectwin->name); ++ return -1; ++ } + + if (is_note_section(sec)) { + sec->status = SAME; +-- +2.43.0 + diff --git a/0064-upatch-diff-allow-.debug_-section-header-changes.patch b/0064-upatch-diff-allow-.debug_-section-header-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..55d3a87c54e3330f8a566e4ef39b5e1cc7a4a7c5 --- /dev/null +++ b/0064-upatch-diff-allow-.debug_-section-header-changes.patch @@ -0,0 +1,50 @@ +From 7e95a981545700bf4f16fed70d8a00c6e410dd40 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 12:16:26 +0800 +Subject: [PATCH] upatch-diff: allow .debug_ section header changes + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 26 ++++++++++++++------------ + 1 file changed, 14 insertions(+), 12 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index c31dbb9..56451af 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -132,18 +132,20 @@ static int compare_correlated_section(struct section *sec, + struct section *sectwin) + { + /* compare section headers */ +- if ((sec->sh.sh_type != sectwin->sh.sh_type) || +- (sec->sh.sh_flags != sectwin->sh.sh_flags) || +- (sec->sh.sh_entsize != sectwin->sh.sh_entsize) || +- ((sec->sh.sh_addralign != sectwin->sh.sh_addralign) && +- !is_text_section(sec) && +- !is_string_section(sec) && +- !is_rodata_section(sec))) { +- /* shaddralign of .rodata may be changed from 0 to 8 bytes +- once string length is over 30 */ +- ERROR("%s section header details differ from %s", +- sec->name, sectwin->name); +- return -1; ++ if (!is_debug_section(sec)) { ++ if ((sec->sh.sh_type != sectwin->sh.sh_type) || ++ (sec->sh.sh_flags != sectwin->sh.sh_flags) || ++ (sec->sh.sh_entsize != sectwin->sh.sh_entsize) || ++ ((sec->sh.sh_addralign != sectwin->sh.sh_addralign) && ++ !is_text_section(sec) && ++ !is_string_section(sec) && ++ !is_rodata_section(sec))) { ++ /* shaddralign of .rodata may be changed from 0 to 8 bytes ++ once string length is over 30 */ ++ ERROR("%s section header details differ from %s", ++ sec->name, sectwin->name); ++ return -1; ++ } + } + + if (is_note_section(sec)) { +-- +2.43.0 + diff --git a/0065-upatch-diff-allow-tls-variable-changes.patch b/0065-upatch-diff-allow-tls-variable-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..78e77ee73cf28f30842a6ebe65175b29160cb4d7 --- /dev/null +++ b/0065-upatch-diff-allow-tls-variable-changes.patch @@ -0,0 +1,29 @@ +From 92d344dffe9100be71e2af3c3fcd9aaea3919bf1 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 15:28:45 +0800 +Subject: [PATCH] upatch-diff: allow tls variable changes + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 9c3bc7e..371bb81 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -837,11 +837,6 @@ static int verify_symbol_patchability(struct upatch_elf *uelf) + if (sym->include == 0) { + continue; + } +- if (sym->type == STT_TLS) { +- log_warn("Symbol '%s' is included, but TLS is not supported\n", +- sym->name); +- err_count++; +- } + if (sym->type == STT_GNU_IFUNC) { + log_warn("Symbol '%s' is included, but IFUNC is not supported\n", + sym->name); +-- +2.43.0 + diff --git a/0066-upatch-diff-allow-delete-local-static-variables.patch b/0066-upatch-diff-allow-delete-local-static-variables.patch new file mode 100644 index 0000000000000000000000000000000000000000..4f7b24b4983296a2ab51161e278e3cd89a3d0010 --- /dev/null +++ b/0066-upatch-diff-allow-delete-local-static-variables.patch @@ -0,0 +1,39 @@ +From 0f755236dd37724b9176b29dba3ec85e5f4456dc Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 17:26:52 +0800 +Subject: [PATCH] upatch-diff: allow delete local static variables + +Signed-off-by: renoseven +--- + upatch-diff/elf-correlate.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 35039b2..56ee48c 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -266,8 +266,9 @@ static void check_static_variable_correlate(struct upatch_elf *uelf_source, + continue; + } + if (!sym->twin || !relasec->twin) { +- ERROR("reference to static local variable %s in %s was removed", ++ log_warn("reference to static local variable %s in %s was removed", + sym->name, section_function_name(relasec)); ++ continue; + } + if (!find_static_twin_ref(relasec->twin, sym)) { + ERROR("static local %s has been correlated with %s, " +@@ -424,8 +425,9 @@ void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + + patched_sym = find_static_twin(relasec, sym); + if (!patched_sym) { +- ERROR("reference to static local variable %s in %s was removed", ++ log_warn("reference to static local variable %s in %s was removed", + sym->name, section_function_name(relasec)); ++ continue; + } + + patched_bundled = (patched_sym == patched_sym->sec->sym) ? 1 : 0; +-- +2.43.0 + diff --git a/0067-upatch-diff-fix-lookup_relf-returns-wrong-sympos-iss.patch b/0067-upatch-diff-fix-lookup_relf-returns-wrong-sympos-iss.patch new file mode 100644 index 0000000000000000000000000000000000000000..8c93663a8d1f6371a25638e62ec8041eb34d1ea0 --- /dev/null +++ b/0067-upatch-diff-fix-lookup_relf-returns-wrong-sympos-iss.patch @@ -0,0 +1,41 @@ +From 98dd69b509f30ba01ba4dc2310f541c661f62064 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 17:28:55 +0800 +Subject: [PATCH] upatch-diff: fix lookup_relf() returns wrong sympos issue + +Signed-off-by: renoseven +--- + upatch-diff/running-elf.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c +index d9c1d6f..b4d6d24 100644 +--- a/upatch-diff/running-elf.c ++++ b/upatch-diff/running-elf.c +@@ -125,14 +125,12 @@ bool lookup_relf(struct running_elf *relf, struct symbol *lookup_sym, + struct lookup_result *result) + { + struct debug_symbol *symbol = NULL; +- unsigned long sympos = 0; + + log_debug("looking up symbol '%s'\n", lookup_sym->name); + memset(result, 0, sizeof(*result)); + + for (int i = 0; i < relf->obj_nr; i++) { + symbol = &relf->obj_syms[i]; +- sympos++; + + if (result->symbol != NULL && symbol->type == STT_FILE) { + break; +@@ -149,7 +147,7 @@ bool lookup_relf(struct running_elf *relf, struct symbol *lookup_sym, + } + + result->symbol = symbol; +- result->sympos = sympos; ++ result->sympos = (unsigned long)i; + result->global = + ((symbol->bind == STB_GLOBAL) || (symbol->bind == STB_WEAK)); + log_debug("found symbol '%s'\n", lookup_sym->name); +-- +2.43.0 + diff --git a/0068-upatch-diff-change-include-ignored-grouped-data-type.patch b/0068-upatch-diff-change-include-ignored-grouped-data-type.patch new file mode 100644 index 0000000000000000000000000000000000000000..82fd4115c8d926efa6882ea43455614d2551b986 --- /dev/null +++ b/0068-upatch-diff-change-include-ignored-grouped-data-type.patch @@ -0,0 +1,163 @@ +From 40dcb22fb74c4c61e5419b1e87cce6526ceebae2 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 16:16:09 +0800 +Subject: [PATCH] upatch-diff: change include & ignored & grouped data type to + bool + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 26 +++++++++++++------------- + upatch-diff/elf-debug.c | 4 ++-- + upatch-diff/upatch-elf.h | 8 ++++---- + 3 files changed, 19 insertions(+), 19 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 371bb81..0c2c22d 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -542,7 +542,7 @@ static void mark_grouped_sections(struct upatch_elf *uelf) + if (!sec) { + ERROR("Group section not found"); + } +- sec->grouped = 1; ++ sec->grouped = true; + log_debug("Marking section '%s' (%d) as grouped\n", + sec->name, sec->index); + data++; +@@ -724,12 +724,12 @@ static void include_special_local_section(struct upatch_elf *uelf) { + if (sym->sec && sym->bind == STB_LOCAL && + sym->status == SAME && !sym->sec->include) { + sym->sym.st_other |= SYM_OTHER; +- sym->sec->include = 1; ++ sym->sec->include = true; + sym->sec->data->d_buf = NULL; + sym->sec->data->d_size = 0; + // arm error: (.debug_info+0x...) undefined reference to `no symbol' + if (sym->sec->secsym) { +- sym->sec->secsym->include = 1; ++ sym->sec->secsym->include = true; + } + } + } +@@ -739,7 +739,7 @@ static void include_special_local_section(struct upatch_elf *uelf) { + static void include_section(struct section *sec); + static void include_symbol(struct symbol *sym) + { +- if ((sym == NULL) || (sym->include != 0)) { ++ if ((sym == NULL) || sym->include) { + return; + } + /* +@@ -749,7 +749,7 @@ static void include_symbol(struct symbol *sym) + */ + log_debug("Include symbol '%s', status: %s\n", + sym->name, status_str(sym->status)); +- sym->include = 1; ++ sym->include = true; + /* + * For a function/object symbol, if it has a section, we only need to + * include the section if it has changed. Otherwise the symbol will be +@@ -765,13 +765,13 @@ static void include_symbol(struct symbol *sym) + + static void include_section(struct section *sec) + { +- if ((sec == NULL) || (sec->include != 0)) { ++ if ((sec == NULL) || sec->include) { + return; + } + + log_debug("Include section '%s', status: %s\n", + sec->name, status_str(sec->status)); +- sec->include = 1; ++ sec->include = true; + + if (is_rela_section(sec)) { + struct rela *rela = NULL; +@@ -834,7 +834,7 @@ static int verify_symbol_patchability(struct upatch_elf *uelf) + + struct symbol *sym = NULL; + list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->include == 0) { ++ if (!sym->include) { + continue; + } + if (sym->type == STT_GNU_IFUNC) { +@@ -853,22 +853,22 @@ static int verify_section_patchability(struct upatch_elf *uelf) + + struct section *sec = NULL; + list_for_each_entry(sec, &uelf->sections, list) { +- if ((sec->status == NEW) && (sec->include == 0)) { ++ if ((sec->status == NEW) && !sec->include) { + // new sections should be included + log_warn("Section '%s' is %s, but it is not included\n", + sec->name, status_str(sec->status)); + err_count++; +- } else if ((sec->status == CHANGED) && (sec->include == 0)) { +- // changed sections should be included, except rela & debug section ++ } else if ((sec->status == CHANGED) && !sec->include) { ++ // changed sections should be included + if (is_rela_section(sec) || is_debug_section(sec)) { + continue; + } + log_warn("Section '%s' is %s, but it is not included\n", + sec->name, status_str(sec->status)); + err_count++; +- } else if ((sec->status == CHANGED) && (sec->include != 0)) { ++ } else if ((sec->status == CHANGED) && sec->include) { + // changed group section cannot be included +- if (is_group_section(sec) || (sec->grouped != 0)) { ++ if (is_group_section(sec) || sec->grouped) { + log_warn("Section '%s' is %s, but it is not supported\n", + sec->name, status_str(sec->status)); + err_count++; +diff --git a/upatch-diff/elf-debug.c b/upatch-diff/elf-debug.c +index 983bf03..fd95fc8 100644 +--- a/upatch-diff/elf-debug.c ++++ b/upatch-diff/elf-debug.c +@@ -80,7 +80,7 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("Included symbol\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->include != 0) { ++ if (sym->include) { + log_normal("idx: %04u, name: '%s', status: %s\n", + sym->index, sym->name, status_str(sym->status)); + } +@@ -91,7 +91,7 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("Included section\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { +- if (sec->include != 0) { ++ if (sec->include) { + log_normal("idx: %04u, name: '%s', status: %s\n", + sec->index, sec->name, status_str(sec->status)); + } +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index 030feca..653439e 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -71,9 +71,9 @@ struct section { + enum data_source data_source; + enum data_source dbuf_source; + GElf_Shdr sh; +- int ignore; +- int include; +- int grouped; ++ bool ignored; ++ bool include; ++ bool grouped; + unsigned int index; + enum status status; + union { +@@ -118,7 +118,7 @@ struct symbol { + unsigned char type; + enum status status; + union { +- int include; /* used in the patched elf */ ++ bool include; /* used in the patched elf */ + enum symbol_strip strip; /* used in the output elf */ + }; + }; +-- +2.43.0 + diff --git a/0069-upatch-diff-compare-symbol-by-checking-it-s-section-.patch b/0069-upatch-diff-compare-symbol-by-checking-it-s-section-.patch new file mode 100644 index 0000000000000000000000000000000000000000..ed6607f1a3bdfdf522a63b8f741277a6e6eec67a --- /dev/null +++ b/0069-upatch-diff-compare-symbol-by-checking-it-s-section-.patch @@ -0,0 +1,128 @@ +From 2b288b188223413ac9b65c93e887720a9f918970 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 15:19:15 +0800 +Subject: [PATCH] upatch-diff: compare symbol by checking it's section & + relocation + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 63 ++++++++++++++++++++++----------------- + 1 file changed, 36 insertions(+), 27 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 56451af..b461c2a 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -35,32 +35,42 @@ static void compare_correlated_symbol(struct symbol *sym, struct symbol *twin) + { + // symbol type & binding cannot be changed + if (sym->type != twin->type) { +- ERROR("symbol '%s' type mismatched", sym->name); ++ ERROR("Symbol '%s' type mismatched", sym->name); + } + if (sym->sym.st_info != twin->sym.st_info) { +- ERROR("symbol '%s' binding mismatched", sym->name); ++ ERROR("Symbol '%s' st_info mismatched", sym->name); + } + // object symbol size cannot be changed + if ((sym->type == STT_OBJECT) && (sym->sym.st_size != twin->sym.st_size)) { +- ERROR("symbol '%s' object size mismatched", sym->name); ++ ERROR("Symbol '%s' object size mismatched", sym->name); + } + +- // compare symbol sections +- if ((sym->sec != NULL) && (twin->sec != NULL)) { +- // symbol section correlation cannot be changed +- if (sym->sec->twin != twin->sec) { +- ERROR("symbol '%s' section mismatched", sym->name); +- } +- compare_correlated_section(sym->sec, twin->sec); +- sym->status = sym->sec->status; ++ /* ++ * For local symbols, we handle them based on their matching sections. ++ */ ++ if ((sym->sym.st_shndx == SHN_UNDEF) || (sym->sym.st_shndx == SHN_ABS)) { ++ sym->status = SAME; ++ return; + } + +- if ((sym->sym.st_shndx == SHN_UNDEF) || (sym->sym.st_shndx == SHN_ABS)) { ++ if ((sym->sec == NULL) || (sym->sym.st_shndx == SHN_ABS)) { ++ ERROR("Symbol '%s' don't have section\n", sym->name); ++ } ++ ++ if (sym->sec->twin != twin->sec) { ++ ERROR("Symbol '%s' section mismatched", sym->name); ++ } ++ ++ compare_correlated_section(sym->sec, twin->sec); ++ if (sym->sec->status == CHANGED) { ++ sym->status = CHANGED; ++ } else if (!is_rela_section(sym->sec) && ++ (sym->sec->rela != NULL) && ++ (sym->sec->rela->status == CHANGED)) { ++ sym->status = CHANGED; ++ } else { + sym->status = SAME; + } +- /* +- * For local symbols, we handle them based on their matching sections. +- */ + } + + void upatch_compare_symbols(struct upatch_elf *uelf) +@@ -128,22 +138,21 @@ static void compare_correlated_nonrela_section(struct section *sec, + } + + // we may change status of sec, they are not same +-static int compare_correlated_section(struct section *sec, +- struct section *sectwin) ++static int compare_correlated_section(struct section *sec, struct section *twin) + { + /* compare section headers */ + if (!is_debug_section(sec)) { +- if ((sec->sh.sh_type != sectwin->sh.sh_type) || +- (sec->sh.sh_flags != sectwin->sh.sh_flags) || +- (sec->sh.sh_entsize != sectwin->sh.sh_entsize) || +- ((sec->sh.sh_addralign != sectwin->sh.sh_addralign) && ++ if ((sec->sh.sh_type != twin->sh.sh_type) || ++ (sec->sh.sh_flags != twin->sh.sh_flags) || ++ (sec->sh.sh_entsize != twin->sh.sh_entsize) || ++ ((sec->sh.sh_addralign != twin->sh.sh_addralign) && + !is_text_section(sec) && + !is_string_section(sec) && + !is_rodata_section(sec))) { + /* shaddralign of .rodata may be changed from 0 to 8 bytes + once string length is over 30 */ + ERROR("%s section header details differ from %s", +- sec->name, sectwin->name); ++ sec->name, twin->name); + return -1; + } + } +@@ -159,17 +168,17 @@ static int compare_correlated_section(struct section *sec, + goto out; + } + /* compare file size and data size(memory size) */ +- if (sec->sh.sh_size != sectwin->sh.sh_size || +- sec->data->d_size != sectwin->data->d_size || +- (sec->rela && !sectwin->rela) || (!sec->rela && sectwin->rela)) { ++ if (sec->sh.sh_size != twin->sh.sh_size || ++ sec->data->d_size != twin->data->d_size || ++ (sec->rela && !twin->rela) || (!sec->rela && twin->rela)) { + sec->status = CHANGED; + goto out; + } + + if (is_rela_section(sec)) { +- compare_correlated_rela_section(sec, sectwin); ++ compare_correlated_rela_section(sec, twin); + } else { +- compare_correlated_nonrela_section(sec, sectwin); ++ compare_correlated_nonrela_section(sec, twin); + } + + out: +-- +2.43.0 + diff --git a/0070-upatch-diff-ignore-debug-sections.patch b/0070-upatch-diff-ignore-debug-sections.patch new file mode 100644 index 0000000000000000000000000000000000000000..f3730f54ce7f0f09ea9d388d8d36661909cd1dd0 --- /dev/null +++ b/0070-upatch-diff-ignore-debug-sections.patch @@ -0,0 +1,296 @@ +From 06a7b099a67de722d197951becd63e744d54e499 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 17:24:03 +0800 +Subject: [PATCH] upatch-diff: ignore debug sections + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 44 ++++++++++++++++++++------------ + upatch-diff/elf-common.h | 5 ++++ + upatch-diff/elf-compare.c | 32 +++++++++++++---------- + upatch-diff/elf-correlate.c | 3 +++ + upatch-diff/elf-create.c | 16 ++++++++++-- + upatch-diff/elf-debug.c | 7 +++-- + 6 files changed, 71 insertions(+), 36 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 0c2c22d..a3bc988 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -681,20 +681,28 @@ static void replace_section_syms(struct upatch_elf *uelf) + static void mark_ignored_sections(struct upatch_elf *uelf) + { + static const char *const IGNORED_SECTIONS[] = { ++ ".eh_frame", ++ ".note", ++ ".debug_", + ".comment", + ".discard", + ".rela.discard", + ".GCC.command.line", + }; ++ static const size_t IGNORED_SECTION_NUM = ++ sizeof(IGNORED_SECTIONS) / sizeof(IGNORED_SECTIONS[0]); + +- for (size_t i = 0; i < ARRAY_SIZE(IGNORED_SECTIONS); i++) { +- const char *const ignored_name = IGNORED_SECTIONS[i]; +- const size_t name_len = strlen(ignored_name); +- +- struct section *sec = NULL; +- list_for_each_entry(sec, &uelf->sections, list) { +- if (strncmp(sec->name, ignored_name, name_len) == 0) { +- sec->status = SAME; ++ struct section *sec = NULL; ++ list_for_each_entry(sec, &uelf->sections, list) { ++ for (size_t i = 0; i < IGNORED_SECTION_NUM; i++) { ++ const char *const ignored_name = IGNORED_SECTIONS[i]; ++ const size_t name_len = strlen(ignored_name); ++ const char *sec_name = is_rela_section(sec) ? ++ sec->base->name : sec->name; ++ if (strncmp(sec_name, ignored_name, name_len) == 0) { ++ sec->ignored = true; ++ log_debug("Marking section '%s' (%d) as ignored\n", ++ sec->name, sec->index); + break; + } + } +@@ -790,6 +798,9 @@ static void include_standard_elements(struct upatch_elf *uelf) + struct section *sec = NULL; + + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } + if (is_symtab_section(sec) || is_strtab_section(sec)) { + include_section(sec); + } +@@ -804,7 +815,7 @@ static int include_changes(struct upatch_elf *uelf) + + struct symbol *sym = NULL; + list_for_each_entry(sym, &uelf->symbols, list) { +- if (sym->status == SAME) { ++ if ((sym->status == SAME) || is_symbol_ignored(sym)) { + continue; + } + +@@ -816,8 +827,7 @@ static int include_changes(struct upatch_elf *uelf) + include_symbol(sym); + count++; + } else if (sym->type == STT_SECTION) { +- if ((sym->sec != NULL) && +- (is_rela_section(sym->sec) || is_debug_section(sym->sec))) { ++ if ((sym->sec != NULL) && is_rela_section(sym->sec)) { + continue; + } + include_symbol(sym); +@@ -853,6 +863,9 @@ static int verify_section_patchability(struct upatch_elf *uelf) + + struct section *sec = NULL; + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } + if ((sec->status == NEW) && !sec->include) { + // new sections should be included + log_warn("Section '%s' is %s, but it is not included\n", +@@ -860,7 +873,7 @@ static int verify_section_patchability(struct upatch_elf *uelf) + err_count++; + } else if ((sec->status == CHANGED) && !sec->include) { + // changed sections should be included +- if (is_rela_section(sec) || is_debug_section(sec)) { ++ if (is_rela_section(sec)) { + continue; + } + log_warn("Section '%s' is %s, but it is not included\n", +@@ -995,6 +1008,9 @@ int main(int argc, char*argv[]) + detect_child_functions(&uelf_source); + detect_child_functions(&uelf_patched); + ++ mark_ignored_sections(&uelf_source); ++ mark_ignored_sections(&uelf_patched); ++ + mark_grouped_sections(&uelf_patched); + + replace_section_syms(&uelf_source); +@@ -1003,10 +1019,6 @@ int main(int argc, char*argv[]) + upatch_correlate_elf(&uelf_source, &uelf_patched); + upatch_correlate_static_local_variables(&uelf_source, &uelf_patched); + +- /* Now, we can only check uelf_patched, all we need is in the twin part */ +- /* Also, we choose part of uelf_patched and output new object */ +- mark_ignored_sections(&uelf_patched); +- + upatch_compare_correlated_elements(&uelf_patched); + mark_file_symbols(&uelf_source); + find_debug_symbol(&uelf_source, &relf); +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index 5e63b09..3454c21 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -150,6 +150,11 @@ static inline bool is_debug_section(struct section *sec) + !strncmp(name, ".eh_frame", 9); + } + ++static inline bool is_symbol_ignored(struct symbol *sym) ++{ ++ return (sym->sec != NULL) && sym->sec->ignored; ++} ++ + static inline struct symbol *find_symbol_by_index(struct list_head *list, unsigned int index) + { + struct symbol *sym; +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index b461c2a..b37167c 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -78,6 +78,9 @@ void upatch_compare_symbols(struct upatch_elf *uelf) + struct symbol *sym; + + list_for_each_entry(sym, &uelf->symbols, list) { ++ if (is_symbol_ignored(sym)) { ++ continue; ++ } + if (sym->twin) { + compare_correlated_symbol(sym, sym->twin); + } else { +@@ -141,20 +144,18 @@ static void compare_correlated_nonrela_section(struct section *sec, + static int compare_correlated_section(struct section *sec, struct section *twin) + { + /* compare section headers */ +- if (!is_debug_section(sec)) { +- if ((sec->sh.sh_type != twin->sh.sh_type) || +- (sec->sh.sh_flags != twin->sh.sh_flags) || +- (sec->sh.sh_entsize != twin->sh.sh_entsize) || +- ((sec->sh.sh_addralign != twin->sh.sh_addralign) && +- !is_text_section(sec) && +- !is_string_section(sec) && +- !is_rodata_section(sec))) { +- /* shaddralign of .rodata may be changed from 0 to 8 bytes +- once string length is over 30 */ +- ERROR("%s section header details differ from %s", +- sec->name, twin->name); +- return -1; +- } ++ if ((sec->sh.sh_type != twin->sh.sh_type) || ++ (sec->sh.sh_flags != twin->sh.sh_flags) || ++ (sec->sh.sh_entsize != twin->sh.sh_entsize) || ++ ((sec->sh.sh_addralign != twin->sh.sh_addralign) && ++ !is_text_section(sec) && ++ !is_string_section(sec) && ++ !is_rodata_section(sec))) { ++ /* shaddralign of .rodata may be changed from 0 to 8 bytes ++ once string length is over 30 */ ++ ERROR("%s section header details differ from %s", ++ sec->name, twin->name); ++ return -1; + } + + if (is_note_section(sec)) { +@@ -213,6 +214,9 @@ void upatch_compare_sections(struct upatch_elf *uelf) + struct section *sec = NULL; + + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } + if (sec->twin == NULL) { + sec->status = NEW; + } else { +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 56ee48c..b39ce7d 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -54,6 +54,9 @@ void upatch_correlate_symbols(struct upatch_elf *uelf_source, + struct symbol *sym_patched; + + list_for_each_entry(sym_orig, &uelf_source->symbols, list) { ++ if (is_symbol_ignored(sym_orig)) { ++ continue; ++ } + if (sym_orig->twin) { + continue; + } +diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c +index 20d1994..112be83 100644 +--- a/upatch-diff/elf-create.c ++++ b/upatch-diff/elf-create.c +@@ -221,8 +221,7 @@ static bool need_dynrela(struct running_elf *relf, struct section *relasec, + { + struct lookup_result symbol; + +- if (is_debug_section(relasec) || +- is_note_section(relasec)) { ++ if (relasec->ignored) { + return false; + } + +@@ -521,6 +520,9 @@ void upatch_create_shstrtab(struct upatch_elf *uelf) + /* determine size of string table */ + size = 1; + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } + size += strlen(sec->name) + 1; + } + +@@ -531,6 +533,9 @@ void upatch_create_shstrtab(struct upatch_elf *uelf) + + offset = 1; + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } + len = strlen(sec->name) + 1; + sec->sh.sh_name = (unsigned int)offset; + memcpy(buf + offset, sec->name, len); +@@ -551,6 +556,9 @@ void upatch_create_shstrtab(struct upatch_elf *uelf) + log_debug("\n"); + + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } + log_debug("%s @ shstrtab offset %d\n", sec->name, sec->sh.sh_name); + } + } +@@ -713,6 +721,10 @@ void upatch_write_output_elf(struct upatch_elf *uelf, Elf *elf, + + /* add changed sections */ + list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->ignored) { ++ continue; ++ } ++ + scn = elf_newscn(elfout); + if (!scn) { + ERROR("elf_newscn failed."); +diff --git a/upatch-diff/elf-debug.c b/upatch-diff/elf-debug.c +index fd95fc8..90e112b 100644 +--- a/upatch-diff/elf-debug.c ++++ b/upatch-diff/elf-debug.c +@@ -110,10 +110,10 @@ void upatch_dump_kelf(struct upatch_elf *uelf) + log_debug("%02d %s (%s)", + sec->index, sec->name, status_str(sec->status)); + if (is_rela_section(sec)) { +- log_debug(", base-> %s\n", sec->base->name); +- if (is_debug_section(sec) || is_note_section(sec)) { +- goto next; ++ if (sec->ignored) { ++ continue; + } ++ log_debug(", base-> %s\n", sec->base->name); + log_debug("rela section expansion\n"); + list_for_each_entry(rela, &sec->relas, list) { + log_debug("sym %d, offset %ld, type %d, %s %s %ld\n", +@@ -133,7 +133,6 @@ void upatch_dump_kelf(struct upatch_elf *uelf) + log_debug(", rela-> %s", sec->rela->name); + } + } +-next: + log_debug("\n"); + } + +-- +2.43.0 + diff --git a/0071-upatch-diff-allow-sh_flags-sh_addralign-changes.patch b/0071-upatch-diff-allow-sh_flags-sh_addralign-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..6066748080304445879487e27bb3630811e5ccf0 --- /dev/null +++ b/0071-upatch-diff-allow-sh_flags-sh_addralign-changes.patch @@ -0,0 +1,50 @@ +From e02b463ad3a5c09baa49961ac4e872d2c6b3049d Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 28 Apr 2025 17:40:43 +0800 +Subject: [PATCH] upatch-diff: allow sh_flags & sh_addralign changes + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index b37167c..7ccc952 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -143,20 +143,23 @@ static void compare_correlated_nonrela_section(struct section *sec, + // we may change status of sec, they are not same + static int compare_correlated_section(struct section *sec, struct section *twin) + { +- /* compare section headers */ ++ /* We allow sh_flags and sh_addralign changes. ++ When we change the initial value of variables ++ sh_flags & sh_addralign may change in .rodata section */ + if ((sec->sh.sh_type != twin->sh.sh_type) || +- (sec->sh.sh_flags != twin->sh.sh_flags) || +- (sec->sh.sh_entsize != twin->sh.sh_entsize) || +- ((sec->sh.sh_addralign != twin->sh.sh_addralign) && +- !is_text_section(sec) && +- !is_string_section(sec) && +- !is_rodata_section(sec))) { +- /* shaddralign of .rodata may be changed from 0 to 8 bytes +- once string length is over 30 */ ++ (sec->sh.sh_entsize != twin->sh.sh_entsize)) { + ERROR("%s section header details differ from %s", + sec->name, twin->name); + return -1; + } ++ if (sec->sh.sh_flags != twin->sh.sh_flags) { ++ log_warn("Section '%s' sh_flags changed from %ld to %ld\n", ++ sec->name, sec->sh.sh_flags, twin->sh.sh_flags); ++ } ++ if (sec->sh.sh_addralign != twin->sh.sh_addralign) { ++ log_warn("Section '%s' sh_addralign changed from %ld to %ld\n", ++ sec->name, sec->sh.sh_addralign, twin->sh.sh_addralign); ++ } + + if (is_note_section(sec)) { + sec->status = SAME; +-- +2.43.0 + diff --git a/0072-upatch-diff-fix-null-symbol-lookup-error.patch b/0072-upatch-diff-fix-null-symbol-lookup-error.patch new file mode 100644 index 0000000000000000000000000000000000000000..8cd0cedfb71f977d0a1db144374b805b9f0dc410 --- /dev/null +++ b/0072-upatch-diff-fix-null-symbol-lookup-error.patch @@ -0,0 +1,48 @@ +From cf34c2b14c93cd56dc8bb2fab578801980823063 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 30 Apr 2025 18:45:00 +0800 +Subject: [PATCH] upatch-diff: fix null symbol lookup error + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 7 ++++++- + upatch-diff/elf-common.h | 4 +++- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index a3bc988..1de2f1b 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -805,8 +805,13 @@ static void include_standard_elements(struct upatch_elf *uelf) + include_section(sec); + } + } ++ + /* include the NULL symbol */ +- include_symbol(list_entry(uelf->symbols.next, struct symbol, list)); ++ struct symbol *sym = find_symbol_by_index(&uelf->symbols, 0); ++ if (sym == NULL) { ++ ERROR("Cannot find null symbol"); ++ } ++ include_symbol(sym); + } + + static int include_changes(struct upatch_elf *uelf) +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index 3454c21..89d238a 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -273,7 +273,9 @@ static inline unsigned int absolute_rela_type(struct upatch_elf *uelf) + + static inline bool is_null_sym(struct symbol *sym) + { +- return !strlen(sym->name); ++ return (sym->type == STT_NOTYPE) && ++ (sym->bind == STB_LOCAL) && ++ (strlen(sym->name) == 0); + } + + static inline bool is_file_sym(struct symbol *sym) +-- +2.43.0 + diff --git a/0073-upatch-diff-use-kpatch-way-to-deal-with-static-local.patch b/0073-upatch-diff-use-kpatch-way-to-deal-with-static-local.patch new file mode 100644 index 0000000000000000000000000000000000000000..94db7302828aeadb5f3e9b130eac3a0b9e4901e4 --- /dev/null +++ b/0073-upatch-diff-use-kpatch-way-to-deal-with-static-local.patch @@ -0,0 +1,280 @@ +From fde9ebc5ede3fd24b0674651358b06b333e0e039 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 29 Apr 2025 16:55:42 +0800 +Subject: [PATCH] upatch-diff: use kpatch way to deal with static local + variables + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 13 +++++ + upatch-diff/elf-common.c | 91 ++++++++++++++++++++++++++++++-- + upatch-diff/elf-common.h | 9 ++++ + upatch-diff/elf-compare.c | 14 +++-- + upatch-diff/elf-correlate.c | 41 +++++++++----- + 5 files changed, 145 insertions(+), 23 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 1de2f1b..c911129 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -758,6 +758,13 @@ static void include_symbol(struct symbol *sym) + log_debug("Include symbol '%s', status: %s\n", + sym->name, status_str(sym->status)); + sym->include = true; ++ /* ++ * For special static symbols, we need include it's section ++ * to ensure we don't get link error. ++ */ ++ if (is_special_static_symbol(sym)) { ++ sym->sec->include = true; ++ } + /* + * For a function/object symbol, if it has a section, we only need to + * include the section if it has changed. Otherwise the symbol will be +@@ -852,6 +859,12 @@ static int verify_symbol_patchability(struct upatch_elf *uelf) + if (!sym->include) { + continue; + } ++ if ((sym->bind == STB_LOCAL) && (sym->sym.st_shndx == SHN_UNDEF) && ++ (sym->index != 0)) { ++ log_warn("Symbol '%s' is local, but sh_shndx is SHN_UNDEF\n", ++ sym->name); ++ err_count++; ++ } + if (sym->type == STT_GNU_IFUNC) { + log_warn("Symbol '%s' is included, but IFUNC is not supported\n", + sym->name); +diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c +index 5aa35f1..6e13c34 100644 +--- a/upatch-diff/elf-common.c ++++ b/upatch-diff/elf-common.c +@@ -26,6 +26,89 @@ + + #include "elf-common.h" + ++static bool is_dynamic_debug_symbol(struct symbol *sym) ++{ ++ static const char *SEC_NAMES[] = { ++ "__verbose", ++ "__dyndbg", ++ NULL, ++ }; ++ ++ if ((sym->type == STT_OBJECT) || (sym->type == STT_SECTION)) { ++ const char **sec_name; ++ for (sec_name = SEC_NAMES; *sec_name; sec_name++) { ++ if (strcmp(sym->sec->name, *sec_name) == 0) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++bool is_special_static_symbol(struct symbol *sym) ++{ ++ static const char *SYM_NAMES[] = { ++ ".__key", ++ ".__warned", ++ ".__already_done.", ++ ".__func__", ++ ".__FUNCTION__", ++ ".__PRETTY_FUNCTION__", ++ "._rs", ++ ".CSWTCH", ++ "._entry", ++ ".C", ++ NULL, ++ }; ++ ++ if (sym == NULL) { ++ return false; ++ } ++ ++ /* pr_debug() uses static local variables in __verbose or __dyndbg section */ ++ if (is_dynamic_debug_symbol(sym)) { ++ return true; ++ } ++ ++ if (sym->type == STT_SECTION) { ++ /* make sure section is bundled */ ++ if (is_rela_section(sym->sec) || (sym->sec->sym == NULL)) { ++ return false; ++ } ++ /* use bundled object object/function symbol for matching */ ++ sym = sym->sec->sym; ++ } ++ ++ if ((sym->type != STT_OBJECT) || (sym->bind != STB_LOCAL)) { ++ return false; ++ } ++ if (!strcmp(sym->sec->name, ".data.once")) { ++ return true; ++ } ++ ++ const char **sym_name; ++ for (sym_name = SYM_NAMES; *sym_name; sym_name++) { ++ /* Check gcc-style statics: '.' */ ++ if (strcmp(sym->name, (*sym_name + 1)) == 0) { ++ return true; ++ } ++ /* Check clang-style statics: '.' */ ++ if (strstr(sym->name, *sym_name)) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool is_special_static_section(struct section *sec) ++{ ++ struct symbol *sym = is_rela_section(sec) ? ++ sec->base->secsym : sec->secsym; ++ return is_special_static_symbol(sym); ++} ++ + int mangled_strcmp(char *str1, char *str2) + { + /* +@@ -78,11 +161,9 @@ bool is_normal_static_local(struct symbol *sym) + if (!strchr(sym->name, '.')) { + return false; + } +- +- /* +- * TODO: Special static local variables should never be correlated and should always +- * be included if they are referenced by an included function. +- */ ++ if (is_special_static_symbol(sym)) { ++ return false; ++ } + return true; + } + +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index 89d238a..672295c 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -303,4 +303,13 @@ bool is_gcc6_localentry_bundled_sym(struct upatch_elf *); + */ + bool is_mapping_symbol(struct upatch_elf *, struct symbol *); + ++/* ++ * This function detects whether the given symbol is a "special" static local ++ * variable (for lack of a better term). ++ * Special static local variables shoe never be correlated and should always ++ * be included if they are referenced by an included function. ++ */ ++bool is_special_static_symbol(struct symbol *sym); ++bool is_special_static_section(struct section *sec); ++ + #endif +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 7ccc952..7314a27 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -81,12 +81,14 @@ void upatch_compare_symbols(struct upatch_elf *uelf) + if (is_symbol_ignored(sym)) { + continue; + } +- if (sym->twin) { +- compare_correlated_symbol(sym, sym->twin); +- } else { ++ if (is_special_static_symbol(sym)) { ++ sym->status = SAME; ++ } else if (sym->twin == NULL) { + sym->status = NEW; ++ } else { ++ compare_correlated_symbol(sym, sym->twin); + } +- log_debug("symbol %s is %s\n", sym->name, status_str(sym->status)); ++ log_debug("Symbol '%s' is %s\n", sym->name, status_str(sym->status)); + } + } + +@@ -220,7 +222,9 @@ void upatch_compare_sections(struct upatch_elf *uelf) + if (sec->ignored) { + continue; + } +- if (sec->twin == NULL) { ++ if (is_special_static_section(sec)) { ++ sec->status = SAME; ++ } else if (sec->twin == NULL) { + sec->status = NEW; + } else { + compare_correlated_section(sec, sec->twin); +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index b39ce7d..59069a3 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -60,17 +60,24 @@ void upatch_correlate_symbols(struct upatch_elf *uelf_source, + if (sym_orig->twin) { + continue; + } ++ /* ++ * Special static local variables should never be correlated ++ * and should always be included if they are referenced by ++ * an included function. ++ */ ++ if (is_special_static_symbol(sym_orig)) { ++ continue; ++ } ++ + /* find matched symbol */ + list_for_each_entry(sym_patched, &uelf_patched->symbols, list) { + if (mangled_strcmp(sym_orig->name, sym_patched->name) || + sym_orig->type != sym_patched->type || sym_patched->twin) { + continue; + } +- /* +- * TODO: Special static local variables should never be correlated +- * and should always be included if they are referenced by +- * an included function. +- */ ++ if (is_special_static_symbol(sym_patched)) { ++ continue; ++ } + /* + * The .LCx symbols point to string literals in + * '.rodata..str1.*' sections. They get included +@@ -148,19 +155,27 @@ void upatch_correlate_sections(struct upatch_elf *uelf_source, + + list_for_each_entry(sec_orig, &uelf_source->sections, list) { + /* already found */ +- if (sec_orig->twin) { ++ if (sec_orig->twin != NULL) { ++ continue; ++ } ++ /* ++ * Special static local variables should never be correlated ++ * and should always be included if they are referenced by ++ * an included function. ++ */ ++ if (is_special_static_section(sec_orig)) { + continue; + } + list_for_each_entry(sec_patched, &uelf_patched->sections, list) { +- if (mangled_strcmp(sec_orig->name, sec_patched->name) || +- sec_patched->twin) { ++ if (sec_patched->twin != NULL) { ++ continue; ++ } ++ if (is_special_static_section(sec_patched)) { ++ continue; ++ } ++ if (mangled_strcmp(sec_orig->name, sec_patched->name)) { + continue; + } +- /* +- * TODO: Special static local variables should never be correlated +- * and should always be included if they are referenced by +- * an included function. +- */ + /* + * Group sections must match exactly to be correlated. + */ +-- +2.43.0 + diff --git a/0074-upatch-diff-remove-intermediate-sections.patch b/0074-upatch-diff-remove-intermediate-sections.patch new file mode 100644 index 0000000000000000000000000000000000000000..8a5869e5d08723292c01f061dca9bcb6114d4cfb --- /dev/null +++ b/0074-upatch-diff-remove-intermediate-sections.patch @@ -0,0 +1,156 @@ +From c4542aef5d2b3b6cfc2c833909d225e10ee448f0 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 29 Apr 2025 18:11:50 +0800 +Subject: [PATCH] upatch-diff: remove intermediate sections + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 2 - + upatch-diff/elf-create.c | 104 ------------------------------- + upatch-diff/elf-create.h | 2 - + 3 files changed, 108 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index c911129..0b580a9 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -1070,8 +1070,6 @@ int main(int argc, char*argv[]) + + upatch_create_patches_sections(&uelf_out, &relf); + +- upatch_create_intermediate_sections(&uelf_out, &relf); +- + create_kpatch_arch_section(); + + upatch_build_strings_section_data(&uelf_out); +diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c +index 112be83..139f92d 100644 +--- a/upatch-diff/elf-create.c ++++ b/upatch-diff/elf-create.c +@@ -216,110 +216,6 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, + } + } + +-static bool need_dynrela(struct running_elf *relf, struct section *relasec, +- struct rela *rela) +-{ +- struct lookup_result symbol; +- +- if (relasec->ignored) { +- return false; +- } +- +- if (!lookup_relf(relf, rela->sym, &symbol)) { +- /* relocation is based on new symbol. */ +- return false; +- } +- +- if (rela->sym->bind == STB_LOCAL) { +- if (symbol.global) { +- ERROR("No releated local symbol found.\n"); +- } +- return true; +- } +- +- return false; +-} +- +-/* +- * This function is used to handle relocations which cannot be handled normally +- * +- * Situations: +- * 1. refer to old symbols +- * +- */ +-void upatch_create_intermediate_sections(struct upatch_elf *uelf, +- struct running_elf *relf) +-{ +- struct rela *rela, *rela_safe; +- struct section *relasec, *usym_sec, *urela_sec; +- struct upatch_symbol *usyms; +- struct upatch_relocation *urelas; +- struct symbol *strsym, *usym_sec_sym; +- unsigned int nr = 0, index = 0; +- +- list_for_each_entry(relasec, &uelf->sections, list) { +- if (!is_rela_section(relasec)) { +- continue; +- } +- /* no need to handle upatch meta section. */ +- if (!strcmp(relasec->name, ".rela.upatch.funcs")) { +- continue; +- } +- list_for_each_entry(rela, &relasec->relas, list) { +- nr++; +- if (need_dynrela(relf, relasec, rela)) { +- rela->need_dynrela = 1; +- } +- } +- } +- +- urela_sec = create_section_pair(uelf, ".upatch.relocations", +- sizeof(*urelas), nr); +- urelas = urela_sec->data->d_buf; +- +- usym_sec = create_section_pair(uelf, ".upatch.symbols", +- sizeof(*usyms), nr); +- usyms = usym_sec->data->d_buf; +- +- ALLOC_LINK(usym_sec_sym, &uelf->symbols); +- usym_sec_sym->sec = usym_sec; +- usym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); +- usym_sec_sym->type = STT_SECTION; +- usym_sec_sym->bind = STB_LOCAL; +- usym_sec_sym->name = ".upatch.symbols"; +- +- strsym = find_symbol_by_name(&uelf->symbols, ".upatch.strings"); +- if (!strsym) { +- ERROR("can't find .upatch.strings symbol.\n"); +- } +- +- list_for_each_entry(relasec, &uelf->sections, list) { +- if (!is_rela_section(relasec)) { +- continue; +- } +- if (!strcmp(relasec->name, ".rela.upatch.funcs") || +- !strcmp(relasec->name, ".rela.upatch.relocations") || +- !strcmp(relasec->name, ".rela.upatch.symbols")) { +- continue; +- } +- list_for_each_entry_safe(rela, rela_safe, &relasec->relas, list) { +- if (!rela->need_dynrela) { +- rela->sym->strip = SYMBOL_USED; +- continue; +- } +- } +- } +- +- log_debug("generate %d dynamic relocations.\n", index); +- +- /* set size to actual number of kyms/krelas */ +- usym_sec->data->d_size = index * sizeof(struct upatch_symbol); +- usym_sec->sh.sh_size = usym_sec->data->d_size; +- +- urela_sec->data->d_size = index * sizeof(struct upatch_relocation); +- urela_sec->sh.sh_size = urela_sec->data->d_size; +-} +- + void upatch_build_strings_section_data(struct upatch_elf *uelf) + { + struct section *sec; +diff --git a/upatch-diff/elf-create.h b/upatch-diff/elf-create.h +index b9efbb3..f18d40b 100644 +--- a/upatch-diff/elf-create.h ++++ b/upatch-diff/elf-create.h +@@ -31,8 +31,6 @@ void upatch_create_strings_elements(struct upatch_elf *); + + void upatch_create_patches_sections(struct upatch_elf *, struct running_elf *); + +-void upatch_create_intermediate_sections(struct upatch_elf *, struct running_elf *); +- + static inline void create_kpatch_arch_section(void) {} + + void upatch_build_strings_section_data(struct upatch_elf *); +-- +2.43.0 + diff --git a/0075-upatch-diff-rewrite-local-symbol-matching-logic.patch b/0075-upatch-diff-rewrite-local-symbol-matching-logic.patch new file mode 100644 index 0000000000000000000000000000000000000000..a5d1df3b085664780cb8d7360e17fe986ef1f3e0 --- /dev/null +++ b/0075-upatch-diff-rewrite-local-symbol-matching-logic.patch @@ -0,0 +1,305 @@ +From d1c15c984d02894046e3c87662725ba624b349bc Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 29 Apr 2025 18:09:30 +0800 +Subject: [PATCH] upatch-diff: rewrite local symbol matching logic + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 191 ++++++++++++++----------------- + upatch-diff/elf-correlate.c | 4 +- + upatch-diff/upatch-elf.h | 2 +- + 3 files changed, 88 insertions(+), 109 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 0b580a9..3d6edc8 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -329,7 +329,7 @@ static void detect_child_functions(struct upatch_elf *uelf) + } + } + +-static bool discarded_sym(struct running_elf *relf, struct symbol *sym) ++static bool is_discarded_sym(struct running_elf *relf, struct symbol *sym) + { + if (!sym || !sym->sec || !sym->sec->name) { + return false; +@@ -348,140 +348,115 @@ static bool discarded_sym(struct running_elf *relf, struct symbol *sym) + return false; + } + +-enum local_match { +- FOUND, +- NOT_FOUND, +- EMPTY, +-}; +- +-static enum local_match locals_match(struct upatch_elf *uelf, +- struct running_elf *relf, struct symbol *file_sym, int file_sym_idx) ++static struct symbol* match_uelf_local_symbol(struct upatch_elf *uelf, ++ struct symbol *start_sym, struct debug_symbol *relf_sym) + { +- struct symbol *uelf_sym = NULL; +- struct debug_symbol *relf_sym = NULL; +- enum local_match found = EMPTY; +- +- for (int i = file_sym_idx + 1; i < relf->obj_nr; i++) { +- relf_sym = &relf->obj_syms[i]; +- +- if (relf_sym->type == STT_FILE) { ++ struct symbol *uelf_sym = start_sym; ++ list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { ++ if (uelf_sym->type == STT_FILE) { + break; // find until next file + } +- if (relf_sym->bind != STB_LOCAL) { ++ if (uelf_sym->bind != STB_LOCAL) { + continue; + } +- if ((relf_sym->type != STT_FUNC) && +- (relf_sym->type != STT_OBJECT)) { ++ if ((uelf_sym->type != STT_FUNC) && (uelf_sym->type != STT_OBJECT)) { + continue; + } +- +- found = NOT_FOUND; +- uelf_sym = file_sym; +- list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { +- if (uelf_sym->type == STT_FILE) { +- break; // find until next file +- } +- if (uelf_sym->bind != STB_LOCAL) { +- continue; +- } +- if ((uelf_sym->type == relf_sym->type) && +- (strcmp(uelf_sym->name, relf_sym->name) == 0)) { +- found = FOUND; +- break; +- } +- } +- +- if (found == NOT_FOUND) { +- log_warn("Cannot find symbol '%s' in %s\n", +- relf_sym->name, g_relf_name); +- return NOT_FOUND; ++ if ((uelf_sym->type != relf_sym->type) || ++ (strcmp(uelf_sym->name, relf_sym->name) != 0)) { ++ continue; + } ++ return uelf_sym; + } + +- uelf_sym = file_sym; +- list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { +- if (uelf_sym->type == STT_FILE) { ++ return NULL; ++} ++ ++static struct debug_symbol* match_relf_local_symbol(struct running_elf *relf, ++ int start_index, struct symbol *uelf_sym) ++{ ++ for (int i = start_index + 1; i < relf->obj_nr; i++) { ++ struct debug_symbol *relf_sym = &relf->obj_syms[i]; ++ ++ if (relf_sym->type == STT_FILE) { + break; // find until next file + } +- if (uelf_sym->bind != STB_LOCAL) { ++ if (relf_sym->bind != STB_LOCAL) { + continue; + } +- if ((relf_sym->type != STT_FUNC) && +- (relf_sym->type != STT_OBJECT)) { ++ if ((relf_sym->type != STT_FUNC) && (relf_sym->type != STT_OBJECT)) { + continue; + } +- if (discarded_sym(relf, uelf_sym)) { ++ if ((relf_sym->type != uelf_sym->type) || ++ (strcmp(relf_sym->name, uelf_sym->name) != 0)) { + continue; + } +- +- found = NOT_FOUND; +- for (int i = file_sym_idx + 1; i < relf->obj_nr; i++) { +- relf_sym = &relf->obj_syms[i]; +- +- if (relf_sym->type == STT_FILE) { +- break; // find until next file +- } +- if (relf_sym->bind != STB_LOCAL) { +- continue; +- } +- if ((uelf_sym->type == relf_sym->type) && +- (strcmp(uelf_sym->name, relf_sym->name) == 0)) { +- found = FOUND; +- break; +- } +- } +- +- if (found == NOT_FOUND) { +- log_warn("Cannot find symbol '%s' in %s\n", +- uelf_sym->name, g_uelf_name); +- return NOT_FOUND; +- } ++ return relf_sym; + } + +- return found; ++ return NULL; + } + +-static void find_local_syms(struct upatch_elf *uelf, struct running_elf *relf, +- struct symbol *file_sym) ++static void match_file_local_symbols( ++ struct upatch_elf *uelf, struct running_elf *relf, ++ struct symbol *uelf_filesym, int relf_fileidx) + { +- struct debug_symbol *relf_sym = NULL; +- struct debug_symbol *found_sym = NULL; +- enum local_match found; ++ for (int i = relf_fileidx + 1; i < relf->obj_nr; i++) { ++ struct debug_symbol *sym = &relf->obj_syms[i]; + +- for (int i = 0; i < relf->obj_nr; i++) { +- relf_sym = &relf->obj_syms[i]; ++ if (sym->type == STT_FILE) { ++ break; // match within current file ++ } ++ if ((sym->bind != STB_LOCAL) || ++ ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { ++ continue; ++ } + +- if (relf_sym->type != STT_FILE) { ++ struct symbol *matched = match_uelf_local_symbol(uelf, uelf_filesym, sym); ++ if (matched == NULL) { ++ ERROR("Cannot find symbol '%s' in %s", sym->name, g_uelf_name); ++ } ++ } ++ ++ struct symbol *sym = uelf_filesym; ++ list_for_each_entry_continue(sym, &uelf->symbols, list) { ++ if (sym->type == STT_FILE) { ++ break; // match within current file ++ } ++ if ((sym->bind != STB_LOCAL) || ++ ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { + continue; + } +- if (strcmp(file_sym->name, relf_sym->name)) { ++ if (is_discarded_sym(relf, sym)) { + continue; + } + +- found = locals_match(uelf, relf, file_sym, i); +- if (found == NOT_FOUND) { +- continue; +- } else if (found == EMPTY) { +- found_sym = relf_sym; +- break; +- } else { +- if (found_sym) { +- ERROR("Found duplicate local symbols in '%s'", g_relf_name); +- } +- found_sym = relf_sym; ++ struct debug_symbol *matched = match_relf_local_symbol(relf, relf_fileidx, sym); ++ if (matched == NULL) { ++ ERROR("Cannot find symbol '%s' in %s", sym->name, g_relf_name); ++ } ++ if ((sym->debug_sym != NULL) && (sym->debug_sym != matched)) { ++ ERROR("Found duplicated symbol '%s' in %s", sym->name, g_relf_name); + } ++ sym->debug_sym = matched; + } ++} + +- if (!found_sym) { +- ERROR("Cannot find local symbol in '%s'", g_relf_name); +- } ++static bool find_debug_file_symbol(struct running_elf *relf, ++ struct symbol *file_sym, int *index) ++{ ++ for (int i = 0; i < relf->obj_nr; i++) { ++ struct debug_symbol *debug_sym = &relf->obj_syms[i]; + +- list_for_each_entry_continue(file_sym, &uelf->symbols, list) { +- if (file_sym->type == STT_FILE) { +- break; ++ if (debug_sym->type != STT_FILE) { ++ continue; ++ } ++ if (strcmp(file_sym->name, debug_sym->name) == 0) { ++ *index = i; ++ return true; + } +- file_sym->relf_sym = found_sym; + } ++ return false; + } + + /* +@@ -492,15 +467,19 @@ static void find_local_syms(struct upatch_elf *uelf, struct running_elf *relf, + * We then compare local symbol lists from both blocks and store the pointer + * to STT_FILE symbol in running elf for later using. + */ +-static void find_debug_symbol(struct upatch_elf *uelf, struct running_elf *relf) ++static void match_local_symbols(struct upatch_elf *uelf, struct running_elf *relf) + { +- struct symbol *file_sym = NULL; ++ struct symbol *uelf_filesym = NULL; + +- list_for_each_entry(file_sym, &uelf->symbols, list) { +- if ((file_sym->type == STT_FILE) && (file_sym->status == CHANGED)) { +- log_debug("file '%s' is CHANGED\n", file_sym->name); +- find_local_syms(uelf, relf, file_sym); ++ list_for_each_entry(uelf_filesym, &uelf->symbols, list) { ++ if ((uelf_filesym->type != STT_FILE) || (uelf_filesym->status != CHANGED)) { ++ continue; ++ } ++ int relf_fileidx = 0; ++ if (!find_debug_file_symbol(relf, uelf_filesym, &relf_fileidx)) { ++ ERROR("Cannot find file '%s' in %s", uelf_filesym->name, g_relf_name); + } ++ match_file_local_symbols(uelf, relf, uelf_filesym, relf_fileidx); + } + } + +@@ -1039,7 +1018,7 @@ int main(int argc, char*argv[]) + + upatch_compare_correlated_elements(&uelf_patched); + mark_file_symbols(&uelf_source); +- find_debug_symbol(&uelf_source, &relf); ++ match_local_symbols(&uelf_source, &relf); + + include_standard_elements(&uelf_patched); + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 59069a3..9738784 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -42,8 +42,8 @@ static void correlate_symbol(struct symbol *sym_orig, + sym_patched->name = sym_orig->name; + sym_patched->name_source = DATA_SOURCE_REF; + } +- if (sym_orig->relf_sym && !sym_patched->relf_sym) { +- sym_patched->relf_sym = sym_orig->relf_sym; ++ if (sym_orig->debug_sym && !sym_patched->debug_sym) { ++ sym_patched->debug_sym = sym_orig->debug_sym; + } + } + +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index 653439e..df5111a 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -112,7 +112,7 @@ struct symbol { + GElf_Sym sym; + char *name; + enum data_source name_source; +- struct debug_symbol *relf_sym; ++ struct debug_symbol *debug_sym; + unsigned int index; + unsigned char bind; + unsigned char type; +-- +2.43.0 + diff --git a/0076-upatch-diff-skip-local-symbols-like-__func__.xxx.patch b/0076-upatch-diff-skip-local-symbols-like-__func__.xxx.patch new file mode 100644 index 0000000000000000000000000000000000000000..a308dd1ab18fe0f6d1e5a824061b31f710b8fc35 --- /dev/null +++ b/0076-upatch-diff-skip-local-symbols-like-__func__.xxx.patch @@ -0,0 +1,62 @@ +From 63ac3788d9a24edaeda90eaa9c1a71caf9f0c3b3 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 29 Apr 2025 21:03:27 +0800 +Subject: [PATCH] upatch-diff: skip local symbols like __func__.xxx + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 3d6edc8..96fcbd4 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -348,6 +348,24 @@ static bool is_discarded_sym(struct running_elf *relf, struct symbol *sym) + return false; + } + ++static bool has_function_prefix(const char *sym_name) ++{ ++ static const char *SYM_NAMES[] = { ++ "__func__.", ++ "__FUNC__.", ++ "__PRETTY_FUNCTION__.", ++ NULL, ++ }; ++ ++ const char **name; ++ for (name = SYM_NAMES; *name; name++) { ++ if (strcmp(sym_name, *name) == 0) { ++ return true; ++ } ++ } ++ return false; ++} ++ + static struct symbol* match_uelf_local_symbol(struct upatch_elf *uelf, + struct symbol *start_sym, struct debug_symbol *relf_sym) + { +@@ -411,6 +429,9 @@ static void match_file_local_symbols( + ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { + continue; + } ++ if (has_function_prefix(sym->name)) { ++ continue; ++ } + + struct symbol *matched = match_uelf_local_symbol(uelf, uelf_filesym, sym); + if (matched == NULL) { +@@ -427,6 +448,9 @@ static void match_file_local_symbols( + ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { + continue; + } ++ if (has_function_prefix(sym->name)) { ++ continue; ++ } + if (is_discarded_sym(relf, sym)) { + continue; + } +-- +2.43.0 + diff --git a/0077-upatch-diff-warning-on-no-function-changed-when-crea.patch b/0077-upatch-diff-warning-on-no-function-changed-when-crea.patch new file mode 100644 index 0000000000000000000000000000000000000000..e9b147291bd4f764524b4f8c19434925067f1adb --- /dev/null +++ b/0077-upatch-diff-warning-on-no-function-changed-when-crea.patch @@ -0,0 +1,30 @@ +From e206547f6b1570054f7aec8594fd3c7bd997b247 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 7 May 2025 10:41:06 +0800 +Subject: [PATCH] upatch-diff: warning on no function changed when creating + patch + +Signed-off-by: renoseven +--- + upatch-diff/elf-create.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c +index 139f92d..63dbdb2 100644 +--- a/upatch-diff/elf-create.c ++++ b/upatch-diff/elf-create.c +@@ -161,6 +161,11 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, + nr++; + } + ++ if (nr == 0) { ++ log_warn("No functional changes\n"); ++ return; ++ } ++ + /* create text/rela section pair */ + sec = create_section_pair(uelf, ".upatch.funcs", sizeof(*funcs), nr); + relasec = sec->rela; +-- +2.43.0 + diff --git a/0078-upatch-diff-treat-.L-symbol-as-special-symbol.patch b/0078-upatch-diff-treat-.L-symbol-as-special-symbol.patch new file mode 100644 index 0000000000000000000000000000000000000000..38acccc64473dbf7d8736c7ff0c4747b66addecf --- /dev/null +++ b/0078-upatch-diff-treat-.L-symbol-as-special-symbol.patch @@ -0,0 +1,37 @@ +From 6956275e7e1298df7bf99355afdb111453295972 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 7 May 2025 10:55:42 +0800 +Subject: [PATCH] upatch-diff: treat .L symbol as special symbol + +Signed-off-by: renoseven +--- + upatch-diff/elf-common.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c +index 6e13c34..0ac8188 100644 +--- a/upatch-diff/elf-common.c ++++ b/upatch-diff/elf-common.c +@@ -59,6 +59,7 @@ bool is_special_static_symbol(struct symbol *sym) + ".CSWTCH", + "._entry", + ".C", ++ ".L", + NULL, + }; + +@@ -153,11 +154,6 @@ bool is_normal_static_local(struct symbol *sym) + if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) { + return false; + } +- // TODO: .Local ? need a example here +- if (!strncmp(sym->name, ".L", 2)) { +- ERROR("find no-local variable\n"); +- return false; +- } + if (!strchr(sym->name, '.')) { + return false; + } +-- +2.43.0 + diff --git a/0079-upatch-diff-allow-object-symbol-size-changes.patch b/0079-upatch-diff-allow-object-symbol-size-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..f4a9fe5ae5f5b4a838eef983f0a249fcb0dc65ca --- /dev/null +++ b/0079-upatch-diff-allow-object-symbol-size-changes.patch @@ -0,0 +1,28 @@ +From a4491cbb9ecf111891e9ca443d43184d9497ea5c Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 7 May 2025 11:02:31 +0800 +Subject: [PATCH] upatch-diff: allow object symbol size changes + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 7314a27..9aa92bf 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -40,10 +40,6 @@ static void compare_correlated_symbol(struct symbol *sym, struct symbol *twin) + if (sym->sym.st_info != twin->sym.st_info) { + ERROR("Symbol '%s' st_info mismatched", sym->name); + } +- // object symbol size cannot be changed +- if ((sym->type == STT_OBJECT) && (sym->sym.st_size != twin->sym.st_size)) { +- ERROR("Symbol '%s' object size mismatched", sym->name); +- } + + /* + * For local symbols, we handle them based on their matching sections. +-- +2.43.0 + diff --git a/0080-upatch-diff-optimize-log.patch b/0080-upatch-diff-optimize-log.patch new file mode 100644 index 0000000000000000000000000000000000000000..6adeed78c9e8540ff91820cddc65b8f03153707f --- /dev/null +++ b/0080-upatch-diff-optimize-log.patch @@ -0,0 +1,59 @@ +From 2216b99f6c74af08c41ab7858931da59cf62f128 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 14:37:17 +0800 +Subject: [PATCH] upatch-diff: optimize log + +Signed-off-by: renoseven +--- + upatch-diff/log.h | 27 +++++++++++++-------------- + 1 file changed, 13 insertions(+), 14 deletions(-) + +diff --git a/upatch-diff/log.h b/upatch-diff/log.h +index a109958..b91ae9a 100644 +--- a/upatch-diff/log.h ++++ b/upatch-diff/log.h +@@ -32,15 +32,16 @@ + extern enum log_level g_loglevel; + extern char *g_logprefix; + +-enum exit_status { +- EXIT_STATUS_SUCCESS = 0, +- EXIT_STATUS_ERROR = 1, +-}; +- + /* Since upatch-build is an one-shot program, we do not care about failure handler */ +-#define ERROR(format, ...) \ +- error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, \ +- g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) ++#define ERROR(format, ...) do { \ ++ if (g_logprefix) { \ ++ error(EXIT_FAILURE, 0, "ERROR: %s: %s:%d: " format, \ ++ g_logprefix, __func__, __LINE__, ##__VA_ARGS__); \ ++ } else { \ ++ error(EXIT_FAILURE, 0, "ERROR: %s:%d: " format, \ ++ __func__, __LINE__, ##__VA_ARGS__); \ ++ } \ ++} while (0) + + /* it is time cost */ + #define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__) +@@ -48,12 +49,10 @@ enum exit_status { + #define log_warn(format, ...) log(WARN, format, ##__VA_ARGS__) + #define log_error(format, ...) log(ERR, format, ##__VA_ARGS__) + +-#define log(level, format, ...) \ +- do { \ +- if (g_loglevel <= (level)) { \ +- printf(format, ##__VA_ARGS__); \ +- } \ +- } while (0) ++#define log(level, format, ...) do { \ ++ if (g_loglevel > (level)) { break; } \ ++ fprintf((level <= NORMAL) ? stdout : stderr, format, ##__VA_ARGS__); \ ++} while (0) + + #define REQUIRE(COND, message) \ + do { \ +-- +2.43.0 + diff --git a/0081-upatch-diff-enrich-error-message.patch b/0081-upatch-diff-enrich-error-message.patch new file mode 100644 index 0000000000000000000000000000000000000000..aab9e7014f6e79d92bef22764a7b6bf1e960ab78 --- /dev/null +++ b/0081-upatch-diff-enrich-error-message.patch @@ -0,0 +1,55 @@ +From 7a69921495c2e6d0f65d6d34abde0f8964355f87 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 14:37:32 +0800 +Subject: [PATCH] upatch-diff: enrich error message + +Signed-off-by: renoseven +--- + upatch-diff/upatch-elf.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c +index a51f008..ea09e54 100644 +--- a/upatch-diff/upatch-elf.c ++++ b/upatch-diff/upatch-elf.c +@@ -333,12 +333,12 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + + fd = open(name, O_RDONLY); + if (fd == -1) { +- ERROR("open %s failed with errno %d\n", name, errno); ++ ERROR("Failed to open '%s', %s", name, strerror(errno)); + } + + elf = elf_begin(fd, ELF_C_RDWR, NULL); + if (!elf) { +- ERROR("open elf %s failed with error %s\n", name, elf_errmsg(0)); ++ ERROR("Failed to parse elf, %s", elf_errmsg(0)); + } + + memset(uelf, 0, sizeof(*uelf)); +@@ -350,11 +350,10 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + uelf->fd = fd; + + if (!gelf_getehdr(uelf->elf, &ehdr)) { +- ERROR("get file %s elf header failed with error %s\n", name, +- elf_errmsg(0)); ++ ERROR("Failed to read elf header, %s", elf_errmsg(0)); + } + if (ehdr.e_type != ET_REL) { +- ERROR("only handles relocatable files\n"); ++ ERROR("File is not an object"); + } + + /* +@@ -369,7 +368,7 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + uelf->arch = X86_64; + break; + default: +- ERROR("unsupported architecture here"); ++ ERROR("Unsupported architecture"); + } + + create_section_list(uelf); +-- +2.43.0 + diff --git a/0082-upatch-diff-improve-initializing-object-handling.patch b/0082-upatch-diff-improve-initializing-object-handling.patch new file mode 100644 index 0000000000000000000000000000000000000000..bc54d11a706c1f747f76d8faa5553b1d818628d0 --- /dev/null +++ b/0082-upatch-diff-improve-initializing-object-handling.patch @@ -0,0 +1,548 @@ +From 79930859794b188c16c16e35798ae037e4b415c3 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 18:53:52 +0800 +Subject: [PATCH] upatch-diff: improve initializing & object handling + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 179 +++++++++++++------------------ + upatch-diff/running-elf.c | 72 ++++++------- + upatch-diff/running-elf.h | 5 +- + upatch-diff/upatch-elf.c | 53 ++++----- + upatch-diff/upatch-elf.h | 9 +- + 5 files changed, 141 insertions(+), 177 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 96fcbd4..9973ee0 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -57,7 +57,6 @@ + #include "elf-resolve.h" + #include "elf-create.h" + #include "running-elf.h" +-//#include "upatch-manage.h" + #include "upatch-patch.h" + + #define PROG_VERSION "upatch-diff "BUILD_VERSION +@@ -75,37 +74,17 @@ struct arguments { + bool debug; + }; + +-static struct argp_option options[] = { +- {"debug", 'd', NULL, 0, "Show debug output", 0}, +- {"source", 's', "source", 0, "Source object", 0}, +- {"patched", 'p', "patched", 0, "Patched object", 0}, +- {"running", 'r', "running", 0, "Running binary file", 0}, +- {"output", 'o', "output", 0, "Output object", 0}, ++static const struct argp_option ARGP_OPTION[] = { ++ {"source", 's', "", 0, "Source object", 0}, ++ {"patched", 'p', "", 0, "Patched object", 1}, ++ {"running", 'r', "", 0, "Running binary file", 2}, ++ {"output", 'o', "", 0, "Output object", 3}, ++ {"debug", 'd', NULL, 0, "Show debug output", 4}, + {NULL} + }; +- +-static char program_doc[] = +- "upatch-build -- generate a patch object based on the source object"; +- +-static char args_doc[] = +- "-s source_obj -p patched_obj -r elf_file -o output_obj"; +- ++static const char ARGP_DOC[] = "Generate a patch object based on source object"; + const char *argp_program_version = PROG_VERSION; + +-static error_t check_opt(struct argp_state *state) +-{ +- struct arguments *arguments = state->input; +- +- if (arguments->source_obj == NULL || +- arguments->patched_obj == NULL || +- arguments->running_elf == NULL || +- arguments->output_obj == NULL) { +- argp_usage(state); +- return ARGP_ERR_UNKNOWN; +- } +- return 0; +-} +- + static error_t parse_opt(int key, char *arg, struct argp_state *state) + { + struct arguments *arguments = state->input; +@@ -126,35 +105,43 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) + case 'o': + arguments->output_obj = arg; + break; +- case ARGP_KEY_ARG: +- break; +- case ARGP_KEY_END: +- return check_opt(state); + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + +-static struct argp argp = { +- options, parse_opt, args_doc, program_doc, NULL, NULL, NULL +-}; +- +-/* +- * Key point for chreate-diff-object: +- * 1. find changed func/data for each object +- * 2. link all these objects into a relocatable file +- * 3. add sections for management (hash/init/patch info etc.) +- * 4. locate old symbols for the relocatable file +- */ ++static bool check_args(struct arguments *arguments) ++{ ++ if (arguments->source_obj == NULL) { ++ log_error("The argument '--source ' requires a value\n"); ++ return false; ++ } ++ if (arguments->patched_obj == NULL) { ++ log_error("The argument '--patched ' requires a value\n"); ++ return false; ++ } ++ if (arguments->running_elf == NULL) { ++ log_error("The argument '--running ' requires a value\n"); ++ return false; ++ } ++ if (arguments->output_obj == NULL) { ++ log_error("The argument '--output ' requires a value\n"); ++ return false; ++ } ++ return true; ++} + +-/* Format of output file is the only export API */ + static void show_program_info(struct arguments *arguments) + { +- log_debug("source object: %s\n", arguments->source_obj); ++ log_debug("==============================\n"); ++ log_debug("%s\n", PROG_VERSION); ++ log_debug("==============================\n"); ++ log_debug("source object: %s\n", arguments->source_obj); + log_debug("patched object: %s\n", arguments->patched_obj); + log_debug("running binary: %s\n", arguments->running_elf); +- log_debug("output object: %s\n", arguments->output_obj); ++ log_debug("output object: %s\n", arguments->output_obj); ++ log_debug("------------------------------\n\n"); + } + + static void compare_elf_headers(struct upatch_elf *uelf_source, +@@ -182,19 +169,6 @@ static void compare_elf_headers(struct upatch_elf *uelf_source, + } + } + +-/* we can sure we only handle relocatable file, this is unnecessary */ +-static void check_program_headers(struct upatch_elf *uelf) +-{ +- size_t ph_nr; +- +- if (elf_getphdrnum(uelf->elf, &ph_nr)) { +- ERROR("elf_getphdrnum with error %s.", elf_errmsg(0)); +- } +- if (ph_nr != 0) { +- ERROR("ELF contains program header."); +- } +-} +- + static char *strarrcmp(char *name, char **prefix) + { + size_t len; +@@ -948,7 +922,6 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, + struct symbol *sym; + struct symbol *safesym; + +- memset(uelf_out, 0, sizeof(struct upatch_elf)); + uelf_out->arch = uelf_patched->arch; + + INIT_LIST_HEAD(&uelf_out->sections); +@@ -988,40 +961,48 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, + } + } + +-int main(int argc, char*argv[]) ++/* ++ * Key point for upatch-diff: ++ * 1. find changed func/data for each object ++ * 2. link all these objects into a relocatable file ++ * 3. add sections for management (hash/init/patch info etc.) ++ * 4. locate old symbols for the relocatable file ++ */ ++int main(int argc, char **argv) + { +- struct arguments arguments; +- struct upatch_elf uelf_source; +- struct upatch_elf uelf_patched; +- struct upatch_elf uelf_out; +- struct running_elf relf; +- +- memset(&arguments, 0, sizeof(arguments)); +- argp_parse(&argp, argc, argv, 0, NULL, &arguments); +- +- if (arguments.debug) { ++ static const struct argp ARGP = { ++ ARGP_OPTION, parse_opt, NULL, ARGP_DOC, NULL, NULL, NULL ++ }; ++ struct arguments args = { 0 }; ++ struct upatch_elf uelf_source = { 0 }; ++ struct upatch_elf uelf_patched = { 0 }; ++ struct upatch_elf uelf_out = { 0 }; ++ struct running_elf relf = { 0 }; ++ ++ if (argp_parse(&ARGP, argc, argv, 0, NULL, &args) != 0) { ++ return EXIT_FAILURE; ++ } ++ if (!check_args(&args)) { ++ return EXIT_FAILURE; ++ } ++ if (args.debug) { + g_loglevel = DEBUG; + } +- g_logprefix = basename(arguments.source_obj); +- show_program_info(&arguments); ++ show_program_info(&args); + + if (elf_version(EV_CURRENT) == EV_NONE) { +- ERROR("ELF library initialization failed"); ++ log_error("Failed to initialize elf library\n"); ++ return EXIT_FAILURE; + } + +- /* TODO: with debug info, this may changed */ +- g_uelf_name = arguments.source_obj; +- g_relf_name = arguments.running_elf; +- +- /* check error in log, since errno may be from libelf */ +- upatch_elf_open(&uelf_source, arguments.source_obj); +- upatch_elf_open(&uelf_patched, arguments.patched_obj); +- +- relf_init(arguments.running_elf, &relf); ++ uelf_open(&uelf_source, args.source_obj); ++ uelf_open(&uelf_patched, args.patched_obj); ++ relf_open(&relf, args.running_elf); + ++ g_logprefix = basename(args.source_obj); ++ g_uelf_name = args.source_obj; ++ g_relf_name = args.running_elf; + compare_elf_headers(&uelf_source, &uelf_patched); +- check_program_headers(&uelf_source); +- check_program_headers(&uelf_patched); + + bundle_symbols(&uelf_source); + bundle_symbols(&uelf_patched); +@@ -1031,7 +1012,6 @@ int main(int argc, char*argv[]) + + mark_ignored_sections(&uelf_source); + mark_ignored_sections(&uelf_patched); +- + mark_grouped_sections(&uelf_patched); + + replace_section_syms(&uelf_source); +@@ -1049,12 +1029,8 @@ int main(int argc, char*argv[]) + int change_count = include_changes(&uelf_patched); + if (change_count == 0) { + log_normal("No functional changes\n"); +- upatch_elf_destroy(&uelf_source); +- upatch_elf_destroy(&uelf_patched); +- +- upatch_elf_close(&uelf_source); +- upatch_elf_close(&uelf_patched); +- ++ uelf_close(&uelf_source); ++ uelf_close(&uelf_patched); + relf_close(&relf); + return 0; + } +@@ -1103,22 +1079,15 @@ int main(int argc, char*argv[]) + + upatch_dump_kelf(&uelf_out); + +- upatch_write_output_elf(&uelf_out, uelf_patched.elf, arguments.output_obj, +- 0664); +- +- upatch_elf_destroy(&uelf_source); +- upatch_elf_destroy(&uelf_patched); +- upatch_elf_destroy(&uelf_out); +- +- upatch_elf_close(&uelf_source); +- upatch_elf_close(&uelf_patched); +- upatch_elf_close(&uelf_out); ++ upatch_write_output_elf(&uelf_out, uelf_patched.elf, args.output_obj, 0664); ++ log_normal("Done\n"); + ++ uelf_close(&uelf_source); ++ uelf_close(&uelf_patched); ++ uelf_close(&uelf_out); + relf_close(&relf); + +- log_normal("Done\n"); + fflush(stdout); + fflush(stderr); +- +- return 0; ++ return EXIT_SUCCESS; + } +diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c +index b4d6d24..f984a26 100644 +--- a/upatch-diff/running-elf.c ++++ b/upatch-diff/running-elf.c +@@ -35,72 +35,63 @@ + #include "running-elf.h" + #include "log.h" + +-/* TODO: +- * need to judge whether running_elf is a Position-Independent Executable file +- * https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c +- */ +-static bool is_pie(void) +-{ +- return true; +-} +- +-static bool is_exec(struct Elf *elf) ++void relf_open(struct running_elf *relf, const char *name) + { + GElf_Ehdr ehdr; +- +- if (!gelf_getehdr(elf, &ehdr)) { +- ERROR("gelf_getehdr running_file failed for %s.", elf_errmsg(0)); +- } +- return ehdr.e_type == ET_EXEC || (ehdr.e_type == ET_DYN && is_pie()); +-} +- +-void relf_init(char *elf_name, struct running_elf *relf) +-{ + GElf_Shdr shdr; +- Elf_Scn *scn = NULL; +- Elf_Data *data; + GElf_Sym sym; + +- relf->fd = open(elf_name, O_RDONLY); ++ if (relf == NULL) { ++ return; ++ } ++ ++ relf->fd = open(name, O_RDONLY); + if (relf->fd == -1) { +- ERROR("open with errno = %d", errno); ++ ERROR("Failed to open '%s', %s", name, strerror(errno)); + } + + relf->elf = elf_begin(relf->fd, ELF_C_READ, NULL); + if (!relf->elf) { +- ERROR("elf_begin with error %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s', %s", name, elf_errmsg(0)); + } + +- relf->is_exec = is_exec(relf->elf); ++ if (!gelf_getehdr(relf->elf, &ehdr)) { ++ ERROR("Failed to read file '%s' elf header, %s", name, elf_errmsg(0)); ++ } ++ relf->is_exec = ((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN)); + ++ Elf_Scn *scn = NULL; + while ((scn = elf_nextscn(relf->elf, scn)) != NULL) { + if (!gelf_getshdr(scn, &shdr)) { +- ERROR("gelf_getshdr with error %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s' section header, %s", ++ name, elf_errmsg(0)); + } + if (shdr.sh_type == SHT_SYMTAB) { + break; + } + } + +- data = elf_getdata(scn, NULL); ++ Elf_Data *data = elf_getdata(scn, NULL); + if (!data) { +- ERROR("elf_getdata with error %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s' section data, %s", name, elf_errmsg(0)); + } + + relf->obj_nr = (int)(shdr.sh_size / shdr.sh_entsize); + relf->obj_syms = calloc((size_t)relf->obj_nr, sizeof(struct debug_symbol)); + if (!relf->obj_syms) { +- ERROR("calloc with errno = %d", errno); ++ ERROR("Failed to alloc memory, %s", strerror(errno)); + } + + for (int i = 0; i < relf->obj_nr; i++) { + if (!gelf_getsym(data, i, &sym)) { +- ERROR("gelf_getsym with error %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s' symbol, index=%d, %s", ++ name, i, elf_errmsg(0)); + } + relf->obj_syms[i].name = elf_strptr(relf->elf, + shdr.sh_link, sym.st_name); + if (!relf->obj_syms[i].name) { +- ERROR("elf_strptr with error %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s' symbol name, index=%d, %s", ++ name, i, elf_errmsg(0)); + } + relf->obj_syms[i].type = GELF_ST_TYPE(sym.st_info); + relf->obj_syms[i].bind = GELF_ST_BIND(sym.st_info); +@@ -110,15 +101,22 @@ void relf_init(char *elf_name, struct running_elf *relf) + } + } + +-int relf_close(struct running_elf *relf) ++void relf_close(struct running_elf *relf) + { +- free(relf->obj_syms); +- elf_end(relf->elf); ++ if (relf == NULL) { ++ return; ++ } ++ if (relf->obj_syms) { ++ free(relf->obj_syms); ++ } ++ if (relf->elf) { ++ elf_end(relf->elf); ++ } ++ if (relf->fd > 0) { ++ close(relf->fd); ++ } + relf->elf = NULL; +- close(relf->fd); + relf->fd = -1; +- +- return 0; + } + + bool lookup_relf(struct running_elf *relf, struct symbol *lookup_sym, +diff --git a/upatch-diff/running-elf.h b/upatch-diff/running-elf.h +index cdf61d1..23dd9c1 100644 +--- a/upatch-diff/running-elf.h ++++ b/upatch-diff/running-elf.h +@@ -56,9 +56,8 @@ struct running_elf { + bool is_exec; + }; + +-void relf_init(char *, struct running_elf *); +- +-int relf_close(struct running_elf *); ++void relf_open(struct running_elf *relf, const char *name); ++void relf_close(struct running_elf *relf); + + bool lookup_relf(struct running_elf *, struct symbol *, struct lookup_result *); + +diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c +index ea09e54..2f88a92 100644 +--- a/upatch-diff/upatch-elf.c ++++ b/upatch-diff/upatch-elf.c +@@ -322,38 +322,34 @@ static void destroy_string_list(struct upatch_elf *uelf) + INIT_LIST_HEAD(&uelf->strings); + } + +-void upatch_elf_open(struct upatch_elf *uelf, const char *name) ++void uelf_open(struct upatch_elf *uelf, const char *name) + { +- int fd = 1; +- +- Elf *elf = NULL; +- + GElf_Ehdr ehdr; +- struct section *sec; + +- fd = open(name, O_RDONLY); ++ if (uelf == NULL) { ++ return; ++ } ++ INIT_LIST_HEAD(&uelf->sections); ++ INIT_LIST_HEAD(&uelf->symbols); ++ INIT_LIST_HEAD(&uelf->strings); ++ ++ int fd = open(name, O_RDONLY); + if (fd == -1) { + ERROR("Failed to open '%s', %s", name, strerror(errno)); + } ++ uelf->fd = fd; + +- elf = elf_begin(fd, ELF_C_RDWR, NULL); ++ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + if (!elf) { +- ERROR("Failed to parse elf, %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s', %s", name, elf_errmsg(0)); + } +- +- memset(uelf, 0, sizeof(*uelf)); +- INIT_LIST_HEAD(&uelf->sections); +- INIT_LIST_HEAD(&uelf->symbols); +- INIT_LIST_HEAD(&uelf->strings); +- + uelf->elf = elf; +- uelf->fd = fd; + + if (!gelf_getehdr(uelf->elf, &ehdr)) { +- ERROR("Failed to read elf header, %s", elf_errmsg(0)); ++ ERROR("Failed to read file '%s' elf header, %s", name, elf_errmsg(0)); + } + if (ehdr.e_type != ET_REL) { +- ERROR("File is not an object"); ++ ERROR("File '%s' is not object file", name); + } + + /* +@@ -374,6 +370,7 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + create_section_list(uelf); + create_symbol_list(uelf); + ++ struct section *sec; + list_for_each_entry(sec, &uelf->sections, list) { + if (is_rela_section(sec)) { + create_rela_list(uelf, sec); +@@ -381,16 +378,22 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) + } + } + +-void upatch_elf_destroy(struct upatch_elf *uelf) ++void uelf_close(struct upatch_elf *uelf) + { ++ if (uelf == NULL) { ++ return; ++ } + destroy_section_list(uelf); + destroy_symbol_list(uelf); + destroy_string_list(uelf); +-} + +-void upatch_elf_close(struct upatch_elf *uelf) +-{ +- elf_end(uelf->elf); +- close(uelf->fd); +- memset(uelf, 0, sizeof(*uelf)); ++ if (uelf->elf) { ++ elf_end(uelf->elf); ++ } ++ if (uelf->fd > 0) { ++ close(uelf->fd); ++ } ++ uelf->elf = NULL; ++ uelf->fd = -1; ++ uelf->symtab_shndx = NULL; + } +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index df5111a..d8af728 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -138,12 +138,7 @@ struct upatch_elf { + int fd; + }; + +-// init a upatch_elf from a path +-void upatch_elf_open(struct upatch_elf *, const char *); +- +-// Destory upatch_elf struct +-void upatch_elf_destroy(struct upatch_elf *); +- +-void upatch_elf_close(struct upatch_elf *); ++void uelf_open(struct upatch_elf *uelf, const char *name); ++void uelf_close(struct upatch_elf *uelf); + + #endif +-- +2.43.0 + diff --git a/0083-upatch-diff-fix-uelf-list-insert-order-issue.patch b/0083-upatch-diff-fix-uelf-list-insert-order-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..56483b2aa9f0f6004e7e71e31b5f30b6fbc211b7 --- /dev/null +++ b/0083-upatch-diff-fix-uelf-list-insert-order-issue.patch @@ -0,0 +1,26 @@ +From 9248663d9a1c866abc9222dc671b4f3f9bb2607f Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 9 May 2025 15:15:08 +0800 +Subject: [PATCH] upatch-diff: fix uelf list insert order issue + +Signed-off-by: renoseven +--- + upatch-diff/elf-common.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index 672295c..01a1152 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -45,7 +45,7 @@ + } \ + INIT_LIST_HEAD(&(_new)->list); \ + if (_list) { \ +- list_add(&(_new)->list, (_list)); \ ++ list_add_tail(&(_new)->list, (_list)); \ + } \ + } while (0) + +-- +2.43.0 + diff --git a/0084-upatch-diff-reorganize-relf-struct.patch b/0084-upatch-diff-reorganize-relf-struct.patch new file mode 100644 index 0000000000000000000000000000000000000000..2ee77d8d851dba9ae195e30e054dcde7804c0f4f --- /dev/null +++ b/0084-upatch-diff-reorganize-relf-struct.patch @@ -0,0 +1,303 @@ +From 86401af6eff9cbc17450877f56a7b21c668dfef4 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 9 May 2025 17:10:03 +0800 +Subject: [PATCH] upatch-diff: reorganize relf struct + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 75 ++++++++++++++++---------------- + upatch-diff/elf-correlate.c | 4 +- + upatch-diff/running-elf.c | 34 +++++++-------- + upatch-diff/running-elf.h | 19 ++++---- + upatch-diff/upatch-elf.h | 2 +- + 5 files changed, 67 insertions(+), 67 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 9973ee0..b9ec36d 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -340,8 +340,8 @@ static bool has_function_prefix(const char *sym_name) + return false; + } + +-static struct symbol* match_uelf_local_symbol(struct upatch_elf *uelf, +- struct symbol *start_sym, struct debug_symbol *relf_sym) ++static struct symbol* find_uelf_local_sym(struct upatch_elf *uelf, ++ struct symbol *start_sym, struct relf_symbol *relf_sym) + { + struct symbol *uelf_sym = start_sym; + list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { +@@ -364,11 +364,11 @@ static struct symbol* match_uelf_local_symbol(struct upatch_elf *uelf, + return NULL; + } + +-static struct debug_symbol* match_relf_local_symbol(struct running_elf *relf, +- int start_index, struct symbol *uelf_sym) ++static struct relf_symbol* find_relf_local_sym(struct running_elf *relf, ++ struct relf_symbol *relf_file, struct symbol *uelf_sym) + { +- for (int i = start_index + 1; i < relf->obj_nr; i++) { +- struct debug_symbol *relf_sym = &relf->obj_syms[i]; ++ for (int i = relf_file->index + 1; i < relf->symbol_count; i++) { ++ struct relf_symbol *relf_sym = &relf->symbols[i]; + + if (relf_sym->type == STT_FILE) { + break; // find until next file +@@ -389,12 +389,28 @@ static struct debug_symbol* match_relf_local_symbol(struct running_elf *relf, + return NULL; + } + ++static struct relf_symbol* find_relf_file_sym(struct running_elf *relf, ++ struct symbol *file_sym) ++{ ++ for (int i = 0; i < relf->symbol_count; i++) { ++ struct relf_symbol *relf_sym = &relf->symbols[i]; ++ ++ if (relf_sym->type != STT_FILE) { ++ continue; ++ } ++ if (strcmp(file_sym->name, relf_sym->name) == 0) { ++ return relf_sym; ++ } ++ } ++ return NULL; ++} ++ + static void match_file_local_symbols( + struct upatch_elf *uelf, struct running_elf *relf, +- struct symbol *uelf_filesym, int relf_fileidx) ++ struct symbol *uelf_file, struct relf_symbol *relf_file) + { +- for (int i = relf_fileidx + 1; i < relf->obj_nr; i++) { +- struct debug_symbol *sym = &relf->obj_syms[i]; ++ for (int i = relf_file->index + 1; i < relf->symbol_count; i++) { ++ struct relf_symbol *sym = &relf->symbols[i]; + + if (sym->type == STT_FILE) { + break; // match within current file +@@ -407,13 +423,13 @@ static void match_file_local_symbols( + continue; + } + +- struct symbol *matched = match_uelf_local_symbol(uelf, uelf_filesym, sym); ++ struct symbol *matched = find_uelf_local_sym(uelf, uelf_file, sym); + if (matched == NULL) { + ERROR("Cannot find symbol '%s' in %s", sym->name, g_uelf_name); + } + } + +- struct symbol *sym = uelf_filesym; ++ struct symbol *sym = uelf_file; + list_for_each_entry_continue(sym, &uelf->symbols, list) { + if (sym->type == STT_FILE) { + break; // match within current file +@@ -429,34 +445,17 @@ static void match_file_local_symbols( + continue; + } + +- struct debug_symbol *matched = match_relf_local_symbol(relf, relf_fileidx, sym); ++ struct relf_symbol *matched = find_relf_local_sym(relf, relf_file, sym); + if (matched == NULL) { + ERROR("Cannot find symbol '%s' in %s", sym->name, g_relf_name); + } +- if ((sym->debug_sym != NULL) && (sym->debug_sym != matched)) { ++ if ((sym->relf_sym != NULL) && (sym->relf_sym != matched)) { + ERROR("Found duplicated symbol '%s' in %s", sym->name, g_relf_name); + } +- sym->debug_sym = matched; ++ sym->relf_sym = matched; + } + } + +-static bool find_debug_file_symbol(struct running_elf *relf, +- struct symbol *file_sym, int *index) +-{ +- for (int i = 0; i < relf->obj_nr; i++) { +- struct debug_symbol *debug_sym = &relf->obj_syms[i]; +- +- if (debug_sym->type != STT_FILE) { +- continue; +- } +- if (strcmp(file_sym->name, debug_sym->name) == 0) { +- *index = i; +- return true; +- } +- } +- return false; +-} +- + /* + * Because there can be duplicate symbols in elf, we need correlate each symbol + * from source elf to it's corresponding symbol in running elf. +@@ -467,17 +466,17 @@ static bool find_debug_file_symbol(struct running_elf *relf, + */ + static void match_local_symbols(struct upatch_elf *uelf, struct running_elf *relf) + { +- struct symbol *uelf_filesym = NULL; ++ struct symbol *uelf_file = NULL; + +- list_for_each_entry(uelf_filesym, &uelf->symbols, list) { +- if ((uelf_filesym->type != STT_FILE) || (uelf_filesym->status != CHANGED)) { ++ list_for_each_entry(uelf_file, &uelf->symbols, list) { ++ if ((uelf_file->type != STT_FILE) || (uelf_file->status != CHANGED)) { + continue; + } +- int relf_fileidx = 0; +- if (!find_debug_file_symbol(relf, uelf_filesym, &relf_fileidx)) { +- ERROR("Cannot find file '%s' in %s", uelf_filesym->name, g_relf_name); ++ struct relf_symbol *relf_file = find_relf_file_sym(relf, uelf_file); ++ if (relf_file == NULL) { ++ ERROR("Cannot find file '%s' in %s", uelf_file->name, g_relf_name); + } +- match_file_local_symbols(uelf, relf, uelf_filesym, relf_fileidx); ++ match_file_local_symbols(uelf, relf, uelf_file, relf_file); + } + } + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 9738784..59069a3 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -42,8 +42,8 @@ static void correlate_symbol(struct symbol *sym_orig, + sym_patched->name = sym_orig->name; + sym_patched->name_source = DATA_SOURCE_REF; + } +- if (sym_orig->debug_sym && !sym_patched->debug_sym) { +- sym_patched->debug_sym = sym_orig->debug_sym; ++ if (sym_orig->relf_sym && !sym_patched->relf_sym) { ++ sym_patched->relf_sym = sym_orig->relf_sym; + } + } + +diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c +index f984a26..db44cb4 100644 +--- a/upatch-diff/running-elf.c ++++ b/upatch-diff/running-elf.c +@@ -75,29 +75,29 @@ void relf_open(struct running_elf *relf, const char *name) + if (!data) { + ERROR("Failed to read file '%s' section data, %s", name, elf_errmsg(0)); + } +- +- relf->obj_nr = (int)(shdr.sh_size / shdr.sh_entsize); +- relf->obj_syms = calloc((size_t)relf->obj_nr, sizeof(struct debug_symbol)); +- if (!relf->obj_syms) { ++ relf->symbol_count = (int)(shdr.sh_size / shdr.sh_entsize); ++ relf->symbols = calloc((size_t)relf->symbol_count, sizeof(struct relf_symbol)); ++ if (!relf->symbols) { + ERROR("Failed to alloc memory, %s", strerror(errno)); + } + +- for (int i = 0; i < relf->obj_nr; i++) { ++ for (int i = 0; i < relf->symbol_count; i++) { + if (!gelf_getsym(data, i, &sym)) { + ERROR("Failed to read file '%s' symbol, index=%d, %s", + name, i, elf_errmsg(0)); + } +- relf->obj_syms[i].name = elf_strptr(relf->elf, ++ relf->symbols[i].name = elf_strptr(relf->elf, + shdr.sh_link, sym.st_name); +- if (!relf->obj_syms[i].name) { ++ if (!relf->symbols[i].name) { + ERROR("Failed to read file '%s' symbol name, index=%d, %s", + name, i, elf_errmsg(0)); + } +- relf->obj_syms[i].type = GELF_ST_TYPE(sym.st_info); +- relf->obj_syms[i].bind = GELF_ST_BIND(sym.st_info); +- relf->obj_syms[i].shndx = sym.st_shndx; +- relf->obj_syms[i].addr = sym.st_value; +- relf->obj_syms[i].size = sym.st_size; ++ relf->symbols[i].index = i; ++ relf->symbols[i].type = GELF_ST_TYPE(sym.st_info); ++ relf->symbols[i].bind = GELF_ST_BIND(sym.st_info); ++ relf->symbols[i].shndx = sym.st_shndx; ++ relf->symbols[i].addr = sym.st_value; ++ relf->symbols[i].size = sym.st_size; + } + } + +@@ -106,8 +106,8 @@ void relf_close(struct running_elf *relf) + if (relf == NULL) { + return; + } +- if (relf->obj_syms) { +- free(relf->obj_syms); ++ if (relf->symbols) { ++ free(relf->symbols); + } + if (relf->elf) { + elf_end(relf->elf); +@@ -122,13 +122,13 @@ void relf_close(struct running_elf *relf) + bool lookup_relf(struct running_elf *relf, struct symbol *lookup_sym, + struct lookup_result *result) + { +- struct debug_symbol *symbol = NULL; ++ struct relf_symbol *symbol = NULL; + + log_debug("looking up symbol '%s'\n", lookup_sym->name); + memset(result, 0, sizeof(*result)); + +- for (int i = 0; i < relf->obj_nr; i++) { +- symbol = &relf->obj_syms[i]; ++ for (int i = 0; i < relf->symbol_count; i++) { ++ symbol = &relf->symbols[i]; + + if (result->symbol != NULL && symbol->type == STT_FILE) { + break; +diff --git a/upatch-diff/running-elf.h b/upatch-diff/running-elf.h +index 23dd9c1..ae05d83 100644 +--- a/upatch-diff/running-elf.h ++++ b/upatch-diff/running-elf.h +@@ -34,13 +34,8 @@ + + struct symbol; + +-struct lookup_result { +- struct debug_symbol *symbol; +- unsigned long sympos; +- bool global; +-}; +- +-struct debug_symbol { ++struct relf_symbol { ++ int index; + char *name; + unsigned char type, bind; + unsigned int shndx; +@@ -49,13 +44,19 @@ struct debug_symbol { + }; + + struct running_elf { +- int obj_nr; +- struct debug_symbol *obj_syms; + int fd; + Elf *elf; ++ struct relf_symbol *symbols; ++ int symbol_count; + bool is_exec; + }; + ++struct lookup_result { ++ struct relf_symbol *symbol; ++ unsigned long sympos; ++ bool global; ++}; ++ + void relf_open(struct running_elf *relf, const char *name); + void relf_close(struct running_elf *relf); + +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index d8af728..eeb7fb2 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -112,7 +112,7 @@ struct symbol { + GElf_Sym sym; + char *name; + enum data_source name_source; +- struct debug_symbol *debug_sym; ++ struct relf_symbol *relf_sym; + unsigned int index; + unsigned char bind; + unsigned char type; +-- +2.43.0 + diff --git a/0085-upatch-diff-fix-cannot-find-local-symbol-in-relf-iss.patch b/0085-upatch-diff-fix-cannot-find-local-symbol-in-relf-iss.patch new file mode 100644 index 0000000000000000000000000000000000000000..57c7049f7f43142afaa6861ceaf452dff20d318a --- /dev/null +++ b/0085-upatch-diff-fix-cannot-find-local-symbol-in-relf-iss.patch @@ -0,0 +1,56 @@ +From ac18ffa5525029a4b69b11540e8f818a95d0a739 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 9 May 2025 18:04:16 +0800 +Subject: [PATCH] upatch-diff: fix cannot find local symbol in relf issue + + In some case, compiler may generate multiple parts of +function symbol like 'xxx.part.0' or 'xxxx.cold', which +is called subfunction. They were already handled by +detect_child_functions(). + + When matching local symbols between objects & debuginfos, +the debuginfo may only contain function symbol, but no +subfuction symbol, even they were compiled with same compiler. + + To solve this, we need to check if the symbol is a subfunction +and stop searching it in debuginfo file. + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 3 +++ + upatch-diff/elf-common.h | 5 +++++ + 2 files changed, 8 insertions(+) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index b9ec36d..291542f 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -438,6 +438,9 @@ static void match_file_local_symbols( + ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { + continue; + } ++ if (is_subfunction_sym(sym)) { ++ continue; ++ } + if (has_function_prefix(sym->name)) { + continue; + } +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index 01a1152..d3dbd2f 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -288,6 +288,11 @@ static inline bool is_local_func_sym(struct symbol *sym) + return sym->bind == STB_LOCAL && sym->type == STT_FUNC; + } + ++static inline bool is_subfunction_sym(struct symbol *sym) ++{ ++ return sym->parent != NULL; ++} ++ + static inline bool is_local_sym(struct symbol *sym) + { + return sym->bind == STB_LOCAL; +-- +2.43.0 + diff --git a/0086-upatch-diff-fix-coredump-in-checking-symbol.patch b/0086-upatch-diff-fix-coredump-in-checking-symbol.patch new file mode 100644 index 0000000000000000000000000000000000000000..6f6f8a6475b6753b1810627d13f121d25b12e32c --- /dev/null +++ b/0086-upatch-diff-fix-coredump-in-checking-symbol.patch @@ -0,0 +1,27 @@ +From 7040621e4d664fab6ef9e765e575c104f5c28c93 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 9 May 2025 18:32:51 +0800 +Subject: [PATCH] upatch-diff: fix coredump in checking symbol + +Signed-off-by: renoseven +--- + upatch-diff/elf-common.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c +index 0ac8188..3636c01 100644 +--- a/upatch-diff/elf-common.c ++++ b/upatch-diff/elf-common.c +@@ -34,6 +34,9 @@ static bool is_dynamic_debug_symbol(struct symbol *sym) + NULL, + }; + ++ if (sym->sec == NULL) { ++ return false; ++ } + if ((sym->type == STT_OBJECT) || (sym->type == STT_SECTION)) { + const char **sec_name; + for (sec_name = SEC_NAMES; *sec_name; sec_name++) { +-- +2.43.0 + diff --git a/0087-upatch-diff-fix-compare-common-section-symbol-failur.patch b/0087-upatch-diff-fix-compare-common-section-symbol-failur.patch new file mode 100644 index 0000000000000000000000000000000000000000..7e38147a542af64aef6a163723eb61320d989866 --- /dev/null +++ b/0087-upatch-diff-fix-compare-common-section-symbol-failur.patch @@ -0,0 +1,32 @@ +From 18abe2a81f2e1af6a98e9fc8290e670e6b46812b Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 12 May 2025 09:53:10 +0800 +Subject: [PATCH] upatch-diff: fix compare common section symbol failure + + Comparing symbol with st_shndx == SHN_COMMON would cause an +error "Symbol '%s' don't have section", which is not as expected. +We need to handle SHN_COMMON before comparasion. + +Signed-off-by: renoseven +--- + upatch-diff/elf-compare.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 9aa92bf..227b600 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -44,7 +44,9 @@ static void compare_correlated_symbol(struct symbol *sym, struct symbol *twin) + /* + * For local symbols, we handle them based on their matching sections. + */ +- if ((sym->sym.st_shndx == SHN_UNDEF) || (sym->sym.st_shndx == SHN_ABS)) { ++ if ((sym->sym.st_shndx == SHN_UNDEF) || ++ (sym->sym.st_shndx == SHN_ABS) || ++ (sym->sym.st_shndx == SHN_COMMON)) { + sym->status = SAME; + return; + } +-- +2.43.0 + diff --git a/0088-upatch-diff-optimize-debug-output.patch b/0088-upatch-diff-optimize-debug-output.patch new file mode 100644 index 0000000000000000000000000000000000000000..599a26f77e86c104f23d68de26235bc8bb6752ae --- /dev/null +++ b/0088-upatch-diff-optimize-debug-output.patch @@ -0,0 +1,591 @@ +From 8dcce329e2f8e2297fe96520d12f0f7de4f7e08e Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 12 May 2025 11:50:09 +0800 +Subject: [PATCH] upatch-diff: optimize debug output + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 30 ++------ + upatch-diff/elf-compare.c | 12 +--- + upatch-diff/elf-correlate.c | 9 --- + upatch-diff/elf-create.c | 38 ---------- + upatch-diff/elf-create.h | 2 - + upatch-diff/elf-debug.c | 120 +++++++++++++------------------ + upatch-diff/elf-debug.h | 5 +- + upatch-diff/upatch-elf.c | 39 ---------- + 8 files changed, 60 insertions(+), 195 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 291542f..c7be45d 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -522,8 +522,8 @@ static void mark_grouped_sections(struct upatch_elf *uelf) + ERROR("Group section not found"); + } + sec->grouped = true; +- log_debug("Marking section '%s' (%d) as grouped\n", +- sec->name, sec->index); ++ log_debug("Marking section grouped, index: %d, name: '%s'\n", ++ sec->index, sec->name); + data++; + } + } +@@ -555,17 +555,12 @@ static void replace_section_syms(struct upatch_elf *uelf) + rela->sym->type != STT_SECTION) { + continue; + } +- log_debug("Found replace symbol for section '%s'\n", +- rela->sym->name); +- + /* + * for section symbol, rela->sym->sec is the section itself. + * rela->sym->sec->sym is the bundleable symbol which is + * a function or object. + */ + if (rela->sym->sec->sym) { +- log_debug("Act: Replace it with '%s' <- '%s'\n", +- rela->sym->sec->sym->name, rela->sym->sec->name); + rela->sym = rela->sym->sec->sym; + if (rela->sym->sym.st_value != 0) { + ERROR("Symbol offset is not zero."); +@@ -601,14 +596,10 @@ static void replace_section_syms(struct upatch_elf *uelf) + if (is_mapping_symbol(uelf, sym)) { + continue; + } +- log_debug("Find relocation reference for empty symbol.\n"); + } else if (target_off < start || target_off >= end) { + continue; + } + +- log_debug("'%s': Replacing '%s+%ld' reference with '%s+%ld'\n", +- relasec->name, rela->sym->name, rela->addend, +- sym->name, rela->addend - start); + found = true; + rela->sym = sym; + rela->addend -= start; +@@ -680,8 +671,8 @@ static void mark_ignored_sections(struct upatch_elf *uelf) + sec->base->name : sec->name; + if (strncmp(sec_name, ignored_name, name_len) == 0) { + sec->ignored = true; +- log_debug("Marking section '%s' (%d) as ignored\n", +- sec->name, sec->index); ++ log_debug("Marking section ignored, index: %d, name: '%s'\n", ++ sec->index, sec->name); + break; + } + } +@@ -734,8 +725,6 @@ static void include_symbol(struct symbol *sym) + * might be needed: either permanently for a rela, or temporarily for + * the later creation of a dynrela. + */ +- log_debug("Include symbol '%s', status: %s\n", +- sym->name, status_str(sym->status)); + sym->include = true; + /* + * For special static symbols, we need include it's section +@@ -763,8 +752,6 @@ static void include_section(struct section *sec) + return; + } + +- log_debug("Include section '%s', status: %s\n", +- sec->name, status_str(sec->status)); + sec->include = true; + + if (is_rela_section(sec)) { +@@ -1021,13 +1008,13 @@ int main(int argc, char **argv) + + upatch_correlate_elf(&uelf_source, &uelf_patched); + upatch_correlate_static_local_variables(&uelf_source, &uelf_patched); ++ upatch_print_correlation(&uelf_patched); + + upatch_compare_correlated_elements(&uelf_patched); + mark_file_symbols(&uelf_source); + match_local_symbols(&uelf_source, &relf); + + include_standard_elements(&uelf_patched); +- + int change_count = include_changes(&uelf_patched); + if (change_count == 0) { + log_normal("No functional changes\n"); +@@ -1036,11 +1023,8 @@ int main(int argc, char **argv) + relf_close(&relf); + return 0; + } +- + upatch_print_changes(&uelf_patched); + +- upatch_dump_kelf(&uelf_patched); +- + verify_patchability(&uelf_patched); + + include_special_local_section(&uelf_patched); +@@ -1069,8 +1053,6 @@ int main(int argc, char **argv) + + upatch_rebuild_relocations(&uelf_out); + +- upatch_check_relocations(); +- + upatch_create_shstrtab(&uelf_out); + + upatch_create_strtab(&uelf_out); +@@ -1079,8 +1061,6 @@ int main(int argc, char **argv) + + upatch_create_symtab(&uelf_out); + +- upatch_dump_kelf(&uelf_out); +- + upatch_write_output_elf(&uelf_out, uelf_patched.elf, args.output_obj, 0664); + log_normal("Done\n"); + +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 227b600..97daa0b 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -86,7 +86,6 @@ void upatch_compare_symbols(struct upatch_elf *uelf) + } else { + compare_correlated_symbol(sym, sym->twin); + } +- log_debug("Symbol '%s' is %s\n", sym->name, status_str(sym->status)); + } + } + +@@ -163,20 +162,20 @@ static int compare_correlated_section(struct section *sec, struct section *twin) + + if (is_note_section(sec)) { + sec->status = SAME; +- goto out; ++ return 0; + } + /* As above but for aarch64 */ + if (!strcmp(sec->name, ".rela__patchable_function_entries") || + !strcmp(sec->name, "__patchable_function_entries")) { + sec->status = SAME; +- goto out; ++ return 0; + } + /* compare file size and data size(memory size) */ + if (sec->sh.sh_size != twin->sh.sh_size || + sec->data->d_size != twin->data->d_size || + (sec->rela && !twin->rela) || (!sec->rela && twin->rela)) { + sec->status = CHANGED; +- goto out; ++ return 0; + } + + if (is_rela_section(sec)) { +@@ -185,11 +184,6 @@ static int compare_correlated_section(struct section *sec, struct section *twin) + compare_correlated_nonrela_section(sec, twin); + } + +-out: +- if (sec->status == CHANGED) { +- log_debug("section %s has changed\n", sec->name); +- } +- + return 0; + } + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 59069a3..d5e76f6 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -30,9 +30,6 @@ + static void correlate_symbol(struct symbol *sym_orig, + struct symbol *sym_patched) + { +- log_debug("correlate symbol %s <-> %s\n", +- sym_orig->name, sym_patched->name); +- + sym_orig->twin = sym_patched; + sym_patched->twin = sym_orig; + sym_orig->status = sym_patched->status = SAME; +@@ -109,9 +106,6 @@ void upatch_correlate_symbols(struct upatch_elf *uelf_source, + static void correlate_section_impl(struct section *sec_orig, + struct section *sec_patched) + { +- log_debug("correlate section %s <-> %s\n", +- sec_orig->name, sec_patched->name); +- + sec_orig->twin = sec_patched; + sec_patched->twin = sec_orig; + /* set initial status, might change */ +@@ -384,15 +378,12 @@ void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + continue; + } + +- log_debug("find normal symbol %s\n", sym->name); + if (sym->twin) { + uncorrelate_symbol(sym); + } + + bundled = (sym == sym->sec->sym) ? 1 : 0; + if (bundled && sym->sec->twin) { +- log_debug("find bundled static symbol %s\n", sym->name); +- + uncorrelate_section(sym->sec); + if (sym->sec->secsym) { + uncorrelate_symbol(sym->sec->secsym); +diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c +index 63dbdb2..964079d 100644 +--- a/upatch-diff/elf-create.c ++++ b/upatch-diff/elf-create.c +@@ -384,25 +384,6 @@ void upatch_rebuild_relocations(struct upatch_elf *uelf) + } + } + +-void upatch_check_relocations(void) +-{ +- log_debug("upatch_check_relocations does not work now.\n"); +- return; +-} +- +-static void print_strtab(char *buf, size_t size) +-{ +- size_t i; +- +- for (i = 0; i < size; i++) { +- if (buf[i] == 0) { +- log_debug("\\0"); +- } else { +- log_debug("%c", buf[i]); +- } +- } +-} +- + void upatch_create_shstrtab(struct upatch_elf *uelf) + { + struct section *shstrtab; +@@ -451,17 +432,6 @@ void upatch_create_shstrtab(struct upatch_elf *uelf) + shstrtab->data->d_buf = buf; + shstrtab->data->d_size = size; + shstrtab->dbuf_source = DATA_SOURCE_ALLOC; +- +- log_debug("shstrtab: "); +- print_strtab(buf, size); +- log_debug("\n"); +- +- list_for_each_entry(sec, &uelf->sections, list) { +- if (sec->ignored) { +- continue; +- } +- log_debug("%s @ shstrtab offset %d\n", sec->name, sec->sh.sh_name); +- } + } + + void upatch_create_strtab(struct upatch_elf *uelf) +@@ -507,14 +477,6 @@ void upatch_create_strtab(struct upatch_elf *uelf) + strtab->data->d_buf = buf; + strtab->data->d_size = size; + strtab->dbuf_source = DATA_SOURCE_ALLOC; +- +- log_debug("strtab: "); +- print_strtab(buf, size); +- log_debug("\n"); +- +- list_for_each_entry(sym, &uelf->symbols, list) { +- log_debug("%s @ strtab offset %d\n", sym->name, sym->sym.st_name); +- } + } + + void upatch_create_symtab(struct upatch_elf *uelf) +diff --git a/upatch-diff/elf-create.h b/upatch-diff/elf-create.h +index f18d40b..18778b5 100644 +--- a/upatch-diff/elf-create.h ++++ b/upatch-diff/elf-create.h +@@ -43,8 +43,6 @@ void upatch_reindex_elements(struct upatch_elf *); + + void upatch_rebuild_relocations(struct upatch_elf *); + +-void upatch_check_relocations(void); +- + void upatch_create_shstrtab(struct upatch_elf *); + + void upatch_create_strtab(struct upatch_elf *); +diff --git a/upatch-diff/elf-debug.c b/upatch-diff/elf-debug.c +index 90e112b..44b0e09 100644 +--- a/upatch-diff/elf-debug.c ++++ b/upatch-diff/elf-debug.c +@@ -31,17 +31,58 @@ + #include "elf-debug.h" + #include "upatch-elf.h" + ++void upatch_print_correlation(struct upatch_elf *uelf) ++{ ++ if (uelf == NULL) { ++ return; ++ } ++ ++ log_debug("\n------------------------------\n"); ++ log_debug("Section\n"); ++ log_debug("------------------------------\n"); ++ struct section *sec; ++ list_for_each_entry(sec, &uelf->sections, list) { ++ if (sec->twin != NULL) { ++ log_debug("index: %04d, name: '%s' -> index: %04d, name: '%s'\n", ++ sec->index, sec->name, sec->twin->index, sec->twin->name); ++ } else { ++ log_debug("index: %04d, name: '%s' -> None\n", ++ sec->index, sec->name); ++ } ++ } ++ log_debug("------------------------------\n"); ++ log_debug("\n"); ++ log_debug("------------------------------\n"); ++ log_debug("Symbol\n"); ++ log_debug("------------------------------\n"); ++ struct symbol *sym; ++ list_for_each_entry(sym, &uelf->symbols, list) { ++ if (sym->twin != NULL) { ++ log_debug("index: %04d, name: '%s' -> index: %04d, name: '%s'\n", ++ sym->index, sym->name, sym->twin->index, sym->twin->name); ++ } else { ++ log_debug("index: %04d, name: '%s' -> None\n", ++ sym->index, sym->name); ++ } ++ } ++ log_debug("------------------------------\n"); ++} ++ + void upatch_print_changes(struct upatch_elf *uelf) + { +- struct symbol *sym = NULL; +- struct section *sec = NULL; ++ struct symbol *sym; ++ struct section *sec; + +- log_normal("------------------------------\n"); ++ if (uelf == NULL) { ++ return; ++ } ++ ++ log_normal("\n------------------------------\n"); + log_normal("New symbol\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->status == NEW) { +- log_normal("idx: %04u, name: '%s'\n", sym->index, sym->name); ++ log_normal("index: %04d, name: '%s'\n", sym->index, sym->name); + } + } + log_normal("------------------------------\n"); +@@ -51,7 +92,7 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->status == NEW) { +- log_normal("idx: %04u, name: '%s'\n", sec->index, sec->name); ++ log_normal("index: %04d, name: '%s'\n", sec->index, sec->name); + } + } + log_normal("------------------------------\n"); +@@ -61,7 +102,7 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->status == CHANGED) { +- log_normal("idx: %04u, name: '%s'\n", sym->index, sym->name); ++ log_normal("index: %04d, name: '%s'\n", sym->index, sym->name); + } + } + log_normal("------------------------------\n"); +@@ -71,7 +112,7 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->status == CHANGED) { +- log_normal("idx: %04u, name: '%s'\n", sec->index, sec->name); ++ log_normal("index: %04d, name: '%s'\n", sec->index, sec->name); + } + } + log_normal("------------------------------\n"); +@@ -81,7 +122,7 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->include) { +- log_normal("idx: %04u, name: '%s', status: %s\n", ++ log_normal("index: %04d, name: '%s', status: %s\n", + sym->index, sym->name, status_str(sym->status)); + } + } +@@ -92,70 +133,9 @@ void upatch_print_changes(struct upatch_elf *uelf) + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->include) { +- log_normal("idx: %04u, name: '%s', status: %s\n", ++ log_normal("index: %04d, name: '%s', status: %s\n", + sec->index, sec->name, status_str(sec->status)); + } + } + log_normal("------------------------------\n"); + } +- +-void upatch_dump_kelf(struct upatch_elf *uelf) +-{ +- struct section *sec; +- struct symbol *sym; +- struct rela *rela; +- +- log_debug("\n=== Sections ===\n"); +- list_for_each_entry(sec, &uelf->sections, list) { +- log_debug("%02d %s (%s)", +- sec->index, sec->name, status_str(sec->status)); +- if (is_rela_section(sec)) { +- if (sec->ignored) { +- continue; +- } +- log_debug(", base-> %s\n", sec->base->name); +- log_debug("rela section expansion\n"); +- list_for_each_entry(rela, &sec->relas, list) { +- log_debug("sym %d, offset %ld, type %d, %s %s %ld\n", +- rela->sym->index, rela->offset, +- rela->type, rela->sym->name, +- (rela->addend < 0) ? "-" : "+", +- labs(rela->addend)); +- } +- } else { +- if (sec->sym) { +- log_debug(", sym-> %s", sec->sym->name); +- } +- if (sec->secsym) { +- log_debug(", secsym-> %s", sec->secsym->name); +- } +- if (sec->rela) { +- log_debug(", rela-> %s", sec->rela->name); +- } +- } +- log_debug("\n"); +- } +- +- log_debug("\n=== Symbols ===\n"); +- list_for_each_entry(sym, &uelf->symbols, list) { +- log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)", +- sym->index, sym->type, sym->bind, sym->sym.st_shndx, +- sym->name, status_str(sym->status)); +- if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT)) { +- log_debug(" -> %s", sym->sec->name); +- } +- log_debug("\n"); +- } +-} +- +-/* debuginfo releated */ +-static inline bool skip_bytes(unsigned char **iter, unsigned char *end, +- unsigned int len) +-{ +- if ((unsigned int)(end - *iter) < len) { +- *iter = end; +- return false; +- } +- *iter += len; +- return true; +-} +diff --git a/upatch-diff/elf-debug.h b/upatch-diff/elf-debug.h +index 7c8db40..09122f6 100644 +--- a/upatch-diff/elf-debug.h ++++ b/upatch-diff/elf-debug.h +@@ -27,8 +27,7 @@ + + #include "upatch-elf.h" + +-void upatch_print_changes(struct upatch_elf *); +- +-void upatch_dump_kelf(struct upatch_elf *); ++void upatch_print_correlation(struct upatch_elf *uelf); ++void upatch_print_changes(struct upatch_elf *uelf); + + #endif /* __UPATCH_ELF_DEBUG_H_ */ +diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c +index 2f88a92..81567f5 100644 +--- a/upatch-diff/upatch-elf.c ++++ b/upatch-diff/upatch-elf.c +@@ -56,7 +56,6 @@ static void create_section_list(struct upatch_elf *uelf) + ERROR("elf_getshdrstrndx with error %s", elf_errmsg(0)); + } + +- log_debug("=== section list (%zu) ===\n", sections_nr); + while (sections_nr--) { + ALLOC_LINK(sec, &uelf->sections); + +@@ -87,9 +86,6 @@ static void create_section_list(struct upatch_elf *uelf) + if (sec->sh.sh_type == SHT_SYMTAB_SHNDX) { + uelf->symtab_shndx = sec->data; /* correct ? */ + } +- +- log_debug("ndx %02d, data %p, size %zu, name %s\n", +- sec->index, sec->data->d_buf, sec->data->d_size, sec->name); + } + + if (elf_nextscn(uelf->elf, scn)) { +@@ -114,7 +110,6 @@ static void create_symbol_list(struct upatch_elf *uelf) + + symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); + +- log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr); + while (symbols_nr--) { + ALLOC_LINK(sym, &uelf->symbols); + INIT_LIST_HEAD(&sym->children); +@@ -157,12 +152,6 @@ static void create_symbol_list(struct upatch_elf *uelf) + sym->name = sym->sec->name; + } + } +- log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s", +- sym->index, sym->type, sym->bind, sym->sym.st_shndx, sym->name); +- if (sym->sec) { +- log_debug(" -> %s", sym->sec->name); +- } +- log_debug("\n"); + } + } + +@@ -173,7 +162,6 @@ static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) + + unsigned int symndx; + int index = 0; +- int skip = 0; + + INIT_LIST_HEAD(&relasec->relas); + +@@ -182,23 +170,9 @@ static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) + if (!relasec->base) { + ERROR("no base section found for relocation section %s", relasec->name); + } +- + relasec->base->rela = relasec; + rela_nr = relasec->sh.sh_size / relasec->sh.sh_entsize; + +- log_debug("\n=== rela list for %s (%ld entries) ===\n", +- relasec->base->name, rela_nr); +- +- if (is_debug_section(relasec)) { +- log_debug("skipping rela listing for .debug_* section\n"); +- skip = 1; +- } +- +- if (is_note_section(relasec)) { +- log_debug("skipping rela listing for .note_* section\n"); +- skip = 1; +- } +- + while (rela_nr--) { + ALLOC_LINK(rela, &relasec->relas); + +@@ -226,19 +200,6 @@ static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) + rela->sym->name, rela->addend); + } + } +- +- if (skip) { +- continue; +- } +- +- log_debug("offset %ld, type %d, %s %s %ld", rela->offset, +- rela->type, rela->sym->name, +- (rela->addend < 0) ? "-" : "+", labs(rela->addend)); +- if (rela->string) { +- // rela->string is not utf8 +- log_debug(" string = %s", rela->string); +- } +- log_debug("\n"); + } + } + +-- +2.43.0 + diff --git a/0089-upatch-diff-support-text-section-offset.patch b/0089-upatch-diff-support-text-section-offset.patch new file mode 100644 index 0000000000000000000000000000000000000000..157dc131bd8c4d9ed4bce44632a16346f4b07aee --- /dev/null +++ b/0089-upatch-diff-support-text-section-offset.patch @@ -0,0 +1,154 @@ +From f7acf7f2daf4c0bf8f4f5755532f3c9489c87ec0 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 12 May 2025 10:59:38 +0800 +Subject: [PATCH] upatch-diff: support text section offset + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 46 +++++++++++++++++++++++++------- + upatch-diff/elf-create.c | 8 ++++-- + upatch-diff/elf-create.h | 3 ++- + 3 files changed, 45 insertions(+), 12 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index c7be45d..1ffc9f5 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -71,28 +71,42 @@ struct arguments { + char *patched_obj; + char *running_elf; + char *output_obj; ++ unsigned long text_offset; + bool debug; + }; + + static const struct argp_option ARGP_OPTION[] = { +- {"source", 's', "", 0, "Source object", 0}, +- {"patched", 'p', "", 0, "Patched object", 1}, +- {"running", 'r', "", 0, "Running binary file", 2}, +- {"output", 'o', "", 0, "Output object", 3}, +- {"debug", 'd', NULL, 0, "Show debug output", 4}, ++ {"source", 's', "", 0, "Source object", 0}, ++ {"patched", 'p', "", 0, "Patched object", 1}, ++ {"running", 'r', "", 0, "Running binary file", 2}, ++ {"output", 'o', "", 0, "Output object", 3}, ++ {"text-offset", 't', "", 0, "Text section offset", 4}, ++ {"debug", 'd', NULL, 0, "Show debug output", 5}, + {NULL} + }; + static const char ARGP_DOC[] = "Generate a patch object based on source object"; + const char *argp_program_version = PROG_VERSION; + ++static void parse_text_offset(struct argp_state *state, const char *arg) ++{ ++ errno = 0; ++ char *endptr = NULL; ++ ++ unsigned long offset = strtoul(arg, &endptr, 0); ++ if ((errno != 0) || (*endptr != '\0') || ++ ((errno == ERANGE) && (offset == ULONG_MAX))) { ++ argp_error(state, "ERROR: Invalid text section offset '%s'", arg); ++ } ++ ++ struct arguments *arguments = state->input; ++ arguments->text_offset = offset; ++} ++ + static error_t parse_opt(int key, char *arg, struct argp_state *state) + { + struct arguments *arguments = state->input; + + switch (key) { +- case 'd': +- arguments->debug = true; +- break; + case 's': + arguments->source_obj = arg; + break; +@@ -105,6 +119,12 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) + case 'o': + arguments->output_obj = arg; + break; ++ case 't': ++ parse_text_offset(state, arg); ++ break; ++ case 'd': ++ arguments->debug = true; ++ break; + default: + return ARGP_ERR_UNKNOWN; + } +@@ -129,6 +149,13 @@ static bool check_args(struct arguments *arguments) + log_error("The argument '--output ' requires a value\n"); + return false; + } ++ if (arguments->text_offset > UINT32_MAX) { ++ ERROR("Text section offset 0x%lx overflow", arguments->text_offset); ++ } ++ if ((arguments->text_offset & 0xFFF) != 0) { ++ ERROR("Text section offset 0x%lx is not 4K-aligned", ++ arguments->text_offset); ++ } + return true; + } + +@@ -141,6 +168,7 @@ static void show_program_info(struct arguments *arguments) + log_debug("patched object: %s\n", arguments->patched_obj); + log_debug("running binary: %s\n", arguments->running_elf); + log_debug("output object: %s\n", arguments->output_obj); ++ log_debug("text offset: 0x%lx\n", arguments->text_offset); + log_debug("------------------------------\n\n"); + } + +@@ -1033,7 +1061,7 @@ int main(int argc, char **argv) + + upatch_create_strings_elements(&uelf_out); + +- upatch_create_patches_sections(&uelf_out, &relf); ++ upatch_create_patches_sections(&uelf_out, &relf, args.text_offset); + + create_kpatch_arch_section(); + +diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c +index 964079d..eea1315 100644 +--- a/upatch-diff/elf-create.c ++++ b/upatch-diff/elf-create.c +@@ -138,7 +138,7 @@ void upatch_create_strings_elements(struct upatch_elf *uelf) + + /* create upatch func info section */ + void upatch_create_patches_sections(struct upatch_elf *uelf, +- struct running_elf *relf) ++ struct running_elf *relf, unsigned long text_offset) + { + struct symbol *sym; + struct symbol *strsym; +@@ -190,7 +190,11 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, + sym->name, symbol.symbol->name, symbol.sympos, symbol.symbol->size); + + /* ATTENTION: kpatch convert global symbols to local symbols here. */ +- funcs[index].old_addr = symbol.symbol->addr; ++ if (symbol.symbol->addr < text_offset) { ++ ERROR("Text section offset 0x%lx overflow, sym_addr=0x%lx", ++ text_offset, symbol.symbol->addr); ++ } ++ funcs[index].old_addr = symbol.symbol->addr - text_offset; + funcs[index].old_size = symbol.symbol->size; + funcs[index].new_size = sym->sym.st_size; + funcs[index].sympos = symbol.sympos; +diff --git a/upatch-diff/elf-create.h b/upatch-diff/elf-create.h +index 18778b5..0c76a46 100644 +--- a/upatch-diff/elf-create.h ++++ b/upatch-diff/elf-create.h +@@ -29,7 +29,8 @@ + + void upatch_create_strings_elements(struct upatch_elf *); + +-void upatch_create_patches_sections(struct upatch_elf *, struct running_elf *); ++void upatch_create_patches_sections(struct upatch_elf *, struct running_elf *, ++ unsigned long); + + static inline void create_kpatch_arch_section(void) {} + +-- +2.43.0 + diff --git a/0090-upatch-diff-remove-match_local_symbols.patch b/0090-upatch-diff-remove-match_local_symbols.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d87e4bb20e8934934680875ff6672662975856a --- /dev/null +++ b/0090-upatch-diff-remove-match_local_symbols.patch @@ -0,0 +1,212 @@ +From cc64287f73348ff40de10d4d547c53e0c73fc57a Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 12 May 2025 19:54:45 +0800 +Subject: [PATCH] upatch-diff: remove match_local_symbols() + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 181 ------------------------------- + 1 file changed, 181 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 1ffc9f5..b27351f 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -331,186 +331,6 @@ static void detect_child_functions(struct upatch_elf *uelf) + } + } + +-static bool is_discarded_sym(struct running_elf *relf, struct symbol *sym) +-{ +- if (!sym || !sym->sec || !sym->sec->name) { +- return false; +- } +- /* +- * ".gnu.warning." section is to prevent some symbols +- * in the dynamic library being used by external programs. +- * In the exec program, these sections are discarded in linker. +- * so we discard these symbols. +- */ +- if (relf->is_exec && +- !strncmp(sym->sec->name, ".gnu.warning.", strlen(".gnu.warning."))) { +- return true; +- } +- +- return false; +-} +- +-static bool has_function_prefix(const char *sym_name) +-{ +- static const char *SYM_NAMES[] = { +- "__func__.", +- "__FUNC__.", +- "__PRETTY_FUNCTION__.", +- NULL, +- }; +- +- const char **name; +- for (name = SYM_NAMES; *name; name++) { +- if (strcmp(sym_name, *name) == 0) { +- return true; +- } +- } +- return false; +-} +- +-static struct symbol* find_uelf_local_sym(struct upatch_elf *uelf, +- struct symbol *start_sym, struct relf_symbol *relf_sym) +-{ +- struct symbol *uelf_sym = start_sym; +- list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { +- if (uelf_sym->type == STT_FILE) { +- break; // find until next file +- } +- if (uelf_sym->bind != STB_LOCAL) { +- continue; +- } +- if ((uelf_sym->type != STT_FUNC) && (uelf_sym->type != STT_OBJECT)) { +- continue; +- } +- if ((uelf_sym->type != relf_sym->type) || +- (strcmp(uelf_sym->name, relf_sym->name) != 0)) { +- continue; +- } +- return uelf_sym; +- } +- +- return NULL; +-} +- +-static struct relf_symbol* find_relf_local_sym(struct running_elf *relf, +- struct relf_symbol *relf_file, struct symbol *uelf_sym) +-{ +- for (int i = relf_file->index + 1; i < relf->symbol_count; i++) { +- struct relf_symbol *relf_sym = &relf->symbols[i]; +- +- if (relf_sym->type == STT_FILE) { +- break; // find until next file +- } +- if (relf_sym->bind != STB_LOCAL) { +- continue; +- } +- if ((relf_sym->type != STT_FUNC) && (relf_sym->type != STT_OBJECT)) { +- continue; +- } +- if ((relf_sym->type != uelf_sym->type) || +- (strcmp(relf_sym->name, uelf_sym->name) != 0)) { +- continue; +- } +- return relf_sym; +- } +- +- return NULL; +-} +- +-static struct relf_symbol* find_relf_file_sym(struct running_elf *relf, +- struct symbol *file_sym) +-{ +- for (int i = 0; i < relf->symbol_count; i++) { +- struct relf_symbol *relf_sym = &relf->symbols[i]; +- +- if (relf_sym->type != STT_FILE) { +- continue; +- } +- if (strcmp(file_sym->name, relf_sym->name) == 0) { +- return relf_sym; +- } +- } +- return NULL; +-} +- +-static void match_file_local_symbols( +- struct upatch_elf *uelf, struct running_elf *relf, +- struct symbol *uelf_file, struct relf_symbol *relf_file) +-{ +- for (int i = relf_file->index + 1; i < relf->symbol_count; i++) { +- struct relf_symbol *sym = &relf->symbols[i]; +- +- if (sym->type == STT_FILE) { +- break; // match within current file +- } +- if ((sym->bind != STB_LOCAL) || +- ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { +- continue; +- } +- if (has_function_prefix(sym->name)) { +- continue; +- } +- +- struct symbol *matched = find_uelf_local_sym(uelf, uelf_file, sym); +- if (matched == NULL) { +- ERROR("Cannot find symbol '%s' in %s", sym->name, g_uelf_name); +- } +- } +- +- struct symbol *sym = uelf_file; +- list_for_each_entry_continue(sym, &uelf->symbols, list) { +- if (sym->type == STT_FILE) { +- break; // match within current file +- } +- if ((sym->bind != STB_LOCAL) || +- ((sym->type != STT_FUNC) && (sym->type != STT_OBJECT))) { +- continue; +- } +- if (is_subfunction_sym(sym)) { +- continue; +- } +- if (has_function_prefix(sym->name)) { +- continue; +- } +- if (is_discarded_sym(relf, sym)) { +- continue; +- } +- +- struct relf_symbol *matched = find_relf_local_sym(relf, relf_file, sym); +- if (matched == NULL) { +- ERROR("Cannot find symbol '%s' in %s", sym->name, g_relf_name); +- } +- if ((sym->relf_sym != NULL) && (sym->relf_sym != matched)) { +- ERROR("Found duplicated symbol '%s' in %s", sym->name, g_relf_name); +- } +- sym->relf_sym = matched; +- } +-} +- +-/* +- * Because there can be duplicate symbols in elf, we need correlate each symbol +- * from source elf to it's corresponding symbol in running elf. +- * Both the source elf and the running elf can be split on STT_FILE +- * symbols into blocks of symbols originating from a single source file. +- * We then compare local symbol lists from both blocks and store the pointer +- * to STT_FILE symbol in running elf for later using. +- */ +-static void match_local_symbols(struct upatch_elf *uelf, struct running_elf *relf) +-{ +- struct symbol *uelf_file = NULL; +- +- list_for_each_entry(uelf_file, &uelf->symbols, list) { +- if ((uelf_file->type != STT_FILE) || (uelf_file->status != CHANGED)) { +- continue; +- } +- struct relf_symbol *relf_file = find_relf_file_sym(relf, uelf_file); +- if (relf_file == NULL) { +- ERROR("Cannot find file '%s' in %s", uelf_file->name, g_relf_name); +- } +- match_file_local_symbols(uelf, relf, uelf_file, relf_file); +- } +-} +- + static void mark_file_symbols(struct upatch_elf *uelf) + { + struct symbol *curr_sym = NULL; +@@ -1040,7 +860,6 @@ int main(int argc, char **argv) + + upatch_compare_correlated_elements(&uelf_patched); + mark_file_symbols(&uelf_source); +- match_local_symbols(&uelf_source, &relf); + + include_standard_elements(&uelf_patched); + int change_count = include_changes(&uelf_patched); +-- +2.43.0 + diff --git a/0091-upatch-diff-rewrite-uelf-relf-struct.patch b/0091-upatch-diff-rewrite-uelf-relf-struct.patch new file mode 100644 index 0000000000000000000000000000000000000000..f636c9e4b17365cea294942390ad6a093093a0cc --- /dev/null +++ b/0091-upatch-diff-rewrite-uelf-relf-struct.patch @@ -0,0 +1,1192 @@ +From af7ea7faf9d9c7b25b54954634fbfe6bd875fca3 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 13 May 2025 10:02:00 +0800 +Subject: [PATCH] upatch-diff: rewrite uelf & relf struct + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 40 +++--- + upatch-diff/elf-common.c | 6 +- + upatch-diff/elf-common.h | 19 ++- + upatch-diff/elf-compare.c | 8 +- + upatch-diff/elf-compare.h | 2 +- + upatch-diff/elf-correlate.c | 22 ++- + upatch-diff/elf-correlate.h | 2 +- + upatch-diff/elf-create.c | 29 ++-- + upatch-diff/elf-create.h | 3 +- + upatch-diff/elf-debug.h | 2 +- + upatch-diff/elf-insn.c | 1 + + upatch-diff/elf-insn.h | 7 +- + upatch-diff/elf-resolve.c | 12 +- + upatch-diff/elf-resolve.h | 3 + + upatch-diff/list.h | 7 +- + upatch-diff/running-elf.c | 40 ++---- + upatch-diff/running-elf.h | 23 ++- + upatch-diff/upatch-elf.c | 232 +++++++++++++++---------------- + upatch-diff/upatch-elf.h | 138 +++++++++--------- + 19 files changed, 296 insertions(+), 300 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index b27351f..887f9a6 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -284,10 +284,10 @@ static void bundle_symbols(struct upatch_elf *uelf) + ERROR("Symbol '%s' at offset %lu of section '%s', expected 0.", + sym->name, sym->sym.st_value, sym->sec->name); + } +- sym->sec->sym = sym; ++ sym->sec->bundle_sym = sym; + /* except handler is also a kind of bundle symbol */ + } else if (sym->type == STT_SECTION && is_except_section(sym->sec)) { +- sym->sec->sym = sym; ++ sym->sec->bundle_sym = sym; + } + } + } +@@ -353,24 +353,22 @@ static void mark_file_symbols(struct upatch_elf *uelf) + static void mark_grouped_sections(struct upatch_elf *uelf) + { + struct section *groupsec; +- struct section *sec; +- unsigned int *data; +- unsigned int *end; +- + list_for_each_entry(groupsec, &uelf->sections, list) { + if (groupsec->sh.sh_type != SHT_GROUP) { + continue; + } +- data = groupsec->data->d_buf; +- end = groupsec->data->d_buf + groupsec->data->d_size; ++ ++ GElf_Word *data = groupsec->data->d_buf; ++ GElf_Word *end = groupsec->data->d_buf + groupsec->data->d_size; + data++; /* skip first flag word (e.g. GRP_COMDAT) */ ++ + while (data < end) { +- sec = find_section_by_index(&uelf->sections, *data); +- if (!sec) { +- ERROR("Group section not found"); ++ struct section *sec = find_section_by_index(&uelf->sections, (GElf_Section)*data); ++ if (sec == NULL) { ++ ERROR("Cannot find group section, index=%d", *data); + } + sec->grouped = true; +- log_debug("Marking section grouped, index: %d, name: '%s'\n", ++ log_debug("Marking grouped section, index: %d, name: '%s'\n", + sec->index, sec->name); + data++; + } +@@ -408,8 +406,8 @@ static void replace_section_syms(struct upatch_elf *uelf) + * rela->sym->sec->sym is the bundleable symbol which is + * a function or object. + */ +- if (rela->sym->sec->sym) { +- rela->sym = rela->sym->sec->sym; ++ if (rela->sym->sec->bundle_sym) { ++ rela->sym = rela->sym->sec->bundle_sym; + if (rela->sym->sym.st_value != 0) { + ERROR("Symbol offset is not zero."); + } +@@ -519,7 +517,7 @@ static void mark_ignored_sections(struct upatch_elf *uelf) + sec->base->name : sec->name; + if (strncmp(sec_name, ignored_name, name_len) == 0) { + sec->ignored = true; +- log_debug("Marking section ignored, index: %d, name: '%s'\n", ++ log_debug("Marking ignored section, index: %d, name: '%s'\n", + sec->index, sec->name); + break; + } +@@ -554,8 +552,8 @@ static void include_special_local_section(struct upatch_elf *uelf) { + sym->sec->data->d_buf = NULL; + sym->sec->data->d_size = 0; + // arm error: (.debug_info+0x...) undefined reference to `no symbol' +- if (sym->sec->secsym) { +- sym->sec->secsym->include = true; ++ if (sym->sec->sym) { ++ sym->sec->sym->include = true; + } + } + } +@@ -609,7 +607,7 @@ static void include_section(struct section *sec) + } + return; + } else { +- include_symbol(sec->secsym); ++ include_symbol(sec->sym); + include_section(sec->rela); + } + } +@@ -775,8 +773,8 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, + sec->index = 0; + + if (!is_rela_section(sec)) { +- if (sec->secsym && !sec->secsym->include) { +- sec->secsym = NULL; // break link to non-included section symbol ++ if (sec->sym && !sec->sym->include) { ++ sec->sym = NULL; // break link to non-included section symbol + } + } + } +@@ -790,7 +788,7 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, + list_del(&sym->list); + list_add_tail(&sym->list, &uelf_out->symbols); + sym->index = 0; +- sym->strip = SYMBOL_DEFAULT; ++ sym->strip = false; + + if (sym->sec && !sym->sec->include) { + sym->sec = NULL; // break link to non-included section +diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c +index 3636c01..c5d8f61 100644 +--- a/upatch-diff/elf-common.c ++++ b/upatch-diff/elf-common.c +@@ -77,11 +77,11 @@ bool is_special_static_symbol(struct symbol *sym) + + if (sym->type == STT_SECTION) { + /* make sure section is bundled */ +- if (is_rela_section(sym->sec) || (sym->sec->sym == NULL)) { ++ if (is_rela_section(sym->sec) || (sym->sec->bundle_sym == NULL)) { + return false; + } + /* use bundled object object/function symbol for matching */ +- sym = sym->sec->sym; ++ sym = sym->sec->bundle_sym; + } + + if ((sym->type != STT_OBJECT) || (sym->bind != STB_LOCAL)) { +@@ -109,7 +109,7 @@ bool is_special_static_symbol(struct symbol *sym) + bool is_special_static_section(struct section *sec) + { + struct symbol *sym = is_rela_section(sec) ? +- sec->base->secsym : sec->secsym; ++ sec->base->sym : sec->sym; + return is_special_static_symbol(sym); + } + +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index d3dbd2f..3cebd32 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -155,7 +155,7 @@ static inline bool is_symbol_ignored(struct symbol *sym) + return (sym->sec != NULL) && sym->sec->ignored; + } + +-static inline struct symbol *find_symbol_by_index(struct list_head *list, unsigned int index) ++static inline struct symbol *find_symbol_by_index(struct list_head *list, GElf_Word index) + { + struct symbol *sym; + +@@ -181,7 +181,7 @@ static inline struct symbol *find_symbol_by_name(struct list_head *list, const c + return NULL; + } + +-static inline struct section *find_section_by_index(struct list_head *list, unsigned int index) ++static inline struct section *find_section_by_index(struct list_head *list, GElf_Section index) + { + struct section *sec; + +@@ -207,6 +207,19 @@ static inline struct section *find_section_by_name(struct list_head *list, const + return NULL; + } + ++static inline struct section *find_section_by_type(struct list_head *list, GElf_Word type) ++{ ++ struct section *sec; ++ ++ list_for_each_entry(sec, list, list) { ++ if (sec->sh.sh_type == type) { ++ return sec; ++ } ++ } ++ ++ return NULL; ++} ++ + static inline bool has_digit_tail(char *tail) + { + if (*tail != '.') { +@@ -238,7 +251,7 @@ static inline char *section_function_name(struct section *sec) + if (is_rela_section(sec)) { + sec = sec->base; + } +- return sec->sym ? sec->sym->name : sec->name; ++ return sec->bundle_sym ? sec->bundle_sym->name : sec->name; + } + + static inline char *status_str(enum status status) +diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c +index 97daa0b..eb8bdc1 100644 +--- a/upatch-diff/elf-compare.c ++++ b/upatch-diff/elf-compare.c +@@ -196,12 +196,12 @@ static void update_section_status(struct section *sec, enum status status) + sec->twin->status = status; + } + if (is_rela_section(sec)) { +- if ((sec->base != NULL) && (sec->base->sym != NULL) && status != SAME) { +- sec->base->sym->status = status; ++ if ((sec->base != NULL) && (sec->base->bundle_sym != NULL) && status != SAME) { ++ sec->base->bundle_sym->status = status; + } + } else { +- if (sec->sym != NULL) { +- sec->sym->status = status; ++ if (sec->bundle_sym != NULL) { ++ sec->bundle_sym->status = status; + } + } + } +diff --git a/upatch-diff/elf-compare.h b/upatch-diff/elf-compare.h +index 8ec1655..5202fb0 100644 +--- a/upatch-diff/elf-compare.h ++++ b/upatch-diff/elf-compare.h +@@ -25,7 +25,7 @@ + #ifndef __UPATCH_ELF_COMPARE_H_ + #define __UPATCH_ELF_COMPARE_H_ + +-#include "upatch-elf.h" ++struct upatch_elf; + + void upatch_compare_symbols(struct upatch_elf *); + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index d5e76f6..dcf3cfa 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -37,10 +37,6 @@ static void correlate_symbol(struct symbol *sym_orig, + log_debug("renaming symbol %s to %s\n", + sym_patched->name, sym_orig->name); + sym_patched->name = sym_orig->name; +- sym_patched->name_source = DATA_SOURCE_REF; +- } +- if (sym_orig->relf_sym && !sym_patched->relf_sym) { +- sym_patched->relf_sym = sym_orig->relf_sym; + } + } + +@@ -133,12 +129,12 @@ static void correlate_section(struct section *sec_orig, + } else if (sec_orig->rela && sec_patched->rela) { + correlate_section_impl(sec_orig->rela, sec_patched->rela); + } +- if (sec_orig->secsym && sec_patched->secsym) { +- correlate_symbol(sec_orig->secsym, sec_patched->secsym); +- } +- if (sec_orig->sym) { ++ if (sec_orig->sym && sec_patched->sym) { + correlate_symbol(sec_orig->sym, sec_patched->sym); + } ++ if (sec_orig->bundle_sym) { ++ correlate_symbol(sec_orig->bundle_sym, sec_patched->bundle_sym); ++ } + } + + void upatch_correlate_sections(struct upatch_elf *uelf_source, +@@ -382,11 +378,11 @@ void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + uncorrelate_symbol(sym); + } + +- bundled = (sym == sym->sec->sym) ? 1 : 0; ++ bundled = (sym == sym->sec->bundle_sym) ? 1 : 0; + if (bundled && sym->sec->twin) { + uncorrelate_section(sym->sec); +- if (sym->sec->secsym) { +- uncorrelate_symbol(sym->sec->secsym); ++ if (sym->sec->sym) { ++ uncorrelate_symbol(sym->sec->sym); + } + if (sym->sec->rela) { + // uncorrelate relocation section which not equals to reference +@@ -418,7 +414,7 @@ void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + continue; + } + +- bundled = (sym == sym->sec->sym) ? 1 : 0; ++ bundled = (sym == sym->sec->bundle_sym) ? 1 : 0; + if (bundled && sym->sec == relasec->base) { + /* + * TODO: +@@ -439,7 +435,7 @@ void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + continue; + } + +- patched_bundled = (patched_sym == patched_sym->sec->sym) ? 1 : 0; ++ patched_bundled = (patched_sym == patched_sym->sec->bundle_sym) ? 1 : 0; + if (bundled != patched_bundled) { + ERROR("bundle mismatch for symbol %s", sym->name); + } +diff --git a/upatch-diff/elf-correlate.h b/upatch-diff/elf-correlate.h +index 820d07a..2e44302 100644 +--- a/upatch-diff/elf-correlate.h ++++ b/upatch-diff/elf-correlate.h +@@ -25,7 +25,7 @@ + #ifndef __UPATCH_ELF_CORRELATE_H_ + #define __UPATCH_ELF_CORRELATE_H_ + +-#include "upatch-elf.h" ++struct upatch_elf; + + void upatch_correlate_sections(struct upatch_elf *, struct upatch_elf *); + +diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c +index eea1315..c131029 100644 +--- a/upatch-diff/elf-create.c ++++ b/upatch-diff/elf-create.c +@@ -34,6 +34,8 @@ + #include "elf-create.h" + #include "upatch-patch.h" + #include "upatch-dynrela.h" ++#include "upatch-elf.h" ++#include "running-elf.h" + + /* create text and relocation sections */ + static struct section *create_section_pair(struct upatch_elf *uelf, char *name, +@@ -148,7 +150,6 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, + + struct upatch_patch_func *funcs; + struct rela *rela; +- struct lookup_result symbol; + + unsigned int nr = 0; + unsigned int index = 0; +@@ -180,24 +181,26 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, + if (sym->type != STT_FUNC || sym->status != CHANGED || sym->parent) { + continue; + } +- if (!lookup_relf(relf, sym, &symbol)) { ++ ++ struct relf_symbol *symbol = lookup_relf(relf, sym); ++ if (symbol == NULL) { + ERROR("Cannot find symbol '%s' in %s", sym->name, g_relf_name); + } +- if (sym->bind == STB_LOCAL && symbol.global) { ++ if ((sym->bind == STB_LOCAL) && (symbol->bind != STB_LOCAL)) { + ERROR("Cannot find local symbol '%s' in symbol table.", sym->name); + } +- log_debug("lookup for %s: symbol name %s sympos=%lu size=%lu.\n", +- sym->name, symbol.symbol->name, symbol.sympos, symbol.symbol->size); ++ log_debug("lookup for %s: symbol name %s sympos=%u size=%lu.\n", ++ sym->name, symbol->name, symbol->index, symbol->size); + + /* ATTENTION: kpatch convert global symbols to local symbols here. */ +- if (symbol.symbol->addr < text_offset) { ++ if (symbol->addr < text_offset) { + ERROR("Text section offset 0x%lx overflow, sym_addr=0x%lx", +- text_offset, symbol.symbol->addr); ++ text_offset, symbol->addr); + } +- funcs[index].old_addr = symbol.symbol->addr - text_offset; +- funcs[index].old_size = symbol.symbol->size; ++ funcs[index].old_addr = symbol->addr - text_offset; ++ funcs[index].old_size = symbol->size; + funcs[index].new_size = sym->sym.st_size; +- funcs[index].sympos = symbol.sympos; ++ funcs[index].sympos = symbol->index; + + log_debug("change func %s from 0x%lx.\n", + sym->name, funcs[index].old_addr); +@@ -300,7 +303,7 @@ void upatch_strip_unneeded_syms(struct upatch_elf *uelf) + struct symbol *sym, *sym_safe; + + list_for_each_entry_safe(sym, sym_safe, &uelf->symbols, list) { +- if (sym->strip == SYMBOL_STRIP) { ++ if (sym->strip) { + list_del(&sym->list); + free(sym); + } +@@ -311,7 +314,7 @@ void upatch_reindex_elements(struct upatch_elf *uelf) + { + struct section *sec; + struct symbol *sym; +- unsigned int index; ++ GElf_Section index; + + index = 1; + list_for_each_entry(sec, &uelf->sections, list) { +@@ -324,7 +327,7 @@ void upatch_reindex_elements(struct upatch_elf *uelf) + sym->index = index; + index++; + if (sym->sec) { +- sym->sym.st_shndx = (unsigned short)sym->sec->index; ++ sym->sym.st_shndx = sym->sec->index; + } else if (sym->sym.st_shndx != SHN_ABS) { + sym->sym.st_shndx = SHN_UNDEF; + } +diff --git a/upatch-diff/elf-create.h b/upatch-diff/elf-create.h +index 0c76a46..c0f4fa5 100644 +--- a/upatch-diff/elf-create.h ++++ b/upatch-diff/elf-create.h +@@ -25,7 +25,8 @@ + #ifndef __UPATCH_CREATE_H_ + #define __UPATCH_CREATE_H_ + +-#include "upatch-elf.h" ++struct upatch_elf; ++struct running_elf; + + void upatch_create_strings_elements(struct upatch_elf *); + +diff --git a/upatch-diff/elf-debug.h b/upatch-diff/elf-debug.h +index 09122f6..c196ed7 100644 +--- a/upatch-diff/elf-debug.h ++++ b/upatch-diff/elf-debug.h +@@ -25,7 +25,7 @@ + #ifndef __UPATCH_ELF_DEBUG_H_ + #define __UPATCH_ELF_DEBUG_H_ + +-#include "upatch-elf.h" ++struct upatch_elf; + + void upatch_print_correlation(struct upatch_elf *uelf); + void upatch_print_changes(struct upatch_elf *uelf); +diff --git a/upatch-diff/elf-insn.c b/upatch-diff/elf-insn.c +index 8fcfc39..6fabd88 100644 +--- a/upatch-diff/elf-insn.c ++++ b/upatch-diff/elf-insn.c +@@ -25,6 +25,7 @@ + + #include + ++#include "insn/asm/insn.h" + #include "elf-common.h" + #include "elf-insn.h" + +diff --git a/upatch-diff/elf-insn.h b/upatch-diff/elf-insn.h +index f64ebe1..ac48ad0 100644 +--- a/upatch-diff/elf-insn.h ++++ b/upatch-diff/elf-insn.h +@@ -26,8 +26,11 @@ + #ifndef __UPATCH_INSN_H_ + #define __UPATCH_INSN_H_ + +-#include "asm/insn.h" +-#include "upatch-elf.h" ++#include ++ ++struct section; ++struct rela; ++struct insn; + + #define ARM64_INSTR_LEN 4 + +diff --git a/upatch-diff/elf-resolve.c b/upatch-diff/elf-resolve.c +index 67ab069..2375b73 100755 +--- a/upatch-diff/elf-resolve.c ++++ b/upatch-diff/elf-resolve.c +@@ -20,9 +20,10 @@ + * 02110-1301, USA. + */ + +- ++#include + #include + ++#include "upatch-elf.h" + #include "running-elf.h" + #include "upatch-patch.h" + +@@ -30,16 +31,15 @@ + void upatch_partly_resolve(struct upatch_elf *uelf, struct running_elf *relf) + { + struct symbol *sym; +- struct lookup_result symbol; +- + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->sym.st_other & SYM_OTHER) { +- if (!lookup_relf(relf, sym, &symbol)) { ++ struct relf_symbol *symbol = lookup_relf(relf, sym); ++ if (symbol == NULL) { + continue; + } + /* keep it undefined for link purpose */ +- sym->sym.st_value = symbol.symbol->addr; +- sym->sym.st_size = symbol.symbol->size; ++ sym->sym.st_value = symbol->addr; ++ sym->sym.st_size = symbol->size; + } + } + } +diff --git a/upatch-diff/elf-resolve.h b/upatch-diff/elf-resolve.h +index 05b4b35..92a1012 100755 +--- a/upatch-diff/elf-resolve.h ++++ b/upatch-diff/elf-resolve.h +@@ -23,6 +23,9 @@ + #ifndef __UPATCH_RESOLVE_H_ + #define __UPATCH_RESOLVE_H_ + ++struct upatch_elf; ++struct running_elf; ++ + void upatch_partly_resolve(struct upatch_elf *, struct running_elf *); + + #endif /* __UPATCH_RESOLVE_H_ */ +diff --git a/upatch-diff/list.h b/upatch-diff/list.h +index b56a2b0..0b09108 100644 +--- a/upatch-diff/list.h ++++ b/upatch-diff/list.h +@@ -28,6 +28,8 @@ + #ifndef __UPATCH_LIST_H_ + #define __UPATCH_LIST_H_ + ++#include ++ + /* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses +@@ -36,11 +38,6 @@ + #define LIST_POISON1 ((void *) 0x00100100) + #define LIST_POISON2 ((void *) 0x00200200) + +-/** +- * Get offset of a member +- */ +-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +- + /** + * Casts a member of a structure out to the containing structure + * @param ptr the pointer to the member. +diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c +index db44cb4..90969e5 100644 +--- a/upatch-diff/running-elf.c ++++ b/upatch-diff/running-elf.c +@@ -32,6 +32,7 @@ + #include + #include + ++#include "upatch-elf.h" + #include "running-elf.h" + #include "log.h" + +@@ -75,14 +76,14 @@ void relf_open(struct running_elf *relf, const char *name) + if (!data) { + ERROR("Failed to read file '%s' section data, %s", name, elf_errmsg(0)); + } +- relf->symbol_count = (int)(shdr.sh_size / shdr.sh_entsize); ++ relf->symbol_count = (GElf_Word)(shdr.sh_size / shdr.sh_entsize); + relf->symbols = calloc((size_t)relf->symbol_count, sizeof(struct relf_symbol)); + if (!relf->symbols) { + ERROR("Failed to alloc memory, %s", strerror(errno)); + } + +- for (int i = 0; i < relf->symbol_count; i++) { +- if (!gelf_getsym(data, i, &sym)) { ++ for (GElf_Word i = 0; i < relf->symbol_count; i++) { ++ if (!gelf_getsym(data, (int)i, &sym)) { + ERROR("Failed to read file '%s' symbol, index=%d, %s", + name, i, elf_errmsg(0)); + } +@@ -119,37 +120,26 @@ void relf_close(struct running_elf *relf) + relf->fd = -1; + } + +-bool lookup_relf(struct running_elf *relf, struct symbol *lookup_sym, +- struct lookup_result *result) ++struct relf_symbol* lookup_relf(struct running_elf *relf, struct symbol *sym) + { +- struct relf_symbol *symbol = NULL; ++ struct relf_symbol *result = NULL; + +- log_debug("looking up symbol '%s'\n", lookup_sym->name); +- memset(result, 0, sizeof(*result)); ++ for (GElf_Word i = 0; i < relf->symbol_count; i++) { ++ struct relf_symbol *symbol = &relf->symbols[i]; + +- for (int i = 0; i < relf->symbol_count; i++) { +- symbol = &relf->symbols[i]; +- +- if (result->symbol != NULL && symbol->type == STT_FILE) { ++ if ((result != NULL) && (symbol->type == STT_FILE)) { + break; + } +- if (strcmp(symbol->name, lookup_sym->name) != 0 || +- symbol->bind != lookup_sym->bind) { ++ if ((strcmp(symbol->name, sym->name) != 0) || ++ (symbol->bind != sym->bind)) { + continue; + } +- +- if ((result->symbol != NULL) && +- (result->symbol->bind == symbol->bind)) { +- ERROR("Found duplicate symbol '%s' in %s", +- lookup_sym->name, g_relf_name); ++ if ((result != NULL) && (result->bind == symbol->bind)) { ++ ERROR("Found duplicate symbol '%s' in %s", sym->name, g_relf_name); + } + +- result->symbol = symbol; +- result->sympos = (unsigned long)i; +- result->global = +- ((symbol->bind == STB_GLOBAL) || (symbol->bind == STB_WEAK)); +- log_debug("found symbol '%s'\n", lookup_sym->name); ++ result = symbol; + } + +- return (result->symbol != NULL); ++ return result; + } +diff --git a/upatch-diff/running-elf.h b/upatch-diff/running-elf.h +index ae05d83..24e9326 100644 +--- a/upatch-diff/running-elf.h ++++ b/upatch-diff/running-elf.h +@@ -30,36 +30,29 @@ + #include + #include + +-#include "upatch-elf.h" +- + struct symbol; + + struct relf_symbol { +- int index; ++ GElf_Word index; + char *name; +- unsigned char type, bind; +- unsigned int shndx; +- unsigned long addr; +- unsigned long size; ++ unsigned char type; ++ unsigned char bind; ++ GElf_Section shndx; ++ GElf_Addr addr; ++ GElf_Xword size; + }; + + struct running_elf { + int fd; + Elf *elf; + struct relf_symbol *symbols; +- int symbol_count; ++ GElf_Word symbol_count; + bool is_exec; + }; + +-struct lookup_result { +- struct relf_symbol *symbol; +- unsigned long sympos; +- bool global; +-}; +- + void relf_open(struct running_elf *relf, const char *name); + void relf_close(struct running_elf *relf); + +-bool lookup_relf(struct running_elf *, struct symbol *, struct lookup_result *); ++struct relf_symbol* lookup_relf(struct running_elf *relf, struct symbol *sym); + + #endif +diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c +index 81567f5..c21b6ec 100644 +--- a/upatch-diff/upatch-elf.c ++++ b/upatch-diff/upatch-elf.c +@@ -40,180 +40,142 @@ + + static void create_section_list(struct upatch_elf *uelf) + { +- size_t shstrndx; +- size_t sections_nr; +- +- struct section *sec; +- Elf_Scn *scn = NULL; +- +- if (elf_getshdrnum(uelf->elf, §ions_nr)) { +- ERROR("elf_getshdrnum with error %s", elf_errmsg(0)); ++ size_t shstrndx = 0; ++ if (elf_getshdrstrndx(uelf->elf, &shstrndx) != 0) { ++ ERROR("Failed to get section header string index"); + } + +- sections_nr--; ++ Elf_Scn *scn = elf_nextscn(uelf->elf, NULL); ++ while (scn != NULL) { ++ GElf_Section index = (GElf_Section)elf_ndxscn(scn); ++ struct section *sec = NULL; + +- if (elf_getshdrstrndx(uelf->elf, &shstrndx)) { +- ERROR("elf_getshdrstrndx with error %s", elf_errmsg(0)); +- } +- +- while (sections_nr--) { + ALLOC_LINK(sec, &uelf->sections); +- +- scn = elf_nextscn(uelf->elf, scn); +- if (!scn) { +- ERROR("elf_nextscn with error %s", elf_errmsg(0)); +- } +- if (!gelf_getshdr(scn, &sec->sh)) { +- ERROR("gelf_getshdr with error %s", elf_errmsg(0)); ++ if (gelf_getshdr(scn, &sec->sh) == NULL) { ++ ERROR("Failed to parse section, index=%d", index); + } + ++ sec->index = (GElf_Section)index; + sec->name = elf_strptr(uelf->elf, shstrndx, sec->sh.sh_name); +- if (!sec->name) { +- ERROR("elf_strptr with error %s", elf_errmsg(0)); ++ if (sec->name == NULL) { ++ ERROR("Failed to get section name, index=%d", index); + } +- + sec->data = elf_getdata(scn, NULL); +- if (!sec->data) { +- ERROR("elf_getdata with error %s", elf_errmsg(0)); ++ if (sec->data == NULL) { ++ ERROR("Failed to get section '%s' data, index=%d", ++ sec->name, index); + } + +- sec->name_source = DATA_SOURCE_ELF; +- sec->data_source = DATA_SOURCE_ELF; +- sec->dbuf_source = DATA_SOURCE_ELF; ++ sec->name_source = DATA_SOURCE_REF; ++ sec->data_source = DATA_SOURCE_REF; ++ sec->dbuf_source = DATA_SOURCE_REF; + +- sec->index = (unsigned int)elf_ndxscn(scn); +- /* found extended section header */ +- if (sec->sh.sh_type == SHT_SYMTAB_SHNDX) { +- uelf->symtab_shndx = sec->data; /* correct ? */ +- } +- } ++ INIT_LIST_HEAD(&sec->relas); + +- if (elf_nextscn(uelf->elf, scn)) { +- ERROR("elf_nextscn with error %s", elf_errmsg(0)); ++ scn = elf_nextscn(uelf->elf, scn); + } + } + + static void create_symbol_list(struct upatch_elf *uelf) + { +- struct section *symtab; +- Elf32_Word shndx; +- +- unsigned int symbols_nr; +- unsigned int index = 0; +- struct symbol *sym; +- +- /* consider type first */ +- symtab = find_section_by_name(&uelf->sections, ".symtab"); +- if (!symtab) { +- ERROR("can't find symbol table"); ++ struct section *symtab = find_section_by_type(&uelf->sections, SHT_SYMTAB); ++ if (symtab == NULL) { ++ ERROR("Cannot find symbol table"); + } + +- symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); ++ GElf_Word count = (GElf_Word)(symtab->sh.sh_size / symtab->sh.sh_entsize); ++ for (GElf_Word i = 0; i < count; i++) { ++ struct symbol *sym = NULL; + +- while (symbols_nr--) { + ALLOC_LINK(sym, &uelf->symbols); +- INIT_LIST_HEAD(&sym->children); +- +- sym->index = index; +- if (!gelf_getsym(symtab->data, (int)index, &sym->sym)) { +- ERROR("gelf_getsym with error %s", elf_errmsg(0)); ++ if (gelf_getsym(symtab->data, (int)i, &sym->sym) == NULL) { ++ ERROR("Failed to parse symbol, index=%d", i); + } + +- index++; +- ++ sym->index = i; + sym->name = elf_strptr(uelf->elf, symtab->sh.sh_link, sym->sym.st_name); +- if (!sym->name) { +- ERROR("elf_strptr with error %s", elf_errmsg(0)); ++ if (sym->name == NULL) { ++ ERROR("Failed to get symbol name, index=%d", i); + } + +- sym->type = GELF_ST_TYPE(sym->sym.st_info); +- sym->bind = GELF_ST_BIND(sym->sym.st_info); ++ sym->name_source = DATA_SOURCE_REF; + +- shndx = sym->sym.st_shndx; +- /* releated section located in extended header */ +- if (shndx == SHN_XINDEX && +- !gelf_getsymshndx(symtab->data, uelf->symtab_shndx, +- (int)sym->index, &sym->sym, &shndx)) { +- ERROR("gelf_getsymshndx with error %s", elf_errmsg(0)); +- } +- if (sym->sym.st_shndx == SHN_XINDEX || +- (sym->sym.st_shndx > SHN_UNDEF && +- sym->sym.st_shndx < SHN_LORESERVE)) { ++ sym->bind = GELF_ST_BIND(sym->sym.st_info); ++ sym->type = GELF_ST_TYPE(sym->sym.st_info); ++ GElf_Section shndx = sym->sym.st_shndx; ++ if ((sym->sym.st_shndx > SHN_UNDEF) && ++ (sym->sym.st_shndx < SHN_LORESERVE)) { + sym->sec = find_section_by_index(&uelf->sections, shndx); +- if (!sym->sec) { +- ERROR("no releated section found for symbol %s\n", sym->name); ++ if (sym->sec == NULL) { ++ ERROR("Failed to find symbol '%s' section, index=%d, shndx=%d", ++ sym->name, i, shndx); + } +- +- /* this symbol is releated with a section */ + if (sym->type == STT_SECTION) { +- /* secsym must be the bundleable symbol */ +- sym->sec->secsym = sym; ++ /* sym must be the bundleable symbol */ ++ sym->sec->sym = sym; + /* use section name as symbol name */ + sym->name = sym->sec->name; + } + } ++ ++ INIT_LIST_HEAD(&sym->children); ++ INIT_LIST_HEAD(&sym->subfunction_node); + } + } + +-static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) ++static void create_rela_list(struct upatch_elf *uelf, struct section *sec) + { +- struct rela *rela; +- unsigned long rela_nr; +- +- unsigned int symndx; +- int index = 0; +- +- INIT_LIST_HEAD(&relasec->relas); +- + /* for relocation sections, sh_info is the index which these info apply */ +- relasec->base = find_section_by_index(&uelf->sections, relasec->sh.sh_info); +- if (!relasec->base) { +- ERROR("no base section found for relocation section %s", relasec->name); ++ sec->base = (struct section *)sec->info; ++ if (sec->base == NULL) { ++ ERROR("Cannot find section '%s' base section, index=%d", ++ sec->name, sec->index); + } +- relasec->base->rela = relasec; +- rela_nr = relasec->sh.sh_size / relasec->sh.sh_entsize; ++ sec->base->rela = sec; + +- while (rela_nr--) { +- ALLOC_LINK(rela, &relasec->relas); ++ GElf_Word count = (GElf_Word)(sec->sh.sh_size / sec->sh.sh_entsize); ++ for (GElf_Word i = 0; i < count; i++) { ++ struct rela *rela = NULL; + +- /* use index because we need to keep the order of rela */ +- if (!gelf_getrela(relasec->data, index, &rela->rela)) { +- ERROR("gelf_getrela with error %s", elf_errmsg(0)); ++ ALLOC_LINK(rela, &sec->relas); ++ if (gelf_getrela(sec->data, (int)i, &rela->rela) == NULL) { ++ ERROR("Failed to parse rela, index=%d", i); + } +- index++; + +- rela->type = GELF_R_TYPE(rela->rela.r_info); +- rela->addend = rela->rela.r_addend; +- rela->offset = (unsigned int)rela->rela.r_offset; +- symndx = (unsigned int)GELF_R_SYM(rela->rela.r_info); ++ GElf_Word symndx = (GElf_Word)GELF_R_SYM(rela->rela.r_info); + rela->sym = find_symbol_by_index(&uelf->symbols, symndx); +- if (!rela->sym) { +- ERROR("no rela entry symbol found\n"); ++ if (rela->sym == NULL) { ++ ERROR("Cannot find rela symbol, index=%d, symndx=%d", i, symndx); + } ++ rela->type = GELF_R_TYPE(rela->rela.r_info); ++ rela->addend = rela->rela.r_addend; ++ rela->offset = rela->rela.r_offset; ++ ++ if (is_string_section(rela->sym->sec)) { ++ void *data = rela->sym->sec->data->d_buf; ++ GElf_Addr addr = rela->sym->sym.st_value; ++ long offset = rela_target_offset(uelf, sec, rela); + +- if (rela->sym->sec && is_string_section(rela->sym->sec)) { +- rela->string = rela->sym->sec->data->d_buf + +- rela->sym->sym.st_value + +- rela_target_offset(uelf, relasec, rela); +- if (!rela->string) { +- ERROR("could not lookup rela string for %s+%ld", ++ rela->string = data + addr + offset; ++ if (rela->string == NULL) { ++ ERROR("Cannot find rela string %s+%ld", + rela->sym->name, rela->addend); + } + } + } + } + +-static void destroy_rela_list(struct section *relasec) ++static void destroy_rela_list(struct section *sec) + { + struct rela *rela = NULL; +- struct rela *saferela = NULL; ++ struct rela *safe = NULL; + +- list_for_each_entry_safe(rela, saferela, &relasec->relas, list) { ++ list_for_each_entry_safe(rela, safe, &sec->relas, list) { + list_del(&rela->list); + free(rela); + } + +- INIT_LIST_HEAD(&relasec->relas); ++ INIT_LIST_HEAD(&sec->relas); + } + + static void destroy_section_list(struct upatch_elf *uelf) +@@ -283,6 +245,41 @@ static void destroy_string_list(struct upatch_elf *uelf) + INIT_LIST_HEAD(&uelf->strings); + } + ++static void parse_section_metadata(struct upatch_elf *uelf) ++{ ++ struct section *sec; ++ list_for_each_entry(sec, &uelf->sections, list) { ++ /* find sh_link */ ++ if (sec->sh.sh_link != SHN_UNDEF) { ++ sec->link = find_section_by_index(&uelf->sections, ++ (GElf_Section)sec->sh.sh_link); ++ if (sec->link == NULL) { ++ ERROR("Cannot find '%s' link section, sh_link=%d", ++ sec->name, sec->sh.sh_link); ++ } ++ } ++ /* find sh_info */ ++ if ((sec->sh.sh_type == SHT_REL) || (sec->sh.sh_type == SHT_RELA)) { ++ sec->info = find_section_by_index(&uelf->sections, ++ (GElf_Section)sec->sh.sh_info); ++ if (sec->link == NULL) { ++ ERROR("Cannot find '%s' info section, sh_info=%d", ++ sec->name, sec->sh.sh_link); ++ } ++ } else if (sec->sh.sh_type == SHT_GROUP) { ++ sec->info = find_symbol_by_index(&uelf->symbols, sec->sh.sh_info); ++ if (sec->link == NULL) { ++ ERROR("Cannot find '%s' info symbol, sh_info=%d", ++ sec->name, sec->sh.sh_link); ++ } ++ } ++ /* handle rela section */ ++ if (sec->sh.sh_type == SHT_RELA) { ++ create_rela_list(uelf, sec); ++ } ++ } ++} ++ + void uelf_open(struct upatch_elf *uelf, const char *name) + { + GElf_Ehdr ehdr; +@@ -330,13 +327,7 @@ void uelf_open(struct upatch_elf *uelf, const char *name) + + create_section_list(uelf); + create_symbol_list(uelf); +- +- struct section *sec; +- list_for_each_entry(sec, &uelf->sections, list) { +- if (is_rela_section(sec)) { +- create_rela_list(uelf, sec); +- } +- } ++ parse_section_metadata(uelf); + } + + void uelf_close(struct upatch_elf *uelf) +@@ -356,5 +347,4 @@ void uelf_close(struct upatch_elf *uelf) + } + uelf->elf = NULL; + uelf->fd = -1; +- uelf->symtab_shndx = NULL; + } +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index eeb7fb2..858cfa9 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -26,11 +26,10 @@ + #ifndef __UPATCH_ELF_H_ + #define __UPATCH_ELF_H_ + +-#include + #include ++#include + + #include "list.h" +-#include "running-elf.h" + + extern char *g_relf_name; + +@@ -39,103 +38,112 @@ struct section; + struct rela; + struct symbol; + ++enum architecture { ++ X86_64 = 0x1 << 0, ++ AARCH64 = 0x1 << 1, ++}; ++ + enum data_source { +- DATA_SOURCE_ELF, + DATA_SOURCE_REF, + DATA_SOURCE_ALLOC, + }; + + enum status { +- NEW, ++ SAME, + CHANGED, +- SAME +-}; +- +-enum symbol_strip { +- SYMBOL_DEFAULT, +- SYMBOL_USED, +- SYMBOL_STRIP, ++ NEW, + }; + +-struct string { +- struct list_head list; +- char *name; ++struct upatch_elf { ++ int fd; ++ Elf *elf; ++ enum architecture arch; ++ struct list_head sections; ++ struct list_head symbols; ++ struct list_head strings; + }; + + struct section { + struct list_head list; +- struct section *twin; ++ GElf_Shdr sh; ++ ++ GElf_Section index; + char *name; + Elf_Data *data; ++ ++ /* data source */ + enum data_source name_source; + enum data_source data_source; + enum data_source dbuf_source; +- GElf_Shdr sh; +- bool ignored; +- bool include; +- bool grouped; +- unsigned int index; +- enum status status; +- union { +- // section with relocation information +- struct { +- struct section *base; +- struct list_head relas; +- }; +- // other function or data section +- struct { +- struct section *rela; +- struct symbol *sym; +- struct symbol *secsym; +- }; +- }; +-}; + +-struct rela { +- struct list_head list; +- GElf_Rela rela; ++ /* section info */ ++ struct section *link; ++ void *info; ++ ++ /* symbol reference */ + struct symbol *sym; +- unsigned int type; +- unsigned long offset; +- long addend; +- char *string; +- bool need_dynrela; ++ struct symbol *bundle_sym; ++ ++ /* reloc reference */ ++ struct section *base; ++ struct section *rela; ++ struct list_head relas; ++ ++ /* diff metadata */ ++ struct section *twin; ++ enum status status; ++ bool grouped; ++ bool ignored; ++ bool include; + }; + + struct symbol { + struct list_head list; +- struct symbol *twin; +- struct symbol *parent; +- struct list_head children; +- struct list_head subfunction_node; +- struct section *sec; + GElf_Sym sym; ++ ++ GElf_Word index; + char *name; ++ ++ /* data source */ + enum data_source name_source; +- struct relf_symbol *relf_sym; +- unsigned int index; ++ ++ /* symbol info */ + unsigned char bind; + unsigned char type; ++ ++ /* section reference */ ++ struct section *sec; ++ ++ /* subfunction reference */ ++ struct symbol *parent; ++ struct list_head children; ++ struct list_head subfunction_node; ++ ++ /* diff metadata */ ++ struct symbol *twin; + enum status status; +- union { +- bool include; /* used in the patched elf */ +- enum symbol_strip strip; /* used in the output elf */ +- }; ++ bool include; /* used in the patched elf */ ++ bool strip; /* used in the output elf */ + }; + +-enum architecture { +- X86_64 = 0x1 << 0, +- AARCH64 = 0x1 << 1, ++struct rela { ++ struct list_head list; ++ GElf_Rela rela; ++ ++ /* symbol reference */ ++ struct symbol *sym; ++ ++ /* rela info */ ++ GElf_Word type; ++ GElf_Off offset; ++ GElf_Sxword addend; ++ ++ char *string; + }; + +-struct upatch_elf { +- Elf *elf; +- enum architecture arch; +- struct list_head sections; +- struct list_head symbols; +- struct list_head strings; +- Elf_Data *symtab_shndx; +- int fd; ++struct string { ++ struct list_head list; ++ char *name; + }; + + void uelf_open(struct upatch_elf *uelf, const char *name); +-- +2.43.0 + diff --git a/0092-upatch-diff-fix-access-freed-section-data-issue.patch b/0092-upatch-diff-fix-access-freed-section-data-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..227610f8166b0b99ed3d44d30b674cd8605c58dc --- /dev/null +++ b/0092-upatch-diff-fix-access-freed-section-data-issue.patch @@ -0,0 +1,34 @@ +From feb2f1ba319cd6fb4d54bea622cb49b0f032014e Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 14 May 2025 15:02:23 +0800 +Subject: [PATCH] upatch-diff: fix access freed section data issue + + Some sections in output uelf may reference other +uelf section, which would be closed at very last. + This would cause accessing freed section data while +closing output uelf. We need to close it at first. + +Signed-off-by: renoseven +--- + upatch-diff/create-diff-object.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 887f9a6..9e4504e 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -909,9 +909,9 @@ int main(int argc, char **argv) + upatch_write_output_elf(&uelf_out, uelf_patched.elf, args.output_obj, 0664); + log_normal("Done\n"); + +- uelf_close(&uelf_source); +- uelf_close(&uelf_patched); + uelf_close(&uelf_out); ++ uelf_close(&uelf_patched); ++ uelf_close(&uelf_source); + relf_close(&relf); + + fflush(stdout); +-- +2.43.0 + diff --git a/0093-syscared-fix-kernel-compatibility-check-is-not-worki.patch b/0093-syscared-fix-kernel-compatibility-check-is-not-worki.patch new file mode 100644 index 0000000000000000000000000000000000000000..75a351c1c6b7e2cf8a0ae4e072178b98de1425d7 --- /dev/null +++ b/0093-syscared-fix-kernel-compatibility-check-is-not-worki.patch @@ -0,0 +1,26 @@ +From 234a6f213bd9faeac7c4b5a94b449d87b5af9c2f Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sun, 27 Apr 2025 19:40:06 +0800 +Subject: [PATCH] syscared: fix kernel compatibility check is not working issue + +Signed-off-by: renoseven +--- + syscared/src/patch/driver/kpatch/mod.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/syscared/src/patch/driver/kpatch/mod.rs b/syscared/src/patch/driver/kpatch/mod.rs +index bdcdd4c..ba177ac 100644 +--- a/syscared/src/patch/driver/kpatch/mod.rs ++++ b/syscared/src/patch/driver/kpatch/mod.rs +@@ -134,7 +134,7 @@ impl KernelPatchDriver { + debug!("Patch target: '{}'", patch_target); + debug!("Current kernel: '{}'", current_kernel.to_string_lossy()); + +- if !patch_target.starts_with("KERNEL_NAME_PREFIX") { ++ if !patch_target.starts_with(KERNEL_NAME_PREFIX) { + return Ok(()); + } + ensure!( +-- +2.43.0 + diff --git a/0094-syscare-build-add-debug-option-to-kpatch-build.patch b/0094-syscare-build-add-debug-option-to-kpatch-build.patch new file mode 100644 index 0000000000000000000000000000000000000000..620fbf63dc2aa913c05636a5536fc12d375fb409 --- /dev/null +++ b/0094-syscare-build-add-debug-option-to-kpatch-build.patch @@ -0,0 +1,43 @@ +From f6fed7d3a7163c79634c3e9c9921657a987176e8 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 15 May 2025 10:10:09 +0800 +Subject: [PATCH] syscare-build: add "--debug" option to kpatch-build + +Signed-off-by: renoseven +--- + syscare-build/src/patch/kernel_patch/kpatch_builder.rs | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +index 07d4a85..202611d 100644 +--- a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs ++++ b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +@@ -56,6 +56,7 @@ struct KBuildParameters { + patch_files: Vec, + jobs: usize, + skip_compiler_check: bool, ++ verbose: bool, + } + + struct KernelPatchEntity { +@@ -159,6 +160,7 @@ impl KernelPatchBuilder { + patch_files: build_params.patch_files.to_owned(), + jobs: build_params.jobs, + skip_compiler_check: build_params.skip_compiler_check, ++ verbose: build_params.verbose, + }) + } + +@@ -257,6 +259,9 @@ impl KernelPatchBuilder { + if kbuild_params.skip_compiler_check { + cmd_args.arg("--skip-compiler-check"); + } ++ if kbuild_params.verbose { ++ cmd_args.arg("--debug"); ++ } + cmd_args.arg("--skip-cleanup"); + + cmd_args +-- +2.43.0 + diff --git a/0095-Fix-invalid-url-for-syscare-build-readme.patch b/0095-Fix-invalid-url-for-syscare-build-readme.patch new file mode 100644 index 0000000000000000000000000000000000000000..5a0acbdd4cf5b2bb8c2538e45b05812bb4d63994 --- /dev/null +++ b/0095-Fix-invalid-url-for-syscare-build-readme.patch @@ -0,0 +1,43 @@ +From 9616a50288c23336dfcb41c75fadc2b31099960a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=E8=99=AB=E5=84=BF=E9=A3=9E?= +Date: Wed, 19 Mar 2025 01:59:06 +0000 +Subject: [PATCH] Fix invalid url for syscare-build readme +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: 虫儿飞 +--- + README.en.md | 2 +- + README.md | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/README.en.md b/README.en.md +index d004fa8..66734a5 100644 +--- a/README.en.md ++++ b/README.en.md +@@ -72,7 +72,7 @@ $ syscare build \ + --patch ./0001-test.patch + ``` + +-For detailed instructions on patch production, please see builder/README.md ++For detailed instructions on patch production, please see syscare-build/README.md + + ### Patch management + +diff --git a/README.md b/README.md +index 15d4d48..57f2a4e 100644 +--- a/README.md ++++ b/README.md +@@ -93,7 +93,7 @@ $ syscare build \ + --patch ./0001-test.patch + ``` + +-补丁制作详细使用说明请见[builder/README.md](https://gitee.com/openeuler/syscare/blob/master/builder/README.md) ++补丁制作详细使用说明请见[syscare-build/README.md](https://gitee.com/openeuler/syscare/blob/master/syscare-build/README.md) + + + +-- +2.43.0 + diff --git a/0096-doc-sync-master-document.patch b/0096-doc-sync-master-document.patch new file mode 100644 index 0000000000000000000000000000000000000000..1eef171cefa90b6b02e63ec5c8fbbed7550de8ca --- /dev/null +++ b/0096-doc-sync-master-document.patch @@ -0,0 +1,50 @@ +From e2d5945f7565cb22cd5464a7f4ac78ff90330067 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 15 May 2025 19:25:56 +0800 +Subject: [PATCH] doc: sync master document + +Signed-off-by: renoseven +--- + README.md | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/README.md b/README.md +index 57f2a4e..b740ace 100644 +--- a/README.md ++++ b/README.md +@@ -33,6 +33,8 @@ + + * 编译并安装 + ++ PS: 直接编译在应用补丁的时候会显示缺少依赖,建议通过rpm包安装 ++ + ```bash + git clone https://gitee.com/openeuler/syscare.git + cd syscare +@@ -41,6 +43,11 @@ + cmake -DCMAKE_INSTALL_PREFIX=/usr -DKERNEL_VERSION=$(uname -r) .. + make + make install ++ ++ mkdir -p /usr/lib/syscare/patches ++ systemctl daemon-reload ++ systemctl enable syscare ++ systemctl start syscare + ``` + + * 离线编译 +@@ -63,7 +70,11 @@ + ```bash + rpm -ivh syscare-*.rpm + ``` ++或者 + ++``` ++dnf install syscare-*.rpm ++``` + + + ## 使用说明 +-- +2.43.0 + diff --git a/0097-common-rewrite-fs-glob.patch b/0097-common-rewrite-fs-glob.patch new file mode 100644 index 0000000000000000000000000000000000000000..ffcba5a4f1aea474407744de84d5ae03778a4b24 --- /dev/null +++ b/0097-common-rewrite-fs-glob.patch @@ -0,0 +1,319 @@ +From 2197658b737e221043971909ec22559bea7332f6 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 18 Apr 2025 19:14:27 +0800 +Subject: [PATCH] common: rewrite fs::glob + +1. remove fs_impl::glob() +2. add fs::glob::{Glob, glob} + +Signed-off-by: renoseven +--- + syscare-common/src/fs/fs_impl.rs | 102 ------------------- + syscare-common/src/fs/glob.rs | 166 +++++++++++++++++++++++++++++++ + syscare-common/src/fs/mod.rs | 2 + + 3 files changed, 168 insertions(+), 102 deletions(-) + create mode 100644 syscare-common/src/fs/glob.rs + +diff --git a/syscare-common/src/fs/fs_impl.rs b/syscare-common/src/fs/fs_impl.rs +index 58f97c3..f5393f8 100644 +--- a/syscare-common/src/fs/fs_impl.rs ++++ b/syscare-common/src/fs/fs_impl.rs +@@ -565,108 +565,6 @@ pub fn copy_dir_contents, Q: AsRef>(src_dir: P, dst_dir: Q) + Ok(()) + } + +-pub fn glob>(path: P) -> io::Result> { +- const CURRENT_DIR: Component = Component::CurDir; +- const WILDCARD_ONE: char = '?'; +- const WILDCARD_ALL: char = '*'; +- const WILDCARD_RECURSIVE: &str = "**"; +- +- fn match_name(name: &OsStr, pattern: &OsStr) -> bool { +- let name: Vec<_> = name.chars().collect(); +- let pattern: Vec<_> = pattern.chars().collect(); +- +- let (mut name_idx, mut patt_idx, mut star_idx, mut match_idx) = (0, 0, None, 0); +- +- while name_idx < name.len() { +- if patt_idx < pattern.len() +- && (pattern[patt_idx] == WILDCARD_ONE || pattern[patt_idx] == name[name_idx]) +- { +- name_idx += 1; +- patt_idx += 1; +- } else if patt_idx < pattern.len() && pattern[patt_idx] == WILDCARD_ALL { +- star_idx = Some(patt_idx); +- match_idx = name_idx; +- patt_idx += 1; +- } else if let Some(star) = star_idx { +- patt_idx = star + 1; +- match_idx += 1; +- name_idx = match_idx; +- } else { +- return false; +- } +- } +- +- while patt_idx < pattern.len() && pattern[patt_idx] == WILDCARD_ALL { +- patt_idx += 1; +- } +- +- patt_idx == pattern.len() +- } +- +- let pattern = path.as_ref(); +- let base_dir = if pattern.is_relative() && !pattern.starts_with(CURRENT_DIR.as_os_str()) { +- PathBuf::from(CURRENT_DIR.as_os_str()) +- } else { +- PathBuf::new() +- }; +- let components = pattern.components().collect::>(); +- +- let mut result = Vec::new(); +- let mut stack = vec![(base_dir, 0)]; +- +- while let Some((mut curr_dir, mut comp_idx)) = stack.pop() { +- let comp_len = components.len(); +- while comp_idx < comp_len { +- if let Component::Normal(pattern) = components[comp_idx] { +- if pattern == WILDCARD_RECURSIVE { +- if let Ok(read_dir) = self::read_dir(&curr_dir) { +- stack.push((curr_dir.clone(), comp_idx + 1)); +- for dir_entry in read_dir.flatten() { +- let next_path = dir_entry.path(); +- if next_path.is_dir() { +- stack.push((next_path.clone(), comp_idx)); +- } +- if comp_idx + 1 == comp_len { +- result.push(next_path); +- continue; +- } +- stack.push((next_path, comp_idx + 1)); +- } +- } +- break; +- } else if pattern.contains(WILDCARD_ONE) || pattern.contains(WILDCARD_ALL) { +- if let Ok(read_dir) = self::read_dir(&curr_dir) { +- for dir_entry in read_dir.flatten() { +- let next_path = dir_entry.path(); +- let file_name = next_path.file_name().unwrap_or_default(); +- if !match_name(file_name, pattern) { +- continue; +- } +- if comp_idx + 1 == comp_len { +- result.push(next_path); +- continue; +- } +- if next_path.is_dir() { +- stack.push((next_path, comp_idx + 1)); +- } +- } +- } +- break; +- } +- } +- +- curr_dir.push(components[comp_idx]); +- if curr_dir.exists() && (comp_idx + 1 == comp_len) { +- result.push(curr_dir.clone()); +- break; +- } +- comp_idx += 1; +- } +- } +- +- Ok(result) +-} +- + pub fn sync() { + nix::unistd::sync() + } +diff --git a/syscare-common/src/fs/glob.rs b/syscare-common/src/fs/glob.rs +new file mode 100644 +index 0000000..9f4027f +--- /dev/null ++++ b/syscare-common/src/fs/glob.rs +@@ -0,0 +1,166 @@ ++use std::{ ++ ffi::{OsStr, OsString}, ++ fs, ++ io::Result, ++ path::{Path, PathBuf}, ++}; ++ ++use crate::{ffi::OsStrExt, os_str::CharByte}; ++ ++const WILDCARD_ONE: char = '?'; ++const WILDCARD_ALL: char = '*'; ++const WILDCARD_RECURSIVE: &str = "**"; ++ ++pub struct Glob { ++ components: Vec, ++ stack: Vec<(PathBuf, usize)>, // current path, component index ++} ++ ++impl Glob { ++ fn match_chars(name: I, mut pattern: P) -> bool ++ where ++ I: Iterator, ++ P: Iterator, ++ { ++ for current in name { ++ match pattern.next() { ++ Some(matching) => { ++ if matching == WILDCARD_ONE { ++ continue; // matched one char ++ } ++ if matching == WILDCARD_ALL { ++ return true; // matched all chars ++ } ++ if current != matching { ++ return false; // not matched ++ } ++ } ++ None => return false, // pattern not enough ++ } ++ } ++ ++ true ++ } ++ ++ fn match_component_name(name: &OsStr, component: &OsStr) -> bool { ++ if !Self::match_chars(name.chars(), component.chars()) { ++ return false; ++ } ++ // If pattern contains "*", we have to do reverse match to ++ // make sure tail chars were also matched to the pattern. ++ if component.contains(WILDCARD_ALL) { ++ return Self::match_chars(name.chars().rev(), component.chars().rev()); ++ } ++ ++ true ++ } ++ ++ fn match_component(&mut self, path: PathBuf, index: usize) -> Result> { ++ let last_index = self.components.len() - 1; ++ ++ for dir_entry in fs::read_dir(&path)? { ++ let next_path = dir_entry?.path(); ++ let next_name = next_path.file_name().unwrap_or_default(); ++ ++ let component = self.components[index].as_os_str(); ++ if !Self::match_component_name(next_name, component) { ++ continue; // not matched, skip ++ } ++ ++ if index == last_index { ++ return Ok(Some(next_path)); ++ } ++ ++ if next_path.is_dir() { ++ self.stack.push((next_path, index + 1)); ++ } ++ } ++ ++ Ok(None) ++ } ++ ++ fn match_recursive_wildcard(&mut self, path: PathBuf, index: usize) -> Result<()> { ++ // push files & directories in current directory to stack ++ for dir_entry in fs::read_dir(&path)? { ++ let next_path = dir_entry?.path(); ++ if next_path.is_dir() { ++ self.stack.push((next_path, index)); // recursive match, keep index ++ } ++ } ++ // push current path back to stack, match next component ++ self.stack.push((path, index + 1)); ++ ++ Ok(()) ++ } ++ ++ fn match_multiple_wildcard(&mut self, path: PathBuf, index: usize) -> Result<()> { ++ // push files & directories in current directory to stack ++ for dir_entry in fs::read_dir(&path)? { ++ let next_path = dir_entry?.path(); ++ if next_path.is_dir() { ++ self.stack.push((next_path, index + 1)); ++ } ++ } ++ // push current path back to stack, match next component ++ self.stack.push((path, index + 1)); ++ ++ Ok(()) ++ } ++} ++ ++impl Iterator for Glob { ++ type Item = Result; ++ ++ fn next(&mut self) -> Option { ++ while let Some((mut curr_path, mut index)) = self.stack.pop() { ++ let last_index = self.components.len() - 1; ++ ++ // iterate all of components over matching path ++ while index <= last_index { ++ let component = self.components[index].as_os_str(); ++ ++ if component == WILDCARD_RECURSIVE { ++ if let Err(e) = self.match_recursive_wildcard(curr_path, index) { ++ return Some(Err(e)); ++ } ++ } else if component == OsStr::new(&WILDCARD_ALL.to_string()) { ++ if let Err(e) = self.match_multiple_wildcard(curr_path, index) { ++ return Some(Err(e)); ++ } ++ } else if component.contains(WILDCARD_ONE) || component.contains(WILDCARD_ALL) { ++ let result = self.match_component(curr_path, index).transpose(); ++ if result.is_some() { ++ return result; ++ } ++ } else { ++ // normal component, push to current path ++ curr_path.push(component); ++ ++ if (index == last_index) && curr_path.exists() { ++ return Some(Ok(curr_path.clone())); ++ } ++ ++ index += 1; ++ continue; ++ } ++ ++ break; ++ } ++ } ++ ++ None ++ } ++} ++ ++pub fn glob>(path: P) -> Glob { ++ let match_dir = path.as_ref().to_path_buf(); ++ let matching = match_dir ++ .components() ++ .map(|c| c.as_os_str().to_os_string()) ++ .collect::>(); ++ ++ Glob { ++ components: matching, ++ stack: vec![(match_dir, 0)], ++ } ++} +diff --git a/syscare-common/src/fs/mod.rs b/syscare-common/src/fs/mod.rs +index 6091455..a5e43cd 100644 +--- a/syscare-common/src/fs/mod.rs ++++ b/syscare-common/src/fs/mod.rs +@@ -14,8 +14,10 @@ + + mod flock; + mod fs_impl; ++mod glob; + mod mapped_file; + + pub use flock::*; + pub use fs_impl::*; ++pub use glob::*; + pub use mapped_file::*; +-- +2.43.0 + diff --git a/0098-common-rewrite-fs-components.patch b/0098-common-rewrite-fs-components.patch new file mode 100644 index 0000000000000000000000000000000000000000..4ae5447f1dff00fb9f62b458e41927d3e97f14f3 --- /dev/null +++ b/0098-common-rewrite-fs-components.patch @@ -0,0 +1,488 @@ +From 774d52917b516c2b8b88d196d5b56d19bdfc5d39 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 21 Apr 2025 19:35:29 +0800 +Subject: [PATCH] common: rewrite fs components + +FileLock: +1. change fs::FileLock::new() from private fn +2. change fs::FileLock::new() retval + from anyhow::Result to std::io::Result +3. remove fs::FileLock member: path, kind +4. impl std::ops::{Deref, DerefMut} + for fs::FileLock +5. impl fs::flock() + +FileMmap: +1. rename fs::mapped_file -> fs::mmap +2. rename fs::MappedFile to fs::FileMmap +3. rename fs::FileMmap::open() to FileMmap::new() +4. change fs::FileMmap::new() to private fn +5. change fs::FileMmap::new() retval + from anyhow::Result to std::io::Result +6. remove fs::FileMmap::as_bytes() +7. impl std::ops::Deref for fs::FileMmap +8. impl fs::mmap() + +Glob: +1. impl fs::Glob::new() + +Signed-off-by: renoseven +--- + syscare-common/src/fs/flock.rs | 133 +++++++++++++-------------- + syscare-common/src/fs/glob.rs | 24 +++-- + syscare-common/src/fs/mapped_file.rs | 109 ---------------------- + syscare-common/src/fs/mmap.rs | 77 ++++++++++++++++ + syscare-common/src/fs/mod.rs | 4 +- + 5 files changed, 156 insertions(+), 191 deletions(-) + delete mode 100644 syscare-common/src/fs/mapped_file.rs + create mode 100644 syscare-common/src/fs/mmap.rs + +diff --git a/syscare-common/src/fs/flock.rs b/syscare-common/src/fs/flock.rs +index 29bff55..a1ca6ca 100644 +--- a/syscare-common/src/fs/flock.rs ++++ b/syscare-common/src/fs/flock.rs +@@ -14,11 +14,12 @@ + + use std::{ + fs::File, +- os::unix::io::{AsRawFd, RawFd}, +- path::{Path, PathBuf}, ++ io::Result, ++ ops::{Deref, DerefMut}, ++ os::unix::io::AsRawFd, ++ path::Path, + }; + +-use anyhow::{anyhow, Context, Result}; + use nix::fcntl; + + #[derive(Debug)] +@@ -31,112 +32,104 @@ pub enum FileLockType { + + #[derive(Debug)] + pub struct FileLock { +- path: PathBuf, + file: File, +- kind: FileLockType, + } + + impl FileLock { +- pub fn new>(path: P, kind: FileLockType) -> Result { +- let file_path = path.as_ref(); +- +- let file = if file_path.is_file() { +- File::open(file_path) +- .map_err(|e| anyhow!("Failed to open lock file {}, {}", file_path.display(), e))? +- } else { +- File::create(file_path) +- .map_err(|e| anyhow!("Failed to create lock file {}, {}", file_path.display(), e))? +- }; +- ++ fn new>(file_path: P, kind: FileLockType) -> Result { ++ let file_path = file_path.as_ref(); + let flock = Self { +- path: file_path.to_path_buf(), +- file, +- kind, ++ file: if file_path.exists() { ++ File::open(file_path)? ++ } else { ++ File::create(file_path)? ++ }, + }; +- flock.acquire_lock()?; ++ flock.acquire(kind)?; + + Ok(flock) + } +-} + +-impl FileLock { + #[inline] +- fn acquire_lock(&self) -> Result<()> { ++ fn acquire(&self, kind: FileLockType) -> Result<()> { + let fd = self.file.as_raw_fd(); +- let arg = match self.kind { ++ let arg = match kind { + FileLockType::Shared => fcntl::FlockArg::LockShared, + FileLockType::Exclusive => fcntl::FlockArg::LockExclusive, + FileLockType::SharedNonBlock => fcntl::FlockArg::LockSharedNonblock, + FileLockType::ExclusiveNonBlock => fcntl::FlockArg::LockExclusiveNonblock, + }; +- fcntl::flock(fd, arg) +- .with_context(|| format!("Failed to acquire flock on {}", self.path.display())) ++ fcntl::flock(fd, arg)?; ++ ++ Ok(()) + } + + #[inline] +- fn release_lock(&self) -> Result<()> { ++ fn release(&self) { + let fd = self.file.as_raw_fd(); + let arg = fcntl::FlockArg::Unlock; +- fcntl::flock(fd, arg) +- .with_context(|| format!("Failed to release flock on {}", self.path.display())) ++ fcntl::flock(fd, arg).expect("Failed to release file lock"); + } + } + +-impl AsRawFd for FileLock { +- fn as_raw_fd(&self) -> RawFd { +- self.file.as_raw_fd() ++impl Deref for FileLock { ++ type Target = File; ++ ++ fn deref(&self) -> &Self::Target { ++ &self.file ++ } ++} ++ ++impl DerefMut for FileLock { ++ fn deref_mut(&mut self) -> &mut Self::Target { ++ &mut self.file + } + } + + impl Drop for FileLock { + fn drop(&mut self) { +- self.release_lock().ok(); ++ self.release(); + } + } + +-#[test] +-fn test() { +- use std::fs; +- +- const EXIST_FILE: &str = "/etc/os-release"; +- const NON_EXIST_FILE: &str = "/tmp/flock_test"; ++pub fn flock>(file_path: P, kind: FileLockType) -> Result { ++ FileLock::new(file_path, kind) ++} + +- println!("Testing exclusive flock on {}...", NON_EXIST_FILE); +- fs::remove_file(NON_EXIST_FILE).ok(); ++#[test] ++fn test() -> anyhow::Result<()> { ++ use anyhow::{ensure, Context}; + +- let exclusive_lock = FileLock::new(NON_EXIST_FILE, FileLockType::ExclusiveNonBlock) +- .expect("Failed to create exclusive flock"); +- drop(exclusive_lock); ++ use std::fs; + +- println!("Testing shared flock on {}...", NON_EXIST_FILE); +- fs::remove_file(NON_EXIST_FILE).ok(); +- let shared_lock = FileLock::new(NON_EXIST_FILE, FileLockType::SharedNonBlock) +- .expect("Failed to create shared flock"); ++ let file_path = std::env::temp_dir().join("flock_test"); ++ fs::remove_file(&file_path)?; ++ ++ println!("Testing shared flock on {}...", file_path.display()); ++ let shared_lock = self::flock(&file_path, FileLockType::SharedNonBlock) ++ .context("Failed to create shared flock")?; ++ let shared_lock1 = self::flock(&file_path, FileLockType::SharedNonBlock) ++ .context("Failed to create shared flock")?; ++ ensure!( ++ self::flock(&file_path, FileLockType::ExclusiveNonBlock).is_err(), ++ "Exclusive flock should be failed" ++ ); + drop(shared_lock); ++ drop(shared_lock1); + +- fs::remove_file(NON_EXIST_FILE).ok(); +- +- println!("Testing exclusive flock on {}...", EXIST_FILE); +- let exclusive_lock = FileLock::new(EXIST_FILE, FileLockType::ExclusiveNonBlock) +- .expect("Failed to create exclusive flock"); +- let _exclusive_err = FileLock::new(EXIST_FILE, FileLockType::ExclusiveNonBlock) +- .expect_err("Exclusive flock should be failed"); +- let _shared_err = FileLock::new(EXIST_FILE, FileLockType::SharedNonBlock) +- .expect_err("Shared flock should be failed"); ++ println!("Testing exclusive flock on {}...", file_path.display()); ++ let exclusive_lock = self::flock(&file_path, FileLockType::ExclusiveNonBlock) ++ .context("Failed to create exclusive flock")?; ++ ensure!( ++ self::flock(&file_path, FileLockType::SharedNonBlock).is_err(), ++ "Shared flock should be failed" ++ ); ++ ensure!( ++ self::flock(&file_path, FileLockType::ExclusiveNonBlock).is_err(), ++ "Exclusive flock should be failed" ++ ); + + drop(exclusive_lock); + +- println!("Testing shared flock on {}...", EXIST_FILE); +- let shared_lock1 = FileLock::new(EXIST_FILE, FileLockType::SharedNonBlock) +- .expect("Failed to create shared flock"); +- let shared_lock2 = FileLock::new(EXIST_FILE, FileLockType::SharedNonBlock) +- .expect("Failed to create shared flock"); +- let _exclusive_err = FileLock::new(EXIST_FILE, FileLockType::ExclusiveNonBlock) +- .expect_err("Exclusive flock should be failed"); +- let shared_lock3 = FileLock::new(EXIST_FILE, FileLockType::SharedNonBlock) +- .expect("Failed to create shared flock"); +- +- drop(shared_lock1); +- drop(shared_lock2); +- drop(shared_lock3); ++ Ok(()) + } +diff --git a/syscare-common/src/fs/glob.rs b/syscare-common/src/fs/glob.rs +index 9f4027f..8165343 100644 +--- a/syscare-common/src/fs/glob.rs ++++ b/syscare-common/src/fs/glob.rs +@@ -17,6 +17,19 @@ pub struct Glob { + } + + impl Glob { ++ fn new>(path: P) -> Self { ++ let match_dir = path.as_ref().to_path_buf(); ++ let matching = match_dir ++ .components() ++ .map(|c| c.as_os_str().to_os_string()) ++ .collect::>(); ++ ++ Glob { ++ components: matching, ++ stack: vec![(match_dir, 0)], ++ } ++ } ++ + fn match_chars(name: I, mut pattern: P) -> bool + where + I: Iterator, +@@ -153,14 +166,5 @@ impl Iterator for Glob { + } + + pub fn glob>(path: P) -> Glob { +- let match_dir = path.as_ref().to_path_buf(); +- let matching = match_dir +- .components() +- .map(|c| c.as_os_str().to_os_string()) +- .collect::>(); +- +- Glob { +- components: matching, +- stack: vec![(match_dir, 0)], +- } ++ Glob::new(path) + } +diff --git a/syscare-common/src/fs/mapped_file.rs b/syscare-common/src/fs/mapped_file.rs +deleted file mode 100644 +index d05e307..0000000 +--- a/syscare-common/src/fs/mapped_file.rs ++++ /dev/null +@@ -1,109 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ +- io::{BufRead, Cursor, Read}, +- os::unix::io::AsRawFd, +- path::Path, +-}; +- +-use anyhow::{ensure, Context, Result}; +-use memmap2::{Mmap, MmapOptions}; +- +-use super::flock::{FileLock, FileLockType}; +- +-#[derive(Debug)] +-pub struct MappedFile { +- _flock: FileLock, +- cursor: Cursor, +-} +- +-impl MappedFile { +- pub fn open>(path: P) -> Result { +- let file_path = path.as_ref(); +- ensure!( +- !file_path.starts_with("/proc"), +- "Mmap does not support procfs" +- ); +- +- let flock = FileLock::new(file_path, FileLockType::Shared)?; +- /* +- * SAFETY: +- * All file-backed memory map constructors are marked unsafe because of the +- * potential for Undefined Behavior (UB) using the map if the underlying file +- * is subsequently modified, in or out of process. +- * Our implementation uses shared file lock to avoid cross-process file access. +- * This mapped area would be safe. +- */ +- let mmap = unsafe { +- MmapOptions::new() +- .map(flock.as_raw_fd()) +- .with_context(|| format!("Failed to mmap file {}", path.as_ref().display()))? +- }; +- +- let cursor = Cursor::new(mmap); +- Ok(Self { +- _flock: flock, +- cursor, +- }) +- } +- +- pub fn as_bytes(&self) -> &[u8] { +- self.cursor.get_ref() +- } +-} +- +-impl Read for MappedFile { +- fn read(&mut self, buf: &mut [u8]) -> std::io::Result { +- self.cursor.read(buf) +- } +-} +- +-impl BufRead for MappedFile { +- fn fill_buf(&mut self) -> std::io::Result<&[u8]> { +- self.cursor.fill_buf() +- } +- +- fn consume(&mut self, amt: usize) { +- self.cursor.consume(amt) +- } +-} +- +-#[test] +-fn test() { +- use std::{fs::File, io::Read}; +- +- const PROC_FS_PATH: &str = "/proc/version"; +- const FILE_PATH: &str = "/etc/os-release"; +- +- println!("Testing MappedFile..."); +- let mut file_buf = vec![]; +- let mut mapped_buf = vec![]; +- +- let mut normal_file = File::open(FILE_PATH).expect("Failed to open normal file"); +- normal_file +- .read_to_end(&mut file_buf) +- .expect("Failed to read normal file"); +- +- let mut mapped_file = MappedFile::open(FILE_PATH).expect("Failed to open mapped file"); +- mapped_file +- .read_to_end(&mut mapped_buf) +- .expect("Failed to read mapped file"); +- +- let _map_procfs_err = +- MappedFile::open(PROC_FS_PATH).expect_err("Procfs should not be supported"); +- +- assert_eq!(file_buf, mapped_buf); +- assert_eq!(mapped_buf, mapped_file.as_bytes()); +-} +diff --git a/syscare-common/src/fs/mmap.rs b/syscare-common/src/fs/mmap.rs +new file mode 100644 +index 0000000..b65f170 +--- /dev/null ++++ b/syscare-common/src/fs/mmap.rs +@@ -0,0 +1,77 @@ ++// SPDX-License-Identifier: Mulan PSL v2 ++/* ++ * Copyright (c) 2024 Huawei Technologies Co., Ltd. ++ * syscare-common is licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ */ ++ ++use std::{io::Result, ops::Deref, os::fd::AsRawFd, path::Path}; ++ ++use memmap2::{Advice, Mmap, MmapOptions}; ++ ++use super::flock::*; ++ ++#[derive(Debug)] ++pub struct FileMmap { ++ _lock: FileLock, ++ mmap: Mmap, ++} ++ ++impl FileMmap { ++ fn new>(file_path: P) -> Result { ++ /* ++ * SAFETY: ++ * All file-backed memory map constructors are marked unsafe because of the ++ * potential for Undefined Behavior (UB) using the map if the underlying file ++ * is subsequently modified, in or out of process. ++ * Our implementation uses shared file lock to avoid cross-process file access. ++ * This mapped area would be safe. ++ */ ++ let lock = flock(file_path, FileLockType::Shared)?; ++ let mmap = unsafe { MmapOptions::new().map(lock.as_raw_fd())? }; ++ mmap.advise(Advice::Random)?; ++ ++ Ok(Self { _lock: lock, mmap }) ++ } ++} ++ ++impl Deref for FileMmap { ++ type Target = [u8]; ++ ++ fn deref(&self) -> &Self::Target { ++ &self.mmap ++ } ++} ++ ++pub fn mmap>(file_path: P) -> std::io::Result { ++ FileMmap::new(file_path) ++} ++ ++#[test] ++fn test() -> anyhow::Result<()> { ++ use anyhow::Context; ++ ++ const FILE_PATH: &str = "/etc/os-release"; ++ const SYS_FS_PATH: &str = "/sys/kernel/vmcoreinfo"; ++ const PROC_FS_PATH: &str = "/proc/version"; ++ ++ println!("Testing FileMmap..."); ++ let orig_file = ++ std::fs::read(FILE_PATH).with_context(|| format!("Failed to open file {}", FILE_PATH))?; ++ let map_file = ++ self::mmap(FILE_PATH).with_context(|| format!("Failed to mmap file {}", FILE_PATH))?; ++ ++ let _ = self::mmap(SYS_FS_PATH).expect_err("Sysfs cannot not be mapped"); ++ let _ = self::mmap(PROC_FS_PATH).expect_err("Procfs cannot not be mapped"); ++ ++ assert_eq!(orig_file, map_file.as_ref()); ++ ++ Ok(()) ++} +diff --git a/syscare-common/src/fs/mod.rs b/syscare-common/src/fs/mod.rs +index a5e43cd..3526915 100644 +--- a/syscare-common/src/fs/mod.rs ++++ b/syscare-common/src/fs/mod.rs +@@ -15,9 +15,9 @@ + mod flock; + mod fs_impl; + mod glob; +-mod mapped_file; ++mod mmap; + + pub use flock::*; + pub use fs_impl::*; + pub use glob::*; +-pub use mapped_file::*; ++pub use mmap::*; +-- +2.43.0 + diff --git a/0099-syscare-adapt-common-fs-changes.patch b/0099-syscare-adapt-common-fs-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..9dedc6e38aa371edb7c07ffd2b710ee7fc80387d --- /dev/null +++ b/0099-syscare-adapt-common-fs-changes.patch @@ -0,0 +1,45 @@ +From 3a38a683eb0137e3348a35ab35bac5459bcd75d4 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 21 Apr 2025 20:00:59 +0800 +Subject: [PATCH] syscare: adapt common::fs changes + +Signed-off-by: renoseven +--- + syscare/src/rpc/client.rs | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/syscare/src/rpc/client.rs b/syscare/src/rpc/client.rs +index 3428984..e24227a 100644 +--- a/syscare/src/rpc/client.rs ++++ b/syscare/src/rpc/client.rs +@@ -17,7 +17,7 @@ use std::{ + path::{Path, PathBuf}, + }; + +-use anyhow::{anyhow, ensure, Error, Result}; ++use anyhow::{anyhow, ensure, Context, Error, Result}; + use jsonrpc::{ + serde_json::value::RawValue, + simple_uds::{self, UdsTransport}, +@@ -26,7 +26,7 @@ use jsonrpc::{ + use log::debug; + use serde::{Deserialize, Serialize}; + +-use syscare_common::fs::{FileLock, FileLockType}; ++use syscare_common::fs::{self, FileLock}; + + #[derive(Debug, Default)] + pub struct RpcArguments { +@@ -80,7 +80,8 @@ impl RpcClient { + } + + pub fn lock(&self) -> Result { +- FileLock::new(&self.lock, FileLockType::Exclusive) ++ fs::flock(&self.lock, fs::FileLockType::Exclusive) ++ .with_context(|| format!("Failed to lock {}", self.lock.display())) + } + + pub fn call(&self, cmd: &str) -> Result +-- +2.43.0 + diff --git a/0100-syscared-adapt-common-fs-changes.patch b/0100-syscared-adapt-common-fs-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..4ff8918b9518707c10240076b29d13c9048f28de --- /dev/null +++ b/0100-syscared-adapt-common-fs-changes.patch @@ -0,0 +1,48 @@ +From c0b9d8e0d415758a72743500c7d90af00e719730 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 21 Apr 2025 20:02:16 +0800 +Subject: [PATCH] syscared: adapt common::fs changes + +Signed-off-by: renoseven +--- + syscared/src/patch/resolver/kpatch.rs | 6 +++--- + syscared/src/patch/resolver/upatch.rs | 6 +++--- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/syscared/src/patch/resolver/kpatch.rs b/syscared/src/patch/resolver/kpatch.rs +index 4ec35f4..f8885ff 100644 +--- a/syscared/src/patch/resolver/kpatch.rs ++++ b/syscared/src/patch/resolver/kpatch.rs +@@ -112,9 +112,9 @@ pub struct KpatchResolverImpl; + impl KpatchResolverImpl { + #[inline] + fn resolve_patch_file(patch: &mut KernelPatch) -> Result<()> { +- let patch_file = +- fs::MappedFile::open(&patch.patch_file).context("Failed to map patch file")?; +- let patch_elf = NativeFile::parse(patch_file.as_bytes()).context("Invalid patch format")?; ++ let patch_file = fs::mmap(&patch.patch_file) ++ .with_context(|| format!("Failed to mmap file {}", patch.patch_file.display()))?; ++ let patch_elf = NativeFile::parse(patch_file.as_ref()).context("Invalid patch format")?; + + // Read sections + let function_section = patch_elf +diff --git a/syscared/src/patch/resolver/upatch.rs b/syscared/src/patch/resolver/upatch.rs +index e49dff9..a5e4ad0 100644 +--- a/syscared/src/patch/resolver/upatch.rs ++++ b/syscared/src/patch/resolver/upatch.rs +@@ -93,9 +93,9 @@ pub struct UpatchResolverImpl; + impl UpatchResolverImpl { + #[inline] + fn resolve_patch_elf(patch: &mut UserPatch) -> Result<()> { +- let patch_file = +- fs::MappedFile::open(&patch.patch_file).context("Failed to map patch file")?; +- let patch_elf = NativeFile::parse(patch_file.as_bytes()).context("Invalid patch format")?; ++ let patch_file = fs::mmap(&patch.patch_file) ++ .with_context(|| format!("Failed to mmap file {}", patch.patch_file.display()))?; ++ let patch_elf = NativeFile::parse(patch_file.as_ref()).context("Invalid patch format")?; + + // Read sections + let function_section = patch_elf +-- +2.43.0 + diff --git a/0101-upatch-helper-optimize-compiler-flags.patch b/0101-upatch-helper-optimize-compiler-flags.patch new file mode 100644 index 0000000000000000000000000000000000000000..5e0c4304aa5b7744d1283b51211e05c6b28baa04 --- /dev/null +++ b/0101-upatch-helper-optimize-compiler-flags.patch @@ -0,0 +1,49 @@ +From 48918131470019e3918b27921c0b1bf388c29c23 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 23 Apr 2025 12:01:28 +0800 +Subject: [PATCH] upatch-helper: optimize compiler flags + +Signed-off-by: renoseven +--- + upatch-helper/src/main.rs | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs +index 303088c..782776f 100644 +--- a/upatch-helper/src/main.rs ++++ b/upatch-helper/src/main.rs +@@ -19,14 +19,17 @@ use uuid::Uuid; + + const UPATCH_CC_ENV: &str = "UPATCH_HELPER_CC"; + const UPATCH_CXX_ENV: &str = "UPATCH_HELPER_CXX"; ++const UPATCH_ID_PREFIX: &str = ".upatch_"; + + const OUTPUT_FLAG: &str = "-o"; +-const APPEND_ARGS: [&str; 3] = [ +- "-gdwarf", /* obtain debug information */ +- "-ffunction-sections", +- "-fdata-sections", ++ ++const APPEND_FLAGS: &[&str] = &[ ++ "-gdwarf", // generate dwarf debuginfo ++ "-ffunction-sections", // generate corresponding section for each function ++ "-fdata-sections", // generate corresponding section for each data ++ "-fmerge-constants", // merge constants with same value into one ++ "-fno-common", // avoid generating common block for uninitialized global variables + ]; +-const UPATCH_ID_PREFIX: &str = ".upatch_"; + + fn main() -> anyhow::Result<()> { + let exec_args = std::env::args_os().collect::>(); +@@ -48,7 +51,7 @@ fn main() -> anyhow::Result<()> { + + command.args(exec_args.iter().skip(1)); + if exec_args.iter().any(|arg| arg == OUTPUT_FLAG) { +- command.args(APPEND_ARGS); ++ command.args(APPEND_FLAGS); + command.arg(format!( + "-Wa,--defsym,{}{}=0", + UPATCH_ID_PREFIX, +-- +2.43.0 + diff --git a/0102-upatch-helper-support-clang-compiler.patch b/0102-upatch-helper-support-clang-compiler.patch new file mode 100644 index 0000000000000000000000000000000000000000..9fe9225aed4cd0e7b6e1c9c6b9350ddffdbc2112 --- /dev/null +++ b/0102-upatch-helper-support-clang-compiler.patch @@ -0,0 +1,163 @@ +From d8fa52663748d66d2e798b6c025eb3d8b115f3ca Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 23 Apr 2025 15:05:17 +0800 +Subject: [PATCH] upatch-helper: support clang compiler + +Signed-off-by: renoseven +--- + upatch-helper/src/main.rs | 124 +++++++++++++++++++++++++++----------- + 1 file changed, 88 insertions(+), 36 deletions(-) + +diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs +index 782776f..cd83bc6 100644 +--- a/upatch-helper/src/main.rs ++++ b/upatch-helper/src/main.rs +@@ -12,57 +12,109 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{os::unix::process::CommandExt, path::Path, process::Command}; ++use std::{ ++ ffi::{OsStr, OsString}, ++ os::unix::{ffi::OsStrExt, process::CommandExt}, ++ path::{Path, PathBuf}, ++ process::Command, ++}; + +-use anyhow::{bail, Context}; ++use anyhow::{bail, Context, Result}; + use uuid::Uuid; + +-const UPATCH_CC_ENV: &str = "UPATCH_HELPER_CC"; +-const UPATCH_CXX_ENV: &str = "UPATCH_HELPER_CXX"; +-const UPATCH_ID_PREFIX: &str = ".upatch_"; ++const COMPILER_KEYWORDS_CC: &[&str] = &["cc", "clang"]; ++const COMPILER_KEYWORDS_CXX: &[&str] = &["++", "xx"]; + +-const OUTPUT_FLAG: &str = "-o"; ++const HELPER_ENV_NAME_CC: &str = "UPATCH_HELPER_CC"; ++const HELPER_ENV_NAME_CXX: &str = "UPATCH_HELPER_CXX"; ++const HELPER_ENV_NAMES: &[(&[&str], &str)] = &[ ++ (COMPILER_KEYWORDS_CC, HELPER_ENV_NAME_CC), ++ (COMPILER_KEYWORDS_CXX, HELPER_ENV_NAME_CXX), ++]; + +-const APPEND_FLAGS: &[&str] = &[ +- "-gdwarf", // generate dwarf debuginfo +- "-ffunction-sections", // generate corresponding section for each function +- "-fdata-sections", // generate corresponding section for each data +- "-fmerge-constants", // merge constants with same value into one +- "-fno-common", // avoid generating common block for uninitialized global variables ++const COMPILE_FLAG_NAME: &str = "-c"; ++const COMPILE_OPTIONS_GNU: &[&str] = &[ ++ "-gdwarf", // generate dwarf debuginfo ++ "-ffunction-sections", // generate corresponding section for each function ++ "-fdata-sections", // generate corresponding section for each data ++ "-fmerge-constants", // merge constants with same value into one ++ "-fno-common", // avoid generating common block for uninitialized global variables ++ "-fno-tree-slp-vectorize", // avoid converting scalar operations into SIMD instructions + ]; ++const COMPILE_OPTIONS_CLANG: &[&str] = &[ ++ "-gdwarf", ++ "-ffunction-sections", ++ "-fdata-sections", ++ "-fno-common", ++ "-fno-slp-vectorize", // avoid converting scalar operations into SIMD instructions ++ "-fno-integrated-as", // avoid using built-in llvm-as, which doesn't support "--defsym" ++]; ++ ++const UPATCH_ID_PREFIX: &str = ".upatch_"; ++ ++#[inline(always)] ++fn is_compilation(args: &[OsString]) -> bool { ++ args.iter().any(|arg| arg == COMPILE_FLAG_NAME) ++} ++ ++#[inline(always)] ++fn find_compiler(arg0: &OsStr) -> Result { ++ let file_name = Path::new(arg0).file_name().unwrap_or_default(); + +-fn main() -> anyhow::Result<()> { +- let exec_args = std::env::args_os().collect::>(); +- let exec_name = Path::new(&exec_args[0]) ++ // match compiler by file name ++ let env_entry = HELPER_ENV_NAMES.iter().find(|(keys, _)| { ++ keys.iter().any(|str| { ++ let key_bytes = str.as_bytes(); ++ file_name ++ .as_bytes() ++ .windows(key_bytes.len()) ++ .any(|window| window == key_bytes) ++ }) ++ }); ++ if let Some((_, env_name)) = env_entry { ++ return std::env::var_os(env_name) ++ .map(PathBuf::from) ++ .with_context(|| format!("Environment variable '{}' was not set", env_name)); ++ } ++ ++ bail!("No compiler found"); ++} ++ ++#[inline(always)] ++fn add_compile_options(command: &mut Command) { ++ let prog_name = Path::new(command.get_program()) + .file_name() +- .context("Cannot parse exec name")? +- .to_string_lossy(); +- let exec_path = if exec_name.contains("cc") { +- std::env::var_os(UPATCH_CC_ENV) +- .with_context(|| format!("Environment variable '{}' is not set", UPATCH_CC_ENV))? +- } else if exec_name.contains("++") { +- std::env::var_os(UPATCH_CXX_ENV) +- .with_context(|| format!("Environment variable '{}' is not set", UPATCH_CXX_ENV))? +- } else { +- bail!("Invalid exec name '{}'", exec_name); ++ .unwrap_or_default(); ++ let clang_name_bytes = COMPILER_KEYWORDS_CC[1].as_bytes(); ++ let is_clang = prog_name ++ .as_bytes() ++ .windows(clang_name_bytes.len()) ++ .any(|window| window == clang_name_bytes); ++ ++ let compiler_args = match is_clang { ++ true => COMPILE_OPTIONS_CLANG, ++ false => COMPILE_OPTIONS_GNU, + }; ++ let assembler_arg = format!("-Wa,--defsym,{}{}=0", UPATCH_ID_PREFIX, Uuid::new_v4()); ++ ++ command.args(compiler_args); ++ command.arg(assembler_arg); ++} + +- let mut command = Command::new(&exec_path); ++fn main() -> Result<()> { ++ let args: Vec<_> = std::env::args_os().collect(); ++ let compiler = self::find_compiler(&args[0])?; + +- command.args(exec_args.iter().skip(1)); +- if exec_args.iter().any(|arg| arg == OUTPUT_FLAG) { +- command.args(APPEND_FLAGS); +- command.arg(format!( +- "-Wa,--defsym,{}{}=0", +- UPATCH_ID_PREFIX, +- Uuid::new_v4(), +- )); ++ let mut command = Command::new(&compiler); ++ command.args(&args[1..]); ++ if self::is_compilation(&args) { ++ self::add_compile_options(&mut command); + } + + let err = command.exec(); + bail!( +- "Exec '{}' failed, {}", +- exec_path.to_string_lossy(), ++ "Failed to execute '{}', {}", ++ compiler.display(), + err.to_string().to_lowercase() + ); + } +-- +2.43.0 + diff --git a/0103-upatch-helper-support-cross-compilation.patch b/0103-upatch-helper-support-cross-compilation.patch new file mode 100644 index 0000000000000000000000000000000000000000..2dff7b0aad97b0386355aabfd5f51fcfb8a541f0 --- /dev/null +++ b/0103-upatch-helper-support-cross-compilation.patch @@ -0,0 +1,40 @@ +From 506f4ce996902f110f0b82e5a77e77251b5920fd Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 23 Apr 2025 18:09:04 +0800 +Subject: [PATCH] upatch-helper: support cross compilation + +Signed-off-by: renoseven +--- + upatch-helper/src/main.rs | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs +index cd83bc6..9676bef 100644 +--- a/upatch-helper/src/main.rs ++++ b/upatch-helper/src/main.rs +@@ -77,6 +77,22 @@ fn find_compiler(arg0: &OsStr) -> Result { + .with_context(|| format!("Environment variable '{}' was not set", env_name)); + } + ++ // exec name matched, read environment variable directly ++ let exec_path = std::env::current_exe()?; ++ let exec_name = exec_path.file_name().unwrap_or_default(); ++ if exec_name == file_name { ++ return HELPER_ENV_NAMES ++ .iter() ++ .rev() ++ .find_map(|&(_, env_name)| std::env::var_os(env_name).map(PathBuf::from)) ++ .with_context(|| { ++ format!( ++ "Environment variables '{}' and '{}' were not set", ++ HELPER_ENV_NAME_CC, HELPER_ENV_NAME_CXX ++ ) ++ }); ++ } ++ + bail!("No compiler found"); + } + +-- +2.43.0 + diff --git a/0104-upatch-helper-add-Werror-uninitialized-for-clang.patch b/0104-upatch-helper-add-Werror-uninitialized-for-clang.patch new file mode 100644 index 0000000000000000000000000000000000000000..688fbb7e174a7d266c8f47d36990221a1568a191 --- /dev/null +++ b/0104-upatch-helper-add-Werror-uninitialized-for-clang.patch @@ -0,0 +1,25 @@ +From 82b76a766f3cbab144c8bc8e7ab61e67b92ada06 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 11:38:33 +0800 +Subject: [PATCH] upatch-helper: add --Werror=uninitialized for clang + +Signed-off-by: renoseven +--- + upatch-helper/src/main.rs | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs +index 9676bef..3992367 100644 +--- a/upatch-helper/src/main.rs ++++ b/upatch-helper/src/main.rs +@@ -48,6 +48,7 @@ const COMPILE_OPTIONS_CLANG: &[&str] = &[ + "-fno-common", + "-fno-slp-vectorize", // avoid converting scalar operations into SIMD instructions + "-fno-integrated-as", // avoid using built-in llvm-as, which doesn't support "--defsym" ++ "-Werror=uninitialized", // uninitialized variable may generate unexpected code under O2 optimization + ]; + + const UPATCH_ID_PREFIX: &str = ".upatch_"; +-- +2.43.0 + diff --git a/0105-upatch-build-execute-clean-before-first-time-buildin.patch b/0105-upatch-build-execute-clean-before-first-time-buildin.patch new file mode 100644 index 0000000000000000000000000000000000000000..a1e6ca11417d7f89aed0444ee698e6087dd13533 --- /dev/null +++ b/0105-upatch-build-execute-clean-before-first-time-buildin.patch @@ -0,0 +1,122 @@ +From 293508085f6cd28faed6d1181652b385ac49c48c Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 3 Apr 2025 16:20:20 +0800 +Subject: [PATCH] upatch-build: execute clean before first-time building + +1. set '--clean-cmd' default value to empty +2. execute clean command before first building +3. check '--prepare-cmd' existence before executing prepare command +4. check '--clean-cmd' existence before executing clean command + +Signed-off-by: renoseven +--- + upatch-build/src/args.rs | 13 +++++-------- + upatch-build/src/main.rs | 34 ++++++++++++++++++++++++++-------- + 2 files changed, 31 insertions(+), 16 deletions(-) + +diff --git a/upatch-build/src/args.rs b/upatch-build/src/args.rs +index 5871f82..f840095 100644 +--- a/upatch-build/src/args.rs ++++ b/upatch-build/src/args.rs +@@ -23,8 +23,8 @@ use which::which; + use super::{CLI_ABOUT, CLI_NAME, CLI_VERSION}; + + const DEFAULT_EMPTY_VALUE: &str = ""; +-const DEFAULT_SOURCE_EXT: [&str; 8] = ["h", "hpp", "hxx", "c", "cpp", "cxx", "in", "inc"]; +-const DEFAULT_COMPILERS: [&str; 2] = ["gcc", "g++"]; ++const DEFAULT_SOURCE_EXTS: &[&str] = &["h", "hpp", "hxx", "c", "cpp", "cxx", "in", "inc"]; ++const DEFAULT_COMPILERS: &[&str] = &["gcc", "g++"]; + const DEFAULT_BUILD_ROOT: &str = "."; + const DEFAULT_OUTPUT_DIR: &str = "."; + +@@ -52,7 +52,7 @@ pub struct Arguments { + pub source_dir: PathBuf, + + /// Source file extension(s) +- #[clap(long, multiple = true, default_values = &DEFAULT_SOURCE_EXT)] ++ #[clap(long, multiple = true, default_values = &DEFAULT_SOURCE_EXTS)] + pub source_ext: Vec, + + /// Build compiler(s) +@@ -60,14 +60,14 @@ pub struct Arguments { + pub compiler: Vec, + + /// Build prepare command +- #[clap(long, default_value = DEFAULT_EMPTY_VALUE)] ++ #[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)] + pub prepare_cmd: OsString, + + /// Build command + #[clap(short('c'), long)] + pub build_cmd: OsString, + +- /// Build clean command [default: ] ++ /// Build clean command + #[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)] + pub clean_cmd: OsString, + +@@ -139,9 +139,6 @@ impl Arguments { + if self.binary_dir.as_os_str().is_empty() { + self.binary_dir = self.source_dir.clone(); + } +- if self.clean_cmd.is_empty() { +- self.clean_cmd = self.prepare_cmd.clone(); +- } + + self + } +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 51956d8..73d286f 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -276,10 +276,19 @@ impl UpatchBuild { + self.check_debuginfo().context("Debuginfo check failed")?; + } + +- info!("Preparing '{}'", project); +- project +- .prepare() +- .with_context(|| format!("Failed to prepare {}", project))?; ++ if !self.args.prepare_cmd.is_empty() { ++ info!("Preparing '{}'", project); ++ project ++ .prepare() ++ .with_context(|| format!("Failed to prepare {}", project))?; ++ } ++ ++ if !self.args.clean_cmd.is_empty() { ++ info!("Cleaning '{}'", project); ++ project ++ .clean() ++ .with_context(|| format!("Failed to clean {}", project))?; ++ } + + if !self.args.keep_line_macros { + info!("Overriding line macros"); +@@ -297,10 +306,19 @@ impl UpatchBuild { + self.file_relation + .collect_original_build(object_dir, original_dir)?; + +- info!("Cleaning '{}'", project); +- project +- .clean() +- .with_context(|| format!("Failed to clean {}", project))?; ++ if !self.args.prepare_cmd.is_empty() { ++ info!("Preparing '{}'", project); ++ project ++ .prepare() ++ .with_context(|| format!("Failed to prepare {}", project))?; ++ } ++ ++ if !self.args.clean_cmd.is_empty() { ++ info!("Cleaning '{}'", project); ++ project ++ .clean() ++ .with_context(|| format!("Failed to clean {}", project))?; ++ } + + info!("Patching '{}'", project); + project +-- +2.43.0 + diff --git a/0106-upatch-build-rewrite-compiler-detection.patch b/0106-upatch-build-rewrite-compiler-detection.patch new file mode 100644 index 0000000000000000000000000000000000000000..a8611fa936ccba4a476deefbb438be6503cc6439 --- /dev/null +++ b/0106-upatch-build-rewrite-compiler-detection.patch @@ -0,0 +1,1545 @@ +From 84b924bc883fa1f3590ef08689a297f9b5ad0d29 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 17 Apr 2025 09:49:22 +0800 +Subject: [PATCH] upatch-build: rewrite compiler detection + +Dwarf: +1. remove dwarf::{Dwarf, Relocation} +2. add dwarf::{Producer, ProducerType, ProducerParser, ProducerIterator} +3. rewrite pruducer name parsing logic +4. rewrite producer vesrion parsing logic +5. support llvm assembler & clang compiler + +Compiler: +1. rename compiler::CompilerInfo -> compiler::Compiler +2. detection only compiles one test object +3. support cross compiler +4. support llvm linker + +Builder: +1. rewrite compiler detection logic +2. rewrite compiler version check logic +3. rewrite patch linking logic + +Signed-off-by: renoseven +--- + upatch-build/Cargo.toml | 2 + + upatch-build/src/compiler.rs | 274 ++++++++++----------- + upatch-build/src/dwarf.rs | 379 +++++++++++++++++++++++++++++ + upatch-build/src/dwarf/mod.rs | 274 --------------------- + upatch-build/src/dwarf/relocate.rs | 146 ----------- + upatch-build/src/main.rs | 210 +++++++++------- + upatch-build/src/project.rs | 21 +- + 7 files changed, 651 insertions(+), 655 deletions(-) + create mode 100644 upatch-build/src/dwarf.rs + delete mode 100644 upatch-build/src/dwarf/mod.rs + delete mode 100644 upatch-build/src/dwarf/relocate.rs + +diff --git a/upatch-build/Cargo.toml b/upatch-build/Cargo.toml +index 88d88b3..5834d28 100644 +--- a/upatch-build/Cargo.toml ++++ b/upatch-build/Cargo.toml +@@ -21,6 +21,8 @@ log = { version = "0.4" } + memmap2 = { version = "0.9" } + memoffset = { version = "0.7" } + object = { version = "0.29", features = ["write"] } ++once_cell = { version = "1.18" } ++regex = { version = "1.7" } + serde = { version = "1.0", features = ["derive"] } + typed-arena = { version = "2.0" } + which = { version = "4.4" } +diff --git a/upatch-build/src/compiler.rs b/upatch-build/src/compiler.rs +index ed23bf4..0711033 100644 +--- a/upatch-build/src/compiler.rs ++++ b/upatch-build/src/compiler.rs +@@ -13,183 +13,171 @@ + */ + + use std::{ +- ffi::OsString, ++ ffi::{OsStr, OsString}, ++ os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, + }; + +-use anyhow::{Context, Result}; +-use indexmap::{IndexMap, IndexSet}; +-use log::debug; ++use anyhow::{bail, Context, Result}; ++use indexmap::IndexSet; + use which::which; + +-use syscare_common::{ffi::OsStrExt, fs, process::Command}; ++use syscare_common::{concat_os, ffi::OsStrExt as _, fs, process::Command}; + +-use crate::dwarf::{Dwarf, ProducerType}; +- +-const STD_NAMES: [&str; 45] = [ +- "c89", "c90", "c99", "c9x", "c11", "c17", "c18", "c1x", "c2x", "gnu", "gnu89", "gnu90", +- "gnu99", "gnu9x", "gnu11", "gnu17", "gnu18", "gnu1x", "gnu2x", "c++98", "c++03", "c++0x", +- "c++11", "c++14", "c++17", "c++1y", "c++1z", "c++20", "c++2a", "gnu++98", "gnu++03", "gnu++0x", +- "gnu++11", "gnu++14", "gnu++17", "gnu++1y", "gnu++1z", "gnu++20", "gnu++2a", "f95", "f2003", +- "f2008", "f2008ts", "f2018", "legacy", +-]; +-const ASSEMBLER_NAME: &str = "as"; +-const LINKER_NAME: &str = "ld"; ++use crate::dwarf::{Producer, ProducerParser, ProducerType}; + + #[derive(Debug, Clone)] +-pub struct CompilerInfo { +- pub binary: PathBuf, +- pub assembler: PathBuf, ++pub struct Compiler { ++ pub prefix: Option, ++ pub name: OsString, ++ pub kind: ProducerType, ++ pub version: OsString, ++ pub path: PathBuf, + pub linker: PathBuf, +- pub producers: IndexSet, + } + +-impl CompilerInfo { +- fn get_component_name>(compiler: P, name: &str) -> Result { +- let output = Command::new(compiler.as_ref()) +- .arg(format!("-print-prog-name={}", name)) +- .run_with_output()?; +- +- output.exit_ok()?; +- Ok(output.stdout) +- } +- +- fn build_test_objects( +- binary: P, +- assembler: Q, +- output_dir: R, +- ) -> Result> ++impl Compiler { ++ pub fn parse(path: P, output_dir: Q) -> Result + where + P: AsRef, + Q: AsRef, +- R: AsRef, + { +- let source_file = output_dir.as_ref().join("test.c"); +- let assembly_file = output_dir.as_ref().join("test.S"); +- let assembly_object = output_dir.as_ref().join("test.o"); +- +- fs::write(&source_file, "int main() { return 0; }")?; ++ let path = path.as_ref().to_path_buf(); ++ let name = path ++ .file_name() ++ .context("Failed to parse compiler name")? ++ .to_os_string(); ++ ++ let output_path = Self::build_test_object(&path, &name, output_dir.as_ref()) ++ .context("Failed to build test object")?; ++ let prefix = Self::parse_compiler_prefix(&name).map(OsStr::to_os_string); ++ let producer = Self::parse_compiler_producer(&output_path) ++ .context("Failed to parse compiler producer")?; ++ let linker = Self::get_compiler_linker(&path, &prefix, &producer) ++ .context("Failed to get compiler linker")?; ++ ++ Ok(Self { ++ prefix, ++ name, ++ kind: producer.kind, ++ version: producer.version, ++ path, ++ linker, ++ }) ++ } + +- let mut object_files = IndexSet::new(); ++ fn build_test_object(path: &Path, name: &OsStr, output_dir: &Path) -> Result { ++ let source_file = output_dir.join("test.c"); ++ let output_file = output_dir.join(concat_os!(name, "-test")); + +- Command::new(binary.as_ref()) +- .arg("-S") ++ if !source_file.exists() { ++ fs::write(&source_file, "int main() { return 0; }")?; ++ } ++ Command::new(path) ++ .arg("-g") + .arg(&source_file) + .arg("-o") +- .arg(&assembly_file) ++ .arg(&output_file) + .run()? + .exit_ok()?; + +- Command::new(assembler.as_ref()) +- .arg("-g") +- .arg(assembly_file) +- .arg("-o") +- .arg(&assembly_object) +- .run()? +- .exit_ok()?; ++ Ok(output_file) ++ } ++ ++ fn parse_compiler_prefix(compiler: &OsStr) -> Option<&OsStr> { ++ /* ++ * Matches compiler prefix of compiler name ++ * eg. x86_64-linux-gnu-gcc -> x86_64-linux-gnu- ++ * eg. aarch64-target-linux-clang -> aarch64-target-linux- ++ */ ++ let slice = compiler.as_bytes(); ++ ++ let spliter_indices = slice.iter().enumerate().rev().filter_map(|(index, &b)| { ++ if b == b'-' { ++ return Some(index); ++ } ++ None ++ }); + +- object_files.insert(assembly_object); +- +- for std_name in STD_NAMES { +- let compiler_object = output_dir.as_ref().join(format!("test_{}.o", std_name)); +- let build_success = Command::new(binary.as_ref()) +- .arg(format!("-std={}", std_name)) +- .args(["-g", "-c"]) +- .arg(&source_file) +- .arg("-o") +- .arg(&compiler_object) +- .stderr(log::Level::Trace) +- .run_with_output()? +- .success(); +- +- if build_success { +- object_files.insert(compiler_object); ++ for pos in spliter_indices { ++ let (prefix, name) = slice.split_at(pos + 1); ++ if name.iter().any(|&b| !b.is_ascii_digit()) { ++ return Some(OsStr::from_bytes(prefix)); + } + } +- object_files.sort(); + +- Ok(object_files) ++ None + } + +- fn run_compiler_detection( +- binary: P, +- assembler: Q, +- output_dir: R, +- ) -> Result> +- where +- P: AsRef, +- Q: AsRef, +- R: AsRef, +- { +- let mut producers = IndexSet::new(); +- +- let test_objects = Self::build_test_objects(binary, assembler, &output_dir) +- .context("Failed to build test objects")?; +- for test_object in test_objects { +- producers.extend(Dwarf::parse(test_object)?.producers()); ++ fn parse_compiler_producer(path: &Path) -> Result { ++ let producer_parser = ProducerParser::open(path) ++ .with_context(|| format!("Failed to open {}", path.display()))?; ++ let producer_iter = producer_parser ++ .parse() ++ .with_context(|| format!("Failed to parse {}", path.display()))?; ++ ++ let mut producer_map = IndexSet::new(); ++ for parse_result in producer_iter { ++ let producer = parse_result.context("Failed to parse object producer")?; ++ producer_map.insert(producer); + } +- producers.sort(); ++ producer_map.sort(); + +- fs::remove_dir_all(&output_dir)?; +- Ok(producers) ++ // Compiler producer would be highest supported. ++ producer_map.pop().context("No object producer") + } +-} + +-impl CompilerInfo { +- pub fn parse(compilers: I, temp_dir: Q) -> Result> +- where +- I: IntoIterator, +- P: AsRef, +- Q: AsRef, +- { +- let mut compiler_map = IndexMap::new(); +- +- for compiler in compilers { +- let binary_file = compiler.as_ref(); +- let binary_name = binary_file +- .file_name() +- .context("Failed to parse binary name")?; +- +- let output_dir = temp_dir.as_ref().join(binary_name); +- fs::create_dir_all(&output_dir)?; +- +- debug!("- Checking {}", binary_file.display()); +- let assembler_name = Self::get_component_name(binary_file, ASSEMBLER_NAME) +- .with_context(|| { +- format!("Failed to get assembler name of {}", binary_file.display()) +- })?; +- let assembler = fs::normalize(which(assembler_name.trim()).with_context(|| { +- format!("Cannot find assembler {}", assembler_name.to_string_lossy()) +- })?)?; +- +- let linker_name = +- Self::get_component_name(binary_file, LINKER_NAME).with_context(|| { +- format!("Failed to get linker name of {}", binary_file.display()) +- })?; +- let linker = fs::normalize(which(linker_name.trim()).with_context(|| { +- format!("Cannot find linker {}", linker_name.to_string_lossy()) +- })?)?; +- let producers = Self::run_compiler_detection(&compiler, &assembler, &output_dir) +- .context("Failed to detect compiler")?; +- +- for producer in &producers { +- let producer_type = ProducerType::from(producer); +- if (producer_type == ProducerType::As) || (producer_type == ProducerType::Unknown) { +- continue; +- } +- compiler_map.insert( +- producer_type, +- Self { +- binary: binary_file.to_path_buf(), +- assembler: assembler.clone(), +- linker: linker.clone(), +- producers: producers.clone(), +- }, +- ); ++ fn get_component( ++ path: &Path, ++ prefix: &Option, ++ name: &str, ++ ) -> Result> { ++ let get_component_path = |name: &OsStr| -> Result> { ++ let output = Command::new(path) ++ .arg(concat_os!("-print-prog-name=", name)) ++ .run_with_output()?; ++ output.exit_ok()?; ++ Ok(which(output.stdout.trim()).ok()) ++ }; ++ ++ if let Some(prefixed_name) = prefix.as_ref().map(|pfx| concat_os!(pfx, name)) { ++ let component = get_component_path(&prefixed_name)?; ++ if component.is_some() { ++ return Ok(component); + } + } +- compiler_map.sort_keys(); ++ get_component_path(OsStr::new(name)) ++ } ++ ++ fn get_compiler_linker( ++ path: &Path, ++ prefix: &Option, ++ producer: &Producer, ++ ) -> Result { ++ const CLANG_LINKER_NAMES: &[&str] = &["ld.lld", "ld"]; ++ const GNU_LINKER_NAMES: &[&str] = &["ld"]; ++ ++ let linkers = if matches!(producer.kind, ProducerType::ClangC | ProducerType::ClangCxx) { ++ // Clang may use llvm linker, we will try it firstly. ++ CLANG_LINKER_NAMES ++ } else { ++ GNU_LINKER_NAMES ++ }; ++ for name in linkers { ++ if let Some(path) = Self::get_component(path, prefix, name)? { ++ return Ok(path); ++ } ++ } ++ bail!("No suitable linker") ++ } ++} + +- Ok(compiler_map) ++impl std::fmt::Display for Compiler { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ f.write_fmt(format_args!( ++ "{}{}", ++ self.prefix.as_deref().unwrap_or_default().to_string_lossy(), ++ self.name.to_string_lossy() ++ )) + } + } +diff --git a/upatch-build/src/dwarf.rs b/upatch-build/src/dwarf.rs +new file mode 100644 +index 0000000..c93cfa9 +--- /dev/null ++++ b/upatch-build/src/dwarf.rs +@@ -0,0 +1,379 @@ ++use std::{ ++ cell::RefCell, ++ collections::HashMap, ++ ffi::{OsStr, OsString}, ++ os::unix::ffi::OsStrExt, ++ path::Path, ++ rc::Rc, ++}; ++ ++use anyhow::{bail, Context, Result}; ++use gimli::{ ++ constants::*, AttributeValue, DebugInfoUnitHeadersIter, DebuggingInformationEntry, DwLang, ++ Dwarf, EndianRcSlice, Endianity, Reader, RunTimeEndian, SectionId, Unit, UnitOffset, ++}; ++use memmap2::Mmap; ++use object::{Endianness, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget}; ++use once_cell::sync::Lazy; ++use regex::bytes::Regex; ++ ++#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] ++pub enum ProducerType { ++ GnuAs, ++ LlvmAs, ++ GnuC, ++ ClangC, ++ GnuCxx, ++ ClangCxx, ++ Unknown, ++} ++ ++impl std::fmt::Display for ProducerType { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ let name = match self { ++ ProducerType::GnuAs => "GNU AS", ++ ProducerType::LlvmAs => "LLVM AS", ++ ProducerType::GnuC => "GNU C", ++ ProducerType::ClangC => "Clang C", ++ ProducerType::GnuCxx => "GNU C++", ++ ProducerType::ClangCxx => "Clang C++", ++ ProducerType::Unknown => "Unknown", ++ }; ++ f.write_str(name) ++ } ++} ++ ++#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] ++pub struct Producer { ++ pub kind: ProducerType, ++ pub name: OsString, ++ pub version: OsString, ++} ++ ++impl Producer { ++ pub fn is_assembler(&self) -> bool { ++ matches!(self.kind, ProducerType::GnuAs | ProducerType::LlvmAs) ++ } ++} ++ ++impl std::fmt::Display for Producer { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ f.write_fmt(format_args!( ++ "{} {}", ++ self.name.to_string_lossy(), ++ self.version.to_string_lossy() ++ )) ++ } ++} ++ ++pub struct ProducerParser { ++ _file: std::fs::File, ++ mmap: Mmap, ++ data_map: RefCell>>, ++} ++ ++impl ProducerParser { ++ pub fn open>(path: P) -> Result { ++ let _file = std::fs::File::open(path)?; ++ let mmap = unsafe { Mmap::map(&_file)? }; ++ mmap.advise(memmap2::Advice::Random)?; ++ ++ Ok(Self { ++ _file, ++ mmap, ++ data_map: RefCell::new(HashMap::new()), ++ }) ++ } ++ ++ pub fn parse(&self) -> Result + '_>> { ++ let dwarf = Dwarf::load(|section_id| -> Result<_> { self.load_section(section_id) }) ++ .context("Failed to load DWARF information")?; ++ let headers = dwarf.units(); ++ ++ Ok(ProducerIterator { ++ dwarf, ++ headers, ++ state: None, ++ }) ++ } ++} ++ ++impl ProducerParser { ++ fn load_section(&self, section_id: SectionId) -> Result + '_> { ++ const U8_TYPE_SIZE: usize = 1; ++ const U16_TYPE_SIZE: usize = 2; ++ const U32_TYPE_SIZE: usize = 4; ++ const U64_TYPE_SIZE: usize = 8; ++ const BYTE_BIT_NUM: u8 = 8; ++ ++ let file = object::File::parse(self.mmap.as_ref())?; ++ let endian = match file.endianness() { ++ Endianness::Little => RunTimeEndian::Little, ++ Endianness::Big => RunTimeEndian::Big, ++ }; ++ ++ let section_name = section_id.name(); ++ let section = match file.section_by_name(section_name) { ++ Some(section) => section, ++ None => return Ok(EndianRcSlice::new(Rc::default(), endian)), ++ }; ++ ++ let mut section_data = section ++ .uncompressed_data() ++ .map(|slice| slice.into_owned()) ++ .with_context(|| format!("Failed to read section {}", section_name))?; ++ for (offset, reloc) in section.relocations() { ++ if let RelocationTarget::Symbol(index) = reloc.target() { ++ if !matches!(reloc.kind(), RelocationKind::Absolute) { ++ continue; ++ } ++ ++ let symbol = file.symbol_by_index(index)?; ++ let addend = reloc.addend(); ++ let value = if addend >= 0 { ++ symbol.address().checked_add(addend.unsigned_abs()) ++ } else { ++ symbol.address().checked_sub(addend.unsigned_abs()) ++ } ++ .context("Relocation overflow")?; ++ ++ let len = (reloc.size() / BYTE_BIT_NUM) as usize; ++ let buf = &mut section_data[offset as usize..offset as usize + len]; ++ ++ match len { ++ U8_TYPE_SIZE => buf[0] = value as u8, ++ U16_TYPE_SIZE => endian.write_u16(buf, value as u16), ++ U32_TYPE_SIZE => endian.write_u32(buf, value as u32), ++ U64_TYPE_SIZE => endian.write_u64(buf, value), ++ _ => bail!("Invalid relocation length"), ++ } ++ } else { ++ bail!("Unsupported relocation type"); ++ } ++ } ++ ++ let bytes: Rc<[u8]> = Rc::from(section_data.into_boxed_slice()); ++ self.data_map.borrow_mut().insert(section_id, bytes.clone()); ++ ++ Ok(EndianRcSlice::new(bytes, endian)) ++ } ++ ++ fn parse_producer_attr( ++ dwarf: &Dwarf, ++ unit: &Unit, ++ attr: AttributeValue, ++ ) -> Result ++ where ++ R: Reader, ++ { ++ let attr = dwarf ++ .attr_string(unit, attr) ++ .context("Cannot find attribute string")?; ++ let slice = attr ++ .to_slice() ++ .context("Failed to read attribute string data")?; ++ ++ Ok(OsStr::from_bytes(&slice).to_os_string()) ++ } ++ ++ fn parse_producer_name(str: &OsStr) -> Option<&OsStr> { ++ /* ++ * Matches name in producer string ++ * eg. GNU C17 12.3.1 (openEuler 12.3.1-62.oe2403sp1) -> GNU C17 ++ * eg. clang version 17.0.6 (17.0.6-30-oe2043sp1) -> clang ++ */ ++ static PRODUCER_NAME_REGEX: Lazy = Lazy::new(|| { ++ Regex::new(r"^((?:\s?[A-Za-z]+\d*)+)").expect("Invalid producer name regex") ++ }); ++ ++ PRODUCER_NAME_REGEX ++ .captures(str.as_bytes()) ++ .and_then(|captures| captures.get(1)) ++ .map(|matched| matched.as_bytes()) ++ .map(|bytes| bytes.strip_suffix(b" version").unwrap_or(bytes)) ++ .map(OsStr::from_bytes) ++ } ++ ++ fn parse_producer_version(str: &OsStr) -> Option<&OsStr> { ++ /* ++ * Matches version in producer string ++ * eg. GNU C17 12.3.1 (openEuler 12.3.1-62.oe2403sp1) -> 12.3.1 ++ * eg. clang version 17.0.6 (17.0.6-30-oe2043sp1) -> 17.0.6 ++ */ ++ static PRODUCER_VERSION_REGEX: Lazy = ++ Lazy::new(|| Regex::new(r"(\d+(?:\.\d+)+)").expect("Invalid producer version regex")); ++ ++ PRODUCER_VERSION_REGEX ++ .captures(str.as_bytes()) ++ .and_then(|captures| captures.get(1)) ++ .map(|matched| matched.as_bytes()) ++ .map(OsStr::from_bytes) ++ } ++ ++ fn parse_producer_type(str: &OsStr, attr: AttributeValue) -> Result ++ where ++ R: Reader, ++ { ++ const DW_LANGS_AS: &[DwLang] = &[ ++ DW_LANG_Mips_Assembler, ++ DW_LANG_SUN_Assembler, ++ DW_LANG_ALTIUM_Assembler, ++ ]; ++ const DW_LANGS_C: &[DwLang] = &[ ++ DW_LANG_C, ++ DW_LANG_C89, ++ DW_LANG_C99, ++ DW_LANG_C11, ++ DW_LANG_C17, ++ ]; ++ const DW_LANGS_CXX: &[DwLang] = &[ ++ DW_LANG_C_plus_plus, ++ DW_LANG_C_plus_plus_03, ++ DW_LANG_C_plus_plus_11, ++ DW_LANG_C_plus_plus_14, ++ DW_LANG_C_plus_plus_17, ++ DW_LANG_C_plus_plus_20, ++ ]; ++ const GNU_PRODUCER_PREFIX: &str = "GNU"; ++ ++ let lang = match attr { ++ AttributeValue::Language(lang) => lang, ++ _ => bail!("Unexpected attribute type"), ++ }; ++ let is_gnu = str.to_string_lossy().starts_with(GNU_PRODUCER_PREFIX); ++ let kind = match lang { ++ lang if DW_LANGS_AS.contains(&lang) => { ++ if is_gnu { ++ ProducerType::GnuAs ++ } else { ++ ProducerType::LlvmAs ++ } ++ } ++ lang if DW_LANGS_C.contains(&lang) => { ++ if is_gnu { ++ ProducerType::GnuC ++ } else { ++ ProducerType::ClangC ++ } ++ } ++ lang if DW_LANGS_CXX.contains(&lang) => { ++ if is_gnu { ++ ProducerType::GnuCxx ++ } else { ++ ProducerType::ClangCxx ++ } ++ } ++ _ => ProducerType::Unknown, ++ }; ++ ++ Ok(kind) ++ } ++ ++ fn parse_producer( ++ dwarf: &Dwarf, ++ unit: &Unit, ++ die: DebuggingInformationEntry, ++ ) -> Result> ++ where ++ R: Reader, ++ { ++ if die.tag() != DW_TAG_compile_unit { ++ return Ok(None); ++ } ++ ++ let str = match die.attr_value(DW_AT_producer)? { ++ Some(attr) => Self::parse_producer_attr(dwarf, unit, attr)?, ++ None => bail!("Invalid DW_AT_producer attribute"), ++ }; ++ let kind = match die.attr_value(DW_AT_language)? { ++ Some(attr) => Self::parse_producer_type(&str, attr)?, ++ _ => bail!("Invalid DW_AT_language attribute"), ++ }; ++ let name = Self::parse_producer_name(&str).context("Invalid producer name")?; ++ let version = Self::parse_producer_version(&str).context("Invalid producer version")?; ++ ++ Ok(Some(Producer { ++ kind, ++ name: name.to_os_string(), ++ version: version.to_os_string(), ++ })) ++ } ++} ++ ++pub struct ProducerIterator { ++ dwarf: Dwarf, ++ headers: DebugInfoUnitHeadersIter, ++ state: Option<(Unit, Vec)>, ++} ++ ++impl> ProducerIterator { ++ fn current(&self) -> Result, DebuggingInformationEntry)>> { ++ if let Some((unit, offsets)) = &self.state { ++ if let Some(offset) = offsets.last() { ++ return Ok(Some((unit, unit.entry(*offset)?))); ++ } ++ } ++ Ok(None) ++ } ++ ++ fn has_die(&self) -> bool { ++ self.state ++ .as_ref() ++ .map(|(_, offsets)| !offsets.is_empty()) ++ .unwrap_or(false) ++ } ++ ++ fn next_die(&mut self) { ++ if let Some((_, offsets)) = &mut self.state { ++ offsets.pop(); ++ } ++ } ++ ++ fn next_unit(&mut self) -> Result<()> { ++ if let Some(header) = self.headers.next()? { ++ let unit = self.dwarf.unit(header)?; ++ ++ let mut offsets = Vec::new(); ++ let mut cursor = unit.entries(); ++ while let Some((_, entry)) = cursor.next_dfs()? { ++ offsets.push(entry.offset()); ++ } ++ offsets.reverse(); ++ ++ self.state = Some((unit, offsets)); ++ } ++ ++ Ok(()) ++ } ++} ++ ++impl> Iterator for ProducerIterator { ++ type Item = Result; ++ ++ fn next(&mut self) -> Option { ++ loop { ++ let producer = match self.current() { ++ Ok(Some((unit, die))) => { ++ ProducerParser::parse_producer(&self.dwarf, unit, die).transpose() ++ } ++ Ok(None) => None, ++ Err(e) => Some(Err(e)), ++ }; ++ ++ self.next_die(); ++ ++ if producer.is_some() { ++ return producer; ++ } ++ ++ if !self.has_die() { ++ if let Err(e) = self.next_unit() { ++ return Some(Err(e)); ++ } ++ if !self.has_die() { ++ return None; ++ } ++ } ++ } ++ } ++} +diff --git a/upatch-build/src/dwarf/mod.rs b/upatch-build/src/dwarf/mod.rs +deleted file mode 100644 +index 4399b51..0000000 +--- a/upatch-build/src/dwarf/mod.rs ++++ /dev/null +@@ -1,274 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * upatch-build is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-// mod dump; +-mod relocate; +- +-use std::{ +- borrow::{Borrow, Cow}, +- ffi::{OsStr, OsString}, +- os::unix::ffi::OsStrExt as UnixOsStrExt, +- path::{Path, PathBuf}, +-}; +- +-use anyhow::Result; +-use gimli::{ +- constants, Attribute, AttributeValue, EndianSlice, Endianity, Reader, RunTimeEndian, SectionId, +-}; +-use indexmap::{IndexMap, IndexSet}; +-use log::trace; +-use object::{ +- File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationKind, RelocationTarget, +- Section, +-}; +-use typed_arena::Arena; +- +-use syscare_common::{ffi::OsStrExt, fs::MappedFile}; +- +-use relocate::Relocate; +- +-#[allow(non_snake_case)] +-#[derive(Debug, Clone, Hash, PartialEq, Eq)] +-pub struct CompileUnit { +- producer: OsString, // DW_AT_producer +- compile_dir: PathBuf, // DW_AT_comp_dir +- file_name: PathBuf, // DW_AT_name +-} +- +-impl CompileUnit { +- pub fn producer(&self) -> OsString { +- self.producer +- .split('-') +- .next() +- .map(|s| s.trim().to_os_string()) +- .unwrap_or_else(|| self.producer.clone()) +- } +- +- pub fn producer_type(&self) -> ProducerType { +- ProducerType::from(&self.producer) +- } +-} +- +-#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +-pub enum ProducerType { +- C, +- Cxx, +- As, +- Unknown, +-} +- +-impl> From for ProducerType { +- fn from(s: S) -> Self { +- if s.as_ref().contains("C++") { +- ProducerType::Cxx +- } else if s.as_ref().contains("C") { +- ProducerType::C +- } else if s.as_ref().contains("AS") { +- ProducerType::As +- } else { +- ProducerType::Unknown +- } +- } +-} +- +-impl std::fmt::Display for ProducerType { +- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +- f.write_str(match self { +- ProducerType::C => "C", +- ProducerType::Cxx => "CXX", +- ProducerType::As => "AS", +- ProducerType::Unknown => "Unknown", +- }) +- } +-} +- +-pub struct Dwarf { +- pub units: IndexSet, +-} +- +-impl Dwarf { +- pub fn parse>(file_path: P) -> Result { +- let mmap = MappedFile::open(file_path)?; +- let object = File::parse(mmap.as_bytes())?; +- let endian = if object.is_little_endian() { +- gimli::RunTimeEndian::Little +- } else { +- gimli::RunTimeEndian::Big +- }; +- +- Ok(Self { +- units: Self::parse_compile_units(&object, endian)?, +- }) +- } +- +- pub fn producers(&self) -> IndexSet { +- self.units.iter().map(|unit| unit.producer()).collect() +- } +- +- pub fn producer_types(&self) -> IndexSet { +- self.units.iter().map(|unit| unit.producer_type()).collect() +- } +-} +- +-impl Dwarf { +- fn add_relocations( +- relocations: &mut IndexMap, +- file: &File, +- section: &Section, +- ) { +- const INVALID_SECTION_NAME: &str = ".invalid"; +- +- for (offset64, mut relocation) in section.relocations() { +- let offset = offset64 as usize; +- if offset as u64 != offset64 { +- continue; +- } +- match relocation.kind() { +- RelocationKind::Absolute => { +- if let RelocationTarget::Symbol(symbol_idx) = relocation.target() { +- match file.symbol_by_index(symbol_idx) { +- Ok(symbol) => { +- let addend = +- symbol.address().wrapping_add(relocation.addend() as u64); +- relocation.set_addend(addend as i64); +- } +- Err(_) => { +- trace!("Relocation with invalid symbol for section {} at offset 0x{:08x}", +- section.name().unwrap_or(INVALID_SECTION_NAME), offset +- ); +- } +- } +- } +- if relocations.insert(offset, relocation).is_some() { +- trace!( +- "Multiple relocations for section {} at offset 0x{:08x}", +- section.name().unwrap_or(INVALID_SECTION_NAME), +- offset +- ); +- } +- } +- _ => { +- trace!( +- "Unsupported relocation for section {} at offset 0x{:08x}", +- section.name().unwrap_or(INVALID_SECTION_NAME), +- offset +- ); +- } +- } +- } +- } +- +- fn load_file_section<'input, 'arena, Endian: Endianity>( +- id: SectionId, +- file: &File<'input>, +- endian: Endian, +- arena_data: &'arena Arena>, +- arena_relocations: &'arena Arena>, +- ) -> Result>> { +- let mut relocation_map = IndexMap::new(); +- let name = Some(id.name()); +- let data = match name.and_then(|section_name| file.section_by_name(section_name)) { +- Some(ref section) => { +- // DWO sections never have relocations, so don't bother. +- Self::add_relocations(&mut relocation_map, file, section); +- section.uncompressed_data()? +- } +- // Use a non-zero capacity so that `ReaderOffsetId`s are unique. +- None => Cow::Owned(Vec::with_capacity(1)), +- }; +- let data_ref = (*arena_data.alloc(data)).borrow(); +- let reader = EndianSlice::new(data_ref, endian); +- let section = reader; +- let relocations = (*arena_relocations.alloc(relocation_map)).borrow(); +- Ok(Relocate { +- relocations, +- section, +- reader, +- }) +- } +- +- fn parse_compile_units(file: &File, endian: RunTimeEndian) -> Result> { +- let arena_data = Arena::new(); +- let arena_relocations = Arena::new(); +- +- // Load a section and return as `Cow<[u8]>`. +- let mut load_section = |id: SectionId| -> Result<_> { +- Self::load_file_section(id, file, endian, &arena_data, &arena_relocations) +- }; +- +- let dwarf = gimli::Dwarf::load(&mut load_section)?; +- +- Self::build_compile_units(&dwarf) +- } +- +- fn build_compile_units(dwarf: &gimli::Dwarf) -> Result> { +- let mut result = IndexSet::new(); +- let mut iter = dwarf.units(); +- while let Some(header) = iter.next()? { +- let unit = dwarf.unit(header)?; +- let mut entries = unit.entries(); +- while let Some((_, entry)) = entries.next_dfs()? { +- if entry.tag() != constants::DW_TAG_compile_unit { +- break; +- } +- // Iterate over the attributes in the DIE. +- let mut attrs = entry.attrs(); +- let mut element = CompileUnit { +- producer: OsString::new(), +- compile_dir: PathBuf::new(), +- file_name: PathBuf::new(), +- }; +- +- while let Some(attr) = attrs.next()? { +- match attr.name() { +- constants::DW_AT_comp_dir => { +- element.compile_dir.push(Self::attr_value(&attr, dwarf)); +- } +- constants::DW_AT_name => { +- element.file_name.push(Self::attr_value(&attr, dwarf)); +- } +- constants::DW_AT_producer => { +- element.producer.push(Self::attr_value(&attr, dwarf)); +- } +- _ => continue, +- } +- } +- +- result.insert(element); +- } +- } +- Ok(result) +- } +- +- fn attr_value(attr: &Attribute, dwarf: &gimli::Dwarf) -> OsString { +- let value = attr.value(); +- match value { +- AttributeValue::DebugLineStrRef(offset) => { +- if let Ok(s) = dwarf.debug_line_str.get_str(offset) { +- OsStr::from_bytes(&s.to_slice().ok().unwrap_or_default()).to_os_string() +- } else { +- OsString::default() +- } +- } +- AttributeValue::DebugStrRef(offset) => { +- if let Ok(s) = dwarf.debug_str.get_str(offset) { +- OsStr::from_bytes(&s.to_slice().ok().unwrap_or_default()).to_os_string() +- } else { +- OsString::default() +- } +- } +- _ => OsString::default(), +- } +- } +-} +diff --git a/upatch-build/src/dwarf/relocate.rs b/upatch-build/src/dwarf/relocate.rs +deleted file mode 100644 +index a6e8fb9..0000000 +--- a/upatch-build/src/dwarf/relocate.rs ++++ /dev/null +@@ -1,146 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * upatch-build is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::borrow::Cow; +- +-use gimli::ReaderOffset; +-use indexmap::IndexMap; +-use object::Relocation; +- +-#[derive(Debug, Clone)] +-pub struct Relocate<'a, R: gimli::Reader> { +- pub relocations: &'a IndexMap, +- pub section: R, +- pub reader: R, +-} +- +-impl> Relocate<'_, R> { +- pub fn relocate(&self, offset: usize, value: u64) -> u64 { +- if let Some(relocation) = self.relocations.get(&offset) { +- if relocation.kind() == object::RelocationKind::Absolute { +- return if relocation.has_implicit_addend() { +- // Use the explicit addend too, because it may have the symbol value. +- value.wrapping_add(relocation.addend() as u64) +- } else { +- relocation.addend() as u64 +- }; +- } +- }; +- value +- } +-} +- +-impl> gimli::Reader for Relocate<'_, R> { +- type Endian = R::Endian; +- type Offset = R::Offset; +- +- fn read_address(&mut self, address_size: u8) -> gimli::Result { +- let offset = self.reader.offset_from(&self.section); +- let value = self.reader.read_address(address_size)?; +- Ok(self.relocate(offset, value)) +- } +- +- fn read_length(&mut self, format: gimli::Format) -> gimli::Result { +- let offset = self.reader.offset_from(&self.section); +- let value = self.reader.read_length(format)?; +- gimli::ReaderOffset::from_u64(self.relocate(offset, value.into_u64())) +- } +- +- fn read_offset(&mut self, format: gimli::Format) -> gimli::Result { +- let offset = self.reader.offset_from(&self.section); +- let value = self.reader.read_offset(format)?; +- gimli::ReaderOffset::from_u64(self.relocate(offset, value.into_u64())) +- } +- +- fn read_sized_offset(&mut self, size: u8) -> gimli::Result { +- let offset = self.reader.offset_from(&self.section); +- let value = self.reader.read_sized_offset(size)?; +- gimli::ReaderOffset::from_u64(self.relocate(offset, value.into_u64())) +- } +- +- #[inline] +- fn split(&mut self, len: Self::Offset) -> gimli::Result { +- let mut other = self.clone(); +- other.reader.truncate(len)?; +- self.reader.skip(len)?; +- Ok(other) +- } +- +- // All remaining methods simply delegate to `self.reader`. +- +- #[inline] +- fn endian(&self) -> Self::Endian { +- self.reader.endian() +- } +- +- #[inline] +- fn len(&self) -> Self::Offset { +- self.reader.len() +- } +- +- #[inline] +- fn empty(&mut self) { +- self.reader.empty() +- } +- +- #[inline] +- fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> { +- self.reader.truncate(len) +- } +- +- #[inline] +- fn offset_from(&self, base: &Self) -> Self::Offset { +- self.reader.offset_from(&base.reader) +- } +- +- #[inline] +- fn offset_id(&self) -> gimli::ReaderOffsetId { +- self.reader.offset_id() +- } +- +- #[inline] +- fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option { +- self.reader.lookup_offset_id(id) +- } +- +- #[inline] +- fn find(&self, byte: u8) -> gimli::Result { +- self.reader.find(byte) +- } +- +- #[inline] +- fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> { +- self.reader.skip(len) +- } +- +- #[inline] +- fn to_slice(&self) -> gimli::Result> { +- self.reader.to_slice() +- } +- +- #[inline] +- fn to_string(&self) -> gimli::Result> { +- self.reader.to_string() +- } +- +- #[inline] +- fn to_string_lossy(&self) -> gimli::Result> { +- self.reader.to_string_lossy() +- } +- +- #[inline] +- fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> { +- self.reader.read_slice(buf) +- } +-} +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 73d286f..56a8715 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -12,15 +12,23 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{env, ffi::OsStr, fs::Permissions, os::unix::fs::PermissionsExt, path::Path, process}; ++use std::{ ++ env, ++ ffi::OsStr, ++ fs::Permissions, ++ os::unix::fs::PermissionsExt, ++ path::{Path, PathBuf}, ++ process, ++}; + +-use anyhow::{ensure, Context, Result}; ++use anyhow::{bail, ensure, Context, Result}; + use flexi_logger::{ + DeferredNow, Duplicate, FileSpec, LogSpecification, Logger, LoggerHandle, WriteMode, + }; + use indexmap::{IndexMap, IndexSet}; + use log::{debug, error, info, trace, warn, Level, LevelFilter, Record}; + use object::{write, Object, ObjectKind, ObjectSection, SectionKind}; ++ + use syscare_common::{ + concat_os, + fs::{self, MappedFile}, +@@ -37,12 +45,14 @@ mod file_relation; + mod project; + mod resolve; + +-use args::Arguments; +-use build_root::BuildRoot; +-use compiler::CompilerInfo; +-use dwarf::{Dwarf, ProducerType}; +-use file_relation::FileRelation; +-use project::Project; ++use crate::{ ++ args::Arguments, ++ build_root::BuildRoot, ++ compiler::Compiler, ++ dwarf::{ProducerParser, ProducerType}, ++ file_relation::FileRelation, ++ project::Project, ++}; + + const CLI_NAME: &str = "upatch build"; + const CLI_VERSION: &str = env!("CARGO_PKG_VERSION"); +@@ -59,7 +69,7 @@ struct UpatchBuild { + args: Arguments, + logger: LoggerHandle, + build_root: BuildRoot, +- compiler_map: IndexMap, ++ compiler_map: IndexMap, + file_relation: FileRelation, + } + +@@ -107,25 +117,101 @@ impl UpatchBuild { + }) + } + +- fn check_debuginfo(&self) -> Result<()> { +- let supported_compilers = self +- .compiler_map +- .values() +- .flat_map(|info| &info.producers) +- .collect::>(); +- for debuginfo in &self.args.debuginfo { +- for producer in Dwarf::parse(debuginfo)?.producers() { +- ensure!( +- supported_compilers.contains(&producer), +- "{} is not supported", +- producer.to_string_lossy() +- ); ++ fn detect_compilers(&mut self) -> Result<()> { ++ let mut c_compilers = 0usize; ++ let mut cxx_compilers = 0usize; ++ ++ for compiler_path in &self.args.compiler { ++ let compiler = Compiler::parse(compiler_path, &self.build_root.build_dir) ++ .with_context(|| format!("Failed to detect {}", compiler_path.display()))?; ++ match compiler.kind { ++ ProducerType::GnuC | ProducerType::ClangC => c_compilers += 1, ++ ProducerType::GnuCxx | ProducerType::ClangCxx => cxx_compilers += 1, ++ _ => bail!("Unknown compiler type"), + } ++ info!( ++ "[{}] name: {}, version: {}", ++ compiler.kind, ++ compiler, ++ compiler.version.to_string_lossy(), ++ ); ++ self.compiler_map.insert(compiler.kind, compiler); + } + ++ ensure!( ++ c_compilers <= 1 && cxx_compilers <= 1, ++ "Cannot define multiple C/C++ compilers" ++ ); ++ self.compiler_map.sort_keys(); ++ + Ok(()) + } + ++ fn check_compiler_version(&self) -> Result<()> { ++ for path in &self.args.debuginfo { ++ let producer_parser = ProducerParser::open(path) ++ .with_context(|| format!("Failed to open {}", path.display()))?; ++ let producer_iter = producer_parser ++ .parse() ++ .with_context(|| format!("Failed to parse {}", path.display()))?; ++ ++ for parse_result in producer_iter { ++ let producer = parse_result.context("Failed to parse debuginfo producer")?; ++ if producer.is_assembler() { ++ continue; ++ } ++ let matched = self ++ .compiler_map ++ .get(&producer.kind) ++ .map(|compiler| compiler.version == producer.version) ++ .unwrap_or(false); ++ ensure!(matched, "Producer {} mismatched", producer); ++ } ++ } ++ ++ Ok(()) ++ } ++ ++ fn find_linker(&self, objects: &[PathBuf]) -> Result<&Path> { ++ let mut producers = IndexSet::new(); ++ for path in objects { ++ let producer_parser = ProducerParser::open(path) ++ .with_context(|| format!("Failed to open {}", path.display()))?; ++ let producer_iter = producer_parser ++ .parse() ++ .with_context(|| format!("Failed to parse {}", path.display()))?; ++ ++ for parse_result in producer_iter { ++ let producer = parse_result.context("Failed to parse object producer")?; ++ if producer.is_assembler() { ++ continue; ++ } ++ producers.insert(producer); ++ } ++ } ++ producers.sort(); ++ ++ let producer = producers.pop().context("No object producer")?; ++ let compiler = self ++ .compiler_map ++ .get(&producer.kind) ++ .with_context(|| format!("Cannot find {} compiler", producer.kind))?; ++ ++ Ok(compiler.linker.as_path()) ++ } ++ ++ fn link_objects(&self, objects: &[PathBuf], output: &Path) -> Result<()> { ++ let linker = self.find_linker(objects).context("Cannot find linker")?; ++ ++ Command::new(linker) ++ .args(["-r", "-o"]) ++ .arg(output) ++ .args(objects) ++ .stdout(Level::Trace) ++ .run_with_output()? ++ .exit_ok() ++ } ++ + fn build_patch(&self, patch_name: &OsStr, binary: &Path, debuginfo: &Path) -> Result<()> { + const NOTES_OBJECT_NAME: &str = "notes.o"; + +@@ -170,9 +256,9 @@ impl UpatchBuild { + } + + debug!("- Collecting changes"); +- let mut changed_objects = +- elf::find_elf_files(&temp_dir, |_, obj_kind| obj_kind == ObjectKind::Relocatable)?; +- if changed_objects.is_empty() { ++ let mut objects = ++ elf::find_elf_files(&temp_dir, |_, kind| matches!(kind, ObjectKind::Relocatable))?; ++ if objects.is_empty() { + debug!("- No functional changes"); + return Ok(()); + } +@@ -181,25 +267,10 @@ impl UpatchBuild { + let notes_object = temp_dir.join(NOTES_OBJECT_NAME); + Self::create_note(&debuginfo_file, ¬es_object) + .context("Failed to create patch notes")?; +- changed_objects.push(notes_object); +- +- debug!("- Linking patch objects"); +- let mut link_compiler = ProducerType::C; +- for object in &changed_objects { +- if Dwarf::parse(object)? +- .producer_types() +- .contains(&ProducerType::Cxx) +- { +- link_compiler = ProducerType::Cxx; +- break; +- } +- } ++ objects.push(notes_object); + +- let compiler_info = self +- .compiler_map +- .get(&link_compiler) +- .with_context(|| format!("Failed to get link compiler {}", link_compiler))?; +- Self::link_objects(&compiler_info.linker, &changed_objects, &output_file) ++ debug!("- Linking patch"); ++ self.link_objects(&objects, &output_file) + .context("Failed to link patch objects")?; + + debug!("- Resolving patch"); +@@ -231,36 +302,16 @@ impl UpatchBuild { + } + + fn run(&mut self) -> Result<()> { +- let compilers = self.args.compiler.as_slice(); +- let binary_dir = self.args.binary_dir.as_path(); +- let object_dir = self.args.object_dir.as_path(); +- let binaries = self.args.binary.as_slice(); +- let debuginfos = self.args.debuginfo.as_slice(); +- +- let temp_dir = self.build_root.build_dir.as_path(); +- let original_dir = self.build_root.original_dir.as_path(); +- let patched_dir = self.build_root.patched_dir.as_path(); +- + info!("=============================="); + info!("{}", CLI_ABOUT); + info!("=============================="); + trace!("{:#?}", self.args); + +- info!("Checking compiler(s)"); +- self.compiler_map = CompilerInfo::parse(compilers, temp_dir)?; +- ++ info!("Detecting compiler(s)"); + info!("------------------------------"); + info!("Compiler"); + info!("------------------------------"); +- for (producer_type, compiler_info) in &self.compiler_map { +- info!( +- "[{}] compiler: {}, assembler: {}, linker: {}", +- producer_type, +- compiler_info.binary.display(), +- compiler_info.assembler.display(), +- compiler_info.linker.display(), +- ); +- } ++ self.detect_compilers()?; + + let project = Project::new(&self.args, &self.build_root, &self.compiler_map)?; + info!("------------------------------"); +@@ -269,11 +320,12 @@ impl UpatchBuild { + info!("Testing patch file(s)"); + project.test_patches().context("Patch test failed")?; + +- info!("Checking debuginfo version(s)"); ++ info!("Checking compiler version(s)"); + if self.args.skip_compiler_check { + warn!("Warning: Skipped compiler version check!") + } else { +- self.check_debuginfo().context("Debuginfo check failed")?; ++ self.check_compiler_version() ++ .context("Compiler version check failed")?; + } + + if !self.args.prepare_cmd.is_empty() { +@@ -297,6 +349,14 @@ impl UpatchBuild { + .context("Failed to override line macros")?; + } + ++ let binary_dir = self.args.binary_dir.as_path(); ++ let object_dir = self.args.object_dir.as_path(); ++ let binaries = self.args.binary.as_slice(); ++ let debuginfos = self.args.debuginfo.as_slice(); ++ ++ let original_dir = self.build_root.original_dir.as_path(); ++ let patched_dir = self.build_root.patched_dir.as_path(); ++ + info!("Building '{}'", project); + project + .build() +@@ -426,22 +486,6 @@ impl UpatchBuild { + + command.stdout(Level::Trace).run_with_output()?.exit_ok() + } +- +- fn link_objects(linker: P, objects: I, output: Q) -> Result<()> +- where +- P: AsRef, +- I: IntoIterator, +- S: AsRef, +- Q: AsRef, +- { +- Command::new(linker.as_ref()) +- .args(["-r", "-o"]) +- .arg(output.as_ref()) +- .args(objects) +- .stdout(Level::Trace) +- .run_with_output()? +- .exit_ok() +- } + } + + impl Drop for UpatchBuild { +diff --git a/upatch-build/src/project.rs b/upatch-build/src/project.rs +index d563c02..c05cd18 100644 +--- a/upatch-build/src/project.rs ++++ b/upatch-build/src/project.rs +@@ -26,7 +26,7 @@ use indexmap::IndexMap; + use log::{debug, Level}; + use which::which; + +-use crate::{args::Arguments, build_root::BuildRoot, compiler::CompilerInfo, dwarf::ProducerType}; ++use crate::{args::Arguments, build_root::BuildRoot, compiler::Compiler, dwarf::ProducerType}; + use syscare_common::{concat_os, ffi::OsStrExt, fs, process::Command}; + + const PATCH_BIN: &str = "patch"; +@@ -60,22 +60,25 @@ impl<'a> Project<'a> { + pub fn new( + args: &'a Arguments, + build_root: &'a BuildRoot, +- compiler_map: &'a IndexMap, ++ compiler_map: &'a IndexMap, + ) -> Result { + let path_env = env::var_os(PATH_ENV) + .with_context(|| format!("Cannot read environment variable '{}'", PATH_ENV))?; + let upatch_helper = which(UPATCH_HELPER_BIN) + .with_context(|| format!("Cannot find component '{}'", UPATCH_HELPER_BIN))?; + +- for (producer_type, compiler_info) in compiler_map { +- let compiler_bin = compiler_info.binary.as_path(); +- let compiler_name = compiler_bin ++ for (kind, compiler) in compiler_map { ++ let compiler_path = compiler.path.as_path(); ++ let compiler_name = compiler_path + .file_name() + .context("Failed to parse compiler name")?; +- +- match producer_type { +- ProducerType::C => env::set_var(UPATCH_CC_ENV, compiler_bin), +- ProducerType::Cxx => env::set_var(UPATCH_CXX_ENV, compiler_bin), ++ match kind { ++ ProducerType::GnuC | ProducerType::ClangC => { ++ env::set_var(UPATCH_CC_ENV, compiler_path) ++ } ++ ProducerType::GnuCxx | ProducerType::ClangCxx => { ++ env::set_var(UPATCH_CXX_ENV, compiler_path) ++ } + _ => {} + } + fs::soft_link(&upatch_helper, build_root.bin_dir.join(compiler_name))?; +-- +2.43.0 + diff --git a/0107-upatch-build-adapt-fs-glob-changes.patch b/0107-upatch-build-adapt-fs-glob-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..3e05821a81bb2dd5e8b558b1804e11b89cfc59bc --- /dev/null +++ b/0107-upatch-build-adapt-fs-glob-changes.patch @@ -0,0 +1,78 @@ +From e7f21ead8370e9ee99437779f0dd79d8a312119a Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 21 Apr 2025 10:36:11 +0800 +Subject: [PATCH] upatch-build: adapt fs::glob() changes + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 33 ++++++++++++++++++++----------- + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 2571873..c6ab75d 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -26,7 +26,6 @@ use syscare_common::{concat_os, ffi::OsStrExt, fs}; + + use crate::elf; + +-const BUILD_ROOT_PREFIX: &str = "upatch-build."; + const UPATCH_ID_PREFIX: &str = ".upatch_"; + + const NON_EXIST_PATH: &str = "/dev/null"; +@@ -205,31 +204,41 @@ impl FileRelation { + } + + fn collect_objects( +- search_dir: P, ++ object_dir: P, + target_dir: Q, + ) -> Result> + where + P: AsRef, + Q: AsRef, + { +- let mut object_files = IndexSet::new(); ++ let object_dir = object_dir.as_ref(); ++ let target_dir = target_dir.as_ref(); + +- let matched_dirs = fs::glob(&search_dir) +- .with_context(|| format!("Cannot match path {}", search_dir.as_ref().display()))?; +- for matched_dir in matched_dirs { +- object_files.extend(elf::find_elf_files(&matched_dir, |obj_path, obj_kind| { +- !obj_path.contains(BUILD_ROOT_PREFIX) && (obj_kind == ObjectKind::Relocatable) +- })?); ++ let mut object_files = IndexSet::new(); ++ for match_result in fs::glob(object_dir) { ++ let matched_dir = match_result.with_context(|| { ++ format!("Cannot match object directory {}", object_dir.display()) ++ })?; ++ let found_files = ++ fs::list_files(&matched_dir, fs::TraverseOptions { recursive: true })? ++ .into_iter() ++ .filter(|file_path| { ++ matches!( ++ elf::parse_file_kind(file_path).unwrap_or(ObjectKind::Unknown), ++ ObjectKind::Relocatable ++ ) ++ }); ++ object_files.extend(found_files); + } + ensure!( + !object_files.is_empty(), +- "Cannot find any valid objects in {}, please add compile flag '-save-temps' manually", +- search_dir.as_ref().display() ++ "Cannot find any object in {}", ++ object_dir.display() + ); + + let mut object_relations = Vec::with_capacity(object_files.len()); + for (file_id, object_file) in object_files.into_iter().enumerate() { +- let object_archive = target_dir.as_ref().join(concat_os!( ++ let object_archive = target_dir.join(concat_os!( + format!("{:04}-", file_id), + object_file.file_name().with_context(|| { + format!("Failed to parse file name of {}", object_file.display()) +-- +2.43.0 + diff --git a/0108-upatch-build-binary-dir-supports-wildcard-path.patch b/0108-upatch-build-binary-dir-supports-wildcard-path.patch new file mode 100644 index 0000000000000000000000000000000000000000..cc2c5e3dcc93ebffdedfe4e401fdd65543600841 --- /dev/null +++ b/0108-upatch-build-binary-dir-supports-wildcard-path.patch @@ -0,0 +1,73 @@ +From b98c6423abb6018b93fce727d147915ae84d3b8d Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 21 Apr 2025 11:49:37 +0800 +Subject: [PATCH] upatch-build: '--binary-dir' supports wildcard path + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 43 +++++++++++++++++-------------- + 1 file changed, 24 insertions(+), 19 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index c6ab75d..6aa6111 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -75,31 +75,36 @@ impl FileRelation { + let mut debuginfo_iter = debuginfos.into_iter(); + + while let (Some(binary), Some(debuginfo)) = (binary_iter.next(), debuginfo_iter.next()) { +- let binary_path = binary_dir.as_ref().join(&binary); +- let binary_files = +- fs::list_files(&binary_dir, fs::TraverseOptions { recursive: true })? +- .into_iter() +- .filter(|file_path| { +- file_path.ends_with(binary.as_ref().as_os_str()) +- && matches!( ++ let binary_dir = binary_dir.as_ref(); ++ let binary_path = binary.as_ref().as_os_str(); ++ ++ let mut binary_files = IndexSet::new(); ++ for match_result in fs::glob(binary_dir) { ++ let matched_dir = match_result.with_context(|| { ++ format!("Cannot match binary directory {}", binary_dir.display()) ++ })?; ++ let found_files = ++ fs::list_files(matched_dir, fs::TraverseOptions { recursive: true })? ++ .into_iter() ++ .filter(|file_path| file_path.ends_with(binary_path)) ++ .filter(|file_path| { ++ matches!( + elf::parse_file_kind(file_path).unwrap_or(ObjectKind::Unknown), + ObjectKind::Executable | ObjectKind::Dynamic + ) +- }) +- .collect::>(); +- +- ensure!( +- !binary_files.is_empty(), +- "Path {} does not match to any file", +- binary_path.display() +- ); ++ }); ++ binary_files.extend(found_files); ++ } ++ let binary_file = binary_files ++ .pop() ++ .with_context(|| format!("Cannot find any binary in {}", binary_dir.display()))?; + ensure!( +- binary_files.len() == 1, +- "Path {} matches to too many file", +- binary_path.display() ++ binary_files.is_empty(), ++ "Binary {} matched to too many files", ++ binary_path.to_string_lossy() + ); + self.binary_debuginfo_map +- .insert(binary_files[0].clone(), debuginfo.as_ref().to_path_buf()); ++ .insert(binary_file, debuginfo.as_ref().to_path_buf()); + } + + Ok(()) +-- +2.43.0 + diff --git a/0109-upatch-build-adapt-common-fs-changes.patch b/0109-upatch-build-adapt-common-fs-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..b33490c3f9b4c9f5926437d113e4100fd05b7567 --- /dev/null +++ b/0109-upatch-build-adapt-common-fs-changes.patch @@ -0,0 +1,171 @@ +From 996090a92a147bde7d7b652ae3b5c3bcef496147 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 21 Apr 2025 20:02:38 +0800 +Subject: [PATCH] upatch-build: adapt common::fs changes + +Signed-off-by: renoseven +--- + upatch-build/src/dwarf.rs | 19 ++++++++----------- + upatch-build/src/elf/elfs.rs | 16 +++++++++++++--- + upatch-build/src/file_relation.rs | 7 ++----- + upatch-build/src/main.rs | 29 +++++++++++++---------------- + 4 files changed, 36 insertions(+), 35 deletions(-) + +diff --git a/upatch-build/src/dwarf.rs b/upatch-build/src/dwarf.rs +index c93cfa9..4934a7a 100644 +--- a/upatch-build/src/dwarf.rs ++++ b/upatch-build/src/dwarf.rs +@@ -12,11 +12,12 @@ use gimli::{ + constants::*, AttributeValue, DebugInfoUnitHeadersIter, DebuggingInformationEntry, DwLang, + Dwarf, EndianRcSlice, Endianity, Reader, RunTimeEndian, SectionId, Unit, UnitOffset, + }; +-use memmap2::Mmap; + use object::{Endianness, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget}; + use once_cell::sync::Lazy; + use regex::bytes::Regex; + ++use syscare_common::fs::{self, FileMmap}; ++ + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum ProducerType { + GnuAs, +@@ -67,22 +68,18 @@ impl std::fmt::Display for Producer { + } + + pub struct ProducerParser { +- _file: std::fs::File, +- mmap: Mmap, ++ mmap: FileMmap, + data_map: RefCell>>, + } + + impl ProducerParser { + pub fn open>(path: P) -> Result { +- let _file = std::fs::File::open(path)?; +- let mmap = unsafe { Mmap::map(&_file)? }; +- mmap.advise(memmap2::Advice::Random)?; +- +- Ok(Self { +- _file, +- mmap, ++ let parser = Self { ++ mmap: fs::mmap(&path) ++ .with_context(|| format!("Failed to mmap file {}", path.as_ref().display()))?, + data_map: RefCell::new(HashMap::new()), +- }) ++ }; ++ Ok(parser) + } + + pub fn parse(&self) -> Result + '_>> { +diff --git a/upatch-build/src/elf/elfs.rs b/upatch-build/src/elf/elfs.rs +index 25e462d..d6548a9 100644 +--- a/upatch-build/src/elf/elfs.rs ++++ b/upatch-build/src/elf/elfs.rs +@@ -25,8 +25,18 @@ use syscare_common::fs; + + use super::{Endian, Endianness}; + +-pub fn parse_file_kind>(file_path: P) -> Result { +- Ok(object::File::parse(fs::MappedFile::open(&file_path)?.as_bytes())?.kind()) ++fn parse_elf_kind>(file_path: P) -> Result { ++ let path = file_path.as_ref(); ++ let mmap = ++ fs::mmap(path).with_context(|| format!("Failed to mmap file {}", path.display()))?; ++ let file = object::File::parse(mmap.as_ref()) ++ .with_context(|| format!("Failed to parse {}", path.display()))?; ++ ++ Ok(file.kind()) ++} ++ ++pub fn elf_kind>(file_path: P) -> ObjectKind { ++ self::parse_elf_kind(file_path).unwrap_or(ObjectKind::Unknown) + } + + pub fn find_elf_files(directory: P, predicate: F) -> Result> +@@ -36,7 +46,7 @@ where + { + let mut elf_files = Vec::new(); + for file_path in fs::list_files(&directory, fs::TraverseOptions { recursive: true })? { +- if let Ok(obj_kind) = self::parse_file_kind(&file_path) { ++ if let Ok(obj_kind) = self::parse_elf_kind(&file_path) { + if predicate(&file_path, obj_kind) { + elf_files.push(file_path); + } +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 6aa6111..0ac03ca 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -89,7 +89,7 @@ impl FileRelation { + .filter(|file_path| file_path.ends_with(binary_path)) + .filter(|file_path| { + matches!( +- elf::parse_file_kind(file_path).unwrap_or(ObjectKind::Unknown), ++ elf::elf_kind(file_path), + ObjectKind::Executable | ObjectKind::Dynamic + ) + }); +@@ -228,10 +228,7 @@ impl FileRelation { + fs::list_files(&matched_dir, fs::TraverseOptions { recursive: true })? + .into_iter() + .filter(|file_path| { +- matches!( +- elf::parse_file_kind(file_path).unwrap_or(ObjectKind::Unknown), +- ObjectKind::Relocatable +- ) ++ matches!(elf::elf_kind(file_path), ObjectKind::Relocatable) + }); + object_files.extend(found_files); + } +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 56a8715..c791795 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -29,12 +29,7 @@ use indexmap::{IndexMap, IndexSet}; + use log::{debug, error, info, trace, warn, Level, LevelFilter, Record}; + use object::{write, Object, ObjectKind, ObjectSection, SectionKind}; + +-use syscare_common::{ +- concat_os, +- fs::{self, MappedFile}, +- os, +- process::Command, +-}; ++use syscare_common::{concat_os, fs, os, process::Command}; + + mod args; + mod build_root; +@@ -425,18 +420,20 @@ impl UpatchBuild { + write!(w, "{}", record.args()) + } + +- fn create_note, Q: AsRef>(debuginfo: P, output_file: Q) -> Result<()> { +- let debuginfo_file = MappedFile::open(&debuginfo)?; +- let object_file = object::File::parse(debuginfo_file.as_bytes()) +- .with_context(|| format!("Failed to parse {}", debuginfo.as_ref().display()))?; ++ fn create_note, Q: AsRef>( ++ debuginfo_file: P, ++ output_file: Q, ++ ) -> Result<()> { ++ let debuginfo_file = debuginfo_file.as_ref(); ++ let mmap = fs::mmap(debuginfo_file) ++ .with_context(|| format!("Failed to mmap file {}", debuginfo_file.display()))?; ++ let file = object::File::parse(mmap.as_ref()) ++ .with_context(|| format!("Failed to parse {}", debuginfo_file.display()))?; + +- let mut new_object = write::Object::new( +- object_file.format(), +- object_file.architecture(), +- object_file.endianness(), +- ); ++ let mut new_object = ++ write::Object::new(file.format(), file.architecture(), file.endianness()); + +- for section in object_file.sections() { ++ for section in file.sections() { + if section.kind() != SectionKind::Note { + continue; + } +-- +2.43.0 + diff --git a/0110-upatch-build-format-code.patch b/0110-upatch-build-format-code.patch new file mode 100644 index 0000000000000000000000000000000000000000..b16194952a85e093997015dd8d87a4e6b39e791d --- /dev/null +++ b/0110-upatch-build-format-code.patch @@ -0,0 +1,27 @@ +From c69ae01bb53458dd7fdfda0d0b3c5dba385f6539 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 22 Apr 2025 16:53:41 +0800 +Subject: [PATCH] upatch-build: format code + +Signed-off-by: renoseven +--- + upatch-build/src/elf/elfs.rs | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/upatch-build/src/elf/elfs.rs b/upatch-build/src/elf/elfs.rs +index d6548a9..8d9abb1 100644 +--- a/upatch-build/src/elf/elfs.rs ++++ b/upatch-build/src/elf/elfs.rs +@@ -27,8 +27,7 @@ use super::{Endian, Endianness}; + + fn parse_elf_kind>(file_path: P) -> Result { + let path = file_path.as_ref(); +- let mmap = +- fs::mmap(path).with_context(|| format!("Failed to mmap file {}", path.display()))?; ++ let mmap = fs::mmap(path).with_context(|| format!("Failed to mmap file {}", path.display()))?; + let file = object::File::parse(mmap.as_ref()) + .with_context(|| format!("Failed to parse {}", path.display()))?; + +-- +2.43.0 + diff --git a/0111-upatch-build-fix-upatch-id-parsing-failure.patch b/0111-upatch-build-fix-upatch-id-parsing-failure.patch new file mode 100644 index 0000000000000000000000000000000000000000..604ee90a2efe50443343dd49484235d306e04fa5 --- /dev/null +++ b/0111-upatch-build-fix-upatch-id-parsing-failure.patch @@ -0,0 +1,47 @@ +From d4db3bae1888725a24d308c2af0fe195f8f1249f Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 22 Apr 2025 16:02:36 +0800 +Subject: [PATCH] upatch-build: fix upatch id parsing failure + +1. fix upatch id parsing failure when some object + does not have '.symtab' section + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 0ac03ca..81cd974 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -173,15 +173,20 @@ impl FileRelation { + impl FileRelation { + fn parse_upatch_ids>(file_path: P) -> Result> { + let file_path = file_path.as_ref(); +- let elf_file = elf::read::Elf::parse(file_path).context("Failed to parse elf")?; +- let symbols = elf_file.symbols().context("Failed to read elf symbols")?; ++ let mmap = fs::mmap(file_path) ++ .with_context(|| format!("Failed to mmap {}", file_path.display()))?; ++ let file = object::File::parse(mmap.as_ref()) ++ .with_context(|| format!("Failed to parse {}", file_path.display()))?; + + let mut upatch_ids = IndexSet::new(); +- for symbol in symbols { +- let symbol_name = symbol.get_st_name(); +- if symbol_name.starts_with(UPATCH_ID_PREFIX) { +- upatch_ids.insert(symbol_name.to_os_string()); ++ for symbol in file.symbols() { ++ let name_slice = symbol.name_bytes().with_context(|| { ++ format!("Failed to parse symbol name, index={}", symbol.index().0) ++ })?; ++ if !name_slice.starts_with(UPATCH_ID_PREFIX.as_bytes()) { ++ continue; + } ++ upatch_ids.insert(OsStr::from_bytes(name_slice).to_os_string()); + } + + Ok(upatch_ids) +-- +2.43.0 + diff --git a/0112-upatch-build-rename-patch-note-object.patch b/0112-upatch-build-rename-patch-note-object.patch new file mode 100644 index 0000000000000000000000000000000000000000..b2f628b26d4b59ace18bace84da980133f302e61 --- /dev/null +++ b/0112-upatch-build-rename-patch-note-object.patch @@ -0,0 +1,29 @@ +From b4885afe364e28888d4e6e25b519b4f3760d6886 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 22 Apr 2025 17:42:17 +0800 +Subject: [PATCH] upatch-build: rename patch note object + +1. added patch name before note object name + e.g. notes.o -> xxxx-notes.o + +Signed-off-by: renoseven +--- + upatch-build/src/main.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index c791795..bb71793 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -259,7 +259,7 @@ impl UpatchBuild { + } + + debug!("- Creating patch notes"); +- let notes_object = temp_dir.join(NOTES_OBJECT_NAME); ++ let notes_object = temp_dir.join(concat_os!(patch_name, "-", NOTES_OBJECT_NAME)); + Self::create_note(&debuginfo_file, ¬es_object) + .context("Failed to create patch notes")?; + objects.push(notes_object); +-- +2.43.0 + diff --git a/0113-upatch-build-rewrite-object-collecting.patch b/0113-upatch-build-rewrite-object-collecting.patch new file mode 100644 index 0000000000000000000000000000000000000000..b5efb975e94669b132ce0b81b1544cc26e2c39b8 --- /dev/null +++ b/0113-upatch-build-rewrite-object-collecting.patch @@ -0,0 +1,171 @@ +From 72b7c9bcd51f2779c4c5d060ac7b61d4c35e5228 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 22 Apr 2025 15:25:35 +0800 +Subject: [PATCH] upatch-build: rewrite object collecting + +1. parsing object type & upatch id while collecting, + so that we don't need access object file many times +2. optimize upatch id to object path mapping + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 116 +++++++++++++++++++----------- + 1 file changed, 73 insertions(+), 43 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 81cd974..15aeb33 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -13,7 +13,8 @@ + */ + + use std::{ +- ffi::OsString, ++ ffi::{OsStr, OsString}, ++ os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, + }; + +@@ -21,8 +22,9 @@ use anyhow::{ensure, Context, Result}; + + use indexmap::{IndexMap, IndexSet}; + use log::warn; +-use object::ObjectKind; +-use syscare_common::{concat_os, ffi::OsStrExt, fs}; ++use object::{Object, ObjectKind, ObjectSymbol}; ++ ++use syscare_common::{concat_os, ffi::OsStrExt as _, fs}; + + use crate::elf; + +@@ -224,59 +226,87 @@ impl FileRelation { + let object_dir = object_dir.as_ref(); + let target_dir = target_dir.as_ref(); + +- let mut object_files = IndexSet::new(); ++ let mut file_id = 1usize; ++ let mut object_info = Vec::new(); + for match_result in fs::glob(object_dir) { + let matched_dir = match_result.with_context(|| { + format!("Cannot match object directory {}", object_dir.display()) + })?; +- let found_files = +- fs::list_files(&matched_dir, fs::TraverseOptions { recursive: true })? +- .into_iter() +- .filter(|file_path| { +- matches!(elf::elf_kind(file_path), ObjectKind::Relocatable) +- }); +- object_files.extend(found_files); ++ ++ let file_list = fs::list_files(&matched_dir, fs::TraverseOptions { recursive: true })?; ++ for object_file in file_list { ++ let mmap = fs::mmap(&object_file) ++ .with_context(|| format!("Failed to mmap {}", object_file.display()))?; ++ ++ // Try to parse file as elf ++ let file = match object::File::parse(mmap.as_ref()) { ++ Ok(f) => f, ++ Err(_) => continue, ++ }; ++ ++ // We only care about object file ++ if !matches!(file.kind(), ObjectKind::Relocatable) { ++ continue; ++ } ++ ++ // Copy object file to target directory ++ let obj_name = object_file.file_name().unwrap_or_default(); ++ let archive_file = ++ target_dir.join(concat_os!(format!("{:04}-", file_id), obj_name)); ++ fs::copy(&object_file, &archive_file)?; ++ file_id += 1; ++ ++ // Parse upatch id of the object ++ let upatch_ids = file ++ .symbols() ++ .filter_map(|symbol| { ++ let sym_name = symbol.name_bytes().unwrap_or_default(); ++ if !sym_name.starts_with(UPATCH_ID_PREFIX.as_bytes()) { ++ return None; ++ } ++ Some(OsStr::from_bytes(sym_name).to_os_string()) ++ }) ++ .collect::>(); ++ if upatch_ids.is_empty() { ++ warn!( ++ "Object {} does not contain upatch id", ++ object_file.display() ++ ); ++ continue; ++ } ++ ++ object_info.push((object_file, archive_file, upatch_ids)); ++ } + } + ensure!( +- !object_files.is_empty(), ++ !object_info.is_empty(), + "Cannot find any object in {}", + object_dir.display() + ); + +- let mut object_relations = Vec::with_capacity(object_files.len()); +- for (file_id, object_file) in object_files.into_iter().enumerate() { +- let object_archive = target_dir.join(concat_os!( +- format!("{:04}-", file_id), +- object_file.file_name().with_context(|| { +- format!("Failed to parse file name of {}", object_file.display()) +- })? +- )); +- fs::copy(&object_file, &object_archive)?; +- +- let upatch_ids = Self::parse_upatch_ids(&object_file).with_context(|| { +- format!("Failed to parse upatch id of {}", object_file.display()) +- })?; +- object_relations.push((object_file, object_archive, upatch_ids)); +- } ++ // We want subsequent objects to contain more identifiers. ++ object_info.sort_by(|(_, _, lhs), (_, _, rhs)| lhs.len().cmp(&rhs.len())); + +- let mut id_object_map = IndexMap::with_capacity(object_relations.len()); +- for (object_file, object_archive, object_ids) in &object_relations { +- if object_relations.iter().all(|(obj, _, ids)| { +- if (obj != object_file) && !ids.is_empty() && ids.is_subset(object_ids) { +- warn!("Skipped object {}", object_archive.display()); +- return false; +- } +- true +- }) { +- id_object_map.extend(object_ids.iter().map(|id| { +- ( +- id.to_os_string(), +- (object_file.clone(), object_archive.clone()), +- ) +- })); ++ let mut upatch_id_map = IndexMap::new(); ++ while let Some((object_file, archive_file, mut upatch_ids)) = object_info.pop() { ++ // Remove identifiers in other objects ++ for (_, _, ids) in &object_info { ++ upatch_ids.retain(|id| !ids.contains(id)); ++ } ++ // Current object is fully composed of other objects, we skip it. ++ if upatch_ids.is_empty() { ++ warn!("Skipped {}", object_file.display()); ++ } ++ for upatch_id in upatch_ids { ++ upatch_id_map.insert(upatch_id, (object_file.clone(), archive_file.clone())); + } + } ++ ensure!( ++ !upatch_id_map.is_empty(), ++ "Cannot find any upatch id in {}", ++ object_dir.display() ++ ); + +- Ok(id_object_map) ++ Ok(upatch_id_map) + } + } +-- +2.43.0 + diff --git a/0114-upatch-build-fix-unable-to-find-object-linker-error.patch b/0114-upatch-build-fix-unable-to-find-object-linker-error.patch new file mode 100644 index 0000000000000000000000000000000000000000..88096896172a9977b43fbd225b70fbc535301075 --- /dev/null +++ b/0114-upatch-build-fix-unable-to-find-object-linker-error.patch @@ -0,0 +1,76 @@ +From 448679c40d56fec8e8c53e1d41dcfb9b44077233 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 16:14:44 +0800 +Subject: [PATCH] upatch-build: fix unable to find object linker error + +Signed-off-by: renoseven +--- + upatch-build/src/main.rs | 39 ++++++++++++++------------------------- + 1 file changed, 14 insertions(+), 25 deletions(-) + +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index bb71793..b23d3b2 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -167,36 +167,25 @@ impl UpatchBuild { + Ok(()) + } + +- fn find_linker(&self, objects: &[PathBuf]) -> Result<&Path> { +- let mut producers = IndexSet::new(); +- for path in objects { +- let producer_parser = ProducerParser::open(path) +- .with_context(|| format!("Failed to open {}", path.display()))?; +- let producer_iter = producer_parser +- .parse() +- .with_context(|| format!("Failed to parse {}", path.display()))?; +- +- for parse_result in producer_iter { +- let producer = parse_result.context("Failed to parse object producer")?; +- if producer.is_assembler() { +- continue; +- } +- producers.insert(producer); +- } +- } ++ fn find_linker(&self, debuginfo: &Path) -> Result<&Path> { ++ let mut producers = ProducerParser::open(debuginfo) ++ .with_context(|| format!("Failed to open {}", debuginfo.display()))? ++ .parse() ++ .with_context(|| format!("Failed to parse {}", debuginfo.display()))? ++ .filter_map(|result| result.ok()) ++ .collect::>(); + producers.sort(); + +- let producer = producers.pop().context("No object producer")?; +- let compiler = self +- .compiler_map +- .get(&producer.kind) +- .with_context(|| format!("Cannot find {} compiler", producer.kind))?; ++ let compiler = producers ++ .pop() ++ .and_then(|producer| self.compiler_map.get(&producer.kind)) ++ .context("Cannot find linking compiler")?; + + Ok(compiler.linker.as_path()) + } + +- fn link_objects(&self, objects: &[PathBuf], output: &Path) -> Result<()> { +- let linker = self.find_linker(objects).context("Cannot find linker")?; ++ fn link_objects(&self, objects: &[PathBuf], debuginfo: &Path, output: &Path) -> Result<()> { ++ let linker = self.find_linker(debuginfo).context("Cannot find linker")?; + + Command::new(linker) + .args(["-r", "-o"]) +@@ -265,7 +254,7 @@ impl UpatchBuild { + objects.push(notes_object); + + debug!("- Linking patch"); +- self.link_objects(&objects, &output_file) ++ self.link_objects(&objects, &debuginfo_file, &output_file) + .context("Failed to link patch objects")?; + + debug!("- Resolving patch"); +-- +2.43.0 + diff --git a/0115-upatch-build-warning-on-finding-patched-object-failu.patch b/0115-upatch-build-warning-on-finding-patched-object-failu.patch new file mode 100644 index 0000000000000000000000000000000000000000..4b033bb9144b7383b8597779b7b719685abd0a14 --- /dev/null +++ b/0115-upatch-build-warning-on-finding-patched-object-failu.patch @@ -0,0 +1,56 @@ +From f5c4b148e5288ca3dbc779331e6184a9ba436661 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 11:14:30 +0800 +Subject: [PATCH] upatch-build: warning on finding patched object failure + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 32 +++++++++++++++++-------------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 15aeb33..c33983d 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -137,20 +137,24 @@ impl FileRelation { + let mut object_relation = IndexMap::new(); + + for upatch_id in upatch_ids { +- let (object_file, patched_object) = +- id_object_map.get(&upatch_id).with_context(|| { +- format!( +- "Cannot find patched object of {}", +- upatch_id.to_string_lossy() +- ) +- })?; +- let original_object = self +- .original_object_map +- .get(object_file) +- .map(|p| p.as_path()) +- .unwrap_or_else(|| Path::new(NON_EXIST_PATH)); +- +- object_relation.insert(patched_object.to_path_buf(), original_object.to_path_buf()); ++ match id_object_map.get(&upatch_id) { ++ Some((object_file, patched_object)) => { ++ let original_object = self ++ .original_object_map ++ .get(object_file) ++ .map(|p| p.as_path()) ++ .unwrap_or_else(|| Path::new(NON_EXIST_PATH)); ++ object_relation ++ .insert(patched_object.to_path_buf(), original_object.to_path_buf()); ++ } ++ None => { ++ warn!( ++ "Cannot find patched object of {} in target {}", ++ upatch_id.to_string_lossy(), ++ binary_file.display() ++ ); ++ } ++ } + } + object_relation.sort_keys(); + +-- +2.43.0 + diff --git a/0116-upatch-build-try-hardlink-before-copy-files.patch b/0116-upatch-build-try-hardlink-before-copy-files.patch new file mode 100644 index 0000000000000000000000000000000000000000..2944db325949734720a7690496ee15ecff396bc6 --- /dev/null +++ b/0116-upatch-build-try-hardlink-before-copy-files.patch @@ -0,0 +1,44 @@ +From c3b6011bd6a43c32281f43c11b0a14ae056ff535 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 12:48:20 +0800 +Subject: [PATCH] upatch-build: try hardlink before copy files + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 4 +++- + upatch-build/src/main.rs | 4 +++- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index c33983d..3c71e50 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -257,7 +257,9 @@ impl FileRelation { + let obj_name = object_file.file_name().unwrap_or_default(); + let archive_file = + target_dir.join(concat_os!(format!("{:04}-", file_id), obj_name)); +- fs::copy(&object_file, &archive_file)?; ++ if fs::hard_link(&object_file, &archive_file).is_err() { ++ fs::copy(&object_file, &archive_file)?; ++ } + file_id += 1; + + // Parse upatch id of the object +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index b23d3b2..45be298 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -210,7 +210,9 @@ impl UpatchBuild { + let output_file = output_dir.join(patch_name); + + fs::create_dir_all(&temp_dir)?; +- fs::copy(debuginfo, &debuginfo_file)?; ++ if fs::hard_link(debuginfo, &debuginfo_file).is_err() { ++ fs::copy(debuginfo, &debuginfo_file)?; ++ } + fs::set_permissions(&debuginfo_file, Permissions::from_mode(0o644))?; + + debug!("- Resolving debuginfo"); +-- +2.43.0 + diff --git a/0117-upatch-build-optimize-code.patch b/0117-upatch-build-optimize-code.patch new file mode 100644 index 0000000000000000000000000000000000000000..b4b4b71d417044b84f9b3c15c0eecf7cf39cf6b2 --- /dev/null +++ b/0117-upatch-build-optimize-code.patch @@ -0,0 +1,88 @@ +From fb4201b9ffeefb785266a1f097efbb142959d4df Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 16:19:46 +0800 +Subject: [PATCH] upatch-build: optimize code + +1. remove fn format_log() +2. change fn create_note() param types + +Signed-off-by: renoseven +--- + upatch-build/src/main.rs | 31 ++++++++----------------------- + 1 file changed, 8 insertions(+), 23 deletions(-) + +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 45be298..6b48b78 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -22,11 +22,9 @@ use std::{ + }; + + use anyhow::{bail, ensure, Context, Result}; +-use flexi_logger::{ +- DeferredNow, Duplicate, FileSpec, LogSpecification, Logger, LoggerHandle, WriteMode, +-}; ++use flexi_logger::{Duplicate, FileSpec, LogSpecification, Logger, LoggerHandle, WriteMode}; + use indexmap::{IndexMap, IndexSet}; +-use log::{debug, error, info, trace, warn, Level, LevelFilter, Record}; ++use log::{debug, error, info, trace, warn, Level, LevelFilter}; + use object::{write, Object, ObjectKind, ObjectSection, SectionKind}; + + use syscare_common::{concat_os, fs, os, process::Command}; +@@ -98,7 +96,7 @@ impl UpatchBuild { + let logger = Logger::with(log_spec) + .log_to_file(file_spec) + .duplicate_to_stdout(Duplicate::from(log_level_stdout)) +- .format(Self::format_log) ++ .format(|w, _, record| write!(w, "{}", record.args())) + .write_mode(WriteMode::Direct) + .start() + .context("Failed to initialize logger")?; +@@ -403,23 +401,11 @@ impl UpatchBuild { + + /* Tool functions */ + impl UpatchBuild { +- fn format_log( +- w: &mut dyn std::io::Write, +- _now: &mut DeferredNow, +- record: &Record, +- ) -> std::io::Result<()> { +- write!(w, "{}", record.args()) +- } +- +- fn create_note, Q: AsRef>( +- debuginfo_file: P, +- output_file: Q, +- ) -> Result<()> { +- let debuginfo_file = debuginfo_file.as_ref(); +- let mmap = fs::mmap(debuginfo_file) +- .with_context(|| format!("Failed to mmap file {}", debuginfo_file.display()))?; ++ fn create_note(debuginfo: &Path, output: &Path) -> Result<()> { ++ let mmap = fs::mmap(debuginfo) ++ .with_context(|| format!("Failed to mmap file {}", debuginfo.display()))?; + let file = object::File::parse(mmap.as_ref()) +- .with_context(|| format!("Failed to parse {}", debuginfo_file.display()))?; ++ .with_context(|| format!("Failed to parse {}", debuginfo.display()))?; + + let mut new_object = + write::Object::new(file.format(), file.architecture(), file.endianness()); +@@ -428,7 +414,6 @@ impl UpatchBuild { + if section.kind() != SectionKind::Note { + continue; + } +- + let section_name = section.name().context("Failed to get section name")?; + let section_data = section.data().context("Failed to get section data")?; + let section_id = +@@ -442,7 +427,7 @@ impl UpatchBuild { + let contents = new_object + .write() + .context("Failed to serialize note object")?; +- fs::write(output_file, contents)?; ++ fs::write(output, contents)?; + + Ok(()) + } +-- +2.43.0 + diff --git a/0118-upatch-build-support-text-section-offset.patch b/0118-upatch-build-support-text-section-offset.patch new file mode 100644 index 0000000000000000000000000000000000000000..1350de99c91bf96b0486c42540213ac0e199b1e1 --- /dev/null +++ b/0118-upatch-build-support-text-section-offset.patch @@ -0,0 +1,101 @@ +From ea5db116bc4e72ad8ff91afc88d6f7b9abcd5d92 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 12 May 2025 11:23:52 +0800 +Subject: [PATCH] upatch-build: support text section offset + +Signed-off-by: renoseven +--- + upatch-build/src/main.rs | 49 ++++++++++++++++++++++++++++++++-------- + 1 file changed, 39 insertions(+), 10 deletions(-) + +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 6b48b78..29d68f6 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -194,6 +194,23 @@ impl UpatchBuild { + .exit_ok() + } + ++ fn parse_text_offset(&self, binary: &Path) -> Result { ++ const TEXT_SECTION_NAME: &str = ".text"; ++ ++ let mmap = fs::mmap(binary)?; ++ let file = object::File::parse(mmap.as_ref())?; ++ let text_offset = file ++ .section_by_name(TEXT_SECTION_NAME) ++ .map(|section| { ++ let address = section.address(); ++ let offset = section.file_range().map(|(start, _)| start).unwrap_or(0); ++ address - offset ++ }) ++ .unwrap_or(0); ++ ++ Ok(text_offset) ++ } ++ + fn build_patch(&self, patch_name: &OsStr, binary: &Path, debuginfo: &Path) -> Result<()> { + const NOTES_OBJECT_NAME: &str = "notes.o"; + +@@ -222,6 +239,9 @@ impl UpatchBuild { + .binary_objects(binary) + .with_context(|| format!("Failed to find objects of {}", binary.display()))?; + ++ let text_offset = self ++ .parse_text_offset(binary) ++ .with_context(|| format!("Failed to parse {} text section offset", binary.display()))?; + for (patched_object, original_object) in binary_objects { + debug!( + "* {}", +@@ -230,13 +250,19 @@ impl UpatchBuild { + .unwrap_or(patched_object.as_os_str()) + .to_string_lossy() + ); +- Self::create_diff_objs(original_object, patched_object, &debuginfo_file, &temp_dir) +- .with_context(|| { +- format!( +- "Failed to create diff objects for {}", +- patch_name.to_string_lossy() +- ) +- })?; ++ Self::create_diff_objs( ++ original_object, ++ patched_object, ++ &debuginfo_file, ++ text_offset, ++ &temp_dir, ++ ) ++ .with_context(|| { ++ format!( ++ "Failed to create diff objects for {}", ++ patch_name.to_string_lossy() ++ ) ++ })?; + } + + debug!("- Collecting changes"); +@@ -436,6 +462,7 @@ impl UpatchBuild { + original_object: &Path, + patched_object: &Path, + debuginfo: &Path, ++ text_offset: u64, + output_dir: &Path, + ) -> Result<()> { + let ouput_name = original_object.file_name().with_context(|| { +@@ -453,9 +480,11 @@ impl UpatchBuild { + .arg("-p") + .arg(patched_object) + .arg("-r") +- .arg(debuginfo) +- .arg("-o") +- .arg(output_file); ++ .arg(debuginfo); ++ if text_offset > 0 { ++ command.arg("-t").arg(text_offset.to_string()); ++ } ++ command.arg("-o").arg(output_file); + + command.stdout(Level::Trace).run_with_output()?.exit_ok() + } +-- +2.43.0 + diff --git a/0119-syscare-build-fix-clippy-warnings.patch b/0119-syscare-build-fix-clippy-warnings.patch new file mode 100644 index 0000000000000000000000000000000000000000..7c018101d153230699611f78f7ffc242df41b0a5 --- /dev/null +++ b/0119-syscare-build-fix-clippy-warnings.patch @@ -0,0 +1,26 @@ +From d840052b40dc5fba5e73671b103412e6554a2e9f Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 8 May 2025 16:23:05 +0800 +Subject: [PATCH] syscare-build: fix clippy warnings + +Signed-off-by: renoseven +--- + syscare-build/src/package/rpm/spec_writer.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/syscare-build/src/package/rpm/spec_writer.rs b/syscare-build/src/package/rpm/spec_writer.rs +index 97218c2..0059aee 100644 +--- a/syscare-build/src/package/rpm/spec_writer.rs ++++ b/syscare-build/src/package/rpm/spec_writer.rs +@@ -121,7 +121,7 @@ impl PackageSpecWriter for RpmSpecWriter { + let mut lines_to_write = BTreeSet::new(); + let last_tag_id = source_tags + .into_iter() +- .last() ++ .next_back() + .map(|tag| tag.id) + .unwrap_or_default(); + +-- +2.43.0 + diff --git a/0120-project-update-Cargo.lock.patch b/0120-project-update-Cargo.lock.patch new file mode 100644 index 0000000000000000000000000000000000000000..ee2c6a950ca6681320caea76aaa24aa424f55da9 --- /dev/null +++ b/0120-project-update-Cargo.lock.patch @@ -0,0 +1,26 @@ +From 62117683ff750b1b9209ca835a92606defad1198 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 17 Apr 2025 20:05:38 +0800 +Subject: [PATCH] project: update Cargo.lock + +Signed-off-by: renoseven +--- + Cargo.lock | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/Cargo.lock b/Cargo.lock +index 6ae9f36..b4798de 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -1315,6 +1315,8 @@ dependencies = [ + "memmap2", + "memoffset", + "object", ++ "once_cell", ++ "regex", + "serde", + "syscare-common", + "typed-arena", +-- +2.43.0 + diff --git a/0121-project-change-compile-options.patch b/0121-project-change-compile-options.patch new file mode 100644 index 0000000000000000000000000000000000000000..5262e1c500a6d05913d368ec0c46cd8fd1b1a4dc --- /dev/null +++ b/0121-project-change-compile-options.patch @@ -0,0 +1,56 @@ +From 119b79e891ff2337b625db822294d278b93f22ad Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 23 Apr 2025 10:53:19 +0800 +Subject: [PATCH] project: change compile options + +Rust compiler options: +1. add "-C strip=symbols" +2. add "-C force-frame-pointers=true" + +Cargo.toml: +1. change opt-level from 's' to 3 +2. remove "debug-assertions = false" +3. remove "overflow-checks = false" + +Signed-off-by: renoseven +--- + CMakeLists.txt | 6 ++++-- + Cargo.toml | 4 +--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index d8f3258..9d8b305 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -69,10 +69,12 @@ list(APPEND PROJECT_C_BUILD_FLAGS + ) + list(APPEND PROJECT_RUST_FLAGS + --cfg unsound_local_offset +- -C relocation_model=pic + -D warnings + -C link-arg=-s +- -C overflow_checks ++ -C strip=symbols ++ -C overflow_checks=yes ++ -C relocation_model=pic ++ -C force-frame-pointers=yes + -W rust_2021_incompatible_closure_captures + ) + +diff --git a/Cargo.toml b/Cargo.toml +index 07c796f..b22335f 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -10,8 +10,6 @@ members = [ + ] + + [profile.release] +-opt-level = 's' ++opt-level = 3 + debug = true + rpath = false +-debug-assertions = false +-overflow-checks = false +-- +2.43.0 + diff --git a/0122-syscare-build-fix-build-kernel-module-patch-failure.patch b/0122-syscare-build-fix-build-kernel-module-patch-failure.patch new file mode 100644 index 0000000000000000000000000000000000000000..212d613ebd964ffe8e2de0f433aabb0b81f443cc --- /dev/null +++ b/0122-syscare-build-fix-build-kernel-module-patch-failure.patch @@ -0,0 +1,60 @@ +From bb3dcae9a342b35283862aed1e5c3c4dd9f744f3 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sun, 27 Apr 2025 10:36:44 +0800 +Subject: [PATCH] syscare-build: fix build kernel module patch failure + +add following environment variables: +1. OOT_MODULE=yes +2. USERMODBUILDDIR=%{oot_source_dir} + +Signed-off-by: renoseven +--- + .../src/patch/kernel_patch/kpatch_builder.rs | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +index 202611d..7d44dd4 100644 +--- a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs ++++ b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +@@ -12,7 +12,7 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::path::{Path, PathBuf}; ++use std::path::PathBuf; + + use anyhow::{Context, Result}; + use log::{info, Level}; +@@ -267,14 +267,17 @@ impl KernelPatchBuilder { + cmd_args + } + +- fn parse_kbuild_cmd_envs(&self, build_root: &Path) -> CommandEnvs { ++ fn parse_kbuild_cmd_envs(&self, kbuild_params: &KBuildParameters) -> CommandEnvs { + let mut cmd_envs = CommandEnvs::new(); + cmd_envs +- .env("CACHEDIR", build_root) ++ .env("CACHEDIR", &kbuild_params.patch_build_root) + .env("NO_PROFILING_CALLS", "yes") + .env("DISABLE_AFTER_LOAD", "yes") + .env("KEEP_JUMP_LABEL", "yes"); +- ++ if let Some(oot_source_dir) = &kbuild_params.oot_source_dir { ++ cmd_envs.env("OOT_MODULE", "yes"); ++ cmd_envs.env("USERMODBUILDDIR", oot_source_dir); ++ } + cmd_envs + } + +@@ -289,7 +292,7 @@ impl KernelPatchBuilder { + for mut kbuild_entity in kpatch_entities { + Command::new(KPATCH_BUILD_BIN) + .args(self.parse_kbuild_cmd_args(kbuild_params, &kbuild_entity)) +- .envs(self.parse_kbuild_cmd_envs(&kbuild_params.patch_build_root)) ++ .envs(self.parse_kbuild_cmd_envs(kbuild_params)) + .stdout(Level::Debug) + .run_with_output()? + .exit_ok()?; +-- +2.43.0 + diff --git a/0123-syscare-build-kmod-patch-entity-name-uses-shorter-uu.patch b/0123-syscare-build-kmod-patch-entity-name-uses-shorter-uu.patch new file mode 100644 index 0000000000000000000000000000000000000000..f3ce6ce20a574c49befdbafec5559d9ffc7113a7 --- /dev/null +++ b/0123-syscare-build-kmod-patch-entity-name-uses-shorter-uu.patch @@ -0,0 +1,107 @@ +From d652b14eeecac7a65caa1f611634081606c4229c Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 16 May 2025 12:50:16 +0800 +Subject: [PATCH] syscare-build: kmod patch entity name uses shorter uuid + +Signed-off-by: renoseven +--- + .../src/patch/kernel_patch/kpatch_builder.rs | 67 +++++++++++-------- + 1 file changed, 38 insertions(+), 29 deletions(-) + +diff --git a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +index 7d44dd4..18c59ef 100644 +--- a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs ++++ b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +@@ -173,19 +173,23 @@ impl KernelPatchBuilder { + match &kbuild_params.oot_source_dir { + // Kernel patch + None => { +- let entity_uuid = Uuid::new_v4(); +- let entity_target = VMLINUX_FILE_NAME; +- let entity_name = format!("{}-{}", entity_target, entity_uuid); +- ++ let uuid = Uuid::new_v4(); ++ let uuid_short = uuid ++ .to_string() ++ .split_once('-') ++ .map(|s| s.0.to_string()) ++ .expect("Unexpected kernel patch uuid"); ++ ++ let patch_entity = PatchEntity { ++ uuid, ++ patch_name: concat_os!(VMLINUX_FILE_NAME, "-", uuid_short), ++ patch_target: VMLINUX_FILE_NAME.into(), ++ checksum: String::new(), ++ }; + entity_list.push(KernelPatchEntity { + source_dir: kbuild_params.kernel_source_dir.to_owned(), + module_path: None, +- patch_entity: PatchEntity { +- uuid: entity_uuid, +- patch_name: entity_name.into(), +- patch_target: entity_target.into(), +- checksum: String::default(), +- }, ++ patch_entity, + }); + } + // Kernel module patch +@@ -193,31 +197,36 @@ impl KernelPatchBuilder { + let module_list = + KernelPatchHelper::find_kernel_modules(&kbuild_params.pkg_build_dir) + .context("Failed to find any kernel module")?; +- let module_suffix = format!(".{}", KPATCH_SUFFIX); +- + for module_path in module_list { ++ let uuid = Uuid::new_v4(); ++ let uuid_short = uuid ++ .to_string() ++ .split_once('-') ++ .map(|s| s.0.to_string()) ++ .expect("Unexpected kernel module uuid"); + let file_name = module_path + .file_name() +- .context("Cannot get patch file name")?; +- let module_name = file_name +- .strip_suffix(&module_suffix) +- .context("Unexpected patch suffix")? +- .to_string_lossy() +- .replace(['.', '-'], "_"); +- +- let entity_uuid = Uuid::new_v4(); +- let entitiy_name = format!("{}-{}", module_name, entity_uuid); +- let entity_target = file_name.to_os_string(); +- ++ .expect("Unexpected kernel module file name"); ++ let module_name = { ++ let mut module_file = module_path.clone(); ++ module_file.set_extension(""); ++ ++ module_file ++ .file_name() ++ .expect("Unexpected kernel module name") ++ .replace(['.', '-'], "_") ++ }; ++ ++ let patch_entity = PatchEntity { ++ uuid, ++ patch_name: concat_os!(module_name, "-", uuid_short), ++ patch_target: file_name.into(), ++ checksum: String::new(), ++ }; + entity_list.push(KernelPatchEntity { + source_dir: oot_source_dir.to_owned(), + module_path: Some(module_path), +- patch_entity: PatchEntity { +- uuid: entity_uuid, +- patch_name: entitiy_name.into(), +- patch_target: entity_target.into(), +- checksum: String::default(), +- }, ++ patch_entity, + }); + } + } +-- +2.43.0 + diff --git a/0124-syscare-build-optimize-kpatch-build-arguments.patch b/0124-syscare-build-optimize-kpatch-build-arguments.patch new file mode 100644 index 0000000000000000000000000000000000000000..2a568b402ba2ed81252f74a744056759caf24df2 --- /dev/null +++ b/0124-syscare-build-optimize-kpatch-build-arguments.patch @@ -0,0 +1,168 @@ +From 8c8d355d023202b6568ff763e9d98c3ab152bf13 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 16 May 2025 15:34:01 +0800 +Subject: [PATCH] syscare-build: optimize kpatch-build arguments + +Signed-off-by: renoseven +--- + .../src/patch/kernel_patch/kpatch_builder.rs | 58 ++++++++++--------- + 1 file changed, 31 insertions(+), 27 deletions(-) + +diff --git a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +index 18c59ef..930e6fb 100644 +--- a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs ++++ b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +@@ -43,7 +43,7 @@ struct KBuildParameters { + patch_build_root: PathBuf, + patch_output_dir: PathBuf, + kernel_source_dir: PathBuf, +- oot_source_dir: Option, ++ kmod_source_dir: Option, + config_file: PathBuf, + vmlinux_file: PathBuf, + patch_name: String, +@@ -61,7 +61,7 @@ struct KBuildParameters { + + struct KernelPatchEntity { + source_dir: PathBuf, +- module_path: Option, ++ kmod_path: Option, + patch_entity: PatchEntity, + } + +@@ -97,8 +97,8 @@ impl KernelPatchBuilder { + }, + ) + .context("Cannot find kernel source directory")?; ++ let kmod_source_dir = oot_module_entry.map(|entry| entry.build_source.clone()); + let kernel_debug_dir = &build_params.build_root.package.debuginfo; +- let oot_source_dir = oot_module_entry.map(|build_entry| build_entry.build_source.clone()); + + /* + * Kernel config: +@@ -147,7 +147,7 @@ impl KernelPatchBuilder { + patch_build_root, + patch_output_dir, + kernel_source_dir, +- oot_source_dir, ++ kmod_source_dir, + config_file, + vmlinux_file, + patch_name: build_params.patch_name.to_owned(), +@@ -170,7 +170,7 @@ impl KernelPatchBuilder { + ) -> Result> { + let mut entity_list = Vec::new(); + +- match &kbuild_params.oot_source_dir { ++ match &kbuild_params.kmod_source_dir { + // Kernel patch + None => { + let uuid = Uuid::new_v4(); +@@ -188,30 +188,30 @@ impl KernelPatchBuilder { + }; + entity_list.push(KernelPatchEntity { + source_dir: kbuild_params.kernel_source_dir.to_owned(), +- module_path: None, ++ kmod_path: None, + patch_entity, + }); + } + // Kernel module patch +- Some(oot_source_dir) => { +- let module_list = ++ Some(kmod_source_dir) => { ++ let kmod_list = + KernelPatchHelper::find_kernel_modules(&kbuild_params.pkg_build_dir) + .context("Failed to find any kernel module")?; +- for module_path in module_list { ++ for kmod_path in kmod_list { + let uuid = Uuid::new_v4(); + let uuid_short = uuid + .to_string() + .split_once('-') + .map(|s| s.0.to_string()) + .expect("Unexpected kernel module uuid"); +- let file_name = module_path ++ let file_name = kmod_path + .file_name() + .expect("Unexpected kernel module file name"); + let module_name = { +- let mut module_file = module_path.clone(); +- module_file.set_extension(""); ++ let mut kmod_file = kmod_path.clone(); ++ kmod_file.set_extension(""); + +- module_file ++ kmod_file + .file_name() + .expect("Unexpected kernel module name") + .replace(['.', '-'], "_") +@@ -224,8 +224,8 @@ impl KernelPatchBuilder { + checksum: String::new(), + }; + entity_list.push(KernelPatchEntity { +- source_dir: oot_source_dir.to_owned(), +- module_path: Some(module_path), ++ source_dir: kmod_source_dir.to_owned(), ++ kmod_path: Some(kmod_path), + patch_entity, + }); + } +@@ -250,20 +250,20 @@ impl KernelPatchBuilder { + .arg("--config") + .arg(&kbuild_params.config_file) + .arg("--vmlinux") +- .arg(&kbuild_params.vmlinux_file) +- .arg("--jobs") +- .arg(kbuild_params.jobs.to_string()) +- .arg("--output") +- .arg(&kbuild_params.patch_output_dir) +- .arg("--non-replace"); ++ .arg(&kbuild_params.vmlinux_file); + +- if let Some(oot_module) = &kbuild_entity.module_path { +- cmd_args.arg("--oot-module").arg(oot_module); ++ if let Some(kmod_path) = &kbuild_entity.kmod_path { ++ cmd_args.arg("--oot-module").arg(kmod_path); + cmd_args + .arg("--oot-module-src") + .arg(&kbuild_entity.source_dir); + } +- cmd_args.args(kbuild_params.patch_files.iter().map(|patch| &patch.path)); ++ ++ cmd_args ++ .arg("--jobs") ++ .arg(kbuild_params.jobs.to_string()) ++ .arg("--non-replace") ++ .arg("--skip-cleanup"); + + if kbuild_params.skip_compiler_check { + cmd_args.arg("--skip-compiler-check"); +@@ -271,7 +271,11 @@ impl KernelPatchBuilder { + if kbuild_params.verbose { + cmd_args.arg("--debug"); + } +- cmd_args.arg("--skip-cleanup"); ++ ++ cmd_args ++ .arg("--output") ++ .arg(&kbuild_params.patch_output_dir) ++ .args(kbuild_params.patch_files.iter().map(|f| &f.path)); + + cmd_args + } +@@ -283,9 +287,9 @@ impl KernelPatchBuilder { + .env("NO_PROFILING_CALLS", "yes") + .env("DISABLE_AFTER_LOAD", "yes") + .env("KEEP_JUMP_LABEL", "yes"); +- if let Some(oot_source_dir) = &kbuild_params.oot_source_dir { ++ if let Some(kmod_source_dir) = &kbuild_params.kmod_source_dir { + cmd_envs.env("OOT_MODULE", "yes"); +- cmd_envs.env("USERMODBUILDDIR", oot_source_dir); ++ cmd_envs.env("USERMODBUILDDIR", kmod_source_dir); + } + cmd_envs + } +-- +2.43.0 + diff --git a/0125-upatch-diff-sync-from-25.03-to-24.03-LTS.patch b/0125-upatch-diff-sync-from-25.03-to-24.03-LTS.patch new file mode 100644 index 0000000000000000000000000000000000000000..37fea1afe9860a6d7476742a5de637802514c91e --- /dev/null +++ b/0125-upatch-diff-sync-from-25.03-to-24.03-LTS.patch @@ -0,0 +1,268 @@ +From 8006398aaab567664062ed6edab5388b976b8c3f Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Wed, 28 May 2025 11:12:32 +0800 +Subject: [PATCH] upatch-diff: sync from 25.03 to 24.03-LTS + +Signed-off-by: Jvle +--- + upatch-diff/create-diff-object.c | 28 +++++++++++++++++++++- + upatch-diff/elf-common.c | 41 +++++++++++++++++++++++++++++--- + upatch-diff/elf-common.h | 2 ++ + upatch-diff/elf-correlate.c | 6 +++++ + upatch-diff/elf-insn.c | 8 +++++++ + upatch-diff/elf-insn.h | 5 ++++ + upatch-diff/upatch-elf.c | 10 ++++++++ + upatch-diff/upatch-elf.h | 1 + + 8 files changed, 97 insertions(+), 4 deletions(-) + +diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c +index 9e4504e..886d8b2 100644 +--- a/upatch-diff/create-diff-object.c ++++ b/upatch-diff/create-diff-object.c +@@ -434,7 +434,8 @@ static void replace_section_syms(struct upatch_elf *uelf) + !is_text_section(sym->sec) && + (rela->type == R_X86_64_32S || + rela->type == R_X86_64_32 || +- rela->type == R_AARCH64_ABS64) && ++ rela->type == R_AARCH64_ABS64 || ++ rela->type == R_RISCV_64) && + rela->addend == (long)sym->sec->sh.sh_size && + end == (long)sym->sec->sh.sh_size) { + ERROR("Relocation refer end of data sections."); +@@ -590,6 +591,12 @@ static void include_symbol(struct symbol *sym) + if ((sym->status != SAME) || (sym->type == STT_SECTION)) { + include_section(sym->sec); + } ++#ifdef __riscv ++ /* .L symbols not exist in EXE. If they are included, so are their sections. */ ++ else if (sym->sec && !sym->sec->include && !strncmp(sym->name, ".L", 2)) { ++ include_section(sym->sec); ++ } ++#endif + } + + static void include_section(struct section *sec) +@@ -749,6 +756,23 @@ static void verify_patchability(struct upatch_elf *uelf) + } + } + ++/* ++ * These types are for linker optimization and memory layout. ++ * They have no associated symbols and their names are empty ++ * string which would mismatch running-elf symbols in later ++ * lookup_relf(). Drop these useless items now. ++ */ ++static void rv_drop_useless_rela(struct section *relasec) ++{ ++ struct rela *rela, *saferela; ++ list_for_each_entry_safe(rela, saferela, &relasec->relas, list) ++ if (rela->type == R_RISCV_RELAX || rela->type == R_RISCV_ALIGN) { ++ list_del(&rela->list); ++ memset(rela, 0, sizeof(*rela)); ++ free(rela); ++ } ++} ++ + static void migrate_included_elements(struct upatch_elf *uelf_patched, + struct upatch_elf *uelf_out) + { +@@ -776,6 +800,8 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, + if (sec->sym && !sec->sym->include) { + sec->sym = NULL; // break link to non-included section symbol + } ++ } else if (uelf_patched->arch == RISCV64) { ++ rv_drop_useless_rela(sec); + } + } + +diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c +index c5d8f61..22d12ca 100644 +--- a/upatch-diff/elf-common.c ++++ b/upatch-diff/elf-common.c +@@ -26,6 +26,35 @@ + + #include "elf-common.h" + ++#ifdef __riscv ++/* ++ * .L local symbols are named as ".L" + "class prefix" + "number". ++ * The numbers are volatile due to code change. ++ * Compare class prefix(composed of letters) only. ++ */ ++static int mangled_strcmp_dot_L(char *str1, char *str2) ++{ ++ if (!*str2 || strncmp(str2, ".L", 2)) { ++ return 1; ++ } ++ ++ /* RISCV_FAKE_LABEL_NAME matched exactly */ ++ if (!strcmp(str1, ".L0 ") || !strcmp(str2, ".L0 ")) { ++ return strcmp(str1, str2); ++ } ++ ++ char *p = str1 + 2; ++ char *q = str2 + 2; ++ while (*p < '0' || *p > '9') p++; ++ while (*q < '0' || *q > '9') q++; ++ if ((p - str1 != q - str2) || strncmp(str1, str2, (size_t)(p - str1))) { ++ return 1; ++ } ++ ++ return 0; ++} ++#endif ++ + static bool is_dynamic_debug_symbol(struct symbol *sym) + { + static const char *SEC_NAMES[] = { +@@ -122,6 +151,12 @@ int mangled_strcmp(char *str1, char *str2) + return strcmp(str1, str2); + } + ++#ifdef __riscv ++ if (!strncmp(str1, ".L", 2)) { ++ return mangled_strcmp_dot_L(str1, str2); ++ } ++#endif ++ + while (*str1 == *str2) { + if (!*str2) { + return 0; +@@ -180,7 +215,6 @@ int offset_of_string(struct list_head *list, char *name) + + ALLOC_LINK(string, list); + string->name = name; +- + return index; + } + +@@ -192,16 +226,17 @@ bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf) + return false; + case X86_64: + return false; ++ case RISCV64: ++ return false; + default: + ERROR("unsupported arch"); + } +- + return false; + } + + bool is_mapping_symbol(struct upatch_elf *uelf, struct symbol *sym) + { +- if (uelf->arch != AARCH64) { ++ if ((uelf->arch != AARCH64) && (uelf->arch != RISCV64)) { + return false; + } + if (sym->name && sym->name[0] == '$' && +diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h +index 3cebd32..f252cb9 100644 +--- a/upatch-diff/elf-common.h ++++ b/upatch-diff/elf-common.h +@@ -278,6 +278,8 @@ static inline unsigned int absolute_rela_type(struct upatch_elf *uelf) + return R_AARCH64_ABS64; + case X86_64: + return R_X86_64_64; ++ case RISCV64: ++ return R_RISCV_64; + default: + ERROR("unsupported arch"); + } +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index dcf3cfa..8e2f765 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -83,6 +83,12 @@ void upatch_correlate_symbols(struct upatch_elf *uelf_source, + continue; + } + ++ /* on RISC-V: .L symbols should not change section */ ++ if (uelf_source->arch == RISCV64 && !strncmp(sym_orig->name, ".L", 2) && ++ sym_orig->sec && sym_orig->sec->twin != sym_patched->sec) { ++ continue; ++ } ++ + if (is_mapping_symbol(uelf_source, sym_orig)) { + continue; + } +diff --git a/upatch-diff/elf-insn.c b/upatch-diff/elf-insn.c +index 6fabd88..466a00c 100644 +--- a/upatch-diff/elf-insn.c ++++ b/upatch-diff/elf-insn.c +@@ -67,6 +67,8 @@ long rela_target_offset(struct upatch_elf *uelf, struct section *relasec, struct + long add_off; + + switch (uelf->arch) { ++ case RISCV64: ++ /* fall through */ + case AARCH64: + add_off = 0; + break; +@@ -106,6 +108,12 @@ unsigned int insn_length(struct upatch_elf *uelf, void *addr) + insn_init(&decoded_insn, addr, 1); + insn_get_length(&decoded_insn); + return decoded_insn.length; ++ case RISCV64: ++ /* LSB 2 bits distinguish insn size. Now only RV32, RVC supported. */ ++ if ((*(char *)addr & 0x3) == 0x3) { ++ return RISCV64_INSN_LEN_4; ++ } ++ return RISCV64_INSN_LEN_2; + default: + ERROR("unsupported arch"); + } +diff --git a/upatch-diff/elf-insn.h b/upatch-diff/elf-insn.h +index ac48ad0..fdbc2e3 100644 +--- a/upatch-diff/elf-insn.h ++++ b/upatch-diff/elf-insn.h +@@ -32,8 +32,13 @@ struct section; + struct rela; + struct insn; + ++// arm + #define ARM64_INSTR_LEN 4 + ++// riscv ++#define RISCV64_INSN_LEN_4 4 ++#define RISCV64_INSN_LEN_2 2 ++ + void rela_insn(const struct section *sec, const struct rela *rela, struct insn *insn); + + /* +diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c +index c21b6ec..331504b 100644 +--- a/upatch-diff/upatch-elf.c ++++ b/upatch-diff/upatch-elf.c +@@ -321,6 +321,16 @@ void uelf_open(struct upatch_elf *uelf, const char *name) + case EM_X86_64: + uelf->arch = X86_64; + break; ++ case EM_RISCV: ++ /* ++ | Val | Macros | Description | ++ | 1 | ELFCLASS32 | riscv32 | ++ | 2 | ELFCLASS64 | riscv64 | ++ */ ++ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { ++ uelf->arch = RISCV64; ++ } ++ break; + default: + ERROR("Unsupported architecture"); + } +diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h +index 858cfa9..b34c1af 100644 +--- a/upatch-diff/upatch-elf.h ++++ b/upatch-diff/upatch-elf.h +@@ -41,6 +41,7 @@ struct symbol; + enum architecture { + X86_64 = 0x1 << 0, + AARCH64 = 0x1 << 1, ++ RISCV64 = 0x1 << 2, + }; + + enum data_source { +-- +2.43.0 + diff --git a/0042-riscv64-sync-riscv64-support-from-master-to-2503.patch b/0126-upatch-manage-sync-from-25.03-to-24.03-LTS.patch similarity index 71% rename from 0042-riscv64-sync-riscv64-support-from-master-to-2503.patch rename to 0126-upatch-manage-sync-from-25.03-to-24.03-LTS.patch index 148d896d6ac97df5ff4b3540eb4377f3ef89cf16..9a3cb8a03af8b7f83cdfac3b688fdfc4e5dd68d2 100644 --- a/0042-riscv64-sync-riscv64-support-from-master-to-2503.patch +++ b/0126-upatch-manage-sync-from-25.03-to-24.03-LTS.patch @@ -1,364 +1,25 @@ -From 0261efdbb718dd4850232940f9f9b7f156b19675 Mon Sep 17 00:00:00 2001 +From 240c0077a2b4b8a0898d29b2e68e595dbdc1dffc Mon Sep 17 00:00:00 2001 From: Jvle -Date: Mon, 3 Mar 2025 10:14:04 +0800 -Subject: [PATCH] riscv64: sync riscv64 support from master to 2503 - -sync from 64e3f03f467be4756d546f68ecbfce5ec93e0d42 +Date: Wed, 28 May 2025 11:13:19 +0800 +Subject: [PATCH] upatch-manage: sync from 25.03 to 24.03-LTS Signed-off-by: Jvle --- - README.md | 11 ++ - upatch-diff/create-diff-object.c | 28 ++- - upatch-diff/elf-common.c | 41 +++- - upatch-diff/elf-common.h | 2 + - upatch-diff/elf-compare.c | 4 +- - upatch-diff/elf-insn.c | 8 + - upatch-diff/elf-insn.h | 5 + - upatch-diff/running-elf.c | 1 - - upatch-diff/upatch-elf.c | 14 +- - upatch-diff/upatch-elf.h | 1 + upatch-manage/arch/riscv64/insn.h | 123 ++++++++++++ upatch-manage/arch/riscv64/process.h | 28 +++ - upatch-manage/arch/riscv64/ptrace.c | 207 ++++++++++++++++++++ - upatch-manage/arch/riscv64/relocation.c | 242 ++++++++++++++++++++++++ + upatch-manage/arch/riscv64/ptrace.c | 219 +++++++++++++++++++++ + upatch-manage/arch/riscv64/relocation.c | 246 ++++++++++++++++++++++++ upatch-manage/arch/riscv64/resolve.c | 154 +++++++++++++++ upatch-manage/upatch-patch.c | 22 +++ upatch-manage/upatch-ptrace.c | 4 + upatch-manage/upatch-ptrace.h | 8 + - 18 files changed, 894 insertions(+), 9 deletions(-) + 8 files changed, 804 insertions(+) create mode 100644 upatch-manage/arch/riscv64/insn.h create mode 100644 upatch-manage/arch/riscv64/process.h create mode 100644 upatch-manage/arch/riscv64/ptrace.c create mode 100644 upatch-manage/arch/riscv64/relocation.c create mode 100644 upatch-manage/arch/riscv64/resolve.c -diff --git a/README.md b/README.md -index 15d4d48..b34a6c1 100644 ---- a/README.md -+++ b/README.md -@@ -33,6 +33,8 @@ - - * 编译并安装 - -+ PS: 直接编译在应用补丁的时候会显示缺少依赖,建议通过rpm包安装 -+ - ```bash - git clone https://gitee.com/openeuler/syscare.git - cd syscare -@@ -41,6 +43,11 @@ - cmake -DCMAKE_INSTALL_PREFIX=/usr -DKERNEL_VERSION=$(uname -r) .. - make - make install -+ -+ mkdir -p /usr/lib/syscare/patches -+ systemctl daemon-reload -+ systemctl enable syscare -+ systemctl start syscare - ``` - - * 离线编译 -@@ -63,7 +70,11 @@ - ```bash - rpm -ivh syscare-*.rpm - ``` -+或者 - -+``` -+dnf install syscare-*.rpm -+``` - - - ## 使用说明 -diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c -index 1c14b7a..a06599e 100644 ---- a/upatch-diff/create-diff-object.c -+++ b/upatch-diff/create-diff-object.c -@@ -614,7 +614,8 @@ static void replace_section_syms(struct upatch_elf *uelf) - !is_text_section(sym->sec) && - (rela->type == R_X86_64_32S || - rela->type == R_X86_64_32 || -- rela->type == R_AARCH64_ABS64) && -+ rela->type == R_AARCH64_ABS64 || -+ rela->type == R_RISCV_64) && - rela->addend == (long)sym->sec->sh.sh_size && - end == (long)sym->sec->sh.sh_size) { - ERROR("Relocation refer end of data sections."); -@@ -759,6 +760,12 @@ static void include_symbol(struct symbol *sym) - if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME)) { - include_section(sym->sec); - } -+#ifdef __riscv -+ /* .L symbols not exist in EXE. If they are included, so are their sections. */ -+ else if (sym->sec && !sym->sec->include && !strncmp(sym->name, ".L", 2)) { -+ include_section(sym->sec); -+ } -+#endif - } - - static void include_section(struct section *sec) -@@ -949,6 +956,23 @@ static void verify_patchability(struct upatch_elf *uelf) - } - } - -+/* -+ * These types are for linker optimization and memory layout. -+ * They have no associated symbols and their names are empty -+ * string which would mismatch running-elf symbols in later -+ * lookup_relf(). Drop these useless items now. -+ */ -+static void rv_drop_useless_rela(struct section *relasec) -+{ -+ struct rela *rela, *saferela; -+ list_for_each_entry_safe(rela, saferela, &relasec->relas, list) -+ if (rela->type == R_RISCV_RELAX || rela->type == R_RISCV_ALIGN) { -+ list_del(&rela->list); -+ memset(rela, 0, sizeof(*rela)); -+ free(rela); -+ } -+} -+ - static void migrate_included_elements(struct upatch_elf *uelf_patched, - struct upatch_elf *uelf_out) - { -@@ -975,6 +999,8 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, - - if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include) { - sec->secsym = NULL; // break link to non-included section symbol -+ } else if (uelf_patched->arch == RISCV64) { -+ rv_drop_useless_rela(sec); - } - } - -diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c -index 5aa35f1..3b62a5e 100644 ---- a/upatch-diff/elf-common.c -+++ b/upatch-diff/elf-common.c -@@ -26,6 +26,35 @@ - - #include "elf-common.h" - -+#ifdef __riscv -+/* -+ * .L local symbols are named as ".L" + "class prefix" + "number". -+ * The numbers are volatile due to code change. -+ * Compare class prefix(composed of letters) only. -+ */ -+static int mangled_strcmp_dot_L(char *str1, char *str2) -+{ -+ if (!*str2 || strncmp(str2, ".L", 2)) { -+ return 1; -+ } -+ -+ /* RISCV_FAKE_LABEL_NAME matched exactly */ -+ if (!strcmp(str1, ".L0 ") || !strcmp(str2, ".L0 ")) { -+ return strcmp(str1, str2); -+ } -+ -+ char *p = str1 + 2; -+ char *q = str2 + 2; -+ while (*p < '0' || *p > '9') p++; -+ while (*q < '0' || *q > '9') q++; -+ if ((p - str1 != q - str2) || strncmp(str1, str2, p - str1)) { -+ return 1; -+ } -+ -+ return 0; -+} -+#endif -+ - int mangled_strcmp(char *str1, char *str2) - { - /* -@@ -35,6 +64,12 @@ int mangled_strcmp(char *str1, char *str2) - return strcmp(str1, str2); - } - -+#ifdef __riscv -+ if (!strncmp(str1, ".L", 2)) { -+ return mangled_strcmp_dot_L(str1, str2); -+ } -+#endif -+ - while (*str1 == *str2) { - if (!*str2) { - return 0; -@@ -100,7 +135,6 @@ int offset_of_string(struct list_head *list, char *name) - - ALLOC_LINK(string, list); - string->name = name; -- - return index; - } - -@@ -112,16 +146,17 @@ bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf) - return false; - case X86_64: - return false; -+ case RISCV64: -+ return false; - default: - ERROR("unsupported arch"); - } -- - return false; - } - - bool is_mapping_symbol(struct upatch_elf *uelf, struct symbol *sym) - { -- if (uelf->arch != AARCH64) { -+ if ((uelf->arch != AARCH64) && (uelf->arch != RISCV64)) { - return false; - } - if (sym->name && sym->name[0] == '$' && -diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h -index bc2c1ac..0127314 100644 ---- a/upatch-diff/elf-common.h -+++ b/upatch-diff/elf-common.h -@@ -224,6 +224,8 @@ static inline unsigned int absolute_rela_type(struct upatch_elf *uelf) - return R_AARCH64_ABS64; - case X86_64: - return R_X86_64_64; -+ case RISCV64: -+ return R_RISCV_64; - default: - ERROR("unsupported arch"); - } -diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c -index 7ca6845..da31522 100644 ---- a/upatch-diff/elf-compare.c -+++ b/upatch-diff/elf-compare.c -@@ -175,7 +175,6 @@ bool upatch_handle_redis_line(const char *symname) - !strncmp(symname, "RedisModule__Assert", 19)) { - return true; - } -- - return false; - } - -@@ -362,6 +361,9 @@ static bool line_macro_change_only(struct upatch_elf *uelf, struct section *sec) - return line_macro_change_only_aarch64(uelf, sec); - case X86_64: - return line_macro_change_only_x86_64(uelf, sec); -+ case RISCV64: -+ /* TODO: not support */ -+ break; - default: - ERROR("unsupported arch"); - } -diff --git a/upatch-diff/elf-insn.c b/upatch-diff/elf-insn.c -index 8fcfc39..bb913dd 100644 ---- a/upatch-diff/elf-insn.c -+++ b/upatch-diff/elf-insn.c -@@ -66,6 +66,8 @@ long rela_target_offset(struct upatch_elf *uelf, struct section *relasec, struct - long add_off; - - switch (uelf->arch) { -+ case RISCV64: -+ /* fall through */ - case AARCH64: - add_off = 0; - break; -@@ -105,6 +107,12 @@ unsigned int insn_length(struct upatch_elf *uelf, void *addr) - insn_init(&decoded_insn, addr, 1); - insn_get_length(&decoded_insn); - return decoded_insn.length; -+ case RISCV64: -+ /* LSB 2 bits distinguish insn size. Now only RV32, RVC supported. */ -+ if ((*(char *)addr & 0x3) == 0x3) { -+ return RISCV64_INSN_LEN_4; -+ } -+ return RISCV64_INSN_LEN_2; - default: - ERROR("unsupported arch"); - } -diff --git a/upatch-diff/elf-insn.h b/upatch-diff/elf-insn.h -index f64ebe1..96d2225 100644 ---- a/upatch-diff/elf-insn.h -+++ b/upatch-diff/elf-insn.h -@@ -29,8 +29,13 @@ - #include "asm/insn.h" - #include "upatch-elf.h" - -+// arm - #define ARM64_INSTR_LEN 4 - -+// riscv -+#define RISCV64_INSN_LEN_4 4 -+#define RISCV64_INSN_LEN_2 2 -+ - void rela_insn(const struct section *sec, const struct rela *rela, struct insn *insn); - - /* -diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c -index d9c1d6f..61c5057 100644 ---- a/upatch-diff/running-elf.c -+++ b/upatch-diff/running-elf.c -@@ -86,7 +86,6 @@ void relf_init(char *elf_name, struct running_elf *relf) - if (!data) { - ERROR("elf_getdata with error %s", elf_errmsg(0)); - } -- - relf->obj_nr = (int)(shdr.sh_size / shdr.sh_entsize); - relf->obj_syms = calloc((size_t)relf->obj_nr, sizeof(struct debug_symbol)); - if (!relf->obj_syms) { -diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c -index a51f008..06a3123 100644 ---- a/upatch-diff/upatch-elf.c -+++ b/upatch-diff/upatch-elf.c -@@ -89,7 +89,7 @@ static void create_section_list(struct upatch_elf *uelf) - } - - log_debug("ndx %02d, data %p, size %zu, name %s\n", -- sec->index, sec->data->d_buf, sec->data->d_size, sec->name); -+ sec->index, sec->data->d_buf, sec->data->d_size, sec->name); - } - - if (elf_nextscn(uelf->elf, scn)) { -@@ -111,7 +111,6 @@ static void create_symbol_list(struct upatch_elf *uelf) - if (!symtab) { - ERROR("can't find symbol table"); - } -- - symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); - - log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr); -@@ -130,7 +129,6 @@ static void create_symbol_list(struct upatch_elf *uelf) - if (!sym->name) { - ERROR("elf_strptr with error %s", elf_errmsg(0)); - } -- - sym->type = GELF_ST_TYPE(sym->sym.st_info); - sym->bind = GELF_ST_BIND(sym->sym.st_info); - -@@ -368,6 +366,16 @@ void upatch_elf_open(struct upatch_elf *uelf, const char *name) - case EM_X86_64: - uelf->arch = X86_64; - break; -+ case EM_RISCV: -+ /* -+ | Val | Macros | Description | -+ | 1 | ELFCLASS32 | riscv32 | -+ | 2 | ELFCLASS64 | riscv64 | -+ */ -+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { -+ uelf->arch = RISCV64; -+ } -+ break; - default: - ERROR("unsupported architecture here"); - } -diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h -index 030feca..7597e8e 100644 ---- a/upatch-diff/upatch-elf.h -+++ b/upatch-diff/upatch-elf.h -@@ -126,6 +126,7 @@ struct symbol { - enum architecture { - X86_64 = 0x1 << 0, - AARCH64 = 0x1 << 1, -+ RISCV64 = 0x1 << 2, - }; - - struct upatch_elf { diff --git a/upatch-manage/arch/riscv64/insn.h b/upatch-manage/arch/riscv64/insn.h new file mode 100644 index 0000000..8ab4daf @@ -524,10 +185,10 @@ index 0000000..91926fa +#endif diff --git a/upatch-manage/arch/riscv64/ptrace.c b/upatch-manage/arch/riscv64/ptrace.c new file mode 100644 -index 0000000..c5691c9 +index 0000000..2ccdc9e --- /dev/null +++ b/upatch-manage/arch/riscv64/ptrace.c -@@ -0,0 +1,207 @@ +@@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage @@ -552,6 +213,7 @@ index 0000000..c5691c9 +#include +#include +#include ++#include + +#include "upatch-ptrace.h" + @@ -630,19 +292,29 @@ index 0000000..c5691c9 + const void *data) +{ + struct user_regs_struct orig_regs, regs; -+ unsigned char orig_code[codelen]; ++ if (!codelen) { ++ log_error("Invalid codelen\n"); ++ return -1; ++ } ++ unsigned char *orig_code = (unsigned char *)malloc(sizeof(*orig_code) * codelen); ++ if (orig_code == NULL) { ++ log_error("Malloc orig_code failed\n"); ++ return -1; ++ } + long ret; + struct upatch_process *proc = pctx->proc; + unsigned long libc_base = proc->libc_base; + + ret = read_gregs(pctx->pid, &orig_regs); + if (ret < 0) { ++ free(orig_code); + return -1; + } + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); ++ free(orig_code); + return -1; + } + ret = upatch_process_mem_write(proc, (const unsigned long *)code, libc_base, @@ -683,6 +355,7 @@ index 0000000..c5691c9 +poke_back: + upatch_process_mem_write(proc, (unsigned long *)orig_code, libc_base, + codelen); ++ free(orig_code); + return ret; +} + @@ -737,10 +410,10 @@ index 0000000..c5691c9 +} diff --git a/upatch-manage/arch/riscv64/relocation.c b/upatch-manage/arch/riscv64/relocation.c new file mode 100644 -index 0000000..6c28bf8 +index 0000000..ba82599 --- /dev/null +++ b/upatch-manage/arch/riscv64/relocation.c -@@ -0,0 +1,242 @@ +@@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage @@ -794,6 +467,9 @@ index 0000000..6c28bf8 + * The patch is a .o file, has only static relocations, all symbols + * have been resolved with our jump table act as got/plt. + */ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wconversion" ++#pragma GCC diagnostic ignored "-Wsign-conversion" +int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, + unsigned int relsec) +{ @@ -983,9 +659,10 @@ index 0000000..6c28bf8 + (int)GELF_R_TYPE(rel[i].r_info), val); + return -ENOEXEC; +} ++#pragma GCC diagnostic pop diff --git a/upatch-manage/arch/riscv64/resolve.c b/upatch-manage/arch/riscv64/resolve.c new file mode 100644 -index 0000000..127fcc0 +index 0000000..0b4aa8b --- /dev/null +++ b/upatch-manage/arch/riscv64/resolve.c @@ -0,0 +1,154 @@ @@ -1056,9 +733,9 @@ index 0000000..127fcc0 + index * sizeof(struct upatch_jmp_table_entry)); +} + -+static unsigned long setup_got_table(struct upatch_elf *uelf, -+ unsigned long jmp_addr, -+ unsigned long tls_addr) ++unsigned long setup_got_table(struct upatch_elf *uelf, ++ unsigned long jmp_addr, ++ unsigned long tls_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; @@ -1079,7 +756,7 @@ index 0000000..127fcc0 +} + +unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, -+ unsigned long r_type, unsigned long addr) ++ unsigned long r_type __attribute__((unused)), unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; @@ -1144,10 +821,10 @@ index 0000000..127fcc0 + return elf_addr; +} diff --git a/upatch-manage/upatch-patch.c b/upatch-manage/upatch-patch.c -index 4a1e568..c57ae74 100644 +index 4b1b1be..fd2c5f2 100644 --- a/upatch-manage/upatch-patch.c +++ b/upatch-manage/upatch-patch.c -@@ -476,6 +476,23 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, +@@ -475,6 +475,23 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, upatch_func->addr = upatch_funcs_addr[i].addr; upatch_func->addr.old_addr += uelf->relf->load_bias; @@ -1160,8 +837,8 @@ index 4a1e568..c57ae74 100644 + * new_insn field is only 8 bytes. We can only jump into + * +-2G ranges. Here do the check. + */ -+ long offset = upatch_func->addr.new_addr - upatch_func->addr.old_addr; -+ if (offset >= RISCV_MAX_JUMP_RANGE || offset < -RISCV_MAX_JUMP_RANGE) { ++ long riscv_offset = (long)(upatch_func->addr.new_addr - upatch_func->addr.old_addr); ++ if (riscv_offset >= RISCV_MAX_JUMP_RANGE || riscv_offset < -RISCV_MAX_JUMP_RANGE) { + log_error("new_addr=%lx old_addr=%lx exceed +-2G range\n", + upatch_func->addr.new_addr, upatch_func->addr.old_addr); + goto out; @@ -1171,7 +848,7 @@ index 4a1e568..c57ae74 100644 ret = upatch_process_mem_read(obj->proc, upatch_func->addr.old_addr, &upatch_func->old_insn, get_origin_insn_len()); if (ret) { -@@ -484,7 +501,12 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, +@@ -483,7 +500,12 @@ static int complete_info(struct upatch_elf *uelf, struct object_file *obj, goto out; } @@ -1226,5 +903,5 @@ index 8f36565..f2503cf 100644 #endif -- -2.34.1 +2.43.0 diff --git a/0127-CMake-fix-Werror-cast-align-for-riscv64.patch b/0127-CMake-fix-Werror-cast-align-for-riscv64.patch new file mode 100644 index 0000000000000000000000000000000000000000..a43d992abfae010a29c36f39e23806863edb43ba --- /dev/null +++ b/0127-CMake-fix-Werror-cast-align-for-riscv64.patch @@ -0,0 +1,42 @@ +From ba6fee40405c9baabcd878a68501646e5ecff143 Mon Sep 17 00:00:00 2001 +From: Jvle +Date: Wed, 23 Apr 2025 17:08:09 +0800 +Subject: [PATCH] CMake: fix -Werror=cast-align for riscv64 + +Signed-off-by: Jvle +--- + CMakeLists.txt | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 9d8b305..95a917f 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -62,11 +62,23 @@ list(APPEND PROJECT_C_BUILD_FLAGS + -DBUILD_VERSION="${PROJECT_BUILD_VERSION}" -D_FORTIFY_SOURCE=2 + -Wtrampolines -Wformat=2 -Wstrict-prototypes -Wdate-time + -Wstack-usage=8192 -Wfloat-equal -Wswitch-default +- -Wshadow -Wconversion -Wcast-qual -Wcast-align -Wunused -Wundef ++ -Wshadow -Wconversion -Wcast-qual -Wunused -Wundef + -funsigned-char -fstack-protector-all -fpic -fpie -ftrapv + -fstack-check -freg-struct-return -fno-canonical-system-headers + -fno-common -pipe -fdebug-prefix-map=old=new + ) ++ ++# The -Werror=cast-align compiler flag causes issues on riscv64 GCC, ++# while the same operations do not error on aarch64. This appears to be ++# a compiler-specific problem. Temporarily disable this option as a ++# workaround since applying fixes would require intrusive code changes ++# across multiple files. ++if(NOT ARCH STREQUAL "riscv64") ++ list(APPEND PROJECT_C_BUILD_FLAGS ++ -Wcast-align ++ ) ++endif() ++ + list(APPEND PROJECT_RUST_FLAGS + --cfg unsound_local_offset + -D warnings +-- +2.43.0 + diff --git a/0128-upatch-build-fix-compile-failure-for-lower-rust-vers.patch b/0128-upatch-build-fix-compile-failure-for-lower-rust-vers.patch new file mode 100644 index 0000000000000000000000000000000000000000..0799757eab0256393f3ffb1f51ea276b2bfb56b9 --- /dev/null +++ b/0128-upatch-build-fix-compile-failure-for-lower-rust-vers.patch @@ -0,0 +1,26 @@ +From 56106014c3928e0cad1fa94ef2b2fd3abf5bec90 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 12 Jun 2025 18:45:00 +0800 +Subject: [PATCH] upatch-build: fix compile failure for lower rust version + +Signed-off-by: renoseven +--- + upatch-build/src/dwarf.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upatch-build/src/dwarf.rs b/upatch-build/src/dwarf.rs +index 4934a7a..fe0821c 100644 +--- a/upatch-build/src/dwarf.rs ++++ b/upatch-build/src/dwarf.rs +@@ -112,7 +112,7 @@ impl ProducerParser { + let section_name = section_id.name(); + let section = match file.section_by_name(section_name) { + Some(section) => section, +- None => return Ok(EndianRcSlice::new(Rc::default(), endian)), ++ None => return Ok(EndianRcSlice::new(Rc::new([]), endian)), + }; + + let mut section_data = section +-- +2.43.0 + diff --git a/0038-metadata-viewer-add-component.patch b/0129-metadata-viewer-add-component.patch similarity index 98% rename from 0038-metadata-viewer-add-component.patch rename to 0129-metadata-viewer-add-component.patch index 95b92d8db932882296482b86f02efe05279cabbe..1a755017b894575828c8e79af192f8806751797e 100644 --- a/0038-metadata-viewer-add-component.patch +++ b/0129-metadata-viewer-add-component.patch @@ -1,4 +1,4 @@ -From d903d529a5d503d688aa41276240408e747c3366 Mon Sep 17 00:00:00 2001 +From d8db0142d5f0240d3e33a5707bd878a9a81c3d89 Mon Sep 17 00:00:00 2001 From: renoseven Date: Wed, 12 Feb 2025 14:40:32 +0800 Subject: [PATCH] metadata-viewer: add component @@ -18,10 +18,10 @@ Signed-off-by: renoseven create mode 100644 metadata-viewer/src/main.rs diff --git a/CMakeLists.txt b/CMakeLists.txt -index ca9fdfe..573752a 100644 +index 95a917f..f0a46c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -171,6 +171,7 @@ install( +@@ -190,6 +190,7 @@ install( ${RUST_OUTPUT_DIR}/upatch-helper ${RUST_OUTPUT_DIR}/upatch-cc ${RUST_OUTPUT_DIR}/upatch-c++ @@ -30,7 +30,7 @@ index ca9fdfe..573752a 100644 OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE diff --git a/Cargo.toml b/Cargo.toml -index 07c796f..52a4255 100644 +index b22335f..1373560 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ members = [ @@ -280,5 +280,5 @@ index 0000000..8097788 + Ok(()) +} -- -2.34.1 +2.43.0 diff --git a/0039-metadata-generator-add-component.patch b/0130-metadata-generator-add-component.patch similarity index 99% rename from 0039-metadata-generator-add-component.patch rename to 0130-metadata-generator-add-component.patch index cdf4b0d8d581edccf3db592f0275851d4880f472..15921533cf8cac6d3ea42acf770ec69130793539 100644 --- a/0039-metadata-generator-add-component.patch +++ b/0130-metadata-generator-add-component.patch @@ -1,4 +1,4 @@ -From cce391fe5268723a890fdf9de78144ec046bd8d2 Mon Sep 17 00:00:00 2001 +From ffbf8957e442867baa21495e705a064d8bd8b8d3 Mon Sep 17 00:00:00 2001 From: renoseven Date: Thu, 13 Feb 2025 11:27:26 +0800 Subject: [PATCH] metadata-generator: add component @@ -18,10 +18,10 @@ Signed-off-by: renoseven create mode 100644 metadata-generator/src/main.rs diff --git a/CMakeLists.txt b/CMakeLists.txt -index 573752a..2f658b9 100644 +index f0a46c9..e1a9051 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -172,6 +172,7 @@ install( +@@ -191,6 +191,7 @@ install( ${RUST_OUTPUT_DIR}/upatch-cc ${RUST_OUTPUT_DIR}/upatch-c++ ${RUST_OUTPUT_DIR}/metadata-viewer @@ -30,7 +30,7 @@ index 573752a..2f658b9 100644 OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE diff --git a/Cargo.toml b/Cargo.toml -index 52a4255..a514352 100644 +index 1373560..eb7ea7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ @@ -529,5 +529,5 @@ index 0000000..b37a5e3 + Ok(()) +} -- -2.34.1 +2.43.0 diff --git a/0040-project-update-Cargo.lock.patch b/0131-project-update-Cargo.lock.patch similarity index 88% rename from 0040-project-update-Cargo.lock.patch rename to 0131-project-update-Cargo.lock.patch index a3ea709a6caeb3d690fa77d554e830f273471d22..2d28dda466c4274fe19230e327844653b248ba73 100644 --- a/0040-project-update-Cargo.lock.patch +++ b/0131-project-update-Cargo.lock.patch @@ -1,4 +1,4 @@ -From be50605c9b97f43af34e5bfdd284927514e9cdd4 Mon Sep 17 00:00:00 2001 +From 83762aa78f04dfa1842e239d4dc9a1d70d746938 Mon Sep 17 00:00:00 2001 From: renoseven Date: Thu, 13 Feb 2025 11:38:02 +0800 Subject: [PATCH] project: update Cargo.lock @@ -9,7 +9,7 @@ Signed-off-by: renoseven 1 file changed, 25 insertions(+) diff --git a/Cargo.lock b/Cargo.lock -index 6ae9f36..ce0177e 100644 +index b4798de..b1598cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,6 +679,31 @@ dependencies = [ @@ -45,5 +45,5 @@ index 6ae9f36..ce0177e 100644 name = "miniz_oxide" version = "0.5.4" -- -2.34.1 +2.43.0 diff --git a/0132-upatch-build-rename-keep-line-macros-to-override-lin.patch b/0132-upatch-build-rename-keep-line-macros-to-override-lin.patch new file mode 100644 index 0000000000000000000000000000000000000000..a6fd2146294fe1878bc7b8516f1016d769b539a9 --- /dev/null +++ b/0132-upatch-build-rename-keep-line-macros-to-override-lin.patch @@ -0,0 +1,53 @@ +From f7f2d2299609a9f5a11976161ad8e1c7bb73ff94 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 14 Jun 2025 18:40:44 +0800 +Subject: [PATCH] upatch-build: rename '--keep-line-macros' to + '--override-line-macros' + +Signed-off-by: renoseven +--- + upatch-build/src/args.rs | 4 ++-- + upatch-build/src/main.rs | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/upatch-build/src/args.rs b/upatch-build/src/args.rs +index f840095..e3fc45b 100644 +--- a/upatch-build/src/args.rs ++++ b/upatch-build/src/args.rs +@@ -95,9 +95,9 @@ pub struct Arguments { + #[clap(short, long, default_value = DEFAULT_OUTPUT_DIR)] + pub output_dir: PathBuf, + +- /// Keep line macro unchanged ++ /// Override line macros to a fixed value + #[clap(long)] +- pub keep_line_macros: bool, ++ pub override_line_macros: bool, + + /// Skip compiler version check (not recommended) + #[clap(long)] +diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs +index 29d68f6..2ae47fe 100644 +--- a/upatch-build/src/main.rs ++++ b/upatch-build/src/main.rs +@@ -352,7 +352,7 @@ impl UpatchBuild { + .with_context(|| format!("Failed to clean {}", project))?; + } + +- if !self.args.keep_line_macros { ++ if self.args.override_line_macros { + info!("Overriding line macros"); + project + .override_line_macros() +@@ -395,7 +395,7 @@ impl UpatchBuild { + .apply_patches() + .with_context(|| format!("Failed to patch {}", project))?; + +- if !self.args.keep_line_macros { ++ if self.args.override_line_macros { + info!("Overriding line macros"); + project + .override_line_macros() +-- +2.43.0 + diff --git a/0133-syscare-build-rename-keep-line-macros-to-override-li.patch b/0133-syscare-build-rename-keep-line-macros-to-override-li.patch new file mode 100644 index 0000000000000000000000000000000000000000..50a52687a66ec12ffc46bd46200aa838182b7196 --- /dev/null +++ b/0133-syscare-build-rename-keep-line-macros-to-override-li.patch @@ -0,0 +1,92 @@ +From 3aac147a5b0a2ae44257474520fc3119f030eefc Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 14 Jun 2025 18:40:11 +0800 +Subject: [PATCH] syscare-build: rename '--keep-line-macros' to + '--override-line-macros' + +Signed-off-by: renoseven +--- + syscare-build/src/args.rs | 4 ++-- + syscare-build/src/build_params.rs | 2 +- + syscare-build/src/main.rs | 2 +- + syscare-build/src/patch/user_patch/upatch_builder.rs | 8 ++++---- + 4 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/syscare-build/src/args.rs b/syscare-build/src/args.rs +index 4a51715..cba93f5 100644 +--- a/syscare-build/src/args.rs ++++ b/syscare-build/src/args.rs +@@ -97,9 +97,9 @@ pub struct Arguments { + #[clap(short, long, default_value = &DEFAULT_BUILD_JOBS)] + pub jobs: usize, + +- /// Keep line macro unchanged (userspace patch only) ++ /// Override line macros to a fixed value (userspace patch only) + #[clap(long)] +- pub keep_line_macros: bool, ++ pub override_line_macros: bool, + + /// Skip compiler version check (not recommended) + #[clap(long)] +diff --git a/syscare-build/src/build_params.rs b/syscare-build/src/build_params.rs +index 29dd960..40d17dc 100644 +--- a/syscare-build/src/build_params.rs ++++ b/syscare-build/src/build_params.rs +@@ -39,7 +39,7 @@ pub struct BuildParameters { + pub patch_description: String, + pub patch_files: Vec, + pub jobs: usize, +- pub keep_line_macros: bool, ++ pub override_line_macros: bool, + pub skip_compiler_check: bool, + pub skip_cleanup: bool, + pub verbose: bool, +diff --git a/syscare-build/src/main.rs b/syscare-build/src/main.rs +index 51f9050..27754af 100644 +--- a/syscare-build/src/main.rs ++++ b/syscare-build/src/main.rs +@@ -305,7 +305,7 @@ impl SyscareBuild { + patch_type, + patch_files, + jobs: self.args.jobs, +- keep_line_macros: self.args.keep_line_macros, ++ override_line_macros: self.args.override_line_macros, + skip_compiler_check: self.args.skip_compiler_check, + skip_cleanup: self.args.skip_cleanup, + verbose: self.args.verbose, +diff --git a/syscare-build/src/patch/user_patch/upatch_builder.rs b/syscare-build/src/patch/user_patch/upatch_builder.rs +index a69373c..b54d11f 100644 +--- a/syscare-build/src/patch/user_patch/upatch_builder.rs ++++ b/syscare-build/src/patch/user_patch/upatch_builder.rs +@@ -54,7 +54,7 @@ struct UBuildParameters { + patch_target: PackageInfo, + patch_description: String, + patch_files: Vec, +- keep_line_macros: bool, ++ override_line_macros: bool, + skip_compiler_check: bool, + verbose: bool, + } +@@ -182,7 +182,7 @@ impl UserPatchBuilder { + patch_target: build_params.build_entry.target_pkg.to_owned(), + patch_description: build_params.patch_description.to_owned(), + patch_files: build_params.patch_files.to_owned(), +- keep_line_macros: build_params.keep_line_macros, ++ override_line_macros: build_params.override_line_macros, + skip_compiler_check: build_params.skip_compiler_check, + verbose: build_params.verbose, + }; +@@ -225,8 +225,8 @@ impl UserPatchBuilder { + .arg("--output-dir") + .arg(&ubuild_params.patch_output_dir); + +- if ubuild_params.keep_line_macros { +- cmd_args.arg("--keep-line-macros"); ++ if ubuild_params.override_line_macros { ++ cmd_args.arg("--override-line-macros"); + } + if ubuild_params.skip_compiler_check { + cmd_args.arg("--skip-compiler-check"); +-- +2.43.0 + diff --git a/0134-common-update-component.patch b/0134-common-update-component.patch new file mode 100644 index 0000000000000000000000000000000000000000..669e46aa1e6e7c602267b0759b0510bffd03b8d8 --- /dev/null +++ b/0134-common-update-component.patch @@ -0,0 +1,3784 @@ +From ba2be6caf6062d753e91fb4e4f762effdad928a2 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 29 May 2025 18:12:32 +0800 +Subject: [PATCH] common: update component + +1. rewrite common::os +2. rewrite common::process +3. rewrite common::util::digest +4. remove common::io +5. move fs::fs_impl::xattr to fs::xattr +6. impl fs::flock::flock_exists() +7. prevent file creation in fs::mmap() +8. support kernel module management + +Signed-off-by: renoseven +--- + syscare-common/Cargo.toml | 3 +- + syscare-common/src/fs/flock.rs | 130 ++++-- + syscare-common/src/fs/fs_impl.rs | 97 +---- + syscare-common/src/fs/glob.rs | 4 +- + syscare-common/src/fs/mmap.rs | 87 ++-- + syscare-common/src/fs/mod.rs | 2 + + syscare-common/src/fs/xattr.rs | 97 +++++ + syscare-common/src/io/mod.rs | 19 - + syscare-common/src/io/os_lines.rs | 78 ---- + syscare-common/src/io/select.rs | 77 ---- + syscare-common/src/lib.rs | 1 - + syscare-common/src/os/cpu.rs | 46 +-- + syscare-common/src/os/kernel.rs | 161 +++++++- + syscare-common/src/os/mod.rs | 2 - + syscare-common/src/os/platform.rs | 82 ++-- + syscare-common/src/os/proc_maps.rs | 90 ---- + syscare-common/src/os/proc_mounts.rs | 160 -------- + syscare-common/src/os/process.rs | 68 +--- + syscare-common/src/os/selinux.rs | 331 +++++++++++---- + syscare-common/src/os/umask.rs | 80 ++-- + syscare-common/src/os/user.rs | 122 +++--- + syscare-common/src/process/args.rs | 55 --- + syscare-common/src/process/child.rs | 68 ++-- + syscare-common/src/process/command.rs | 133 ------ + syscare-common/src/process/envs.rs | 68 ---- + syscare-common/src/process/mod.rs | 563 +++++++++++++++++++------- + syscare-common/src/process/output.rs | 179 ++++++++ + syscare-common/src/process/stdio.rs | 179 -------- + syscare-common/src/util/digest.rs | 216 +++++++++- + 29 files changed, 1702 insertions(+), 1496 deletions(-) + create mode 100644 syscare-common/src/fs/xattr.rs + delete mode 100644 syscare-common/src/io/mod.rs + delete mode 100644 syscare-common/src/io/os_lines.rs + delete mode 100644 syscare-common/src/io/select.rs + delete mode 100644 syscare-common/src/os/proc_maps.rs + delete mode 100644 syscare-common/src/os/proc_mounts.rs + delete mode 100644 syscare-common/src/process/args.rs + delete mode 100644 syscare-common/src/process/command.rs + delete mode 100644 syscare-common/src/process/envs.rs + create mode 100644 syscare-common/src/process/output.rs + delete mode 100644 syscare-common/src/process/stdio.rs + +diff --git a/syscare-common/Cargo.toml b/syscare-common/Cargo.toml +index c559631..f0b6446 100644 +--- a/syscare-common/Cargo.toml ++++ b/syscare-common/Cargo.toml +@@ -10,10 +10,11 @@ build = "build.rs" + + [dependencies] + anyhow = { version = "1.0" } +-lazy_static = { version = "1.4" } + log = { version = "0.4" } + memmap2 = { version = "0.9" } + nix = { version = "0.26" } ++num_cpus = { version = "1.14" } ++object = { version = "0.29" } + regex = { version = "1.7" } + sha2 = { version = "0.10" } + serde = { version = "1.0", features = ["derive"] } +diff --git a/syscare-common/src/fs/flock.rs b/syscare-common/src/fs/flock.rs +index a1ca6ca..931a9c7 100644 +--- a/syscare-common/src/fs/flock.rs ++++ b/syscare-common/src/fs/flock.rs +@@ -16,7 +16,7 @@ use std::{ + fs::File, + io::Result, + ops::{Deref, DerefMut}, +- os::unix::io::AsRawFd, ++ os::{fd::RawFd, unix::io::AsRawFd}, + path::Path, + }; + +@@ -36,23 +36,15 @@ pub struct FileLock { + } + + impl FileLock { +- fn new>(file_path: P, kind: FileLockType) -> Result { +- let file_path = file_path.as_ref(); +- let flock = Self { +- file: if file_path.exists() { +- File::open(file_path)? +- } else { +- File::create(file_path)? +- }, +- }; +- flock.acquire(kind)?; ++ fn new(file_path: &Path, kind: FileLockType) -> Result { ++ let file = File::open(file_path)?; ++ Self::acquire_flock(file.as_raw_fd(), kind)?; + +- Ok(flock) ++ Ok(Self { file }) + } + + #[inline] +- fn acquire(&self, kind: FileLockType) -> Result<()> { +- let fd = self.file.as_raw_fd(); ++ fn acquire_flock(fd: RawFd, kind: FileLockType) -> Result<()> { + let arg = match kind { + FileLockType::Shared => fcntl::FlockArg::LockShared, + FileLockType::Exclusive => fcntl::FlockArg::LockExclusive, +@@ -65,10 +57,8 @@ impl FileLock { + } + + #[inline] +- fn release(&self) { +- let fd = self.file.as_raw_fd(); +- let arg = fcntl::FlockArg::Unlock; +- fcntl::flock(fd, arg).expect("Failed to release file lock"); ++ fn release_flock(fd: RawFd) { ++ fcntl::flock(fd, fcntl::FlockArg::Unlock).expect("Failed to release file lock"); + } + } + +@@ -88,48 +78,110 @@ impl DerefMut for FileLock { + + impl Drop for FileLock { + fn drop(&mut self) { +- self.release(); ++ Self::release_flock(self.file.as_raw_fd()); + } + } + ++pub fn flock_exists>(file_path: P, kind: FileLockType) -> Result { ++ FileLock::new(file_path.as_ref(), kind) ++} ++ + pub fn flock>(file_path: P, kind: FileLockType) -> Result { +- FileLock::new(file_path, kind) ++ let file_path = file_path.as_ref(); ++ if !file_path.exists() { ++ File::create(file_path)?; ++ } ++ self::flock_exists(file_path, kind) + } + + #[test] + fn test() -> anyhow::Result<()> { +- use anyhow::{ensure, Context}; +- ++ use anyhow::{anyhow, ensure}; + use std::fs; + + let file_path = std::env::temp_dir().join("flock_test"); +- fs::remove_file(&file_path)?; +- +- println!("Testing shared flock on {}...", file_path.display()); +- let shared_lock = self::flock(&file_path, FileLockType::SharedNonBlock) +- .context("Failed to create shared flock")?; +- let shared_lock1 = self::flock(&file_path, FileLockType::SharedNonBlock) +- .context("Failed to create shared flock")?; ++ let non_exist_file = std::env::temp_dir().join("flock_test_non_exist"); ++ ++ fs::remove_file(&file_path).ok(); ++ fs::remove_file(&non_exist_file).ok(); ++ ++ fs::write(&file_path, "flock_test")?; ++ ++ println!("Testing fs::flock_exists()..."); ++ println!("- Shared flock '{}'...", file_path.display()); ++ let shared_lock = ++ self::flock_exists(&file_path, FileLockType::SharedNonBlock).map_err(|e| { ++ anyhow!( ++ "Failed to create shared flock '{}', {}", ++ file_path.display(), ++ e ++ ) ++ })?; ++ let shared_lock1 = ++ self::flock_exists(&file_path, FileLockType::SharedNonBlock).map_err(|e| { ++ anyhow!( ++ "Failed to create shared flock '{}', {}", ++ file_path.display(), ++ e ++ ) ++ })?; + ensure!( +- self::flock(&file_path, FileLockType::ExclusiveNonBlock).is_err(), +- "Exclusive flock should be failed" ++ self::flock_exists(&file_path, FileLockType::ExclusiveNonBlock).is_err(), ++ "Exclusive flock '{}' should be failed", ++ file_path.display() + ); + drop(shared_lock); + drop(shared_lock1); + +- println!("Testing exclusive flock on {}...", file_path.display()); +- let exclusive_lock = self::flock(&file_path, FileLockType::ExclusiveNonBlock) +- .context("Failed to create exclusive flock")?; ++ println!("- Exclusive flock '{}'...", file_path.display()); ++ let exclusive_lock = ++ self::flock_exists(&file_path, FileLockType::ExclusiveNonBlock).map_err(|e| { ++ anyhow!( ++ "Failed to create exclusive flock '{}', {}", ++ file_path.display(), ++ e ++ ) ++ })?; + ensure!( +- self::flock(&file_path, FileLockType::SharedNonBlock).is_err(), +- "Shared flock should be failed" ++ self::flock_exists(&file_path, FileLockType::SharedNonBlock).is_err(), ++ "Shared flock '{}' should be failed", ++ file_path.display() + ); + ensure!( +- self::flock(&file_path, FileLockType::ExclusiveNonBlock).is_err(), +- "Exclusive flock should be failed" ++ self::flock_exists(&file_path, FileLockType::ExclusiveNonBlock).is_err(), ++ "Exclusive flock '{}' should be failed", ++ file_path.display() + ); +- + drop(exclusive_lock); + ++ println!("- Non-exist flock '{}'...", non_exist_file.display()); ++ ensure!( ++ self::flock_exists(&non_exist_file, FileLockType::SharedNonBlock).is_err(), ++ "Shared flock '{}' should be failed", ++ non_exist_file.display() ++ ); ++ ensure!( ++ self::flock_exists(&non_exist_file, FileLockType::ExclusiveNonBlock).is_err(), ++ "Exclusive flock '{}' should be failed", ++ non_exist_file.display() ++ ); ++ ++ println!("Testing fs::flock()..."); ++ println!("- Non-exist flock '{}'...", non_exist_file.display()); ++ let _ = self::flock(&non_exist_file, FileLockType::SharedNonBlock).map_err(|e| { ++ anyhow!( ++ "Failed to create shared flock '{}', {}", ++ file_path.display(), ++ e ++ ) ++ })?; ++ let _ = self::flock(&non_exist_file, FileLockType::ExclusiveNonBlock).map_err(|e| { ++ anyhow!( ++ "Failed to create exclusive flock '{}', {}", ++ file_path.display(), ++ e ++ ) ++ })?; ++ + Ok(()) + } +diff --git a/syscare-common/src/fs/fs_impl.rs b/syscare-common/src/fs/fs_impl.rs +index f5393f8..a048804 100644 +--- a/syscare-common/src/fs/fs_impl.rs ++++ b/syscare-common/src/fs/fs_impl.rs +@@ -14,15 +14,14 @@ + + use std::{ + env, +- ffi::{CStr, OsStr, OsString}, ++ ffi::{OsStr, OsString}, + fs::{File, FileType, Metadata, Permissions, ReadDir}, + io, +- os::{raw::c_void, unix::fs::PermissionsExt}, ++ os::unix::fs::PermissionsExt, + path::{Component, Path, PathBuf}, +- ptr::null_mut, + }; + +-use crate::ffi::{CStrExt, OsStrExt}; ++use crate::ffi::OsStrExt; + + trait RewriteError { + fn rewrite_err(self, err_msg: String) -> Self; +@@ -196,96 +195,6 @@ pub fn file_ext>(path: P) -> OsString { + .unwrap_or_default() + } + +-pub fn getxattr(path: P, name: S) -> std::io::Result +-where +- P: AsRef, +- S: AsRef, +-{ +- let file_path = path.as_ref().to_cstring()?; +- let xattr_name = name.as_ref().to_cstring()?; +- +- /* +- * SAFETY: +- * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. +- * In our implementation, the buffer is checked properly, so that would be safe. +- */ +- let mut ret = +- unsafe { nix::libc::getxattr(file_path.as_ptr(), xattr_name.as_ptr(), null_mut(), 0) }; +- if ret == -1 { +- return Err(std::io::Error::last_os_error()).rewrite_err(format!( +- "Cannot get path {} xattr {}", +- file_path.to_string_lossy(), +- xattr_name.to_string_lossy() +- )); +- } +- +- let mut buf = vec![0; ret.unsigned_abs()]; +- let value_ptr = buf.as_mut_ptr().cast::(); +- +- /* +- * SAFETY: +- * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. +- * In our implementation, the buffer is checked properly, so that would be safe. +- */ +- ret = unsafe { +- nix::libc::getxattr( +- file_path.as_ptr(), +- xattr_name.as_ptr(), +- value_ptr, +- buf.len(), +- ) +- }; +- if ret == -1 { +- return Err(std::io::Error::last_os_error()).rewrite_err(format!( +- "Cannot get path {} xattr {}", +- file_path.to_string_lossy(), +- xattr_name.to_string_lossy(), +- )); +- } +- +- let value = CStr::from_bytes_with_nul(&buf[0..ret.unsigned_abs()]) +- .unwrap_or_default() +- .to_os_string(); +- +- Ok(value) +-} +- +-pub fn setxattr(path: P, name: S, value: T) -> std::io::Result<()> +-where +- P: AsRef, +- S: AsRef, +- T: AsRef, +-{ +- let file_path = path.as_ref().to_cstring()?; +- let xattr_name = name.as_ref().to_cstring()?; +- let xattr_value = value.as_ref().to_cstring()?; +- let size = xattr_value.to_bytes_with_nul().len(); +- +- /* +- * SAFETY: +- * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. +- * In our implementation, the buffer is checked properly, so that would be safe. +- */ +- let ret = unsafe { +- nix::libc::setxattr( +- file_path.as_ptr(), +- xattr_name.as_ptr(), +- xattr_value.as_ptr().cast::(), +- size, +- 0, +- ) +- }; +- if ret == -1 { +- return Err(std::io::Error::last_os_error()).rewrite_err(format!( +- "Cannot set {} xattr {}", +- file_path.to_string_lossy(), +- xattr_name.to_string_lossy() +- )); +- } +- +- Ok(()) +-} +- + pub fn normalize>(path: P) -> io::Result { + let mut new_path = PathBuf::new(); + +diff --git a/syscare-common/src/fs/glob.rs b/syscare-common/src/fs/glob.rs +index 8165343..9c115f4 100644 +--- a/syscare-common/src/fs/glob.rs ++++ b/syscare-common/src/fs/glob.rs +@@ -69,7 +69,7 @@ impl Glob { + } + + fn match_component(&mut self, path: PathBuf, index: usize) -> Result> { +- let last_index = self.components.len() - 1; ++ let last_index = self.components.len().saturating_sub(1); + + for dir_entry in fs::read_dir(&path)? { + let next_path = dir_entry?.path(); +@@ -126,7 +126,7 @@ impl Iterator for Glob { + + fn next(&mut self) -> Option { + while let Some((mut curr_path, mut index)) = self.stack.pop() { +- let last_index = self.components.len() - 1; ++ let last_index = self.components.len().saturating_sub(1); + + // iterate all of components over matching path + while index <= last_index { +diff --git a/syscare-common/src/fs/mmap.rs b/syscare-common/src/fs/mmap.rs +index b65f170..07c9a17 100644 +--- a/syscare-common/src/fs/mmap.rs ++++ b/syscare-common/src/fs/mmap.rs +@@ -12,36 +12,18 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{io::Result, ops::Deref, os::fd::AsRawFd, path::Path}; ++use std::{ops::Deref, os::unix::io::AsRawFd, path::Path}; + + use memmap2::{Advice, Mmap, MmapOptions}; + +-use super::flock::*; ++use super::flock; + + #[derive(Debug)] + pub struct FileMmap { +- _lock: FileLock, ++ _lock: flock::FileLock, + mmap: Mmap, + } + +-impl FileMmap { +- fn new>(file_path: P) -> Result { +- /* +- * SAFETY: +- * All file-backed memory map constructors are marked unsafe because of the +- * potential for Undefined Behavior (UB) using the map if the underlying file +- * is subsequently modified, in or out of process. +- * Our implementation uses shared file lock to avoid cross-process file access. +- * This mapped area would be safe. +- */ +- let lock = flock(file_path, FileLockType::Shared)?; +- let mmap = unsafe { MmapOptions::new().map(lock.as_raw_fd())? }; +- mmap.advise(Advice::Random)?; +- +- Ok(Self { _lock: lock, mmap }) +- } +-} +- + impl Deref for FileMmap { + type Target = [u8]; + +@@ -51,27 +33,58 @@ impl Deref for FileMmap { + } + + pub fn mmap>(file_path: P) -> std::io::Result { +- FileMmap::new(file_path) ++ /* ++ * SAFETY: ++ * All file-backed memory map constructors are marked unsafe because of the ++ * potential for Undefined Behavior (UB) using the map if the underlying file ++ * is subsequently modified, in or out of process. ++ * Our implementation uses shared file lock to avoid cross-process file access. ++ * This mapped area would be safe. ++ */ ++ let lock = flock::flock_exists(file_path, flock::FileLockType::Shared)?; ++ let mmap = unsafe { MmapOptions::new().map(lock.as_raw_fd())? }; ++ mmap.advise(Advice::Random)?; ++ ++ Ok(FileMmap { _lock: lock, mmap }) + } + +-#[test] +-fn test() -> anyhow::Result<()> { +- use anyhow::Context; ++#[cfg(test)] ++mod test { ++ use std::fs; ++ ++ use super::*; + +- const FILE_PATH: &str = "/etc/os-release"; +- const SYS_FS_PATH: &str = "/sys/kernel/vmcoreinfo"; +- const PROC_FS_PATH: &str = "/proc/version"; ++ #[test] ++ fn mmap_file() -> std::io::Result<()> { ++ let file_path = std::env::temp_dir().join("mmap_test"); ++ fs::remove_file(&file_path).ok(); ++ fs::write(&file_path, "mmap_test")?; + +- println!("Testing FileMmap..."); +- let orig_file = +- std::fs::read(FILE_PATH).with_context(|| format!("Failed to open file {}", FILE_PATH))?; +- let map_file = +- self::mmap(FILE_PATH).with_context(|| format!("Failed to mmap file {}", FILE_PATH))?; ++ let orig_file = fs::read(&file_path)?; ++ let map_file = self::mmap(&file_path)?; ++ assert_eq!(orig_file, map_file.as_ref()); + +- let _ = self::mmap(SYS_FS_PATH).expect_err("Sysfs cannot not be mapped"); +- let _ = self::mmap(PROC_FS_PATH).expect_err("Procfs cannot not be mapped"); ++ fs::remove_file(&file_path)?; ++ Ok(()) ++ } + +- assert_eq!(orig_file, map_file.as_ref()); ++ #[test] ++ fn mmap_non_exists_file() { ++ const NON_EXISTS_FILE: &str = "/non_exists_file"; ++ fs::remove_file(NON_EXISTS_FILE).ok(); + +- Ok(()) ++ assert!(self::mmap(NON_EXISTS_FILE).is_err(),); ++ } ++ ++ #[test] ++ fn mmap_proc_file() { ++ const PROC_FS_PATH: &str = "/proc/version"; ++ assert!(self::mmap(PROC_FS_PATH).is_err(),); ++ } ++ ++ #[test] ++ fn mmap_sys_file() { ++ const SYS_FS_PATH: &str = "/sys/kernel/vmcoreinfo"; ++ assert!(self::mmap(SYS_FS_PATH).is_err(),); ++ } + } +diff --git a/syscare-common/src/fs/mod.rs b/syscare-common/src/fs/mod.rs +index 3526915..b6d2d04 100644 +--- a/syscare-common/src/fs/mod.rs ++++ b/syscare-common/src/fs/mod.rs +@@ -16,8 +16,10 @@ mod flock; + mod fs_impl; + mod glob; + mod mmap; ++mod xattr; + + pub use flock::*; + pub use fs_impl::*; + pub use glob::*; + pub use mmap::*; ++pub use xattr::*; +diff --git a/syscare-common/src/fs/xattr.rs b/syscare-common/src/fs/xattr.rs +new file mode 100644 +index 0000000..237c70b +--- /dev/null ++++ b/syscare-common/src/fs/xattr.rs +@@ -0,0 +1,97 @@ ++// SPDX-License-Identifier: Mulan PSL v2 ++/* ++ * Copyright (c) 2024 Huawei Technologies Co., Ltd. ++ * syscare-common is licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ */ ++ ++use std::{ ++ ffi::{c_void, CString, OsStr, OsString}, ++ os::unix::ffi::{OsStrExt, OsStringExt}, ++ path::Path, ++ ptr, ++}; ++ ++pub fn getxattr(path: P, name: S) -> std::io::Result ++where ++ P: AsRef, ++ S: AsRef, ++{ ++ let file_path = CString::new(path.as_ref().as_os_str().as_bytes())?; ++ let xattr_name = CString::new(name.as_ref().as_bytes())?; ++ ++ /* ++ * SAFETY: ++ * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. ++ * In our implementation, the buffer is checked properly, so that would be safe. ++ */ ++ let buf_size = ++ unsafe { nix::libc::getxattr(file_path.as_ptr(), xattr_name.as_ptr(), ptr::null_mut(), 0) }; ++ if buf_size == -1 { ++ return Err(std::io::Error::last_os_error()); ++ } ++ ++ let mut buf = vec![0; buf_size.unsigned_abs()]; ++ let value_ptr = buf.as_mut_ptr().cast::(); ++ ++ /* ++ * SAFETY: ++ * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. ++ * In our implementation, the buffer is checked properly, so that would be safe. ++ */ ++ let bytes_read = unsafe { ++ nix::libc::getxattr( ++ file_path.as_ptr(), ++ xattr_name.as_ptr(), ++ value_ptr, ++ buf.len(), ++ ) ++ }; ++ if bytes_read == -1 { ++ return Err(std::io::Error::last_os_error()); ++ } ++ if buf.last() == Some(&0) { ++ buf.pop(); ++ } ++ ++ Ok(OsString::from_vec(buf)) ++} ++ ++pub fn setxattr(path: P, name: S, value: T) -> std::io::Result<()> ++where ++ P: AsRef, ++ S: AsRef, ++ T: AsRef, ++{ ++ let file_path = CString::new(path.as_ref().as_os_str().as_bytes())?; ++ let xattr_name = CString::new(name.as_ref().as_bytes())?; ++ let xattr_value = CString::new(value.as_ref().as_bytes())?; ++ let size = xattr_value.to_bytes_with_nul().len(); ++ ++ /* ++ * SAFETY: ++ * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. ++ * In our implementation, the buffer is checked properly, so that would be safe. ++ */ ++ let ret = unsafe { ++ nix::libc::setxattr( ++ file_path.as_ptr(), ++ xattr_name.as_ptr(), ++ xattr_value.as_ptr().cast::(), ++ size, ++ 0, ++ ) ++ }; ++ if ret == -1 { ++ return Err(std::io::Error::last_os_error()); ++ } ++ ++ Ok(()) ++} +diff --git a/syscare-common/src/io/mod.rs b/syscare-common/src/io/mod.rs +deleted file mode 100644 +index a36f750..0000000 +--- a/syscare-common/src/io/mod.rs ++++ /dev/null +@@ -1,19 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-mod os_lines; +-mod select; +- +-pub use os_lines::*; +-pub use select::*; +diff --git a/syscare-common/src/io/os_lines.rs b/syscare-common/src/io/os_lines.rs +deleted file mode 100644 +index 08ae0ce..0000000 +--- a/syscare-common/src/io/os_lines.rs ++++ /dev/null +@@ -1,78 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ffi::OsString, io::BufRead, os::unix::prelude::OsStringExt}; +- +-pub struct OsLines { +- buf: R, +-} +- +-impl Iterator for OsLines { +- type Item = std::io::Result; +- +- fn next(&mut self) -> Option { +- const CHAR_LF: [u8; 1] = [b'\n']; +- const CHAR_CR: [u8; 1] = [b'\r']; +- +- let mut buf = Vec::new(); +- match self.buf.read_until(CHAR_LF[0], &mut buf) { +- Ok(0) => None, +- Ok(_) => { +- // Drop "\n" or "\r\n" on the buf tail +- if buf.ends_with(&CHAR_LF) { +- buf.pop(); +- if buf.ends_with(&CHAR_CR) { +- buf.pop(); +- } +- } +- buf.shrink_to_fit(); +- Some(Ok(OsString::from_vec(buf))) +- } +- Err(_) => Some(self.buf.read_to_end(&mut buf).map(|_| { +- buf.shrink_to_fit(); +- OsString::from_vec(buf) +- })), +- } +- } +-} +- +-pub trait BufReadOsLines: BufRead + Sized { +- fn os_lines(self) -> OsLines { +- OsLines { buf: self } +- } +-} +- +-impl BufReadOsLines for R {} +- +-#[test] +-fn test() { +- use crate::fs; +- use std::io::BufReader; +- +- let buf_reader = +- BufReader::new(fs::open_file("/proc/self/cmdline").expect("Failed to open procfs")); +- let lines = buf_reader.lines(); +- for str in lines.flatten() { +- println!("{}", str); +- assert!(!str.is_empty()); +- } +- +- let buf_reader = +- BufReader::new(fs::open_file("/proc/self/cmdline").expect("Failed to open procfs")); +- let os_lines = buf_reader.os_lines(); +- for str in os_lines.flatten() { +- println!("{}", str.to_string_lossy()); +- assert!(!str.is_empty()); +- } +-} +diff --git a/syscare-common/src/io/select.rs b/syscare-common/src/io/select.rs +deleted file mode 100644 +index 5d9400f..0000000 +--- a/syscare-common/src/io/select.rs ++++ /dev/null +@@ -1,77 +0,0 @@ +-use std::{ +- os::unix::io::{AsRawFd, RawFd}, +- time::Duration, +-}; +- +-use anyhow::Result; +-use nix::sys::{ +- select::{select, FdSet}, +- time::TimeVal, +-}; +- +-pub enum SelectResult { +- Readable(RawFd), +- Writable(RawFd), +- Error(RawFd), +-} +- +-pub struct Select { +- fd_set: FdSet, +- readfds: FdSet, +- writefds: FdSet, +- errorfds: FdSet, +- timeout: Option, +-} +- +-impl Select { +- pub fn new(fds: I) -> Self +- where +- I: IntoIterator, +- F: AsRawFd, +- { +- Self::with_timeout(fds, None) +- } +- +- pub fn with_timeout(fds: I, duration: Option) -> Self +- where +- I: IntoIterator, +- F: AsRawFd, +- { +- let mut fd_set = FdSet::new(); +- for fd in fds { +- fd_set.insert(fd.as_raw_fd()); +- } +- let readfds = FdSet::new(); +- let writefds = FdSet::new(); +- let errorfds = FdSet::new(); +- let timeout = duration.map(|t| TimeVal::new(t.as_secs() as i64, t.subsec_micros() as i64)); +- +- Self { +- fd_set, +- readfds, +- writefds, +- errorfds, +- timeout, +- } +- } +- +- pub fn select(&mut self) -> Result + '_> { +- self.readfds = self.fd_set; +- self.writefds = self.fd_set; +- self.errorfds = self.fd_set; +- +- select( +- None, +- &mut self.readfds, +- &mut self.writefds, +- &mut self.errorfds, +- &mut self.timeout, +- )?; +- +- let rd_fds = self.readfds.fds(None).map(SelectResult::Readable); +- let wr_fds = self.writefds.fds(None).map(SelectResult::Writable); +- let err_fds = self.errorfds.fds(None).map(SelectResult::Error); +- +- Ok(rd_fds.chain(wr_fds).chain(err_fds)) +- } +-} +diff --git a/syscare-common/src/lib.rs b/syscare-common/src/lib.rs +index a3188e6..b568a2c 100644 +--- a/syscare-common/src/lib.rs ++++ b/syscare-common/src/lib.rs +@@ -14,7 +14,6 @@ + + pub mod ffi; + pub mod fs; +-pub mod io; + mod macros; + pub mod os; + pub mod os_str; +diff --git a/syscare-common/src/os/cpu.rs b/syscare-common/src/os/cpu.rs +index ead7eee..9d39bca 100644 +--- a/syscare-common/src/os/cpu.rs ++++ b/syscare-common/src/os/cpu.rs +@@ -12,39 +12,35 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::OsStr; ++use std::ffi::OsString; + +-use lazy_static::lazy_static; +- +-use nix::{ +- sched::{sched_getaffinity, CpuSet}, +- unistd::getpid, +-}; ++use num_cpus; + + use super::platform; + +-pub fn arch() -> &'static OsStr { ++pub fn arch() -> OsString { + platform::arch() + } + + pub fn num() -> usize { +- lazy_static! { +- static ref CPU_NUM: usize = { +- let cpu_set = sched_getaffinity(getpid()).unwrap_or_default(); +- let mut cpu_count = 0; +- for i in 0..CpuSet::count() { +- if cpu_set.is_set(i).unwrap_or_default() { +- cpu_count += 1; +- } +- } +- cpu_count +- }; +- } +- *CPU_NUM ++ num_cpus::get() + } + +-#[test] +-fn test() { +- println!("arch: {}", arch().to_string_lossy()); +- println!("num: {}", num()) ++#[cfg(test)] ++mod test { ++ use super::*; ++ ++ #[test] ++ fn test_arch() { ++ let arch = self::arch(); ++ println!("cpu arch: {}", arch.to_string_lossy()); ++ assert!(!arch.is_empty()); ++ } ++ ++ #[test] ++ fn test_num() { ++ let num = self::num(); ++ println!("cpu num: {}", num); ++ assert_ne!(num, 0); ++ } + } +diff --git a/syscare-common/src/os/kernel.rs b/syscare-common/src/os/kernel.rs +index a29e663..c355167 100644 +--- a/syscare-common/src/os/kernel.rs ++++ b/syscare-common/src/os/kernel.rs +@@ -12,10 +12,165 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::OsStr; ++// SPDX-License-Identifier: Mulan PSL v2 ++/* ++ * Copyright (c) 2024 Huawei Technologies Co., Ltd. ++ * syscare-common is licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ */ ++ ++use std::{ ++ collections::HashSet, ++ ffi::{CStr, CString, OsStr, OsString}, ++ fs::File, ++ os::unix::ffi::OsStrExt, ++ path::{Path, PathBuf}, ++}; ++ ++use log::{error, trace}; ++use nix::{errno::Errno, kmod}; ++use object::{Object, ObjectSection}; ++ ++use super::{platform, selinux}; ++use crate::fs; ++ ++const SYS_MODULE_DIR: &str = "/sys/module"; ++ ++pub type KernelModuleInfo = ModuleInfo; ++pub type KernelModuleGuard = ModuleGuard; + +-use super::platform; ++#[derive(Debug)] ++pub struct ModuleInfo { ++ pub name: OsString, ++ pub depends: HashSet, ++ pub module_path: PathBuf, ++} ++ ++#[derive(Debug)] ++pub struct ModuleGuard(ModuleInfo); ++ ++impl Drop for ModuleGuard { ++ fn drop(&mut self) { ++ if !self.0.module_path.exists() { ++ return; ++ } ++ if let Err(e) = self::remove_module(&self.0.name) { ++ error!( ++ "Failed to remove kernel module '{}', {}", ++ self.0.name.to_string_lossy(), ++ e.to_string().to_lowercase() ++ ); ++ } ++ } ++} + +-pub fn version() -> &'static OsStr { ++pub fn version() -> OsString { + platform::release() + } ++ ++pub fn list_modules() -> std::io::Result> { ++ const LIST_OPTIONS: fs::TraverseOptions = fs::TraverseOptions { recursive: false }; ++ ++ let modules = fs::list_dirs(SYS_MODULE_DIR, LIST_OPTIONS)? ++ .into_iter() ++ .filter_map(|path| path.file_name().map(OsStr::to_os_string)) ++ .collect(); ++ Ok(modules) ++} ++ ++pub fn relable_module_file>(file_path: P) -> std::io::Result<()> { ++ const KMOD_FILE_TYPE: &str = "modules_object_t"; ++ ++ let file_path = file_path.as_ref(); ++ let mut context = selinux::get_security_context(file_path)?; ++ if context.get_type() == KMOD_FILE_TYPE { ++ return Ok(()); ++ } ++ ++ context.set_type(KMOD_FILE_TYPE)?; ++ selinux::set_security_context(file_path, &context)?; ++ ++ Ok(()) ++} ++ ++pub fn module_info>(file_path: P) -> std::io::Result { ++ const MODINFO_SECTION_NAME: &str = ".modinfo"; ++ const MODINFO_NAME_PREFIX: &[u8] = b"name="; ++ const MODINFO_DEPENDS_PREFIX: &[u8] = b"depends="; ++ ++ let file_path = file_path.as_ref().to_path_buf(); ++ let mmap = fs::mmap(&file_path)?; ++ let file = object::File::parse(&*mmap).map_err(|_| Errno::ENOEXEC)?; ++ if file.format() != object::BinaryFormat::Elf { ++ return Err(std::io::Error::from(Errno::ENOEXEC)); ++ } ++ ++ let data = file ++ .section_by_name(MODINFO_SECTION_NAME) ++ .and_then(|section| section.data().ok()) ++ .ok_or(Errno::ENOEXEC)?; ++ let name = data ++ .split(|b| *b == b'\0') ++ .find_map(|entry| entry.strip_prefix(MODINFO_NAME_PREFIX)) ++ .map(|slice| OsStr::from_bytes(slice).to_os_string()) ++ .ok_or(Errno::ENOEXEC)?; ++ let depends = data ++ .split(|b| *b == b'\0') ++ .find_map(|entry| entry.strip_prefix(MODINFO_DEPENDS_PREFIX)) ++ .unwrap_or_default() ++ .split(|b| *b == b',') ++ .filter(|b| !b.is_empty()) ++ .map(|b| OsStr::from_bytes(b).to_os_string()) ++ .collect(); ++ let module_path = Path::new(SYS_MODULE_DIR).join(&name); ++ ++ let kmod = ModuleInfo { ++ name, ++ depends, ++ module_path, ++ }; ++ Ok(kmod) ++} ++ ++pub fn insert_module>(file_path: P) -> std::io::Result<()> { ++ const PARAM_VALUES: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; ++ ++ let file_path = file_path.as_ref(); ++ trace!("Inserting kernel module '{}'...", file_path.display()); ++ ++ let file = File::open(file_path)?; ++ kmod::finit_module(&file, PARAM_VALUES, kmod::ModuleInitFlags::empty())?; ++ ++ Ok(()) ++} ++ ++pub fn insert_module_guarded>(file_path: P) -> std::io::Result { ++ let file_path = file_path.as_ref(); ++ ++ let modinfo = self::module_info(file_path)?; ++ if !modinfo.module_path.exists() { ++ self::insert_module(file_path)?; ++ } ++ ++ Ok(ModuleGuard(modinfo)) ++} ++ ++pub fn remove_module>(module_name: S) -> std::io::Result<()> { ++ let module_name = module_name.as_ref(); ++ trace!( ++ "Removing kernel module '{}'...", ++ module_name.to_string_lossy() ++ ); ++ ++ let name = CString::new(module_name.as_bytes())?; ++ kmod::delete_module(&name, kmod::DeleteModuleFlags::O_NONBLOCK)?; ++ ++ Ok(()) ++} +diff --git a/syscare-common/src/os/mod.rs b/syscare-common/src/os/mod.rs +index 6a93a20..9d33010 100644 +--- a/syscare-common/src/os/mod.rs ++++ b/syscare-common/src/os/mod.rs +@@ -15,8 +15,6 @@ + pub mod cpu; + pub mod kernel; + pub mod platform; +-pub mod proc_maps; +-pub mod proc_mounts; + pub mod process; + pub mod selinux; + pub mod umask; +diff --git a/syscare-common/src/os/platform.rs b/syscare-common/src/os/platform.rs +index 8d38b44..178ecd4 100644 +--- a/syscare-common/src/os/platform.rs ++++ b/syscare-common/src/os/platform.rs +@@ -12,59 +12,71 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::OsStr; ++use std::ffi::OsString; + +-use lazy_static::lazy_static; +-use nix::sys::utsname::{uname, UtsName}; ++use nix::sys::utsname; + + #[inline(always)] +-fn info() -> &'static UtsName { +- lazy_static! { +- static ref PLATFORM_INFO: UtsName = uname().expect("Failed to get uname"); +- } +- &PLATFORM_INFO ++fn sysinfo() -> utsname::UtsName { ++ utsname::uname().expect("Failed to get system infomation") + } + +-pub fn hostname() -> &'static OsStr { +- info().nodename() ++pub fn sysname() -> OsString { ++ self::sysinfo().sysname().to_os_string() + } + +-pub fn sysname() -> &'static OsStr { +- info().sysname() ++pub fn hostname() -> OsString { ++ self::sysinfo().nodename().to_os_string() + } + +-pub fn release() -> &'static OsStr { +- info().release() ++pub fn release() -> OsString { ++ self::sysinfo().release().to_os_string() + } + +-pub fn version() -> &'static OsStr { +- info().version() ++pub fn version() -> OsString { ++ self::sysinfo().version().to_os_string() + } + +-pub fn arch() -> &'static OsStr { +- info().machine() ++pub fn arch() -> OsString { ++ self::sysinfo().machine().to_os_string() + } + +-#[test] +-fn test() { +- let sysname = sysname(); +- let hostname = hostname(); +- let release = release(); +- let version = version(); +- let arch = arch(); ++#[cfg(test)] ++mod test { ++ use super::*; + +- println!("sysname: {}", sysname.to_string_lossy()); +- assert!(!sysname.is_empty()); ++ #[test] ++ fn test_sysname() { ++ let sysname = self::sysname(); ++ println!("sysname: {}", sysname.to_string_lossy()); ++ assert!(!sysname.is_empty()); ++ } + +- println!("hostname: {}", hostname.to_string_lossy()); +- assert!(!hostname.is_empty()); ++ #[test] ++ fn test_hostname() { ++ let hostname = self::hostname(); ++ println!("hostname: {}", hostname.to_string_lossy()); ++ assert!(!hostname.is_empty()); ++ } + +- println!("release: {}", release.to_string_lossy()); +- assert!(!release.is_empty()); ++ #[test] ++ fn test_release() { ++ let release = self::release(); ++ println!("release: {}", release.to_string_lossy()); ++ assert!(!release.is_empty()); ++ } + +- println!("version: {}", version.to_string_lossy()); +- assert!(!version.is_empty()); ++ #[test] ++ fn test_version() { ++ let version = self::version(); ++ println!("version: {}", version.to_string_lossy()); ++ assert!(!version.is_empty()); ++ } + +- println!("arch: {}", arch.to_string_lossy()); +- assert!(!arch.is_empty()); ++ #[test] ++ fn test_arch() { ++ let arch = self::arch(); ++ println!("arch: {}", arch.to_string_lossy()); ++ assert!(!arch.is_empty()); ++ } + } +diff --git a/syscare-common/src/os/proc_maps.rs b/syscare-common/src/os/proc_maps.rs +deleted file mode 100644 +index 2855a4a..0000000 +--- a/syscare-common/src/os/proc_maps.rs ++++ /dev/null +@@ -1,90 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{convert::TryFrom, ffi::OsString, fs::File, io::BufReader}; +- +-use anyhow::{ensure, Result}; +- +-use crate::{ +- ffi::OsStrExt, +- fs, +- io::{BufReadOsLines, OsLines}, +-}; +- +-#[derive(Debug)] +-pub struct ProcMap { +- pub address: OsString, +- pub permission: OsString, +- pub offset: OsString, +- pub dev: OsString, +- pub inode: OsString, +- pub path_name: OsString, +-} +- +-impl TryFrom for ProcMap { +- type Error = anyhow::Error; +- +- fn try_from(value: OsString) -> std::result::Result { +- const MAP_FIELD_NUM: usize = 6; +- +- let fields = value.split_whitespace().collect::>(); +- ensure!( +- fields.len() == MAP_FIELD_NUM, +- "Failed to parse process mapping" +- ); +- +- Ok(Self { +- address: fields[0].to_owned(), +- permission: fields[1].to_owned(), +- offset: fields[2].to_owned(), +- dev: fields[3].to_owned(), +- inode: fields[4].to_owned(), +- path_name: fields[5].to_owned(), +- }) +- } +-} +- +-pub struct ProcMaps { +- lines: OsLines>, +-} +- +-impl ProcMaps { +- pub fn new(pid: i32) -> Result { +- let file_path = format!("/proc/{}/maps", pid); +- let lines = BufReader::new(fs::open_file(file_path)?).os_lines(); +- +- Ok(Self { lines }) +- } +-} +- +-impl Iterator for ProcMaps { +- type Item = ProcMap; +- +- fn next(&mut self) -> Option { +- self.lines +- .next() +- .and_then(Result::ok) +- .map(ProcMap::try_from) +- .and_then(Result::ok) +- } +-} +- +-#[test] +-fn test() { +- use super::process; +- +- for map in ProcMaps::new(process::id()).expect("Failed to read proc maps") { +- println!("{:#?}", map); +- } +-} +diff --git a/syscare-common/src/os/proc_mounts.rs b/syscare-common/src/os/proc_mounts.rs +deleted file mode 100644 +index 1e720bf..0000000 +--- a/syscare-common/src/os/proc_mounts.rs ++++ /dev/null +@@ -1,160 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::ffi::{OsStr, OsString}; +-use std::fs::File; +-use std::io::BufReader; +-use std::os::unix::ffi::{OsStrExt as StdOsStrExt, OsStringExt}; +-use std::path::PathBuf; +- +-use anyhow::Result; +- +-use crate::{ +- ffi::OsStrExt, +- fs, +- io::{BufReadOsLines, OsLines}, +-}; +- +-#[derive(Debug)] +-pub struct MountInfo { +- pub mount_id: u32, +- pub parent_id: u32, +- pub device_id: OsString, +- pub root: PathBuf, +- pub mount_point: PathBuf, +- pub mount_opts: Vec, +- pub optional: Vec, +- pub filesystem: OsString, +- pub mount_source: PathBuf, +- pub super_opts: Vec, +-} +- +-struct MountInfoParser<'a> { +- data: &'a [u8], +- num: usize, +- pos: usize, +-} +- +-impl<'a> Iterator for MountInfoParser<'a> { +- type Item = &'a OsStr; +- +- fn next(&mut self) -> Option { +- const OPTION_INDEX: usize = 6; +- const NORMAL_SPLITTER: char = ' '; +- const OPTION_SPLITTER: char = '-'; +- +- let data = &self.data[self.pos..]; +- let new_str = OsStr::from_bytes(data); +- if new_str.is_empty() { +- return None; +- } +- +- for (index, char) in new_str.char_indices() { +- let pattern; +- let skip_len; +- +- match self.num { +- OPTION_INDEX => { +- pattern = OPTION_SPLITTER; +- skip_len = 2; +- } +- _ => { +- pattern = NORMAL_SPLITTER; +- skip_len = 1; +- } +- }; +- if char == pattern { +- self.num += 1; +- self.pos += index + skip_len; +- +- return Some(OsStr::from_bytes(&data[..index])); +- } +- } +- +- self.pos = self.data.len() - 1; +- Some(OsStr::from_bytes(data)) +- } +-} +- +-pub struct Mounts { +- lines: OsLines>, +-} +- +-impl Mounts { +- pub fn new() -> Result { +- const MOUNTINFO_PATH: &str = "/proc/self/mountinfo"; +- +- Ok(Self { +- lines: BufReader::new(fs::open_file(MOUNTINFO_PATH)?).os_lines(), +- }) +- } +-} +- +-impl Mounts { +- fn parse_line(str: OsString) -> Option { +- const VALUE_SPLITTER: char = ','; +- +- let str_bytes = str.into_vec(); +- let mut iter = MountInfoParser { +- data: &str_bytes, +- num: 0, +- pos: 0, +- }; +- +- Some(MountInfo { +- mount_id: iter.next()?.to_string_lossy().parse::().ok()?, +- parent_id: iter.next()?.to_string_lossy().parse::().ok()?, +- device_id: iter.next()?.to_os_string(), +- root: PathBuf::from(iter.next()?), +- mount_point: PathBuf::from(iter.next()?), +- mount_opts: iter +- .next()? +- .split(VALUE_SPLITTER) +- .map(OsStrExt::trim) +- .map(OsString::from) +- .collect::>(), +- optional: iter +- .next()? +- .split_whitespace() +- .map(OsString::from) +- .collect::>(), +- filesystem: iter.next()?.to_os_string(), +- mount_source: PathBuf::from(iter.next()?), +- super_opts: iter +- .next()? +- .split(VALUE_SPLITTER) +- .map(OsStrExt::trim) +- .map(OsString::from) +- .collect::>(), +- }) +- } +-} +- +-impl Iterator for Mounts { +- type Item = MountInfo; +- +- fn next(&mut self) -> Option { +- self.lines.next()?.ok().and_then(Self::parse_line) +- } +-} +- +-#[test] +-fn test() { +- let mount_info = Mounts::new().expect("Failed to read mount info"); +- for mount in mount_info { +- println!(); +- println!("{:#?}", mount); +- assert!(mount.mount_point.exists()) +- } +-} +diff --git a/syscare-common/src/os/process.rs b/syscare-common/src/os/process.rs +index 4569587..fc8c6cd 100644 +--- a/syscare-common/src/os/process.rs ++++ b/syscare-common/src/os/process.rs +@@ -13,72 +13,48 @@ + */ + + use std::ffi::{OsStr, OsString}; +-use std::path::{Path, PathBuf}; ++use std::path::PathBuf; + +-use lazy_static::lazy_static; +-use nix::unistd::getpid; +- +-use crate::fs; ++use nix::errno::Errno; ++use nix::unistd; + + pub fn id() -> i32 { +- lazy_static! { +- static ref PROCESS_ID: i32 = getpid().as_raw(); +- } +- *PROCESS_ID ++ unistd::getpid().as_raw() + } + +-pub fn path() -> &'static Path { +- lazy_static! { +- static ref PROCESS_PATH: PathBuf = +- std::env::current_exe().unwrap_or_else(|_| PathBuf::from("/")); +- } +- PROCESS_PATH.as_path() ++pub fn path() -> std::io::Result { ++ std::env::current_exe() + } + +-pub fn name() -> &'static OsStr { +- lazy_static! { +- static ref PROCESS_NAME: OsString = fs::file_name(path()); +- } +- PROCESS_NAME.as_os_str() ++pub fn name() -> std::io::Result { ++ self::path()? ++ .file_name() ++ .map(OsStr::to_os_string) ++ .ok_or(std::io::Error::from(Errno::EINVAL)) + } + + #[cfg(test)] +-mod tests_process { +- use crate::os::process::{id, name, path}; +- use std::process::Command; +- use std::{println, string::ToString}; +- +- fn build_commd(s: &str) -> String { +- let mut cmd = "ps -ef |grep ".to_string(); +- cmd = cmd + s + "|grep -v grep"; +- let output = Command::new("bash").arg("-c").arg(cmd).output().unwrap(); +- String::from_utf8(output.stdout).unwrap() +- } ++mod test { ++ use super::*; + + #[test] + fn test_id() { +- let process_id = id().to_string(); +- println!("This process id is {}", process_id); +- +- let sys_proc = build_commd(&process_id); +- assert!(!sys_proc.is_empty()); ++ let pid = self::id(); ++ println!("pid: {}", pid); ++ assert!(pid > 1) + } + + #[test] + fn test_path() { +- let process_path = path().display().to_string(); +- println!("This path is {:#?}", process_path); +- +- let sys_path = build_commd(&process_path); +- assert!(!sys_path.is_empty()); ++ let path = self::path().expect("Failed to get executable path"); ++ println!("path: {}", path.display()); ++ assert!(path.exists()); + } + + #[test] + fn test_name() { +- let process_name = name().to_string_lossy(); +- println!("This name is {:#?}", process_name); +- +- let sys_name = build_commd(&process_name); +- assert!(!sys_name.is_empty()); ++ let name = self::name().expect("Failed to get executable name"); ++ println!("name: {}", name.to_string_lossy()); ++ assert!(!name.is_empty()); + } + } +diff --git a/syscare-common/src/os/selinux.rs b/syscare-common/src/os/selinux.rs +index bb9864c..e1aa73e 100644 +--- a/syscare-common/src/os/selinux.rs ++++ b/syscare-common/src/os/selinux.rs +@@ -12,119 +12,304 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::OsString; +-use std::os::unix::ffi::OsStringExt as UnixOsStringExt; +-use std::path::Path; ++use std::{ ++ ffi::{OsStr, OsString}, ++ fmt::Debug, ++ os::unix::ffi::{OsStrExt, OsStringExt}, ++ path::Path, ++}; + +-use anyhow::{bail, ensure, Context, Result}; ++use nix::errno::Errno; + +-use crate::{concat_os, ffi::OsStrExt, fs}; ++use crate::fs; + + const SELINUX_SYS_FILE: &str = "/sys/fs/selinux/enforce"; +-const SELINUX_XATTR_NAME: &str = "security.selinux"; +-const SELINUX_XATTR_SPLITTER: &str = ":"; +-const SECURITY_XATTR_LEN: usize = 4; ++const SELINUX_STATUS_PERMISSIVE: &str = "0"; ++const SELINUX_STATUS_ENFORCING: &str = "1"; ++ ++const SECURITY_CONTEXT_XATTR_NAME: &str = "security.selinux"; ++const SECURITY_CONTEXT_SPLITER: u8 = b':'; ++const SECURITY_CONTEXT_SPLITER_COUNT: usize = 3; ++const SECURITY_CONTEXT_ATTR_COUNT: usize = SECURITY_CONTEXT_SPLITER_COUNT + 1; ++ ++const SECURITY_CONTEXT_USER_INDEX: usize = 0; ++const SECURITY_CONTEXT_ROLE_INDEX: usize = 1; ++const SECURITY_CONTEXT_TYPE_INDEX: usize = 2; ++const SECURITY_CONTEXT_LEVEL_INDEX: usize = 3; ++ ++pub type SELinuxStatus = Status; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Status { ++ Disabled, + Permissive, + Enforcing, +- Disabled, + } + + impl std::fmt::Display for Status { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +- f.write_fmt(format_args!("{:?}", self)) ++ write!(f, "{:?}", self) + } + } + +-pub fn get_status() -> Result { +- if !Path::new(SELINUX_SYS_FILE).is_file() { +- return Ok(Status::Disabled); ++pub fn get_status() -> Status { ++ let value = fs::read_to_string(SELINUX_SYS_FILE).unwrap_or_default(); ++ match value.as_str() { ++ SELINUX_STATUS_PERMISSIVE => Status::Permissive, ++ SELINUX_STATUS_ENFORCING => Status::Enforcing, ++ _ => Status::Disabled, + } ++} + +- let value = OsString::from_vec(fs::read(SELINUX_SYS_FILE)?) +- .to_string_lossy() +- .parse::() +- .context("Failed to parse selinux status")?; ++pub fn set_status(value: Status) -> std::io::Result<()> { ++ let contents = match value { ++ Status::Permissive => SELINUX_STATUS_PERMISSIVE, ++ Status::Enforcing => SELINUX_STATUS_ENFORCING, ++ _ => return Err(std::io::Error::from(Errno::EINVAL)), ++ }; ++ fs::write(SELINUX_SYS_FILE, contents)?; + +- Ok(match value { +- 0 => Status::Permissive, +- 1 => Status::Enforcing, +- _ => Status::Disabled, +- }) ++ Ok(()) + } + +-pub fn set_status(value: Status) -> Result<()> { +- if (value != Status::Permissive) && (value != Status::Enforcing) { +- bail!("Status {} is invalid", value); ++#[derive(Clone, PartialEq, Eq)] ++pub struct SecurityContext(OsString); ++ ++impl SecurityContext { ++ fn get_attribute(&self, index: usize) -> &OsStr { ++ self.0 ++ .as_bytes() ++ .splitn(SECURITY_CONTEXT_ATTR_COUNT, |&b| { ++ b == SECURITY_CONTEXT_SPLITER ++ }) ++ .nth(index) ++ .map(OsStr::from_bytes) ++ .expect("Unexpected security context format") + } +- fs::write( +- SELINUX_SYS_FILE, +- match value { +- Status::Enforcing => "1", +- _ => "0", +- }, +- )?; + +- Ok(()) ++ fn set_attribute>(&mut self, index: usize, value: S) -> std::io::Result<()> { ++ let value = value.as_ref().as_bytes(); ++ ++ if value.is_empty() { ++ return Err(std::io::Error::from(Errno::EINVAL)); ++ } ++ if (index != SECURITY_CONTEXT_LEVEL_INDEX) && (value.contains(&SECURITY_CONTEXT_SPLITER)) { ++ return Err(std::io::Error::from(Errno::EINVAL)); ++ } ++ let attrs = self.0.as_bytes().splitn(SECURITY_CONTEXT_ATTR_COUNT, |&b| { ++ b == SECURITY_CONTEXT_SPLITER ++ }); ++ ++ let mut new_context = Vec::new(); ++ for (i, attr) in attrs.enumerate() { ++ new_context.extend_from_slice(if i != index { attr } else { value }); ++ new_context.push(SECURITY_CONTEXT_SPLITER); ++ } ++ new_context.pop(); ++ ++ self.0 = OsString::from_vec(new_context); ++ Ok(()) ++ } + } + +-#[derive(Debug, Clone, PartialEq, Eq)] +-pub struct SecurityContext { +- pub user: OsString, +- pub role: OsString, +- pub kind: OsString, +- pub level: OsString, ++impl SecurityContext { ++ pub fn parse>(value: S) -> std::io::Result { ++ let context = value.as_ref(); ++ if context.is_empty() { ++ return Err(std::io::Error::from(Errno::EINVAL)); ++ } ++ ++ let spliter_count = context ++ .as_bytes() ++ .iter() ++ .filter(|&b| *b == SECURITY_CONTEXT_SPLITER) ++ .count(); ++ if spliter_count < SECURITY_CONTEXT_SPLITER_COUNT { ++ return Err(std::io::Error::from(Errno::EINVAL)); ++ } ++ ++ Ok(Self(context.to_os_string())) ++ } ++ ++ pub fn get_user(&self) -> &OsStr { ++ self.get_attribute(SECURITY_CONTEXT_USER_INDEX) ++ } ++ ++ pub fn get_role(&self) -> &OsStr { ++ self.get_attribute(SECURITY_CONTEXT_ROLE_INDEX) ++ } ++ ++ pub fn get_type(&self) -> &OsStr { ++ self.get_attribute(SECURITY_CONTEXT_TYPE_INDEX) ++ } ++ ++ pub fn get_level(&self) -> &OsStr { ++ self.get_attribute(SECURITY_CONTEXT_LEVEL_INDEX) ++ } ++ ++ pub fn set_user>(&mut self, value: S) -> std::io::Result<()> { ++ self.set_attribute(SECURITY_CONTEXT_USER_INDEX, value) ++ } ++ ++ pub fn set_role>(&mut self, value: S) -> std::io::Result<()> { ++ self.set_attribute(SECURITY_CONTEXT_ROLE_INDEX, value) ++ } ++ ++ pub fn set_type>(&mut self, value: S) -> std::io::Result<()> { ++ self.set_attribute(SECURITY_CONTEXT_TYPE_INDEX, value) ++ } ++ ++ pub fn set_level>(&mut self, value: S) -> std::io::Result<()> { ++ self.set_attribute(SECURITY_CONTEXT_LEVEL_INDEX, value) ++ } ++ ++ pub fn as_os_str(&self) -> &OsStr { ++ &self.0 ++ } + } + +-impl AsRef for SecurityContext { +- fn as_ref(&self) -> &SecurityContext { +- self ++impl Debug for SecurityContext { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ write!(f, "{:?}", self.0) + } + } + +-pub fn get_security_context

(file_path: P) -> Result ++impl AsRef for SecurityContext { ++ fn as_ref(&self) -> &OsStr { ++ self.as_os_str() ++ } ++} ++ ++pub fn get_security_context

(file_path: P) -> std::io::Result + where + P: AsRef, + { +- let value = fs::getxattr(file_path, SELINUX_XATTR_NAME)?; +- let data = value.split(SELINUX_XATTR_SPLITTER).collect::>(); +- ensure!( +- data.len() == SECURITY_XATTR_LEN, +- "Failed to parse selinux security context" +- ); +- +- Ok(SecurityContext { +- user: data[0].to_os_string(), +- role: data[1].to_os_string(), +- kind: data[2].to_os_string(), +- level: data[3].to_os_string(), +- }) ++ SecurityContext::parse(fs::getxattr(file_path, SECURITY_CONTEXT_XATTR_NAME)?) + } + +-pub fn set_security_context(file_path: P, value: S) -> Result<()> ++pub fn set_security_context

(file_path: P, value: &SecurityContext) -> std::io::Result<()> + where + P: AsRef, +- S: AsRef, + { +- let old_value = get_security_context(&file_path)?; +- let new_value = value.as_ref(); ++ fs::setxattr(file_path, SECURITY_CONTEXT_XATTR_NAME, value) ++} ++ ++#[cfg(test)] ++mod test { ++ use std::{fs, path::Path}; + +- if &old_value == new_value { +- return Ok(()); ++ use super::*; ++ ++ #[test] ++ fn test_selinux_status() { ++ let status = self::get_status(); ++ println!("SELinux status: {}", status); ++ ++ let sys_file = Path::new(self::SELINUX_SYS_FILE); ++ if sys_file.exists() { ++ assert!(self::get_status() == Status::Disabled); ++ } else { ++ assert!(self::get_status() == Status::Disabled); ++ } ++ assert!(self::set_status(Status::Disabled).is_err()); + } + +- let new_context = concat_os!( +- &new_value.user, +- SELINUX_XATTR_SPLITTER, +- &new_value.role, +- SELINUX_XATTR_SPLITTER, +- &new_value.kind, +- SELINUX_XATTR_SPLITTER, +- &new_value.level, +- ); +- fs::setxattr(&file_path, SELINUX_XATTR_NAME, new_context)?; ++ #[test] ++ fn test_security_context_parse() { ++ const TEST_CASES: &[&str] = &[ ++ "system_u:object_r:bin_t:s0", ++ "user_u:role_r:type_t:s0:c1,c2", ++ "user.dom:role-1:type_x:s0:c1.c2", ++ "a:b:c:d.e-f,g", ++ "a_:b_:c_:d_", ++ ]; ++ for str in TEST_CASES { ++ let result = SecurityContext::parse(str).is_ok(); ++ assert!(result, "Failed to parse security context '{}'", str); ++ } ++ } + +- Ok(()) ++ #[test] ++ fn test_security_context_get() { ++ const TEST_CASES: &[(&str, [&str; 4])] = &[ ++ ( ++ "system_u:object_r:bin_t:s0", ++ ["system_u", "object_r", "bin_t", "s0"], ++ ), ++ ( ++ "user_u:role_r:type_t:s0:c1,c2", ++ ["user_u", "role_r", "type_t", "s0:c1,c2"], ++ ), ++ ( ++ "user.dom:role-1:type_x:s0:c1.c2", ++ ["user.dom", "role-1", "type_x", "s0:c1.c2"], ++ ), ++ ("a:b:c:d.e-f,g", ["a", "b", "c", "d.e-f,g"]), ++ ("a_:b_:c_:d_", ["a_", "b_", "c_", "d_"]), ++ ]; ++ ++ for (case, attrs) in TEST_CASES { ++ let context = SecurityContext::parse(case).expect("Failed to parse security context"); ++ assert_eq!(context.get_user(), attrs[0]); ++ assert_eq!(context.get_role(), attrs[1]); ++ assert_eq!(context.get_type(), attrs[2]); ++ assert_eq!(context.get_level(), attrs[3]); ++ } ++ } ++ ++ #[test] ++ fn test_security_context_set() { ++ const DEFAULT_CONTEXT: &str = "unconfined_u:object_r:default_t:s0"; ++ const TEST_CASES: &[(&str, [&str; 4])] = &[ ++ ( ++ "system_u:object_r:bin_t:s0", ++ ["system_u", "object_r", "bin_t", "s0"], ++ ), ++ ( ++ "user_u:role_r:type_t:s0:c1,c2", ++ ["user_u", "role_r", "type_t", "s0:c1,c2"], ++ ), ++ ( ++ "user.dom:role-1:type_x:s0:c1.c2", ++ ["user.dom", "role-1", "type_x", "s0:c1.c2"], ++ ), ++ ("a:b:c:d.e-f,g", ["a", "b", "c", "d.e-f,g"]), ++ ("a_:b_:c_:d_", ["a_", "b_", "c_", "d_"]), ++ ]; ++ ++ for (result, attrs) in TEST_CASES { ++ let mut context = ++ SecurityContext::parse(DEFAULT_CONTEXT).expect("Failed to parse security context"); ++ assert!(context.set_user(attrs[0]).is_ok()); ++ assert!(context.set_role(attrs[1]).is_ok()); ++ assert!(context.set_type(attrs[2]).is_ok()); ++ assert!(context.set_level(attrs[3]).is_ok()); ++ println!("{:?}", context); ++ ++ assert_eq!(context.as_os_str(), *result); ++ } ++ } ++ ++ #[test] ++ fn test_get_set_security_context() { ++ const TEST_FILE: &str = "selinux_test"; ++ const TEST_CONTEXT: &str = "unconfined_u:object_r:default_t:s0"; ++ ++ let file_path = std::env::temp_dir().join(TEST_FILE); ++ ++ fs::remove_file(&file_path).ok(); ++ fs::write(&file_path, TEST_FILE).expect("Failed to write test file"); ++ ++ let set_context = SecurityContext::parse(TEST_CONTEXT).expect("Invalid security context"); ++ self::set_security_context(&file_path, &set_context) ++ .expect("Failed to set security context"); ++ println!("set context: {:#?}", set_context); ++ ++ let get_context = ++ self::get_security_context(&file_path).expect("Failed to get security context"); ++ println!("get context: {:#?}", get_context); ++ ++ assert_eq!(set_context, get_context); ++ fs::remove_file(&file_path).expect("Failed to remove test file"); ++ } + } +diff --git a/syscare-common/src/os/umask.rs b/syscare-common/src/os/umask.rs +index ab624cf..cb0d3ee 100644 +--- a/syscare-common/src/os/umask.rs ++++ b/syscare-common/src/os/umask.rs +@@ -12,45 +12,49 @@ + * See the Mulan PSL v2 for more details. + */ + +-use nix::sys::stat::{umask, Mode}; ++use nix::{libc::mode_t, sys::stat}; + +-pub fn set_umask(mode: u32) -> u32 { +- umask(Mode::from_bits_truncate(mode)).bits() ++pub fn set_umask(mode: mode_t) -> mode_t { ++ stat::umask(stat::Mode::from_bits_truncate(mode)).bits() + } + +-#[test] +-fn test() { +- use std::{fs, fs::File, os::unix::fs::PermissionsExt}; +- +- const FILE_PATH: &str = "/tmp/umask_test"; +- const UMASK1: u32 = 0o077; // 10600 +- const UMASK2: u32 = 0o022; // 10644 +- +- fs::remove_file(FILE_PATH).ok(); +- +- println!("Testing umask {:03o}...", UMASK1); +- set_umask(UMASK1); +- let file1 = File::create(FILE_PATH).expect("Failed to create file"); +- let perm1 = file1 +- .metadata() +- .map(|s| s.permissions()) +- .expect("Failed to read file permission"); +- +- println!("umask: {:03o}, perm: {:05o}", UMASK1, perm1.mode()); +- +- drop(file1); +- fs::remove_file(FILE_PATH).ok(); +- +- println!("Testing umask {:03o}...", UMASK2); +- set_umask(UMASK2); +- let file2 = File::create(FILE_PATH).expect("Failed to create file"); +- let perm2 = file2 +- .metadata() +- .map(|s| s.permissions()) +- .expect("Failed to read file permission"); +- +- println!("umask: {:03o}, perm: {:05o}", UMASK2, perm2.mode()); +- +- drop(file2); +- fs::remove_file(FILE_PATH).ok(); ++#[cfg(test)] ++mod test { ++ #[test] ++ fn test_set_umask() { ++ use std::{fs, fs::File, os::unix::fs::PermissionsExt}; ++ ++ let file_path = std::env::temp_dir().join("umask_test"); ++ const UMASK1: u32 = 0o077; // 10600 ++ const UMASK2: u32 = 0o022; // 10644 ++ ++ fs::remove_file(&file_path).ok(); ++ ++ println!("Testing umask {:03o}...", UMASK1); ++ super::set_umask(UMASK1); ++ let file1 = File::create(&file_path).expect("Failed to create file"); ++ let perm1 = file1 ++ .metadata() ++ .map(|s| s.permissions()) ++ .expect("Failed to read file permission"); ++ println!("umask: {:03o}, perm: {:05o}", UMASK1, perm1.mode()); ++ assert_eq!(perm1.mode() & 0o777, 0o600); ++ ++ drop(file1); ++ fs::remove_file(&file_path).expect("Failed to remove file"); ++ ++ println!("Testing umask {:03o}...", UMASK2); ++ super::set_umask(UMASK2); ++ let file2 = File::create(&file_path).expect("Failed to create file"); ++ let perm2 = file2 ++ .metadata() ++ .map(|s| s.permissions()) ++ .expect("Failed to read file permission"); ++ ++ println!("umask: {:03o}, perm: {:05o}", UMASK2, perm2.mode()); ++ assert_eq!(perm2.mode() & 0o777, 0o644); ++ ++ drop(file2); ++ fs::remove_file(&file_path).expect("Failed to remove file"); ++ } + } +diff --git a/syscare-common/src/os/user.rs b/syscare-common/src/os/user.rs +index db2e10e..1e79cdb 100644 +--- a/syscare-common/src/os/user.rs ++++ b/syscare-common/src/os/user.rs +@@ -12,80 +12,94 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{ +- ffi::{CString, OsStr}, +- path::{Path, PathBuf}, +-}; +- +-use lazy_static::lazy_static; +-use nix::unistd::{getuid, Gid, Uid, User}; +- +-use crate::ffi::CStrExt; +- +-fn info() -> &'static User { +- lazy_static! { +- static ref USER_INFO: User = User::from_uid(getuid()) +- .unwrap_or_default() +- .unwrap_or(User { +- name: String::from("root"), +- passwd: CString::default(), +- uid: Uid::from_raw(0), +- gid: Gid::from_raw(0), +- gecos: CString::default(), +- dir: PathBuf::from("/root"), +- shell: PathBuf::from("/bin/sh"), +- }); +- } +- &USER_INFO ++use std::{ffi::OsString, os::unix::ffi::OsStringExt, path::PathBuf}; ++ ++use nix::unistd; ++ ++#[inline(always)] ++fn userinfo() -> unistd::User { ++ unistd::User::from_uid(unistd::getuid()) ++ .expect("Failed to get user infomation") ++ .expect("Invalid user id") + } + +-pub fn name() -> &'static str { +- self::info().name.as_str() ++pub fn uid() -> u32 { ++ unistd::getuid().as_raw() + } + +-pub fn passwd() -> &'static OsStr { +- self::info().passwd.as_os_str() ++pub fn gid() -> u32 { ++ unistd::getgid().as_raw() + } + +-pub fn id() -> u32 { +- self::info().uid.as_raw() ++pub fn name() -> String { ++ self::userinfo().name + } + +-pub fn gid() -> u32 { +- self::info().gid.as_raw() ++pub fn passwd() -> OsString { ++ OsString::from_vec(self::userinfo().passwd.into_bytes()) + } + +-pub fn gecos() -> &'static OsStr { +- self::info().gecos.as_os_str() ++pub fn gecos() -> OsString { ++ OsString::from_vec(self::userinfo().gecos.into_bytes()) + } + +-pub fn home() -> &'static Path { +- self::info().dir.as_path() ++pub fn home() -> PathBuf { ++ self::userinfo().dir + } + +-pub fn shell() -> &'static Path { +- self::info().shell.as_path() ++pub fn shell() -> PathBuf { ++ self::userinfo().shell + } + +-#[test] +-fn test() { +- println!("name: {}", self::name()); +- assert!(!self::name().is_empty()); ++#[cfg(test)] ++mod test { ++ use super::*; + +- println!("passwd: {}", self::passwd().to_string_lossy()); +- assert!(!self::passwd().is_empty()); ++ #[test] ++ fn test_uid() { ++ let uid = self::uid(); ++ println!("uid: {}", uid); ++ assert!(uid < u32::MAX); ++ } + +- println!("id: {}", self::id()); +- assert!(id() < u32::MAX); ++ #[test] ++ fn test_gid() { ++ let gid = self::gid(); ++ println!("gid: {}", gid); ++ assert!(gid < u32::MAX); ++ } + +- println!("gid: {}", self::gid()); +- assert!(gid() < u32::MAX); ++ #[test] ++ fn test_name() { ++ let name = self::name(); ++ println!("name: {}", name); ++ assert!(!name.is_empty()); ++ } ++ ++ #[test] ++ fn test_passwd() { ++ let passwd = self::passwd(); ++ println!("passwd: {}", passwd.to_string_lossy()); ++ assert!(!passwd.is_empty()); ++ } + +- println!("gecos: {}", self::gecos().to_string_lossy()); ++ #[test] ++ fn test_gecos() { ++ let gecos = self::gecos(); ++ println!("gecos: {}", gecos.to_string_lossy()); ++ } + +- println!("home: {}", self::home().display()); +- assert!(self::home().exists()); ++ #[test] ++ fn test_home() { ++ let home = self::home(); ++ println!("home: {}", home.display()); ++ assert!(home.exists()); ++ } + +- println!("shell: {}", self::shell().display()); +- assert!(self::home().exists()); ++ #[test] ++ fn test_shell() { ++ let shell = self::shell(); ++ println!("shell: {}", shell.display()); ++ assert!(shell.exists()); ++ } + } +diff --git a/syscare-common/src/process/args.rs b/syscare-common/src/process/args.rs +deleted file mode 100644 +index 7d7e094..0000000 +--- a/syscare-common/src/process/args.rs ++++ /dev/null +@@ -1,55 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::ffi::{OsStr, OsString}; +- +-#[derive(Clone, Default)] +-pub struct CommandArgs { +- args: Vec, +-} +- +-impl CommandArgs { +- pub fn new() -> Self { +- Self { args: Vec::new() } +- } +- +- pub fn arg(&mut self, arg: S) -> &mut Self +- where +- S: AsRef, +- { +- self.args.push(arg.as_ref().to_os_string()); +- self +- } +- +- pub fn args(&mut self, args: I) -> &mut Self +- where +- I: IntoIterator, +- S: AsRef, +- { +- for arg in args { +- self.arg(arg); +- } +- self +- } +-} +- +-impl IntoIterator for CommandArgs { +- type Item = OsString; +- +- type IntoIter = std::vec::IntoIter; +- +- fn into_iter(self) -> Self::IntoIter { +- self.args.into_iter() +- } +-} +diff --git a/syscare-common/src/process/child.rs b/syscare-common/src/process/child.rs +index 0e8cf54..5379ef2 100644 +--- a/syscare-common/src/process/child.rs ++++ b/syscare-common/src/process/child.rs +@@ -13,58 +13,57 @@ + */ + + use std::{ +- ffi::OsString, +- ops::Deref, +- os::unix::process::ExitStatusExt, +- process::{Child as StdChild, ExitStatus as StdExitStatus}, +- thread::JoinHandle, ++ ffi::OsString, ops::Deref, os::unix::process::ExitStatusExt, process, thread::JoinHandle, + }; + + use anyhow::{anyhow, ensure, Context, Result}; + use log::trace; + +-use super::{Stdio, StdioLevel}; ++use super::output; + + pub struct Child { +- pub(super) id: u32, + pub(super) name: String, +- pub(super) stdio_level: StdioLevel, +- pub(super) inner: StdChild, ++ pub(super) child: process::Child, ++ pub(super) log_level: output::LogLevel, + } + + impl Child { +- fn capture_stdio(&mut self) -> Result> { +- Stdio::new( +- self.name.clone(), +- self.inner +- .stdout +- .take() +- .context("Failed to capture stdout")?, +- self.inner +- .stderr +- .take() +- .context("Failed to capture stderr")?, +- self.stdio_level, +- ) +- .capture() ++ fn redirect_outputs(&mut self) -> Result> { ++ let stdout = self ++ .child ++ .stdout ++ .take() ++ .context("Failed to capture stdout")?; ++ let stderr = self ++ .child ++ .stderr ++ .take() ++ .context("Failed to capture stderr")?; ++ let outputs = output::Outputs::new(stdout, stderr, self.log_level); ++ ++ std::thread::Builder::new() ++ .name(self.name.clone()) ++ .spawn(|| outputs.redirect()) ++ .with_context(|| format!("Failed to create thread {}", self.name)) + } + } + + impl Child { + pub fn kill(&mut self) -> Result<()> { +- self.inner ++ let id = self.child.id(); ++ self.child + .kill() +- .with_context(|| format!("Failed to kill process {} ({})", self.name, self.id)) ++ .with_context(|| format!("Failed to kill process {} ({})", self.name, id)) + } + + pub fn wait(&mut self) -> Result { ++ let id = self.child.id(); + let status = self +- .inner ++ .child + .wait() +- .with_context(|| format!("Failed to wait process {} ({})", self.name, self.id))?; +- ++ .with_context(|| format!("Failed to wait process {} ({})", self.name, id))?; + let exit_status = ExitStatus { +- id: self.id, ++ id, + name: self.name.clone(), + status, + }; +@@ -79,24 +78,25 @@ impl Child { + } + + pub fn wait_with_output(&mut self) -> Result { +- let stdio_thread = self.capture_stdio()?; ++ let thread = self.redirect_outputs()?; + let status = self.wait()?; +- let (stdout, stderr) = stdio_thread ++ let (stdout, stderr) = thread + .join() + .map_err(|_| anyhow!("Failed to join stdio thread"))?; + +- Ok(Output { ++ let output = Output { + status, + stdout, + stderr, +- }) ++ }; ++ Ok(output) + } + } + + pub struct ExitStatus { + id: u32, + name: String, +- status: StdExitStatus, ++ status: process::ExitStatus, + } + + impl ExitStatus { +diff --git a/syscare-common/src/process/command.rs b/syscare-common/src/process/command.rs +deleted file mode 100644 +index 06d3568..0000000 +--- a/syscare-common/src/process/command.rs ++++ /dev/null +@@ -1,133 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ +- ffi::OsStr, +- path::Path, +- process::{Command as StdCommand, Stdio}, +-}; +- +-use anyhow::{Context, Result}; +-use log::{trace, Level}; +- +-use super::{Child, ExitStatus, Output, StdioLevel}; +- +-pub struct Command { +- inner: StdCommand, +- stdio_level: StdioLevel, +-} +- +-impl Command { +- pub fn new>(program: S) -> Self { +- Self { +- inner: StdCommand::new(program), +- stdio_level: StdioLevel::default(), +- } +- } +- +- pub fn arg>(&mut self, arg: S) -> &mut Self { +- self.inner.arg(arg); +- self +- } +- +- pub fn args(&mut self, args: I) -> &mut Self +- where +- I: IntoIterator, +- S: AsRef, +- { +- for arg in args { +- self.arg(arg.as_ref()); +- } +- self +- } +- +- pub fn env(&mut self, key: K, val: V) -> &mut Self +- where +- K: AsRef, +- V: AsRef, +- { +- self.inner.env(key, val); +- self +- } +- +- pub fn envs(&mut self, vars: I) -> &mut Self +- where +- I: IntoIterator, +- K: AsRef, +- V: AsRef, +- { +- for (ref key, ref val) in vars { +- self.env(key, val); +- } +- self +- } +- +- pub fn env_clear(&mut self) -> &mut Self { +- self.inner.env_clear(); +- self +- } +- +- pub fn current_dir>(&mut self, dir: P) -> &mut Self { +- self.inner.current_dir(dir); +- self +- } +- +- pub fn stdin>(&mut self, cfg: T) -> &mut Self { +- self.inner.stdin(cfg); +- self +- } +- +- pub fn stdout>>(&mut self, level: T) -> &mut Self { +- self.stdio_level.stdout = level.into(); +- self +- } +- +- pub fn stderr(&mut self, level: Level) -> &mut Self { +- self.stdio_level.stderr = level.into(); +- self +- } +- +- pub fn spawn(&mut self) -> Result { +- let name = Path::new(self.inner.get_program()) +- .file_name() +- .context("Failed to get process name")? +- .to_string_lossy() +- .to_string(); +- +- trace!("Executing {:?}", self.inner); +- let child = self +- .inner +- .spawn() +- .with_context(|| format!("Failed to start {}", name))?; +- +- Ok(Child { +- id: child.id(), +- name, +- stdio_level: self.stdio_level, +- inner: child, +- }) +- } +- +- pub fn run(&mut self) -> Result { +- self.inner.stdout(Stdio::null()).stderr(Stdio::null()); +- +- self.spawn()?.wait() +- } +- +- pub fn run_with_output(&mut self) -> Result { +- self.inner.stdout(Stdio::piped()).stderr(Stdio::piped()); +- +- self.spawn()?.wait_with_output() +- } +-} +diff --git a/syscare-common/src/process/envs.rs b/syscare-common/src/process/envs.rs +deleted file mode 100644 +index 038ad60..0000000 +--- a/syscare-common/src/process/envs.rs ++++ /dev/null +@@ -1,68 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ +- collections::HashMap, +- ffi::{OsStr, OsString}, +-}; +- +-pub struct CommandEnvs { +- envs: HashMap, +-} +- +-impl CommandEnvs { +- pub fn new() -> Self { +- Self { +- envs: HashMap::new(), +- } +- } +- +- pub fn env(&mut self, key: K, value: V) -> &mut Self +- where +- K: AsRef, +- V: AsRef, +- { +- self.envs +- .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string()); +- self +- } +- +- pub fn envs(&mut self, vars: I) -> &mut Self +- where +- I: IntoIterator, +- K: AsRef, +- V: AsRef, +- { +- for (key, value) in vars { +- self.env(key, value); +- } +- self +- } +-} +- +-impl Default for CommandEnvs { +- fn default() -> Self { +- Self::new() +- } +-} +- +-impl IntoIterator for CommandEnvs { +- type Item = (OsString, OsString); +- +- type IntoIter = std::collections::hash_map::IntoIter; +- +- fn into_iter(self) -> Self::IntoIter { +- self.envs.into_iter() +- } +-} +diff --git a/syscare-common/src/process/mod.rs b/syscare-common/src/process/mod.rs +index 894707c..1fe472c 100644 +--- a/syscare-common/src/process/mod.rs ++++ b/syscare-common/src/process/mod.rs +@@ -12,151 +12,434 @@ + * See the Mulan PSL v2 for more details. + */ + +-mod args; ++use std::{ ++ collections::HashMap, ++ ffi::{OsStr, OsString}, ++ path::Path, ++ process, ++}; ++ ++use anyhow::{Context, Result}; ++use log::{trace, Level}; ++ + mod child; +-mod command; +-mod envs; +-mod stdio; ++mod output; ++ ++#[derive(Debug, Clone)] ++pub struct CommandArgs(Vec); ++ ++impl CommandArgs { ++ pub fn new() -> Self { ++ Self(Vec::new()) ++ } ++ ++ pub fn arg(&mut self, arg: S) -> &mut Self ++ where ++ S: AsRef, ++ { ++ self.0.push(arg.as_ref().to_os_string()); ++ self ++ } ++ ++ pub fn args(&mut self, args: I) -> &mut Self ++ where ++ I: IntoIterator, ++ S: AsRef, ++ { ++ for arg in args { ++ self.arg(arg); ++ } ++ self ++ } ++} + +-pub use args::*; +-pub use child::*; +-pub use command::*; +-pub use envs::*; ++impl Default for CommandArgs { ++ fn default() -> Self { ++ Self::new() ++ } ++} + +-use stdio::{Stdio, StdioLevel}; ++impl IntoIterator for CommandArgs { ++ type Item = OsString; + +-#[test] +-fn test() { +- use log::Level; +- use std::fs::File; ++ type IntoIter = std::vec::IntoIter; ++ ++ fn into_iter(self) -> Self::IntoIter { ++ self.0.into_iter() ++ } ++} ++ ++#[derive(Debug, Clone)] ++pub struct CommandEnvs(HashMap); ++ ++impl CommandEnvs { ++ pub fn new() -> Self { ++ Self(HashMap::new()) ++ } ++ ++ pub fn env(&mut self, key: K, value: V) -> &mut Self ++ where ++ K: AsRef, ++ V: AsRef, ++ { ++ self.0 ++ .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string()); ++ self ++ } ++ ++ pub fn envs(&mut self, vars: I) -> &mut Self ++ where ++ I: IntoIterator, ++ K: AsRef, ++ V: AsRef, ++ { ++ for (key, value) in vars { ++ self.env(key, value); ++ } ++ self ++ } ++} + +- use crate::ffi::OsStrExt; ++impl Default for CommandEnvs { ++ fn default() -> Self { ++ Self::new() ++ } ++} ++ ++impl IntoIterator for CommandEnvs { ++ type Item = (OsString, OsString); + +- println!("Testing Command::new()..."); +- let mut echo_cmd = Command::new("echo"); +- let mut env_cmd = Command::new("env"); +- let mut pwd_cmd = Command::new("pwd"); +- let mut grep_cmd = Command::new("grep"); +- let mut ls_cmd = Command::new("ls"); ++ type IntoIter = std::collections::hash_map::IntoIter; + +- let mut test_cmd = Command::new("test"); +- let mut cat_cmd = Command::new("cat"); +- +- let mut err_cmd = Command::new("/cmd/not/exist"); +- +- println!("Testing Command::arg()..."); +- echo_cmd.arg("Test:"); +- +- test_cmd.arg("1"); +- ls_cmd.arg("/file/not/exist"); +- +- println!("Testing Command::args()..."); +- echo_cmd.args(["Hello", "World!"]); +- +- println!("Testing Command::env_clear()..."); +- env_cmd.env_clear(); +- +- println!("Testing Command::env()..."); +- env_cmd.env("test_key1", "test_val1"); +- +- println!("Testing Command::envs()..."); +- env_cmd.envs([("test_key2", "test_val2"), ("test_key3", "test_val3")]); +- +- println!("Testing Command::current_dir()..."); +- pwd_cmd.current_dir("/tmp"); +- +- println!("Testing Command::stdin()..."); +- grep_cmd.stdin(File::open("/proc/self/maps").expect("Failed to open file")); +- grep_cmd.arg("vdso"); +- +- println!("Testing Command::stdout()..."); +- echo_cmd.stdout(Level::Info); +- +- println!("Testing Command::stderr()..."); +- echo_cmd.stderr(Level::Info); +- +- println!("Testing Command::spawn()..."); +- let mut echo_proc = echo_cmd.spawn().expect("Failed to spawn process"); +- let mut env_proc = env_cmd.spawn().expect("Failed to spawn process"); +- let mut pwd_proc = pwd_cmd.spawn().expect("Failed to spawn process"); +- let mut grep_proc = grep_cmd.spawn().expect("Failed to spawn process"); +- let mut ls_proc = ls_cmd.spawn().expect("Failed to spawn process"); +- +- let mut test_proc = test_cmd.spawn().expect("Failed to spawn process"); +- let mut cat_proc = cat_cmd.spawn().expect("Failed to spawn process"); +- +- assert_eq!(err_cmd.spawn().is_err(), true); +- +- println!("Testing Child::kill()..."); +- cat_proc.kill().expect("Failed to kill process"); +- +- println!("Testing Child::wait()..."); +- let test_status = test_proc.wait().expect("Failed to wait process"); +- let cat_status = cat_proc.wait().expect("Process should not be waited"); +- +- println!("Testing Child::wait_with_output()..."); +- let echo_output = echo_proc +- .wait_with_output() +- .expect("Failed to wait process"); +- let env_output = env_proc.wait_with_output().expect("Failed to wait process"); +- let pwd_output = pwd_proc.wait_with_output().expect("Failed to wait process"); +- let grep_output = grep_proc +- .wait_with_output() +- .expect("Failed to wait process"); +- let ls_output = ls_proc.wait_with_output().expect("Failed to wait process"); +- +- println!("Testing ExitStatus::exit_code()..."); +- assert_eq!(test_status.exit_code(), 0); +- assert_eq!(cat_status.exit_code(), 137); +- +- println!("Testing ExitStatus::exit_ok()..."); +- assert_eq!(test_status.exit_ok().is_ok(), true); +- assert_eq!(cat_status.exit_ok().is_ok(), false); +- +- println!("Testing ExitStatus::success()..."); +- assert_eq!(test_status.success(), true); +- assert_eq!(cat_status.success(), false); +- +- println!("Testing Output::exit_code()..."); +- assert_eq!(echo_output.exit_code(), 0); +- assert_eq!(env_output.exit_code(), 0); +- assert_eq!(pwd_output.exit_code(), 0); +- assert_eq!(grep_output.exit_code(), 0); +- assert_eq!(ls_output.exit_code(), 2); +- +- println!("Testing Output::exit_ok()..."); +- assert_eq!(echo_output.exit_ok().is_ok(), true); +- assert_eq!(env_output.exit_ok().is_ok(), true); +- assert_eq!(pwd_output.exit_ok().is_ok(), true); +- assert_eq!(grep_output.exit_ok().is_ok(), true); +- assert_eq!(ls_output.exit_ok().is_ok(), false); +- +- println!("Testing Output::success()..."); +- assert_eq!(echo_output.success(), true); +- assert_eq!(env_output.success(), true); +- assert_eq!(pwd_output.success(), true); +- assert_eq!(grep_output.success(), true); +- assert_eq!(ls_output.success(), false); +- +- println!("Testing Output::stdout..."); +- assert_eq!(echo_output.stdout.is_empty(), false); +- assert_eq!(env_output.stdout.is_empty(), false); +- assert_eq!(pwd_output.stdout.is_empty(), false); +- assert_eq!(grep_output.stdout.is_empty(), false); +- assert_eq!(ls_output.stdout.is_empty(), true); +- +- assert_eq!(echo_output.stdout, "Test: Hello World!"); +- assert_eq!( +- env_output.stdout, +- "test_key1=test_val1\ntest_key2=test_val2\ntest_key3=test_val3" +- ); +- assert_eq!(pwd_output.stdout, "/tmp"); +- assert_eq!(grep_output.stdout.contains("vdso"), true); +- +- println!("Testing ProcessOutput::stderr..."); +- assert_eq!(echo_output.stderr.is_empty(), true); +- assert_eq!(env_output.stderr.is_empty(), true); +- assert_eq!(pwd_output.stderr.is_empty(), true); +- assert_eq!(grep_output.stderr.is_empty(), true); +- assert_eq!(ls_output.stderr.is_empty(), false); ++ fn into_iter(self) -> Self::IntoIter { ++ self.0.into_iter() ++ } ++} ++ ++pub struct Command { ++ inner: process::Command, ++ log_level: output::LogLevel, ++} ++ ++impl Command { ++ pub fn new>(program: S) -> Self { ++ Self { ++ inner: process::Command::new(program), ++ log_level: output::LogLevel::default(), ++ } ++ } ++ ++ pub fn arg>(&mut self, arg: S) -> &mut Self { ++ self.inner.arg(arg); ++ self ++ } ++ ++ pub fn args(&mut self, args: I) -> &mut Self ++ where ++ I: IntoIterator, ++ S: AsRef, ++ { ++ for arg in args { ++ self.arg(arg.as_ref()); ++ } ++ self ++ } ++ ++ pub fn env(&mut self, key: K, val: V) -> &mut Self ++ where ++ K: AsRef, ++ V: AsRef, ++ { ++ self.inner.env(key, val); ++ self ++ } ++ ++ pub fn envs(&mut self, vars: I) -> &mut Self ++ where ++ I: IntoIterator, ++ K: AsRef, ++ V: AsRef, ++ { ++ for (key, val) in vars { ++ self.env(key, val); ++ } ++ self ++ } ++ ++ pub fn env_clear(&mut self) -> &mut Self { ++ self.inner.env_clear(); ++ self ++ } ++ ++ pub fn current_dir>(&mut self, dir: P) -> &mut Self { ++ self.inner.current_dir(dir); ++ self ++ } ++ ++ pub fn stdin>(&mut self, cfg: T) -> &mut Self { ++ self.inner.stdin(cfg); ++ self ++ } ++ ++ pub fn stdout(&mut self, level: Level) -> &mut Self { ++ self.log_level.stdout = Some(level); ++ self ++ } ++ ++ pub fn stderr(&mut self, level: Level) -> &mut Self { ++ self.log_level.stderr = Some(level); ++ self ++ } ++ ++ pub fn pipe_output(&mut self) -> &mut Self { ++ self.inner ++ .stdout(process::Stdio::piped()) ++ .stderr(process::Stdio::piped()); ++ self ++ } ++ ++ pub fn ignore_output(&mut self) -> &mut Self { ++ self.inner ++ .stdout(process::Stdio::null()) ++ .stderr(process::Stdio::null()); ++ self ++ } ++ ++ pub fn spawn(&mut self) -> Result { ++ let name = Path::new(self.inner.get_program()) ++ .file_name() ++ .map(|s| s.to_string_lossy().to_string()) ++ .unwrap_or_default(); ++ ++ trace!("Executing {:?}", self.inner); ++ let child = self ++ .inner ++ .spawn() ++ .with_context(|| format!("Failed to start {}", name))?; ++ let log_level = self.log_level; ++ ++ Ok(child::Child { ++ name, ++ child, ++ log_level, ++ }) ++ } ++ ++ pub fn run(&mut self) -> Result { ++ self.ignore_output().spawn()?.wait() ++ } ++ ++ pub fn run_with_output(&mut self) -> Result { ++ self.pipe_output().spawn()?.wait_with_output() ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use log::Level; ++ use std::ffi::OsStr; ++ use std::path::Path; ++ use std::time::Duration; ++ ++ #[test] ++ fn test_command_args_new() { ++ let args = CommandArgs::new(); ++ assert!(args.0.is_empty()); ++ } ++ ++ #[test] ++ fn test_command_args_default() { ++ let args = CommandArgs::default(); ++ assert!(args.0.is_empty()); ++ } ++ ++ #[test] ++ fn test_command_args_arg() { ++ let mut args = CommandArgs::new(); ++ args.arg("test"); ++ assert_eq!(args.0.len(), 1); ++ assert_eq!(args.0[0], OsStr::new("test")); ++ } ++ ++ #[test] ++ fn test_command_args_args() { ++ let mut args = CommandArgs::new(); ++ args.args(vec!["arg1", "arg2"]); ++ assert_eq!(args.0.len(), 2); ++ assert_eq!(args.0[0], OsStr::new("arg1")); ++ assert_eq!(args.0[1], OsStr::new("arg2")); ++ } ++ ++ #[test] ++ fn test_command_args_into_iter() { ++ let mut args = CommandArgs::new(); ++ args.args(vec!["a", "b", "c"]); ++ let mut iter = args.into_iter(); ++ assert_eq!(iter.next(), Some(OsString::from("a"))); ++ assert_eq!(iter.next(), Some(OsString::from("b"))); ++ assert_eq!(iter.next(), Some(OsString::from("c"))); ++ assert_eq!(iter.next(), None); ++ } ++ ++ #[test] ++ fn test_command_envs_new() { ++ let envs = CommandEnvs::new(); ++ assert!(envs.0.is_empty()); ++ } ++ ++ #[test] ++ fn test_command_envs_default() { ++ let envs = CommandEnvs::default(); ++ assert!(envs.0.is_empty()); ++ } ++ ++ #[test] ++ fn test_command_envs_env() { ++ let mut envs = CommandEnvs::new(); ++ envs.env("KEY", "VALUE"); ++ assert_eq!(envs.0.len(), 1); ++ assert_eq!( ++ envs.0.get(&OsString::from("KEY")), ++ Some(&OsString::from("VALUE")) ++ ); ++ } ++ ++ #[test] ++ fn test_command_envs_envs() { ++ let mut envs = CommandEnvs::new(); ++ envs.envs([("K1", "V1"), ("K2", "V2")]); ++ assert_eq!(envs.0.len(), 2); ++ assert_eq!( ++ envs.0.get(&OsString::from("K1")), ++ Some(&OsString::from("V1")) ++ ); ++ assert_eq!( ++ envs.0.get(&OsString::from("K2")), ++ Some(&OsString::from("V2")) ++ ); ++ } ++ ++ #[test] ++ fn test_command_new() { ++ let cmd = Command::new("test"); ++ assert_eq!(cmd.inner.get_program(), OsStr::new("test")); ++ assert!(cmd.inner.get_args().next().is_none()); ++ } ++ ++ #[test] ++ fn test_command_arg() { ++ let mut cmd = Command::new("test"); ++ cmd.arg("arg1"); ++ let mut args = cmd.inner.get_args(); ++ assert_eq!(args.next(), Some(OsStr::new("arg1"))); ++ assert!(args.next().is_none()); ++ } ++ ++ #[test] ++ fn test_command_args() { ++ let mut cmd = Command::new("test"); ++ cmd.args(["a", "b"]); ++ let mut args = cmd.inner.get_args(); ++ assert_eq!(args.next(), Some(OsStr::new("a"))); ++ assert_eq!(args.next(), Some(OsStr::new("b"))); ++ assert!(args.next().is_none()); ++ } ++ ++ #[test] ++ fn test_command_env() { ++ let mut cmd = Command::new("test"); ++ cmd.env("K", "V"); ++ let envs = cmd.inner.get_envs().collect::>(); ++ assert_eq!(envs.get(&OsStr::new("K")), Some(&Some(OsStr::new("V")))); ++ } ++ ++ #[test] ++ fn test_command_envs() { ++ let mut cmd = Command::new("test"); ++ cmd.envs([("K1", "V1"), ("K2", "V2")]); ++ ++ let envs = cmd.inner.get_envs().collect::>(); ++ assert_eq!(envs.len(), 2); ++ assert_eq!(envs.get(&OsStr::new("K1")), Some(&Some(OsStr::new("V1")))); ++ assert_eq!(envs.get(&OsStr::new("K2")), Some(&Some(OsStr::new("V2")))); ++ } ++ ++ #[test] ++ fn test_command_env_clear() { ++ let mut cmd = Command::new("test"); ++ cmd.env("K", "V").env_clear(); ++ let envs = cmd.inner.get_envs().collect::>(); ++ assert!(envs.is_empty()); ++ } ++ ++ #[test] ++ fn test_command_current_dir() { ++ let mut cmd = Command::new("test"); ++ cmd.current_dir("/tmp"); ++ assert_eq!(cmd.inner.get_current_dir(), Some(Path::new("/tmp"))); ++ } ++ ++ #[test] ++ fn test_command_stdout_stderr() { ++ let mut cmd = Command::new("test"); ++ cmd.stdout(Level::Info).stderr(Level::Error); ++ assert_eq!(cmd.log_level.stdout, Some(Level::Info)); ++ assert_eq!(cmd.log_level.stderr, Some(Level::Error)); ++ } ++ ++ #[test] ++ fn test_command_pipe_output() { ++ let mut cmd = Command::new("test"); ++ ++ cmd.pipe_output(); ++ assert!(cmd.inner.spawn().unwrap().stdout.is_some()); ++ assert!(cmd.inner.spawn().unwrap().stderr.is_some()); ++ } ++ ++ #[test] ++ fn test_command_ignore_output() { ++ let mut cmd = Command::new("test"); ++ ++ cmd.pipe_output(); ++ assert!(cmd.inner.spawn().unwrap().stdout.is_some()); ++ assert!(cmd.inner.spawn().unwrap().stderr.is_some()); ++ } ++ ++ #[test] ++ fn test_command_spawn_kill() { ++ let mut cmd = Command::new("yes"); ++ cmd.ignore_output(); ++ ++ let mut child = cmd.spawn().unwrap(); ++ std::thread::sleep(Duration::from_millis(100)); ++ ++ assert!(child.kill().is_ok()); ++ } ++ ++ #[test] ++ fn test_command_spawn_wait() { ++ let mut cmd = Command::new("echo"); ++ cmd.arg("test").ignore_output(); ++ ++ let mut child = cmd.spawn().unwrap(); ++ let status = child.wait().unwrap(); ++ assert!(status.success()); ++ } ++ ++ #[test] ++ fn test_command_run() { ++ let mut cmd = Command::new("true"); ++ let status = cmd.run().unwrap(); ++ assert!(status.success()); ++ } ++ ++ #[test] ++ fn test_command_run_with_output() { ++ let mut cmd = Command::new("env"); ++ let output = cmd.run_with_output().unwrap(); ++ ++ assert!(output.status.success()); ++ assert!(!output.stdout.is_empty()); ++ } + } +diff --git a/syscare-common/src/process/output.rs b/syscare-common/src/process/output.rs +new file mode 100644 +index 0000000..5e97c7e +--- /dev/null ++++ b/syscare-common/src/process/output.rs +@@ -0,0 +1,179 @@ ++// SPDX-License-Identifier: Mulan PSL v2 ++/* ++ * Copyright (c) 2024 Huawei Technologies Co., Ltd. ++ * syscare-common is licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ */ ++ ++use std::{ ++ ffi::OsString, ++ io::Read, ++ os::unix::{ffi::OsStringExt, io::AsRawFd}, ++ process::{ChildStderr, ChildStdout}, ++}; ++ ++use log::{error, log, Level}; ++use nix::poll::{poll, PollFd, PollFlags}; ++ ++const STREAM_BUFFER_SIZE: usize = 4096; ++ ++#[derive(Debug, Clone, Copy)] ++pub struct LogLevel { ++ pub stdout: Option, ++ pub stderr: Option, ++} ++ ++impl Default for LogLevel { ++ fn default() -> Self { ++ Self { ++ stdout: None, ++ stderr: Some(Level::Error), ++ } ++ } ++} ++ ++struct Stream { ++ stream: R, ++ buffer: Vec, ++ offset: usize, ++ log_level: Option, ++ is_closed: bool, ++} ++ ++impl Stream { ++ fn new(stream: R, log_level: Option) -> Self { ++ Self { ++ stream, ++ buffer: Vec::with_capacity(STREAM_BUFFER_SIZE), ++ offset: 0, ++ log_level, ++ is_closed: false, ++ } ++ } ++ ++ fn read_buf(&mut self) -> std::io::Result { ++ if self.buffer.capacity().wrapping_sub(self.buffer.len()) < STREAM_BUFFER_SIZE { ++ self.buffer.reserve(STREAM_BUFFER_SIZE); ++ } ++ ++ let spare_cap = self.buffer.spare_capacity_mut(); ++ let spare_buf = unsafe { ++ std::slice::from_raw_parts_mut(spare_cap.as_mut_ptr() as *mut u8, spare_cap.len()) ++ }; ++ ++ let len = self.stream.read(spare_buf)?; ++ unsafe { ++ self.buffer.set_len(self.buffer.len() + len); ++ } ++ ++ Ok(len) ++ } ++ ++ fn print_logs(&mut self) { ++ if let Some(level) = self.log_level { ++ let start = self.offset; ++ if start >= self.buffer.len() { ++ return; ++ } ++ ++ let slice = if !self.is_closed { ++ let end = match self.buffer[start..].iter().rposition(|&b| b == b'\n') { ++ Some(pos) => start + pos, ++ None => return, ++ }; ++ self.offset = end + 1; // skip '\n' ++ &self.buffer[start..end] ++ } else { ++ self.offset = self.buffer.len(); ++ &self.buffer[start..] ++ }; ++ if slice.is_empty() { ++ return; ++ } ++ ++ let lines = slice.split(|&b| b == b'\n').map(String::from_utf8_lossy); ++ for line in lines { ++ log!(level, "{}", line); ++ } ++ } ++ } ++ ++ fn handle_revents(&mut self, revents: PollFlags) { ++ if revents.contains(PollFlags::POLLIN) { ++ match self.read_buf() { ++ Ok(0) => self.is_closed = true, // EOF ++ Ok(_) => {} ++ Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {} ++ Err(e) => { ++ error!("Failed to read stream, {}", e); ++ self.is_closed = true; ++ } ++ } ++ } ++ if revents.contains(PollFlags::POLLHUP) { ++ self.is_closed = true; ++ } ++ ++ self.print_logs(); ++ } ++} ++ ++pub struct Outputs { ++ fds: [PollFd; 2], ++ stdout: Stream, ++ stderr: Stream, ++} ++ ++impl Outputs { ++ pub fn new(stdout: ChildStdout, stderr: ChildStderr, log_level: LogLevel) -> Self { ++ Self { ++ fds: [ ++ PollFd::new(stdout.as_raw_fd(), PollFlags::POLLIN | PollFlags::POLLHUP), ++ PollFd::new(stderr.as_raw_fd(), PollFlags::POLLIN | PollFlags::POLLHUP), ++ ], ++ stdout: Stream::new(stdout, log_level.stdout), ++ stderr: Stream::new(stderr, log_level.stderr), ++ } ++ } ++ ++ pub fn redirect(mut self) -> (OsString, OsString) { ++ const POLL_TIMEOUT: i32 = -1; ++ ++ loop { ++ match poll(&mut self.fds, POLL_TIMEOUT) { ++ Ok(events) => { ++ if events == 0 { ++ break; ++ } ++ for (i, fd) in self.fds.iter().enumerate() { ++ let revents = fd.revents().expect("Invalid poll event"); ++ match i { ++ 0 => self.stdout.handle_revents(revents), ++ 1 => self.stderr.handle_revents(revents), ++ _ => unreachable!("Invalid poll fd"), ++ }; ++ } ++ } ++ Err(e) => { ++ error!("Failed to poll events, {}", e); ++ break; ++ } ++ } ++ if self.stdout.is_closed && self.stderr.is_closed { ++ break; ++ } ++ } ++ ++ ( ++ OsString::from_vec(self.stdout.buffer), ++ OsString::from_vec(self.stderr.buffer), ++ ) ++ } ++} +diff --git a/syscare-common/src/process/stdio.rs b/syscare-common/src/process/stdio.rs +deleted file mode 100644 +index 9a93e56..0000000 +--- a/syscare-common/src/process/stdio.rs ++++ /dev/null +@@ -1,179 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscare-common is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ +- collections::HashMap, +- ffi::OsString, +- io::BufReader, +- os::unix::{ +- ffi::OsStringExt, +- io::{AsRawFd, RawFd}, +- }, +- process::{ChildStderr, ChildStdout}, +- thread::JoinHandle, +-}; +- +-use anyhow::{Context, Result}; +-use log::{error, log, Level}; +- +-use crate::io::{BufReadOsLines, OsLines, Select, SelectResult}; +- +-#[derive(Debug, Clone, Copy)] +-pub struct StdioLevel { +- pub(super) stdout: Option, +- pub(super) stderr: Option, +-} +- +-impl Default for StdioLevel { +- fn default() -> Self { +- Self { +- stdout: None, +- stderr: Some(Level::Error), +- } +- } +-} +- +-pub enum StdioOutput { +- Stdout(OsString), +- Stderr(OsString), +-} +- +-enum StdioLines { +- Stdout(OsLines>), +- Stderr(OsLines>), +-} +- +-struct StdioReader { +- select: Select, +- stdio_map: HashMap, +- line_buf: Vec, +-} +- +-impl StdioReader { +- fn new(stdout: ChildStdout, stderr: ChildStderr) -> Self { +- let line_buf = Vec::new(); +- let stdio_map = HashMap::from([ +- ( +- stdout.as_raw_fd(), +- StdioLines::Stdout(BufReader::new(stdout).os_lines()), +- ), +- ( +- stderr.as_raw_fd(), +- StdioLines::Stderr(BufReader::new(stderr).os_lines()), +- ), +- ]); +- let select = Select::new(stdio_map.keys().copied()); +- +- Self { +- select, +- stdio_map, +- line_buf, +- } +- } +-} +- +-impl Iterator for StdioReader { +- type Item = StdioOutput; +- +- fn next(&mut self) -> Option { +- match self.select.select().context("Failed to select stdio") { +- Ok(result) => { +- let stdio_map = &mut self.stdio_map; +- let outputs = result.into_iter().filter_map(|income| match income { +- SelectResult::Readable(fd) => { +- stdio_map.get_mut(&fd).and_then(|stdio| match stdio { +- StdioLines::Stdout(lines) => { +- lines.next().and_then(Result::ok).map(StdioOutput::Stdout) +- } +- StdioLines::Stderr(lines) => { +- lines.next().and_then(Result::ok).map(StdioOutput::Stderr) +- } +- }) +- } +- _ => None, +- }); +- self.line_buf.extend(outputs); +- } +- Err(e) => { +- error!("{:?}", e); +- } +- }; +- +- self.line_buf.pop() +- } +-} +- +-pub struct Stdio { +- name: String, +- stdout: ChildStdout, +- stderr: ChildStderr, +- level: StdioLevel, +-} +- +-impl Stdio { +- pub fn new(name: String, stdout: ChildStdout, stderr: ChildStderr, level: StdioLevel) -> Self { +- Self { +- name, +- stdout, +- stderr, +- level, +- } +- } +- +- pub fn capture(self) -> Result> { +- let stdio_level = self.level; +- let stdio_reader = StdioReader::new(self.stdout, self.stderr); +- +- let thread_name = self.name.as_str(); +- let thread = std::thread::Builder::new() +- .name(thread_name.to_string()) +- .spawn(move || -> (OsString, OsString) { +- let mut stdout_buf = Vec::new(); +- let mut stderr_buf = Vec::new(); +- +- for output in stdio_reader { +- match output { +- StdioOutput::Stdout(str) => { +- if let Some(level) = stdio_level.stdout { +- log!(level, "{}", str.to_string_lossy()); +- } +- stdout_buf.extend(str.into_vec()); +- stdout_buf.push(b'\n'); +- } +- StdioOutput::Stderr(str) => { +- if let Some(level) = stdio_level.stderr { +- log!(level, "{}", str.to_string_lossy()); +- } +- stderr_buf.extend(str.into_vec()); +- stderr_buf.push(b'\n'); +- } +- } +- } +- if stdout_buf.ends_with(b"\n") { +- stdout_buf.pop(); +- } +- if stderr_buf.ends_with(b"\n") { +- stderr_buf.pop(); +- } +- +- ( +- OsString::from_vec(stdout_buf), +- OsString::from_vec(stderr_buf), +- ) +- }) +- .with_context(|| format!("Failed to create thread {}", thread_name))?; +- +- Ok(thread) +- } +-} +diff --git a/syscare-common/src/util/digest.rs b/syscare-common/src/util/digest.rs +index 086b636..4ed7357 100644 +--- a/syscare-common/src/util/digest.rs ++++ b/syscare-common/src/util/digest.rs +@@ -14,23 +14,21 @@ + + use std::path::Path; + +-use sha2::Digest; +-use sha2::Sha256; ++use nix::errno::Errno; ++use sha2::{Digest, Sha256}; + + use crate::fs; + +-pub fn bytes>(bytes: S) -> String { +- let mut hasher = Sha256::new(); +- hasher.update(bytes); +- +- format!("{:#x}", hasher.finalize()) ++pub fn bytes>(bytes: T) -> String { ++ format!("{:#x}", Sha256::digest(bytes)) + } + +-pub fn file>(file: P) -> std::io::Result { +- let mut hasher = Sha256::new(); +- hasher.update(fs::read(file)?); +- +- Ok(format!("{:#x}", hasher.finalize())) ++pub fn file>(path: P) -> std::io::Result { ++ let file_path = path.as_ref(); ++ if !file_path.is_file() { ++ return Err(std::io::Error::from(Errno::EINVAL)); ++ } ++ Ok(self::bytes(&*fs::mmap(file_path)?)) + } + + pub fn file_list(file_list: I) -> std::io::Result +@@ -39,9 +37,201 @@ where + P: AsRef, + { + let mut hasher = Sha256::new(); ++ + for file in file_list { +- hasher.update(fs::read(file)?); ++ let file_path = file.as_ref(); ++ if file_path.is_file() { ++ hasher.update(&*fs::mmap(file)?); ++ } + } + + Ok(format!("{:#x}", hasher.finalize())) + } ++ ++pub fn dir>(path: P) -> std::io::Result { ++ let dir_path = path.as_ref(); ++ if !dir_path.is_dir() { ++ return Err(std::io::Error::from(Errno::EINVAL)); ++ } ++ ++ let mut file_list = fs::list_files(path, fs::TraverseOptions { recursive: true })?; ++ file_list.sort_unstable(); ++ ++ self::file_list(file_list) ++} ++ ++pub fn path>(path: P) -> std::io::Result { ++ if path.as_ref().is_file() { ++ self::file(path) ++ } else { ++ self::dir(path) ++ } ++} ++ ++#[cfg(test)] ++mod tests { ++ use std::{ ++ fs::File, ++ io::Write, ++ path::PathBuf, ++ time::{SystemTime, UNIX_EPOCH}, ++ }; ++ ++ use super::*; ++ ++ fn unique_name(prefix: &str) -> std::io::Result { ++ let timestamp = SystemTime::now() ++ .duration_since(UNIX_EPOCH) ++ .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; ++ Ok(format!("{}_{}", prefix, timestamp.as_nanos())) ++ } ++ ++ fn create_temp_file(content: &[u8]) -> std::io::Result { ++ let temp_file = std::env::temp_dir().join(self::unique_name("digest_test")?); ++ ++ let mut file = File::create(&temp_file)?; ++ file.write_all(content)?; ++ ++ Ok(temp_file) ++ } ++ ++ fn create_temp_dir() -> std::io::Result { ++ let temp_dir = std::env::temp_dir(); ++ let test_dir = temp_dir.join(unique_name("test_dir")?); ++ ++ fs::create_dir(&test_dir)?; ++ File::create(test_dir.join("file1.txt"))?.write_all(b"file1")?; ++ File::create(test_dir.join("file2.bin"))?.write_all(b"file2")?; ++ fs::create_dir(test_dir.join("subdir"))?; ++ File::create(test_dir.join("subdir/file3.txt"))?.write_all(b"file3")?; ++ ++ Ok(test_dir) ++ } ++ ++ #[test] ++ fn test_bytes() -> std::io::Result<()> { ++ assert_eq!( ++ self::bytes(b"hello"), ++ "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" ++ ); ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_file() -> std::io::Result<()> { ++ let file_path = self::create_temp_file(b"hello")?; ++ let hash = self::file(&file_path)?; ++ ++ std::fs::remove_file(&file_path).ok(); ++ assert_eq!( ++ hash, ++ "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" ++ ); ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_file_not_exists() -> std::io::Result<()> { ++ let result = self::file("/non_exist_file"); ++ ++ assert!(result.is_err()); ++ if let Err(e) = &result { ++ assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); ++ } ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_file_is_dir() -> std::io::Result<()> { ++ let result = self::file(std::env::temp_dir()); ++ ++ assert!(result.is_err()); ++ if let Err(e) = &result { ++ assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); ++ } ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_file_list() -> std::io::Result<()> { ++ let files = vec![ ++ self::create_temp_file(b"file1")?, ++ self::create_temp_file(b"file2")?, ++ self::create_temp_file(b"file3")?, ++ ]; ++ ++ let hash = self::file_list(&files)?; ++ for file in files { ++ std::fs::remove_file(file)?; ++ } ++ assert_eq!( ++ hash, ++ "d944e85974a48cfc20a944738d9617ad5ffde6e1219cf4c362dc058a47419848" ++ ); ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_dir() -> std::io::Result<()> { ++ let test_dir = self::create_temp_dir()?; ++ let hash = self::dir(&test_dir)?; ++ std::fs::remove_dir_all(test_dir)?; ++ ++ assert_eq!( ++ hash, ++ "d944e85974a48cfc20a944738d9617ad5ffde6e1219cf4c362dc058a47419848" ++ ); ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_dir_not_exists() -> std::io::Result<()> { ++ let result = self::dir("/non_exist_file"); ++ ++ assert!(result.is_err()); ++ if let Err(e) = &result { ++ assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); ++ } ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_dir_is_file() -> std::io::Result<()> { ++ let file_path = self::create_temp_file(b"hello")?; ++ let result = self::dir(&file_path); ++ std::fs::remove_file(file_path)?; ++ ++ assert!(result.is_err()); ++ if let Err(e) = &result { ++ assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); ++ } ++ ++ Ok(()) ++ } ++ ++ #[test] ++ fn test_path() -> std::io::Result<()> { ++ let file_path = self::create_temp_file(b"hello")?; ++ let dir_path = self::create_temp_dir()?; ++ ++ let file_hash = self::path(&file_path)?; ++ let dir_hash = self::path(&dir_path)?; ++ std::fs::remove_file(&file_path)?; ++ std::fs::remove_dir_all(&dir_path)?; ++ ++ assert_eq!( ++ file_hash, ++ "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" ++ ); ++ assert_eq!( ++ dir_hash, ++ "d944e85974a48cfc20a944738d9617ad5ffde6e1219cf4c362dc058a47419848" ++ ); ++ Ok(()) ++ } ++} +-- +2.43.0 + diff --git a/0135-syscared-adapt-common-crate-change.patch b/0135-syscared-adapt-common-crate-change.patch new file mode 100644 index 0000000000000000000000000000000000000000..713e193e4aa95e6d8ce5593f93a47106bc0d383b --- /dev/null +++ b/0135-syscared-adapt-common-crate-change.patch @@ -0,0 +1,98 @@ +From 217a30547f8c6c52cbed382a7e2162b3caf90899 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 29 May 2025 18:13:14 +0800 +Subject: [PATCH] syscared: adapt common crate change + +Signed-off-by: renoseven +--- + syscared/src/main.rs | 21 ++++++++------------- + syscared/src/patch/driver/kpatch/sys.rs | 12 ++++++------ + 2 files changed, 14 insertions(+), 19 deletions(-) + +diff --git a/syscared/src/main.rs b/syscared/src/main.rs +index f965497..bfff3d7 100644 +--- a/syscared/src/main.rs ++++ b/syscared/src/main.rs +@@ -59,10 +59,6 @@ const LOG_DIR_PERM: u32 = 0o700; + const SOCKET_FILE_PERM: u32 = 0o660; + const SOCKET_FILE_PERM_STRICT: u32 = 0o600; + +-const MAIN_THREAD_NAME: &str = "main"; +-const UNNAMED_THREAD_NAME: &str = ""; +-const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f"; +- + struct Daemon { + args: Arguments, + config: Config, +@@ -74,15 +70,14 @@ impl Daemon { + now: &mut DeferredNow, + record: &Record, + ) -> std::io::Result<()> { ++ const UNNAMED_THREAD_NAME: &str = ""; ++ const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f"; ++ + thread_local! { +- static THREAD_NAME: String = std::thread::current().name().and_then(|name| { +- if name == MAIN_THREAD_NAME { +- return os::process::name().to_str(); +- } +- Some(name) +- }) +- .unwrap_or(UNNAMED_THREAD_NAME) +- .to_string(); ++ static THREAD_NAME: String = std::thread::current() ++ .name() ++ .unwrap_or(UNNAMED_THREAD_NAME) ++ .to_string(); + } + + THREAD_NAME.with(|thread_name| { +@@ -100,7 +95,7 @@ impl Daemon { + fn new() -> Result { + // Check root permission + ensure!( +- os::user::id() == 0, ++ os::user::uid() == 0, + "This command has to be run with superuser privileges (under the root user on most systems)." + ); + +diff --git a/syscared/src/patch/driver/kpatch/sys.rs b/syscared/src/patch/driver/kpatch/sys.rs +index fd5160c..adabfb4 100644 +--- a/syscared/src/patch/driver/kpatch/sys.rs ++++ b/syscared/src/patch/driver/kpatch/sys.rs +@@ -19,7 +19,7 @@ use log::debug; + use nix::kmod; + + use syscare_abi::PatchStatus; +-use syscare_common::{ffi::OsStrExt, fs, os}; ++use syscare_common::{ffi::OsStrExt, fs, os::selinux}; + + use crate::patch::entity::KernelPatch; + +@@ -39,7 +39,7 @@ pub fn list_kernel_modules() -> Result> { + pub fn selinux_relable_patch(patch: &KernelPatch) -> Result<()> { + const KPATCH_PATCH_SEC_TYPE: &str = "modules_object_t"; + +- if os::selinux::get_status()? != os::selinux::Status::Enforcing { ++ if selinux::get_status() != selinux::Status::Enforcing { + return Ok(()); + } + +@@ -47,10 +47,10 @@ pub fn selinux_relable_patch(patch: &KernelPatch) -> Result<()> { + "Relabeling patch module '{}'...", + patch.module_name.to_string_lossy() + ); +- let mut sec_context = os::selinux::get_security_context(&patch.patch_file)?; +- if sec_context.kind != KPATCH_PATCH_SEC_TYPE { +- sec_context.kind = OsString::from(KPATCH_PATCH_SEC_TYPE); +- os::selinux::set_security_context(&patch.patch_file, sec_context)?; ++ let mut sec_context = selinux::get_security_context(&patch.patch_file)?; ++ if sec_context.get_type() != KPATCH_PATCH_SEC_TYPE { ++ sec_context.set_type(OsString::from(KPATCH_PATCH_SEC_TYPE))?; ++ selinux::set_security_context(&patch.patch_file, &sec_context)?; + } + + Ok(()) +-- +2.43.0 + diff --git a/0136-syscared-rewrite-patch-parsing-management.patch b/0136-syscared-rewrite-patch-parsing-management.patch new file mode 100644 index 0000000000000000000000000000000000000000..a58ea1ca9d92c797291eb25f6a48e1e63da0ff13 --- /dev/null +++ b/0136-syscared-rewrite-patch-parsing-management.patch @@ -0,0 +1,3184 @@ +From b491611bc9ddf46af6b428e3d77e4e93731e2711 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 3 Jun 2025 15:55:03 +0800 +Subject: [PATCH] syscared: rewrite patch parsing & management + +Signed-off-by: renoseven +--- + syscared/src/config.rs | 22 +- + syscared/src/patch/driver/kpatch/mod.rs | 302 +++++++---------- + syscared/src/patch/driver/kpatch/sys.rs | 119 +++---- + syscared/src/patch/driver/kpatch/target.rs | 144 +++----- + syscared/src/patch/driver/mod.rs | 84 +++-- + syscared/src/patch/driver/upatch/entity.rs | 60 ---- + syscared/src/patch/driver/upatch/mod.rs | 348 +++++++------------- + syscared/src/patch/driver/upatch/monitor.rs | 30 +- + syscared/src/patch/driver/upatch/sys.rs | 32 +- + syscared/src/patch/driver/upatch/target.rs | 158 ++++----- + syscared/src/patch/entity/kpatch.rs | 224 ++++++++++++- + syscared/src/patch/entity/patch.rs | 5 +- + syscared/src/patch/entity/symbol.rs | 54 --- + syscared/src/patch/entity/upatch.rs | 181 +++++++++- + syscared/src/patch/manager.rs | 169 +++++++--- + syscared/src/patch/mod.rs | 1 - + syscared/src/patch/resolver/kpatch.rs | 226 ------------- + syscared/src/patch/resolver/mod.rs | 63 ---- + syscared/src/patch/resolver/upatch.rs | 182 ---------- + 19 files changed, 1053 insertions(+), 1351 deletions(-) + delete mode 100644 syscared/src/patch/driver/upatch/entity.rs + delete mode 100644 syscared/src/patch/entity/symbol.rs + delete mode 100644 syscared/src/patch/resolver/kpatch.rs + delete mode 100644 syscared/src/patch/resolver/mod.rs + delete mode 100644 syscared/src/patch/resolver/upatch.rs + +diff --git a/syscared/src/config.rs b/syscared/src/config.rs +index 7103273..5e0d736 100644 +--- a/syscared/src/config.rs ++++ b/syscared/src/config.rs +@@ -83,17 +83,27 @@ pub struct Config { + impl Config { + pub fn parse>(path: P) -> Result { + let config_path = path.as_ref(); +- let instance = serde_yaml::from_reader(fs::open_file(config_path)?) +- .map_err(|_| anyhow!("Failed to parse config {}", config_path.display()))?; +- +- Ok(instance) ++ let config = serde_yaml::from_reader(fs::open_file(config_path)?).map_err(|e| { ++ anyhow!( ++ "Failed to parse config '{}', {}", ++ config_path.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ ++ Ok(config) + } + + pub fn write>(&self, path: P) -> Result<()> { + let config_path = path.as_ref(); + let config_file = fs::create_file(config_path)?; +- serde_yaml::to_writer(config_file, self) +- .map_err(|_| anyhow!("Failed to write config {}", config_path.display()))?; ++ serde_yaml::to_writer(config_file, self).map_err(|e| { ++ anyhow!( ++ "Failed to write config '{}', {}", ++ config_path.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; + + Ok(()) + } +diff --git a/syscared/src/patch/driver/kpatch/mod.rs b/syscared/src/patch/driver/kpatch/mod.rs +index ba177ac..8e39670 100644 +--- a/syscared/src/patch/driver/kpatch/mod.rs ++++ b/syscared/src/patch/driver/kpatch/mod.rs +@@ -13,103 +13,58 @@ + */ + + use std::{ +- ffi::{OsStr, OsString}, ++ collections::{HashMap, HashSet}, ++ ffi::OsString, + fmt::Write, + iter::FromIterator, + }; + +-use anyhow::{ensure, Result}; +-use indexmap::{indexset, IndexMap, IndexSet}; +-use log::{debug, info}; ++use anyhow::{anyhow, ensure, Context, Result}; ++use log::debug; + + use syscare_abi::PatchStatus; +-use syscare_common::{concat_os, os, util::digest}; +- +-use crate::{ +- config::KernelPatchConfig, +- patch::entity::{KernelPatch, KernelPatchFunction}, ++use syscare_common::{ ++ concat_os, ++ os::{self, kernel, selinux}, ++ util::digest, + }; + ++use crate::{config::KernelPatchConfig, patch::entity::KernelPatch}; ++ + mod sys; + mod target; + + use target::PatchTarget; + + pub struct KernelPatchDriver { +- target_map: IndexMap, +- blocked_targets: IndexSet, ++ target_map: HashMap, // object name -> object ++ blocked_targets: HashSet, + } + + impl KernelPatchDriver { + pub fn new(config: &KernelPatchConfig) -> Result { + Ok(Self { +- target_map: IndexMap::new(), +- blocked_targets: IndexSet::from_iter(config.blocked.iter().cloned()), ++ target_map: HashMap::new(), ++ blocked_targets: HashSet::from_iter(config.blocked.iter().cloned()), + }) + } + } + + impl KernelPatchDriver { +- fn group_patch_targets(patch: &KernelPatch) -> IndexSet<&OsStr> { +- let mut patch_targets = IndexSet::new(); +- +- for function in &patch.functions { +- patch_targets.insert(function.object.as_os_str()); +- } +- patch_targets +- } +- +- pub fn group_patch_functions( +- patch: &KernelPatch, +- ) -> IndexMap<&OsStr, Vec<&KernelPatchFunction>> { +- let mut patch_function_map: IndexMap<&OsStr, Vec<&KernelPatchFunction>> = IndexMap::new(); +- +- for function in &patch.functions { +- patch_function_map +- .entry(function.object.as_os_str()) +- .or_default() +- .push(function); +- } +- patch_function_map +- } +-} +- +-impl KernelPatchDriver { +- fn add_patch_target(&mut self, patch: &KernelPatch) { +- for target_name in Self::group_patch_targets(patch) { +- if !self.target_map.contains_key(target_name) { +- self.target_map.insert( +- target_name.to_os_string(), +- PatchTarget::new(target_name.to_os_string()), +- ); +- } ++ fn register_patch(&mut self, patch: &KernelPatch) { ++ for object_name in patch.functions.keys() { ++ self.target_map ++ .entry(object_name.clone()) ++ .or_insert_with(|| PatchTarget::new(object_name.clone())) ++ .add_patch(patch); + } + } + +- fn remove_patch_target(&mut self, patch: &KernelPatch) { +- for target_name in Self::group_patch_targets(patch) { +- if let Some(target) = self.target_map.get_mut(target_name) { +- if !target.has_function() { +- self.target_map.remove(target_name); +- } +- } +- } +- } +- +- fn add_patch_functions(&mut self, patch: &KernelPatch) { +- for (target_name, functions) in Self::group_patch_functions(patch) { +- if let Some(target) = self.target_map.get_mut(target_name) { +- target.add_functions(patch.uuid, functions); +- } +- } +- } +- +- fn remove_patch_functions(&mut self, patch: &KernelPatch) { +- for (target_name, functions) in Self::group_patch_functions(patch) { +- if let Some(target) = self.target_map.get_mut(target_name) { +- target.remove_functions(&patch.uuid, functions); +- } +- } ++ fn unregister_patch(&mut self, patch: &KernelPatch) { ++ self.target_map.retain(|_, object| { ++ object.remove_patch(patch); ++ object.is_patched() ++ }); + } + } + +@@ -130,13 +85,14 @@ impl KernelPatchDriver { + const KERNEL_NAME_PREFIX: &str = "kernel-"; + + let patch_target = patch.pkg_name.as_str(); ++ if !patch_target.starts_with(KERNEL_NAME_PREFIX) { ++ return Ok(()); ++ } ++ + let current_kernel = concat_os!(KERNEL_NAME_PREFIX, os::kernel::version()); + debug!("Patch target: '{}'", patch_target); + debug!("Current kernel: '{}'", current_kernel.to_string_lossy()); + +- if !patch_target.starts_with(KERNEL_NAME_PREFIX) { +- return Ok(()); +- } + ensure!( + current_kernel == patch_target, + "Kpatch: Patch is incompatible", +@@ -147,156 +103,134 @@ impl KernelPatchDriver { + fn check_dependency(patch: &KernelPatch) -> Result<()> { + const VMLINUX_MODULE_NAME: &str = "vmlinux"; + +- let mut non_exist_kmod = IndexSet::new(); +- +- let kmod_list = sys::list_kernel_modules()?; +- for kmod_name in Self::group_patch_targets(patch) { +- if kmod_name == VMLINUX_MODULE_NAME { +- continue; +- } +- if kmod_list.iter().any(|name| name == kmod_name) { +- continue; +- } +- non_exist_kmod.insert(kmod_name); +- } +- +- ensure!(non_exist_kmod.is_empty(), { +- let mut err_msg = String::new(); +- +- writeln!(&mut err_msg, "Kpatch: Patch target does not exist")?; +- for kmod_name in non_exist_kmod { +- writeln!(&mut err_msg, "* Module '{}'", kmod_name.to_string_lossy())?; ++ let depend_modules = patch.functions.keys().cloned().collect::>(); ++ let inserted_modules = ++ kernel::list_modules().context("Kpatch: Failed to list kernel modules")?; ++ let needed_modules = depend_modules ++ .difference(&inserted_modules) ++ .filter(|&module_name| module_name != VMLINUX_MODULE_NAME) ++ .collect::>(); ++ ++ ensure!(needed_modules.is_empty(), { ++ let mut msg = String::new(); ++ writeln!(msg, "Kpatch: Patch target does not exist")?; ++ for name in needed_modules { ++ writeln!(msg, "* Module '{}'", name.to_string_lossy())?; + } +- err_msg.pop(); +- +- err_msg ++ msg.pop(); ++ msg + }); + Ok(()) + } + +- pub fn check_conflict_functions(&self, patch: &KernelPatch) -> Result<()> { +- let mut conflict_patches = indexset! {}; +- +- let target_functions = Self::group_patch_functions(patch); +- for (target_name, functions) in target_functions { +- if let Some(target) = self.target_map.get(target_name) { +- conflict_patches.extend( +- target +- .get_conflicts(functions) +- .into_iter() +- .map(|record| record.uuid), +- ); +- } +- } +- +- ensure!(conflict_patches.is_empty(), { +- let mut err_msg = String::new(); ++ pub fn check_conflicted_patches(&self, patch: &KernelPatch) -> Result<()> { ++ let conflicted: HashSet<_> = self ++ .target_map ++ .values() ++ .flat_map(|object| object.get_conflicted_patches(patch)) ++ .collect(); + +- writeln!(&mut err_msg, "Kpatch: Patch is conflicted with")?; +- for uuid in conflict_patches.into_iter() { +- writeln!(&mut err_msg, "* Patch '{}'", uuid)?; ++ ensure!(conflicted.is_empty(), { ++ let mut msg = String::new(); ++ writeln!(msg, "Kpatch: Patch is conflicted with")?; ++ for uuid in conflicted { ++ writeln!(msg, "* Patch '{}'", uuid)?; + } +- err_msg.pop(); +- +- err_msg ++ msg.pop(); ++ msg + }); + Ok(()) + } + +- pub fn check_override_functions(&self, patch: &KernelPatch) -> Result<()> { +- let mut override_patches = indexset! {}; +- +- let target_functions = Self::group_patch_functions(patch); +- for (target_name, functions) in target_functions { +- if let Some(target) = self.target_map.get(target_name) { +- override_patches.extend( +- target +- .get_overrides(&patch.uuid, functions) +- .into_iter() +- .map(|record| record.uuid), +- ); +- } +- } +- +- ensure!(override_patches.is_empty(), { +- let mut err_msg = String::new(); ++ pub fn check_overridden_patches(&self, patch: &KernelPatch) -> Result<()> { ++ let overridden: HashSet<_> = self ++ .target_map ++ .values() ++ .flat_map(|object| object.get_overridden_patches(patch)) ++ .collect(); + +- writeln!(&mut err_msg, "Kpatch: Patch is overrided by")?; +- for uuid in override_patches.into_iter() { +- writeln!(&mut err_msg, "* Patch '{}'", uuid)?; ++ ensure!(overridden.is_empty(), { ++ let mut msg = String::new(); ++ writeln!(msg, "Kpatch: Patch is overridden by")?; ++ for uuid in overridden { ++ writeln!(msg, "* Patch '{}'", uuid)?; + } +- err_msg.pop(); +- +- err_msg ++ msg.pop(); ++ msg + }); + Ok(()) + } + } + + impl KernelPatchDriver { +- pub fn status(&self, patch: &KernelPatch) -> Result { +- sys::read_patch_status(patch) +- } +- +- pub fn check(&self, patch: &KernelPatch) -> Result<()> { ++ pub fn check_patch(&self, patch: &KernelPatch) -> Result<()> { + Self::check_consistency(patch)?; + Self::check_compatiblity(patch)?; + Self::check_dependency(patch)?; +- + Ok(()) + } + +- pub fn apply(&mut self, patch: &KernelPatch) -> Result<()> { +- info!( +- "Applying patch '{}' ({})", +- patch.uuid, +- patch.patch_file.display() +- ); ++ pub fn get_patch_status(&self, patch: &KernelPatch) -> Result { ++ sys::get_patch_status(&patch.status_file).map_err(|e| { ++ anyhow!( ++ "Kpatch: Failed to get patch status, {}", ++ e.to_string().to_lowercase() ++ ) ++ }) ++ } + ++ pub fn load_patch(&mut self, patch: &KernelPatch) -> Result<()> { + ensure!( + !self.blocked_targets.contains(&patch.target_name), +- "Patch target '{}' is blocked", ++ "Kpatch: Patch target '{}' is blocked", + patch.target_name.to_string_lossy(), + ); +- sys::selinux_relable_patch(patch)?; +- sys::apply_patch(patch)?; +- self.add_patch_target(patch); + +- Ok(()) ++ if selinux::get_status() == selinux::Status::Enforcing { ++ kernel::relable_module_file(&patch.patch_file).map_err(|e| { ++ anyhow!( ++ "Kpatch: Failed to relable patch file, {}", ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ } ++ sys::load_patch(&patch.patch_file).map_err(|e| { ++ anyhow!( ++ "Kpatch: Failed to load patch, {}", ++ e.to_string().to_lowercase() ++ ) ++ }) + } + +- pub fn remove(&mut self, patch: &KernelPatch) -> Result<()> { +- info!( +- "Removing patch '{}' ({})", +- patch.uuid, +- patch.patch_file.display() +- ); +- sys::remove_patch(patch)?; +- self.remove_patch_target(patch); +- +- Ok(()) ++ pub fn remove_patch(&mut self, patch: &KernelPatch) -> Result<()> { ++ sys::remove_patch(&patch.module.name).map_err(|e| { ++ anyhow!( ++ "Kpatch: Failed to remove patch, {}", ++ e.to_string().to_lowercase() ++ ) ++ }) + } + +- pub fn active(&mut self, patch: &KernelPatch) -> Result<()> { +- info!( +- "Activating patch '{}' ({})", +- patch.uuid, +- patch.patch_file.display() +- ); +- sys::active_patch(patch)?; +- self.add_patch_functions(patch); ++ pub fn active_patch(&mut self, patch: &KernelPatch) -> Result<()> { ++ sys::active_patch(&patch.status_file).map_err(|e| { ++ anyhow!( ++ "Kpatch: Failed to active patch, {}", ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ self.register_patch(patch); + + Ok(()) + } + +- pub fn deactive(&mut self, patch: &KernelPatch) -> Result<()> { +- info!( +- "Deactivating patch '{}' ({})", +- patch.uuid, +- patch.patch_file.display() +- ); +- sys::deactive_patch(patch)?; +- self.remove_patch_functions(patch); ++ pub fn deactive_patch(&mut self, patch: &KernelPatch) -> Result<()> { ++ sys::deactive_patch(&patch.status_file).map_err(|e| { ++ anyhow!( ++ "Kpatch: Failed to deactive patch, {}", ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ self.unregister_patch(patch); + + Ok(()) + } +diff --git a/syscared/src/patch/driver/kpatch/sys.rs b/syscared/src/patch/driver/kpatch/sys.rs +index adabfb4..838fe3b 100644 +--- a/syscared/src/patch/driver/kpatch/sys.rs ++++ b/syscared/src/patch/driver/kpatch/sys.rs +@@ -12,106 +12,69 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::{CString, OsString}; ++use std::{ffi::OsStr, fs, path::Path}; + +-use anyhow::{anyhow, bail, Context, Result}; + use log::debug; +-use nix::kmod; ++use nix::errno::Errno; + + use syscare_abi::PatchStatus; +-use syscare_common::{ffi::OsStrExt, fs, os::selinux}; ++use syscare_common::os::kernel; + +-use crate::patch::entity::KernelPatch; ++const KPATCH_STATUS_DISABLE: &str = "0"; ++const KPATCH_STATUS_ENABLE: &str = "1"; + +-const SYS_MODULE_DIR: &str = "/sys/module"; +-const KPATCH_STATUS_DISABLED: &str = "0"; +-const KPATCH_STATUS_ENABLED: &str = "1"; +- +-pub fn list_kernel_modules() -> Result> { +- let module_names = fs::list_dirs(SYS_MODULE_DIR, fs::TraverseOptions { recursive: false })? +- .into_iter() +- .filter_map(|dir| dir.file_name().map(|name| name.to_os_string())) +- .collect(); +- +- Ok(module_names) +-} +- +-pub fn selinux_relable_patch(patch: &KernelPatch) -> Result<()> { +- const KPATCH_PATCH_SEC_TYPE: &str = "modules_object_t"; +- +- if selinux::get_status() != selinux::Status::Enforcing { +- return Ok(()); +- } ++pub fn load_patch>(patch_file: P) -> std::io::Result<()> { ++ let patch_file = patch_file.as_ref(); + + debug!( +- "Relabeling patch module '{}'...", +- patch.module_name.to_string_lossy() ++ "Kpatch: Inserting patch module '{}'...", ++ patch_file.display() + ); +- let mut sec_context = selinux::get_security_context(&patch.patch_file)?; +- if sec_context.get_type() != KPATCH_PATCH_SEC_TYPE { +- sec_context.set_type(OsString::from(KPATCH_PATCH_SEC_TYPE))?; +- selinux::set_security_context(&patch.patch_file, &sec_context)?; +- } +- +- Ok(()) ++ kernel::insert_module(patch_file) + } + +-pub fn read_patch_status(patch: &KernelPatch) -> Result { +- let sys_file = patch.sys_file.as_path(); +- debug!("Reading {}", sys_file.display()); +- +- let status = match fs::read_to_string(sys_file) { +- Ok(str) => match str.trim() { +- KPATCH_STATUS_DISABLED => Ok(PatchStatus::Deactived), +- KPATCH_STATUS_ENABLED => Ok(PatchStatus::Actived), +- _ => bail!("Kpatch: Invalid patch status"), +- }, +- Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(PatchStatus::NotApplied), +- Err(e) => Err(e), +- } +- .context("Kpatch: Failed to read patch status")?; ++pub fn remove_patch>(module_name: S) -> std::io::Result<()> { ++ let module_name = module_name.as_ref(); + +- Ok(status) ++ debug!( ++ "Kpatch: Removing patch module '{}'...", ++ module_name.to_string_lossy() ++ ); ++ kernel::remove_module(module_name) + } + +-fn write_patch_status(patch: &KernelPatch, value: &str) -> Result<()> { +- let sys_file = patch.sys_file.as_path(); ++pub fn active_patch>(status_file: P) -> std::io::Result<()> { ++ let status_file = status_file.as_ref(); + +- debug!("Writing '{}' to {}", value, sys_file.display()); +- fs::write(sys_file, value).context("Kpatch: Failed to write patch status") +-} +- +-pub fn apply_patch(patch: &KernelPatch) -> Result<()> { + debug!( +- "Inserting patch module '{}'...", +- patch.module_name.to_string_lossy() ++ "Kpatch: Writing '{}' to '{}'...", ++ stringify!(KPATCH_STATUS_ENABLE), ++ status_file.display() + ); +- let patch_module = fs::open_file(&patch.patch_file)?; +- kmod::finit_module( +- &patch_module, +- CString::new("")?.as_c_str(), +- kmod::ModuleInitFlags::empty(), +- ) +- .map_err(|e| anyhow!("Kpatch: {}", std::io::Error::from(e))) ++ fs::write(status_file, KPATCH_STATUS_ENABLE) + } + +-pub fn remove_patch(patch: &KernelPatch) -> Result<()> { ++pub fn deactive_patch>(status_file: P) -> std::io::Result<()> { ++ let status_file = status_file.as_ref(); ++ + debug!( +- "Removing patch module '{}'...", +- patch.module_name.to_string_lossy() ++ "Kpatch: Writing '{}' to '{}'...", ++ stringify!(KPATCH_STATUS_DISABLE), ++ status_file.display() + ); +- +- kmod::delete_module( +- patch.module_name.to_cstring()?.as_c_str(), +- kmod::DeleteModuleFlags::O_NONBLOCK, +- ) +- .map_err(|e| anyhow!("Kpatch: {}", std::io::Error::from(e))) ++ fs::write(status_file, KPATCH_STATUS_DISABLE) + } + +-pub fn active_patch(patch: &KernelPatch) -> Result<()> { +- self::write_patch_status(patch, KPATCH_STATUS_ENABLED) +-} ++pub fn get_patch_status>(status_file: P) -> std::io::Result { ++ let status_file = status_file.as_ref(); ++ if !status_file.exists() { ++ return Ok(PatchStatus::NotApplied); ++ } + +-pub fn deactive_patch(patch: &KernelPatch) -> Result<()> { +- self::write_patch_status(patch, KPATCH_STATUS_DISABLED) ++ debug!("Kpatch: Reading '{}'...", status_file.display()); ++ match fs::read_to_string(status_file)?.trim() { ++ KPATCH_STATUS_DISABLE => Ok(PatchStatus::Deactived), ++ KPATCH_STATUS_ENABLE => Ok(PatchStatus::Actived), ++ _ => Err(std::io::Error::from(Errno::EINVAL)), ++ } + } +diff --git a/syscared/src/patch/driver/kpatch/target.rs b/syscared/src/patch/driver/kpatch/target.rs +index 49f1185..3f4a465 100644 +--- a/syscared/src/patch/driver/kpatch/target.rs ++++ b/syscared/src/patch/driver/kpatch/target.rs +@@ -12,126 +12,88 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::OsString; ++use std::{ ++ collections::{hash_map::Entry, HashMap}, ++ ffi::OsString, ++}; + +-use indexmap::IndexMap; ++use indexmap::IndexSet; + use uuid::Uuid; + +-use crate::patch::entity::KernelPatchFunction; +- +-#[derive(Debug)] +-pub struct PatchFunction { +- pub uuid: Uuid, +- pub name: OsString, +- pub size: u64, +-} +- +-impl PatchFunction { +- fn new(uuid: Uuid, function: &KernelPatchFunction) -> Self { +- Self { +- uuid, +- name: function.name.to_os_string(), +- size: function.new_size, +- } +- } +- +- fn is_same_function(&self, uuid: &Uuid, function: &KernelPatchFunction) -> bool { +- (self.uuid == *uuid) && (self.name == function.name) && (self.size == function.new_size) +- } +-} ++use crate::patch::entity::KernelPatch; + + #[derive(Debug)] + pub struct PatchTarget { +- name: OsString, +- function_map: IndexMap>, // function addr -> function collision list ++ object_name: OsString, ++ collision_map: HashMap>, // function name -> patch collision list + } + + impl PatchTarget { +- pub fn new(name: OsString) -> Self { ++ pub fn new(object_name: OsString) -> Self { + Self { +- name, +- function_map: IndexMap::new(), ++ object_name, ++ collision_map: HashMap::new(), + } + } + } + + impl PatchTarget { +- pub fn has_function(&self) -> bool { +- self.function_map.is_empty() +- } +- +- pub fn add_functions<'a, I>(&mut self, uuid: Uuid, functions: I) +- where +- I: IntoIterator, +- { +- for function in functions { +- if self.name != function.object { +- continue; ++ pub fn add_patch(&mut self, patch: &KernelPatch) { ++ if let Some(functions) = patch.functions.get(&self.object_name) { ++ for function in functions { ++ self.collision_map ++ .entry(function.name.clone()) ++ .or_default() ++ .insert(patch.uuid); + } +- self.function_map +- .entry(function.name.clone()) +- .or_default() +- .push(PatchFunction::new(uuid, function)); + } + } + +- pub fn remove_functions<'a, I>(&mut self, uuid: &Uuid, functions: I) +- where +- I: IntoIterator, +- { +- for function in functions { +- if self.name != function.object { +- continue; +- } +- if let Some(collision_list) = self.function_map.get_mut(&function.name) { +- if let Some(index) = collision_list +- .iter() +- .position(|patch_function| patch_function.is_same_function(uuid, function)) ++ pub fn remove_patch(&mut self, patch: &KernelPatch) { ++ if let Some(functions) = patch.functions.get(&self.object_name) { ++ for function in functions { ++ if let Entry::Occupied(mut entry) = self.collision_map.entry(function.name.clone()) + { +- collision_list.remove(index); +- if collision_list.is_empty() { +- self.function_map.remove(&function.name); ++ let patch_set = entry.get_mut(); ++ patch_set.shift_remove(&patch.uuid); ++ ++ if patch_set.is_empty() { ++ entry.remove(); + } + } + } + } + } +-} + +-impl PatchTarget { +- pub fn get_conflicts<'a, I>( ++ pub fn is_patched(&self) -> bool { ++ !self.collision_map.is_empty() ++ } ++ ++ pub fn get_conflicted_patches<'a>( + &'a self, +- functions: I, +- ) -> impl IntoIterator +- where +- I: IntoIterator, +- { +- functions.into_iter().filter_map(move |function| { +- if self.name != function.object { +- return None; +- } +- self.function_map +- .get(&function.name) +- .and_then(|list| list.last()) +- }) ++ patch: &'a KernelPatch, ++ ) -> impl Iterator + 'a { ++ let functions = patch.functions.get(&self.object_name).into_iter().flatten(); ++ functions ++ .filter_map(move |function| self.collision_map.get(&function.name)) ++ .flatten() ++ .copied() ++ .filter(move |&uuid| uuid != patch.uuid) + } + +- pub fn get_overrides<'a, I>( ++ pub fn get_overridden_patches<'a>( + &'a self, +- uuid: &'a Uuid, +- functions: I, +- ) -> impl IntoIterator +- where +- I: IntoIterator, +- { +- functions.into_iter().filter_map(move |function| { +- if self.name != function.object { +- return None; +- } +- self.function_map +- .get(&function.name) +- .and_then(|list| list.last()) +- .filter(|patch_function| !patch_function.is_same_function(uuid, function)) +- }) ++ patch: &'a KernelPatch, ++ ) -> impl Iterator + 'a { ++ let functions = patch.functions.get(&self.object_name).into_iter().flatten(); ++ functions ++ .filter_map(move |function| self.collision_map.get(&function.name)) ++ .flat_map(move |collision_list| { ++ collision_list ++ .iter() ++ .copied() ++ .skip_while(move |&uuid| uuid != patch.uuid) ++ .skip(1) ++ }) + } + } +diff --git a/syscared/src/patch/driver/mod.rs b/syscared/src/patch/driver/mod.rs +index 707bf2f..3752ca8 100644 +--- a/syscared/src/patch/driver/mod.rs ++++ b/syscared/src/patch/driver/mod.rs +@@ -14,7 +14,7 @@ + + use anyhow::{Context, Result}; + +-use log::info; ++use log::{debug, info}; + use syscare_abi::PatchStatus; + + mod kpatch; +@@ -39,17 +39,17 @@ pub struct PatchDriver { + } + + impl PatchDriver { +- fn check_conflict_functions(&self, patch: &Patch) -> Result<()> { ++ fn check_conflicted_patches(&self, patch: &Patch) -> Result<()> { + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.check_conflict_functions(kpatch), +- Patch::UserPatch(upatch) => self.upatch.check_conflict_functions(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.check_conflicted_patches(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.check_conflicted_patches(upatch), + } + } + +- fn check_override_functions(&self, patch: &Patch) -> Result<()> { ++ fn check_overridden_patches(&self, patch: &Patch) -> Result<()> { + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.check_override_functions(kpatch), +- Patch::UserPatch(upatch) => self.upatch.check_override_functions(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.check_overridden_patches(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.check_overridden_patches(upatch), + } + } + } +@@ -70,54 +70,62 @@ impl PatchDriver { + }) + } + +- /// Fetch and return the patch status. +- pub fn patch_status(&self, patch: &Patch) -> Result { +- match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.status(kpatch), +- Patch::UserPatch(upatch) => self.upatch.status(upatch), ++ /// Perform patch confliction check.
++ /// Used for patch check. ++ pub fn check_confliction(&self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { ++ if flag == PatchOpFlag::Force { ++ return Ok(()); + } +- .with_context(|| format!("Failed to get patch '{}' status", patch)) ++ self.check_conflicted_patches(patch) ++ .with_context(|| format!("Patch '{}' is conflicted", patch)) + } + + /// Perform patch file intergrity & consistency check.
+ /// Should be used before patch application. + pub fn check_patch(&self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { ++ info!("Checking patch '{}'...", patch); ++ + if flag == PatchOpFlag::Force { + return Ok(()); + } + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.check(kpatch), +- Patch::UserPatch(upatch) => self.upatch.check(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.check_patch(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.check_patch(upatch), + } + .with_context(|| format!("Patch '{}' is not patchable", patch)) + } + +- /// Perform patch confliction check.
+- /// Used for patch check. +- pub fn check_confliction(&self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { +- if flag == PatchOpFlag::Force { +- return Ok(()); ++ /// Fetch and return the patch status. ++ pub fn get_patch_status(&self, patch: &Patch) -> Result { ++ debug!("Fetching patch '{}' status...", patch); ++ ++ match patch { ++ Patch::KernelPatch(kpatch) => self.kpatch.get_patch_status(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.get_patch_status(upatch), + } +- self.check_conflict_functions(patch) +- .with_context(|| format!("Patch '{}' is conflicted", patch)) ++ .with_context(|| format!("Failed to get patch '{}' status", patch)) + } + +- /// Apply a patch.
++ /// Load a patch.
+ /// After this action, the patch status would be changed to 'DEACTIVED'. +- pub fn apply_patch(&mut self, patch: &Patch) -> Result<()> { ++ pub fn load_patch(&mut self, patch: &Patch) -> Result<()> { ++ info!("Loading patch '{}'...", patch); ++ + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.apply(kpatch), +- Patch::UserPatch(upatch) => self.upatch.apply(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.load_patch(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.load_patch(upatch), + } +- .with_context(|| format!("Failed to apply patch '{}'", patch)) ++ .with_context(|| format!("Failed to load patch '{}'", patch)) + } + + /// Remove a patch.
+ /// After this action, the patch status would be changed to 'NOT-APPLIED'. + pub fn remove_patch(&mut self, patch: &Patch) -> Result<()> { ++ info!("Removing patch '{}'...", patch); ++ + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.remove(kpatch), +- Patch::UserPatch(upatch) => self.upatch.remove(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.remove_patch(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.remove_patch(upatch), + } + .with_context(|| format!("Failed to remove patch '{}'", patch)) + } +@@ -125,12 +133,15 @@ impl PatchDriver { + /// Active a patch.
+ /// After this action, the patch status would be changed to 'ACTIVED'. + pub fn active_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { ++ info!("Activating patch '{}'...", patch); ++ + if flag != PatchOpFlag::Force { +- self.check_conflict_functions(patch)?; ++ self.check_conflicted_patches(patch)?; + } ++ + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.active(kpatch), +- Patch::UserPatch(upatch) => self.upatch.active(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.active_patch(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.active_patch(upatch), + } + .with_context(|| format!("Failed to active patch '{}'", patch)) + } +@@ -138,12 +149,15 @@ impl PatchDriver { + /// Deactive a patch.
+ /// After this action, the patch status would be changed to 'DEACTIVED'. + pub fn deactive_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { ++ info!("Deactivating patch '{}'...", patch); ++ + if flag != PatchOpFlag::Force { +- self.check_override_functions(patch)?; ++ self.check_overridden_patches(patch)?; + } ++ + match patch { +- Patch::KernelPatch(kpatch) => self.kpatch.deactive(kpatch), +- Patch::UserPatch(upatch) => self.upatch.deactive(upatch), ++ Patch::KernelPatch(kpatch) => self.kpatch.deactive_patch(kpatch), ++ Patch::UserPatch(upatch) => self.upatch.deactive_patch(upatch), + } + .with_context(|| format!("Failed to deactive patch '{}'", patch)) + } +diff --git a/syscared/src/patch/driver/upatch/entity.rs b/syscared/src/patch/driver/upatch/entity.rs +deleted file mode 100644 +index 5d0a9c0..0000000 +--- a/syscared/src/patch/driver/upatch/entity.rs ++++ /dev/null +@@ -1,60 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscared is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::path::PathBuf; +- +-use indexmap::{indexset, IndexSet}; +- +-#[derive(Debug)] +-pub struct PatchEntity { +- pub patch_file: PathBuf, +- process_list: IndexSet, +-} +- +-impl PatchEntity { +- pub fn new(patch_file: PathBuf) -> Self { +- Self { +- patch_file, +- process_list: indexset! {}, +- } +- } +-} +- +-impl PatchEntity { +- pub fn add_process(&mut self, pid: i32) { +- self.process_list.insert(pid); +- } +- +- pub fn remove_process(&mut self, pid: i32) { +- self.process_list.remove(&pid); +- } +- +- pub fn clean_dead_process(&mut self, process_list: &IndexSet) { +- self.process_list.retain(|pid| process_list.contains(pid)); +- } +- +- pub fn need_actived(&self, process_list: &IndexSet) -> IndexSet { +- process_list +- .difference(&self.process_list) +- .copied() +- .collect() +- } +- +- pub fn need_deactived(&self, process_list: &IndexSet) -> IndexSet { +- process_list +- .intersection(&self.process_list) +- .copied() +- .collect() +- } +-} +diff --git a/syscared/src/patch/driver/upatch/mod.rs b/syscared/src/patch/driver/upatch/mod.rs +index c31ef78..cc00f53 100644 +--- a/syscared/src/patch/driver/upatch/mod.rs ++++ b/syscared/src/patch/driver/upatch/mod.rs +@@ -13,6 +13,7 @@ + */ + + use std::{ ++ collections::{HashMap, HashSet}, + ffi::OsStr, + fmt::Write, + iter::FromIterator, +@@ -21,21 +22,16 @@ use std::{ + sync::Arc, + }; + +-use anyhow::{bail, ensure, Context, Result}; +-use indexmap::{indexset, IndexMap, IndexSet}; +-use log::{debug, info, warn}; ++use anyhow::{bail, ensure, Result}; ++use log::{debug, warn}; + use parking_lot::RwLock; + use uuid::Uuid; + + use syscare_abi::PatchStatus; + use syscare_common::{fs, util::digest}; + +-use crate::{ +- config::UserPatchConfig, +- patch::{driver::upatch::entity::PatchEntity, entity::UserPatch}, +-}; ++use crate::{config::UserPatchConfig, patch::entity::UserPatch}; + +-mod entity; + mod monitor; + mod sys; + mod target; +@@ -44,19 +40,19 @@ use monitor::UserPatchMonitor; + use target::PatchTarget; + + pub struct UserPatchDriver { +- status_map: IndexMap, +- target_map: Arc>>, +- skipped_files: Arc>, ++ status_map: HashMap, ++ target_map: Arc>>, ++ skipped_files: Arc>, + monitor: UserPatchMonitor, + } + + impl UserPatchDriver { + pub fn new(config: &UserPatchConfig) -> Result { +- let target_map = Arc::new(RwLock::new(IndexMap::new())); +- let skipped_files = Arc::new(IndexSet::from_iter(config.skipped.iter().cloned())); ++ let target_map = Arc::new(RwLock::new(HashMap::new())); ++ let skipped_files = Arc::new(HashSet::from_iter(config.skipped.iter().cloned())); + + Ok(Self { +- status_map: IndexMap::new(), ++ status_map: HashMap::new(), + target_map: target_map.clone(), + skipped_files: skipped_files.clone(), + monitor: UserPatchMonitor::new(move |target_elfs| { +@@ -70,7 +66,7 @@ impl UserPatchDriver { + + impl UserPatchDriver { + #[inline] +- fn get_patch_status(&self, uuid: &Uuid) -> PatchStatus { ++ fn read_patch_status(&self, uuid: &Uuid) -> PatchStatus { + self.status_map + .get(uuid) + .copied() +@@ -78,7 +74,7 @@ impl UserPatchDriver { + } + + #[inline] +- fn set_patch_status(&mut self, uuid: &Uuid, value: PatchStatus) { ++ fn write_patch_status(&mut self, uuid: &Uuid, value: PatchStatus) { + *self.status_map.entry(*uuid).or_default() = value; + } + +@@ -87,28 +83,6 @@ impl UserPatchDriver { + } + } + +-impl UserPatchDriver { +- fn add_patch_target(&mut self, patch: &UserPatch) { +- let target_elf = patch.target_elf.as_path(); +- let mut target_map = self.target_map.write(); +- +- if !target_map.contains_key(target_elf) { +- target_map.insert(target_elf.to_path_buf(), PatchTarget::default()); +- } +- } +- +- fn remove_patch_target(&mut self, patch: &UserPatch) { +- let target_elf = patch.target_elf.as_path(); +- let mut target_map = self.target_map.write(); +- +- if let Some(target) = target_map.get_mut(target_elf) { +- if !target.is_patched() { +- target_map.remove(target_elf); +- } +- } +- } +-} +- + impl UserPatchDriver { + fn check_consistency(patch: &UserPatch) -> Result<()> { + let real_checksum = digest::file(&patch.patch_file)?; +@@ -122,56 +96,39 @@ impl UserPatchDriver { + Ok(()) + } + +- fn check_compatiblity(_patch: &UserPatch) -> Result<()> { +- Ok(()) +- } +- +- pub fn check_conflict_functions(&self, patch: &UserPatch) -> Result<()> { +- let conflict_patches = match self.target_map.read().get(&patch.target_elf) { +- Some(target) => target +- .get_conflicts(&patch.functions) +- .into_iter() +- .map(|record| record.uuid) +- .collect(), +- None => indexset! {}, ++ pub fn check_conflicted_patches(&self, patch: &UserPatch) -> Result<()> { ++ let conflicted = match self.target_map.read().get(&patch.target_elf) { ++ Some(target) => target.get_conflicted_patches(patch).collect(), ++ None => HashSet::new(), + }; + +- ensure!(conflict_patches.is_empty(), { +- let mut err_msg = String::new(); +- +- writeln!(&mut err_msg, "Upatch: Patch is conflicted with")?; +- for uuid in conflict_patches.into_iter() { +- writeln!(&mut err_msg, "* Patch '{}'", uuid)?; ++ ensure!(conflicted.is_empty(), { ++ let mut msg = String::new(); ++ writeln!(msg, "Upatch: Patch is conflicted with")?; ++ for uuid in conflicted.into_iter() { ++ writeln!(msg, "* Patch '{}'", uuid)?; + } +- err_msg.pop(); +- +- err_msg ++ msg.pop(); ++ msg + }); + Ok(()) + } + +- pub fn check_override_functions(&self, patch: &UserPatch) -> Result<()> { +- let override_patches = match self.target_map.read().get(&patch.target_elf) { +- Some(target) => target +- .get_overrides(&patch.uuid, &patch.functions) +- .into_iter() +- .map(|record| record.uuid) +- .collect(), +- None => indexset! {}, ++ pub fn check_overridden_patches(&self, patch: &UserPatch) -> Result<()> { ++ let overridden = match self.target_map.read().get(&patch.target_elf) { ++ Some(target) => target.get_overridden_patches(patch).collect(), ++ None => HashSet::new(), + }; + +- ensure!(override_patches.is_empty(), { +- let mut err_msg = String::new(); +- +- writeln!(&mut err_msg, "Upatch: Patch is overrided by")?; +- for uuid in override_patches.into_iter() { +- writeln!(&mut err_msg, "* Patch '{}'", uuid)?; ++ ensure!(overridden.is_empty(), { ++ let mut msg = String::new(); ++ writeln!(msg, "Upatch: Patch is overridden by")?; ++ for uuid in overridden.into_iter() { ++ writeln!(msg, "* Patch '{}'", uuid)?; + } +- err_msg.pop(); +- +- err_msg ++ msg.pop(); ++ msg + }); +- + Ok(()) + } + } +@@ -187,10 +144,10 @@ impl UserPatchDriver { + } + + fn find_target_process>( +- skipped_files: &IndexSet, ++ skipped_files: &HashSet, + target_elf: P, +- ) -> Result> { +- let mut target_pids = IndexSet::new(); ++ ) -> Result> { ++ let mut target_pids = HashSet::new(); + let target_path = target_elf.as_ref(); + let target_inode = target_path.metadata()?.st_ino(); + +@@ -233,8 +190,8 @@ impl UserPatchDriver { + } + + fn patch_new_process( +- target_map: &RwLock>, +- skipped_files: &IndexSet, ++ target_map: &RwLock>, ++ skipped_files: &HashSet, + target_elf: &Path, + ) { + let process_list = match Self::find_target_process(skipped_files, target_elf) { +@@ -242,33 +199,32 @@ impl UserPatchDriver { + Err(_) => return, + }; + +- let mut patch_target_map = target_map.write(); +- let patch_target = match patch_target_map.get_mut(target_elf) { ++ let mut target_map = target_map.write(); ++ let patch_target = match target_map.get_mut(target_elf) { + Some(target) => target, + None => return, + }; ++ patch_target.clean_dead_process(&process_list); + +- for (patch_uuid, patch_entity) in patch_target.all_patches() { +- patch_entity.clean_dead_process(&process_list); ++ let all_patches = patch_target.all_patches().collect::>(); ++ let need_actived = patch_target.need_actived(&process_list); + +- // Active patch +- let need_actived = patch_entity.need_actived(&process_list); ++ for (uuid, patch_file) in all_patches { + if !need_actived.is_empty() { + debug!( +- "Activating patch '{}' ({}) for process {:?}", +- patch_uuid, ++ "Upatch: Activating patch '{}' ({}) for process {:?}", ++ uuid, + target_elf.display(), + need_actived, + ); + } +- +- for pid in need_actived { +- match sys::active_patch(patch_uuid, pid, target_elf, &patch_entity.patch_file) { +- Ok(_) => patch_entity.add_process(pid), ++ for &pid in &need_actived { ++ match sys::active_patch(&uuid, pid, target_elf, &patch_file) { ++ Ok(_) => patch_target.add_process(pid), + Err(e) => { + warn!( + "Upatch: Failed to active patch '{}' for process {}, {}", +- patch_uuid, ++ uuid, + pid, + e.to_string().to_lowercase(), + ); +@@ -280,198 +236,140 @@ impl UserPatchDriver { + } + + impl UserPatchDriver { +- pub fn status(&self, patch: &UserPatch) -> Result { +- Ok(self.get_patch_status(&patch.uuid)) +- } +- +- pub fn check(&self, patch: &UserPatch) -> Result<()> { ++ pub fn check_patch(&self, patch: &UserPatch) -> Result<()> { + Self::check_consistency(patch)?; +- Self::check_compatiblity(patch)?; +- + Ok(()) + } + +- pub fn apply(&mut self, patch: &UserPatch) -> Result<()> { +- info!( +- "Applying patch '{}' ({})", +- patch.uuid, +- patch.patch_file.display() +- ); +- +- self.add_patch_target(patch); +- self.set_patch_status(&patch.uuid, PatchStatus::Deactived); ++ pub fn get_patch_status(&self, patch: &UserPatch) -> Result { ++ Ok(self.read_patch_status(&patch.uuid)) ++ } + ++ pub fn load_patch(&mut self, patch: &UserPatch) -> Result<()> { ++ self.write_patch_status(&patch.uuid, PatchStatus::Deactived); + Ok(()) + } + +- pub fn remove(&mut self, patch: &UserPatch) -> Result<()> { +- info!( +- "Removing patch '{}' ({})", +- patch.uuid, +- patch.patch_file.display() +- ); +- +- self.remove_patch_target(patch); ++ pub fn remove_patch(&mut self, patch: &UserPatch) -> Result<()> { + self.remove_patch_status(&patch.uuid); +- + Ok(()) + } + +- pub fn active(&mut self, patch: &UserPatch) -> Result<()> { +- let patch_uuid = &patch.uuid; +- let patch_file = patch.patch_file.as_path(); +- let patch_functions = patch.functions.as_slice(); +- let target_elf = patch.target_elf.as_path(); +- +- let process_list = Self::find_target_process(&self.skipped_files, target_elf)?; ++ pub fn active_patch(&mut self, patch: &UserPatch) -> Result<()> { ++ let process_list = Self::find_target_process(&self.skipped_files, &patch.target_elf)?; + + let mut target_map = self.target_map.write(); +- let patch_target = target_map +- .get_mut(target_elf) +- .context("Upatch: Cannot find patch target")?; +- let mut patch_entity = match patch_target.get_patch(patch_uuid) { +- Some(_) => bail!("Upatch: Patch is already exist"), +- None => PatchEntity::new(patch_file.to_path_buf()), +- }; ++ let patch_target = target_map.entry(patch.target_elf.clone()).or_default(); ++ patch_target.clean_dead_process(&process_list); ++ ++ // If target is not patched before, start watching it ++ let start_watch = !patch_target.is_patched(); + + // Active patch +- info!( +- "Activating patch '{}' ({}) for {}", +- patch_uuid, +- patch_file.display(), +- target_elf.display(), +- ); ++ let need_actived = patch_target.need_actived(&process_list); ++ + let mut results = Vec::new(); +- for pid in patch_entity.need_actived(&process_list) { +- let result = sys::active_patch(patch_uuid, pid, target_elf, patch_file); +- if result.is_ok() { +- patch_entity.add_process(pid); +- } ++ for pid in need_actived { ++ let result = sys::active_patch(&patch.uuid, pid, &patch.target_elf, &patch.patch_file); + results.push((pid, result)); + } + +- // Check results, return error if all process fails ++ // Return error if all process fails + if !results.is_empty() && results.iter().all(|(_, result)| result.is_err()) { +- let mut err_msg = String::new(); +- +- writeln!(err_msg, "Upatch: Failed to active patch")?; ++ let mut msg = String::new(); ++ writeln!(msg, "Upatch: Failed to active patch")?; + for (pid, result) in &results { + if let Err(e) = result { +- writeln!(err_msg, "* Process {}: {}", pid, e)?; ++ writeln!(msg, "* Process {}: {}", pid, e)?; + } + } +- err_msg.pop(); +- bail!(err_msg); ++ msg.pop(); ++ bail!(msg); + } + +- // Print failure results +- for (pid, result) in &results { +- if let Err(e) = result { +- warn!( +- "Upatch: Failed to active patch '{}' for process {}, {}", +- patch_uuid, +- pid, +- e.to_string().to_lowercase(), +- ); ++ // Process results ++ for (pid, result) in results { ++ match result { ++ Ok(_) => patch_target.add_process(pid), ++ Err(e) => { ++ warn!( ++ "Upatch: Failed to active patch '{}' for process {}, {}", ++ patch.uuid, ++ pid, ++ e.to_string().to_lowercase(), ++ ); ++ } + } + } +- +- // If target is no patched before, start watching it +- let need_start_watch = !patch_target.is_patched(); +- +- // Apply patch to target +- patch_target.add_patch(*patch_uuid, patch_entity); +- patch_target.add_functions(*patch_uuid, patch_functions); ++ patch_target.add_patch(patch); + + // Drop the lock + drop(target_map); + +- if need_start_watch { +- self.monitor.watch_file(target_elf)?; ++ if start_watch { ++ self.monitor.watch_file(&patch.target_elf)?; + } +- self.set_patch_status(patch_uuid, PatchStatus::Actived); + ++ self.write_patch_status(&patch.uuid, PatchStatus::Actived); + Ok(()) + } + +- pub fn deactive(&mut self, patch: &UserPatch) -> Result<()> { +- let patch_uuid = &patch.uuid; +- let patch_file = patch.patch_file.as_path(); +- let patch_functions = patch.functions.as_slice(); +- let target_elf = patch.target_elf.as_path(); +- +- let process_list = Self::find_target_process(&self.skipped_files, target_elf)?; ++ pub fn deactive_patch(&mut self, patch: &UserPatch) -> Result<()> { ++ let process_list = Self::find_target_process(&self.skipped_files, &patch.target_elf)?; + + let mut target_map = self.target_map.write(); +- let patch_target = target_map +- .get_mut(target_elf) +- .context("Upatch: Cannot find patch target")?; +- let patch_entity = patch_target +- .get_patch(patch_uuid) +- .context("Upatch: Cannot find patch entity")?; +- +- // Remove dead process +- patch_entity.clean_dead_process(&process_list); ++ let patch_target = target_map.entry(patch.target_elf.clone()).or_default(); ++ patch_target.clean_dead_process(&process_list); + + // Deactive patch +- info!( +- "Deactivating patch '{}' ({}) for {}", +- patch_uuid, +- patch_file.display(), +- target_elf.display(), +- ); +- +- let need_deactived = patch_entity.need_deactived(&process_list); ++ let need_deactive = patch_target.need_deactived(&process_list); + + let mut results = Vec::new(); +- for pid in need_deactived { +- let result = sys::deactive_patch(patch_uuid, pid, target_elf, patch_file); +- if result.is_ok() { +- patch_entity.remove_process(pid) +- } ++ for pid in need_deactive { ++ let result = ++ sys::deactive_patch(&patch.uuid, pid, &patch.target_elf, &patch.patch_file); + results.push((pid, result)); + } + +- // Check results, return error if any process failes ++ // Return error if all process fails + if !results.is_empty() && results.iter().any(|(_, result)| result.is_err()) { +- let mut err_msg = String::new(); +- +- writeln!(err_msg, "Upatch: Failed to deactive patch")?; ++ let mut msg = String::new(); ++ writeln!(msg, "Upatch: Failed to deactive patch")?; + for (pid, result) in &results { + if let Err(e) = result { +- writeln!(err_msg, "* Process {}: {}", pid, e)?; ++ writeln!(msg, "* Process {}: {}", pid, e)?; + } + } +- err_msg.pop(); +- bail!(err_msg); ++ msg.pop(); ++ bail!(msg); + } + +- // Print failure results +- for (pid, result) in &results { +- if let Err(e) = result { +- warn!( +- "Upatch: Failed to deactive patch '{}' for process {}, {}", +- patch_uuid, +- pid, +- e.to_string().to_lowercase(), +- ); ++ // Process results ++ for (pid, result) in results { ++ match result { ++ Ok(_) => patch_target.remove_process(pid), ++ Err(e) => { ++ warn!( ++ "Upatch: Failed to deactive patch '{}' for process {}, {}", ++ patch.uuid, ++ pid, ++ e.to_string().to_lowercase(), ++ ); ++ } + } + } ++ patch_target.remove_patch(patch); + +- // Remove patch functions from target +- patch_target.remove_patch(patch_uuid); +- patch_target.remove_functions(patch_uuid, patch_functions); +- +- // If target is no longer patched, stop watching it +- let need_stop_watch = !patch_target.is_patched(); ++ // If target is no longer has patch, stop watching it ++ let stop_watch = !patch_target.is_patched(); + + drop(target_map); + +- if need_stop_watch { +- self.monitor.ignore_file(target_elf)?; ++ if stop_watch { ++ self.monitor.ignore_file(&patch.target_elf)?; + } +- self.set_patch_status(patch_uuid, PatchStatus::Deactived); + ++ self.write_patch_status(&patch.uuid, PatchStatus::Deactived); + Ok(()) + } + } +diff --git a/syscared/src/patch/driver/upatch/monitor.rs b/syscared/src/patch/driver/upatch/monitor.rs +index c0c1b3c..150d1a3 100644 +--- a/syscared/src/patch/driver/upatch/monitor.rs ++++ b/syscared/src/patch/driver/upatch/monitor.rs +@@ -20,7 +20,7 @@ use std::{ + time::Duration, + }; + +-use anyhow::{bail, Context, Result}; ++use anyhow::{anyhow, bail, Context, Result}; + use indexmap::IndexMap; + use inotify::{Inotify, WatchDescriptor, WatchMask}; + use log::info; +@@ -75,13 +75,19 @@ impl UserPatchMonitor { + Some(inotify) => { + let wd = inotify + .add_watch(watch_file, WatchMask::OPEN) +- .with_context(|| format!("Failed to watch file {}", watch_file.display()))?; ++ .map_err(|e| { ++ anyhow!( ++ "Failed to watch file '{}', {}", ++ watch_file.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; + + self.watch_file_map + .write() + .insert(wd.clone(), watch_file.to_owned()); + self.watch_wd_map.lock().insert(watch_file.to_owned(), wd); +- info!("Start watching file {}", watch_file.display()); ++ info!("Start watching file '{}'", watch_file.display()); + } + None => bail!("Inotify does not exist"), + } +@@ -97,10 +103,14 @@ impl UserPatchMonitor { + Some(inotify) => { + self.watch_file_map.write().remove(&wd); + +- inotify.rm_watch(wd).with_context(|| { +- format!("Failed to stop watch file {}", ignore_file.display()) ++ inotify.rm_watch(wd).map_err(|e| { ++ anyhow!( ++ "Failed to stop watch file '{}', {}", ++ ignore_file.display(), ++ e.to_string().to_lowercase() ++ ) + })?; +- info!("Stop watching file {}", ignore_file.display()); ++ info!("Stop watching file '{}'", ignore_file.display()); + } + None => bail!("Inotify does not exist"), + } +@@ -124,7 +134,13 @@ where + thread::Builder::new() + .name(MONITOR_THREAD_NAME.to_string()) + .spawn(move || self.thread_main()) +- .with_context(|| format!("Failed to create thread '{}'", MONITOR_THREAD_NAME)) ++ .map_err(|e| { ++ anyhow!( ++ "Failed to create thread '{}', {}", ++ MONITOR_THREAD_NAME, ++ e.to_string().to_lowercase() ++ ) ++ }) + } + + #[inline] +diff --git a/syscared/src/patch/driver/upatch/sys.rs b/syscared/src/patch/driver/upatch/sys.rs +index ecc0522..8518840 100644 +--- a/syscared/src/patch/driver/upatch/sys.rs ++++ b/syscared/src/patch/driver/upatch/sys.rs +@@ -1,14 +1,27 @@ + use std::path::Path; + + use anyhow::{bail, Result}; +-use log::Level; ++use log::{debug, Level}; + use uuid::Uuid; + + use syscare_common::process::Command; + + const UPATCH_MANAGE_BIN: &str = "upatch-manage"; + +-pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) -> Result<()> { ++pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: P, patch_file: Q) -> Result<()> ++where ++ P: AsRef, ++ Q: AsRef, ++{ ++ let target_elf = target_elf.as_ref(); ++ let patch_file = patch_file.as_ref(); ++ ++ debug!( ++ "Upatch: Patching '{}' to '{}' (pid: {})...", ++ patch_file.display(), ++ target_elf.display(), ++ pid, ++ ); + let exit_code = Command::new(UPATCH_MANAGE_BIN) + .arg("patch") + .arg("--uuid") +@@ -29,7 +42,20 @@ pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) + } + } + +-pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) -> Result<()> { ++pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: P, patch_file: Q) -> Result<()> ++where ++ P: AsRef, ++ Q: AsRef, ++{ ++ let target_elf = target_elf.as_ref(); ++ let patch_file = patch_file.as_ref(); ++ ++ debug!( ++ "Upatch: Unpatching '{}' from '{}' (pid: {})...", ++ patch_file.display(), ++ target_elf.display(), ++ pid, ++ ); + let exit_code = Command::new(UPATCH_MANAGE_BIN) + .arg("unpatch") + .arg("--uuid") +diff --git a/syscared/src/patch/driver/upatch/target.rs b/syscared/src/patch/driver/upatch/target.rs +index 26c3ed3..e0e9f88 100644 +--- a/syscared/src/patch/driver/upatch/target.rs ++++ b/syscared/src/patch/driver/upatch/target.rs +@@ -12,125 +12,113 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::ffi::OsString; ++use std::{ ++ collections::{hash_map::Entry, HashMap, HashSet}, ++ path::PathBuf, ++}; + +-use indexmap::IndexMap; ++use indexmap::IndexSet; + use uuid::Uuid; + +-use crate::patch::entity::UserPatchFunction; +- +-use super::entity::PatchEntity; +- +-#[derive(Debug)] +-pub struct PatchFunction { +- pub uuid: Uuid, +- pub name: OsString, +- pub size: u64, +-} +- +-impl PatchFunction { +- pub fn new(uuid: Uuid, function: &UserPatchFunction) -> Self { +- Self { +- uuid, +- name: function.name.to_os_string(), +- size: function.new_size, +- } +- } +- +- pub fn is_same_function(&self, uuid: &Uuid, function: &UserPatchFunction) -> bool { +- (self.uuid == *uuid) && (self.name == function.name) && (self.size == function.new_size) +- } +-} ++use crate::patch::entity::UserPatch; + + #[derive(Debug, Default)] + pub struct PatchTarget { +- patch_map: IndexMap, // patched file data +- function_map: IndexMap>, // function addr -> function collision list ++ process_list: HashSet, ++ patch_map: HashMap, // uuid -> patch file ++ collision_map: HashMap>, // function old addr -> patch collision list + } + + impl PatchTarget { +- pub fn is_patched(&self) -> bool { +- !self.patch_map.is_empty() ++ pub fn add_process(&mut self, pid: i32) { ++ self.process_list.insert(pid); + } + +- pub fn add_patch(&mut self, uuid: Uuid, entity: PatchEntity) { +- self.patch_map.insert(uuid, entity); ++ pub fn remove_process(&mut self, pid: i32) { ++ self.process_list.remove(&pid); + } + +- pub fn remove_patch(&mut self, uuid: &Uuid) { +- self.patch_map.remove(uuid); ++ pub fn clean_dead_process(&mut self, process_list: &HashSet) { ++ self.process_list.retain(|pid| process_list.contains(pid)); + } + +- pub fn get_patch(&mut self, uuid: &Uuid) -> Option<&mut PatchEntity> { +- self.patch_map.get_mut(uuid) ++ pub fn need_actived(&self, process_list: &HashSet) -> HashSet { ++ process_list ++ .difference(&self.process_list) ++ .copied() ++ .collect() + } + +- pub fn all_patches(&mut self) -> impl IntoIterator { +- self.patch_map.iter_mut() ++ pub fn need_deactived(&self, process_list: &HashSet) -> HashSet { ++ process_list ++ .intersection(&self.process_list) ++ .copied() ++ .collect() + } + } + + impl PatchTarget { +- pub fn add_functions<'a, I>(&mut self, uuid: Uuid, functions: I) +- where +- I: IntoIterator, +- { +- for function in functions { +- self.function_map ++ pub fn add_patch(&mut self, patch: &UserPatch) { ++ for function in &patch.functions { ++ self.collision_map + .entry(function.old_addr) + .or_default() +- .push(PatchFunction::new(uuid, function)); ++ .insert(patch.uuid); + } ++ self.patch_map.insert(patch.uuid, patch.patch_file.clone()); + } + +- pub fn remove_functions<'a, I>(&mut self, uuid: &Uuid, functions: I) +- where +- I: IntoIterator, +- { +- for function in functions { +- if let Some(collision_list) = self.function_map.get_mut(&function.old_addr) { +- if let Some(index) = collision_list +- .iter() +- .position(|patch_function| patch_function.is_same_function(uuid, function)) +- { +- collision_list.remove(index); +- if collision_list.is_empty() { +- self.function_map.remove(&function.old_addr); +- } ++ pub fn remove_patch(&mut self, patch: &UserPatch) { ++ for function in &patch.functions { ++ if let Entry::Occupied(mut entry) = self.collision_map.entry(function.old_addr) { ++ let patch_set = entry.get_mut(); ++ patch_set.shift_remove(&patch.uuid); ++ ++ if patch_set.is_empty() { ++ entry.remove(); + } + } + } ++ self.patch_map.remove(&patch.uuid); + } +-} + +-impl PatchTarget { +- pub fn get_conflicts<'a, I>( ++ pub fn is_patched(&self) -> bool { ++ !self.collision_map.is_empty() ++ } ++ ++ pub fn all_patches(&self) -> impl Iterator + '_ { ++ self.patch_map ++ .iter() ++ .map(|(uuid, path)| (*uuid, path.to_path_buf())) ++ } ++ ++ pub fn get_conflicted_patches<'a>( + &'a self, +- functions: I, +- ) -> impl IntoIterator +- where +- I: IntoIterator, +- { +- functions.into_iter().filter_map(move |function| { +- self.function_map +- .get(&function.old_addr) +- .and_then(|list| list.last()) +- }) ++ patch: &'a UserPatch, ++ ) -> impl Iterator + 'a { ++ patch ++ .functions ++ .iter() ++ .filter_map(move |function| self.collision_map.get(&function.old_addr)) ++ .flatten() ++ .copied() ++ .filter(move |&uuid| uuid != patch.uuid) + } + +- pub fn get_overrides<'a, I>( ++ pub fn get_overridden_patches<'a>( + &'a self, +- uuid: &'a Uuid, +- functions: I, +- ) -> impl IntoIterator +- where +- I: IntoIterator, +- { +- functions.into_iter().filter_map(move |function| { +- self.function_map +- .get(&function.old_addr) +- .and_then(|list| list.last()) +- .filter(|patch_function| !patch_function.is_same_function(uuid, function)) +- }) ++ patch: &'a UserPatch, ++ ) -> impl Iterator + 'a { ++ patch ++ .functions ++ .iter() ++ .filter_map(move |function| self.collision_map.get(&function.old_addr)) ++ .flat_map(move |collision_list| { ++ collision_list ++ .iter() ++ .copied() ++ .skip_while(move |&uuid| uuid != patch.uuid) ++ .skip(1) ++ }) + } + } +diff --git a/syscared/src/patch/entity/kpatch.rs b/syscared/src/patch/entity/kpatch.rs +index 4c0be9c..09e759d 100644 +--- a/syscared/src/patch/entity/kpatch.rs ++++ b/syscared/src/patch/entity/kpatch.rs +@@ -12,11 +12,78 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{ffi::OsString, path::PathBuf, sync::Arc}; ++use std::{ ++ collections::HashMap, ++ ffi::{CStr, OsStr, OsString}, ++ path::{Path, PathBuf}, ++ sync::Arc, ++}; + +-use syscare_abi::PatchInfo; ++use anyhow::{anyhow, Context, Result}; ++use object::{File, Object, ObjectSection}; + use uuid::Uuid; + ++use syscare_abi::{PatchEntity, PatchInfo}; ++use syscare_common::{ffi::CStrExt, fs, os::kernel}; ++ ++mod ffi { ++ use std::os::raw::{c_char, c_long, c_ulong}; ++ ++ use object::{Pod, Relocation, SectionRelocationIterator}; ++ ++ #[repr(C)] ++ #[derive(Debug, Clone, Copy)] ++ /// Corresponds to `struct kpatch_patch_func` defined in `kpatch-patch.h` ++ pub struct KpatchFunction { ++ pub new_addr: c_ulong, ++ pub new_size: c_ulong, ++ pub old_addr: c_ulong, ++ pub old_size: c_ulong, ++ pub sympos: u64, ++ pub name: *const c_char, ++ pub obj_name: *const c_char, ++ pub ref_name: *const c_char, ++ pub ref_offset: c_long, ++ } ++ ++ pub const KPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); ++ pub const KPATCH_FUNCTION_OFFSET: usize = 40; ++ pub const KPATCH_OBJECT_OFFSET: usize = 48; ++ ++ /* ++ * SAFETY: This struct is ++ * - #[repr(C)] ++ * - have no invalid byte values ++ * - have no padding ++ */ ++ unsafe impl Pod for KpatchFunction {} ++ ++ pub struct KpatchRelocation { ++ pub _addr: (u64, Relocation), ++ pub name: (u64, Relocation), ++ pub object: (u64, Relocation), ++ } ++ ++ pub struct KpatchRelocationIterator<'data, 'file>(pub SectionRelocationIterator<'data, 'file>); ++ ++ impl Iterator for KpatchRelocationIterator<'_, '_> { ++ type Item = KpatchRelocation; ++ ++ fn next(&mut self) -> Option { ++ if let (Some(addr), Some(name), Some(object)) = ++ (self.0.next(), self.0.next(), self.0.next()) ++ { ++ return Some(KpatchRelocation { ++ _addr: addr, ++ name, ++ object, ++ }); ++ } ++ None ++ } ++ } ++} ++ + /// Kernel patch function definition + #[derive(Clone)] + pub struct KernelPatchFunction { +@@ -63,10 +130,157 @@ pub struct KernelPatch { + pub name: OsString, + pub info: Arc, + pub pkg_name: String, +- pub module_name: OsString, + pub target_name: OsString, +- pub functions: Vec, + pub patch_file: PathBuf, +- pub sys_file: PathBuf, ++ pub status_file: PathBuf, ++ pub module: kernel::ModuleInfo, ++ pub functions: HashMap>, // object name -> function list + pub checksum: String, + } ++ ++impl KernelPatch { ++ fn parse_functions(patch_file: &Path) -> Result>> { ++ const KPATCH_FUNCS_SECTION: &str = ".kpatch.funcs"; ++ const KPATCH_STRINGS_SECTION: &str = ".kpatch.strings"; ++ ++ let mmap = fs::mmap(patch_file).map_err(|e| { ++ anyhow!( ++ "Failed to mmap '{}', {}", ++ patch_file.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ let file = File::parse(mmap.as_ref()).map_err(|e| { ++ anyhow!( ++ "Failed to parse '{}', {}", ++ patch_file.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ ++ // Read sections ++ let function_section = file ++ .section_by_name(KPATCH_FUNCS_SECTION) ++ .with_context(|| format!("Cannot find section '{}'", KPATCH_FUNCS_SECTION))?; ++ let string_section = file ++ .section_by_name(KPATCH_STRINGS_SECTION) ++ .with_context(|| format!("Cannot find section '{}'", KPATCH_STRINGS_SECTION))?; ++ let function_data = function_section.data().map_err(|e| { ++ anyhow!( ++ "Failed to read section '{}', {}", ++ KPATCH_FUNCS_SECTION, ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ let string_data = string_section.data().map_err(|e| { ++ anyhow!( ++ "Failed to read section '{}', {}", ++ KPATCH_STRINGS_SECTION, ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ ++ // Resolve patch functions ++ let (slice, _) = object::slice_from_bytes::( ++ function_data, ++ function_data.len() / ffi::KPATCH_FUNCTION_SIZE, ++ ) ++ .map_err(|_| anyhow!("Invalid patch function layout"))?; ++ ++ let mut functions: Vec<_> = slice ++ .iter() ++ .map(|function| KernelPatchFunction { ++ name: OsString::new(), ++ object: OsString::new(), ++ old_addr: function.old_addr, ++ old_size: function.old_size, ++ new_addr: function.new_addr, ++ new_size: function.new_size, ++ }) ++ .collect(); ++ ++ // Relocate patch functions ++ for relocation in ffi::KpatchRelocationIterator(function_section.relocations()) { ++ let (name_offset, name_reloc) = relocation.name; ++ let (object_offset, obj_reloc) = relocation.object; ++ ++ // Relocate patch function name ++ let name_index = ++ (name_offset as usize - ffi::KPATCH_FUNCTION_OFFSET) / ffi::KPATCH_FUNCTION_SIZE; ++ let name_function = functions ++ .get_mut(name_index) ++ .with_context(|| format!("Invalid patch function index, index={}", name_index))?; ++ let name_addend = name_reloc.addend() as usize; ++ name_function.name = CStr::from_bytes_with_next_nul(&string_data[name_addend..]) ++ .map_err(|_| anyhow!("Invalid patch function name"))? ++ .to_os_string(); ++ ++ // Relocate patch function object ++ let object_index = ++ (object_offset as usize - ffi::KPATCH_OBJECT_OFFSET) / ffi::KPATCH_FUNCTION_SIZE; ++ let object_function = functions ++ .get_mut(object_index) ++ .with_context(|| format!("Invalid patch object index, index={}", object_index))?; ++ let object_addend = obj_reloc.addend() as usize; ++ object_function.object = CStr::from_bytes_with_next_nul(&string_data[object_addend..]) ++ .map_err(|_| anyhow!("Invalid patch object name"))? ++ .to_os_string(); ++ } ++ ++ // group functions by it's object ++ let mut function_map: HashMap<_, Vec<_>> = HashMap::new(); ++ for function in functions { ++ function_map ++ .entry(function.object.clone()) ++ .or_default() ++ .push(function); ++ } ++ ++ Ok(function_map) ++ } ++ ++ pub fn parse( ++ name: S, ++ patch_info: Arc, ++ patch_entity: &PatchEntity, ++ patch_file: P, ++ ) -> Result ++ where ++ S: AsRef, ++ P: AsRef, ++ { ++ const KPATCH_SYS_DIR: &str = "/sys/kernel/livepatch"; ++ const KPATCH_STATUS_FILE_NAME: &str = "enabled"; ++ ++ let patch_file = patch_file.as_ref(); ++ let module = kernel::module_info(patch_file).map_err(|e| { ++ anyhow!( ++ "Failed to parse '{}' modinfo, {}", ++ patch_file.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ ++ let patch = Self { ++ uuid: patch_entity.uuid, ++ name: name.as_ref().to_os_string(), ++ info: patch_info.clone(), ++ pkg_name: patch_info.target.full_name(), ++ target_name: patch_entity.patch_name.as_os_str().to_os_string(), ++ patch_file: patch_file.to_path_buf(), ++ status_file: PathBuf::from(KPATCH_SYS_DIR) ++ .join(&module.name) ++ .join(KPATCH_STATUS_FILE_NAME), ++ module, ++ functions: Self::parse_functions(patch_file)?, ++ checksum: patch_entity.checksum.clone(), ++ }; ++ Ok(patch) ++ } ++} ++ ++impl std::fmt::Display for KernelPatch { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ write!(f, "{}", self.name.to_string_lossy()) ++ } ++} +diff --git a/syscared/src/patch/entity/patch.rs b/syscared/src/patch/entity/patch.rs +index 2ff398a..0941389 100644 +--- a/syscared/src/patch/entity/patch.rs ++++ b/syscared/src/patch/entity/patch.rs +@@ -78,6 +78,9 @@ impl std::cmp::Ord for Patch { + + impl std::fmt::Display for Patch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +- write!(f, "{}", self.name().to_string_lossy()) ++ match self { ++ Patch::KernelPatch(patch) => write!(f, "{}", patch), ++ Patch::UserPatch(patch) => write!(f, "{}", patch), ++ } + } + } +diff --git a/syscared/src/patch/entity/symbol.rs b/syscared/src/patch/entity/symbol.rs +deleted file mode 100644 +index c7845aa..0000000 +--- a/syscared/src/patch/entity/symbol.rs ++++ /dev/null +@@ -1,54 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscared is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::ffi::OsString; +- +-/// Patch function definiation +-#[derive(Clone)] +-pub struct PatchFunction { +- pub name: OsString, +- pub target: OsString, +- pub old_addr: u64, +- pub old_size: u64, +- pub new_addr: u64, +- pub new_size: u64, +-} +- +-impl std::fmt::Debug for PatchFunction { +- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +- f.debug_struct("PatchFunction") +- .field("name", &self.name) +- .field("target", &self.target) +- .field("old_addr", &format!("0x{}", self.old_addr)) +- .field("old_size", &format!("0x{}", self.old_size)) +- .field("new_addr", &format!("0x{}", self.new_addr)) +- .field("new_size", &format!("0x{}", self.new_size)) +- .finish() +- } +-} +- +-impl std::fmt::Display for PatchFunction { +- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +- write!( +- f, +- "name: {}, target: {}, old_addr: 0x{:x}, old_size: 0x{:x}, new_addr: 0x{:x}, new_size: 0x{:x}", +- self.name.to_string_lossy(), +- self.target.to_string_lossy(), +- self.old_addr, +- self.old_size, +- self.new_addr, +- self.new_size, +- ) +- } +-} +diff --git a/syscared/src/patch/entity/upatch.rs b/syscared/src/patch/entity/upatch.rs +index 0fbacb4..aaaee1c 100644 +--- a/syscared/src/patch/entity/upatch.rs ++++ b/syscared/src/patch/entity/upatch.rs +@@ -12,11 +12,66 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{ffi::OsString, path::PathBuf, sync::Arc}; ++use std::{ ++ ffi::{CStr, OsStr, OsString}, ++ path::{Path, PathBuf}, ++ sync::Arc, ++}; + +-use syscare_abi::PatchInfo; ++use anyhow::{anyhow, Context, Result}; ++use object::{File, Object, ObjectSection}; + use uuid::Uuid; + ++use syscare_abi::{PatchEntity, PatchInfo}; ++use syscare_common::{ffi::CStrExt, fs}; ++ ++mod ffi { ++ use std::os::raw::{c_char, c_ulong}; ++ ++ use object::{Pod, Relocation, SectionRelocationIterator}; ++ ++ #[repr(C)] ++ #[derive(Debug, Clone, Copy)] ++ /// Corresponds to `struct upatch_path_func` defined in `upatch-patch.h` ++ pub struct UpatchFunction { ++ pub new_addr: c_ulong, ++ pub new_size: c_ulong, ++ pub old_addr: c_ulong, ++ pub old_size: c_ulong, ++ pub sympos: c_ulong, ++ pub name: *const c_char, ++ } ++ ++ /* ++ * SAFETY: This struct is ++ * - #[repr(C)] ++ * - have no invalid byte values ++ * - have no padding ++ */ ++ unsafe impl Pod for UpatchFunction {} ++ ++ pub const UPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); ++ pub const UPATCH_FUNCTION_OFFSET: usize = 40; ++ ++ pub struct UpatchRelocation { ++ pub _addr: (u64, Relocation), ++ pub name: (u64, Relocation), ++ } ++ ++ pub struct UpatchRelocationIterator<'data, 'file>(pub SectionRelocationIterator<'data, 'file>); ++ ++ impl Iterator for UpatchRelocationIterator<'_, '_> { ++ type Item = UpatchRelocation; ++ ++ fn next(&mut self) -> Option { ++ if let (Some(addr), Some(name)) = (self.0.next(), self.0.next()) { ++ return Some(UpatchRelocation { _addr: addr, name }); ++ } ++ None ++ } ++ } ++} ++ + /// User patch function definition + #[derive(Clone)] + pub struct UserPatchFunction { +@@ -31,10 +86,10 @@ impl std::fmt::Debug for UserPatchFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UserPatchFunction") + .field("name", &self.name) +- .field("old_addr", &format!("0x{}", self.old_addr)) +- .field("old_size", &format!("0x{}", self.old_size)) +- .field("new_addr", &format!("0x{}", self.new_addr)) +- .field("new_size", &format!("0x{}", self.new_size)) ++ .field("old_addr", &format!("0x{:x}", self.old_addr)) ++ .field("old_size", &format!("0x{:x}", self.old_size)) ++ .field("new_addr", &format!("0x{:x}", self.new_addr)) ++ .field("new_size", &format!("0x{:x}", self.new_size)) + .finish() + } + } +@@ -60,8 +115,118 @@ pub struct UserPatch { + pub name: OsString, + pub info: Arc, + pub pkg_name: String, +- pub functions: Vec, +- pub patch_file: PathBuf, + pub target_elf: PathBuf, ++ pub patch_file: PathBuf, ++ pub functions: Vec, + pub checksum: String, + } ++ ++impl UserPatch { ++ fn parse_functions(patch_file: &Path) -> Result> { ++ const UPATCH_FUNCS_SECTION: &str = ".upatch.funcs"; ++ const UPATCH_STRINGS_SECTION: &str = ".upatch.strings"; ++ ++ let mmap = fs::mmap(patch_file).map_err(|e| { ++ anyhow!( ++ "Failed to mmap '{}', {}", ++ patch_file.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ let file = File::parse(mmap.as_ref()).map_err(|e| { ++ anyhow!( ++ "Failed to parse '{}', {}", ++ patch_file.display(), ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ ++ // Read sections ++ let function_section = file ++ .section_by_name(UPATCH_FUNCS_SECTION) ++ .with_context(|| format!("Cannot find section '{}'", UPATCH_FUNCS_SECTION))?; ++ let string_section = file ++ .section_by_name(UPATCH_STRINGS_SECTION) ++ .with_context(|| format!("Cannot find section '{}'", UPATCH_STRINGS_SECTION))?; ++ let function_data = function_section.data().map_err(|e| { ++ anyhow!( ++ "Failed to read section '{}', {}", ++ UPATCH_FUNCS_SECTION, ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ let string_data = string_section.data().map_err(|e| { ++ anyhow!( ++ "Failed to read section '{}', {}", ++ UPATCH_STRINGS_SECTION, ++ e.to_string().to_lowercase() ++ ) ++ })?; ++ ++ // Resolve patch functions ++ let (slice, _) = object::slice_from_bytes::( ++ function_data, ++ function_data.len() / ffi::UPATCH_FUNCTION_SIZE, ++ ) ++ .map_err(|_| anyhow!("Invalid patch function layout"))?; ++ ++ let mut functions: Vec<_> = slice ++ .iter() ++ .map(|function| UserPatchFunction { ++ name: OsString::new(), ++ old_addr: function.old_addr, ++ old_size: function.old_size, ++ new_addr: function.new_addr, ++ new_size: function.new_size, ++ }) ++ .collect(); ++ ++ // Relocate patch functions ++ for relocation in ffi::UpatchRelocationIterator(function_section.relocations()) { ++ let (value, reloc) = relocation.name; ++ ++ let index = (value as usize - ffi::UPATCH_FUNCTION_OFFSET) / ffi::UPATCH_FUNCTION_SIZE; ++ let function = functions ++ .get_mut(index) ++ .with_context(|| format!("Invalid patch function index, index={}", index))?; ++ let addend = reloc.addend() as usize; ++ ++ function.name = CStr::from_bytes_with_next_nul(&string_data[addend..]) ++ .map_err(|_| anyhow!("Invalid patch function name"))? ++ .to_os_string(); ++ } ++ ++ Ok(functions) ++ } ++ ++ pub fn parse( ++ name: S, ++ patch_info: Arc, ++ patch_entity: &PatchEntity, ++ patch_file: P, ++ ) -> Result ++ where ++ S: AsRef, ++ P: AsRef, ++ { ++ let patch_file = patch_file.as_ref(); ++ ++ let patch = Self { ++ uuid: patch_entity.uuid, ++ name: name.as_ref().to_os_string(), ++ info: patch_info.clone(), ++ pkg_name: patch_info.target.full_name(), ++ target_elf: patch_entity.patch_target.clone(), ++ patch_file: patch_file.to_path_buf(), ++ functions: Self::parse_functions(patch_file)?, ++ checksum: patch_entity.checksum.clone(), ++ }; ++ Ok(patch) ++ } ++} ++ ++impl std::fmt::Display for UserPatch { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ write!(f, "{}", self.name.to_string_lossy()) ++ } ++} +diff --git a/syscared/src/patch/manager.rs b/syscared/src/patch/manager.rs +index 4bf65ac..7248430 100644 +--- a/syscared/src/patch/manager.rs ++++ b/syscared/src/patch/manager.rs +@@ -24,10 +24,16 @@ use lazy_static::lazy_static; + use log::{debug, error, info, trace, warn}; + use uuid::Uuid; + +-use syscare_abi::PatchStatus; ++use syscare_abi::{PatchEntity, PatchInfo, PatchStatus, PatchType, PATCH_INFO_MAGIC}; + use syscare_common::{concat_os, ffi::OsStrExt, fs, util::serde}; + +-use crate::{config::PatchConfig, patch::resolver::PatchResolver}; ++use crate::{ ++ config::PatchConfig, ++ patch::{ ++ entity::{KernelPatch, UserPatch}, ++ PATCH_INFO_FILE_NAME, ++ }, ++}; + + use super::{ + driver::{PatchDriver, PatchOpFlag}, +@@ -40,7 +46,7 @@ type TransitionAction = + &'static (dyn Fn(&mut PatchManager, &Patch, PatchOpFlag) -> Result<()> + Sync); + + const PATCH_CHECK: TransitionAction = &PatchManager::driver_check_patch; +-const PATCH_APPLY: TransitionAction = &PatchManager::driver_apply_patch; ++const PATCH_LOAD: TransitionAction = &PatchManager::driver_load_patch; + const PATCH_REMOVE: TransitionAction = &PatchManager::driver_remove_patch; + const PATCH_ACTIVE: TransitionAction = &PatchManager::driver_active_patch; + const PATCH_DEACTIVE: TransitionAction = &PatchManager::driver_deactive_patch; +@@ -49,9 +55,9 @@ const PATCH_DECLINE: TransitionAction = &PatchManager::driver_decline_patch; + + lazy_static! { + static ref STATUS_TRANSITION_MAP: IndexMap> = indexmap! { +- (PatchStatus::NotApplied, PatchStatus::Deactived) => vec![PATCH_CHECK, PATCH_APPLY], +- (PatchStatus::NotApplied, PatchStatus::Actived) => vec![PATCH_CHECK, PATCH_APPLY, PATCH_ACTIVE], +- (PatchStatus::NotApplied, PatchStatus::Accepted) => vec![PATCH_CHECK, PATCH_APPLY, PATCH_ACTIVE, PATCH_ACCEPT], ++ (PatchStatus::NotApplied, PatchStatus::Deactived) => vec![PATCH_CHECK, PATCH_LOAD], ++ (PatchStatus::NotApplied, PatchStatus::Actived) => vec![PATCH_CHECK, PATCH_LOAD, PATCH_ACTIVE], ++ (PatchStatus::NotApplied, PatchStatus::Accepted) => vec![PATCH_CHECK, PATCH_LOAD, PATCH_ACTIVE, PATCH_ACCEPT], + (PatchStatus::Deactived, PatchStatus::NotApplied) => vec![PATCH_REMOVE], + (PatchStatus::Deactived, PatchStatus::Actived) => vec![PATCH_CHECK, PATCH_ACTIVE], + (PatchStatus::Deactived, PatchStatus::Accepted) => vec![PATCH_ACTIVE, PATCH_ACCEPT], +@@ -133,7 +139,7 @@ impl PatchManager { + .unwrap_or_default(); + + if status == PatchStatus::Unknown { +- status = self.driver_patch_status(patch, PatchOpFlag::Normal)?; ++ status = self.driver_get_patch_status(patch)?; + self.set_patch_status(patch, status)?; + } + +@@ -141,6 +147,7 @@ impl PatchManager { + } + + pub fn check_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { ++ info!("Check patch '{}'", patch); + self.driver.check_patch(patch, flag)?; + self.driver.check_confliction(patch, flag)?; + +@@ -245,20 +252,20 @@ impl PatchManager { + + debug!("Reading patch status..."); + let status_map: IndexMap = +- serde::deserialize(&self.patch_status_file).context("Failed to read patch status")?; ++ serde::deserialize(&self.patch_status_file) ++ .context("Failed to read patch status file")?; + for (uuid, status) in &status_map { + debug!("Patch '{}' status: {}", uuid, status); + } +- +- let restore_list = status_map +- .into_iter() +- .filter(|(_, status)| !accepted_only || (*status == PatchStatus::Accepted)); +- for (uuid, status) in restore_list { ++ for (uuid, status) in status_map { ++ if accepted_only && (status != PatchStatus::Accepted) { ++ continue; ++ } + match self.find_patch_by_uuid(&uuid) { + Ok(patch) => { +- debug!("Restore patch '{}' status to '{}'", patch, status); ++ info!("Restore patch '{}' status to '{}'", patch, status); + if let Err(e) = self.do_status_transition(&patch, status, PatchOpFlag::Force) { +- error!("{}", e); ++ error!("{:?}", e); + } + } + Err(e) => { +@@ -277,7 +284,7 @@ impl PatchManager { + let status_keys = self.status_map.keys().copied().collect::>(); + for patch_uuid in status_keys { + if !self.patch_map.contains_key(&patch_uuid) { +- trace!("Patch '{}' was removed, remove its status", patch_uuid); ++ trace!("Patch '{}' was removed", patch_uuid); + self.status_map.remove(&patch_uuid); + } + } +@@ -329,19 +336,110 @@ impl PatchManager { + } + + impl PatchManager { ++ fn parse_user_patch( ++ root_dir: &Path, ++ patch_info: Arc, ++ patch_entity: &PatchEntity, ++ ) -> Result { ++ let patch_name = concat_os!( ++ patch_info.target.short_name(), ++ "/", ++ patch_info.name(), ++ "/", ++ patch_entity.patch_target.file_name().unwrap_or_default() ++ ); ++ let patch_file = root_dir.join(&patch_entity.patch_name); ++ ++ let patch = UserPatch::parse(&patch_name, patch_info, patch_entity, patch_file) ++ .with_context(|| { ++ format!( ++ "Failed to parse patch '{}' ({})", ++ patch_entity.uuid, ++ patch_name.to_string_lossy(), ++ ) ++ })?; ++ ++ debug!("Found patch '{}' ({})", patch.uuid, patch); ++ Ok(patch) ++ } ++ ++ fn parse_kernel_patch( ++ root_dir: &Path, ++ patch_info: Arc, ++ patch_entity: &PatchEntity, ++ ) -> Result { ++ const KPATCH_EXTENSION: &str = "ko"; ++ ++ let patch_name = concat_os!( ++ patch_info.target.short_name(), ++ "/", ++ patch_info.name(), ++ "/", ++ &patch_entity.patch_target, ++ ); ++ let mut patch_file = root_dir.join(&patch_entity.patch_name); ++ patch_file.set_extension(KPATCH_EXTENSION); ++ ++ let patch = KernelPatch::parse(&patch_name, patch_info, patch_entity, patch_file) ++ .with_context(|| { ++ format!( ++ "Failed to parse patch '{}' ({})", ++ patch_entity.uuid, ++ patch_name.to_string_lossy(), ++ ) ++ })?; ++ ++ debug!("Found patch '{}' ({})", patch.uuid, patch); ++ Ok(patch) ++ } ++ ++ fn parse_patches(root_dir: &Path) -> Result> { ++ let root_name = root_dir.file_name().expect("Invalid patch root directory"); ++ let patch_metadata = root_dir.join(PATCH_INFO_FILE_NAME); ++ let patch_info = Arc::new( ++ serde::deserialize_with_magic::(patch_metadata, PATCH_INFO_MAGIC) ++ .with_context(|| { ++ format!( ++ "Failed to parse patch '{}' metadata", ++ root_name.to_string_lossy(), ++ ) ++ })?, ++ ); ++ ++ patch_info ++ .entities ++ .iter() ++ .map(|patch_entity| { ++ let patch_info = patch_info.clone(); ++ match patch_info.kind { ++ PatchType::UserPatch => Ok(Patch::UserPatch(Self::parse_user_patch( ++ root_dir, ++ patch_info, ++ patch_entity, ++ )?)), ++ PatchType::KernelPatch => Ok(Patch::KernelPatch(Self::parse_kernel_patch( ++ root_dir, ++ patch_info, ++ patch_entity, ++ )?)), ++ } ++ }) ++ .collect::>>() ++ } ++ + fn scan_patches>(directory: P) -> Result>> { + const TRAVERSE_OPTION: fs::TraverseOptions = fs::TraverseOptions { recursive: false }; + + let mut patch_map = IndexMap::new(); + +- info!("Scanning patches from {}...", directory.as_ref().display()); +- for patch_dir in fs::list_dirs(directory, TRAVERSE_OPTION)? { +- let resolve_result = PatchResolver::resolve_patch(&patch_dir) +- .with_context(|| format!("Failed to resolve patch from {}", patch_dir.display())); +- match resolve_result { ++ info!( ++ "Scanning patches from '{}'...", ++ directory.as_ref().display() ++ ); ++ for root_dir in fs::list_dirs(directory, TRAVERSE_OPTION)? { ++ match Self::parse_patches(&root_dir) { + Ok(patches) => { + for patch in patches { +- debug!("Detected patch '{}'", patch); + patch_map.insert(*patch.uuid(), Arc::new(patch)); + } + } +@@ -399,15 +497,12 @@ impl PatchManager { + bail!("Cannot set patch '{}' status to '{}'", patch, value); + } + +- let (index, _) = self.status_map.insert_full(*patch.uuid(), value); +- if let Some(last_index) = self +- .status_map +- .last() +- .and_then(|(key, _)| self.status_map.get_index_of(key)) +- { +- if index != last_index { +- self.status_map.move_index(index, last_index); +- } ++ let uuid = *patch.uuid(); ++ let (curr_index, _) = self.status_map.insert_full(uuid, value); ++ ++ let last_index = self.status_map.len().saturating_sub(1); ++ if curr_index != last_index { ++ self.status_map.move_index(curr_index, last_index); + } + + Ok(()) +@@ -415,16 +510,16 @@ impl PatchManager { + } + + impl PatchManager { +- fn driver_patch_status(&self, patch: &Patch, _flag: PatchOpFlag) -> Result { +- self.driver.patch_status(patch) +- } +- + fn driver_check_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { + self.driver.check_patch(patch, flag) + } + +- fn driver_apply_patch(&mut self, patch: &Patch, _flag: PatchOpFlag) -> Result<()> { +- self.driver.apply_patch(patch)?; ++ fn driver_get_patch_status(&self, patch: &Patch) -> Result { ++ self.driver.get_patch_status(patch) ++ } ++ ++ fn driver_load_patch(&mut self, patch: &Patch, _flag: PatchOpFlag) -> Result<()> { ++ self.driver.load_patch(patch)?; + self.set_patch_status(patch, PatchStatus::Deactived) + } + +diff --git a/syscared/src/patch/mod.rs b/syscared/src/patch/mod.rs +index 26a97da..edd247a 100644 +--- a/syscared/src/patch/mod.rs ++++ b/syscared/src/patch/mod.rs +@@ -16,7 +16,6 @@ pub mod driver; + pub mod entity; + pub mod manager; + pub mod monitor; +-pub mod resolver; + pub mod transaction; + + const PATCH_INFO_FILE_NAME: &str = "patch_info"; +diff --git a/syscared/src/patch/resolver/kpatch.rs b/syscared/src/patch/resolver/kpatch.rs +deleted file mode 100644 +index f8885ff..0000000 +--- a/syscared/src/patch/resolver/kpatch.rs ++++ /dev/null +@@ -1,226 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscared is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ +- ffi::{CStr, OsString}, +- path::{Path, PathBuf}, +- sync::Arc, +-}; +- +-use anyhow::{anyhow, Context, Result}; +-use object::{NativeFile, Object, ObjectSection}; +- +-use syscare_abi::{PatchEntity, PatchInfo}; +-use syscare_common::{ +- concat_os, +- ffi::{CStrExt, OsStrExt}, +- fs, +-}; +- +-use super::PatchResolverImpl; +-use crate::patch::entity::{KernelPatch, KernelPatchFunction, Patch}; +- +-const KPATCH_SUFFIX: &str = ".ko"; +-const KPATCH_SYS_DIR: &str = "/sys/kernel/livepatch"; +-const KPATCH_SYS_FILE_NAME: &str = "enabled"; +- +-mod ffi { +- use std::os::raw::{c_char, c_long, c_ulong}; +- +- use object::{ +- read::elf::{ElfSectionRelocationIterator, FileHeader}, +- Pod, Relocation, +- }; +- +- #[repr(C)] +- #[derive(Debug, Clone, Copy)] +- /// Corresponds to `struct kpatch_patch_func` defined in `kpatch-patch.h` +- pub struct KpatchFunction { +- pub new_addr: c_ulong, +- pub new_size: c_ulong, +- pub old_addr: c_ulong, +- pub old_size: c_ulong, +- pub sympos: u64, +- pub name: *const c_char, +- pub obj_name: *const c_char, +- pub ref_name: *const c_char, +- pub ref_offset: c_long, +- } +- +- pub const KPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); +- pub const KPATCH_FUNCTION_OFFSET: usize = 40; +- pub const KPATCH_OBJECT_OFFSET: usize = 48; +- +- /* +- * SAFETY: This struct is +- * - #[repr(C)] +- * - have no invalid byte values +- * - have no padding +- */ +- unsafe impl Pod for KpatchFunction {} +- +- pub struct KpatchRelocation { +- pub _addr: (u64, Relocation), +- pub name: (u64, Relocation), +- pub object: (u64, Relocation), +- } +- +- pub struct KpatchRelocationIterator<'data, 'file, Elf: FileHeader>( +- ElfSectionRelocationIterator<'data, 'file, Elf, &'data [u8]>, +- ); +- +- impl<'data, 'file, Elf: FileHeader> KpatchRelocationIterator<'data, 'file, Elf> { +- pub fn new(relocations: ElfSectionRelocationIterator<'data, 'file, Elf>) -> Self { +- Self(relocations) +- } +- } +- +- impl Iterator for KpatchRelocationIterator<'_, '_, Elf> { +- type Item = KpatchRelocation; +- +- fn next(&mut self) -> Option { +- if let (Some(addr), Some(name), Some(object)) = +- (self.0.next(), self.0.next(), self.0.next()) +- { +- return Some(KpatchRelocation { +- _addr: addr, +- name, +- object, +- }); +- } +- None +- } +- } +-} +- +-const KPATCH_FUNCS_SECTION: &str = ".kpatch.funcs"; +-const KPATCH_STRINGS_SECTION: &str = ".kpatch.strings"; +- +-pub struct KpatchResolverImpl; +- +-impl KpatchResolverImpl { +- #[inline] +- fn resolve_patch_file(patch: &mut KernelPatch) -> Result<()> { +- let patch_file = fs::mmap(&patch.patch_file) +- .with_context(|| format!("Failed to mmap file {}", patch.patch_file.display()))?; +- let patch_elf = NativeFile::parse(patch_file.as_ref()).context("Invalid patch format")?; +- +- // Read sections +- let function_section = patch_elf +- .section_by_name(KPATCH_FUNCS_SECTION) +- .with_context(|| format!("Cannot find section '{}'", KPATCH_FUNCS_SECTION))?; +- let string_section = patch_elf +- .section_by_name(KPATCH_STRINGS_SECTION) +- .with_context(|| format!("Cannot find section '{}'", KPATCH_STRINGS_SECTION))?; +- let function_data = function_section +- .data() +- .with_context(|| format!("Failed to read section '{}'", KPATCH_FUNCS_SECTION))?; +- let string_data = string_section +- .data() +- .with_context(|| format!("Failed to read section '{}'", KPATCH_FUNCS_SECTION))?; +- +- // Resolve patch functions +- let patch_functions = &mut patch.functions; +- let kpatch_function_slice = object::slice_from_bytes::( +- function_data, +- function_data.len() / ffi::KPATCH_FUNCTION_SIZE, +- ) +- .map(|(f, _)| f) +- .map_err(|_| anyhow!("Invalid data format")) +- .context("Failed to resolve patch functions")?; +- +- for function in kpatch_function_slice { +- patch_functions.push(KernelPatchFunction { +- name: OsString::new(), +- object: OsString::new(), +- old_addr: function.old_addr, +- old_size: function.old_size, +- new_addr: function.new_addr, +- new_size: function.new_size, +- }); +- } +- +- // Relocate patch functions +- for relocation in ffi::KpatchRelocationIterator::new(function_section.relocations()) { +- let (name_reloc_offset, name_reloc) = relocation.name; +- let (object_reloc_offset, obj_reloc) = relocation.object; +- +- // Relocate patch function name +- let name_index = (name_reloc_offset as usize - ffi::KPATCH_FUNCTION_OFFSET) +- / ffi::KPATCH_FUNCTION_SIZE; +- let name_function = patch_functions +- .get_mut(name_index) +- .context("Failed to find patch function")?; +- let name_offset = name_reloc.addend() as usize; +- let name_string = CStr::from_bytes_with_next_nul(&string_data[name_offset..]) +- .context("Failed to parse patch object name")? +- .to_os_string(); +- +- name_function.name = name_string; +- +- // Relocate patch function object +- let object_index = (object_reloc_offset as usize - ffi::KPATCH_OBJECT_OFFSET) +- / ffi::KPATCH_FUNCTION_SIZE; +- let object_function = patch_functions +- .get_mut(object_index) +- .context("Failed to find patch function")?; +- let object_offset = obj_reloc.addend() as usize; +- let object_string = CStr::from_bytes_with_next_nul(&string_data[object_offset..]) +- .context("Failed to parse patch function name")? +- .to_os_string(); +- +- object_function.object = object_string; +- } +- +- Ok(()) +- } +-} +- +-impl PatchResolverImpl for KpatchResolverImpl { +- fn resolve_patch( +- &self, +- patch_root: &Path, +- patch_info: Arc, +- patch_entity: &PatchEntity, +- ) -> Result { +- let target_name = patch_entity.patch_name.as_os_str().to_os_string(); +- let module_name = patch_entity.patch_name.replace(['-', '.'], "_"); +- let patch_file = patch_root.join(concat_os!(&patch_entity.patch_name, KPATCH_SUFFIX)); +- let sys_file = PathBuf::from(KPATCH_SYS_DIR) +- .join(&module_name) +- .join(KPATCH_SYS_FILE_NAME); +- +- let mut patch = KernelPatch { +- uuid: patch_entity.uuid, +- name: concat_os!( +- patch_info.target.short_name(), +- "/", +- patch_info.name(), +- "/", +- &patch_entity.patch_target +- ), +- info: patch_info.clone(), +- pkg_name: patch_info.target.full_name(), +- target_name, +- module_name, +- patch_file, +- sys_file, +- functions: Vec::new(), +- checksum: patch_entity.checksum.clone(), +- }; +- Self::resolve_patch_file(&mut patch).context("Failed to resolve patch")?; +- +- Ok(Patch::KernelPatch(patch)) +- } +-} +diff --git a/syscared/src/patch/resolver/mod.rs b/syscared/src/patch/resolver/mod.rs +deleted file mode 100644 +index 7c4a504..0000000 +--- a/syscared/src/patch/resolver/mod.rs ++++ /dev/null +@@ -1,63 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscared is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{path::Path, sync::Arc}; +- +-use super::{entity::Patch, PATCH_INFO_FILE_NAME}; +- +-use anyhow::{Context, Result}; +-use syscare_abi::{PatchEntity, PatchInfo, PatchType, PATCH_INFO_MAGIC}; +-use syscare_common::util::serde; +- +-mod kpatch; +-mod upatch; +- +-use kpatch::KpatchResolverImpl; +-use upatch::UpatchResolverImpl; +- +-pub trait PatchResolverImpl { +- fn resolve_patch( +- &self, +- patch_root: &Path, +- patch_info: Arc, +- patch_entity: &PatchEntity, +- ) -> Result; +-} +- +-pub struct PatchResolver; +- +-impl PatchResolver { +- pub fn resolve_patch>(directory: P) -> Result> { +- let patch_root = directory.as_ref(); +- let patch_info = Arc::new( +- serde::deserialize_with_magic::( +- patch_root.join(PATCH_INFO_FILE_NAME), +- PATCH_INFO_MAGIC, +- ) +- .context("Failed to resolve patch metadata")?, +- ); +- let resolver: &dyn PatchResolverImpl = match patch_info.kind { +- PatchType::UserPatch => &UpatchResolverImpl, +- PatchType::KernelPatch => &KpatchResolverImpl, +- }; +- +- let mut patch_list = Vec::with_capacity(patch_info.entities.len()); +- for patch_entity in &patch_info.entities { +- let patch = resolver.resolve_patch(patch_root, patch_info.clone(), patch_entity)?; +- patch_list.push(patch); +- } +- +- Ok(patch_list) +- } +-} +diff --git a/syscared/src/patch/resolver/upatch.rs b/syscared/src/patch/resolver/upatch.rs +deleted file mode 100644 +index a5e4ad0..0000000 +--- a/syscared/src/patch/resolver/upatch.rs ++++ /dev/null +@@ -1,182 +0,0 @@ +-// SPDX-License-Identifier: Mulan PSL v2 +-/* +- * Copyright (c) 2024 Huawei Technologies Co., Ltd. +- * syscared is licensed under Mulan PSL v2. +- * You can use this software according to the terms and conditions of the Mulan PSL v2. +- * You may obtain a copy of Mulan PSL v2 at: +- * http://license.coscl.org.cn/MulanPSL2 +- * +- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +- * See the Mulan PSL v2 for more details. +- */ +- +-use std::{ +- ffi::{CStr, OsString}, +- path::Path, +- sync::Arc, +-}; +- +-use anyhow::{anyhow, Context, Result}; +-use object::{NativeFile, Object, ObjectSection}; +- +-use syscare_abi::{PatchEntity, PatchInfo}; +-use syscare_common::{concat_os, ffi::CStrExt, fs}; +- +-use super::PatchResolverImpl; +-use crate::patch::entity::{Patch, UserPatch, UserPatchFunction}; +- +-mod ffi { +- use std::os::raw::{c_char, c_ulong}; +- +- use object::{ +- read::elf::{ElfSectionRelocationIterator, FileHeader}, +- Pod, Relocation, +- }; +- +- #[repr(C)] +- #[derive(Debug, Clone, Copy)] +- /// Corresponds to `struct upatch_path_func` defined in `upatch-patch.h` +- pub struct UpatchFunction { +- pub new_addr: c_ulong, +- pub new_size: c_ulong, +- pub old_addr: c_ulong, +- pub old_size: c_ulong, +- pub sympos: c_ulong, +- pub name: *const c_char, +- } +- +- /* +- * SAFETY: This struct is +- * - #[repr(C)] +- * - have no invalid byte values +- * - have no padding +- */ +- unsafe impl Pod for UpatchFunction {} +- +- pub const UPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); +- pub const UPATCH_FUNCTION_OFFSET: usize = 40; +- +- pub struct UpatchRelocation { +- pub _addr: (u64, Relocation), +- pub name: (u64, Relocation), +- } +- +- pub struct UpatchRelocationIterator<'data, 'file, Elf: FileHeader>( +- ElfSectionRelocationIterator<'data, 'file, Elf, &'data [u8]>, +- ); +- +- impl<'data, 'file, Elf: FileHeader> UpatchRelocationIterator<'data, 'file, Elf> { +- pub fn new(relocations: ElfSectionRelocationIterator<'data, 'file, Elf>) -> Self { +- Self(relocations) +- } +- } +- +- impl Iterator for UpatchRelocationIterator<'_, '_, Elf> { +- type Item = UpatchRelocation; +- +- fn next(&mut self) -> Option { +- if let (Some(addr), Some(name)) = (self.0.next(), self.0.next()) { +- return Some(UpatchRelocation { _addr: addr, name }); +- } +- None +- } +- } +-} +- +-const UPATCH_FUNCS_SECTION: &str = ".upatch.funcs"; +-const UPATCH_STRINGS_SECTION: &str = ".upatch.strings"; +- +-pub struct UpatchResolverImpl; +- +-impl UpatchResolverImpl { +- #[inline] +- fn resolve_patch_elf(patch: &mut UserPatch) -> Result<()> { +- let patch_file = fs::mmap(&patch.patch_file) +- .with_context(|| format!("Failed to mmap file {}", patch.patch_file.display()))?; +- let patch_elf = NativeFile::parse(patch_file.as_ref()).context("Invalid patch format")?; +- +- // Read sections +- let function_section = patch_elf +- .section_by_name(UPATCH_FUNCS_SECTION) +- .with_context(|| format!("Cannot find section '{}'", UPATCH_FUNCS_SECTION))?; +- let string_section = patch_elf +- .section_by_name(UPATCH_STRINGS_SECTION) +- .with_context(|| format!("Cannot find section '{}'", UPATCH_STRINGS_SECTION))?; +- let function_data = function_section +- .data() +- .with_context(|| format!("Failed to read section '{}'", UPATCH_FUNCS_SECTION))?; +- let string_data = string_section +- .data() +- .with_context(|| format!("Failed to read section '{}'", UPATCH_FUNCS_SECTION))?; +- +- // Resolve patch functions +- let patch_functions = &mut patch.functions; +- let upatch_function_slice = object::slice_from_bytes::( +- function_data, +- function_data.len() / ffi::UPATCH_FUNCTION_SIZE, +- ) +- .map(|(f, _)| f) +- .map_err(|_| anyhow!("Invalid data format")) +- .context("Failed to resolve patch functions")?; +- +- for function in upatch_function_slice { +- patch_functions.push(UserPatchFunction { +- name: OsString::new(), +- old_addr: function.old_addr, +- old_size: function.old_size, +- new_addr: function.new_addr, +- new_size: function.new_size, +- }); +- } +- +- // Relocate patch functions +- for relocation in ffi::UpatchRelocationIterator::new(function_section.relocations()) { +- let (name_reloc_offset, name_reloc) = relocation.name; +- +- let name_index = (name_reloc_offset as usize - ffi::UPATCH_FUNCTION_OFFSET) +- / ffi::UPATCH_FUNCTION_SIZE; +- let name_function = patch_functions +- .get_mut(name_index) +- .context("Failed to find patch function")?; +- let name_offset = name_reloc.addend() as usize; +- let name_string = CStr::from_bytes_with_next_nul(&string_data[name_offset..]) +- .context("Failed to parse patch function name")? +- .to_os_string(); +- +- name_function.name = name_string; +- } +- +- Ok(()) +- } +-} +- +-impl PatchResolverImpl for UpatchResolverImpl { +- fn resolve_patch( +- &self, +- patch_root: &Path, +- patch_info: Arc, +- patch_entity: &PatchEntity, +- ) -> Result { +- let mut patch = UserPatch { +- uuid: patch_entity.uuid, +- name: concat_os!( +- patch_info.target.short_name(), +- "/", +- patch_info.name(), +- "/", +- fs::file_name(&patch_entity.patch_target) +- ), +- info: patch_info.clone(), +- pkg_name: patch_info.target.full_name(), +- patch_file: patch_root.join(&patch_entity.patch_name), +- target_elf: patch_entity.patch_target.clone(), +- functions: Vec::new(), +- checksum: patch_entity.checksum.clone(), +- }; +- Self::resolve_patch_elf(&mut patch).context("Failed to resolve patch")?; +- +- Ok(Patch::UserPatch(patch)) +- } +-} +-- +2.43.0 + diff --git a/0137-project-update-Cargo.lock.patch b/0137-project-update-Cargo.lock.patch new file mode 100644 index 0000000000000000000000000000000000000000..62bd328e28ad6b3138fc7ad20570ae7709fe5363 --- /dev/null +++ b/0137-project-update-Cargo.lock.patch @@ -0,0 +1,30 @@ +From c62926a1d600225434829f6dd8a308be80a9d357 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 28 May 2025 18:29:34 +0800 +Subject: [PATCH] project: update Cargo.lock + +Signed-off-by: renoseven +--- + Cargo.lock | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Cargo.lock b/Cargo.lock +index b1598cd..2014167 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -1171,10 +1171,11 @@ name = "syscare-common" + version = "1.2.2" + dependencies = [ + "anyhow", +- "lazy_static", + "log", + "memmap2", + "nix", ++ "num_cpus", ++ "object", + "regex", + "serde", + "serde_cbor", +-- +2.43.0 + diff --git a/0138-project-update-CMakeLists.txt.patch b/0138-project-update-CMakeLists.txt.patch new file mode 100644 index 0000000000000000000000000000000000000000..18879f9b6b4794f479cc79c0e40d8005c167c599 --- /dev/null +++ b/0138-project-update-CMakeLists.txt.patch @@ -0,0 +1,166 @@ +From 46340c6b6c14c9d7b582c30659bd37fc16e04f2f Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Wed, 11 Jun 2025 13:31:46 +0800 +Subject: [PATCH] project: update CMakeLists.txt + +Signed-off-by: renoseven +--- + CMakeLists.txt | 80 ++++++++++++++++++++++++-------------------------- + 1 file changed, 39 insertions(+), 41 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e1a9051..e923258 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -37,29 +37,29 @@ if(GIT_FOUND) + ERROR_QUIET + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) +- set(PROJECT_BUILD_VERSION "${BUILD_VERSION}-g${GIT_VERSION}") ++ set(BUILD_VERSION "${BUILD_VERSION}-g${GIT_VERSION}") + else() +- set(PROJECT_BUILD_VERSION "${BUILD_VERSION}") ++ set(BUILD_VERSION "${BUILD_VERSION}") + endif() + + # Build configurations + if(ENABLE_ASAN) +- set(PROJECT_BUILD_VERSION "${PROJECT_BUILD_VERSION}-asan") +- list(APPEND PROJECT_C_BUILD_FLAGS -fsanitize=address -fno-omit-frame-pointer) +- list(APPEND PROJECT_C_LIBRARIES asan) ++ set(BUILD_VERSION "${BUILD_VERSION}-asan") ++ list(APPEND BUILD_FLAGS_C -fsanitize=address -fno-omit-frame-pointer) ++ list(APPEND LINK_LIBRARIES_C asan) + endif() + + if(ENABLE_GCOV) +- set(PROJECT_BUILD_VERSION "${PROJECT_BUILD_VERSION}-gcov") +- list(APPEND PROJECT_C_BUILD_FLAGS -ftest-coverage -fprofile-arcs) +- list(APPEND PROJECT_RUST_FLAGS -C instrument-coverage) +- list(APPEND PROJECT_C_LIBRARIES gcov) ++ set(BUILD_VERSION "${BUILD_VERSION}-gcov") ++ list(APPEND BUILD_FLAGS_C -ftest-coverage -fprofile-arcs) ++ list(APPEND BUILD_FLAGS_RUST -C instrument-coverage) ++ list(APPEND LINK_LIBRARIES_C gcov) + endif() + + # Build flags +-list(APPEND PROJECT_C_BUILD_FLAGS +- -std=gnu99 -Wall -O2 -Werror -Wextra +- -DBUILD_VERSION="${PROJECT_BUILD_VERSION}" -D_FORTIFY_SOURCE=2 ++list(APPEND BUILD_FLAGS_C ++ -std=gnu99 -O2 -Wall -Wextra -Werror ++ -DBUILD_VERSION="${BUILD_VERSION}" -D_FORTIFY_SOURCE=2 + -Wtrampolines -Wformat=2 -Wstrict-prototypes -Wdate-time + -Wstack-usage=8192 -Wfloat-equal -Wswitch-default + -Wshadow -Wconversion -Wcast-qual -Wunused -Wundef +@@ -70,16 +70,16 @@ list(APPEND PROJECT_C_BUILD_FLAGS + + # The -Werror=cast-align compiler flag causes issues on riscv64 GCC, + # while the same operations do not error on aarch64. This appears to be +-# a compiler-specific problem. Temporarily disable this option as a ++# a compiler-specific problem. Temporarily disable this option as a + # workaround since applying fixes would require intrusive code changes + # across multiple files. + if(NOT ARCH STREQUAL "riscv64") +- list(APPEND PROJECT_C_BUILD_FLAGS ++ list(APPEND BUILD_FLAGS_C + -Wcast-align + ) + endif() + +-list(APPEND PROJECT_RUST_FLAGS ++list(APPEND BUILD_FLAGS_RUST + --cfg unsound_local_offset + -D warnings + -C link-arg=-s +@@ -91,7 +91,7 @@ list(APPEND PROJECT_RUST_FLAGS + ) + + # Link flags +-list(APPEND PROJECT_C_LINK_FLAGS ++list(APPEND LINK_FLAGS_C + -pie + -Wl,-z,relro,-z,now + -Wl,-z,noexecstack -rdynamic +@@ -100,9 +100,9 @@ list(APPEND PROJECT_C_LINK_FLAGS + ) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") +- list(APPEND PROJECT_C_BUILD_FLAGS -g) ++ list(APPEND BUILD_FLAGS_C -g) + elseif(CMAKE_BUILD_TYPE STREQUAL "Release") +- list(APPEND PROJECT_C_LINK_FLAGS -s) ++ list(APPEND LINK_FLAGS_C -s) + endif() + + # Install directories +@@ -120,44 +120,42 @@ message("███████║ ██║ ███████║╚█ + message("╚══════╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝") + message("---------------------------------------------------------") + message("-- Build type: ${CMAKE_BUILD_TYPE}") +-message("-- Build version: ${PROJECT_BUILD_VERSION}") +-message("-- Rust flags: ${PROJECT_RUST_FLAGS}") +-message("-- Build flags: ${PROJECT_C_BUILD_FLAGS}") +-message("-- Link flags: ${PROJECT_C_LINK_FLAGS}") +-message("-- Link libraries: ${PROJECT_C_LIBRARIES}") ++message("-- Build version: ${BUILD_VERSION}") ++message("-- Rust flags: ${BUILD_FLAGS_RUST}") ++message("-- Build flags: ${BUILD_FLAGS_C}") ++message("-- Link flags: ${LINK_FLAGS_C}") ++message("-- Link libraries: ${LINK_LIBRARIES_C}") + message("-- Binary directory: ${SYSCARE_BINARY_DIR}") + message("-- Libexec directory: ${SYSCARE_LIBEXEC_DIR}") + message("-- Service directory: ${SYSCARE_SERVICE_DIR}") + message("---------------------------------------------------------") + + # Apply all flags +-add_compile_options(${PROJECT_C_BUILD_FLAGS}) +-add_link_options(${PROJECT_C_LINK_FLAGS}) +-link_libraries(${PROJECT_C_LIBRARIES}) ++add_compile_options(${BUILD_FLAGS_C}) ++add_link_options(${LINK_FLAGS_C}) ++link_libraries(${LINK_LIBRARIES_C}) + + # Build rust executables +-set(RUST_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/rust") ++set(RUST_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/target") + set(RUST_OUTPUT_DIR "${RUST_TARGET_DIR}/release") +-foreach(FLAG IN LISTS PROJECT_RUST_FLAGS) +- set(RUST_FLAGS "${RUST_FLAGS} ${FLAG}") ++foreach(CURR_FLAG IN LISTS BUILD_FLAGS_RUST) ++ set(RUST_FLAGS "${RUST_FLAGS} ${CURR_FLAG}") + endforeach() + +-add_custom_target(rust-build ALL +- COMMENT "Building rust executables..." +- COMMAND ${CMAKE_COMMAND} -E env +- "BUILD_VERSION=${PROJECT_BUILD_VERSION}" +- "RUSTFLAGS=${RUST_FLAGS}" +- cargo build --release --target-dir "${RUST_TARGET_DIR}" +- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ++add_custom_target(syscare ALL ++ COMMAND cargo build --release --target-dir ${RUST_TARGET_DIR} ++ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) + +-# Generate upatch helpers +-add_custom_target(generate-upatch-helpers ALL +- COMMENT "Generating upatch helpers..." ++set_target_properties(syscare PROPERTIES ++ ENVIRONMENT "RUSTFLAGS=${RUST_FLAGS};BUILD_VERSION=${BUILD_VERSION}" ++ ADDITIONAL_CLEAN_FILES "${RUST_TARGET_DIR}" ++) ++ ++add_custom_target(upatch-helpers ALL + COMMAND ln -sf upatch-helper upatch-cc + COMMAND ln -sf upatch-helper upatch-c++ +- DEPENDS +- rust-build ++ DEPENDS syscare + WORKING_DIRECTORY ${RUST_OUTPUT_DIR} + ) + +-- +2.43.0 + diff --git a/0139-upatch-diff-fix-special-section-cannot-be-correlated.patch b/0139-upatch-diff-fix-special-section-cannot-be-correlated.patch new file mode 100644 index 0000000000000000000000000000000000000000..650894409d608465d20ee966e3f95454f8f95f21 --- /dev/null +++ b/0139-upatch-diff-fix-special-section-cannot-be-correlated.patch @@ -0,0 +1,34 @@ +From caf86ca270ef90eb6d80bfb581fe38abde7ad11a Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 24 Jun 2025 17:32:59 +0800 +Subject: [PATCH] upatch-diff: fix special section cannot be correlated issue + + In some special case, section name may be changed like from +'.text.xxxx' to '.text.xxxx.unlikely', while the section content +does not change. This issue caused section correlation failure. +We found that the section corresponding symbol was same. Thus, +we linked symbol section when correlating symbols. + +Signed-off-by: renoseven +--- + upatch-diff/elf-correlate.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c +index 8e2f765..5991c2c 100644 +--- a/upatch-diff/elf-correlate.c ++++ b/upatch-diff/elf-correlate.c +@@ -38,6 +38,10 @@ static void correlate_symbol(struct symbol *sym_orig, + sym_patched->name, sym_orig->name); + sym_patched->name = sym_orig->name; + } ++ if (sym_patched->sec && sym_orig->sec) { ++ sym_patched->sec->twin = sym_orig->sec; ++ sym_orig->sec->twin = sym_patched->sec; ++ } + } + + void upatch_correlate_symbols(struct upatch_elf *uelf_source, +-- +2.43.0 + diff --git a/0140-upatch-helper-fix-compilation-command-detection.patch b/0140-upatch-helper-fix-compilation-command-detection.patch new file mode 100644 index 0000000000000000000000000000000000000000..a3ed4c7df82d66483ac854aceed3a3a587ee5415 --- /dev/null +++ b/0140-upatch-helper-fix-compilation-command-detection.patch @@ -0,0 +1,103 @@ +From cce6eb45c2de98034eb442ceaa851cdde279873c Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 26 Jun 2025 15:31:30 +0800 +Subject: [PATCH] upatch-helper: fix compilation command detection + +Fix the logic for detecting compilation commands to properly handle: +* Non-compilation flags (--help, --version, -E, etc.) +* Special source files (-, @args.txt) +* Source file extensions (.c, .cpp, .s, etc.) +* Compile signal flags (-x) + +This ensures the helper only intercepts actual compilation commands. + +Signed-off-by: renoseven +--- + upatch-helper/src/main.rs | 57 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 55 insertions(+), 2 deletions(-) + +diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs +index 3992367..7c7b2b8 100644 +--- a/upatch-helper/src/main.rs ++++ b/upatch-helper/src/main.rs +@@ -25,6 +25,20 @@ use uuid::Uuid; + const COMPILER_KEYWORDS_CC: &[&str] = &["cc", "clang"]; + const COMPILER_KEYWORDS_CXX: &[&str] = &["++", "xx"]; + ++const COMPILER_EXCLUDE_FLAGS: &[&str] = &[ ++ "-E", // Preprocess only; does not compile. ++ "--version", // Print compiler version and exit. ++ "--help", // Print help message and exit. ++ "--target-help", // Print target-specific help and exit. ++ "-dumpversion", // Print compiler version string (e.g., "11.2.0") and exit. ++ "-dumpmachine", // Print compiler target machine (e.g., "x86_64-linux-gnu") and exit. ++ "-###", // Dry run: print commands that would be executed, but do not run them. ++]; ++const COMPILER_EXCLUDE_FLAG_PREFIXES: &[&str] = &["--print-"]; ++const COMPILER_COMPILE_SIGNAL_FLAGS: &[&str] = &["-x"]; ++const COMPILER_SPECIAL_SOURCE_FILES: &[&str] = &["-", "@args.txt"]; ++const COMPILER_SOURCE_FILE_EXTENSIONS: &[&str] = &["c", "cc", "cpp", "cxx", "s", "S"]; ++ + const HELPER_ENV_NAME_CC: &str = "UPATCH_HELPER_CC"; + const HELPER_ENV_NAME_CXX: &str = "UPATCH_HELPER_CXX"; + const HELPER_ENV_NAMES: &[(&[&str], &str)] = &[ +@@ -32,7 +46,6 @@ const HELPER_ENV_NAMES: &[(&[&str], &str)] = &[ + (COMPILER_KEYWORDS_CXX, HELPER_ENV_NAME_CXX), + ]; + +-const COMPILE_FLAG_NAME: &str = "-c"; + const COMPILE_OPTIONS_GNU: &[&str] = &[ + "-gdwarf", // generate dwarf debuginfo + "-ffunction-sections", // generate corresponding section for each function +@@ -55,7 +68,47 @@ const UPATCH_ID_PREFIX: &str = ".upatch_"; + + #[inline(always)] + fn is_compilation(args: &[OsString]) -> bool { +- args.iter().any(|arg| arg == COMPILE_FLAG_NAME) ++ /* check exclude flags */ ++ for arg in args.iter().skip(1) { ++ if COMPILER_EXCLUDE_FLAGS ++ .iter() ++ .any(|&flag| arg == OsStr::new(flag)) ++ { ++ return false; ++ } ++ if COMPILER_EXCLUDE_FLAG_PREFIXES ++ .iter() ++ .any(|&prefix| arg.as_bytes().starts_with(prefix.as_bytes())) ++ { ++ return false; ++ } ++ } ++ ++ /* check compile flag & source file */ ++ for arg in args.iter().skip(1) { ++ if COMPILER_COMPILE_SIGNAL_FLAGS ++ .iter() ++ .any(|&name| arg == OsStr::new(name)) ++ { ++ return true; ++ } ++ if COMPILER_SPECIAL_SOURCE_FILES ++ .iter() ++ .any(|&name| arg == OsStr::new(name)) ++ { ++ return true; ++ } ++ if let Some(src_ext) = Path::new(arg).extension() { ++ if COMPILER_SOURCE_FILE_EXTENSIONS ++ .iter() ++ .any(|&ext| src_ext == OsStr::new(ext)) ++ { ++ return true; ++ } ++ } ++ } ++ ++ false + } + + #[inline(always)] +-- +2.43.0 + diff --git a/0141-upatch-build-use-last-write-wins-strategy-to-solving.patch b/0141-upatch-build-use-last-write-wins-strategy-to-solving.patch new file mode 100644 index 0000000000000000000000000000000000000000..5bc290a94c0244e05d20db3255818ab8f24c82d8 --- /dev/null +++ b/0141-upatch-build-use-last-write-wins-strategy-to-solving.patch @@ -0,0 +1,59 @@ +From 66e9fa966685c4f37144dc91c2fc276691413a2c Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 26 Jun 2025 17:39:54 +0800 +Subject: [PATCH] upatch-build: use last-write-wins strategy to solving upatch + id conflicts + +Replace the conservative filtering approach with an explicit overwrite policy: +1. Process objects in descending order of ID count (complex objects first) +2. Later objects automatically overwrite existing upatch_id mappings +3. Print detailed conflict warnings + +Signed-off-by: renoseven +--- + upatch-build/src/file_relation.rs | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 3c71e50..677cb8b 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -291,22 +291,23 @@ impl FileRelation { + ); + + // We want subsequent objects to contain more identifiers. +- object_info.sort_by(|(_, _, lhs), (_, _, rhs)| lhs.len().cmp(&rhs.len())); ++ object_info.sort_by(|(_, _, lhs), (_, _, rhs)| rhs.len().cmp(&lhs.len())); + + let mut upatch_id_map = IndexMap::new(); +- while let Some((object_file, archive_file, mut upatch_ids)) = object_info.pop() { +- // Remove identifiers in other objects +- for (_, _, ids) in &object_info { +- upatch_ids.retain(|id| !ids.contains(id)); +- } +- // Current object is fully composed of other objects, we skip it. +- if upatch_ids.is_empty() { +- warn!("Skipped {}", object_file.display()); +- } +- for upatch_id in upatch_ids { +- upatch_id_map.insert(upatch_id, (object_file.clone(), archive_file.clone())); ++ for (object, archive, ids) in object_info { ++ for id in ids { ++ let result = upatch_id_map.insert(id.clone(), (object.clone(), archive.clone())); ++ if let Some((old_object, _)) = result { ++ warn!( ++ "{}: Object {} is replaced by {}", ++ id.to_string_lossy(), ++ old_object.display(), ++ object.display() ++ ); ++ } + } + } ++ + ensure!( + !upatch_id_map.is_empty(), + "Cannot find any upatch id in {}", +-- +2.43.0 + diff --git a/0142-syscare-build-check-source-package-version-release-m.patch b/0142-syscare-build-check-source-package-version-release-m.patch new file mode 100644 index 0000000000000000000000000000000000000000..367d96bd3b88a9ff8399a12392266e02dc131a80 --- /dev/null +++ b/0142-syscare-build-check-source-package-version-release-m.patch @@ -0,0 +1,64 @@ +From 6f11929a3a4d5b1918296b128b51184590dad16a Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 27 Jun 2025 11:52:07 +0800 +Subject: [PATCH] syscare-build: check source package version & release matched + to debuginfo package + +Ensure each debug package's version and release matches at least one source +package to prevent symbol mismatches. + +Signed-off-by: renoseven +--- + syscare-build/src/main.rs | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/syscare-build/src/main.rs b/syscare-build/src/main.rs +index 27754af..7d4d201 100644 +--- a/syscare-build/src/main.rs ++++ b/syscare-build/src/main.rs +@@ -122,7 +122,8 @@ impl SyscareBuild { + } + + fn collect_package_info(&self) -> Result> { +- let mut pkg_list = Vec::new(); ++ let mut source_pkg_list = Vec::new(); ++ let mut debug_pkg_list = Vec::new(); + + for pkg_path in self.args.source.clone() { + let mut pkg_info = PKG_IMPL.parse_package_info(&pkg_path)?; +@@ -139,7 +140,7 @@ impl SyscareBuild { + bail!("File {} is not a source package", pkg_info.short_name()); + } + +- pkg_list.push(pkg_info); ++ source_pkg_list.push(pkg_info); + } + + for pkg_path in self.args.debuginfo.clone() { +@@ -160,10 +161,22 @@ impl SyscareBuild { + self.args.patch_arch + ); + } ++ debug_pkg_list.push(pkg_info); + } + info!("------------------------------"); + +- Ok(pkg_list) ++ for source_pkg in &source_pkg_list { ++ ensure!( ++ debug_pkg_list.iter().any(|debug_pkg| { ++ source_pkg.version == debug_pkg.version ++ && source_pkg.release == debug_pkg.release ++ }), ++ "Package {} has no matching debuginfo package", ++ source_pkg.full_name(), ++ ); ++ } ++ ++ Ok(source_pkg_list) + } + + fn prepare_source_code( +-- +2.43.0 + diff --git a/0143-syscare-build-match-source-package-to-debuginfo-pack.patch b/0143-syscare-build-match-source-package-to-debuginfo-pack.patch new file mode 100644 index 0000000000000000000000000000000000000000..7b0b3ed7aefa45572798e4ca4e5de5534778b510 --- /dev/null +++ b/0143-syscare-build-match-source-package-to-debuginfo-pack.patch @@ -0,0 +1,41 @@ +From 993b4b629b7698d91c9bd17685e7e433a65e0d6c Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 3 Jul 2025 20:10:59 +0800 +Subject: [PATCH] syscare-build: match source package to debuginfo package + +In some case, source packages would be more than debuginfo packages. +This caused a package matching error. So we changed to use debuginfo as the key. + +Signed-off-by: renoseven +--- + syscare-build/src/main.rs | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/syscare-build/src/main.rs b/syscare-build/src/main.rs +index 7d4d201..8df6e02 100644 +--- a/syscare-build/src/main.rs ++++ b/syscare-build/src/main.rs +@@ -165,14 +165,14 @@ impl SyscareBuild { + } + info!("------------------------------"); + +- for source_pkg in &source_pkg_list { ++ for debug_pkg in &debug_pkg_list { + ensure!( +- debug_pkg_list.iter().any(|debug_pkg| { +- source_pkg.version == debug_pkg.version +- && source_pkg.release == debug_pkg.release ++ source_pkg_list.iter().any(|source_pkg| { ++ debug_pkg.version == source_pkg.version ++ && debug_pkg.release == source_pkg.release + }), +- "Package {} has no matching debuginfo package", +- source_pkg.full_name(), ++ "Package {} has no matching source package", ++ debug_pkg.full_name(), + ); + } + +-- +2.43.0 + diff --git a/0144-syscare-build-fix-patch-file-list-disorder-when-buil.patch b/0144-syscare-build-fix-patch-file-list-disorder-when-buil.patch new file mode 100644 index 0000000000000000000000000000000000000000..d4d9ea299851fa4e42dce7a4ca9315d5d2f60b01 --- /dev/null +++ b/0144-syscare-build-fix-patch-file-list-disorder-when-buil.patch @@ -0,0 +1,103 @@ +From f03f87fc88db7743af8533c5c0a51843539e5fd3 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Thu, 3 Jul 2025 21:43:48 +0800 +Subject: [PATCH] syscare-build: fix patch file list disorder when building + cumulative patch + +1. stop sorting user-input patch list when collecting patch files +2. rewrite all patch file paths in metadata with metadata directory +3. use patch file list instead of looking up patches from metadata directory + +Signed-off-by: renoseven +--- + syscare-build/src/main.rs | 15 ++++++++------- + syscare-build/src/patch/metadata.rs | 12 ++++++++++-- + syscare-build/src/patch/mod.rs | 2 -- + syscare-build/src/patch/patch_helper.rs | 2 +- + 4 files changed, 19 insertions(+), 12 deletions(-) + +diff --git a/syscare-build/src/main.rs b/syscare-build/src/main.rs +index 8df6e02..df38a17 100644 +--- a/syscare-build/src/main.rs ++++ b/syscare-build/src/main.rs +@@ -37,7 +37,7 @@ use package::{ + PackageBuildRoot, PackageBuilderFactory, PackageFormat, PackageImpl, PackageSpecBuilderFactory, + PackageSpecWriterFactory, + }; +-use patch::{PatchBuilderFactory, PatchHelper, PatchMetadata, PATCH_FILE_EXT}; ++use patch::{PatchBuilderFactory, PatchHelper, PatchMetadata}; + + const CLI_NAME: &str = "syscare build"; + const CLI_VERSION: &str = env!("CARGO_PKG_VERSION"); +@@ -292,12 +292,13 @@ impl SyscareBuild { + } + + // Override patch list +- let mut new_patch_files = PatchHelper::collect_patch_files(fs::list_files_by_ext( +- &patch_metadata.metadata_dir, +- PATCH_FILE_EXT, +- fs::TraverseOptions { recursive: false }, +- )?) +- .context("Failed to collect patch file from metadata directory")?; ++ let mut new_patch_files = PatchHelper::collect_patch_files( ++ saved_patch_info ++ .patches ++ .iter() ++ .map(|patch_file| &patch_file.path), ++ ) ++ .context("Failed to collect patch file from patch metadata")?; + + new_patch_files.extend(patch_files); + patch_files = new_patch_files; +diff --git a/syscare-build/src/patch/metadata.rs b/syscare-build/src/patch/metadata.rs +index 0911693..5eadf52 100644 +--- a/syscare-build/src/patch/metadata.rs ++++ b/syscare-build/src/patch/metadata.rs +@@ -90,8 +90,16 @@ impl PatchMetadata { + .decompress(&self.root_dir) + .context("Failed to decompress patch metadata")?; + +- serde::deserialize_with_magic::(&self.metadata_path, PATCH_INFO_MAGIC) +- .context("Failed to read patch metadata") ++ let mut patch_info: PatchInfo = ++ serde::deserialize_with_magic(&self.metadata_path, PATCH_INFO_MAGIC) ++ .context("Failed to read patch metadata")?; ++ ++ // rewrite file path to metadata directory path ++ for patch_file in &mut patch_info.patches { ++ patch_file.path = self.metadata_dir.join(&patch_file.name) ++ } ++ ++ Ok(patch_info) + } + + pub fn write>( +diff --git a/syscare-build/src/patch/mod.rs b/syscare-build/src/patch/mod.rs +index f590b1c..558bfea 100644 +--- a/syscare-build/src/patch/mod.rs ++++ b/syscare-build/src/patch/mod.rs +@@ -12,8 +12,6 @@ + * See the Mulan PSL v2 for more details. + */ + +-pub const PATCH_FILE_EXT: &str = "patch"; +- + mod kernel_patch; + mod metadata; + mod patch_builder; +diff --git a/syscare-build/src/patch/patch_helper.rs b/syscare-build/src/patch/patch_helper.rs +index fc8324a..9fc3ed1 100644 +--- a/syscare-build/src/patch/patch_helper.rs ++++ b/syscare-build/src/patch/patch_helper.rs +@@ -47,7 +47,7 @@ impl PatchHelper { + digest: file_digest, + }); + } +- patch_list.sort(); ++ + Ok(patch_list) + } + } +-- +2.43.0 + diff --git a/0145-project-fix-version-does-not-affect-on-rust-executab.patch b/0145-project-fix-version-does-not-affect-on-rust-executab.patch new file mode 100644 index 0000000000000000000000000000000000000000..fee949340634841657db6bda1593e0f2ae24aea3 --- /dev/null +++ b/0145-project-fix-version-does-not-affect-on-rust-executab.patch @@ -0,0 +1,34 @@ +From 59553198ac8592d2a018bda27d275cbbd61263a2 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 11 Jul 2025 17:39:15 +0800 +Subject: [PATCH] project: fix version does not affect on rust executable + +Signed-off-by: renoseven +--- + CMakeLists.txt | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e923258..3ad9eca 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -143,12 +143,14 @@ foreach(CURR_FLAG IN LISTS BUILD_FLAGS_RUST) + endforeach() + + add_custom_target(syscare ALL +- COMMAND cargo build --release --target-dir ${RUST_TARGET_DIR} ++ COMMAND ${CMAKE_COMMAND} -E env ++ "BUILD_VERSION=${BUILD_VERSION}" ++ "RUSTFLAGS=${RUST_FLAGS}" ++ cargo build --release --target-dir "${RUST_TARGET_DIR}" + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) + + set_target_properties(syscare PROPERTIES +- ENVIRONMENT "RUSTFLAGS=${RUST_FLAGS};BUILD_VERSION=${BUILD_VERSION}" + ADDITIONAL_CLEAN_FILES "${RUST_TARGET_DIR}" + ) + +-- +2.43.0 + diff --git a/0146-syscared-fix-active-deactive-process-list-calculatio.patch b/0146-syscared-fix-active-deactive-process-list-calculatio.patch new file mode 100644 index 0000000000000000000000000000000000000000..ebfb6d8e7649a14a3ff52d1b1185ab57fc3b3c32 --- /dev/null +++ b/0146-syscared-fix-active-deactive-process-list-calculatio.patch @@ -0,0 +1,197 @@ +From 081c4bf26d53c4cbfed2777c7fa1137d2fac4c24 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 25 Jul 2025 20:11:02 +0800 +Subject: [PATCH] syscared: fix active & deactive process list calculation + error + +Signed-off-by: renoseven +--- + syscared/src/patch/driver/upatch/mod.rs | 39 +++++++++------ + syscared/src/patch/driver/upatch/target.rs | 57 +++++++++++++++------- + 2 files changed, 64 insertions(+), 32 deletions(-) + +diff --git a/syscared/src/patch/driver/upatch/mod.rs b/syscared/src/patch/driver/upatch/mod.rs +index cc00f53..58f01df 100644 +--- a/syscared/src/patch/driver/upatch/mod.rs ++++ b/syscared/src/patch/driver/upatch/mod.rs +@@ -23,7 +23,7 @@ use std::{ + }; + + use anyhow::{bail, ensure, Result}; +-use log::{debug, warn}; ++use log::{debug, info, warn}; + use parking_lot::RwLock; + use uuid::Uuid; + +@@ -207,12 +207,11 @@ impl UserPatchDriver { + patch_target.clean_dead_process(&process_list); + + let all_patches = patch_target.all_patches().collect::>(); +- let need_actived = patch_target.need_actived(&process_list); +- + for (uuid, patch_file) in all_patches { ++ let need_actived = patch_target.need_actived_process(&process_list, &uuid); + if !need_actived.is_empty() { +- debug!( +- "Upatch: Activating patch '{}' ({}) for process {:?}", ++ info!( ++ "Upatch: Activating patch '{}' ({}) on new process {:?}", + uuid, + target_elf.display(), + need_actived, +@@ -220,10 +219,10 @@ impl UserPatchDriver { + } + for &pid in &need_actived { + match sys::active_patch(&uuid, pid, target_elf, &patch_file) { +- Ok(_) => patch_target.add_process(pid), ++ Ok(_) => patch_target.process_register_patch(pid, &uuid), + Err(e) => { + warn!( +- "Upatch: Failed to active patch '{}' for process {}, {}", ++ "Upatch: Failed to active patch '{}' on process {}, {}", + uuid, + pid, + e.to_string().to_lowercase(), +@@ -266,7 +265,13 @@ impl UserPatchDriver { + let start_watch = !patch_target.is_patched(); + + // Active patch +- let need_actived = patch_target.need_actived(&process_list); ++ let need_actived = patch_target.need_actived_process(&process_list, &patch.uuid); ++ info!( ++ "Upatch: Activating patch '{}' ({}) on process {:?}", ++ patch.uuid, ++ patch.target_elf.display(), ++ need_actived, ++ ); + + let mut results = Vec::new(); + for pid in need_actived { +@@ -290,10 +295,10 @@ impl UserPatchDriver { + // Process results + for (pid, result) in results { + match result { +- Ok(_) => patch_target.add_process(pid), ++ Ok(_) => patch_target.process_register_patch(pid, &patch.uuid), + Err(e) => { + warn!( +- "Upatch: Failed to active patch '{}' for process {}, {}", ++ "Upatch: Failed to active patch '{}' on process {}, {}", + patch.uuid, + pid, + e.to_string().to_lowercase(), +@@ -322,10 +327,16 @@ impl UserPatchDriver { + patch_target.clean_dead_process(&process_list); + + // Deactive patch +- let need_deactive = patch_target.need_deactived(&process_list); ++ let need_deactived = patch_target.need_deactived_process(&process_list, &patch.uuid); ++ info!( ++ "Upatch: Deactivating patch '{}' ({}) on process {:?}", ++ patch.uuid, ++ patch.target_elf.display(), ++ need_deactived, ++ ); + + let mut results = Vec::new(); +- for pid in need_deactive { ++ for pid in need_deactived { + let result = + sys::deactive_patch(&patch.uuid, pid, &patch.target_elf, &patch.patch_file); + results.push((pid, result)); +@@ -347,10 +358,10 @@ impl UserPatchDriver { + // Process results + for (pid, result) in results { + match result { +- Ok(_) => patch_target.remove_process(pid), ++ Ok(_) => patch_target.process_unregister_patch(pid, &patch.uuid), + Err(e) => { + warn!( +- "Upatch: Failed to deactive patch '{}' for process {}, {}", ++ "Upatch: Failed to deactive patch '{}' on process {}, {}", + patch.uuid, + pid, + e.to_string().to_lowercase(), +diff --git a/syscared/src/patch/driver/upatch/target.rs b/syscared/src/patch/driver/upatch/target.rs +index e0e9f88..a2a5f81 100644 +--- a/syscared/src/patch/driver/upatch/target.rs ++++ b/syscared/src/patch/driver/upatch/target.rs +@@ -24,36 +24,57 @@ use crate::patch::entity::UserPatch; + + #[derive(Debug, Default)] + pub struct PatchTarget { +- process_list: HashSet, +- patch_map: HashMap, // uuid -> patch file ++ process_map: HashMap>, // pid -> patch list ++ patch_map: HashMap, // uuid -> patch file + collision_map: HashMap>, // function old addr -> patch collision list + } + + impl PatchTarget { +- pub fn add_process(&mut self, pid: i32) { +- self.process_list.insert(pid); ++ pub fn process_register_patch(&mut self, pid: i32, uuid: &Uuid) { ++ self.process_map.entry(pid).or_default().insert(*uuid); + } + +- pub fn remove_process(&mut self, pid: i32) { +- self.process_list.remove(&pid); ++ pub fn process_unregister_patch(&mut self, pid: i32, uuid: &Uuid) { ++ if let Some(patch_list) = self.process_map.get_mut(&pid) { ++ patch_list.remove(uuid); ++ } + } + +- pub fn clean_dead_process(&mut self, process_list: &HashSet) { +- self.process_list.retain(|pid| process_list.contains(pid)); ++ pub fn need_actived_process(&self, process_list: &HashSet, uuid: &Uuid) -> HashSet { ++ let mut need_actived = HashSet::with_capacity(process_list.len()); ++ ++ for pid in process_list { ++ match self.process_map.get(pid) { ++ Some(patch_list) => { ++ if !patch_list.contains(uuid) { ++ need_actived.insert(*pid); ++ } ++ } ++ None => { ++ need_actived.insert(*pid); ++ } ++ } ++ } ++ ++ need_actived + } + +- pub fn need_actived(&self, process_list: &HashSet) -> HashSet { +- process_list +- .difference(&self.process_list) +- .copied() +- .collect() ++ pub fn need_deactived_process(&self, process_list: &HashSet, uuid: &Uuid) -> HashSet { ++ let mut need_deactived = HashSet::with_capacity(process_list.len()); ++ ++ for pid in process_list { ++ if let Some(patch_list) = self.process_map.get(pid) { ++ if patch_list.contains(uuid) { ++ need_deactived.insert(*pid); ++ } ++ } ++ } ++ ++ need_deactived + } + +- pub fn need_deactived(&self, process_list: &HashSet) -> HashSet { +- process_list +- .intersection(&self.process_list) +- .copied() +- .collect() ++ pub fn clean_dead_process(&mut self, process_list: &HashSet) { ++ self.process_map.retain(|pid, _| process_list.contains(pid)); + } + } + +-- +2.43.0 + diff --git a/0147-syscared-fix-new-process-patch-active-disorder-issue.patch b/0147-syscared-fix-new-process-patch-active-disorder-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..dc5229327f3a0756c59ca6a9a0fdbcc1faa23ca4 --- /dev/null +++ b/0147-syscared-fix-new-process-patch-active-disorder-issue.patch @@ -0,0 +1,36 @@ +From 9b11391de94603690f2d197a2993785748d32752 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 29 Jul 2025 15:55:33 +0800 +Subject: [PATCH] syscared: fix new process patch active disorder issue + +Signed-off-by: renoseven +--- + syscared/src/patch/driver/upatch/target.rs | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/syscared/src/patch/driver/upatch/target.rs b/syscared/src/patch/driver/upatch/target.rs +index a2a5f81..be5aabc 100644 +--- a/syscared/src/patch/driver/upatch/target.rs ++++ b/syscared/src/patch/driver/upatch/target.rs +@@ -17,15 +17,15 @@ use std::{ + path::PathBuf, + }; + +-use indexmap::IndexSet; ++use indexmap::{IndexMap, IndexSet}; + use uuid::Uuid; + + use crate::patch::entity::UserPatch; + + #[derive(Debug, Default)] + pub struct PatchTarget { +- process_map: HashMap>, // pid -> patch list +- patch_map: HashMap, // uuid -> patch file ++ process_map: HashMap>, // pid -> patch list ++ patch_map: IndexMap, // uuid -> patch file + collision_map: HashMap>, // function old addr -> patch collision list + } + +-- +2.43.0 + diff --git a/0148-syscare-return-error-if-external-command-is-an-inval.patch b/0148-syscare-return-error-if-external-command-is-an-inval.patch new file mode 100644 index 0000000000000000000000000000000000000000..bd59dd9e9f5d3c95af525c3ec8922c2723b4b38f --- /dev/null +++ b/0148-syscare-return-error-if-external-command-is-an-inval.patch @@ -0,0 +1,66 @@ +From 01582050c03ab434323554e190d99ff769b9e713 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 1 Aug 2025 13:18:53 +0800 +Subject: [PATCH] syscare: return error if external command is an invalid + executable + +Signed-off-by: renoseven +--- + syscare/src/main.rs | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/syscare/src/main.rs b/syscare/src/main.rs +index 38416f2..ff7a4ec 100644 +--- a/syscare/src/main.rs ++++ b/syscare/src/main.rs +@@ -12,9 +12,9 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{env, ffi::OsString, os::unix::process::CommandExt, process::Command}; ++use std::{env, ffi::OsString, process::Command}; + +-use anyhow::{bail, Context, Result}; ++use anyhow::{anyhow, Context, Result}; + use args::SubCommand; + use flexi_logger::{LogSpecification, Logger, WriteMode}; + use log::{debug, LevelFilter}; +@@ -41,22 +41,22 @@ const EXTERNAL_CMD_PREFIX: &str = "syscare-"; + fn exec_external_cmd(mut args: Vec) -> Result<()> { + let program = concat_os!(EXTERNAL_CMD_PREFIX, args.remove(0).trim()); + +- let error = Command::new(&program).args(&args).exec(); +- match error.kind() { +- std::io::ErrorKind::NotFound => { +- bail!( ++ let _ = Command::new(&program) ++ .args(&args) ++ .status() ++ .map_err(|e| match e.kind() { ++ std::io::ErrorKind::NotFound => anyhow!( + "External command '{}' is not installed", + program.to_string_lossy() +- ); +- } +- _ => { +- bail!( +- "Failed to execute '{}', {}", ++ ), ++ _ => anyhow!( ++ "Failed to execute '{}': {}", + program.to_string_lossy(), +- error +- ); +- } +- } ++ e.to_string().to_lowercase() ++ ), ++ })?; ++ ++ Ok(()) + } + + fn main() -> Result<()> { +-- +2.43.0 + diff --git a/0149-syscare-use-os-error-code-for-external-command-failu.patch b/0149-syscare-use-os-error-code-for-external-command-failu.patch new file mode 100644 index 0000000000000000000000000000000000000000..70e3432035ea9f617cb755ddd181b9b080d13665 --- /dev/null +++ b/0149-syscare-use-os-error-code-for-external-command-failu.patch @@ -0,0 +1,79 @@ +From a8a8b0b4f7d50cc55bae8ed3441f3ffae6ef4ccb Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Fri, 8 Aug 2025 15:59:23 +0800 +Subject: [PATCH] syscare: use os error code for external command failures + +Modify exec_external_cmd() to use the underlying OS error code when external +command execution fails, falling back to 1 if no specific error code is +available. + +The change preserves the single exit point while improving error handling +granularity by using std::io::Error::raw_os_error() for failed command executions. + +Signed-off-by: renoseven +--- + syscare/src/main.rs | 30 ++++++++++-------------------- + 1 file changed, 10 insertions(+), 20 deletions(-) + +diff --git a/syscare/src/main.rs b/syscare/src/main.rs +index ff7a4ec..7fde0ca 100644 +--- a/syscare/src/main.rs ++++ b/syscare/src/main.rs +@@ -12,9 +12,9 @@ + * See the Mulan PSL v2 for more details. + */ + +-use std::{env, ffi::OsString, process::Command}; ++use std::{env, ffi::OsString, process}; + +-use anyhow::{anyhow, Context, Result}; ++use anyhow::{Context, Result}; + use args::SubCommand; + use flexi_logger::{LogSpecification, Logger, WriteMode}; + use log::{debug, LevelFilter}; +@@ -38,25 +38,16 @@ const PATH_ENV_NAME: &str = "PATH"; + const PATH_ENV_VALUE: &str = "/usr/libexec/syscare"; + const EXTERNAL_CMD_PREFIX: &str = "syscare-"; + +-fn exec_external_cmd(mut args: Vec) -> Result<()> { ++fn exec_external_cmd(mut args: Vec) -> ! { + let program = concat_os!(EXTERNAL_CMD_PREFIX, args.remove(0).trim()); + +- let _ = Command::new(&program) +- .args(&args) +- .status() +- .map_err(|e| match e.kind() { +- std::io::ErrorKind::NotFound => anyhow!( +- "External command '{}' is not installed", +- program.to_string_lossy() +- ), +- _ => anyhow!( +- "Failed to execute '{}': {}", +- program.to_string_lossy(), +- e.to_string().to_lowercase() +- ), +- })?; ++ let exit_status = process::Command::new(&program).args(&args).status(); ++ let exit_code = match exit_status { ++ Ok(status) => status.code().unwrap_or(1), ++ Err(e) => e.raw_os_error().unwrap_or(1), ++ }; + +- Ok(()) ++ process::exit(exit_code); + } + + fn main() -> Result<()> { +@@ -85,8 +76,7 @@ fn main() -> Result<()> { + + debug!("Start with {:#?}", args); + if let SubCommand::External(cmd_args) = args.subcommand { +- self::exec_external_cmd(cmd_args)?; +- return Ok(()); ++ self::exec_external_cmd(cmd_args); + } + + debug!("Initializing rpc client..."); +-- +2.43.0 + diff --git a/0150-syscare-add-error-log-when-external-command-exec-fai.patch b/0150-syscare-add-error-log-when-external-command-exec-fai.patch new file mode 100644 index 0000000000000000000000000000000000000000..3b5893def4e3f65134998e653f7f5bdf06671934 --- /dev/null +++ b/0150-syscare-add-error-log-when-external-command-exec-fai.patch @@ -0,0 +1,48 @@ +From 39ed31559684760faa1e8fa8e54f42cd7649574d Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Mon, 11 Aug 2025 16:45:24 +0800 +Subject: [PATCH] syscare: add error log when external command exec failure + +Signed-off-by: renoseven +--- + syscare/src/main.rs | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/syscare/src/main.rs b/syscare/src/main.rs +index 7fde0ca..c636199 100644 +--- a/syscare/src/main.rs ++++ b/syscare/src/main.rs +@@ -17,7 +17,7 @@ use std::{env, ffi::OsString, process}; + use anyhow::{Context, Result}; + use args::SubCommand; + use flexi_logger::{LogSpecification, Logger, WriteMode}; +-use log::{debug, LevelFilter}; ++use log::{debug, error, LevelFilter}; + + use syscare_common::{concat_os, ffi::OsStrExt, os}; + +@@ -44,7 +44,20 @@ fn exec_external_cmd(mut args: Vec) -> ! { + let exit_status = process::Command::new(&program).args(&args).status(); + let exit_code = match exit_status { + Ok(status) => status.code().unwrap_or(1), +- Err(e) => e.raw_os_error().unwrap_or(1), ++ Err(e) => { ++ match e.kind() { ++ std::io::ErrorKind::NotFound => error!( ++ "Error: External command '{}' is not installed", ++ program.to_string_lossy() ++ ), ++ _ => error!( ++ "Error: Failed to execute '{}': {}", ++ program.to_string_lossy(), ++ e.to_string().to_lowercase() ++ ), ++ } ++ e.raw_os_error().unwrap_or(1) ++ } + }; + + process::exit(exit_code); +-- +2.43.0 + diff --git a/0151-upatch-helper-stop-compiler-replacing-atomic-ops-wit.patch b/0151-upatch-helper-stop-compiler-replacing-atomic-ops-wit.patch new file mode 100644 index 0000000000000000000000000000000000000000..22006290ac10aeeab3a743158d32eab61a486ea7 --- /dev/null +++ b/0151-upatch-helper-stop-compiler-replacing-atomic-ops-wit.patch @@ -0,0 +1,43 @@ +From 10156b26af7898034744652fe07ab465d9008f83 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Tue, 12 Aug 2025 17:18:26 +0800 +Subject: [PATCH] upatch-helper: stop compiler replacing atomic ops with + function calls on aarch64 + +Signed-off-by: renoseven +--- + upatch-helper/src/main.rs | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs +index 7c7b2b8..36eb3b0 100644 +--- a/upatch-helper/src/main.rs ++++ b/upatch-helper/src/main.rs +@@ -66,6 +66,16 @@ const COMPILE_OPTIONS_CLANG: &[&str] = &[ + + const UPATCH_ID_PREFIX: &str = ".upatch_"; + ++#[cfg(target_arch = "aarch64")] ++fn arch_specific_args() -> &'static [&'static str] { ++ &["-mno-outline-atomics"] ++} ++ ++#[cfg(not(any(target_arch = "aarch64")))] ++fn arch_specific_args() -> &'static [&'static str] { ++ &[] ++} ++ + #[inline(always)] + fn is_compilation(args: &[OsString]) -> bool { + /* check exclude flags */ +@@ -167,6 +177,7 @@ fn add_compile_options(command: &mut Command) { + }; + let assembler_arg = format!("-Wa,--defsym,{}{}=0", UPATCH_ID_PREFIX, Uuid::new_v4()); + ++ command.args(self::arch_specific_args()); + command.args(compiler_args); + command.arg(assembler_arg); + } +-- +2.43.0 + diff --git a/0152-upatch-manage-fix-stack-check-length-judge-error.patch b/0152-upatch-manage-fix-stack-check-length-judge-error.patch new file mode 100644 index 0000000000000000000000000000000000000000..c870b2ec34bc293047284b2f109e5bc6af4560f6 --- /dev/null +++ b/0152-upatch-manage-fix-stack-check-length-judge-error.patch @@ -0,0 +1,25 @@ +From 593472d09ad1b8927c27092d083c93934cb09ac4 Mon Sep 17 00:00:00 2001 +From: qiaojijun +Date: Wed, 13 Aug 2025 15:47:29 +0800 +Subject: [PATCH] upatch-manage: fix stack check length judge error + +--- + upatch-manage/upatch-stack-check.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upatch-manage/upatch-stack-check.c b/upatch-manage/upatch-stack-check.c +index 474d857..06de7d3 100644 +--- a/upatch-manage/upatch-stack-check.c ++++ b/upatch-manage/upatch-stack-check.c +@@ -29,7 +29,7 @@ static int stack_check(struct upatch_info *uinfo, unsigned long pc, upatch_actio + log_error("Unknown upatch action\n"); + return -1; + } +- if (pc >= start && pc <= end) { ++ if (pc >= start && pc < end) { + log_error("Failed to check stack, running function: %s\n", + uinfo->funcs[i].name); + return -1; +-- +2.43.0 + diff --git a/0157-common-fix-compile-failure-on-rust-1.89.patch b/0157-common-fix-compile-failure-on-rust-1.89.patch new file mode 100644 index 0000000000000000000000000000000000000000..45488e3879dff13e7426161dce1eca97ea6f484c --- /dev/null +++ b/0157-common-fix-compile-failure-on-rust-1.89.patch @@ -0,0 +1,26 @@ +From 9d2c23734a759228fa3770bf6b179f10e75175b5 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 30 Aug 2025 15:18:49 +0800 +Subject: [PATCH] common: fix compile failure on rust 1.89 + +Signed-off-by: renoseven +--- + syscare-common/src/ffi/os_str.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/syscare-common/src/ffi/os_str.rs b/syscare-common/src/ffi/os_str.rs +index 0c414e1..1e61fe9 100644 +--- a/syscare-common/src/ffi/os_str.rs ++++ b/syscare-common/src/ffi/os_str.rs +@@ -141,7 +141,7 @@ pub trait OsStrExt: AsRef { + }) + } + +- fn split_whitespace(&self) -> Filter, FilterFn> { ++ fn split_whitespace(&self) -> Filter, FilterFn> { + self.split(SplitFn::from(char::is_whitespace)) + .filter(|s| !s.is_empty()) + } +-- +2.43.0 + diff --git a/0158-upatch-build-fix-build-failure-on-rust-1.89.patch b/0158-upatch-build-fix-build-failure-on-rust-1.89.patch new file mode 100644 index 0000000000000000000000000000000000000000..4c613f19a492912f3fbb0cfade0aebdc263f7687 --- /dev/null +++ b/0158-upatch-build-fix-build-failure-on-rust-1.89.patch @@ -0,0 +1,86 @@ +From 61641f863e511180f99f929dab9e46522b7ab8d5 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 30 Aug 2025 15:17:54 +0800 +Subject: [PATCH] upatch-build: fix build failure on rust 1.89 + +Signed-off-by: renoseven +--- + upatch-build/src/dwarf.rs | 2 +- + upatch-build/src/elf/read/elfs.rs | 6 +++--- + upatch-build/src/elf/write/elfs.rs | 2 +- + upatch-build/src/file_relation.rs | 6 ------ + 4 files changed, 5 insertions(+), 11 deletions(-) + +diff --git a/upatch-build/src/dwarf.rs b/upatch-build/src/dwarf.rs +index fe0821c..a643556 100644 +--- a/upatch-build/src/dwarf.rs ++++ b/upatch-build/src/dwarf.rs +@@ -304,7 +304,7 @@ pub struct ProducerIterator { + } + + impl> ProducerIterator { +- fn current(&self) -> Result, DebuggingInformationEntry)>> { ++ fn current(&self) -> Result, DebuggingInformationEntry<'_, '_, R>)>> { + if let Some((unit, offsets)) = &self.state { + if let Some(offset) = offsets.last() { + return Ok(Some((unit, unit.entry(*offset)?))); +diff --git a/upatch-build/src/elf/read/elfs.rs b/upatch-build/src/elf/read/elfs.rs +index 52c5e6b..e6c65bb 100644 +--- a/upatch-build/src/elf/read/elfs.rs ++++ b/upatch-build/src/elf/read/elfs.rs +@@ -41,11 +41,11 @@ impl Elf { + Ok(Self { mmap, endian }) + } + +- pub fn header(&self) -> Result

{ ++ pub fn header(&self) -> Result> { + Ok(Header::from(&self.mmap, self.endian)) + } + +- pub fn sections(&self) -> Result { ++ pub fn sections(&self) -> Result> { + let header = self.header()?; + let offset = header.get_e_shoff() as usize; + let num = header.get_e_shnum() as usize; +@@ -59,7 +59,7 @@ impl Elf { + )) + } + +- pub fn symbols(&self) -> Result { ++ pub fn symbols(&self) -> Result> { + let sections = self.sections()?; + for section in sections.clone() { + if section.get_sh_type().eq(&SHT_SYMTAB) { +diff --git a/upatch-build/src/elf/write/elfs.rs b/upatch-build/src/elf/write/elfs.rs +index 9ce2847..c2ea121 100644 +--- a/upatch-build/src/elf/write/elfs.rs ++++ b/upatch-build/src/elf/write/elfs.rs +@@ -70,7 +70,7 @@ impl Elf { + Ok(res) + } + +- pub fn symbols(&mut self) -> Result { ++ pub fn symbols(&mut self) -> Result> { + let sections = &self.sections()?; + for section in sections { + if section.get_sh_type().eq(&SHT_SYMTAB) { +diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs +index 677cb8b..c7d941e 100644 +--- a/upatch-build/src/file_relation.rs ++++ b/upatch-build/src/file_relation.rs +@@ -32,12 +32,6 @@ const UPATCH_ID_PREFIX: &str = ".upatch_"; + + const NON_EXIST_PATH: &str = "/dev/null"; + +-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +-pub struct ObjectRelation { +- pub original_object: PathBuf, +- pub patched_object: PathBuf, +-} +- + /* + * The task of this class is to find out: + * 1. relationship between binary and debuginfo +-- +2.43.0 + diff --git a/0159-syscare-build-fix-compile-failure-on-rust-1.89.patch b/0159-syscare-build-fix-compile-failure-on-rust-1.89.patch new file mode 100644 index 0000000000000000000000000000000000000000..938ce77638339105f520195b4ac5ab955d6ed921 --- /dev/null +++ b/0159-syscare-build-fix-compile-failure-on-rust-1.89.patch @@ -0,0 +1,50 @@ +From 7298cf27dc5a368c51767fb49a9c15fc913c5890 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 30 Aug 2025 15:19:20 +0800 +Subject: [PATCH] syscare-build: fix compile failure on rust 1.89 + +Signed-off-by: renoseven +--- + syscare-build/src/package/rpm/tags/attr.rs | 24 ---------------------- + 1 file changed, 24 deletions(-) + +diff --git a/syscare-build/src/package/rpm/tags/attr.rs b/syscare-build/src/package/rpm/tags/attr.rs +index ef226d7..b836e02 100644 +--- a/syscare-build/src/package/rpm/tags/attr.rs ++++ b/syscare-build/src/package/rpm/tags/attr.rs +@@ -29,32 +29,8 @@ impl std::fmt::Display for RpmDefAttr { + } + } + +-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +-pub struct RpmAttr { +- pub mode: u32, +- pub user: String, +- pub group: String, +-} +- +-impl std::fmt::Display for RpmAttr { +- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +- f.write_fmt(format_args!( +- "%attr({:o},{},{})", +- self.mode, self.user, self.group +- )) +- } +-} +- + #[test] + fn test() { +- let attr = RpmAttr { +- mode: 0o755, +- user: String::from("root"), +- group: String::from("nobody"), +- }; +- println!("RpmAttr::new()\n{}\n", attr); +- assert_eq!(attr.to_string(), "%attr(755,root,nobody)"); +- + let def_attr = RpmDefAttr { + file_mode: 0o755, + user: String::from("root"), +-- +2.43.0 + diff --git a/0160-syscared-apply-cargo-fmt-suggestions.patch b/0160-syscared-apply-cargo-fmt-suggestions.patch new file mode 100644 index 0000000000000000000000000000000000000000..ff8e32f4390f31a087dcbc80d0aebaf2c0484d37 --- /dev/null +++ b/0160-syscared-apply-cargo-fmt-suggestions.patch @@ -0,0 +1,28 @@ +From c60d10dba663a8760394cb9cca17fd5a07f4d813 Mon Sep 17 00:00:00 2001 +From: renoseven +Date: Sat, 30 Aug 2025 15:19:37 +0800 +Subject: [PATCH] syscared: apply cargo fmt suggestions + +Signed-off-by: renoseven +--- + syscared/src/patch/driver/upatch/target.rs | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/syscared/src/patch/driver/upatch/target.rs b/syscared/src/patch/driver/upatch/target.rs +index be5aabc..ff35d2f 100644 +--- a/syscared/src/patch/driver/upatch/target.rs ++++ b/syscared/src/patch/driver/upatch/target.rs +@@ -24,8 +24,8 @@ use crate::patch::entity::UserPatch; + + #[derive(Debug, Default)] + pub struct PatchTarget { +- process_map: HashMap>, // pid -> patch list +- patch_map: IndexMap, // uuid -> patch file ++ process_map: HashMap>, // pid -> patch list ++ patch_map: IndexMap, // uuid -> patch file + collision_map: HashMap>, // function old addr -> patch collision list + } + +-- +2.43.0 + diff --git a/generate_package.sh b/generate_package.sh index a6f328e1a76b5d03db8c26db043ac3ea2c74909a..6fd8d2b2bda6edba8eea41ee6e00c5df90221cd9 100755 --- a/generate_package.sh +++ b/generate_package.sh @@ -1,47 +1,130 @@ -#!/bin/bash -e +#!/bin/bash +set -euo pipefail -readonly SPEC_FILE="$(find . -name '*.spec' | head -n 1)" +readonly REPO_URL="git@gitee.com:openeuler/syscare.git" +readonly REPO_NAME="$(basename "${REPO_URL}" .git)" -readonly REPO_NAME="$(basename ${SPEC_FILE} | sed 's/.spec//')" -readonly REPO_URL="https://gitee.com/openeuler/${REPO_NAME}" -readonly REPO_BRANCH="$(git branch --show-current | sed 's/-LTS.*//')" -readonly REPO_VERSION="$(grep Version ${SPEC_FILE} | head -n 1 | awk -F ' ' '{print $NF}')" +readonly CURR_DIR="$(dirname "$(realpath "$0")")" +readonly OUTPUT_DIR="${CURR_DIR}" -readonly PKG_NAME="${REPO_NAME}-${REPO_VERSION}" -readonly PKG_DIR="$(realpath ./${PKG_NAME})" +####################################### +# 1. Parse package info +####################################### +readonly RPM_SPEC="${CURR_DIR}/${REPO_NAME}.spec" +if [[ ! -f "${RPM_SPEC}" ]]; then + echo "Error: Cannot find rpm spec file '${RPM_SPEC}'" >&2 + exit 1 +fi -echo "Preparing..." -rm -rf ./${REPO_NAME} ./${PKG_NAME} -rm -f ./*.tar.gz +readonly RPM_NAME="$(awk '/^Name:[[:space:]]+/ {print $2; exit}' "${RPM_SPEC}" | sed 's/%{?*}//g')" +if [[ -z "${RPM_NAME}" ]]; then + echo "Error: Failed to parse rpm package name" >&2 + exit 1 +fi -echo "--------------------------" +readonly RPM_VERSION="$(awk '/^Version:[[:space:]]+/ {print $2; exit}' "${RPM_SPEC}" | sed 's/%{?*}//g')" +if [[ -z "${RPM_VERSION}" ]]; then + echo "Error: Failed to parse rpm package version" >&2 + exit 1 +fi + +####################################### +# 2. Parse target branch +####################################### +readonly TARGET_BRANCH=$( + branch="$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)" + if [[ "${branch}" != *-Next ]]; then + printf '%s' "${branch}" + exit 0 + fi + + prefix="${branch%-Next}" + candidate="$( + git branch --list "${prefix}*" --format='%(refname:short)' \ + | grep -vF -- "${branch}" \ + | sort -V \ + | tail -n1 + )" + + if [[ -n "${candidate}" ]]; then + printf '%s' "${candidate}" + else + printf '%s' "${branch}" + fi +) + +####################################### +# 3. Show detail info +####################################### +echo "---------------------------------" echo "Name: ${REPO_NAME}" -echo "Branch: ${REPO_BRANCH}" -echo "--------------------------" +echo "Branch: ${TARGET_BRANCH}" +echo "---------------------------------" + +####################################### +# 4. Clone source code +####################################### +readonly SOURCE_DIR_NAME="${RPM_NAME}-${RPM_VERSION}" +readonly SOURCE_DIR="${CURR_DIR}/${SOURCE_DIR_NAME}" echo "Cloning source code..." -git clone ${REPO_URL} -b ${REPO_BRANCH} ${PKG_NAME} +rm -rf "${SOURCE_DIR}" +git clone "${REPO_URL}" -b "${TARGET_BRANCH}" "${SOURCE_DIR_NAME}" +echo "" -echo "Vendoring dependencies..." -pushd ${PKG_DIR} > /dev/null +####################################### +# 5. Vendor dependencies +####################################### +readonly CARGO_DIR="${SOURCE_DIR}/.cargo" +readonly VENDOR_DIR="${SOURCE_DIR}/vendor" +readonly CONFIG_TOML="${CARGO_DIR}/config.toml" -cargo vendor --quiet --respect-source-config --sync Cargo.toml +echo "Vendoring dependencies..." +( + cd "${SOURCE_DIR}" + cargo vendor --respect-source-config --sync Cargo.toml +) -mkdir -p .cargo -cat << EOF > .cargo/config.toml +rm -rf "${CARGO_DIR}" +mkdir -p "${CARGO_DIR}" +cat << EOF > "${CONFIG_TOML}" [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" EOF +echo "" + +####################################### +# 6. Compress vendor package +####################################### +echo "Compressing vendor package..." +readonly VENDOR_PKG="${CURR_DIR}/vendor.tar.gz" + +rm -f "${VENDOR_PKG}" +( + cd "${SOURCE_DIR}" + tar -czvf "${VENDOR_PKG}" ".cargo" "vendor" +) + +rm -rf "${CARGO_DIR}" +rm -rf "${VENDOR_DIR}" +echo "" -popd > /dev/null +####################################### +# 7. Compress source package +####################################### +echo "Compressing source package..." +readonly SOURCE_PKG="${CURR_DIR}/${SOURCE_DIR_NAME}.tar.gz" -echo "Compressing package..." -tar -czf ./${PKG_NAME}.tar.gz ${PKG_NAME} +tar -czvf "${SOURCE_PKG}" "${SOURCE_DIR_NAME}" +echo "" +####################################### +# 8. Clean up +####################################### echo "Cleaning up..." -rm -rf ${PKG_DIR} +rm -rf "${SOURCE_DIR}" echo "Done" diff --git a/generate_patches.sh b/generate_patches.sh index 69e31b4c0024e91d216b0b4ae57c0707e537575a..edd68d537e0a7254468b1ddfee97ca7bbd5415a0 100755 --- a/generate_patches.sh +++ b/generate_patches.sh @@ -1,54 +1,136 @@ -#!/bin/bash -e +#!/bin/bash +set -euo pipefail -readonly SPEC_FILE="$(find . -name '*.spec' | head -n 1)" +readonly REPO_URL="git@gitee.com:openeuler/syscare.git" +readonly REPO_NAME="$(basename "${REPO_URL}" .git)" -readonly REPO_NAME="$(basename ${SPEC_FILE} | sed 's/.spec//')" -readonly REPO_URL="https://gitee.com/openeuler/${REPO_NAME}" -readonly REPO_BRANCH="$(git branch --show-current | sed 's/-LTS.*//')" -readonly REPO_VERSION="$(grep Version ${SPEC_FILE} | head -n 1 | awk -F ' ' '{print $NF}')" +readonly CURR_DIR="$(dirname "$(realpath "$0")")" +readonly OUTPUT_DIR="${CURR_DIR}" -readonly PKG_NAME="${REPO_NAME}-${REPO_VERSION}" -readonly PKG_DIR="$(realpath ./${PKG_NAME})" +####################################### +# 1. Parse package info +####################################### +readonly RPM_SPEC="${CURR_DIR}/${REPO_NAME}.spec" +if [[ ! -f "${RPM_SPEC}" ]]; then + echo "Error: Cannot find rpm spec file '${RPM_SPEC}'" >&2 + exit 1 +fi -readonly PATCH_DIR="$(pwd)" +readonly RPM_NAME="$(awk '/^Name:[[:space:]]+/ {print $2; exit}' "${RPM_SPEC}" | sed 's/%{?*}//g')" +if [[ -z "${RPM_NAME}" ]]; then + echo "Error: Failed to parse rpm package name" >&2 + exit 1 +fi -echo "Preparing..." -rm -rf ${PKG_DIR} -rm -f ./*.patch +readonly RPM_VERSION="$(awk '/^Version:[[:space:]]+/ {print $2; exit}' "${RPM_SPEC}" | sed 's/%{?*}//g')" +if [[ -z "${RPM_VERSION}" ]]; then + echo "Error: Failed to parse rpm package version" >&2 + exit 1 +fi -tar -xf ./${PKG_NAME}.tar.gz +####################################### +# 2. Decompress source package +####################################### +readonly SOURCE_PKG="${CURR_DIR}/${RPM_NAME}-${RPM_VERSION}.tar.gz" +if [[ ! -f "${SOURCE_PKG}" ]]; then + echo "Error: Cannot find source package '${SOURCE_PKG}'" >&2 + exit 1 +fi -pushd ${PKG_DIR} > /dev/null -readonly REPO_BASELINE="$(git rev-parse --short HEAD)" -popd > /dev/null +readonly SOURCE_DIR="${CURR_DIR}/${RPM_NAME}-${RPM_VERSION}" +rm -rf "${SOURCE_DIR}" 2>/dev/null -echo "------------------------------" -echo "Name: ${REPO_NAME}" -echo "Branch: ${REPO_BRANCH}" -echo "Baseline: ${REPO_BASELINE}" -echo "------------------------------" +tar -xzf "${SOURCE_PKG}" || { + echo "Error: Failed to decompress source package" >&2 + exit 1 +} +if [[ ! -d "${SOURCE_DIR}" ]]; then + echo "Error: Cannot find source directory '${SOURCE_DIR}'" >&2 + exit 1 +fi +####################################### +# 3. Parse source package baseline +####################################### +readonly SOURCE_BASELINE="$( + cd "${SOURCE_DIR}" + git rev-parse --short HEAD 2>/dev/null +)" +if [[ -z "${SOURCE_BASELINE}" ]]; then + echo "Error: Failed to parse source package baseline" >&2 + exit 1 +fi + +####################################### +# 4. Parse target branch +####################################### +readonly TARGET_BRANCH=$( + branch="$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)" + if [[ "${branch}" != *-Next ]]; then + printf '%s' "${branch}" + exit 0 + fi + + prefix="${branch%-Next}" + candidate="$( + git branch --list "${prefix}*" --format='%(refname:short)' \ + | grep -vF -- "${branch}" \ + | sort -V \ + | tail -n1 + )" + + if [[ -n "${candidate}" ]]; then + printf '%s' "${candidate}" + else + printf '%s' "${branch}" + fi +) + +####################################### +# 5. Show detail info +####################################### +echo "---------------------------------" +echo "Name: ${RPM_NAME}-${RPM_VERSION}" +echo "Branch: ${TARGET_BRANCH}" +echo "Baseline: ${SOURCE_BASELINE}" +echo "---------------------------------" +echo "" + +####################################### +# 6. Sync repository +####################################### echo "Syncing with remote..." -pushd ${PKG_DIR} > /dev/null -git fetch origin -popd > /dev/null +( + cd "${SOURCE_DIR}" + git remote remove origin 2>/dev/null || true + git remote add origin "${REPO_URL}" + git fetch origin +) +echo "" +####################################### +# 7. Format patches +####################################### echo "Generating patches..." -# format patches -pushd ${PKG_DIR} > /dev/null -git checkout -q origin/${REPO_BRANCH} -git format-patch -qN -o ${PATCH_DIR} ${REPO_BASELINE} -popd > /dev/null - -# print patch list -patch_list="$(find . -maxdepth 1 -name "*.patch" | sort)" -for patch_file in ${patch_list}; do - patch_name="$(basename ${patch_file})" - patch_id="$(echo ${patch_name} | awk -F '-' '{print $1}')" - echo "Patch${patch_id}: ${patch_name}" -done +( + cd "${SOURCE_DIR}" + + git checkout -q --track "origin/${TARGET_BRANCH}" + git format-patch -qN -o "${OUTPUT_DIR}" "${SOURCE_BASELINE}" + + readarray -d '' patch_files < <(find "${OUTPUT_DIR}" -maxdepth 1 -name "*.patch" -print0 | sort -z) + for patch_file in "${patch_files[@]}"; do + patch_name="${patch_file##*/}" + patch_id="${patch_name%%-*}" + printf 'Patch%s: %s\n' "${patch_id}" "${patch_name}" + done +) +echo "" +####################################### +# 8. Clean up +####################################### echo "Cleaning up..." -rm -rf ${PKG_DIR} +rm -rf "${SOURCE_DIR}" echo "Done" diff --git a/syscare-1.2.2.tar.gz b/syscare-1.2.2.tar.gz index f4f3a7ffe8024546da4fb9d362ac3961601b0462..31f3e5d299421ea84d958b49d647e610176deb9f 100644 Binary files a/syscare-1.2.2.tar.gz and b/syscare-1.2.2.tar.gz differ diff --git a/syscare.spec b/syscare.spec index 3c5af8e948fc4a7b490514a7c017bcbe513c248d..0cb73e447b3f99cae7b7662ae0368f4c6c179f49 100644 --- a/syscare.spec +++ b/syscare.spec @@ -5,11 +5,12 @@ ############################################ Name: syscare Version: 1.2.2 -Release: 5 +Release: 7 Summary: System hot-fix service License: MulanPSL-2.0 and GPL-2.0-only URL: https://gitee.com/openeuler/syscare Source0: %{name}-%{version}.tar.gz +Source1: vendor.tar.gz BuildRequires: cmake >= 3.14 make BuildRequires: rust >= 1.51 cargo >= 1.51 @@ -56,16 +57,129 @@ Patch0034: 0034-syscared-impl-syscare-rescan-command.patch Patch0035: 0035-syscare-impl-syscare-rescan-command.patch Patch0036: 0036-syscared-impl-log-config.patch Patch0037: 0037-syscared-impl-patch-blacklist.patch -Patch0038: 0038-metadata-viewer-add-component.patch -Patch0039: 0039-metadata-generator-add-component.patch -Patch0040: 0040-project-update-Cargo.lock.patch -Patch0041: 0041-syscared-fix-patch-target-is-blocked-issue.patch -Patch0042: 0042-riscv64-sync-riscv64-support-from-master-to-2503.patch -Patch0043: 0043-syscare-build-fix-detect-incorrect-kernel-source-dir.patch -Patch0044: 0044-upatch-manage-fix-compile-failure.patch -Patch0045: 0045-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch -Patch0046: 0046-common-fix-failed-to-acquire-flock-issue.patch -Patch0047: 0047-syscare-rewrite-syscare-cli.patch +Patch0038: 0038-syscared-fix-patch-target-is-blocked-issue.patch +Patch0039: 0039-syscare-build-fix-detect-incorrect-kernel-source-dir.patch +Patch0040: 0040-upatch-manage-fix-compile-failure.patch +Patch0041: 0041-syscared-fix-kmod-patch-conflicts-with-itself-issue.patch +Patch0042: 0042-common-fix-failed-to-acquire-flock-issue.patch +Patch0043: 0043-syscare-rewrite-syscare-cli.patch +Patch0044: 0044-upatch-diff-fix-static-local-var-correlate.patch +Patch0045: 0045-upatch-manage-fix-first-object-cannot-find-mem-hole.patch +Patch0046: 0046-syscare-fix-request-is-unrecognized-issue.patch +Patch0047: 0047-upatch-manage-fix-alloc-patch-memory-permission-deni.patch +Patch0048: 0048-upatch-diff-remove-line-macro-related-functions.patch +Patch0049: 0049-upatch-build-support-override-line-macros.patch +Patch0050: 0050-syscare-build-add-keep-line-macros-argument.patch +Patch0051: 0051-common-fs-glob-wildcard-path-includes-current-direct.patch +Patch0052: 0052-upatch-build-only-replace-line-macros-for-source-fil.patch +Patch0053: 0053-upatch-manage-container_of-use-void-instead-of-char.patch +Patch0054: 0054-CMake-move-EXECUTE_PROCESS-from-upatch-manage-to-top.patch +Patch0055: 0055-upatch-diff-impl-section-type-checking.patch +Patch0056: 0056-upatch-diff-allow-string-literal-section-being-inclu.patch +Patch0057: 0057-upatch-diff-fix-code-logic-when-drop-rela.patch +Patch0058: 0058-upatch-diff-fix-cannot-detect-some-changes-issue.patch +Patch0059: 0059-upatch-diff-fix-.text-section-change-does-not-includ.patch +Patch0060: 0060-upatch-diff-fix-.data.rel-section-is-changed-issue.patch +Patch0061: 0061-upatch-diff-rewrite-patch-verification-logic.patch +Patch0062: 0062-upatch-diff-rewrite-symbol-include-logic.patch +Patch0063: 0063-upatch-diff-allow-.rodata-section-sh_addralign-chang.patch +Patch0064: 0064-upatch-diff-allow-.debug_-section-header-changes.patch +Patch0065: 0065-upatch-diff-allow-tls-variable-changes.patch +Patch0066: 0066-upatch-diff-allow-delete-local-static-variables.patch +Patch0067: 0067-upatch-diff-fix-lookup_relf-returns-wrong-sympos-iss.patch +Patch0068: 0068-upatch-diff-change-include-ignored-grouped-data-type.patch +Patch0069: 0069-upatch-diff-compare-symbol-by-checking-it-s-section-.patch +Patch0070: 0070-upatch-diff-ignore-debug-sections.patch +Patch0071: 0071-upatch-diff-allow-sh_flags-sh_addralign-changes.patch +Patch0072: 0072-upatch-diff-fix-null-symbol-lookup-error.patch +Patch0073: 0073-upatch-diff-use-kpatch-way-to-deal-with-static-local.patch +Patch0074: 0074-upatch-diff-remove-intermediate-sections.patch +Patch0075: 0075-upatch-diff-rewrite-local-symbol-matching-logic.patch +Patch0076: 0076-upatch-diff-skip-local-symbols-like-__func__.xxx.patch +Patch0077: 0077-upatch-diff-warning-on-no-function-changed-when-crea.patch +Patch0078: 0078-upatch-diff-treat-.L-symbol-as-special-symbol.patch +Patch0079: 0079-upatch-diff-allow-object-symbol-size-changes.patch +Patch0080: 0080-upatch-diff-optimize-log.patch +Patch0081: 0081-upatch-diff-enrich-error-message.patch +Patch0082: 0082-upatch-diff-improve-initializing-object-handling.patch +Patch0083: 0083-upatch-diff-fix-uelf-list-insert-order-issue.patch +Patch0084: 0084-upatch-diff-reorganize-relf-struct.patch +Patch0085: 0085-upatch-diff-fix-cannot-find-local-symbol-in-relf-iss.patch +Patch0086: 0086-upatch-diff-fix-coredump-in-checking-symbol.patch +Patch0087: 0087-upatch-diff-fix-compare-common-section-symbol-failur.patch +Patch0088: 0088-upatch-diff-optimize-debug-output.patch +Patch0089: 0089-upatch-diff-support-text-section-offset.patch +Patch0090: 0090-upatch-diff-remove-match_local_symbols.patch +Patch0091: 0091-upatch-diff-rewrite-uelf-relf-struct.patch +Patch0092: 0092-upatch-diff-fix-access-freed-section-data-issue.patch +Patch0093: 0093-syscared-fix-kernel-compatibility-check-is-not-worki.patch +Patch0094: 0094-syscare-build-add-debug-option-to-kpatch-build.patch +Patch0095: 0095-Fix-invalid-url-for-syscare-build-readme.patch +Patch0096: 0096-doc-sync-master-document.patch +Patch0097: 0097-common-rewrite-fs-glob.patch +Patch0098: 0098-common-rewrite-fs-components.patch +Patch0099: 0099-syscare-adapt-common-fs-changes.patch +Patch0100: 0100-syscared-adapt-common-fs-changes.patch +Patch0101: 0101-upatch-helper-optimize-compiler-flags.patch +Patch0102: 0102-upatch-helper-support-clang-compiler.patch +Patch0103: 0103-upatch-helper-support-cross-compilation.patch +Patch0104: 0104-upatch-helper-add-Werror-uninitialized-for-clang.patch +Patch0105: 0105-upatch-build-execute-clean-before-first-time-buildin.patch +Patch0106: 0106-upatch-build-rewrite-compiler-detection.patch +Patch0107: 0107-upatch-build-adapt-fs-glob-changes.patch +Patch0108: 0108-upatch-build-binary-dir-supports-wildcard-path.patch +Patch0109: 0109-upatch-build-adapt-common-fs-changes.patch +Patch0110: 0110-upatch-build-format-code.patch +Patch0111: 0111-upatch-build-fix-upatch-id-parsing-failure.patch +Patch0112: 0112-upatch-build-rename-patch-note-object.patch +Patch0113: 0113-upatch-build-rewrite-object-collecting.patch +Patch0114: 0114-upatch-build-fix-unable-to-find-object-linker-error.patch +Patch0115: 0115-upatch-build-warning-on-finding-patched-object-failu.patch +Patch0116: 0116-upatch-build-try-hardlink-before-copy-files.patch +Patch0117: 0117-upatch-build-optimize-code.patch +Patch0118: 0118-upatch-build-support-text-section-offset.patch +Patch0119: 0119-syscare-build-fix-clippy-warnings.patch +Patch0120: 0120-project-update-Cargo.lock.patch +Patch0121: 0121-project-change-compile-options.patch +Patch0122: 0122-syscare-build-fix-build-kernel-module-patch-failure.patch +Patch0123: 0123-syscare-build-kmod-patch-entity-name-uses-shorter-uu.patch +Patch0124: 0124-syscare-build-optimize-kpatch-build-arguments.patch +Patch0125: 0125-upatch-diff-sync-from-25.03-to-24.03-LTS.patch +Patch0126: 0126-upatch-manage-sync-from-25.03-to-24.03-LTS.patch +Patch0127: 0127-CMake-fix-Werror-cast-align-for-riscv64.patch +Patch0128: 0128-upatch-build-fix-compile-failure-for-lower-rust-vers.patch +Patch0129: 0129-metadata-viewer-add-component.patch +Patch0130: 0130-metadata-generator-add-component.patch +Patch0131: 0131-project-update-Cargo.lock.patch +Patch0132: 0132-upatch-build-rename-keep-line-macros-to-override-lin.patch +Patch0133: 0133-syscare-build-rename-keep-line-macros-to-override-li.patch +Patch0134: 0134-common-update-component.patch +Patch0135: 0135-syscared-adapt-common-crate-change.patch +Patch0136: 0136-syscared-rewrite-patch-parsing-management.patch +Patch0137: 0137-project-update-Cargo.lock.patch +Patch0138: 0138-project-update-CMakeLists.txt.patch +Patch0139: 0139-upatch-diff-fix-special-section-cannot-be-correlated.patch +Patch0140: 0140-upatch-helper-fix-compilation-command-detection.patch +Patch0141: 0141-upatch-build-use-last-write-wins-strategy-to-solving.patch +Patch0142: 0142-syscare-build-check-source-package-version-release-m.patch +Patch0143: 0143-syscare-build-match-source-package-to-debuginfo-pack.patch +Patch0144: 0144-syscare-build-fix-patch-file-list-disorder-when-buil.patch +Patch0145: 0145-project-fix-version-does-not-affect-on-rust-executab.patch +Patch0146: 0146-syscared-fix-active-deactive-process-list-calculatio.patch +Patch0147: 0147-syscared-fix-new-process-patch-active-disorder-issue.patch +Patch0148: 0148-syscare-return-error-if-external-command-is-an-inval.patch +Patch0149: 0149-syscare-use-os-error-code-for-external-command-failu.patch +Patch0150: 0150-syscare-add-error-log-when-external-command-exec-fai.patch +Patch0151: 0151-upatch-helper-stop-compiler-replacing-atomic-ops-wit.patch +Patch0152: 0152-upatch-manage-fix-stack-check-length-judge-error.patch +# Patch0153: 0153-doc-add-syscare-user-guide.patch +# Patch0154: 0154-doc-remove-non-existing-packages-in-Installation-sec.patch +# Patch0155: 0155-doc-fix-version-error-in-Installation-section-and-Us.patch +# Patch0156: 0156-doc-add-a-blank-line-after-headings-in-Introduction-.patch +Patch0157: 0157-common-fix-compile-failure-on-rust-1.89.patch +Patch0158: 0158-upatch-build-fix-build-failure-on-rust-1.89.patch +Patch0159: 0159-syscare-build-fix-compile-failure-on-rust-1.89.patch +Patch0160: 0160-syscared-apply-cargo-fmt-suggestions.patch ############### Description ################ %description @@ -74,7 +188,7 @@ The host can fix the system problem without rebooting. ############## BuildPreparare ############## %prep -%autosetup -p1 +%autosetup -p1 -a1 ################## Build ################### %build @@ -166,14 +280,36 @@ Syscare patch building toolset. %attr(555,root,root) /usr/libexec/syscare/upatch-build %attr(555,root,root) /usr/libexec/syscare/upatch-diff %attr(555,root,root) /usr/libexec/syscare/upatch-helper -%attr(555,root,root) /usr/libexec/syscare/upatch-cc -%attr(555,root,root) /usr/libexec/syscare/upatch-c++ %attr(555,root,root) /usr/libexec/syscare/metadata-generator +/usr/libexec/syscare/upatch-cc +/usr/libexec/syscare/upatch-c++ ############################################ ################ Change log ################ ############################################ %changelog +* Sat Aug 30 2025 renoseven - 1.2.2-7 +- change: add debuginfo package version verification in syscare-build +- change: rename "--keep-line-macros" to "--override-line-macros" in syscare-build +- change: rename "--keep-line-macros" to "--override-line-macros" in upatch-build +- bugfix: fix special section correlation failure +- bugfix: fix compilation command misdetection +- bugfix: fix incorrect upatch object lookup when upatch ID conflicts +- bugfix: fix patch file disorder when building cumulative patches +- bugfix: fix abnormal syscare CLI return value when external command failure +- bugfix: fix stack check false warnings under boundary conditions +- bugfix: fix build failure under Rust 1.89 + +* Tue Jun 03 2025 renoseven - 1.2.2-6 +- feature: upatch supports overridding line macros +- bugfix: fix upatch id parsing error +- bugfix: fix upatch object linker detection failure +- bugfix: fix kernel module build failure +- bugfix: fix kernel patch name length limitation +- bugfix: fix multiple upatch-diff issues +- bugfix: fix multiple upatch-manage issues +- rpm: extract vendor from syscare-1.2.2.tar.gz to vendor.tar.gz + * Thu Mar 13 2025 renoseven - 1.2.2-5 - feature: support riscv64 - feature: support 'syscare rescan' command diff --git a/vendor.tar.gz b/vendor.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a3d01bb8522de70ca3f0353dc514afff09b1b240 Binary files /dev/null and b/vendor.tar.gz differ