Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Arm64 Support #1236

Closed
wants to merge 4 commits into from
Closed

Add Arm64 Support #1236

wants to merge 4 commits into from

Conversation

surajjs95
Copy link

@surajjs95 surajjs95 commented Nov 3, 2021

Implement support for building livepatches for arm64

Kernel Support:

With the above kernel tree and the changes in this pull request I have been able to build and apply patches using kpatch.

@@ -214,6 +216,22 @@ static struct rela *toc_rela(const struct rela *rela)
(unsigned int)rela->addend);
}

#ifdef __aarch64__
static int kpatch_is_mapping_symbol(struct symbol *sym)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a comment to describe what a mapping symbol is and why we wouldn't correlate it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, probably best to make it return a bool so the return code semantics are clear.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, noted

rela->type == R_X86_64_32S &&
#elif defined(__aarch64__)
rela->type == R_AARCH64_ABS64 &&
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to '#ifdef' this, the defines exist in /usr/include/elf.h regardless of arch.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand this comment. The ifdef isn't trying to get around it being defined or not, it's trying to change the if condition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess what @jpoimboe is saying is that it can be written down as (rela->type == R_X86_64_32S || rela->type == R_AARCH64_ABS64) which looks nice. We also might want to get rid of all ifdefs at some point (see #1179) so unless it is necessary we are reluctant to add new ones.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, that makes sense

log_normal("function %s has no fentry/mcount call, unable to patch\n",
sym->name);
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another unneeded '#ifdef', instead the existing message can be made arch-independent, e.g. "function %s doesn't have patchable function entry".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted

rela->sym->sec) {
found = true;
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? The check to ensure the symbol is patchable should have already been done already in kpatch_check_func_profiling_calls(). So I could be missing something, but I don't see the need to preserve patchable_sec or to do this check here.

I do notice that rela->addend is needed below for the insn_offset calculation, but if the offset isn't always at the same location, can the function entry point be discovered by just looking for it at the beginning of the function? If so, I'd much rather do that because it seems less complex than the hacks needed to preserve the contents of the table.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent point re kpatch_check_func_profiling_calls().

The offset isn't always the same because there can be an additional branch target identification instruction added to function entry by gcc which precedes the patchable function entry. However this can be identified and accounted for.

@@ -3421,7 +3486,11 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
nr++;

/* create text/rela section pair */
#ifdef __aarch64__
sec = create_section_pair(kelf, "__patchable_function_entries", sizeof(void*), nr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When trying to kpatch-build a change to meminfo_proc_show(), I got the following error:

make[1]: Entering directory '/root/linux'
  LDS     /root/.kpatch/tmp/patch/kpatch.lds
  CC [M]  /root/.kpatch/tmp/patch/patch-hook.o
  LD [M]  /root/.kpatch/tmp/patch/livepatch-a.o
ld: __patchable_function_entries has both ordered [`__patchable_function_entries' in /root/.kpatch/tmp/patch/patch-hook.o] and unordered [`__patchable_function_entries' in /root/.kpatch/tmp/patch/output.o] sections
ld: final link failed: bad value
make[2]: *** [scripts/Makefile.build:431: /root/.kpatch/tmp/patch/livepatch-a.o] Error 1

The problem is that the compiler-created version of the section has the SHF_LINK_ORDER flag set. I "fixed" it with the following hack. sh_link just points to a random section but maybe that's ok, AFAICT its value doesn't matter for the kernel's use of __patchable_function_entries.

diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
index 11a5f94..bd34764 100644
--- a/kpatch-build/create-diff-object.c
+++ b/kpatch-build/create-diff-object.c
@@ -3488,6 +3488,8 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf,
        /* create text/rela section pair */
 #ifdef __aarch64__
        sec = create_section_pair(kelf, "__patchable_function_entries", sizeof(void*), nr);
+       sec->sh.sh_flags |= SHF_LINK_ORDER;
+       sec->sh.sh_link = 1;
 #else /* !__aarch64__ */
        sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr);
 #endif

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't affect how the kernel uses this section and so will be fine. I didn't see the same error so I assume that's down to the linker being used.

Copy link
Author

@surajjs95 surajjs95 Nov 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so I have the opposite problem in that patch-hook.o is unordered and output.o becomes ordered with the change you suggested and the linking fails.

Which version of gcc are you using?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this on both rhel9 and fedora34 (gcc version 11.2.1 20210728 (Red Hat 11.2.1-1) (GCC)). We might want to copy sh_flags/sh_link from __patchable_function_entries of patched objectfile.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that sounds like the best option

}
}
}
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a major hack and I'm not really convinced it's needed. I have a more detailed comment below.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

