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

elf_reader: permit multiple .data* sections #1546

Merged
merged 1 commit into from
Aug 21, 2024

Conversation

mejedi
Copy link
Contributor

@mejedi mejedi commented Aug 17, 2024

Mirror libbpf behavior. Quoting the respective commit message[1]:

Having the ability to use multiple .data sections and putting
dedicated global variables into separate .data.custom section also
opens up some new and interesting possibilities (like dynamically
sizing global arrays, without using BPF_MAP_TYPE_ARRAY map).

[1] libbpf/libbpf#274

Another motivation for dedicated data sections is a more granular access control, such as making a subset of variables BPF_F_RDONLY_PROG (userspace can alter but BPF can't). Also note that verifier doesn't prevent overflowing .data buffers into adjacent variables as long as the write is within the backing array map bounds.

@mejedi mejedi requested a review from a team as a code owner August 17, 2024 20:57
@mejedi mejedi force-pushed the multiple-data-sections branch 3 times, most recently from bb9beb1 to ed1d302 Compare August 18, 2024 09:56
@ti-mo
Copy link
Collaborator

ti-mo commented Aug 20, 2024

@mejedi Thanks, this has been in the back of my mind for years, but no one asked. Did you make this in response to #1545? If so, it would be useful to call out when one PR supersedes another in the future.

Copy link
Collaborator

@ti-mo ti-mo left a comment

Choose a reason for hiding this comment

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

Thanks for adding tests! Could we make the patch shorter, though? There's no real need to gate this on 5.2 as far as I know, and we have a similar test for e.g. .rodata.test in loader.c.

Not sure if using LoadAndAssign and using prog.Test() add much value, either. If a .data.test map is present in the CollectionSpec, we're good.

I'd like to limit the amount of objects we have in testdata/, as each additional object slows down make a bit. I think many of them could be written in a single .c file, or at least fewer than we have today.

Thanks again!

@mejedi mejedi force-pushed the multiple-data-sections branch 5 times, most recently from 53b4fd7 to 76181e1 Compare August 20, 2024 14:21
@@ -405,7 +405,7 @@ func TestProgInfoExtBTF(t *testing.T) {
t.Fatal(err)
}

expectedLineInfoCount := 26
expectedLineInfoCount := 28
Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's the downside of overloading loader.c, I guess. To be honest, I do not understand where 26 is coming from, but it (kind of) makes sense that the value grew by 2 after two lines were added. @ti-mo

Copy link
Collaborator

@ti-mo ti-mo Aug 21, 2024

Choose a reason for hiding this comment

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

Good question, that's quite straightforward to investigate!

		if src := ins.Source(); src != nil {
			lineInfoCount++
+			fmt.Println(src)
+			fmt.Println(ins)
		}

Results in:

...
LoadMapValue dst: r1, fd: 40 off: 0
	return static_fn(arg) + global_fn(arg) + arg2;
AddReg dst: r0 src: r1
	return static_fn(arg) + global_fn(arg) + arg2;
Exit

int __attribute__((noinline)) global_fn(uint32_t arg) {
...
	return static_fn(arg) + global_fn2(arg) + global_fn3(arg);
AddReg dst: r8 src: r0
	return static_fn(arg) + global_fn2(arg) + global_fn3(arg);
MovReg dst: r0 src: r8

One instruction for the variable access and one for accumulating into r0. (this is on main) Adding another variable to the return statement would result in 2 extra insns/lineinfo.

@mejedi mejedi requested a review from ti-mo August 20, 2024 14:30
@mejedi
Copy link
Contributor Author

mejedi commented Aug 20, 2024

@ti-mo Thank you for having a look. I reduced the amount of changes by piggy-backing on the loader.c and existing test in elf_reader_test.

I suspect that all data sections share the same machinery for resolving relocations. Therefore a test ensuring that a program referencing variables in .data.custom actually works as expected is not strictly necessary.

Copy link
Collaborator

@ti-mo ti-mo left a comment

Choose a reason for hiding this comment

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

Thanks!

Mirror libbpf behavior. Quoting the respective commit message[1]:

> Having the ability to use multiple .data sections and putting
> dedicated global variables into separate .data.custom section also
> opens up some new and interesting possibilities (like dynamically
> sizing global arrays, without using BPF_MAP_TYPE_ARRAY map).

[1] libbpf/libbpf#274

Another motivation for dedicated data sections is a more granular access
control, such as making a subset of variables BPF_F_RDONLY_PROG
(userspace can alter but BPF can't). Also note that verifier doesn't
prevent overflowing .data buffers into adjacent variables as long as the
write is within the backing array map bounds.

Signed-off-by: Nick Zavaritsky <mejedi@gmail.com>
@ti-mo ti-mo force-pushed the multiple-data-sections branch from 76181e1 to 2435814 Compare August 21, 2024 08:07
@ti-mo
Copy link
Collaborator

ti-mo commented Aug 21, 2024

Pushed a small fixup to add the volatile qualifier to arg3 for consistency with the others.

@mejedi
Copy link
Contributor Author

mejedi commented Aug 21, 2024

Pushed a small fixup to add the volatile qualifier to arg3 for consistency with the others.

Up to you but volatile is used with const to prevent compiler from eliding load, otherwise https://github.com/torvalds/linux/blob/master/Documentation/process/volatile-considered-harmful.rst

@ti-mo
Copy link
Collaborator

ti-mo commented Aug 21, 2024

Pushed a small fixup to add the volatile qualifier to arg3 for consistency with the others.

Up to you but volatile is used with const to prevent compiler from eliding load, otherwise https://github.com/torvalds/linux/blob/master/Documentation/process/volatile-considered-harmful.rst

It's not only useful for const, in my experience it makes compiler output around global vars more predictable since the compiler can't make any assumptions about the contents of the variable. I know on this particular var it doesn't really matter since it's never written from user space, but historically this has been a pain point.

The readme you linked applies to kernel code proper and mainly cites concurrency concerns, which doesn't apply to bpf as much. There are some mailing list threads about the use of static and volatile (bpf skeleton no longer exposes statics, for example), but usually it's hard to figure out the conclusion(s). If we should really avoid certain qualifiers, we should make a change across the code base so things remain consistent, and also document specifically why they should be avoided.

@ti-mo ti-mo merged commit 642d9ae into cilium:main Aug 21, 2024
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants