diff --git a/crates/rk/src/sections.rs b/crates/rk/src/sections.rs index cde1d5e..c4effe6 100644 --- a/crates/rk/src/sections.rs +++ b/crates/rk/src/sections.rs @@ -76,6 +76,19 @@ const INLINED: &[(&str, u64)] = &[ /// into `vmlinux.o`, and 6.1 writes them into every object. const SITES: &[&str] = &["__mcount_loc", "__patchable_function_entries"]; +/// The sections objtool writes into an object, which are left out of the sections an object has +/// for the same reason they are left out of [`SITES`]: whether one is there follows from the code, +/// a `.retpoline_sites` from an indirect call and a `.return_sites` from a `ret`. +const OBJTOOL: &[&str] = &[ + ".static_call_sites", + ".retpoline_sites", + ".return_sites", + ".call_sites", + ".ibt_endbr_seal", + ".orc_unwind", + ".orc_unwind_ip", +]; + /// Prefixes after which `-ffunction-sections` and `-fdata-sections` put a symbol name, with the /// kernel's own names that start the same way. The kernel writes its own sections with two dots, /// `.data..percpu`, so a single dot and a name is the compiler's. @@ -406,6 +419,7 @@ pub fn read(data: &[u8]) -> Result { ) || name.starts_with(".debug") || name.starts_with(".rela") + || OBJTOOL.contains(&name) { continue; } @@ -859,6 +873,27 @@ mod tests { assert_eq!(unnumbered(".7"), ".7"); } + #[test] + fn a_list_objtool_writes_is_not_a_section_the_object_has() { + let with = |extra: &[&str]| { + let mut b = Builder::new(); + b.function("f", &[0xc3]); + for name in extra { + b.section(name, &[0; 4]); + } + read(&b.bytes()).unwrap() + }; + let reference: Inventory = [("a.o".to_string(), with(&[]))].into(); + let other: Inventory = [( + "a.o".to_string(), + with(&[".retpoline_sites", ".return_sites"]), + )] + .into(); + assert!(compare(&reference, &other).clean()); + let other: Inventory = [("a.o".to_string(), with(&[".init.rodata"]))].into(); + assert!(!compare(&reference, &other).clean()); + } + fn object(sites: usize, jump: usize, license: &str) -> Vec { let mut b = Builder::new(); let f = b.function("f", &[0x90; 8]); diff --git a/docs/plan/09-objtool-linking-and-images.md b/docs/plan/09-objtool-linking-and-images.md index 4cfa4c6..1bf53b7 100644 --- a/docs/plan/09-objtool-linking-and-images.md +++ b/docs/plan/09-objtool-linking-and-images.md @@ -84,7 +84,7 @@ This is the check that would have caught CCC's x86 failure, its 40,000 undefined | set of section names, ignoring per-function names under `-ffunction-sections` | equal. Extra or missing names are failures | | entry counts of the kernel's tables: `__ksymtab`, `__ksymtab_gpl`, `__kcrctab*`, `.init.setup`, `.initcall*.init`, `__param`, `.con_initcall.init`, `__tracepoints*`, `_ftrace_events`, `__syscalls_metadata`, `.BTF_ids` | equal per object | | the call-site lists the compiler writes: `__mcount_loc`, `__patchable_function_entries` | equal per function that exists in both, since they depend on inlining decisions | -| the lists objtool writes: `.static_call_sites`, `.retpoline_sites`, `.return_sites`, `.call_sites`, `.ibt_endbr_seal`, `.orc_unwind_ip` | not counted. objtool builds them from the code, an ORC entry for every stack change and a return site for every `ret`, so the counts are each compiler's own instructions. `rk objtool-report` is the check for them. Before IBT, as in 6.1, every object has them, and from then on only `vmlinux.o` does | +| the lists objtool writes: `.static_call_sites`, `.retpoline_sites`, `.return_sites`, `.call_sites`, `.ibt_endbr_seal`, `.orc_unwind`, `.orc_unwind_ip` | not counted, and not in the sections an object is compared on either. objtool builds them from the code, an ORC entry for every stack change and a return site for every `ret`, so the counts are each compiler's own instructions. `rk objtool-report` is the check for them. Before IBT, as in 6.1, every object has them, and from then on only `vmlinux.o` does | | the tables written by the code they describe: `__jump_table`, `__bug_table`, `__ex_table`, `.altinstructions` | the same set of distinct sites per object. Every inlined copy of a `static_branch_unlikely` or a `WARN_ON` adds an entry, so the counts follow the inliner like the call-site lists do. A site is what the entry says apart from code addresses: the key and branch of a jump label, the format, file, line and flags of a bug, the fixup type of an exception entry without its register, and the CPU feature of an alternative | | `Module.symvers`: exported symbols, their namespaces, and CRCs | equal | | `System.map` global symbol set | equal, apart from compiler-generated local suffixes | diff --git a/sections-divergences.toml b/sections-divergences.toml index 224ae2f..379ed55 100644 --- a/sections-divergences.toml +++ b/sections-divergences.toml @@ -27,6 +27,12 @@ only = "either" item = ".rodata" reason = "a switch turned into a lookup table, or a constant local array kept in memory, which follows each compiler's own switch conversion and scalar replacement" +[[divergence]] +what = "sections" +only = "either" +item = ".init.rodata" +reason = "a constant local array in an __init function kept in memory, such as the ids in add_rtc_cmos on 6.1, which gcc removes after it unrolls the loop over it and folds the loads" + [[divergence]] what = "sections" only = "either" @@ -209,3 +215,40 @@ object = "kernel/signal.o" only = "reference" item = '4="include/linux/thread_info.h" f9000309' reason = "WARN_ON_ONCE(bytes > INT_MAX) in check_copy_size, which lives in thread_info.h on 6.12, with the same bounded copies as on 7.2.8" + +# 6.1.188. The sites carry no format string there either, and the lines are 6.1's. + +[[divergence]] +what = "__bug_table" +object = "drivers/char/random.o" +only = "reference" +item = '4="drivers/char/random.c" fe000000' +reason = "BUG_ON(random_data_len > 32) in crng_fast_key_erasure, with the same bound as on 7.2.8" + +[[divergence]] +what = "__bug_table" +object = "drivers/char/random.o" +only = "reference" +item = '4="drivers/char/random.c" 29010000' +reason = "BUG_ON(random_data_len > 32) in crng_make_state, with the same bound as on 7.2.8" + +[[divergence]] +what = "__bug_table" +object = "fs/ext4/extents.o" +only = "reference" +item = '4="fs/ext4/extents.c" 750c0000' +reason = "the BUG_ON in ext4_split_extent_at for both DATA_VALID flags at once, where ext4_split_extent passes at most one of them and the other two callers pass only the MARK_UNWRIT flags" + +[[divergence]] +what = "__bug_table" +object = "fs/ext4/page-io.o" +only = "reference" +item = '4="fs/ext4/page-io.c" e6000109' +reason = "WARN_ON(!(io_end->flag & EXT4_IO_END_UNWRITTEN)) in ext4_add_complete_io, whose only caller returns early when the flag is clear, as on 6.12" + +[[divergence]] +what = "__bug_table" +object = "kernel/signal.o" +only = "reference" +item = '4="include/linux/thread_info.h" e9000309' +reason = "WARN_ON_ONCE(bytes > INT_MAX) in check_copy_size, with the same bounded copies as on 6.12"