patchable_sec = patchable_sec->rela;
list_del(&patchable_sec->list);
}

Copy link
Member

@jpoimboe jpoimboe Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As described above, I don't think this is needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

*/
if (!sec || !sec->rela)
return;
patchable_sec = sec->rela;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patchable_sec variable is confusingly named, since it's really the relocation section for the patchable sec. I don't think you need a separate variable for it anyway, it can just continue to be referred to by sec->rela.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an optimisation to avoid searching for the "__patchable_function_entries" section each time. Although I'm sure it makes almost zero difference to the run time of the script.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see what you mean. Maybe do the find_section_by_name() before the loop, and give the rela sec a more descriptive name like patchable_rela_sec.

patchable_sec = sec->rela;
}
list_for_each_entry(rela, &patchable_sec->relas,
list) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the line break here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent point :D

[[ "$CONFIG_PRINTK_INDEX" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_i=1"

SPECIAL_VARS="$(readelf -wi "$VMLINUX" |
gawk --non-decimal-data $AWK_OPTIONS '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line breaks shellcheck, which wants everything to be doublequoted, but in this case doublequoting is skipped intentionally so we need to tell shellcheck about this:

Suggested change
gawk --non-decimal-data $AWK_OPTIONS '
# shellcheck disable=SC2086
gawk --non-decimal-data $AWK_OPTIONS '

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted, thanks.

@@ -1536,7 +1564,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
continue;
}

#ifdef __powerpc64__
#if defined(__powerpc64__) || defined(__aarch64__)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, why add_off is 0 in aarch64?
I see different handling in x86 below.
:)

Suraj Jitindar Singh added 4 commits December 15, 2021 17:10
The "has_function_profiling" support field in the symbol struct is used to show
that a function symbol is able to be patched. This is necessary to check that
functions which need to be patched are able to be.

On arm64 this means the presence of 2 NOP instructions at function entry
which are patched by ftrace to call the ftrace handling code. These 2 NOPs
are inserted by the compiler and the location of them is recorded in a
section called "__patchable_function_entries". Check whether a symbol has a
corresponding entry in the "__patchable_function_entries" section and if so
mark it as "has_func_profiling".

Signed-off-by: Suraj Jitindar Singh <[email protected]>

---

V1->V2:
 - Make error message standard across architectures when no patchable entry
 - Don't store __patchable_function_entries section in
   kpatch_find_func_profiling_calls(), instead find it each time
…d populate

The function kpatch_create_mcount_sections() allocates the __mcount_loc section
and then populates it with functions which have a patchable entry.

The following patch will add aarch64 support to this function where the
allocation will have to be done before the kelf_patched is torn down.
Thus split this function so that the allocation can be performed earlier and
the populating as before.

No intended functional change.

Signed-off-by: Suraj Jitindar Singh <[email protected]>

---

V1->V2:
 - Add patch to series
…arch64

The __mcount_loc section contains the addresses of patchable ftrace sites which
is used by the ftrace infrastructure in the kernel to create a list of tracable
functions and to know where to patch to enable tracing of them. On aarch64 this
section is called __patchable_function_entries and is generated by the
compiler. Either of __mcount_loc or __patchable_function_entries is recognised
by the kernel but for aarch64 use __patchable_function_entries as it is what
is expected.

Add aarch64 support to kpatch_alloc_mcount_sections(). The SHF_LINK_ORDER
section flag must be copied to ensure that it matches to avoid the following:

ld: __patchable_function_entries has both ordered [...] and unordered [...] sections

Add aarch64 support to kpatch_populate_mcount_sections(). Check for the 2
required NOP instructions on function entry, which may be preceded by a BTI C
instruction depending on whether the function is a leaf function. This
determines the offset of the patch site.

Signed-off-by: Suraj Jitindar Singh <[email protected]>

---

V1->V2:
 - Don't preserve the __patchable_function_entries section from the patched elf
   as this is already verified by kpatch_check_func_profiling_calls()
 - Instead get the patch entry offset by checking for a preceding BTI C instr
 - Copy the section flags for __patchable_function_entries
