Fix VDSO patching of small entries

Summary:
On Ubuntu 22.04, this is the disassembly of `gettimeofday` and `clock_gettime`:
```
0000000000000bd0 <__vdso_gettimeofday@LINUX_2.6>:
 bd0:   e9 4b fe ff ff          jmp    a20 <LINUX_2.6@LINUX_2.6+0xa20>
 bd5:   66 66 2e 0f 1f 84 00    data16 cs nopw 0x0(%rax,%rax,1)
 bdc:   00 00 00 00

0000000000000c10 <__vdso_clock_gettime@LINUX_2.6>:
 c10:   e9 9b fb ff ff          jmp    7b0 <LINUX_2.6@LINUX_2.6+0x7b0>
 c15:   66 66 2e 0f 1f 84 00    data16 cs nopw 0x0(%rax,%rax,1)
 c1c:   00 00 00 00
```

These implementations just `jmp` to another internal function. The dynamic symbol table reports that these functions are 5 bytes in size, but we can see that they are aligned up to 16 bytes and they are still safe to patch. Thus, our patcher should take this padding and alignment into account.

Fixes https://github.com/facebookexperimental/hermit/issues/16.

Differential Revision: D41565913

fbshipit-source-id: 164aca876abf92642e7ebf4ce96d0860b276647b
This commit is contained in:
Jason White 2022-11-28 16:28:02 -08:00 committed by Facebook GitHub Bot
parent fa44c91a66
commit 5478e47a25

View file

@ -138,6 +138,11 @@ const VDSO_SYMBOLS: &[(&str, &[u8])] = &[
("__kernel_rt_sigreturn", vdso_syms::rt_sigreturn),
];
/// Rounds up `value` so that it is a multiple of `alignment`.
fn align_up(value: usize, alignment: usize) -> usize {
(value + alignment - 1) & alignment.wrapping_neg()
}
lazy_static! {
static ref VDSO_PATCH_INFO: HashMap<&'static str, (u64, usize, &'static [u8])> = {
let info = vdso_get_symbols_info();
@ -145,14 +150,20 @@ lazy_static! {
for (k, v) in VDSO_SYMBOLS {
if let Some(&(base, size)) = info.get(*k) {
// NOTE: There is padding at the end of every VDSO entry to
// bring it up to a 16-byte size alignment. The dynamic symbol
// table doesn't report the aligned size, so we must do the same
// alignment here. For example, some VDSO entries might only be
// 5 bytes, but they have padding to align them up to 16 bytes.
let aligned_size = align_up(size, 16);
assert!(
v.len() <= size,
v.len() <= aligned_size,
"vdso symbol {}'s real size is {} bytes, but trying to replace it with {} bytes",
k,
size,
v.len()
);
res.insert(*k, (base, size, *v));
res.insert(*k, (base, aligned_size, *v));
}
}
@ -252,6 +263,15 @@ where
mod tests {
use super::*;
#[test]
fn test_align_up() {
assert_eq!(align_up(0, 16), 0);
assert_eq!(align_up(1, 16), 16);
assert_eq!(align_up(15, 16), 16);
assert_eq!(align_up(16, 16), 16);
assert_eq!(align_up(17, 16), 32);
}
#[test]
fn can_find_vdso() {
assert!(