Skip to content

Comments

elf_reader: permit multiple .data* sections#1546

Merged
ti-mo merged 1 commit intocilium:mainfrom
mejedi:multiple-data-sections
Aug 21, 2024
Merged

elf_reader: permit multiple .data* sections#1546
ti-mo merged 1 commit intocilium:mainfrom
mejedi:multiple-data-sections

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
Contributor

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
Contributor

@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
}

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
Contributor

@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
Contributor

@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 <[email protected]>
@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
Contributor

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
Contributor

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
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