Add the final support required for aarch64 and enable building on that arch.

Signed-off-by: Suraj Jitindar Singh <[email protected]>

---

V1->V2:
 - Add # shellcheck disable=SC2086
 - Add comment to kpatch_is_mapping_symbol()
@surajjs95
Copy link
Author

Updated with V2 addressing review comments

@joe-lawrence
Copy link
Contributor

Hi @surajjs95 , @keiya-nobuta : some of my recent merges to the master branch introduced a bunch of merge conflicts for this patchset, mainly those where I removed #ifdef __arch__ checks in create-diff-object. Please see my rebase at https://github.com/joe-lawrence/kpatch/tree/arm64-rebase-aaaebaf25895. It builds, but I have not tested it. Can you guys test and if it looks good, incorporate those updates this PR and we can resume reviewing. Thanks.

@joe-lawrence
Copy link
Contributor

When building a small patch test1.tar.gz, I notice a modpost warning:

WARNING: modpost: /root/.kpatch/tmp/patch/livepatch-test.o (__patchable_function_entries): unexpected non-allocatable section.
Did you forget to use "ax"/"aw" in a .S file?
Note that for example <linux/init.h> contains
section definitions for use in .S files.

And see that there are multiple sections in the livepatch.ko file with that name (the second one missing the flags modpost complains about):

[Nr]  Name                          Type      Address           Off     Size    ES  Flg  Lk  Inf  Al
[32]  __patchable_function_entries  PROGBITS  0000000000000000  001a40  000028  00  WAL  4   0    8
[60]  __patchable_function_entries  PROGBITS  0000000000000000  0d8558  000008  00  W    0   0    8

Q: can the modpost warning be ignored?
Q: is there expected to be multiple __patchable_function_entries (and associated rela) sections in the livepatch.ko?

gcc: gcc version 11.2.1 20220127 (Red Hat 11.2.1-9) (GCC)
ld: GNU ld version 2.35.2-17.el9
kernel tree: amazon-arm-livepatch-v5.10
kpatch-build: surajjs95-arm64

*/
tmp = find_section_by_name(&kelf->sections, "__patchable_function_entries");
sec->sh.sh_flags |= (tmp->sh.sh_flags & SHF_LINK_ORDER);
sec->sh.sh_flags = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sec->sh.sh_link = 1 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, this resolves the issue @joe-lawrence reported above. I also thought we decided to copy it over from patched objectfile instead of always setting it.

@keiya-nobuta
Copy link
Contributor

Hi @surajjs95 , @keiya-nobuta : some of my recent merges to the master branch introduced a bunch of merge conflicts for this patchset, mainly those where I removed #ifdef arch checks in create-diff-object. Please see my rebase at https://github.com/joe-lawrence/kpatch/tree/arm64-rebase-aaaebaf25895. It builds, but I have not tested it. Can you guys test and if it looks good, incorporate those updates this PR and we can resume reviewing. Thanks.

Hi @joe-lawrence, thanks your work.
I tested your branch and got some errors in bug-table-section.patch, data-read-mostly.patch, fixup-section.patch, etc:

proc_sysctl.o: changed section __bug_table not selected for inclusion
proc_sysctl.o: changed section .rela__bug_table not selected for inclusion

dev.o: changed section .rodata.str not selected for inclusion
dev.o: changed section .rela__jump_table not selected for inclusion

readdir.o: changed section .rela.fixup not selected for inclusion
readdir.o: changed section .rela__ex_table not selected for inclusion

It has appeared in other patches, but it is mostly summarized for the above reasons.
It looks like good with the below fix:

diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
index 2fc030c..50ca7a5 100644
--- a/kpatch-build/create-diff-object.c
+++ b/kpatch-build/create-diff-object.c
@@ -1813,6 +1813,7 @@ static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
 		    !strcmp(sec->name, ".strtab") ||
 		    !strcmp(sec->name, ".symtab") ||
 		    !strcmp(sec->name, ".toc") ||
