Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kallsyms: support "big" kernel symbols

Rust symbols can become quite long due to namespacing introduced
by modules, types, traits, generics, etc.

Increasing to 255 is not enough in some cases, therefore
introduce longer lengths to the symbol table.

In order to avoid increasing all lengths to 2 bytes (since most
of them are small, including many Rust ones), use ULEB128 to
keep smaller symbols in 1 byte, with the rest in 2 bytes.

Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com>
Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com>
Co-developed-by: Wedson Almeida Filho <wedsonaf@google.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
Co-developed-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
Co-developed-by: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>

+48 -7
+22 -4
kernel/kallsyms.c
··· 50 50 data = &kallsyms_names[off]; 51 51 len = *data; 52 52 data++; 53 + off++; 54 + 55 + /* If MSB is 1, it is a "big" symbol, so needs an additional byte. */ 56 + if ((len & 0x80) != 0) { 57 + len = (len & 0x7F) | (*data << 7); 58 + data++; 59 + off++; 60 + } 53 61 54 62 /* 55 63 * Update the offset to return the offset for the next symbol on 56 64 * the compressed stream. 57 65 */ 58 - off += len + 1; 66 + off += len; 59 67 60 68 /* 61 69 * For every byte on the compressed symbol data, copy the table ··· 116 108 static unsigned int get_symbol_offset(unsigned long pos) 117 109 { 118 110 const u8 *name; 119 - int i; 111 + int i, len; 120 112 121 113 /* 122 114 * Use the closest marker we have. We have markers every 256 positions, ··· 130 122 * so we just need to add the len to the current pointer for every 131 123 * symbol we wish to skip. 132 124 */ 133 - for (i = 0; i < (pos & 0xFF); i++) 134 - name = name + (*name) + 1; 125 + for (i = 0; i < (pos & 0xFF); i++) { 126 + len = *name; 127 + 128 + /* 129 + * If MSB is 1, it is a "big" symbol, so we need to look into 130 + * the next byte (and skip it, too). 131 + */ 132 + if ((len & 0x80) != 0) 133 + len = ((len & 0x7F) | (name[1] << 7)) + 1; 134 + 135 + name = name + len + 1; 136 + } 135 137 136 138 return name - kallsyms_names; 137 139 }
+26 -3
scripts/kallsyms.c
··· 487 487 if ((i & 0xFF) == 0) 488 488 markers[i >> 8] = off; 489 489 490 - printf("\t.byte 0x%02x", table[i]->len); 490 + /* There cannot be any symbol of length zero. */ 491 + if (table[i]->len == 0) { 492 + fprintf(stderr, "kallsyms failure: " 493 + "unexpected zero symbol length\n"); 494 + exit(EXIT_FAILURE); 495 + } 496 + 497 + /* Only lengths that fit in up-to-two-byte ULEB128 are supported. */ 498 + if (table[i]->len > 0x3FFF) { 499 + fprintf(stderr, "kallsyms failure: " 500 + "unexpected huge symbol length\n"); 501 + exit(EXIT_FAILURE); 502 + } 503 + 504 + /* Encode length with ULEB128. */ 505 + if (table[i]->len <= 0x7F) { 506 + /* Most symbols use a single byte for the length. */ 507 + printf("\t.byte 0x%02x", table[i]->len); 508 + off += table[i]->len + 1; 509 + } else { 510 + /* "Big" symbols use two bytes. */ 511 + printf("\t.byte 0x%02x, 0x%02x", 512 + (table[i]->len & 0x7F) | 0x80, 513 + (table[i]->len >> 7) & 0x7F); 514 + off += table[i]->len + 2; 515 + } 491 516 for (k = 0; k < table[i]->len; k++) 492 517 printf(", 0x%02x", table[i]->sym[k]); 493 518 printf("\n"); 494 - 495 - off += table[i]->len + 1; 496 519 } 497 520 printf("\n"); 498 521