-
Notifications
You must be signed in to change notification settings - Fork 6.6k
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
LLEXT: dependency test #77738
LLEXT: dependency test #77738
Conversation
c04e284
to
21f1a03
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this! 🙇♂️ 🚀
One small issue I see with this setup is that it does not reliably test the actual relocation; there is no failure in the dependent if calling the function has no effect.
Instead of reusing the hello_world extension, what do you think about two tiny dep_parent_ext
/dep_child_ext
so that some easy calculation in child depends on the result of a function exported from parent? This way you can use z_assert
in child to ensure the path was taken correctly.
void *dependency_buf = k_heap_alloc(&tllext_heap, sizeof(hello_world_ext), K_NO_WAIT); | ||
|
||
zassert_not_null(dependency_buf); | ||
|
||
void *dependent_buf = k_heap_alloc(&tllext_heap, sizeof(export_dependent_ext), K_NO_WAIT); | ||
|
||
zassert_not_null(dependent_buf); | ||
|
||
memcpy(dependency_buf, hello_world_ext, sizeof(hello_world_ext)); | ||
memcpy(dependent_buf, export_dependent_ext, sizeof(export_dependent_ext)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to copy the _ext
buffers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's a comment in the respective commit - because I reuse "hello world" and I thought it could be done by others from time to time too. And if we relocate extensions in place, that breaks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, it was a quick review and I did not check the commits. 🤦♂️
However there are two things I am not fond of in that commit:
- it is being applied even when
STORAGE_WRITABLE
is not enabled. This makes the LLEXT code always access ELF data in RAM, and thus will no longer catch bugs that cause LLEXT to write something there. - even if applied only on
STORAGE_WRITABLE
, this means that ELF buffers are never written to - they should be made fullyconst
(notLLEXT_CONST
).
|
||
void (*test_entry_fn)() = llext_find_sym(&ext_dependent->exp_tab, "test_dependent"); | ||
|
||
zassert_not_null(test_entry_fn, "test_entry should be an exported symbol"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test_entry
in the message is really test_dependent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, there's one common entry point, so I thought it shouldn't matter
@pillo79 thanks for reviewing! I first have done two extensions, but then I decided that I can reuse "hello world" for the dependency. As for no effect - I thought the effect would be an exception - if we somehow linked wrongly? So, yes, if there's a strong opinion, that we need a more explicit test, we can do that too |
Yes, most of the time a bad byte sequence will cause a CPU exception. However, I am sure I have seen jumps being silently ignored in some early LLEXT debugging. Maybe they were relative and "jumped 0 bytes" without a valid relocation? |
yeah, we could make the extension cryptographically sign our (pseudo-) random challenge... or we can just make it return 42 :-) |
56c7e5c
to
63b376b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree with modifying the test_entry()
signature in all extensions, when the return value is only ever useful for the new custom test.
Instead of the last 2 commits, I suggest to add 2 extensions:
- one exporting an
int get_value() { return 42; }
, - the other a standard
void test_entry() { zassert_equal(get_value(), 42); }
.
@@ -15,7 +15,9 @@ target_include_directories(app PRIVATE | |||
${ZEPHYR_BASE}/arch/${ARCH}/include | |||
) | |||
|
|||
set(ext_names hello_world logging relative_jump object syscalls threads_kernel_objects) | |||
set(ext_names | |||
hello_world logging relative_jump object syscalls threads_kernel_objects export_dependent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please convert this so the names are one per line, I think we are going to need this going forward 🙂
@@ -38,6 +38,10 @@ LOG_MODULE_REGISTER(test_llext_simple); | |||
#define LLEXT_FIND_BUILTIN_SYM(symbol_name) llext_find_sym(NULL, # symbol_name) | |||
#endif | |||
|
|||
#ifdef CONFIG_LLEXT_STORAGE_WRITABLE | |||
K_HEAP_DEFINE(tllext_heap, 32 * 1024); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
K_HEAP_DEFINE(tllext_heap, 32 * 1024); | |
K_HEAP_DEFINE(scratch_heap, CONFIG_LLEXT_HEAP_SIZE * 1024); |
(name is a suggestion but please make it more descriptive, and size it automatically)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to verify - CONFIG_LLEXT_HEAP_SIZE
is used in llext_mem.c as the heap size for LLEXT itself, you want to use the same here for the test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's overkill, but it was the simplest solution I could think of - you can also add a CONFIG_MAX_ELF_SIZE in the test Kconfig
, for example.
IMHO the hardcoded number will bite us often - AArch64 already needs a bigger buffer in the initial PR 🙂
But these comments are not blockers, sorry. Don't know how to explain what I needed to the Github UI, apparently 🤦♂️
@pillo79 I actually think it's good for extensions to return a value, also demonstrates and tests the API better IMHO. @teburd what do you think? |
@lyakh I am totally in favor of doing that; I would also go the extra step of passing a pointer in, TBH. So I would gladly approve either:
|
@pillo79 ok, for option 1 - would it be enough if I extended |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks much better to me, thanks! 👍 2 minor comments below.
short a = mask & (uintptr_t)test_dependent; | ||
short b = mask & ((uintptr_t)test_dependent >> half_ptr_bits); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think short
is correct on 64 bit arches given the values involved (although the assert will be fine).
IMHO a simpler calculation with constant values would make the logic clearer and still test proper linking and argument passing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pillo79 yeah, I was thinking about it too - if addresses are 64-bit, then IIRC int
can still be 32 bits. So I wanted to replace int
with long
and short
with int
, would that be enough?
#include <zephyr/sys/util.h> | ||
extern int test_dependency(short a, short b); | ||
|
||
void test_dependent(void) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think this should be test_entry
, for consistency with all other test suites.
LLEXT loader parameters are input-only, make them "const." Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
When identifying the size of an extension image we simply need its size in bytes, not the number of array elements, that contain it. Both happen to return the same value, because we use arrays of bytes, but using sizeof() is clearer. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This test uses two LLEXT objects: one of them exports a symbol and the other one calls the exported function. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Add a twister test to the new LLEXT dependency infrastructure. It contains #77473 but the goal is to merge them separately