+		    !strcmp(sec->name, ".rodata.str") ||
 		    !strcmp(sec->name, ".rodata") ||
 		    (!strncmp(sec->name, ".rodata.", 8) &&
 		     strstr(sec->name, ".str1."))) {
@@ -2210,22 +2211,22 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset)
 static struct special_section special_sections[] = {
 	{
 		.name		= "__bug_table",
-		.arch		= X86_64 | PPC64,
+		.arch		= AARCH64 | X86_64 | PPC64,
 		.group_size	= bug_table_group_size,
 	},
 	{
 		.name		= ".fixup",
-		.arch		= X86_64 | PPC64,
+		.arch		= AARCH64 | X86_64 | PPC64,
 		.group_size	= fixup_group_size,
 	},
 	{
 		.name		= "__ex_table", /* must come after .fixup */
-		.arch		= X86_64 | PPC64,
+		.arch		= AARCH64 | X86_64 | PPC64,
 		.group_size	= ex_table_group_size,
 	},
 	{
 		.name		= "__jump_table",
-		.arch		= X86_64 | PPC64,
+		.arch		= AARCH64 | X86_64 | PPC64,
 		.group_size	= jump_table_group_size,
 	},
 	{

And warn-detect-FAIL.patch was successfully built, I think this is because line_macro_change_only for aarch64 is not implemented.

ERROR: warn-detect-FAIL: build succeeded when it should have failed

@joe-lawrence
Copy link
Contributor

Hi @joe-lawrence, thanks your work. I tested your branch and got some errors in bug-table-section.patch, data-read-mostly.patch, fixup-section.patch, etc:

Thanks for testing! By the way, what is your gcc / kernel version combo that you tested with? I was hoping to try the PR on a 5.14-based kernel (aka, rhel-9), but I only saw @surajjs95 's tree: amazon-arm-livepatch-v5.10, do you have a kernel branch is closer to up to date?

It looks like good with the below fix: [ snip ]

Great, thanks for those fixups, I'll incorporate those too.

And warn-detect-FAIL.patch was successfully built, I think this is because line_macro_change_only for aarch64 is not implemented.

Yep, that is still on the TODO list for the arm implementation.

@sm00th
Copy link
Contributor

sm00th commented Feb 14, 2022

Thanks for testing! By the way, what is your gcc / kernel version combo that you tested with? I was hoping to try the PR on a 5.14-based kernel (aka, rhel-9), but I only saw @surajjs95 's tree: amazon-arm-livepatch-v5.10, do you have a kernel branch is closer to up to date?

You can use mine https://github.com/sm00th/linux/tree/kpatch/aarch64 it is v13 with dropped STACK_VALIDATION dependency on top of v5.17-rc4

@keiya-nobuta
Copy link
Contributor

Thanks for testing! By the way, what is your gcc / kernel version combo that you tested with?

I'm using:
gcc (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0 /
v5.15.0 based kernel with Madhavan's v10, and my testset

You can use mine https://github.com/sm00th/linux/tree/kpatch/aarch64 it is v13 with dropped STACK_VALIDATION dependency on top of v5.17-rc4

Thanks, I'll try it.

@joe-lawrence
Copy link
Contributor

joe-lawrence commented Feb 15, 2022

Some more incremental work here: https://github.com/joe-lawrence/kpatch/tree/arm64-rebase

  • Using @sm00th 's aarch64 kernel tree and my integration-tests-kernel-5.14.0-17.el9 branch, created an initial set of unit tests (currently pointing to a dev branch in my repro. When we get closer to full support, we should rebuild these and push to the official repo.)
    • Run with git submodule init && git submodule update && make unit, with recent updates to master branch, can test aarch64 from x86 😎
  • First pass at line change detection in the last commit
    • warn-detect-FAIL.patch now fails with "ERROR: no changed objects found" as expected

Finally, if I try this change:

@@ -3567,7 +3567,7 @@ static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_
                 */
                tmp = find_section_by_name(&kelf->sections, "__patchable_function_entries");
                sec->sh.sh_flags |= (tmp->sh.sh_flags & SHF_LINK_ORDER);
-               sec->sh.sh_link = 1;
+               sec->sh.sh_link = tmp->sh.sh_link;
                break;
        }
        case PPC64:

The integration tests fail with "nm: gcc-static-local-var-2.OUTPUT.o: sh_link [2] in section `__patchable_function_entries' is incorrect". Did I implement the suggestion correctly?

@keiya-nobuta
Copy link
Contributor

The integration tests fail with "nm: gcc-static-local-var-2.OUTPUT.o: sh_link [2] in section `__patchable_function_entries' is incorrect". Did I implement the suggestion correctly?

I checked by cross-compile on x86 using gcc-linaro-11.2.1, and I got same error.

On the PATCHED.o, sh_link [2] to point to .text.<...> section, but on the OUTPUT.o, maybe it's incorrect for sh_link [2] to point to the RELA section.

patched/mm/mmap.o

Section Headers:
  [Nr] Name                               Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                                    NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text                              PROGBITS        0000000000000000 000040 000000 00  AX  0   0  1
  [ 2] .text.__traceiter_vm_unmapped_area PROGBITS        0000000000000000 000040 00006c 00  AX  0   0 16
  [185] __patchable_function_entries      PROGBITS        0000000000000000 0062b0 0001f8 00 WAL  2   0  8

Relocation section '.rela__patchable_function_entries' at offset 0x5cc50 contains 63 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  0000000200000101 R_AARCH64_ABS64        0000000000000000 .text.__traceiter_vm_unmapped_area + 4
<...>
00000000000001b0  0000003c00000101 R_AARCH64_ABS64        0000000000000000 .text.mmap_region + 4
<...>

output/mm/mmap.o

Section Headers:
  [Nr] Name                         Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                              NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text.mmap_region            PROGBITS        0000000000000000 000040 000608 00  AX  0   0 16
  [ 2] .rela.text.mmap_region       RELA            0000000000000000 000648 000120 18   I 28   1  8
  [31] __patchable_function_entries PROGBITS        0000000000000000 089a50 000008 08  AL  2   0  8

Relocation section '.rela__patchable_function_entries' at offset 0x89a58 contains 1 entry:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  0000000200000101 R_AARCH64_ABS64        0000000000000000 mmap_region + 4

In most cases Section [1] will be the .text section, so I think sh_link = 1 is fine.
I don't know how to use sh_link, but I think it's safer to set sh_link to point to the first-entry of __patchable_function_entries (after reindex sections). I fixed it with workaround:

diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
index ede5ae3..4303794 100644
--- a/kpatch-build/create-diff-object.c
+++ b/kpatch-build/create-diff-object.c
@@ -3567,7 +3567,6 @@ static void kpatch_alloc_mcount_sections(struct kpatch_elf *kelf, struct kpatch_
 		 */
 		tmp = find_section_by_name(&kelf->sections, "__patchable_function_entries");
 		sec->sh.sh_flags |= (tmp->sh.sh_flags & SHF_LINK_ORDER);
-		sec->sh.sh_link = tmp->sh.sh_link;
 		break;
 	}
 	case PPC64:
@@ -4096,6 +4095,18 @@ int main(int argc, char *argv[])
 	kpatch_strip_unneeded_syms(kelf_out, lookup);
 	kpatch_reindex_elements(kelf_out);
 
+	/*
+	 * At __patchable_function_entries, sh_link points to first-entry.
+	 */
+	sec = find_section_by_name(&kelf_out->sections,
+				   "__patchable_function_entries");
+	if (sec) {
+		struct rela *rela = list_first_entry(&sec->rela->relas,
+						     struct rela, list);
+		if (rela)
+			sec->sh.sh_link = rela->sym->sec->index;
+	}
+
 	/*
 	 * Update rela section headers and rebuild the rela section data
 	 * buffers from the relas lists.

@joe-lawrence
Copy link
Contributor

Re: sh_link: I don't know what sh_link should be either. It seems that ordinary module builds end up with sh_link set to the first non-zero length .text section. Perhaps for now, we should just leave it set to 1?

Re: integration tests: I built the integration-tests-kernel-5.14.0-17.el9 tests and almost all succeeded, save for macro-printk.patch, due to diff context shifts (from 5.14 to 5.17), and gcc-mangled-3.patch:

ERROR: slub.o: 2 function(s) can not be patched
slub.o: function percpu_ref_put_many.constprop.0 doesn't have patchable function entry, unable to patch
slub.o: function memcg_slab_free_hook doesn't have patchable function entry, unable to patch
/root/kpatch/kpatch-build/create-diff-object: unreconcilable difference

Interestingly both percpu_ref_put_many() and memcg_slab_free_hook() are defined in header files as static inlines, the compiler gives them their own .text. sections, but they don't have the usual bti c, nop, nop opening sequence and therefore no spot in rela__patchable_function_entries.

I modified the patch to only add nops to get_slabinfo() and the build succeeds, reporting expected slub.o: changed function: get_slabinfo... I wonder if the real version of the patch somehow provokes the compiler into creating new versions of the static inline code (but not directly affected by the patch). Attached here for someone more qualified at reading aarch64 asm: gcc-mangled-3.tar.gz

Also, I remember reporting a similar problem trying to build for PR #1244. Maybe this is new gcc-11 behavior?

@joe-lawrence
Copy link
Contributor

Some more interesting behavior to report.

Using a rhel-9 kernel and gcc-mangled-3.tar.gz:

slub.ORIG.o: function __list_add doesn't have patchable function entry, unable to patch
ERROR: slub.ORIG.o: 1 function(s) can not be patched
../kpatch-build/create-diff-object: unreconcilable difference

In this case, the entire __list_add function has moved from .text.__list_add to .text.unlikely.__list_add:

$ readelf --wide --sections slub.ORIG.o | grep 'list_add'
  [21] .text.__list_add  PROGBITS        0000000000000000 000620 00004c 00  AX  0   0 16
  [22] .rela.text.__list_add RELA            0000000000000000 0a52a8 000018 18   I 520  21  8
$ readelf --wide --sections slub.PATCHED.o | grep 'list_add'
  [21] .text.unlikely.__list_add PROGBITS        0000000000000000 000618 00004c 00  AX  0   0  4
  [22] .rela.text.unlikely.__list_add RELA            0000000000000000 0a5298 000018 18   I 521  21  8

Yet their compiled code is exactly the same:

$ diff -u <(objdump -Dr -j .text.__list_add slub.ORIG.o) \
          <(objdump -Dr -j .text.unlikely.__list_add slub.PATCHED.o)
--- /dev/fd/63  2022-02-24 11:57:42.497384380 -0500
+++ /dev/fd/62  2022-02-24 11:57:42.497384380 -0500
@@ -1,8 +1,8 @@
 
-slub.ORIG.o:     file format elf64-littleaarch64
+slub.PATCHED.o:     file format elf64-littleaarch64
 
 
-Disassembly of section .text.__list_add:
+Disassembly of section .text.unlikely.__list_add:
 
 0000000000000000 <__list_add>:
    0:  d503233f        paciasp

I googled a bit and found gcc Bug 61958 - functions arbitrarily placed in .text.unlikely section, from @jpoimboe way back in 2014. The gcc-managled-3.patch is similarly adding a printk and functions are moving from .text to .text.likely, however I don't believe that __list_add() is called by get_slabinfo(), nor is __list_add() inlined into its calling functions.

For an additional data point, with the rhel-9 kernel, I see the same section change for .text.irda_usb_dump_class_desc.isra.0 in @keiya-nobuta 's data-reloc.patch. (data-reloc.tar.gz)

@keiya-nobuta
Copy link
Contributor

Sorry for late reply.

I couldn't prepare RHELL9, so I tried it with fedora 35 aarch64.
gcc: gcc (GCC) 11.2.1 20220127 (Red Hat 11.2.1-9)
ld: GNU ld version 2.37-10.fc35

I didn't get the same result but I get similar result that static inline functions were changed which independent of the patched functions. It seems that arm64 gcc11 has optimization features that I don't understand well.

I didn't get this result with defconfig based config, so I tried turning off some configs from fedora's config. When CONFIG_DEBUG_SECTION_MISMATCH=n was set, these static inline function did not change. CONFIG_DEBUG_SECTION_MISMATCH is an option to add -fno-inline-functions-called-once to KBUILD_CFLAGS. I'm not sure what this is used for.

It looks like it is solved, but the non-patched functions seems to be changed :(

slub.o: changed function: process_slab
slub.o: changed function: __kmem_cache_shutdown
slub.o: changed function: __check_heap_object
slub.o: changed function: get_slabinfo

@keiya-nobuta
Copy link
Contributor

I posted this question to GCC Bugzilla.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105326

@joe-lawrence
Copy link
Contributor

I posted this question to GCC Bugzilla. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105326

Thanks for raising the question to the gcc folks. Maybe resurrecting #1045 to at least dump out some of the IPA inlining results could help us cope. (Ideally we could feed such data back to the compiler to essentially repeat the same decisions, but I would assume that is simply not possible.)

@keiya-nobuta
Copy link
Contributor

I think the Arm64 specific gcc issue needs to be resolved separately, but I don't think it's this pull-request issue.
Is it difficult to continue the review?

@swine
Copy link
Contributor

swine commented Jul 2, 2022

I've rebased your arm64 support into v0.9.6 and addressed a few issues which clang-built linux-5.15 throws up, and it builds working patches for most test cases.

I'll clean up and post my tree shortly, after merging with the recent s/390 changes, which cause much incidental merge conflict, as they both add to the switch(kelf->arch) cases, and special_sections[] entries

I think I've solved the problem with recent clang, where a separate __patchable_function_entries section is created for each patchable function, and it's required that create-diff-object can correctly read this different arrangement, and write_elf with the same many-sections format.

One remaining issue I haven't yet solved with 5.15 is insmod failure logging "overflow in relocation type 261 val 4" where clang (or kpatch) is using type 261 (R_AARCH64_PREL32) in a way that's now deprecated due to increased security

@joe-lawrence
Copy link
Contributor

keiya-nobuta says,

I think the Arm64 specific gcc issue needs to be resolved separately, but I don't think it's this pull-request issue. Is it difficult to continue the review?

The most difficult part is finding time during the day to work through it :) But yes, now with the initial s390x code out of the way, let's try continuing with arm64. :)

swine says,

I've rebased your arm64 support into v0.9.6 and addressed a few issues which clang-built linux-5.15 throws up, and it builds working patches for most test cases.
I'll clean up and post my tree shortly, after merging with the recent s/390 changes, which cause much incidental merge conflict, as they both add to the switch(kelf->arch) cases, and special_sections[] entries

Perfect. Apologies for the code churn. We refactored a lot of the architecture-specific code from #ifdef to switch statements. Hopefully it's a little more obvious to fit in new arches and then run cross-arch unit testing. Rebasing would be a good restart for reviewing... I don't know if you can directly push that here, or if @surajjs95 would have to manually import those changes to his branch that the PR was open for.

I think I've solved the problem with recent clang, where a separate __patchable_function_entries section is created for each patchable function, and it's required that create-diff-object can correctly read this different arrangement, and write_elf with the same many-sections format.

Was the many __patchable_function_entries sections issue limited to clang or did it affect gcc, too?

One remaining issue I haven't yet solved with 5.15 is insmod failure logging "overflow in relocation type 261 val 4" where clang (or kpatch) is using type 261 (R_AARCH64_PREL32) in a way that's now deprecated due to increased security

Can you elaborate on that? Also, is this no longer an issue on newer kernels? Thanks.

@t-msn
Copy link

t-msn commented Sep 15, 2022

Hello, is anyone currently working on this?
I'd like to help this PR to be merged.

To see the current status, I also rebased the code to current master : this branch
I run unit & integration tests(linux-5.18) on fedora36 and ubuntu22 with using this version of kernel (based on 5.18) and it basically looks fine. Some notes:

  • I see kpatch-build failure by gcc problem mentioned above in data-reloc test (test: Add data relocation test #1244).
  • module-load test can be built but failed to load module. It seems the kernel side's problem.
  • I added one fix to keep ubsan section if CONFIG_UBSAN is enabled (for ubuntu) but I'm not sure if this is correct.

I didn't test clang build which @swine mentioned.
Also, I think it'd be better to @surajjs95 update this PR.

On the other hand, I believe kernel side needs to/will be updated. I'm not familiar with kernel side at the moment and try to understand the current situation as well. Thanks.

@swine
Copy link
Contributor

swine commented Sep 15, 2022

thanks @t-msn, I wish I'd pushed my arm64 branch earlier to save you duplicated effort, but was hunting clang issues.

your rebase is possibly cleaner (certainly smaller, as it didn't need to address the clang-vs-gcc issues that I've explored in #1302).

BTW, the "overflow in relocation" issue I mentioned earlier is a non-issue, typo in earlier version of the code.

@t-msn
Copy link

t-msn commented Sep 16, 2022

@swine Thank you very much for sharing your reabase code. It looks like the clang support needs some effort... Maybe is it better to support(merge) gcc only part for the starting point? Anyway, I will try to look the code.

BTW, the "overflow in relocation" issue I mentioned earlier is a non-issue, typo in earlier version of the code.

That's good. Thanks.

@github-actions
Copy link

This PR has been open for 60 days with no activity and no assignee. It will be closed in 7 days unless a comment is added.

@github-actions github-actions bot added the stale label Aug 12, 2023
@github-actions
Copy link

This PR was closed because it was inactive for 7 days after being marked stale.

@github-actions github-actions bot closed this Aug 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants