mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 02:05:33 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git
This commit is contained in:
commit
e92f4a48e5
308
Documentation/kbuild/gendwarfksyms.rst
Normal file
308
Documentation/kbuild/gendwarfksyms.rst
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
=======================
|
||||||
|
DWARF module versioning
|
||||||
|
=======================
|
||||||
|
|
||||||
|
1. Introduction
|
||||||
|
===============
|
||||||
|
|
||||||
|
When CONFIG_MODVERSIONS is enabled, symbol versions for modules
|
||||||
|
are typically calculated from preprocessed source code using the
|
||||||
|
**genksyms** tool. However, this is incompatible with languages such
|
||||||
|
as Rust, where the source code has insufficient information about
|
||||||
|
the resulting ABI. With CONFIG_GENDWARFKSYMS (and CONFIG_DEBUG_INFO)
|
||||||
|
selected, **gendwarfksyms** is used instead to calculate symbol versions
|
||||||
|
from the DWARF debugging information, which contains the necessary
|
||||||
|
details about the final module ABI.
|
||||||
|
|
||||||
|
1.1. Usage
|
||||||
|
==========
|
||||||
|
|
||||||
|
gendwarfksyms accepts a list of object files on the command line, and a
|
||||||
|
list of symbol names (one per line) in standard input::
|
||||||
|
|
||||||
|
Usage: gendwarfksyms [options] elf-object-file ... < symbol-list
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-d, --debug Print debugging information
|
||||||
|
--dump-dies Dump DWARF DIE contents
|
||||||
|
--dump-die-map Print debugging information about die_map changes
|
||||||
|
--dump-types Dump type strings
|
||||||
|
--dump-versions Dump expanded type strings used for symbol versions
|
||||||
|
-s, --stable Support kABI stability features
|
||||||
|
-T, --symtypes file Write a symtypes file
|
||||||
|
-h, --help Print this message
|
||||||
|
|
||||||
|
|
||||||
|
2. Type information availability
|
||||||
|
================================
|
||||||
|
|
||||||
|
While symbols are typically exported in the same translation unit (TU)
|
||||||
|
where they're defined, it's also perfectly fine for a TU to export
|
||||||
|
external symbols. For example, this is done when calculating symbol
|
||||||
|
versions for exports in stand-alone assembly code.
|
||||||
|
|
||||||
|
To ensure the compiler emits the necessary DWARF type information in the
|
||||||
|
TU where symbols are actually exported, gendwarfksyms adds a pointer
|
||||||
|
to exported symbols in the `EXPORT_SYMBOL()` macro using the following
|
||||||
|
macro::
|
||||||
|
|
||||||
|
#define __GENDWARFKSYMS_EXPORT(sym) \
|
||||||
|
static typeof(sym) *__gendwarfksyms_ptr_##sym __used \
|
||||||
|
__section(".discard.gendwarfksyms") = &sym;
|
||||||
|
|
||||||
|
|
||||||
|
When a symbol pointer is found in DWARF, gendwarfksyms can use its
|
||||||
|
type for calculating symbol versions even if the symbol is defined
|
||||||
|
elsewhere. The name of the symbol pointer is expected to start with
|
||||||
|
`__gendwarfksyms_ptr_`, followed by the name of the exported symbol.
|
||||||
|
|
||||||
|
3. Symtypes output format
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Similarly to genksyms, gendwarfksyms supports writing a symtypes
|
||||||
|
file for each processed object that contain types for exported
|
||||||
|
symbols and each referenced type that was used in calculating symbol
|
||||||
|
versions. These files can be useful when trying to determine what
|
||||||
|
exactly caused symbol versions to change between builds. To generate
|
||||||
|
symtypes files during a kernel build, set `KBUILD_SYMTYPES=1`.
|
||||||
|
|
||||||
|
Matching the existing format, the first column of each line contains
|
||||||
|
either a type reference or a symbol name. Type references have a
|
||||||
|
one-letter prefix followed by "#" and the name of the type. Four
|
||||||
|
reference types are supported::
|
||||||
|
|
||||||
|
e#<type> = enum
|
||||||
|
s#<type> = struct
|
||||||
|
t#<type> = typedef
|
||||||
|
u#<type> = union
|
||||||
|
|
||||||
|
Type names with spaces in them are wrapped in single quotes, e.g.::
|
||||||
|
|
||||||
|
s#'core::result::Result<u8, core::num::error::ParseIntError>'
|
||||||
|
|
||||||
|
The rest of the line contains a type string. Unlike with genksyms that
|
||||||
|
produces C-style type strings, gendwarfksyms uses the same simple parsed
|
||||||
|
DWARF format produced by **--dump-dies**, but with type references
|
||||||
|
instead of fully expanded strings.
|
||||||
|
|
||||||
|
4. Maintaining a stable kABI
|
||||||
|
============================
|
||||||
|
|
||||||
|
Distribution maintainers often need the ability to make ABI compatible
|
||||||
|
changes to kernel data structures due to LTS updates or backports. Using
|
||||||
|
the traditional `#ifndef __GENKSYMS__` to hide these changes from symbol
|
||||||
|
versioning won't work when processing object files. To support this
|
||||||
|
use case, gendwarfksyms provides kABI stability features designed to
|
||||||
|
hide changes that won't affect the ABI when calculating versions. These
|
||||||
|
features are all gated behind the **--stable** command line flag and are
|
||||||
|
not used in the mainline kernel. To use stable features during a kernel
|
||||||
|
build, set `KBUILD_GENDWARFKSYMS_STABLE=1`.
|
||||||
|
|
||||||
|
Examples for using these features are provided in the
|
||||||
|
**scripts/gendwarfksyms/examples** directory, including helper macros
|
||||||
|
for source code annotation. Note that as these features are only used to
|
||||||
|
transform the inputs for symbol versioning, the user is responsible for
|
||||||
|
ensuring that their changes actually won't break the ABI.
|
||||||
|
|
||||||
|
4.1. kABI rules
|
||||||
|
===============
|
||||||
|
|
||||||
|
kABI rules allow distributions to fine-tune certain parts
|
||||||
|
of gendwarfksyms output and thus control how symbol
|
||||||
|
versions are calculated. These rules are defined in the
|
||||||
|
`.discard.gendwarfksyms.kabi_rules` section of the object file and
|
||||||
|
consist of simple null-terminated strings with the following structure::
|
||||||
|
|
||||||
|
version\0type\0target\0value\0
|
||||||
|
|
||||||
|
This string sequence is repeated as many times as needed to express all
|
||||||
|
the rules. The fields are as follows:
|
||||||
|
|
||||||
|
- `version`: Ensures backward compatibility for future changes to the
|
||||||
|
structure. Currently expected to be "1".
|
||||||
|
- `type`: Indicates the type of rule being applied.
|
||||||
|
- `target`: Specifies the target of the rule, typically the fully
|
||||||
|
qualified name of the DWARF Debugging Information Entry (DIE).
|
||||||
|
- `value`: Provides rule-specific data.
|
||||||
|
|
||||||
|
The following helper macro, for example, can be used to specify rules
|
||||||
|
in the source code::
|
||||||
|
|
||||||
|
#define __KABI_RULE(hint, target, value) \
|
||||||
|
static const char __PASTE(__gendwarfksyms_rule_, \
|
||||||
|
__COUNTER__)[] __used __aligned(1) \
|
||||||
|
__section(".discard.gendwarfksyms.kabi_rules") = \
|
||||||
|
"1\0" #hint "\0" #target "\0" #value
|
||||||
|
|
||||||
|
|
||||||
|
Currently, only the rules discussed in this section are supported, but
|
||||||
|
the format is extensible enough to allow further rules to be added as
|
||||||
|
need arises.
|
||||||
|
|
||||||
|
4.1.1. Managing definition visibility
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
A declaration can change into a full definition when additional includes
|
||||||
|
are pulled into the translation unit. This changes the versions of any
|
||||||
|
symbol that references the type even if the ABI remains unchanged. As
|
||||||
|
it may not be possible to drop includes without breaking the build, the
|
||||||
|
`declonly` rule can be used to specify a type as declaration-only, even
|
||||||
|
if the debugging information contains the full definition.
|
||||||
|
|
||||||
|
The rule fields are expected to be as follows:
|
||||||
|
|
||||||
|
- `type`: "declonly"
|
||||||
|
- `target`: The fully qualified name of the target data structure
|
||||||
|
(as shown in **--dump-dies** output).
|
||||||
|
- `value`: This field is ignored.
|
||||||
|
|
||||||
|
Using the `__KABI_RULE` macro, this rule can be defined as::
|
||||||
|
|
||||||
|
#define KABI_DECLONLY(fqn) __KABI_RULE(declonly, fqn, )
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
struct s {
|
||||||
|
/* definition */
|
||||||
|
};
|
||||||
|
|
||||||
|
KABI_DECLONLY(s);
|
||||||
|
|
||||||
|
4.1.2. Adding enumerators
|
||||||
|
=========================
|
||||||
|
|
||||||
|
For enums, all enumerators and their values are included in calculating
|
||||||
|
symbol versions, which becomes a problem if we later need to add more
|
||||||
|
enumerators without changing symbol versions. The `enumerator_ignore`
|
||||||
|
rule allows us to hide named enumerators from the input.
|
||||||
|
|
||||||
|
The rule fields are expected to be as follows:
|
||||||
|
|
||||||
|
- `type`: "enumerator_ignore"
|
||||||
|
- `target`: The fully qualified name of the target enum
|
||||||
|
(as shown in **--dump-dies** output) and the name of the
|
||||||
|
enumerator field separated by a space.
|
||||||
|
- `value`: This field is ignored.
|
||||||
|
|
||||||
|
Using the `__KABI_RULE` macro, this rule can be defined as::
|
||||||
|
|
||||||
|
#define KABI_ENUMERATOR_IGNORE(fqn, field) \
|
||||||
|
__KABI_RULE(enumerator_ignore, fqn field, )
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
enum e {
|
||||||
|
A, B, C, D,
|
||||||
|
};
|
||||||
|
|
||||||
|
KABI_ENUMERATOR_IGNORE(e, B);
|
||||||
|
KABI_ENUMERATOR_IGNORE(e, C);
|
||||||
|
|
||||||
|
If the enum additionally includes an end marker and new values must
|
||||||
|
be added in the middle, we may need to use the old value for the last
|
||||||
|
enumerator when calculating versions. The `enumerator_value` rule allows
|
||||||
|
us to override the value of an enumerator for version calculation:
|
||||||
|
|
||||||
|
- `type`: "enumerator_value"
|
||||||
|
- `target`: The fully qualified name of the target enum
|
||||||
|
(as shown in **--dump-dies** output) and the name of the
|
||||||
|
enumerator field separated by a space.
|
||||||
|
- `value`: Integer value used for the field.
|
||||||
|
|
||||||
|
Using the `__KABI_RULE` macro, this rule can be defined as::
|
||||||
|
|
||||||
|
#define KABI_ENUMERATOR_VALUE(fqn, field, value) \
|
||||||
|
__KABI_RULE(enumerator_value, fqn field, value)
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
enum e {
|
||||||
|
A, B, C, LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
KABI_ENUMERATOR_IGNORE(e, C);
|
||||||
|
KABI_ENUMERATOR_VALUE(e, LAST, 2);
|
||||||
|
|
||||||
|
4.3. Adding structure members
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Perhaps the most common ABI compatible change is adding a member to a
|
||||||
|
kernel data structure. When changes to a structure are anticipated,
|
||||||
|
distribution maintainers can pre-emptively reserve space in the
|
||||||
|
structure and take it into use later without breaking the ABI. If
|
||||||
|
changes are needed to data structures without reserved space, existing
|
||||||
|
alignment holes can potentially be used instead. While kABI rules could
|
||||||
|
be added for these type of changes, using unions is typically a more
|
||||||
|
natural method. This section describes gendwarfksyms support for using
|
||||||
|
reserved space in data structures and hiding members that don't change
|
||||||
|
the ABI when calculating symbol versions.
|
||||||
|
|
||||||
|
4.3.1. Reserving space and replacing members
|
||||||
|
============================================
|
||||||
|
|
||||||
|
Space is typically reserved for later use by appending integer types, or
|
||||||
|
arrays, to the end of the data structure, but any type can be used. Each
|
||||||
|
reserved member needs a unique name, but as the actual purpose is usually
|
||||||
|
not known at the time the space is reserved, for convenience, names that
|
||||||
|
start with `__kabi_` are left out when calculating symbol versions::
|
||||||
|
|
||||||
|
struct s {
|
||||||
|
long a;
|
||||||
|
long __kabi_reserved_0; /* reserved for future use */
|
||||||
|
};
|
||||||
|
|
||||||
|
The reserved space can be taken into use by wrapping the member in a
|
||||||
|
union, which includes the original type and the replacement member::
|
||||||
|
|
||||||
|
struct s {
|
||||||
|
long a;
|
||||||
|
union {
|
||||||
|
long __kabi_reserved_0; /* original type */
|
||||||
|
struct b b; /* replaced field */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
If the `__kabi_` naming scheme was used when reserving space, the name
|
||||||
|
of the first member of the union must start with `__kabi_reserved`. This
|
||||||
|
ensures the original type is used when calculating versions, but the name
|
||||||
|
is again left out. The rest of the union is ignored.
|
||||||
|
|
||||||
|
If we're replacing a member that doesn't follow this naming convention,
|
||||||
|
we also need to preserve the original name to avoid changing versions,
|
||||||
|
which we can do by changing the first union member's name to start with
|
||||||
|
`__kabi_renamed` followed by the original name.
|
||||||
|
|
||||||
|
The examples include `KABI_(RESERVE|USE|REPLACE)*` macros that help
|
||||||
|
simplify the process and also ensure the replacement member is correctly
|
||||||
|
aligned and its size won't exceed the reserved space.
|
||||||
|
|
||||||
|
4.3.2. Hiding members
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Predicting which structures will require changes during the support
|
||||||
|
timeframe isn't always possible, in which case one might have to resort
|
||||||
|
to placing new members into existing alignment holes::
|
||||||
|
|
||||||
|
struct s {
|
||||||
|
int a;
|
||||||
|
/* a 4-byte alignment hole */
|
||||||
|
unsigned long b;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
While this won't change the size of the data structure, one needs to
|
||||||
|
be able to hide the added members from symbol versioning. Similarly
|
||||||
|
to reserved fields, this can be accomplished by wrapping the added
|
||||||
|
member to a union where one of the fields has a name starting with
|
||||||
|
`__kabi_ignored`::
|
||||||
|
|
||||||
|
struct s {
|
||||||
|
int a;
|
||||||
|
union {
|
||||||
|
char __kabi_ignored_0;
|
||||||
|
int n;
|
||||||
|
};
|
||||||
|
unsigned long b;
|
||||||
|
};
|
||||||
|
|
||||||
|
With **--stable**, both versions produce the same symbol version.
|
@ -21,6 +21,7 @@ Kernel Build System
|
|||||||
reproducible-builds
|
reproducible-builds
|
||||||
gcc-plugins
|
gcc-plugins
|
||||||
llvm
|
llvm
|
||||||
|
gendwarfksyms
|
||||||
|
|
||||||
.. only:: subproject and html
|
.. only:: subproject and html
|
||||||
|
|
||||||
|
@ -423,6 +423,26 @@ Symbols From the Kernel (vmlinux + modules)
|
|||||||
1) It lists all exported symbols from vmlinux and all modules.
|
1) It lists all exported symbols from vmlinux and all modules.
|
||||||
2) It lists the CRC if CONFIG_MODVERSIONS is enabled.
|
2) It lists the CRC if CONFIG_MODVERSIONS is enabled.
|
||||||
|
|
||||||
|
Version Information Formats
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Exported symbols have information stored in __ksymtab or __ksymtab_gpl
|
||||||
|
sections. Symbol names and namespaces are stored in __ksymtab_strings,
|
||||||
|
using a format similar to the string table used for ELF. If
|
||||||
|
CONFIG_MODVERSIONS is enabled, the CRCs corresponding to exported
|
||||||
|
symbols will be added to the __kcrctab or __kcrctab_gpl.
|
||||||
|
|
||||||
|
If CONFIG_BASIC_MODVERSIONS is enabled (default with
|
||||||
|
CONFIG_MODVERSIONS), imported symbols will have their symbol name and
|
||||||
|
CRC stored in the __versions section of the importing module. This
|
||||||
|
mode only supports symbols of length up to 64 bytes.
|
||||||
|
|
||||||
|
If CONFIG_EXTENDED_MODVERSIONS is enabled (required to enable both
|
||||||
|
CONFIG_MODVERSIONS and CONFIG_RUST at the same time), imported symbols
|
||||||
|
will have their symbol name recorded in the __version_ext_names
|
||||||
|
section as a series of concatenated, null-terminated strings. CRCs for
|
||||||
|
these symbols will be recorded in the __version_ext_crcs section.
|
||||||
|
|
||||||
Symbols and External Modules
|
Symbols and External Modules
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@ -59,7 +59,6 @@ iptables 1.4.2 iptables -V
|
|||||||
openssl & libcrypto 1.0.0 openssl version
|
openssl & libcrypto 1.0.0 openssl version
|
||||||
bc 1.06.95 bc --version
|
bc 1.06.95 bc --version
|
||||||
Sphinx\ [#f1]_ 2.4.4 sphinx-build --version
|
Sphinx\ [#f1]_ 2.4.4 sphinx-build --version
|
||||||
cpio any cpio --version
|
|
||||||
GNU tar 1.28 tar --version
|
GNU tar 1.28 tar --version
|
||||||
gtags (optional) 6.6.5 gtags --version
|
gtags (optional) 6.6.5 gtags --version
|
||||||
mkimage (optional) 2017.01 mkimage --version
|
mkimage (optional) 2017.01 mkimage --version
|
||||||
@ -536,11 +535,6 @@ mcelog
|
|||||||
|
|
||||||
- <https://www.mcelog.org/>
|
- <https://www.mcelog.org/>
|
||||||
|
|
||||||
cpio
|
|
||||||
----
|
|
||||||
|
|
||||||
- <https://www.gnu.org/software/cpio/>
|
|
||||||
|
|
||||||
Networking
|
Networking
|
||||||
**********
|
**********
|
||||||
|
|
||||||
|
@ -9548,6 +9548,13 @@ W: https://linuxtv.org
|
|||||||
T: git git://linuxtv.org/media.git
|
T: git git://linuxtv.org/media.git
|
||||||
F: drivers/media/radio/radio-gemtek*
|
F: drivers/media/radio/radio-gemtek*
|
||||||
|
|
||||||
|
GENDWARFKSYMS
|
||||||
|
M: Sami Tolvanen <samitolvanen@google.com>
|
||||||
|
L: linux-modules@vger.kernel.org
|
||||||
|
L: linux-kbuild@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: scripts/gendwarfksyms/
|
||||||
|
|
||||||
GENERIC ARCHITECTURE TOPOLOGY
|
GENERIC ARCHITECTURE TOPOLOGY
|
||||||
M: Sudeep Holla <sudeep.holla@arm.com>
|
M: Sudeep Holla <sudeep.holla@arm.com>
|
||||||
L: linux-kernel@vger.kernel.org
|
L: linux-kernel@vger.kernel.org
|
||||||
|
@ -369,6 +369,24 @@ static void dedotify_versions(struct modversion_info *vers,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Same as normal versions, remove a leading dot if present. */
|
||||||
|
static void dedotify_ext_version_names(char *str_seq, unsigned long size)
|
||||||
|
{
|
||||||
|
unsigned long out = 0;
|
||||||
|
unsigned long in;
|
||||||
|
char last = '\0';
|
||||||
|
|
||||||
|
for (in = 0; in < size; in++) {
|
||||||
|
/* Skip one leading dot */
|
||||||
|
if (last == '\0' && str_seq[in] == '.')
|
||||||
|
in++;
|
||||||
|
last = str_seq[in];
|
||||||
|
str_seq[out++] = last;
|
||||||
|
}
|
||||||
|
/* Zero the trailing portion of the names table for robustness */
|
||||||
|
memset(&str_seq[out], 0, size - out);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Undefined symbols which refer to .funcname, hack to funcname. Make .TOC.
|
* Undefined symbols which refer to .funcname, hack to funcname. Make .TOC.
|
||||||
* seem to be defined (value set later).
|
* seem to be defined (value set later).
|
||||||
@ -438,10 +456,12 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
|
|||||||
me->arch.toc_section = i;
|
me->arch.toc_section = i;
|
||||||
if (sechdrs[i].sh_addralign < 8)
|
if (sechdrs[i].sh_addralign < 8)
|
||||||
sechdrs[i].sh_addralign = 8;
|
sechdrs[i].sh_addralign = 8;
|
||||||
}
|
} else if (strcmp(secstrings + sechdrs[i].sh_name, "__versions") == 0)
|
||||||
else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
|
|
||||||
dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
|
dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
|
||||||
sechdrs[i].sh_size);
|
sechdrs[i].sh_size);
|
||||||
|
else if (strcmp(secstrings + sechdrs[i].sh_name, "__version_ext_names") == 0)
|
||||||
|
dedotify_ext_version_names((void *)hdr + sechdrs[i].sh_offset,
|
||||||
|
sechdrs[i].sh_size);
|
||||||
|
|
||||||
if (sechdrs[i].sh_type == SHT_SYMTAB)
|
if (sechdrs[i].sh_type == SHT_SYMTAB)
|
||||||
dedotify((void *)hdr + sechdrs[i].sh_offset,
|
dedotify((void *)hdr + sechdrs[i].sh_offset,
|
||||||
|
@ -52,9 +52,24 @@
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
#ifdef CONFIG_GENDWARFKSYMS
|
||||||
|
/*
|
||||||
|
* With CONFIG_GENDWARFKSYMS, ensure the compiler emits debugging
|
||||||
|
* information for all exported symbols, including those defined in
|
||||||
|
* different TUs, by adding a __gendwarfksyms_ptr_<symbol> pointer
|
||||||
|
* that's discarded during the final link.
|
||||||
|
*/
|
||||||
|
#define __GENDWARFKSYMS_EXPORT(sym) \
|
||||||
|
static typeof(sym) *__gendwarfksyms_ptr_##sym __used \
|
||||||
|
__section(".discard.gendwarfksyms") = &sym;
|
||||||
|
#else
|
||||||
|
#define __GENDWARFKSYMS_EXPORT(sym)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define __EXPORT_SYMBOL(sym, license, ns) \
|
#define __EXPORT_SYMBOL(sym, license, ns) \
|
||||||
extern typeof(sym) sym; \
|
extern typeof(sym) sym; \
|
||||||
__ADDRESSABLE(sym) \
|
__ADDRESSABLE(sym) \
|
||||||
|
__GENDWARFKSYMS_EXPORT(sym) \
|
||||||
asm(__stringify(___EXPORT_SYMBOL(sym, license, ns)))
|
asm(__stringify(___EXPORT_SYMBOL(sym, license, ns)))
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -430,7 +430,7 @@ struct module {
|
|||||||
|
|
||||||
/* Exported symbols */
|
/* Exported symbols */
|
||||||
const struct kernel_symbol *syms;
|
const struct kernel_symbol *syms;
|
||||||
const s32 *crcs;
|
const u32 *crcs;
|
||||||
unsigned int num_syms;
|
unsigned int num_syms;
|
||||||
|
|
||||||
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
|
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
|
||||||
@ -448,7 +448,7 @@ struct module {
|
|||||||
/* GPL-only exported symbols. */
|
/* GPL-only exported symbols. */
|
||||||
unsigned int num_gpl_syms;
|
unsigned int num_gpl_syms;
|
||||||
const struct kernel_symbol *gpl_syms;
|
const struct kernel_symbol *gpl_syms;
|
||||||
const s32 *gpl_crcs;
|
const u32 *gpl_crcs;
|
||||||
bool using_gplonly_symbols;
|
bool using_gplonly_symbols;
|
||||||
|
|
||||||
#ifdef CONFIG_MODULE_SIG
|
#ifdef CONFIG_MODULE_SIG
|
||||||
|
@ -1959,7 +1959,8 @@ config RUST
|
|||||||
bool "Rust support"
|
bool "Rust support"
|
||||||
depends on HAVE_RUST
|
depends on HAVE_RUST
|
||||||
depends on RUST_IS_AVAILABLE
|
depends on RUST_IS_AVAILABLE
|
||||||
depends on !MODVERSIONS
|
select EXTENDED_MODVERSIONS if MODVERSIONS
|
||||||
|
depends on !MODVERSIONS || GENDWARFKSYMS
|
||||||
depends on !GCC_PLUGIN_RANDSTRUCT
|
depends on !GCC_PLUGIN_RANDSTRUCT
|
||||||
depends on !RANDSTRUCT
|
depends on !RANDSTRUCT
|
||||||
depends on !DEBUG_INFO_BTF || PAHOLE_HAS_LANG_EXCLUDE
|
depends on !DEBUG_INFO_BTF || PAHOLE_HAS_LANG_EXCLUDE
|
||||||
|
@ -7,20 +7,13 @@ set -e
|
|||||||
sfile="$(readlink -f "$0")"
|
sfile="$(readlink -f "$0")"
|
||||||
outdir="$(pwd)"
|
outdir="$(pwd)"
|
||||||
tarfile=$1
|
tarfile=$1
|
||||||
cpio_dir=$outdir/${tarfile%/*}/.tmp_cpio_dir
|
tmpdir=$outdir/${tarfile%/*}/.tmp_dir
|
||||||
|
|
||||||
dir_list="
|
dir_list="
|
||||||
include/
|
include/
|
||||||
arch/$SRCARCH/include/
|
arch/$SRCARCH/include/
|
||||||
"
|
"
|
||||||
|
|
||||||
if ! command -v cpio >/dev/null; then
|
|
||||||
echo >&2 "***"
|
|
||||||
echo >&2 "*** 'cpio' could not be found."
|
|
||||||
echo >&2 "***"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Support incremental builds by skipping archive generation
|
# Support incremental builds by skipping archive generation
|
||||||
# if timestamps of files being archived are not changed.
|
# if timestamps of files being archived are not changed.
|
||||||
|
|
||||||
@ -48,9 +41,9 @@ all_dirs="$all_dirs $dir_list"
|
|||||||
# check include/generated/autoconf.h explicitly.
|
# check include/generated/autoconf.h explicitly.
|
||||||
#
|
#
|
||||||
# Ignore them for md5 calculation to avoid pointless regeneration.
|
# Ignore them for md5 calculation to avoid pointless regeneration.
|
||||||
headers_md5="$(find $all_dirs -name "*.h" |
|
headers_md5="$(find $all_dirs -name "*.h" -a \
|
||||||
grep -v "include/generated/utsversion.h" |
|
! -path include/generated/utsversion.h -a \
|
||||||
grep -v "include/generated/autoconf.h" |
|
! -path include/generated/autoconf.h |
|
||||||
xargs ls -l | md5sum | cut -d ' ' -f1)"
|
xargs ls -l | md5sum | cut -d ' ' -f1)"
|
||||||
|
|
||||||
# Any changes to this script will also cause a rebuild of the archive.
|
# Any changes to this script will also cause a rebuild of the archive.
|
||||||
@ -65,36 +58,43 @@ fi
|
|||||||
|
|
||||||
echo " GEN $tarfile"
|
echo " GEN $tarfile"
|
||||||
|
|
||||||
rm -rf $cpio_dir
|
rm -rf "${tmpdir}"
|
||||||
mkdir $cpio_dir
|
mkdir "${tmpdir}"
|
||||||
|
|
||||||
if [ "$building_out_of_srctree" ]; then
|
if [ "$building_out_of_srctree" ]; then
|
||||||
(
|
(
|
||||||
cd $srctree
|
cd $srctree
|
||||||
for f in $dir_list
|
for f in $dir_list
|
||||||
do find "$f" -name "*.h";
|
do find "$f" -name "*.h";
|
||||||
done | cpio --quiet -pd $cpio_dir
|
done | tar -c -f - -T - | tar -xf - -C "${tmpdir}"
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# The second CPIO can complain if files already exist which can happen with out
|
|
||||||
# of tree builds having stale headers in srctree. Just silence CPIO for now.
|
|
||||||
for f in $dir_list;
|
for f in $dir_list;
|
||||||
do find "$f" -name "*.h";
|
do find "$f" -name "*.h";
|
||||||
done | cpio --quiet -pdu $cpio_dir >/dev/null 2>&1
|
done | tar -c -f - -T - | tar -xf - -C "${tmpdir}"
|
||||||
|
|
||||||
|
# Always exclude include/generated/utsversion.h
|
||||||
|
# Otherwise, the contents of the tarball may vary depending on the build steps.
|
||||||
|
rm -f "${tmpdir}/include/generated/utsversion.h"
|
||||||
|
|
||||||
# Remove comments except SDPX lines
|
# Remove comments except SDPX lines
|
||||||
find $cpio_dir -type f -print0 |
|
# Use a temporary file to store directory contents to prevent find/xargs from
|
||||||
xargs -0 -P8 -n1 perl -pi -e 'BEGIN {undef $/;}; s/\/\*((?!SPDX).)*?\*\///smg;'
|
# seeing temporary files created by perl.
|
||||||
|
find "${tmpdir}" -type f -print0 > "${tmpdir}.contents.txt"
|
||||||
|
xargs -0 -P8 -n1 \
|
||||||
|
perl -pi -e 'BEGIN {undef $/;}; s/\/\*((?!SPDX).)*?\*\///smg;' \
|
||||||
|
< "${tmpdir}.contents.txt"
|
||||||
|
rm -f "${tmpdir}.contents.txt"
|
||||||
|
|
||||||
# Create archive and try to normalize metadata for reproducibility.
|
# Create archive and try to normalize metadata for reproducibility.
|
||||||
tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
|
tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
|
||||||
--exclude=".__afs*" --exclude=".nfs*" \
|
--exclude=".__afs*" --exclude=".nfs*" \
|
||||||
--owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
|
--owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
|
||||||
-I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
|
-I $XZ -cf $tarfile -C "${tmpdir}/" . > /dev/null
|
||||||
|
|
||||||
echo $headers_md5 > kernel/kheaders.md5
|
echo $headers_md5 > kernel/kheaders.md5
|
||||||
echo "$this_file_md5" >> kernel/kheaders.md5
|
echo "$this_file_md5" >> kernel/kheaders.md5
|
||||||
echo "$(md5sum $tarfile | cut -d ' ' -f1)" >> kernel/kheaders.md5
|
echo "$(md5sum $tarfile | cut -d ' ' -f1)" >> kernel/kheaders.md5
|
||||||
|
|
||||||
rm -rf $cpio_dir
|
rm -rf "${tmpdir}"
|
||||||
|
@ -169,6 +169,36 @@ config MODVERSIONS
|
|||||||
make them incompatible with the kernel you are running. If
|
make them incompatible with the kernel you are running. If
|
||||||
unsure, say N.
|
unsure, say N.
|
||||||
|
|
||||||
|
choice
|
||||||
|
prompt "Module versioning implementation"
|
||||||
|
depends on MODVERSIONS
|
||||||
|
help
|
||||||
|
Select the tool used to calculate symbol versions for modules.
|
||||||
|
|
||||||
|
If unsure, select GENKSYMS.
|
||||||
|
|
||||||
|
config GENKSYMS
|
||||||
|
bool "genksyms (from source code)"
|
||||||
|
help
|
||||||
|
Calculate symbol versions from pre-processed source code using
|
||||||
|
genksyms.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config GENDWARFKSYMS
|
||||||
|
bool "gendwarfksyms (from debugging information)"
|
||||||
|
depends on DEBUG_INFO
|
||||||
|
# Requires full debugging information, split DWARF not supported.
|
||||||
|
depends on !DEBUG_INFO_REDUCED && !DEBUG_INFO_SPLIT
|
||||||
|
# Requires ELF object files.
|
||||||
|
depends on !LTO
|
||||||
|
help
|
||||||
|
Calculate symbol versions from DWARF debugging information using
|
||||||
|
gendwarfksyms. Requires DEBUG_INFO to be enabled.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
endchoice
|
||||||
|
|
||||||
config ASM_MODVERSIONS
|
config ASM_MODVERSIONS
|
||||||
bool
|
bool
|
||||||
default HAVE_ASM_MODVERSIONS && MODVERSIONS
|
default HAVE_ASM_MODVERSIONS && MODVERSIONS
|
||||||
@ -177,6 +207,31 @@ config ASM_MODVERSIONS
|
|||||||
assembly. This can be enabled only when the target architecture
|
assembly. This can be enabled only when the target architecture
|
||||||
supports it.
|
supports it.
|
||||||
|
|
||||||
|
config EXTENDED_MODVERSIONS
|
||||||
|
bool "Extended Module Versioning Support"
|
||||||
|
depends on MODVERSIONS
|
||||||
|
help
|
||||||
|
This enables extended MODVERSIONs support, allowing long symbol
|
||||||
|
names to be versioned.
|
||||||
|
|
||||||
|
The most likely reason you would enable this is to enable Rust
|
||||||
|
support. If unsure, say N.
|
||||||
|
|
||||||
|
config BASIC_MODVERSIONS
|
||||||
|
bool "Basic Module Versioning Support"
|
||||||
|
depends on MODVERSIONS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This enables basic MODVERSIONS support, allowing older tools or
|
||||||
|
kernels to potentially load modules.
|
||||||
|
|
||||||
|
Disabling this may cause older `modprobe` or `kmod` to be unable
|
||||||
|
to read MODVERSIONS information from built modules. With this
|
||||||
|
disabled, older kernels may treat this module as unversioned.
|
||||||
|
|
||||||
|
This is enabled by default when MODVERSIONS are enabled.
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
config MODULE_SRCVERSION_ALL
|
config MODULE_SRCVERSION_ALL
|
||||||
bool "Source checksum for all modules"
|
bool "Source checksum for all modules"
|
||||||
help
|
help
|
||||||
|
@ -55,8 +55,8 @@ extern const struct kernel_symbol __start___ksymtab[];
|
|||||||
extern const struct kernel_symbol __stop___ksymtab[];
|
extern const struct kernel_symbol __stop___ksymtab[];
|
||||||
extern const struct kernel_symbol __start___ksymtab_gpl[];
|
extern const struct kernel_symbol __start___ksymtab_gpl[];
|
||||||
extern const struct kernel_symbol __stop___ksymtab_gpl[];
|
extern const struct kernel_symbol __stop___ksymtab_gpl[];
|
||||||
extern const s32 __start___kcrctab[];
|
extern const u32 __start___kcrctab[];
|
||||||
extern const s32 __start___kcrctab_gpl[];
|
extern const u32 __start___kcrctab_gpl[];
|
||||||
|
|
||||||
struct load_info {
|
struct load_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -86,6 +86,8 @@ struct load_info {
|
|||||||
unsigned int vers;
|
unsigned int vers;
|
||||||
unsigned int info;
|
unsigned int info;
|
||||||
unsigned int pcpu;
|
unsigned int pcpu;
|
||||||
|
unsigned int vers_ext_crc;
|
||||||
|
unsigned int vers_ext_name;
|
||||||
} index;
|
} index;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,7 +104,7 @@ struct find_symbol_arg {
|
|||||||
|
|
||||||
/* Output */
|
/* Output */
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
const s32 *crc;
|
const u32 *crc;
|
||||||
const struct kernel_symbol *sym;
|
const struct kernel_symbol *sym;
|
||||||
enum mod_license license;
|
enum mod_license license;
|
||||||
};
|
};
|
||||||
@ -384,16 +386,25 @@ static inline void init_param_lock(struct module *mod) { }
|
|||||||
|
|
||||||
#ifdef CONFIG_MODVERSIONS
|
#ifdef CONFIG_MODVERSIONS
|
||||||
int check_version(const struct load_info *info,
|
int check_version(const struct load_info *info,
|
||||||
const char *symname, struct module *mod, const s32 *crc);
|
const char *symname, struct module *mod, const u32 *crc);
|
||||||
void module_layout(struct module *mod, struct modversion_info *ver, struct kernel_param *kp,
|
void module_layout(struct module *mod, struct modversion_info *ver, struct kernel_param *kp,
|
||||||
struct kernel_symbol *ks, struct tracepoint * const *tp);
|
struct kernel_symbol *ks, struct tracepoint * const *tp);
|
||||||
int check_modstruct_version(const struct load_info *info, struct module *mod);
|
int check_modstruct_version(const struct load_info *info, struct module *mod);
|
||||||
int same_magic(const char *amagic, const char *bmagic, bool has_crcs);
|
int same_magic(const char *amagic, const char *bmagic, bool has_crcs);
|
||||||
|
struct modversion_info_ext {
|
||||||
|
size_t remaining;
|
||||||
|
const u32 *crc;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
void modversion_ext_start(const struct load_info *info, struct modversion_info_ext *ver);
|
||||||
|
void modversion_ext_advance(struct modversion_info_ext *ver);
|
||||||
|
#define for_each_modversion_info_ext(ver, info) \
|
||||||
|
for (modversion_ext_start(info, &ver); ver.remaining > 0; modversion_ext_advance(&ver))
|
||||||
#else /* !CONFIG_MODVERSIONS */
|
#else /* !CONFIG_MODVERSIONS */
|
||||||
static inline int check_version(const struct load_info *info,
|
static inline int check_version(const struct load_info *info,
|
||||||
const char *symname,
|
const char *symname,
|
||||||
struct module *mod,
|
struct module *mod,
|
||||||
const s32 *crc)
|
const u32 *crc)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ struct mod_tree_root mod_tree __cacheline_aligned = {
|
|||||||
|
|
||||||
struct symsearch {
|
struct symsearch {
|
||||||
const struct kernel_symbol *start, *stop;
|
const struct kernel_symbol *start, *stop;
|
||||||
const s32 *crcs;
|
const u32 *crcs;
|
||||||
enum mod_license license;
|
enum mod_license license;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2073,6 +2073,82 @@ static int elf_validity_cache_index_str(struct load_info *info)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elf_validity_cache_index_versions() - Validate and cache version indices
|
||||||
|
* @info: Load info to cache version indices in.
|
||||||
|
* Must have &load_info->sechdrs and &load_info->secstrings populated.
|
||||||
|
* @flags: Load flags, relevant to suppress version loading, see
|
||||||
|
* uapi/linux/module.h
|
||||||
|
*
|
||||||
|
* If we're ignoring modversions based on @flags, zero all version indices
|
||||||
|
* and return validity. Othewrise check:
|
||||||
|
*
|
||||||
|
* * If "__version_ext_crcs" is present, "__version_ext_names" is present
|
||||||
|
* * There is a name present for every crc
|
||||||
|
*
|
||||||
|
* Then populate:
|
||||||
|
*
|
||||||
|
* * &load_info->index.vers
|
||||||
|
* * &load_info->index.vers_ext_crc
|
||||||
|
* * &load_info->index.vers_ext_names
|
||||||
|
*
|
||||||
|
* if present.
|
||||||
|
*
|
||||||
|
* Return: %0 if valid, %-ENOEXEC on failure.
|
||||||
|
*/
|
||||||
|
static int elf_validity_cache_index_versions(struct load_info *info, int flags)
|
||||||
|
{
|
||||||
|
unsigned int vers_ext_crc;
|
||||||
|
unsigned int vers_ext_name;
|
||||||
|
size_t crc_count;
|
||||||
|
size_t remaining_len;
|
||||||
|
size_t name_size;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
/* If modversions were suppressed, pretend we didn't find any */
|
||||||
|
if (flags & MODULE_INIT_IGNORE_MODVERSIONS) {
|
||||||
|
info->index.vers = 0;
|
||||||
|
info->index.vers_ext_crc = 0;
|
||||||
|
info->index.vers_ext_name = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vers_ext_crc = find_sec(info, "__version_ext_crcs");
|
||||||
|
vers_ext_name = find_sec(info, "__version_ext_names");
|
||||||
|
|
||||||
|
/* If we have one field, we must have the other */
|
||||||
|
if (!!vers_ext_crc != !!vers_ext_name) {
|
||||||
|
pr_err("extended version crc+name presence does not match");
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have extended version information, we should have the same
|
||||||
|
* number of entries in every section.
|
||||||
|
*/
|
||||||
|
if (vers_ext_crc) {
|
||||||
|
crc_count = info->sechdrs[vers_ext_crc].sh_size / sizeof(u32);
|
||||||
|
name = (void *)info->hdr +
|
||||||
|
info->sechdrs[vers_ext_name].sh_offset;
|
||||||
|
remaining_len = info->sechdrs[vers_ext_name].sh_size;
|
||||||
|
|
||||||
|
while (crc_count--) {
|
||||||
|
name_size = strnlen(name, remaining_len) + 1;
|
||||||
|
if (name_size > remaining_len) {
|
||||||
|
pr_err("more extended version crcs than names");
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
remaining_len -= name_size;
|
||||||
|
name += name_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info->index.vers = find_sec(info, "__versions");
|
||||||
|
info->index.vers_ext_crc = vers_ext_crc;
|
||||||
|
info->index.vers_ext_name = vers_ext_name;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* elf_validity_cache_index() - Resolve, validate, cache section indices
|
* elf_validity_cache_index() - Resolve, validate, cache section indices
|
||||||
* @info: Load info to read from and update.
|
* @info: Load info to read from and update.
|
||||||
@ -2087,9 +2163,7 @@ static int elf_validity_cache_index_str(struct load_info *info)
|
|||||||
* * elf_validity_cache_index_mod()
|
* * elf_validity_cache_index_mod()
|
||||||
* * elf_validity_cache_index_sym()
|
* * elf_validity_cache_index_sym()
|
||||||
* * elf_validity_cache_index_str()
|
* * elf_validity_cache_index_str()
|
||||||
*
|
* * elf_validity_cache_index_versions()
|
||||||
* If versioning is not suppressed via flags, load the version index from
|
|
||||||
* a section called "__versions" with no validation.
|
|
||||||
*
|
*
|
||||||
* If CONFIG_SMP is enabled, load the percpu section by name with no
|
* If CONFIG_SMP is enabled, load the percpu section by name with no
|
||||||
* validation.
|
* validation.
|
||||||
@ -2112,11 +2186,9 @@ static int elf_validity_cache_index(struct load_info *info, int flags)
|
|||||||
err = elf_validity_cache_index_str(info);
|
err = elf_validity_cache_index_str(info);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
err = elf_validity_cache_index_versions(info, flags);
|
||||||
if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
|
if (err < 0)
|
||||||
info->index.vers = 0; /* Pretend no __versions section! */
|
return err;
|
||||||
else
|
|
||||||
info->index.vers = find_sec(info, "__versions");
|
|
||||||
|
|
||||||
info->index.pcpu = find_pcpusec(info);
|
info->index.pcpu = find_pcpusec(info);
|
||||||
|
|
||||||
@ -2327,6 +2399,10 @@ static int rewrite_section_headers(struct load_info *info, int flags)
|
|||||||
|
|
||||||
/* Track but don't keep modinfo and version sections. */
|
/* Track but don't keep modinfo and version sections. */
|
||||||
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||||
|
info->sechdrs[info->index.vers_ext_crc].sh_flags &=
|
||||||
|
~(unsigned long)SHF_ALLOC;
|
||||||
|
info->sechdrs[info->index.vers_ext_name].sh_flags &=
|
||||||
|
~(unsigned long)SHF_ALLOC;
|
||||||
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -13,17 +13,34 @@
|
|||||||
int check_version(const struct load_info *info,
|
int check_version(const struct load_info *info,
|
||||||
const char *symname,
|
const char *symname,
|
||||||
struct module *mod,
|
struct module *mod,
|
||||||
const s32 *crc)
|
const u32 *crc)
|
||||||
{
|
{
|
||||||
Elf_Shdr *sechdrs = info->sechdrs;
|
Elf_Shdr *sechdrs = info->sechdrs;
|
||||||
unsigned int versindex = info->index.vers;
|
unsigned int versindex = info->index.vers;
|
||||||
unsigned int i, num_versions;
|
unsigned int i, num_versions;
|
||||||
struct modversion_info *versions;
|
struct modversion_info *versions;
|
||||||
|
struct modversion_info_ext version_ext;
|
||||||
|
|
||||||
/* Exporting module didn't supply crcs? OK, we're already tainted. */
|
/* Exporting module didn't supply crcs? OK, we're already tainted. */
|
||||||
if (!crc)
|
if (!crc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
/* If we have extended version info, rely on it */
|
||||||
|
if (info->index.vers_ext_crc) {
|
||||||
|
for_each_modversion_info_ext(version_ext, info) {
|
||||||
|
if (strcmp(version_ext.name, symname) != 0)
|
||||||
|
continue;
|
||||||
|
if (*version_ext.crc == *crc)
|
||||||
|
return 1;
|
||||||
|
pr_debug("Found checksum %X vs module %X\n",
|
||||||
|
*crc, *version_ext.crc);
|
||||||
|
goto bad_version;
|
||||||
|
}
|
||||||
|
pr_warn_once("%s: no extended symbol version for %s\n",
|
||||||
|
info->name, symname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* No versions at all? modprobe --force does this. */
|
/* No versions at all? modprobe --force does this. */
|
||||||
if (versindex == 0)
|
if (versindex == 0)
|
||||||
return try_to_force_load(mod, symname) == 0;
|
return try_to_force_load(mod, symname) == 0;
|
||||||
@ -87,6 +104,34 @@ int same_magic(const char *amagic, const char *bmagic,
|
|||||||
return strcmp(amagic, bmagic) == 0;
|
return strcmp(amagic, bmagic) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void modversion_ext_start(const struct load_info *info,
|
||||||
|
struct modversion_info_ext *start)
|
||||||
|
{
|
||||||
|
unsigned int crc_idx = info->index.vers_ext_crc;
|
||||||
|
unsigned int name_idx = info->index.vers_ext_name;
|
||||||
|
Elf_Shdr *sechdrs = info->sechdrs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Both of these fields are needed for this to be useful
|
||||||
|
* Any future fields should be initialized to NULL if absent.
|
||||||
|
*/
|
||||||
|
if (crc_idx == 0 || name_idx == 0) {
|
||||||
|
start->remaining = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start->crc = (const u32 *)sechdrs[crc_idx].sh_addr;
|
||||||
|
start->name = (const char *)sechdrs[name_idx].sh_addr;
|
||||||
|
start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void modversion_ext_advance(struct modversion_info_ext *vers)
|
||||||
|
{
|
||||||
|
vers->remaining--;
|
||||||
|
vers->crc++;
|
||||||
|
vers->name += strlen(vers->name) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate the signature for all relevant module structures here.
|
* Generate the signature for all relevant module structures here.
|
||||||
* If these change, we don't want to try to parse the module.
|
* If these change, we don't want to try to parse the module.
|
||||||
|
@ -329,10 +329,11 @@ $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ;
|
|||||||
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
|
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
|
||||||
$(call if_changed_dep,bindgen)
|
$(call if_changed_dep,bindgen)
|
||||||
|
|
||||||
|
rust_exports = $(NM) -p --defined-only $(1) | awk '$$2~/(T|R|D|B)/ && $$3!~/__cfi/ { printf $(2),$(3) }'
|
||||||
|
|
||||||
quiet_cmd_exports = EXPORTS $@
|
quiet_cmd_exports = EXPORTS $@
|
||||||
cmd_exports = \
|
cmd_exports = \
|
||||||
$(NM) -p --defined-only $< \
|
$(call rust_exports,$<,"EXPORT_SYMBOL_RUST_GPL(%s);\n",$$3) > $@
|
||||||
| awk '$$2~/(T|R|D|B)/ && $$3!~/__cfi/ {printf "EXPORT_SYMBOL_RUST_GPL(%s);\n",$$3}' > $@
|
|
||||||
|
|
||||||
$(obj)/exports_core_generated.h: $(obj)/core.o FORCE
|
$(obj)/exports_core_generated.h: $(obj)/core.o FORCE
|
||||||
$(call if_changed,exports)
|
$(call if_changed,exports)
|
||||||
@ -401,11 +402,36 @@ ifneq ($(or $(CONFIG_ARM64),$(and $(CONFIG_RISCV),$(CONFIG_64BIT))),)
|
|||||||
__ashlti3 __lshrti3
|
__ashlti3 __lshrti3
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifdef CONFIG_MODVERSIONS
|
||||||
|
cmd_gendwarfksyms = $(if $(skip_gendwarfksyms),, \
|
||||||
|
$(call rust_exports,$@,"%s\n",$$3) | \
|
||||||
|
scripts/gendwarfksyms/gendwarfksyms \
|
||||||
|
$(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable) \
|
||||||
|
$(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes),) \
|
||||||
|
$@ >> $(dot-target).cmd)
|
||||||
|
endif
|
||||||
|
|
||||||
define rule_rustc_library
|
define rule_rustc_library
|
||||||
$(call cmd_and_fixdep,rustc_library)
|
$(call cmd_and_fixdep,rustc_library)
|
||||||
$(call cmd,gen_objtooldep)
|
$(call cmd,gen_objtooldep)
|
||||||
|
$(call cmd,gendwarfksyms)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
define rule_rust_cc_library
|
||||||
|
$(call if_changed_rule,cc_o_c)
|
||||||
|
$(call cmd,force_checksrc)
|
||||||
|
$(call cmd,gendwarfksyms)
|
||||||
|
endef
|
||||||
|
|
||||||
|
# helpers.o uses the same export mechanism as Rust libraries, so ensure symbol
|
||||||
|
# versions are calculated for the helpers too.
|
||||||
|
$(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(recordmcount_source) FORCE
|
||||||
|
+$(call if_changed_rule,rust_cc_library)
|
||||||
|
|
||||||
|
# Disable symbol versioning for exports.o to avoid conflicts with the actual
|
||||||
|
# symbol versions generated from Rust objects.
|
||||||
|
$(obj)/exports.o: private skip_gendwarfksyms = 1
|
||||||
|
|
||||||
$(obj)/core.o: private skip_clippy = 1
|
$(obj)/core.o: private skip_clippy = 1
|
||||||
$(obj)/core.o: private skip_flags = -Wunreachable_pub
|
$(obj)/core.o: private skip_flags = -Wunreachable_pub
|
||||||
$(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
|
$(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
|
||||||
@ -417,13 +443,16 @@ ifneq ($(or $(CONFIG_X86_64),$(CONFIG_X86_32)),)
|
|||||||
$(obj)/core.o: scripts/target.json
|
$(obj)/core.o: scripts/target.json
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
$(obj)/compiler_builtins.o: private skip_gendwarfksyms = 1
|
||||||
$(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*'
|
$(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*'
|
||||||
$(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE
|
$(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE
|
||||||
+$(call if_changed_rule,rustc_library)
|
+$(call if_changed_rule,rustc_library)
|
||||||
|
|
||||||
|
$(obj)/build_error.o: private skip_gendwarfksyms = 1
|
||||||
$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE
|
$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE
|
||||||
+$(call if_changed_rule,rustc_library)
|
+$(call if_changed_rule,rustc_library)
|
||||||
|
|
||||||
|
$(obj)/ffi.o: private skip_gendwarfksyms = 1
|
||||||
$(obj)/ffi.o: $(src)/ffi.rs $(obj)/compiler_builtins.o FORCE
|
$(obj)/ffi.o: $(src)/ffi.rs $(obj)/compiler_builtins.o FORCE
|
||||||
+$(call if_changed_rule,rustc_library)
|
+$(call if_changed_rule,rustc_library)
|
||||||
|
|
||||||
@ -435,6 +464,7 @@ $(obj)/bindings.o: $(src)/bindings/lib.rs \
|
|||||||
+$(call if_changed_rule,rustc_library)
|
+$(call if_changed_rule,rustc_library)
|
||||||
|
|
||||||
$(obj)/uapi.o: private rustc_target_flags = --extern ffi
|
$(obj)/uapi.o: private rustc_target_flags = --extern ffi
|
||||||
|
$(obj)/uapi.o: private skip_gendwarfksyms = 1
|
||||||
$(obj)/uapi.o: $(src)/uapi/lib.rs \
|
$(obj)/uapi.o: $(src)/uapi/lib.rs \
|
||||||
$(obj)/ffi.o \
|
$(obj)/ffi.o \
|
||||||
$(obj)/uapi/uapi_generated.rs FORCE
|
$(obj)/uapi/uapi_generated.rs FORCE
|
||||||
|
@ -53,7 +53,8 @@ hostprogs += unifdef
|
|||||||
targets += module.lds
|
targets += module.lds
|
||||||
|
|
||||||
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
|
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
|
||||||
subdir-$(CONFIG_MODVERSIONS) += genksyms
|
subdir-$(CONFIG_GENKSYMS) += genksyms
|
||||||
|
subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
|
||||||
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
|
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
|
||||||
subdir-$(CONFIG_SECURITY_IPE) += ipe
|
subdir-$(CONFIG_SECURITY_IPE) += ipe
|
||||||
|
|
||||||
|
@ -107,13 +107,24 @@ cmd_cpp_i_c = $(CPP) $(c_flags) -o $@ $<
|
|||||||
$(obj)/%.i: $(obj)/%.c FORCE
|
$(obj)/%.i: $(obj)/%.c FORCE
|
||||||
$(call if_changed_dep,cpp_i_c)
|
$(call if_changed_dep,cpp_i_c)
|
||||||
|
|
||||||
|
getexportsymbols = $(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/$(1)/p'
|
||||||
|
|
||||||
|
gendwarfksyms = $(objtree)/scripts/gendwarfksyms/gendwarfksyms \
|
||||||
|
$(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes)) \
|
||||||
|
$(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable)
|
||||||
|
|
||||||
genksyms = $(objtree)/scripts/genksyms/genksyms \
|
genksyms = $(objtree)/scripts/genksyms/genksyms \
|
||||||
$(if $(KBUILD_SYMTYPES), -T $(@:.o=.symtypes)) \
|
$(if $(KBUILD_SYMTYPES), -T $(@:.o=.symtypes)) \
|
||||||
$(if $(KBUILD_PRESERVE), -p) \
|
$(if $(KBUILD_PRESERVE), -p) \
|
||||||
$(addprefix -r , $(wildcard $(@:.o=.symref)))
|
$(addprefix -r , $(wildcard $(@:.o=.symref)))
|
||||||
|
|
||||||
# These mirror gensymtypes_S and co below, keep them in synch.
|
# These mirror gensymtypes_S and co below, keep them in synch.
|
||||||
|
ifdef CONFIG_GENDWARFKSYMS
|
||||||
|
cmd_gensymtypes_c = $(if $(skip_gendwarfksyms),, \
|
||||||
|
$(call getexportsymbols,\1) | $(gendwarfksyms) $@)
|
||||||
|
else
|
||||||
cmd_gensymtypes_c = $(CPP) -D__GENKSYMS__ $(c_flags) $< | $(genksyms)
|
cmd_gensymtypes_c = $(CPP) -D__GENKSYMS__ $(c_flags) $< | $(genksyms)
|
||||||
|
endif # CONFIG_GENDWARFKSYMS
|
||||||
|
|
||||||
# LLVM assembly
|
# LLVM assembly
|
||||||
# Generate .ll files from .c
|
# Generate .ll files from .c
|
||||||
@ -286,14 +297,26 @@ $(obj)/%.rs: $(obj)/%.rs.S FORCE
|
|||||||
# This is convoluted. The .S file must first be preprocessed to run guards and
|
# This is convoluted. The .S file must first be preprocessed to run guards and
|
||||||
# expand names, then the resulting exports must be constructed into plain
|
# expand names, then the resulting exports must be constructed into plain
|
||||||
# EXPORT_SYMBOL(symbol); to build our dummy C file, and that gets preprocessed
|
# EXPORT_SYMBOL(symbol); to build our dummy C file, and that gets preprocessed
|
||||||
# to make the genksyms input.
|
# to make the genksyms input or compiled into an object for gendwarfksyms.
|
||||||
#
|
#
|
||||||
# These mirror gensymtypes_c and co above, keep them in synch.
|
# These mirror gensymtypes_c and co above, keep them in synch.
|
||||||
cmd_gensymtypes_S = \
|
getasmexports = \
|
||||||
{ echo "\#include <linux/kernel.h>" ; \
|
{ echo "\#include <linux/kernel.h>" ; \
|
||||||
echo "\#include <asm/asm-prototypes.h>" ; \
|
echo "\#include <linux/string.h>" ; \
|
||||||
$(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/EXPORT_SYMBOL(\1);/p' ; } | \
|
echo "\#include <asm/asm-prototypes.h>" ; \
|
||||||
$(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
|
$(call getexportsymbols,EXPORT_SYMBOL(\1);) ; }
|
||||||
|
|
||||||
|
ifdef CONFIG_GENDWARFKSYMS
|
||||||
|
cmd_gensymtypes_S = \
|
||||||
|
$(getasmexports) | \
|
||||||
|
$(CC) $(c_flags) -c -o $(@:.o=.gendwarfksyms.o) -xc -; \
|
||||||
|
$(call getexportsymbols,\1) | \
|
||||||
|
$(gendwarfksyms) $(@:.o=.gendwarfksyms.o)
|
||||||
|
else
|
||||||
|
cmd_gensymtypes_S = \
|
||||||
|
$(getasmexports) | \
|
||||||
|
$(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
|
||||||
|
endif # CONFIG_GENDWARFKSYMS
|
||||||
|
|
||||||
quiet_cmd_cpp_s_S = CPP $(quiet_modtag) $@
|
quiet_cmd_cpp_s_S = CPP $(quiet_modtag) $@
|
||||||
cmd_cpp_s_S = $(CPP) $(a_flags) -o $@ $<
|
cmd_cpp_s_S = $(CPP) $(a_flags) -o $@ $<
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
# Configuration heplers
|
# Configuration heplers
|
||||||
|
|
||||||
|
cmd_merge_fragments = \
|
||||||
|
$(srctree)/scripts/kconfig/merge_config.sh \
|
||||||
|
$4 -m -O $(objtree) $(srctree)/arch/$(SRCARCH)/configs/$2 \
|
||||||
|
$(foreach config,$3,$(srctree)/arch/$(SRCARCH)/configs/$(config).config)
|
||||||
|
|
||||||
# Creates 'merged defconfigs'
|
# Creates 'merged defconfigs'
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Usage:
|
# Usage:
|
||||||
@ -8,9 +13,7 @@
|
|||||||
#
|
#
|
||||||
# Input config fragments without '.config' suffix
|
# Input config fragments without '.config' suffix
|
||||||
define merge_into_defconfig
|
define merge_into_defconfig
|
||||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
|
$(call cmd,merge_fragments,$1,$2)
|
||||||
-m -O $(objtree) $(srctree)/arch/$(SRCARCH)/configs/$(1) \
|
|
||||||
$(foreach config,$(2),$(srctree)/arch/$(SRCARCH)/configs/$(config).config)
|
|
||||||
+$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
|
+$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
|
||||||
endef
|
endef
|
||||||
|
|
||||||
@ -22,8 +25,6 @@ endef
|
|||||||
#
|
#
|
||||||
# Input config fragments without '.config' suffix
|
# Input config fragments without '.config' suffix
|
||||||
define merge_into_defconfig_override
|
define merge_into_defconfig_override
|
||||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
|
$(call cmd,merge_fragments,$1,$2,-Q)
|
||||||
-Q -m -O $(objtree) $(srctree)/arch/$(SRCARCH)/configs/$(1) \
|
|
||||||
$(foreach config,$(2),$(srctree)/arch/$(SRCARCH)/configs/$(config).config)
|
|
||||||
+$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
|
+$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
|
||||||
endef
|
endef
|
||||||
|
@ -43,6 +43,8 @@ MODPOST = $(objtree)/scripts/mod/modpost
|
|||||||
modpost-args = \
|
modpost-args = \
|
||||||
$(if $(CONFIG_MODULES),-M) \
|
$(if $(CONFIG_MODULES),-M) \
|
||||||
$(if $(CONFIG_MODVERSIONS),-m) \
|
$(if $(CONFIG_MODVERSIONS),-m) \
|
||||||
|
$(if $(CONFIG_BASIC_MODVERSIONS),-b) \
|
||||||
|
$(if $(CONFIG_EXTENDED_MODVERSIONS),-x) \
|
||||||
$(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \
|
$(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \
|
||||||
$(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \
|
$(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \
|
||||||
$(if $(KBUILD_MODPOST_WARN),-w) \
|
$(if $(KBUILD_MODPOST_WARN),-w) \
|
||||||
|
2
scripts/gendwarfksyms/.gitignore
vendored
Normal file
2
scripts/gendwarfksyms/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
/gendwarfksyms
|
12
scripts/gendwarfksyms/Makefile
Normal file
12
scripts/gendwarfksyms/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
hostprogs-always-y += gendwarfksyms
|
||||||
|
|
||||||
|
gendwarfksyms-objs += gendwarfksyms.o
|
||||||
|
gendwarfksyms-objs += cache.o
|
||||||
|
gendwarfksyms-objs += die.o
|
||||||
|
gendwarfksyms-objs += dwarf.o
|
||||||
|
gendwarfksyms-objs += kabi.o
|
||||||
|
gendwarfksyms-objs += symbols.o
|
||||||
|
gendwarfksyms-objs += types.o
|
||||||
|
|
||||||
|
HOSTLDLIBS_gendwarfksyms := -ldw -lelf -lz
|
51
scripts/gendwarfksyms/cache.c
Normal file
51
scripts/gendwarfksyms/cache.c
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gendwarfksyms.h"
|
||||||
|
|
||||||
|
struct cache_item {
|
||||||
|
unsigned long key;
|
||||||
|
int value;
|
||||||
|
struct hlist_node hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cache_set(struct cache *cache, unsigned long key, int value)
|
||||||
|
{
|
||||||
|
struct cache_item *ci;
|
||||||
|
|
||||||
|
ci = xmalloc(sizeof(struct cache_item));
|
||||||
|
ci->key = key;
|
||||||
|
ci->value = value;
|
||||||
|
hash_add(cache->cache, &ci->hash, hash_32(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
int cache_get(struct cache *cache, unsigned long key)
|
||||||
|
{
|
||||||
|
struct cache_item *ci;
|
||||||
|
|
||||||
|
hash_for_each_possible(cache->cache, ci, hash, hash_32(key)) {
|
||||||
|
if (ci->key == key)
|
||||||
|
return ci->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cache_init(struct cache *cache)
|
||||||
|
{
|
||||||
|
hash_init(cache->cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cache_free(struct cache *cache)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct cache_item *ci;
|
||||||
|
|
||||||
|
hash_for_each_safe(cache->cache, ci, tmp, hash) {
|
||||||
|
free(ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_init(cache->cache);
|
||||||
|
}
|
166
scripts/gendwarfksyms/die.c
Normal file
166
scripts/gendwarfksyms/die.c
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "gendwarfksyms.h"
|
||||||
|
|
||||||
|
#define DIE_HASH_BITS 15
|
||||||
|
|
||||||
|
/* {die->addr, state} -> struct die * */
|
||||||
|
static HASHTABLE_DEFINE(die_map, 1 << DIE_HASH_BITS);
|
||||||
|
|
||||||
|
static unsigned int map_hits;
|
||||||
|
static unsigned int map_misses;
|
||||||
|
|
||||||
|
static inline unsigned int die_hash(uintptr_t addr, enum die_state state)
|
||||||
|
{
|
||||||
|
return hash_32(addr_hash(addr) ^ (unsigned int)state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_die(struct die *cd)
|
||||||
|
{
|
||||||
|
cd->state = DIE_INCOMPLETE;
|
||||||
|
cd->mapped = false;
|
||||||
|
cd->fqn = NULL;
|
||||||
|
cd->tag = -1;
|
||||||
|
cd->addr = 0;
|
||||||
|
INIT_LIST_HEAD(&cd->fragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct die *create_die(Dwarf_Die *die, enum die_state state)
|
||||||
|
{
|
||||||
|
struct die *cd;
|
||||||
|
|
||||||
|
cd = xmalloc(sizeof(struct die));
|
||||||
|
init_die(cd);
|
||||||
|
cd->addr = (uintptr_t)die->addr;
|
||||||
|
|
||||||
|
hash_add(die_map, &cd->hash, die_hash(cd->addr, state));
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __die_map_get(uintptr_t addr, enum die_state state, struct die **res)
|
||||||
|
{
|
||||||
|
struct die *cd;
|
||||||
|
|
||||||
|
hash_for_each_possible(die_map, cd, hash, die_hash(addr, state)) {
|
||||||
|
if (cd->addr == addr && cd->state == state) {
|
||||||
|
*res = cd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct die *die_map_get(Dwarf_Die *die, enum die_state state)
|
||||||
|
{
|
||||||
|
struct die *cd;
|
||||||
|
|
||||||
|
if (__die_map_get((uintptr_t)die->addr, state, &cd) == 0) {
|
||||||
|
map_hits++;
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_misses++;
|
||||||
|
return create_die(die, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_die(struct die *cd)
|
||||||
|
{
|
||||||
|
struct die_fragment *tmp;
|
||||||
|
struct die_fragment *df;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(df, tmp, &cd->fragments, list) {
|
||||||
|
if (df->type == FRAGMENT_STRING)
|
||||||
|
free(df->data.str);
|
||||||
|
free(df);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cd->fqn && *cd->fqn)
|
||||||
|
free(cd->fqn);
|
||||||
|
init_die(cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void die_map_for_each(die_map_callback_t func, void *arg)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct die *cd;
|
||||||
|
|
||||||
|
hash_for_each_safe(die_map, cd, tmp, hash) {
|
||||||
|
func(cd, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void die_map_free(void)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
unsigned int stats[DIE_LAST + 1];
|
||||||
|
struct die *cd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(stats, 0, sizeof(stats));
|
||||||
|
|
||||||
|
hash_for_each_safe(die_map, cd, tmp, hash) {
|
||||||
|
stats[cd->state]++;
|
||||||
|
reset_die(cd);
|
||||||
|
free(cd);
|
||||||
|
}
|
||||||
|
hash_init(die_map);
|
||||||
|
|
||||||
|
if (map_hits + map_misses > 0)
|
||||||
|
debug("hits %u, misses %u (hit rate %.02f%%)", map_hits,
|
||||||
|
map_misses,
|
||||||
|
(100.0f * map_hits) / (map_hits + map_misses));
|
||||||
|
|
||||||
|
for (i = 0; i <= DIE_LAST; i++)
|
||||||
|
debug("%s: %u entries", die_state_name(i), stats[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct die_fragment *append_item(struct die *cd)
|
||||||
|
{
|
||||||
|
struct die_fragment *df;
|
||||||
|
|
||||||
|
df = xmalloc(sizeof(struct die_fragment));
|
||||||
|
df->type = FRAGMENT_EMPTY;
|
||||||
|
list_add_tail(&df->list, &cd->fragments);
|
||||||
|
return df;
|
||||||
|
}
|
||||||
|
|
||||||
|
void die_map_add_string(struct die *cd, const char *str)
|
||||||
|
{
|
||||||
|
struct die_fragment *df;
|
||||||
|
|
||||||
|
if (!cd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
df = append_item(cd);
|
||||||
|
df->data.str = xstrdup(str);
|
||||||
|
df->type = FRAGMENT_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void die_map_add_linebreak(struct die *cd, int linebreak)
|
||||||
|
{
|
||||||
|
struct die_fragment *df;
|
||||||
|
|
||||||
|
if (!cd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
df = append_item(cd);
|
||||||
|
df->data.linebreak = linebreak;
|
||||||
|
df->type = FRAGMENT_LINEBREAK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void die_map_add_die(struct die *cd, struct die *child)
|
||||||
|
{
|
||||||
|
struct die_fragment *df;
|
||||||
|
|
||||||
|
if (!cd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
df = append_item(cd);
|
||||||
|
df->data.addr = child->addr;
|
||||||
|
df->type = FRAGMENT_DIE;
|
||||||
|
}
|
1159
scripts/gendwarfksyms/dwarf.c
Normal file
1159
scripts/gendwarfksyms/dwarf.c
Normal file
File diff suppressed because it is too large
Load Diff
157
scripts/gendwarfksyms/examples/kabi.h
Normal file
157
scripts/gendwarfksyms/examples/kabi.h
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*
|
||||||
|
* Example macros for maintaining kABI stability.
|
||||||
|
*
|
||||||
|
* This file is based on android_kabi.h, which has the following notice:
|
||||||
|
*
|
||||||
|
* Heavily influenced by rh_kabi.h which came from the RHEL/CENTOS kernel
|
||||||
|
* and was:
|
||||||
|
* Copyright (c) 2014 Don Zickus
|
||||||
|
* Copyright (c) 2015-2018 Jiri Benc
|
||||||
|
* Copyright (c) 2015 Sabrina Dubroca, Hannes Frederic Sowa
|
||||||
|
* Copyright (c) 2016-2018 Prarit Bhargava
|
||||||
|
* Copyright (c) 2017 Paolo Abeni, Larry Woodman
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __KABI_H__
|
||||||
|
#define __KABI_H__
|
||||||
|
|
||||||
|
/* Kernel macros for userspace testing. */
|
||||||
|
#ifndef __aligned
|
||||||
|
#define __aligned(x) __attribute__((__aligned__(x)))
|
||||||
|
#endif
|
||||||
|
#ifndef __used
|
||||||
|
#define __used __attribute__((__used__))
|
||||||
|
#endif
|
||||||
|
#ifndef __section
|
||||||
|
#define __section(section) __attribute__((__section__(section)))
|
||||||
|
#endif
|
||||||
|
#ifndef __PASTE
|
||||||
|
#define ___PASTE(a, b) a##b
|
||||||
|
#define __PASTE(a, b) ___PASTE(a, b)
|
||||||
|
#endif
|
||||||
|
#ifndef __stringify
|
||||||
|
#define __stringify_1(x...) #x
|
||||||
|
#define __stringify(x...) __stringify_1(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __KABI_RULE(hint, target, value) \
|
||||||
|
static const char __PASTE(__gendwarfksyms_rule_, \
|
||||||
|
__COUNTER__)[] __used __aligned(1) \
|
||||||
|
__section(".discard.gendwarfksyms.kabi_rules") = \
|
||||||
|
"1\0" #hint "\0" #target "\0" #value
|
||||||
|
|
||||||
|
#define __KABI_NORMAL_SIZE_ALIGN(_orig, _new) \
|
||||||
|
union { \
|
||||||
|
_Static_assert( \
|
||||||
|
sizeof(struct { _new; }) <= sizeof(struct { _orig; }), \
|
||||||
|
__FILE__ ":" __stringify(__LINE__) ": " __stringify( \
|
||||||
|
_new) " is larger than " __stringify(_orig)); \
|
||||||
|
_Static_assert( \
|
||||||
|
__alignof__(struct { _new; }) <= \
|
||||||
|
__alignof__(struct { _orig; }), \
|
||||||
|
__FILE__ ":" __stringify(__LINE__) ": " __stringify( \
|
||||||
|
_orig) " is not aligned the same as " __stringify(_new)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define __KABI_REPLACE(_orig, _new) \
|
||||||
|
union { \
|
||||||
|
_new; \
|
||||||
|
struct { \
|
||||||
|
_orig; \
|
||||||
|
}; \
|
||||||
|
__KABI_NORMAL_SIZE_ALIGN(_orig, _new); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_DECLONLY(fqn)
|
||||||
|
* Treat the struct/union/enum fqn as a declaration, i.e. even if
|
||||||
|
* a definition is available, don't expand the contents.
|
||||||
|
*/
|
||||||
|
#define KABI_DECLONLY(fqn) __KABI_RULE(declonly, fqn, )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_ENUMERATOR_IGNORE(fqn, field)
|
||||||
|
* When expanding enum fqn, skip the provided field. This makes it
|
||||||
|
* possible to hide added enum fields from versioning.
|
||||||
|
*/
|
||||||
|
#define KABI_ENUMERATOR_IGNORE(fqn, field) \
|
||||||
|
__KABI_RULE(enumerator_ignore, fqn field, )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_ENUMERATOR_VALUE(fqn, field, value)
|
||||||
|
* When expanding enum fqn, use the provided value for the
|
||||||
|
* specified field. This makes it possible to override enumerator
|
||||||
|
* values when calculating versions.
|
||||||
|
*/
|
||||||
|
#define KABI_ENUMERATOR_VALUE(fqn, field, value) \
|
||||||
|
__KABI_RULE(enumerator_value, fqn field, value)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_RESERVE
|
||||||
|
* Reserve some "padding" in a structure for use by LTS backports.
|
||||||
|
* This is normally placed at the end of a structure.
|
||||||
|
* number: the "number" of the padding variable in the structure. Start with
|
||||||
|
* 1 and go up.
|
||||||
|
*/
|
||||||
|
#define KABI_RESERVE(n) unsigned long __kabi_reserved##n
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_RESERVE_ARRAY
|
||||||
|
* Same as _BACKPORT_RESERVE but allocates an array with the specified
|
||||||
|
* size in bytes.
|
||||||
|
*/
|
||||||
|
#define KABI_RESERVE_ARRAY(n, s) \
|
||||||
|
unsigned char __aligned(8) __kabi_reserved##n[s]
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_IGNORE
|
||||||
|
* Add a new field that's ignored in versioning.
|
||||||
|
*/
|
||||||
|
#define KABI_IGNORE(n, _new) \
|
||||||
|
union { \
|
||||||
|
_new; \
|
||||||
|
unsigned char __kabi_ignored##n; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_REPLACE
|
||||||
|
* Replace a field with a compatible new field.
|
||||||
|
*/
|
||||||
|
#define KABI_REPLACE(_oldtype, _oldname, _new) \
|
||||||
|
__KABI_REPLACE(_oldtype __kabi_renamed##_oldname, struct { _new; })
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_USE(number, _new)
|
||||||
|
* Use a previous padding entry that was defined with KABI_RESERVE
|
||||||
|
* number: the previous "number" of the padding variable
|
||||||
|
* _new: the variable to use now instead of the padding variable
|
||||||
|
*/
|
||||||
|
#define KABI_USE(number, _new) __KABI_REPLACE(KABI_RESERVE(number), _new)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KABI_USE2(number, _new1, _new2)
|
||||||
|
* Use a previous padding entry that was defined with KABI_RESERVE for
|
||||||
|
* two new variables that fit into 64 bits. This is good for when you do not
|
||||||
|
* want to "burn" a 64bit padding variable for a smaller variable size if not
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
|
#define KABI_USE2(number, _new1, _new2) \
|
||||||
|
__KABI_REPLACE( \
|
||||||
|
KABI_RESERVE(number), struct { \
|
||||||
|
_new1; \
|
||||||
|
_new2; \
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
* KABI_USE_ARRAY(number, bytes, _new)
|
||||||
|
* Use a previous padding entry that was defined with KABI_RESERVE_ARRAY
|
||||||
|
* number: the previous "number" of the padding variable
|
||||||
|
* bytes: the size in bytes reserved for the array
|
||||||
|
* _new: the variable to use now instead of the padding variable
|
||||||
|
*/
|
||||||
|
#define KABI_USE_ARRAY(number, bytes, _new) \
|
||||||
|
__KABI_REPLACE(KABI_RESERVE_ARRAY(number, bytes), _new)
|
||||||
|
|
||||||
|
#endif /* __KABI_H__ */
|
30
scripts/gendwarfksyms/examples/kabi_ex.c
Normal file
30
scripts/gendwarfksyms/examples/kabi_ex.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* kabi_ex.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*
|
||||||
|
* Examples for kABI stability features with --stable. See kabi_ex.h
|
||||||
|
* for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kabi_ex.h"
|
||||||
|
|
||||||
|
struct s e0;
|
||||||
|
enum e e1;
|
||||||
|
|
||||||
|
struct ex0a ex0a;
|
||||||
|
struct ex0b ex0b;
|
||||||
|
struct ex0c ex0c;
|
||||||
|
|
||||||
|
struct ex1a ex1a;
|
||||||
|
struct ex1b ex1b;
|
||||||
|
struct ex1c ex1c;
|
||||||
|
|
||||||
|
struct ex2a ex2a;
|
||||||
|
struct ex2b ex2b;
|
||||||
|
struct ex2c ex2c;
|
||||||
|
|
||||||
|
struct ex3a ex3a;
|
||||||
|
struct ex3b ex3b;
|
||||||
|
struct ex3c ex3c;
|
263
scripts/gendwarfksyms/examples/kabi_ex.h
Normal file
263
scripts/gendwarfksyms/examples/kabi_ex.h
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* kabi_ex.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*
|
||||||
|
* Examples for kABI stability features with --stable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The comments below each example contain the expected gendwarfksyms
|
||||||
|
* output, which can be verified using LLVM's FileCheck tool:
|
||||||
|
*
|
||||||
|
* https://llvm.org/docs/CommandGuide/FileCheck.html
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* $ gcc -g -c examples/kabi_ex.c -o examples/kabi_ex.o
|
||||||
|
*
|
||||||
|
* $ nm examples/kabi_ex.o | awk '{ print $NF }' | \
|
||||||
|
* ./gendwarfksyms --stable --dump-dies \
|
||||||
|
* examples/kabi_ex.o 2>&1 >/dev/null | \
|
||||||
|
* FileCheck examples/kabi_ex.h --check-prefix=STABLE
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __KABI_EX_H__
|
||||||
|
#define __KABI_EX_H__
|
||||||
|
|
||||||
|
#include "kabi.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example: kABI rules
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct s {
|
||||||
|
int a;
|
||||||
|
};
|
||||||
|
|
||||||
|
KABI_DECLONLY(s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type s {
|
||||||
|
* STABLE-NEXT: }
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum e {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
};
|
||||||
|
|
||||||
|
KABI_ENUMERATOR_IGNORE(e, B);
|
||||||
|
KABI_ENUMERATOR_IGNORE(e, C);
|
||||||
|
KABI_ENUMERATOR_VALUE(e, D, 123456789);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable enumeration_type e {
|
||||||
|
* STABLE-NEXT: enumerator A = 0 ,
|
||||||
|
* STABLE-NEXT: enumerator D = 123456789
|
||||||
|
* STABLE-NEXT: } byte_size(4)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example: Reserved fields
|
||||||
|
*/
|
||||||
|
struct ex0a {
|
||||||
|
int a;
|
||||||
|
KABI_RESERVE(0);
|
||||||
|
KABI_RESERVE(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex0a {
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) data_member_location(8) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
|
||||||
|
* STABLE-NEXT: } byte_size(24)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex0b {
|
||||||
|
int a;
|
||||||
|
KABI_RESERVE(0);
|
||||||
|
KABI_USE2(1, int b, int c);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex0b {
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
|
||||||
|
* STABLE-NEXT: } byte_size(24)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex0c {
|
||||||
|
int a;
|
||||||
|
KABI_USE(0, void *p);
|
||||||
|
KABI_USE2(1, int b, int c);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex0c {
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
|
||||||
|
* STABLE-NEXT: } byte_size(24)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example: A reserved array
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex1a {
|
||||||
|
unsigned int a;
|
||||||
|
KABI_RESERVE_ARRAY(0, 64);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex1a {
|
||||||
|
* STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member array_type[64] {
|
||||||
|
* STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8)
|
||||||
|
* STABLE-NEXT: } data_member_location(8)
|
||||||
|
* STABLE-NEXT: } byte_size(72)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex1b {
|
||||||
|
unsigned int a;
|
||||||
|
KABI_USE_ARRAY(
|
||||||
|
0, 64, struct {
|
||||||
|
void *p;
|
||||||
|
KABI_RESERVE_ARRAY(1, 56);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex1b {
|
||||||
|
* STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member array_type[64] {
|
||||||
|
* STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8)
|
||||||
|
* STABLE-NEXT: } data_member_location(8)
|
||||||
|
* STABLE-NEXT: } byte_size(72)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex1c {
|
||||||
|
unsigned int a;
|
||||||
|
KABI_USE_ARRAY(0, 64, void *p[8]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex1c {
|
||||||
|
* STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member array_type[64] {
|
||||||
|
* STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8)
|
||||||
|
* STABLE-NEXT: } data_member_location(8)
|
||||||
|
* STABLE-NEXT: } byte_size(72)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example: An ignored field added to an alignment hole
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex2a {
|
||||||
|
int a;
|
||||||
|
unsigned long b;
|
||||||
|
int c;
|
||||||
|
unsigned long d;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex2a {
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) b data_member_location(8)
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
|
||||||
|
* STABLE-NEXT: } byte_size(32)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex2b {
|
||||||
|
int a;
|
||||||
|
KABI_IGNORE(0, unsigned int n);
|
||||||
|
unsigned long b;
|
||||||
|
int c;
|
||||||
|
unsigned long d;
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(sizeof(struct ex2a) == sizeof(struct ex2b), "ex2a size doesn't match ex2b");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex2b {
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
|
||||||
|
* STABLE-NEXT: } byte_size(32)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex2c {
|
||||||
|
int a;
|
||||||
|
KABI_IGNORE(0, unsigned int n);
|
||||||
|
unsigned long b;
|
||||||
|
int c;
|
||||||
|
KABI_IGNORE(1, unsigned int m);
|
||||||
|
unsigned long d;
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(sizeof(struct ex2a) == sizeof(struct ex2c), "ex2a size doesn't match ex2c");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex2c {
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
|
||||||
|
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
|
||||||
|
* STABLE-NEXT: } byte_size(32)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example: A replaced field
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex3a {
|
||||||
|
unsigned long a;
|
||||||
|
unsigned long unused;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex3a {
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
|
||||||
|
* STABLE-NEXT: } byte_size(16)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex3b {
|
||||||
|
unsigned long a;
|
||||||
|
KABI_REPLACE(unsigned long, unused, unsigned long renamed);
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(sizeof(struct ex3a) == sizeof(struct ex3b), "ex3a size doesn't match ex3b");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex3b {
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
|
||||||
|
* STABLE-NEXT: } byte_size(16)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ex3c {
|
||||||
|
unsigned long a;
|
||||||
|
KABI_REPLACE(unsigned long, unused, long replaced);
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(sizeof(struct ex3a) == sizeof(struct ex3c), "ex3a size doesn't match ex3c");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STABLE: variable structure_type ex3c {
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
|
||||||
|
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
|
||||||
|
* STABLE-NEXT: } byte_size(16)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* __KABI_EX_H__ */
|
33
scripts/gendwarfksyms/examples/symbolptr.c
Normal file
33
scripts/gendwarfksyms/examples/symbolptr.c
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*
|
||||||
|
* Example for symbol pointers. When compiled with Clang, gendwarfkyms
|
||||||
|
* uses a symbol pointer for `f`.
|
||||||
|
*
|
||||||
|
* $ clang -g -c examples/symbolptr.c -o examples/symbolptr.o
|
||||||
|
* $ echo -e "f\ng\np" | ./gendwarfksyms -d examples/symbolptr.o
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Kernel macros for userspace testing. */
|
||||||
|
#ifndef __used
|
||||||
|
#define __used __attribute__((__used__))
|
||||||
|
#endif
|
||||||
|
#ifndef __section
|
||||||
|
#define __section(section) __attribute__((__section__(section)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __GENDWARFKSYMS_EXPORT(sym) \
|
||||||
|
static typeof(sym) *__gendwarfksyms_ptr_##sym __used \
|
||||||
|
__section(".discard.gendwarfksyms") = &sym;
|
||||||
|
|
||||||
|
extern void f(unsigned int arg);
|
||||||
|
void g(int *arg);
|
||||||
|
void g(int *arg) {}
|
||||||
|
|
||||||
|
struct s;
|
||||||
|
extern struct s *p;
|
||||||
|
|
||||||
|
__GENDWARFKSYMS_EXPORT(f);
|
||||||
|
__GENDWARFKSYMS_EXPORT(g);
|
||||||
|
__GENDWARFKSYMS_EXPORT(p);
|
187
scripts/gendwarfksyms/gendwarfksyms.c
Normal file
187
scripts/gendwarfksyms/gendwarfksyms.c
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "gendwarfksyms.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Print debugging information to stderr */
|
||||||
|
int debug;
|
||||||
|
/* Dump DIE contents */
|
||||||
|
int dump_dies;
|
||||||
|
/* Print debugging information about die_map changes */
|
||||||
|
int dump_die_map;
|
||||||
|
/* Print out type strings (i.e. type_map) */
|
||||||
|
int dump_types;
|
||||||
|
/* Print out expanded type strings used for symbol versions */
|
||||||
|
int dump_versions;
|
||||||
|
/* Support kABI stability features */
|
||||||
|
int stable;
|
||||||
|
/* Write a symtypes file */
|
||||||
|
int symtypes;
|
||||||
|
static const char *symtypes_file;
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
fputs("Usage: gendwarfksyms [options] elf-object-file ... < symbol-list\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -d, --debug Print debugging information\n"
|
||||||
|
" --dump-dies Dump DWARF DIE contents\n"
|
||||||
|
" --dump-die-map Print debugging information about die_map changes\n"
|
||||||
|
" --dump-types Dump type strings\n"
|
||||||
|
" --dump-versions Dump expanded type strings used for symbol versions\n"
|
||||||
|
" -s, --stable Support kABI stability features\n"
|
||||||
|
" -T, --symtypes file Write a symtypes file\n"
|
||||||
|
" -h, --help Print this message\n"
|
||||||
|
"\n",
|
||||||
|
stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_module(Dwfl_Module *mod, void **userdata, const char *name,
|
||||||
|
Dwarf_Addr base, void *arg)
|
||||||
|
{
|
||||||
|
Dwarf_Addr dwbias;
|
||||||
|
Dwarf_Die cudie;
|
||||||
|
Dwarf_CU *cu = NULL;
|
||||||
|
Dwarf *dbg;
|
||||||
|
FILE *symfile = arg;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
debug("%s", name);
|
||||||
|
dbg = dwfl_module_getdwarf(mod, &dwbias);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for exported symbols in each CU, follow the DIE tree, and add
|
||||||
|
* the entries to die_map.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL);
|
||||||
|
if (res < 0)
|
||||||
|
error("dwarf_get_units failed: no debugging information?");
|
||||||
|
if (res == 1)
|
||||||
|
break; /* No more units */
|
||||||
|
|
||||||
|
process_cu(&cudie);
|
||||||
|
} while (cu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use die_map to expand type strings, write them to `symfile`, and
|
||||||
|
* calculate symbol versions.
|
||||||
|
*/
|
||||||
|
generate_symtypes_and_versions(symfile);
|
||||||
|
die_map_free();
|
||||||
|
|
||||||
|
return DWARF_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Dwfl_Callbacks callbacks = {
|
||||||
|
.section_address = dwfl_offline_section_address,
|
||||||
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
FILE *symfile = NULL;
|
||||||
|
unsigned int n;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
static const struct option opts[] = {
|
||||||
|
{ "debug", 0, NULL, 'd' },
|
||||||
|
{ "dump-dies", 0, &dump_dies, 1 },
|
||||||
|
{ "dump-die-map", 0, &dump_die_map, 1 },
|
||||||
|
{ "dump-types", 0, &dump_types, 1 },
|
||||||
|
{ "dump-versions", 0, &dump_versions, 1 },
|
||||||
|
{ "stable", 0, NULL, 's' },
|
||||||
|
{ "symtypes", 1, NULL, 'T' },
|
||||||
|
{ "help", 0, NULL, 'h' },
|
||||||
|
{ 0, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "dsT:h", opts, NULL)) != EOF) {
|
||||||
|
switch (opt) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
debug = 1;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
stable = 1;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
symtypes = 1;
|
||||||
|
symtypes_file = optarg;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dump_die_map)
|
||||||
|
dump_dies = 1;
|
||||||
|
|
||||||
|
if (optind >= argc) {
|
||||||
|
usage();
|
||||||
|
error("no input files?");
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol_read_exports(stdin);
|
||||||
|
|
||||||
|
if (symtypes_file) {
|
||||||
|
symfile = fopen(symtypes_file, "w");
|
||||||
|
if (!symfile)
|
||||||
|
error("fopen failed for '%s': %s", symtypes_file,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = optind; n < argc; n++) {
|
||||||
|
Dwfl *dwfl;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open(argv[n], O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
error("open failed for '%s': %s", argv[n],
|
||||||
|
strerror(errno));
|
||||||
|
|
||||||
|
symbol_read_symtab(fd);
|
||||||
|
kabi_read_rules(fd);
|
||||||
|
|
||||||
|
dwfl = dwfl_begin(&callbacks);
|
||||||
|
if (!dwfl)
|
||||||
|
error("dwfl_begin failed for '%s': %s", argv[n],
|
||||||
|
dwarf_errmsg(-1));
|
||||||
|
|
||||||
|
if (!dwfl_report_offline(dwfl, argv[n], argv[n], fd))
|
||||||
|
error("dwfl_report_offline failed for '%s': %s",
|
||||||
|
argv[n], dwarf_errmsg(-1));
|
||||||
|
|
||||||
|
dwfl_report_end(dwfl, NULL, NULL);
|
||||||
|
|
||||||
|
if (dwfl_getmodules(dwfl, &process_module, symfile, 0))
|
||||||
|
error("dwfl_getmodules failed for '%s'", argv[n]);
|
||||||
|
|
||||||
|
dwfl_end(dwfl);
|
||||||
|
kabi_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symfile)
|
||||||
|
check(fclose(symfile));
|
||||||
|
|
||||||
|
symbol_print_versions();
|
||||||
|
symbol_free();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
296
scripts/gendwarfksyms/gendwarfksyms.h
Normal file
296
scripts/gendwarfksyms/gendwarfksyms.h
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dwarf.h>
|
||||||
|
#include <elfutils/libdw.h>
|
||||||
|
#include <elfutils/libdwfl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <hash.h>
|
||||||
|
#include <hashtable.h>
|
||||||
|
#include <xalloc.h>
|
||||||
|
|
||||||
|
#ifndef __GENDWARFKSYMS_H
|
||||||
|
#define __GENDWARFKSYMS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options -- in gendwarfksyms.c
|
||||||
|
*/
|
||||||
|
extern int debug;
|
||||||
|
extern int dump_dies;
|
||||||
|
extern int dump_die_map;
|
||||||
|
extern int dump_types;
|
||||||
|
extern int dump_versions;
|
||||||
|
extern int stable;
|
||||||
|
extern int symtypes;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output helpers
|
||||||
|
*/
|
||||||
|
#define __PREFIX "gendwarfksyms: "
|
||||||
|
#define __println(prefix, format, ...) \
|
||||||
|
fprintf(stderr, prefix __PREFIX "%s: " format "\n", __func__, \
|
||||||
|
##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define debug(format, ...) \
|
||||||
|
do { \
|
||||||
|
if (debug) \
|
||||||
|
__println("", format, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define warn(format, ...) __println("warning: ", format, ##__VA_ARGS__)
|
||||||
|
#define error(format, ...) \
|
||||||
|
do { \
|
||||||
|
__println("error: ", format, ##__VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define __die_debug(color, format, ...) \
|
||||||
|
do { \
|
||||||
|
if (dump_dies && dump_die_map) \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"\033[" #color "m<" format ">\033[39m", \
|
||||||
|
__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define die_debug_r(format, ...) __die_debug(91, format, __VA_ARGS__)
|
||||||
|
#define die_debug_g(format, ...) __die_debug(92, format, __VA_ARGS__)
|
||||||
|
#define die_debug_b(format, ...) __die_debug(94, format, __VA_ARGS__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error handling helpers
|
||||||
|
*/
|
||||||
|
#define __check(expr, test) \
|
||||||
|
({ \
|
||||||
|
int __res = expr; \
|
||||||
|
if (test) \
|
||||||
|
error("`%s` failed: %d", #expr, __res); \
|
||||||
|
__res; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Error == non-zero values */
|
||||||
|
#define check(expr) __check(expr, __res)
|
||||||
|
/* Error == negative values */
|
||||||
|
#define checkp(expr) __check(expr, __res < 0)
|
||||||
|
|
||||||
|
/* Consistent aliases (DW_TAG_<type>_type) for DWARF tags */
|
||||||
|
#define DW_TAG_enumerator_type DW_TAG_enumerator
|
||||||
|
#define DW_TAG_formal_parameter_type DW_TAG_formal_parameter
|
||||||
|
#define DW_TAG_member_type DW_TAG_member
|
||||||
|
#define DW_TAG_template_type_parameter_type DW_TAG_template_type_parameter
|
||||||
|
#define DW_TAG_typedef_type DW_TAG_typedef
|
||||||
|
#define DW_TAG_variant_part_type DW_TAG_variant_part
|
||||||
|
#define DW_TAG_variant_type DW_TAG_variant
|
||||||
|
|
||||||
|
/*
|
||||||
|
* symbols.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* See symbols.c:is_symbol_ptr */
|
||||||
|
#define SYMBOL_PTR_PREFIX "__gendwarfksyms_ptr_"
|
||||||
|
#define SYMBOL_PTR_PREFIX_LEN (sizeof(SYMBOL_PTR_PREFIX) - 1)
|
||||||
|
|
||||||
|
static inline unsigned int addr_hash(uintptr_t addr)
|
||||||
|
{
|
||||||
|
return hash_ptr((const void *)addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum symbol_state {
|
||||||
|
SYMBOL_UNPROCESSED,
|
||||||
|
SYMBOL_MAPPED,
|
||||||
|
SYMBOL_PROCESSED
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbol_addr {
|
||||||
|
uint32_t section;
|
||||||
|
Elf64_Addr address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbol {
|
||||||
|
const char *name;
|
||||||
|
struct symbol_addr addr;
|
||||||
|
struct hlist_node addr_hash;
|
||||||
|
struct hlist_node name_hash;
|
||||||
|
enum symbol_state state;
|
||||||
|
uintptr_t die_addr;
|
||||||
|
uintptr_t ptr_die_addr;
|
||||||
|
unsigned long crc;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*symbol_callback_t)(struct symbol *, void *arg);
|
||||||
|
|
||||||
|
bool is_symbol_ptr(const char *name);
|
||||||
|
void symbol_read_exports(FILE *file);
|
||||||
|
void symbol_read_symtab(int fd);
|
||||||
|
struct symbol *symbol_get(const char *name);
|
||||||
|
void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr);
|
||||||
|
void symbol_set_die(struct symbol *sym, Dwarf_Die *die);
|
||||||
|
void symbol_set_crc(struct symbol *sym, unsigned long crc);
|
||||||
|
void symbol_for_each(symbol_callback_t func, void *arg);
|
||||||
|
void symbol_print_versions(void);
|
||||||
|
void symbol_free(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* die.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum die_state {
|
||||||
|
DIE_INCOMPLETE,
|
||||||
|
DIE_UNEXPANDED,
|
||||||
|
DIE_COMPLETE,
|
||||||
|
DIE_SYMBOL,
|
||||||
|
DIE_LAST = DIE_SYMBOL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum die_fragment_type {
|
||||||
|
FRAGMENT_EMPTY,
|
||||||
|
FRAGMENT_STRING,
|
||||||
|
FRAGMENT_LINEBREAK,
|
||||||
|
FRAGMENT_DIE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct die_fragment {
|
||||||
|
enum die_fragment_type type;
|
||||||
|
union {
|
||||||
|
char *str;
|
||||||
|
int linebreak;
|
||||||
|
uintptr_t addr;
|
||||||
|
} data;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CASE_CONST_TO_STR(name) \
|
||||||
|
case name: \
|
||||||
|
return #name;
|
||||||
|
|
||||||
|
static inline const char *die_state_name(enum die_state state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
CASE_CONST_TO_STR(DIE_INCOMPLETE)
|
||||||
|
CASE_CONST_TO_STR(DIE_UNEXPANDED)
|
||||||
|
CASE_CONST_TO_STR(DIE_COMPLETE)
|
||||||
|
CASE_CONST_TO_STR(DIE_SYMBOL)
|
||||||
|
}
|
||||||
|
|
||||||
|
error("unexpected die_state: %d", state);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct die {
|
||||||
|
enum die_state state;
|
||||||
|
bool mapped;
|
||||||
|
char *fqn;
|
||||||
|
int tag;
|
||||||
|
uintptr_t addr;
|
||||||
|
struct list_head fragments;
|
||||||
|
struct hlist_node hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*die_map_callback_t)(struct die *, void *arg);
|
||||||
|
|
||||||
|
int __die_map_get(uintptr_t addr, enum die_state state, struct die **res);
|
||||||
|
struct die *die_map_get(Dwarf_Die *die, enum die_state state);
|
||||||
|
void die_map_add_string(struct die *pd, const char *str);
|
||||||
|
void die_map_add_linebreak(struct die *pd, int linebreak);
|
||||||
|
void die_map_for_each(die_map_callback_t func, void *arg);
|
||||||
|
void die_map_add_die(struct die *pd, struct die *child);
|
||||||
|
void die_map_free(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cache.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CACHE_HASH_BITS 10
|
||||||
|
|
||||||
|
/* A cache for addresses we've already seen. */
|
||||||
|
struct cache {
|
||||||
|
HASHTABLE_DECLARE(cache, 1 << CACHE_HASH_BITS);
|
||||||
|
};
|
||||||
|
|
||||||
|
void cache_set(struct cache *cache, unsigned long key, int value);
|
||||||
|
int cache_get(struct cache *cache, unsigned long key);
|
||||||
|
void cache_init(struct cache *cache);
|
||||||
|
void cache_free(struct cache *cache);
|
||||||
|
|
||||||
|
static inline void __cache_mark_expanded(struct cache *cache, uintptr_t addr)
|
||||||
|
{
|
||||||
|
cache_set(cache, addr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool __cache_was_expanded(struct cache *cache, uintptr_t addr)
|
||||||
|
{
|
||||||
|
return cache_get(cache, addr) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cache_mark_expanded(struct cache *cache, void *addr)
|
||||||
|
{
|
||||||
|
__cache_mark_expanded(cache, (uintptr_t)addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool cache_was_expanded(struct cache *cache, void *addr)
|
||||||
|
{
|
||||||
|
return __cache_was_expanded(cache, (uintptr_t)addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dwarf.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct expansion_state {
|
||||||
|
bool expand;
|
||||||
|
const char *current_fqn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kabi_state {
|
||||||
|
int members;
|
||||||
|
Dwarf_Die placeholder;
|
||||||
|
const char *orig_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
struct symbol *sym;
|
||||||
|
Dwarf_Die die;
|
||||||
|
|
||||||
|
/* List expansion */
|
||||||
|
bool first_list_item;
|
||||||
|
|
||||||
|
/* Structure expansion */
|
||||||
|
struct expansion_state expand;
|
||||||
|
struct cache expansion_cache;
|
||||||
|
|
||||||
|
/* Reserved or ignored members */
|
||||||
|
struct kabi_state kabi;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*die_callback_t)(struct state *state, struct die *cache,
|
||||||
|
Dwarf_Die *die);
|
||||||
|
typedef bool (*die_match_callback_t)(Dwarf_Die *die);
|
||||||
|
bool match_all(Dwarf_Die *die);
|
||||||
|
|
||||||
|
int process_die_container(struct state *state, struct die *cache,
|
||||||
|
Dwarf_Die *die, die_callback_t func,
|
||||||
|
die_match_callback_t match);
|
||||||
|
|
||||||
|
void process_cu(Dwarf_Die *cudie);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* types.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
void generate_symtypes_and_versions(FILE *file);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kabi.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool kabi_is_enumerator_ignored(const char *fqn, const char *field);
|
||||||
|
bool kabi_get_enumerator_value(const char *fqn, const char *field,
|
||||||
|
unsigned long *value);
|
||||||
|
bool kabi_is_declonly(const char *fqn);
|
||||||
|
|
||||||
|
void kabi_read_rules(int fd);
|
||||||
|
void kabi_free(void);
|
||||||
|
|
||||||
|
#endif /* __GENDWARFKSYMS_H */
|
336
scripts/gendwarfksyms/kabi.c
Normal file
336
scripts/gendwarfksyms/kabi.c
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "gendwarfksyms.h"
|
||||||
|
|
||||||
|
#define KABI_RULE_SECTION ".discard.gendwarfksyms.kabi_rules"
|
||||||
|
#define KABI_RULE_VERSION "1"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The rule section consists of four null-terminated strings per
|
||||||
|
* entry:
|
||||||
|
*
|
||||||
|
* 1. version
|
||||||
|
* Entry format version. Must match KABI_RULE_VERSION.
|
||||||
|
*
|
||||||
|
* 2. type
|
||||||
|
* Type of the kABI rule. Must be one of the tags defined below.
|
||||||
|
*
|
||||||
|
* 3. target
|
||||||
|
* Rule-dependent target, typically the fully qualified name of
|
||||||
|
* the target DIE.
|
||||||
|
*
|
||||||
|
* 4. value
|
||||||
|
* Rule-dependent value.
|
||||||
|
*/
|
||||||
|
#define KABI_RULE_MIN_ENTRY_SIZE \
|
||||||
|
(/* version\0 */ 2 + /* type\0 */ 2 + /* target\0" */ 1 + \
|
||||||
|
/* value\0 */ 1)
|
||||||
|
#define KABI_RULE_EMPTY_VALUE ""
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rule: declonly
|
||||||
|
* - For the struct/enum/union in the target field, treat it as a
|
||||||
|
* declaration only even if a definition is available.
|
||||||
|
*/
|
||||||
|
#define KABI_RULE_TAG_DECLONLY "declonly"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rule: enumerator_ignore
|
||||||
|
* - For the enum_field in the target field, ignore the enumerator.
|
||||||
|
*/
|
||||||
|
#define KABI_RULE_TAG_ENUMERATOR_IGNORE "enumerator_ignore"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rule: enumerator_value
|
||||||
|
* - For the fqn_field in the target field, set the value to the
|
||||||
|
* unsigned integer in the value field.
|
||||||
|
*/
|
||||||
|
#define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
|
||||||
|
|
||||||
|
enum kabi_rule_type {
|
||||||
|
KABI_RULE_TYPE_UNKNOWN,
|
||||||
|
KABI_RULE_TYPE_DECLONLY,
|
||||||
|
KABI_RULE_TYPE_ENUMERATOR_IGNORE,
|
||||||
|
KABI_RULE_TYPE_ENUMERATOR_VALUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RULE_HASH_BITS 7
|
||||||
|
|
||||||
|
struct rule {
|
||||||
|
enum kabi_rule_type type;
|
||||||
|
const char *target;
|
||||||
|
const char *value;
|
||||||
|
struct hlist_node hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* { type, target } -> struct rule */
|
||||||
|
static HASHTABLE_DEFINE(rules, 1 << RULE_HASH_BITS);
|
||||||
|
|
||||||
|
static inline unsigned int rule_values_hash(enum kabi_rule_type type,
|
||||||
|
const char *target)
|
||||||
|
{
|
||||||
|
return hash_32(type) ^ hash_str(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int rule_hash(const struct rule *rule)
|
||||||
|
{
|
||||||
|
return rule_values_hash(rule->type, rule->target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *get_rule_field(const char **pos, ssize_t *left)
|
||||||
|
{
|
||||||
|
const char *start = *pos;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (*left <= 0)
|
||||||
|
error("unexpected end of kABI rules");
|
||||||
|
|
||||||
|
len = strnlen(start, *left) + 1;
|
||||||
|
*pos += len;
|
||||||
|
*left -= len;
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kabi_read_rules(int fd)
|
||||||
|
{
|
||||||
|
GElf_Shdr shdr_mem;
|
||||||
|
GElf_Shdr *shdr;
|
||||||
|
Elf_Data *rule_data = NULL;
|
||||||
|
Elf_Scn *scn;
|
||||||
|
Elf *elf;
|
||||||
|
size_t shstrndx;
|
||||||
|
const char *rule_str;
|
||||||
|
ssize_t left;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
enum kabi_rule_type type;
|
||||||
|
const char *tag;
|
||||||
|
} rule_types[] = {
|
||||||
|
{
|
||||||
|
.type = KABI_RULE_TYPE_DECLONLY,
|
||||||
|
.tag = KABI_RULE_TAG_DECLONLY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = KABI_RULE_TYPE_ENUMERATOR_IGNORE,
|
||||||
|
.tag = KABI_RULE_TAG_ENUMERATOR_IGNORE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
|
||||||
|
.tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!stable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (elf_version(EV_CURRENT) != EV_CURRENT)
|
||||||
|
error("elf_version failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
||||||
|
if (!elf)
|
||||||
|
error("elf_begin failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
if (elf_getshdrstrndx(elf, &shstrndx) < 0)
|
||||||
|
error("elf_getshdrstrndx failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
scn = elf_nextscn(elf, NULL);
|
||||||
|
|
||||||
|
while (scn) {
|
||||||
|
const char *sname;
|
||||||
|
|
||||||
|
shdr = gelf_getshdr(scn, &shdr_mem);
|
||||||
|
if (!shdr)
|
||||||
|
error("gelf_getshdr failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
sname = elf_strptr(elf, shstrndx, shdr->sh_name);
|
||||||
|
if (!sname)
|
||||||
|
error("elf_strptr failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
if (!strcmp(sname, KABI_RULE_SECTION)) {
|
||||||
|
rule_data = elf_getdata(scn, NULL);
|
||||||
|
if (!rule_data)
|
||||||
|
error("elf_getdata failed: %s", elf_errmsg(-1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scn = elf_nextscn(elf, scn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rule_data) {
|
||||||
|
debug("kABI rules not found");
|
||||||
|
check(elf_end(elf));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_str = rule_data->d_buf;
|
||||||
|
left = shdr->sh_size;
|
||||||
|
|
||||||
|
if (left < KABI_RULE_MIN_ENTRY_SIZE)
|
||||||
|
error("kABI rule section too small: %zd bytes", left);
|
||||||
|
|
||||||
|
if (rule_str[left - 1] != '\0')
|
||||||
|
error("kABI rules are not null-terminated");
|
||||||
|
|
||||||
|
while (left > KABI_RULE_MIN_ENTRY_SIZE) {
|
||||||
|
enum kabi_rule_type type = KABI_RULE_TYPE_UNKNOWN;
|
||||||
|
const char *field;
|
||||||
|
struct rule *rule;
|
||||||
|
|
||||||
|
/* version */
|
||||||
|
field = get_rule_field(&rule_str, &left);
|
||||||
|
|
||||||
|
if (strcmp(field, KABI_RULE_VERSION))
|
||||||
|
error("unsupported kABI rule version: '%s'", field);
|
||||||
|
|
||||||
|
/* type */
|
||||||
|
field = get_rule_field(&rule_str, &left);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(rule_types); i++) {
|
||||||
|
if (!strcmp(field, rule_types[i].tag)) {
|
||||||
|
type = rule_types[i].type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == KABI_RULE_TYPE_UNKNOWN)
|
||||||
|
error("unsupported kABI rule type: '%s'", field);
|
||||||
|
|
||||||
|
rule = xmalloc(sizeof(struct rule));
|
||||||
|
|
||||||
|
rule->type = type;
|
||||||
|
rule->target = xstrdup(get_rule_field(&rule_str, &left));
|
||||||
|
rule->value = xstrdup(get_rule_field(&rule_str, &left));
|
||||||
|
|
||||||
|
hash_add(rules, &rule->hash, rule_hash(rule));
|
||||||
|
|
||||||
|
debug("kABI rule: type: '%s', target: '%s', value: '%s'", field,
|
||||||
|
rule->target, rule->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left > 0)
|
||||||
|
warn("unexpected data at the end of the kABI rules section");
|
||||||
|
|
||||||
|
check(elf_end(elf));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kabi_is_declonly(const char *fqn)
|
||||||
|
{
|
||||||
|
struct rule *rule;
|
||||||
|
|
||||||
|
if (!stable)
|
||||||
|
return false;
|
||||||
|
if (!fqn || !*fqn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
hash_for_each_possible(rules, rule, hash,
|
||||||
|
rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) {
|
||||||
|
if (rule->type == KABI_RULE_TYPE_DECLONLY &&
|
||||||
|
!strcmp(fqn, rule->target))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *get_enumerator_target(const char *fqn, const char *field)
|
||||||
|
{
|
||||||
|
char *target = NULL;
|
||||||
|
|
||||||
|
if (asprintf(&target, "%s %s", fqn, field) < 0)
|
||||||
|
error("asprintf failed for '%s %s'", fqn, field);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long get_ulong_value(const char *value)
|
||||||
|
{
|
||||||
|
unsigned long result = 0;
|
||||||
|
char *endptr = NULL;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
result = strtoul(value, &endptr, 10);
|
||||||
|
|
||||||
|
if (errno || *endptr)
|
||||||
|
error("invalid unsigned value '%s'", value);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kabi_is_enumerator_ignored(const char *fqn, const char *field)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
struct rule *rule;
|
||||||
|
char *target;
|
||||||
|
|
||||||
|
if (!stable)
|
||||||
|
return false;
|
||||||
|
if (!fqn || !*fqn || !field || !*field)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
target = get_enumerator_target(fqn, field);
|
||||||
|
|
||||||
|
hash_for_each_possible(
|
||||||
|
rules, rule, hash,
|
||||||
|
rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) {
|
||||||
|
if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE &&
|
||||||
|
!strcmp(target, rule->target)) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(target);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kabi_get_enumerator_value(const char *fqn, const char *field,
|
||||||
|
unsigned long *value)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
struct rule *rule;
|
||||||
|
char *target;
|
||||||
|
|
||||||
|
if (!stable)
|
||||||
|
return false;
|
||||||
|
if (!fqn || !*fqn || !field || !*field)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
target = get_enumerator_target(fqn, field);
|
||||||
|
|
||||||
|
hash_for_each_possible(rules, rule, hash,
|
||||||
|
rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE,
|
||||||
|
target)) {
|
||||||
|
if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE &&
|
||||||
|
!strcmp(target, rule->target)) {
|
||||||
|
*value = get_ulong_value(rule->value);
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(target);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kabi_free(void)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct rule *rule;
|
||||||
|
|
||||||
|
hash_for_each_safe(rules, rule, tmp, hash) {
|
||||||
|
free((void *)rule->target);
|
||||||
|
free((void *)rule->value);
|
||||||
|
free(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_init(rules);
|
||||||
|
}
|
341
scripts/gendwarfksyms/symbols.c
Normal file
341
scripts/gendwarfksyms/symbols.c
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gendwarfksyms.h"
|
||||||
|
|
||||||
|
#define SYMBOL_HASH_BITS 12
|
||||||
|
|
||||||
|
/* struct symbol_addr -> struct symbol */
|
||||||
|
static HASHTABLE_DEFINE(symbol_addrs, 1 << SYMBOL_HASH_BITS);
|
||||||
|
/* name -> struct symbol */
|
||||||
|
static HASHTABLE_DEFINE(symbol_names, 1 << SYMBOL_HASH_BITS);
|
||||||
|
|
||||||
|
static inline unsigned int symbol_addr_hash(const struct symbol_addr *addr)
|
||||||
|
{
|
||||||
|
return hash_32(addr->section ^ addr_hash(addr->address));
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int __for_each_addr(struct symbol *sym, symbol_callback_t func,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct symbol *match = NULL;
|
||||||
|
unsigned int processed = 0;
|
||||||
|
|
||||||
|
hash_for_each_possible_safe(symbol_addrs, match, tmp, addr_hash,
|
||||||
|
symbol_addr_hash(&sym->addr)) {
|
||||||
|
if (match == sym)
|
||||||
|
continue; /* Already processed */
|
||||||
|
|
||||||
|
if (match->addr.section == sym->addr.section &&
|
||||||
|
match->addr.address == sym->addr.address) {
|
||||||
|
func(match, data);
|
||||||
|
++processed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For symbols without debugging information (e.g. symbols defined in other
|
||||||
|
* TUs), we also match __gendwarfksyms_ptr_<symbol_name> symbols, which the
|
||||||
|
* kernel uses to ensure type information is present in the TU that exports
|
||||||
|
* the symbol. A __gendwarfksyms_ptr pointer must have the same type as the
|
||||||
|
* exported symbol, e.g.:
|
||||||
|
*
|
||||||
|
* typeof(symname) *__gendwarf_ptr_symname = &symname;
|
||||||
|
*/
|
||||||
|
bool is_symbol_ptr(const char *name)
|
||||||
|
{
|
||||||
|
return name && !strncmp(name, SYMBOL_PTR_PREFIX, SYMBOL_PTR_PREFIX_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int for_each(const char *name, symbol_callback_t func,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct symbol *match;
|
||||||
|
|
||||||
|
if (!name || !*name)
|
||||||
|
return 0;
|
||||||
|
if (is_symbol_ptr(name))
|
||||||
|
name += SYMBOL_PTR_PREFIX_LEN;
|
||||||
|
|
||||||
|
hash_for_each_possible_safe(symbol_names, match, tmp, name_hash,
|
||||||
|
hash_str(name)) {
|
||||||
|
if (strcmp(match->name, name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Call func for the match, and all address matches */
|
||||||
|
if (func)
|
||||||
|
func(match, data);
|
||||||
|
|
||||||
|
if (match->addr.section != SHN_UNDEF)
|
||||||
|
return __for_each_addr(match, func, data) + 1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_crc(struct symbol *sym, void *data)
|
||||||
|
{
|
||||||
|
unsigned long *crc = data;
|
||||||
|
|
||||||
|
if (sym->state == SYMBOL_PROCESSED && sym->crc != *crc)
|
||||||
|
warn("overriding version for symbol %s (crc %lx vs. %lx)",
|
||||||
|
sym->name, sym->crc, *crc);
|
||||||
|
|
||||||
|
sym->state = SYMBOL_PROCESSED;
|
||||||
|
sym->crc = *crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_set_crc(struct symbol *sym, unsigned long crc)
|
||||||
|
{
|
||||||
|
if (for_each(sym->name, set_crc, &crc) == 0)
|
||||||
|
error("no matching symbols: '%s'", sym->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_ptr(struct symbol *sym, void *data)
|
||||||
|
{
|
||||||
|
sym->ptr_die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr)
|
||||||
|
{
|
||||||
|
if (for_each(sym->name, set_ptr, ptr) == 0)
|
||||||
|
error("no matching symbols: '%s'", sym->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_die(struct symbol *sym, void *data)
|
||||||
|
{
|
||||||
|
sym->die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
|
||||||
|
sym->state = SYMBOL_MAPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_set_die(struct symbol *sym, Dwarf_Die *die)
|
||||||
|
{
|
||||||
|
if (for_each(sym->name, set_die, die) == 0)
|
||||||
|
error("no matching symbols: '%s'", sym->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_exported(const char *name)
|
||||||
|
{
|
||||||
|
return for_each(name, NULL, NULL) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_read_exports(FILE *file)
|
||||||
|
{
|
||||||
|
struct symbol *sym;
|
||||||
|
char *line = NULL;
|
||||||
|
char *name = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
int nsym = 0;
|
||||||
|
|
||||||
|
while (getline(&line, &size, file) > 0) {
|
||||||
|
if (sscanf(line, "%ms\n", &name) != 1)
|
||||||
|
error("malformed input line: %s", line);
|
||||||
|
|
||||||
|
if (is_exported(name)) {
|
||||||
|
/* Ignore duplicates */
|
||||||
|
free(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sym = xcalloc(1, sizeof(struct symbol));
|
||||||
|
sym->name = name;
|
||||||
|
sym->addr.section = SHN_UNDEF;
|
||||||
|
sym->state = SYMBOL_UNPROCESSED;
|
||||||
|
|
||||||
|
hash_add(symbol_names, &sym->name_hash, hash_str(sym->name));
|
||||||
|
++nsym;
|
||||||
|
|
||||||
|
debug("%s", sym->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
debug("%d exported symbols", nsym);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_symbol(struct symbol *sym, void *arg)
|
||||||
|
{
|
||||||
|
struct symbol **res = arg;
|
||||||
|
|
||||||
|
if (sym->state == SYMBOL_UNPROCESSED)
|
||||||
|
*res = sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct symbol *symbol_get(const char *name)
|
||||||
|
{
|
||||||
|
struct symbol *sym = NULL;
|
||||||
|
|
||||||
|
for_each(name, get_symbol, &sym);
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_for_each(symbol_callback_t func, void *arg)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
|
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
|
||||||
|
func(sym, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*elf_symbol_callback_t)(const char *name, GElf_Sym *sym,
|
||||||
|
Elf32_Word xndx, void *arg);
|
||||||
|
|
||||||
|
static void elf_for_each_global(int fd, elf_symbol_callback_t func, void *arg)
|
||||||
|
{
|
||||||
|
size_t sym_size;
|
||||||
|
GElf_Shdr shdr_mem;
|
||||||
|
GElf_Shdr *shdr;
|
||||||
|
Elf_Data *xndx_data = NULL;
|
||||||
|
Elf_Scn *scn;
|
||||||
|
Elf *elf;
|
||||||
|
|
||||||
|
if (elf_version(EV_CURRENT) != EV_CURRENT)
|
||||||
|
error("elf_version failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
||||||
|
if (!elf)
|
||||||
|
error("elf_begin failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
scn = elf_nextscn(elf, NULL);
|
||||||
|
|
||||||
|
while (scn) {
|
||||||
|
shdr = gelf_getshdr(scn, &shdr_mem);
|
||||||
|
if (!shdr)
|
||||||
|
error("gelf_getshdr failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
if (shdr->sh_type == SHT_SYMTAB_SHNDX) {
|
||||||
|
xndx_data = elf_getdata(scn, NULL);
|
||||||
|
if (!xndx_data)
|
||||||
|
error("elf_getdata failed: %s", elf_errmsg(-1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scn = elf_nextscn(elf, scn);
|
||||||
|
}
|
||||||
|
|
||||||
|
sym_size = gelf_fsize(elf, ELF_T_SYM, 1, EV_CURRENT);
|
||||||
|
scn = elf_nextscn(elf, NULL);
|
||||||
|
|
||||||
|
while (scn) {
|
||||||
|
shdr = gelf_getshdr(scn, &shdr_mem);
|
||||||
|
if (!shdr)
|
||||||
|
error("gelf_getshdr failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
if (shdr->sh_type == SHT_SYMTAB) {
|
||||||
|
unsigned int nsyms;
|
||||||
|
unsigned int n;
|
||||||
|
Elf_Data *data = elf_getdata(scn, NULL);
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
error("elf_getdata failed: %s", elf_errmsg(-1));
|
||||||
|
|
||||||
|
if (shdr->sh_entsize != sym_size)
|
||||||
|
error("expected sh_entsize (%lu) to be %zu",
|
||||||
|
shdr->sh_entsize, sym_size);
|
||||||
|
|
||||||
|
nsyms = shdr->sh_size / shdr->sh_entsize;
|
||||||
|
|
||||||
|
for (n = 1; n < nsyms; ++n) {
|
||||||
|
const char *name = NULL;
|
||||||
|
Elf32_Word xndx = 0;
|
||||||
|
GElf_Sym sym_mem;
|
||||||
|
GElf_Sym *sym;
|
||||||
|
|
||||||
|
sym = gelf_getsymshndx(data, xndx_data, n,
|
||||||
|
&sym_mem, &xndx);
|
||||||
|
if (!sym)
|
||||||
|
error("gelf_getsymshndx failed: %s",
|
||||||
|
elf_errmsg(-1));
|
||||||
|
|
||||||
|
if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sym->st_shndx != SHN_XINDEX)
|
||||||
|
xndx = sym->st_shndx;
|
||||||
|
|
||||||
|
name = elf_strptr(elf, shdr->sh_link,
|
||||||
|
sym->st_name);
|
||||||
|
if (!name)
|
||||||
|
error("elf_strptr failed: %s",
|
||||||
|
elf_errmsg(-1));
|
||||||
|
|
||||||
|
/* Skip empty symbol names */
|
||||||
|
if (*name)
|
||||||
|
func(name, sym, xndx, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scn = elf_nextscn(elf, scn);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(elf_end(elf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_symbol_addr(struct symbol *sym, void *arg)
|
||||||
|
{
|
||||||
|
struct symbol_addr *addr = arg;
|
||||||
|
|
||||||
|
if (sym->addr.section == SHN_UNDEF) {
|
||||||
|
sym->addr = *addr;
|
||||||
|
hash_add(symbol_addrs, &sym->addr_hash,
|
||||||
|
symbol_addr_hash(&sym->addr));
|
||||||
|
|
||||||
|
debug("%s -> { %u, %lx }", sym->name, sym->addr.section,
|
||||||
|
sym->addr.address);
|
||||||
|
} else if (sym->addr.section != addr->section ||
|
||||||
|
sym->addr.address != addr->address) {
|
||||||
|
warn("multiple addresses for symbol %s?", sym->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elf_set_symbol_addr(const char *name, GElf_Sym *sym,
|
||||||
|
Elf32_Word xndx, void *arg)
|
||||||
|
{
|
||||||
|
struct symbol_addr addr = { .section = xndx, .address = sym->st_value };
|
||||||
|
|
||||||
|
/* Set addresses for exported symbols */
|
||||||
|
if (addr.section != SHN_UNDEF)
|
||||||
|
for_each(name, set_symbol_addr, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_read_symtab(int fd)
|
||||||
|
{
|
||||||
|
elf_for_each_global(fd, elf_set_symbol_addr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_print_versions(void)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
|
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
|
||||||
|
if (sym->state != SYMBOL_PROCESSED)
|
||||||
|
warn("no information for symbol %s", sym->name);
|
||||||
|
|
||||||
|
printf("#SYMVER %s 0x%08lx\n", sym->name, sym->crc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_free(void)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
|
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
|
||||||
|
free((void *)sym->name);
|
||||||
|
free(sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_init(symbol_addrs);
|
||||||
|
hash_init(symbol_names);
|
||||||
|
}
|
481
scripts/gendwarfksyms/types.c
Normal file
481
scripts/gendwarfksyms/types.c
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include "gendwarfksyms.h"
|
||||||
|
|
||||||
|
static struct cache expansion_cache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple linked list of shared or owned strings to avoid copying strings
|
||||||
|
* around when not necessary.
|
||||||
|
*/
|
||||||
|
struct type_list_entry {
|
||||||
|
const char *str;
|
||||||
|
void *owned;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void type_list_free(struct list_head *list)
|
||||||
|
{
|
||||||
|
struct type_list_entry *entry;
|
||||||
|
struct type_list_entry *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(entry, tmp, list, list) {
|
||||||
|
if (entry->owned)
|
||||||
|
free(entry->owned);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int type_list_append(struct list_head *list, const char *s, void *owned)
|
||||||
|
{
|
||||||
|
struct type_list_entry *entry;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
entry = xmalloc(sizeof(struct type_list_entry));
|
||||||
|
entry->str = s;
|
||||||
|
entry->owned = owned;
|
||||||
|
list_add_tail(&entry->list, list);
|
||||||
|
|
||||||
|
return strlen(entry->str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void type_list_write(struct list_head *list, FILE *file)
|
||||||
|
{
|
||||||
|
struct type_list_entry *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, list, list) {
|
||||||
|
if (entry->str)
|
||||||
|
checkp(fputs(entry->str, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An expanded type string in symtypes format.
|
||||||
|
*/
|
||||||
|
struct type_expansion {
|
||||||
|
char *name;
|
||||||
|
size_t len;
|
||||||
|
struct list_head expanded;
|
||||||
|
struct hlist_node hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void type_expansion_init(struct type_expansion *type)
|
||||||
|
{
|
||||||
|
type->name = NULL;
|
||||||
|
type->len = 0;
|
||||||
|
INIT_LIST_HEAD(&type->expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void type_expansion_free(struct type_expansion *type)
|
||||||
|
{
|
||||||
|
free(type->name);
|
||||||
|
type->name = NULL;
|
||||||
|
type->len = 0;
|
||||||
|
type_list_free(&type->expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void type_expansion_append(struct type_expansion *type, const char *s,
|
||||||
|
void *owned)
|
||||||
|
{
|
||||||
|
type->len += type_list_append(&type->expanded, s, owned);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* type_map -- the longest expansions for each type.
|
||||||
|
*
|
||||||
|
* const char *name -> struct type_expansion *
|
||||||
|
*/
|
||||||
|
#define TYPE_HASH_BITS 12
|
||||||
|
static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS);
|
||||||
|
|
||||||
|
static int type_map_get(const char *name, struct type_expansion **res)
|
||||||
|
{
|
||||||
|
struct type_expansion *e;
|
||||||
|
|
||||||
|
hash_for_each_possible(type_map, e, hash, hash_str(name)) {
|
||||||
|
if (!strcmp(name, e->name)) {
|
||||||
|
*res = e;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void type_map_add(const char *name, struct type_expansion *type)
|
||||||
|
{
|
||||||
|
struct type_expansion *e;
|
||||||
|
|
||||||
|
if (type_map_get(name, &e)) {
|
||||||
|
e = xmalloc(sizeof(struct type_expansion));
|
||||||
|
type_expansion_init(e);
|
||||||
|
e->name = xstrdup(name);
|
||||||
|
|
||||||
|
hash_add(type_map, &e->hash, hash_str(e->name));
|
||||||
|
|
||||||
|
if (dump_types)
|
||||||
|
debug("adding %s", e->name);
|
||||||
|
} else {
|
||||||
|
/* Use the longest available expansion */
|
||||||
|
if (type->len <= e->len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
type_list_free(&e->expanded);
|
||||||
|
|
||||||
|
if (dump_types)
|
||||||
|
debug("replacing %s", e->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take ownership of type->expanded */
|
||||||
|
list_replace_init(&type->expanded, &e->expanded);
|
||||||
|
e->len = type->len;
|
||||||
|
|
||||||
|
if (dump_types) {
|
||||||
|
checkp(fputs(e->name, stderr));
|
||||||
|
checkp(fputs(" ", stderr));
|
||||||
|
type_list_write(&e->expanded, stderr);
|
||||||
|
checkp(fputs("\n", stderr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void type_map_write(FILE *file)
|
||||||
|
{
|
||||||
|
struct type_expansion *e;
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hash_for_each_safe(type_map, e, tmp, hash) {
|
||||||
|
checkp(fputs(e->name, file));
|
||||||
|
checkp(fputs(" ", file));
|
||||||
|
type_list_write(&e->expanded, file);
|
||||||
|
checkp(fputs("\n", file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void type_map_free(void)
|
||||||
|
{
|
||||||
|
struct type_expansion *e;
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
|
||||||
|
hash_for_each_safe(type_map, e, tmp, hash) {
|
||||||
|
type_expansion_free(e);
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_init(type_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRC for a type, with an optional fully expanded type string for
|
||||||
|
* debugging.
|
||||||
|
*/
|
||||||
|
struct version {
|
||||||
|
struct type_expansion type;
|
||||||
|
unsigned long crc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void version_init(struct version *version)
|
||||||
|
{
|
||||||
|
version->crc = crc32(0, NULL, 0);
|
||||||
|
type_expansion_init(&version->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void version_free(struct version *version)
|
||||||
|
{
|
||||||
|
type_expansion_free(&version->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void version_add(struct version *version, const char *s)
|
||||||
|
{
|
||||||
|
version->crc = crc32(version->crc, (void *)s, strlen(s));
|
||||||
|
if (dump_versions)
|
||||||
|
type_expansion_append(&version->type, s, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type reference format: <prefix>#<name>, where prefix:
|
||||||
|
* s -> structure
|
||||||
|
* u -> union
|
||||||
|
* e -> enum
|
||||||
|
* t -> typedef
|
||||||
|
*
|
||||||
|
* Names with spaces are additionally wrapped in single quotes.
|
||||||
|
*/
|
||||||
|
static inline bool is_type_prefix(const char *s)
|
||||||
|
{
|
||||||
|
return (s[0] == 's' || s[0] == 'u' || s[0] == 'e' || s[0] == 't') &&
|
||||||
|
s[1] == '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
static char get_type_prefix(int tag)
|
||||||
|
{
|
||||||
|
switch (tag) {
|
||||||
|
case DW_TAG_class_type:
|
||||||
|
case DW_TAG_structure_type:
|
||||||
|
return 's';
|
||||||
|
case DW_TAG_union_type:
|
||||||
|
return 'u';
|
||||||
|
case DW_TAG_enumeration_type:
|
||||||
|
return 'e';
|
||||||
|
case DW_TAG_typedef_type:
|
||||||
|
return 't';
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *get_type_name(struct die *cache)
|
||||||
|
{
|
||||||
|
const char *quote;
|
||||||
|
char prefix;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if (cache->state == DIE_INCOMPLETE) {
|
||||||
|
warn("found incomplete cache entry: %p", cache);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cache->state == DIE_SYMBOL)
|
||||||
|
return NULL;
|
||||||
|
if (!cache->fqn || !*cache->fqn)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
prefix = get_type_prefix(cache->tag);
|
||||||
|
if (!prefix)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Wrap names with spaces in single quotes */
|
||||||
|
quote = strstr(cache->fqn, " ") ? "'" : "";
|
||||||
|
|
||||||
|
/* <prefix>#<type_name>\0 */
|
||||||
|
if (asprintf(&name, "%c#%s%s%s", prefix, quote, cache->fqn, quote) < 0)
|
||||||
|
error("asprintf failed for '%s'", cache->fqn);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __calculate_version(struct version *version, struct list_head *list)
|
||||||
|
{
|
||||||
|
struct type_list_entry *entry;
|
||||||
|
struct type_expansion *e;
|
||||||
|
|
||||||
|
/* Calculate a CRC over an expanded type string */
|
||||||
|
list_for_each_entry(entry, list, list) {
|
||||||
|
if (is_type_prefix(entry->str)) {
|
||||||
|
check(type_map_get(entry->str, &e));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It's sufficient to expand each type reference just
|
||||||
|
* once to detect changes.
|
||||||
|
*/
|
||||||
|
if (cache_was_expanded(&expansion_cache, e)) {
|
||||||
|
version_add(version, entry->str);
|
||||||
|
} else {
|
||||||
|
cache_mark_expanded(&expansion_cache, e);
|
||||||
|
__calculate_version(version, &e->expanded);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
version_add(version, entry->str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_version(struct version *version, struct list_head *list)
|
||||||
|
{
|
||||||
|
version_init(version);
|
||||||
|
__calculate_version(version, list);
|
||||||
|
cache_free(&expansion_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __type_expand(struct die *cache, struct type_expansion *type,
|
||||||
|
bool recursive);
|
||||||
|
|
||||||
|
static void type_expand_child(struct die *cache, struct type_expansion *type,
|
||||||
|
bool recursive)
|
||||||
|
{
|
||||||
|
struct type_expansion child;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
name = get_type_name(cache);
|
||||||
|
if (!name) {
|
||||||
|
__type_expand(cache, type, recursive);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recursive && !__cache_was_expanded(&expansion_cache, cache->addr)) {
|
||||||
|
__cache_mark_expanded(&expansion_cache, cache->addr);
|
||||||
|
type_expansion_init(&child);
|
||||||
|
__type_expand(cache, &child, true);
|
||||||
|
type_map_add(name, &child);
|
||||||
|
type_expansion_free(&child);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_expansion_append(type, name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __type_expand(struct die *cache, struct type_expansion *type,
|
||||||
|
bool recursive)
|
||||||
|
{
|
||||||
|
struct die_fragment *df;
|
||||||
|
struct die *child;
|
||||||
|
|
||||||
|
list_for_each_entry(df, &cache->fragments, list) {
|
||||||
|
switch (df->type) {
|
||||||
|
case FRAGMENT_STRING:
|
||||||
|
type_expansion_append(type, df->data.str, NULL);
|
||||||
|
break;
|
||||||
|
case FRAGMENT_DIE:
|
||||||
|
/* Use a complete die_map expansion if available */
|
||||||
|
if (__die_map_get(df->data.addr, DIE_COMPLETE,
|
||||||
|
&child) &&
|
||||||
|
__die_map_get(df->data.addr, DIE_UNEXPANDED,
|
||||||
|
&child))
|
||||||
|
error("unknown child: %" PRIxPTR,
|
||||||
|
df->data.addr);
|
||||||
|
|
||||||
|
type_expand_child(child, type, recursive);
|
||||||
|
break;
|
||||||
|
case FRAGMENT_LINEBREAK:
|
||||||
|
/*
|
||||||
|
* Keep whitespace in the symtypes format, but avoid
|
||||||
|
* repeated spaces.
|
||||||
|
*/
|
||||||
|
if (list_is_last(&df->list, &cache->fragments) ||
|
||||||
|
list_next_entry(df, list)->type !=
|
||||||
|
FRAGMENT_LINEBREAK)
|
||||||
|
type_expansion_append(type, " ", NULL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("empty die_fragment in %p", cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void type_expand(struct die *cache, struct type_expansion *type,
|
||||||
|
bool recursive)
|
||||||
|
{
|
||||||
|
type_expansion_init(type);
|
||||||
|
__type_expand(cache, type, recursive);
|
||||||
|
cache_free(&expansion_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expand_type(struct die *cache, void *arg)
|
||||||
|
{
|
||||||
|
struct type_expansion type;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if (cache->mapped)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cache->mapped = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip unexpanded die_map entries if there's a complete
|
||||||
|
* expansion available for this DIE.
|
||||||
|
*/
|
||||||
|
if (cache->state == DIE_UNEXPANDED &&
|
||||||
|
!__die_map_get(cache->addr, DIE_COMPLETE, &cache)) {
|
||||||
|
if (cache->mapped)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cache->mapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = get_type_name(cache);
|
||||||
|
if (!name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
debug("%s", name);
|
||||||
|
type_expand(cache, &type, true);
|
||||||
|
type_map_add(name, &type);
|
||||||
|
|
||||||
|
type_expansion_free(&type);
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expand_symbol(struct symbol *sym, void *arg)
|
||||||
|
{
|
||||||
|
struct type_expansion type;
|
||||||
|
struct version version;
|
||||||
|
struct die *cache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No need to expand again unless we want a symtypes file entry
|
||||||
|
* for the symbol. Note that this means `sym` has the same address
|
||||||
|
* as another symbol that was already processed.
|
||||||
|
*/
|
||||||
|
if (!symtypes && sym->state == SYMBOL_PROCESSED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
|
||||||
|
return; /* We'll warn about missing CRCs later. */
|
||||||
|
|
||||||
|
type_expand(cache, &type, false);
|
||||||
|
|
||||||
|
/* If the symbol already has a version, don't calculate it again. */
|
||||||
|
if (sym->state != SYMBOL_PROCESSED) {
|
||||||
|
calculate_version(&version, &type.expanded);
|
||||||
|
symbol_set_crc(sym, version.crc);
|
||||||
|
debug("%s = %lx", sym->name, version.crc);
|
||||||
|
|
||||||
|
if (dump_versions) {
|
||||||
|
checkp(fputs(sym->name, stderr));
|
||||||
|
checkp(fputs(" ", stderr));
|
||||||
|
type_list_write(&version.type.expanded, stderr);
|
||||||
|
checkp(fputs("\n", stderr));
|
||||||
|
}
|
||||||
|
|
||||||
|
version_free(&version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* These aren't needed in type_map unless we want a symtypes file. */
|
||||||
|
if (symtypes)
|
||||||
|
type_map_add(sym->name, &type);
|
||||||
|
|
||||||
|
type_expansion_free(&type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_symtypes_and_versions(FILE *file)
|
||||||
|
{
|
||||||
|
cache_init(&expansion_cache);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* die_map processing:
|
||||||
|
*
|
||||||
|
* 1. die_map contains all types referenced in exported symbol
|
||||||
|
* signatures, but can contain duplicates just like the original
|
||||||
|
* DWARF, and some references may not be fully expanded depending
|
||||||
|
* on how far we processed the DIE tree for that specific symbol.
|
||||||
|
*
|
||||||
|
* For each die_map entry, find the longest available expansion,
|
||||||
|
* and add it to type_map.
|
||||||
|
*/
|
||||||
|
die_map_for_each(expand_type, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2. For each exported symbol, expand the die_map type, and use
|
||||||
|
* type_map expansions to calculate a symbol version from the
|
||||||
|
* fully expanded type string.
|
||||||
|
*/
|
||||||
|
symbol_for_each(expand_symbol, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3. If a symtypes file is requested, write type_map contents to
|
||||||
|
* the file.
|
||||||
|
*/
|
||||||
|
type_map_write(file);
|
||||||
|
type_map_free();
|
||||||
|
}
|
@ -12,18 +12,19 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include <hashtable.h>
|
||||||
|
|
||||||
#include "genksyms.h"
|
#include "genksyms.h"
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
#define HASH_BUCKETS 4096
|
static HASHTABLE_DEFINE(symbol_hashtable, 1U << 12);
|
||||||
|
|
||||||
static struct symbol *symtab[HASH_BUCKETS];
|
|
||||||
static FILE *debugfile;
|
static FILE *debugfile;
|
||||||
|
|
||||||
int cur_line = 1;
|
int cur_line = 1;
|
||||||
@ -60,7 +61,7 @@ static void print_type_name(enum symbol_type type, const char *name);
|
|||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
static const unsigned int crctab32[] = {
|
static const uint32_t crctab32[] = {
|
||||||
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
|
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
|
||||||
0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
|
0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
|
||||||
0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
|
0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
|
||||||
@ -115,19 +116,19 @@ static const unsigned int crctab32[] = {
|
|||||||
0x2d02ef8dU
|
0x2d02ef8dU
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
|
static uint32_t partial_crc32_one(uint8_t c, uint32_t crc)
|
||||||
{
|
{
|
||||||
return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
|
return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long partial_crc32(const char *s, unsigned long crc)
|
static uint32_t partial_crc32(const char *s, uint32_t crc)
|
||||||
{
|
{
|
||||||
while (*s)
|
while (*s)
|
||||||
crc = partial_crc32_one(*s++, crc);
|
crc = partial_crc32_one(*s++, crc);
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long crc32(const char *s)
|
static uint32_t crc32(const char *s)
|
||||||
{
|
{
|
||||||
return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
|
return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
|
||||||
}
|
}
|
||||||
@ -151,14 +152,14 @@ static enum symbol_type map_to_ns(enum symbol_type t)
|
|||||||
|
|
||||||
struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
|
struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
|
||||||
{
|
{
|
||||||
unsigned long h = crc32(name) % HASH_BUCKETS;
|
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
|
|
||||||
for (sym = symtab[h]; sym; sym = sym->hash_next)
|
hash_for_each_possible(symbol_hashtable, sym, hnode, crc32(name)) {
|
||||||
if (map_to_ns(sym->type) == map_to_ns(ns) &&
|
if (map_to_ns(sym->type) == map_to_ns(ns) &&
|
||||||
strcmp(name, sym->name) == 0 &&
|
strcmp(name, sym->name) == 0 &&
|
||||||
sym->is_declared)
|
sym->is_declared)
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (exact && sym && sym->type != ns)
|
if (exact && sym && sym->type != ns)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -224,64 +225,56 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
h = crc32(name) % HASH_BUCKETS;
|
h = crc32(name);
|
||||||
for (sym = symtab[h]; sym; sym = sym->hash_next) {
|
hash_for_each_possible(symbol_hashtable, sym, hnode, h) {
|
||||||
if (map_to_ns(sym->type) == map_to_ns(type) &&
|
if (map_to_ns(sym->type) != map_to_ns(type) ||
|
||||||
strcmp(name, sym->name) == 0) {
|
strcmp(name, sym->name))
|
||||||
if (is_reference)
|
continue;
|
||||||
/* fall through */ ;
|
|
||||||
else if (sym->type == type &&
|
if (is_reference) {
|
||||||
equal_list(sym->defn, defn)) {
|
break;
|
||||||
if (!sym->is_declared && sym->is_override) {
|
} else if (sym->type == type && equal_list(sym->defn, defn)) {
|
||||||
print_location();
|
if (!sym->is_declared && sym->is_override) {
|
||||||
print_type_name(type, name);
|
print_location();
|
||||||
fprintf(stderr, " modversion is "
|
print_type_name(type, name);
|
||||||
"unchanged\n");
|
fprintf(stderr, " modversion is unchanged\n");
|
||||||
}
|
|
||||||
sym->is_declared = 1;
|
|
||||||
return sym;
|
|
||||||
} else if (!sym->is_declared) {
|
|
||||||
if (sym->is_override && flag_preserve) {
|
|
||||||
print_location();
|
|
||||||
fprintf(stderr, "ignoring ");
|
|
||||||
print_type_name(type, name);
|
|
||||||
fprintf(stderr, " modversion change\n");
|
|
||||||
sym->is_declared = 1;
|
|
||||||
return sym;
|
|
||||||
} else {
|
|
||||||
status = is_unknown_symbol(sym) ?
|
|
||||||
STATUS_DEFINED : STATUS_MODIFIED;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error_with_pos("redefinition of %s", name);
|
|
||||||
return sym;
|
|
||||||
}
|
}
|
||||||
|
sym->is_declared = 1;
|
||||||
|
} else if (sym->is_declared) {
|
||||||
|
error_with_pos("redefinition of %s", name);
|
||||||
|
} else if (sym->is_override && flag_preserve) {
|
||||||
|
print_location();
|
||||||
|
fprintf(stderr, "ignoring ");
|
||||||
|
print_type_name(type, name);
|
||||||
|
fprintf(stderr, " modversion change\n");
|
||||||
|
sym->is_declared = 1;
|
||||||
|
} else {
|
||||||
|
status = is_unknown_symbol(sym) ?
|
||||||
|
STATUS_DEFINED : STATUS_MODIFIED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
free_list(defn, NULL);
|
||||||
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sym) {
|
if (sym) {
|
||||||
struct symbol **psym;
|
hash_del(&sym->hnode);
|
||||||
|
|
||||||
for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
|
free_list(sym->defn, NULL);
|
||||||
if (*psym == sym) {
|
free(sym->name);
|
||||||
*psym = sym->hash_next;
|
free(sym);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
--nsyms;
|
--nsyms;
|
||||||
}
|
}
|
||||||
|
|
||||||
sym = xmalloc(sizeof(*sym));
|
sym = xmalloc(sizeof(*sym));
|
||||||
sym->name = name;
|
sym->name = xstrdup(name);
|
||||||
sym->type = type;
|
sym->type = type;
|
||||||
sym->defn = defn;
|
sym->defn = defn;
|
||||||
sym->expansion_trail = NULL;
|
sym->expansion_trail = NULL;
|
||||||
sym->visited = NULL;
|
sym->visited = NULL;
|
||||||
sym->is_extern = is_extern;
|
sym->is_extern = is_extern;
|
||||||
|
|
||||||
sym->hash_next = symtab[h];
|
hash_add(symbol_hashtable, &sym->hnode, h);
|
||||||
symtab[h] = sym;
|
|
||||||
|
|
||||||
sym->is_declared = !is_reference;
|
sym->is_declared = !is_reference;
|
||||||
sym->status = status;
|
sym->status = status;
|
||||||
@ -480,7 +473,7 @@ static void read_reference(FILE *f)
|
|||||||
defn = def;
|
defn = def;
|
||||||
def = read_node(f);
|
def = read_node(f);
|
||||||
}
|
}
|
||||||
subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
|
subsym = add_reference_symbol(sym->string, sym->tag,
|
||||||
defn, is_extern);
|
defn, is_extern);
|
||||||
subsym->is_override = is_override;
|
subsym->is_override = is_override;
|
||||||
free_node(sym);
|
free_node(sym);
|
||||||
@ -525,7 +518,7 @@ static void print_list(FILE * f, struct string_list *list)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
|
static uint32_t expand_and_crc_sym(struct symbol *sym, uint32_t crc)
|
||||||
{
|
{
|
||||||
struct string_list *list = sym->defn;
|
struct string_list *list = sym->defn;
|
||||||
struct string_list **e, **b;
|
struct string_list **e, **b;
|
||||||
@ -632,7 +625,7 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
|
|||||||
void export_symbol(const char *name)
|
void export_symbol(const char *name)
|
||||||
{
|
{
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
unsigned long crc;
|
uint32_t crc;
|
||||||
int has_changed = 0;
|
int has_changed = 0;
|
||||||
|
|
||||||
sym = find_symbol(name, SYM_NORMAL, 0);
|
sym = find_symbol(name, SYM_NORMAL, 0);
|
||||||
@ -680,7 +673,7 @@ void export_symbol(const char *name)
|
|||||||
if (flag_dump_defs)
|
if (flag_dump_defs)
|
||||||
fputs(">\n", debugfile);
|
fputs(">\n", debugfile);
|
||||||
|
|
||||||
printf("#SYMVER %s 0x%08lx\n", name, crc);
|
printf("#SYMVER %s 0x%08lx\n", name, (unsigned long)crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
@ -832,9 +825,9 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (flag_debug) {
|
if (flag_debug) {
|
||||||
fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
|
fprintf(debugfile, "Hash table occupancy %d/%zd = %g\n",
|
||||||
nsyms, HASH_BUCKETS,
|
nsyms, HASH_SIZE(symbol_hashtable),
|
||||||
(double)nsyms / (double)HASH_BUCKETS);
|
(double)nsyms / HASH_SIZE(symbol_hashtable));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dumpfile)
|
if (dumpfile)
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <list_types.h>
|
||||||
|
|
||||||
enum symbol_type {
|
enum symbol_type {
|
||||||
SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION,
|
SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION,
|
||||||
SYM_ENUM_CONST
|
SYM_ENUM_CONST
|
||||||
@ -31,8 +33,8 @@ struct string_list {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct symbol {
|
struct symbol {
|
||||||
struct symbol *hash_next;
|
struct hlist_node hnode;
|
||||||
const char *name;
|
char *name;
|
||||||
enum symbol_type type;
|
enum symbol_type type;
|
||||||
struct string_list *defn;
|
struct string_list *defn;
|
||||||
struct symbol *expansion_trail;
|
struct symbol *expansion_trail;
|
||||||
|
@ -152,14 +152,19 @@ simple_declaration:
|
|||||||
;
|
;
|
||||||
|
|
||||||
init_declarator_list_opt:
|
init_declarator_list_opt:
|
||||||
/* empty */ { $$ = NULL; }
|
/* empty */ { $$ = NULL; }
|
||||||
| init_declarator_list
|
| init_declarator_list { free_list(decl_spec, NULL); $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
init_declarator_list:
|
init_declarator_list:
|
||||||
init_declarator
|
init_declarator
|
||||||
{ struct string_list *decl = *$1;
|
{ struct string_list *decl = *$1;
|
||||||
*$1 = NULL;
|
*$1 = NULL;
|
||||||
|
|
||||||
|
/* avoid sharing among multiple init_declarators */
|
||||||
|
if (decl_spec)
|
||||||
|
decl_spec = copy_list_range(decl_spec, NULL);
|
||||||
|
|
||||||
add_symbol(current_name,
|
add_symbol(current_name,
|
||||||
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
|
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
|
||||||
current_name = NULL;
|
current_name = NULL;
|
||||||
@ -170,6 +175,11 @@ init_declarator_list:
|
|||||||
*$3 = NULL;
|
*$3 = NULL;
|
||||||
free_list(*$2, NULL);
|
free_list(*$2, NULL);
|
||||||
*$2 = decl_spec;
|
*$2 = decl_spec;
|
||||||
|
|
||||||
|
/* avoid sharing among multiple init_declarators */
|
||||||
|
if (decl_spec)
|
||||||
|
decl_spec = copy_list_range(decl_spec, NULL);
|
||||||
|
|
||||||
add_symbol(current_name,
|
add_symbol(current_name,
|
||||||
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
|
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
|
||||||
current_name = NULL;
|
current_name = NULL;
|
||||||
@ -472,12 +482,12 @@ enumerator_list:
|
|||||||
enumerator:
|
enumerator:
|
||||||
IDENT
|
IDENT
|
||||||
{
|
{
|
||||||
const char *name = strdup((*$1)->string);
|
const char *name = (*$1)->string;
|
||||||
add_symbol(name, SYM_ENUM_CONST, NULL, 0);
|
add_symbol(name, SYM_ENUM_CONST, NULL, 0);
|
||||||
}
|
}
|
||||||
| IDENT '=' EXPRESSION_PHRASE
|
| IDENT '=' EXPRESSION_PHRASE
|
||||||
{
|
{
|
||||||
const char *name = strdup((*$1)->string);
|
const char *name = (*$1)->string;
|
||||||
struct string_list *expr = copy_list_range(*$3, *$2);
|
struct string_list *expr = copy_list_range(*$3, *$2);
|
||||||
add_symbol(name, SYM_ENUM_CONST, expr, 0);
|
add_symbol(name, SYM_ENUM_CONST, expr, 0);
|
||||||
}
|
}
|
||||||
|
@ -105,9 +105,11 @@ configfiles = $(wildcard $(srctree)/kernel/configs/$(1) $(srctree)/arch/$(SRCARC
|
|||||||
all-config-fragments = $(call configfiles,*.config)
|
all-config-fragments = $(call configfiles,*.config)
|
||||||
config-fragments = $(call configfiles,$@)
|
config-fragments = $(call configfiles,$@)
|
||||||
|
|
||||||
|
cmd_merge_fragments = $(srctree)/scripts/kconfig/merge_config.sh -m $(KCONFIG_CONFIG) $(config-fragments)
|
||||||
|
|
||||||
%.config: $(obj)/conf
|
%.config: $(obj)/conf
|
||||||
$(if $(config-fragments),, $(error $@ fragment does not exists on this architecture))
|
$(if $(config-fragments),, $(error $@ fragment does not exists on this architecture))
|
||||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m $(KCONFIG_CONFIG) $(config-fragments)
|
$(call cmd,merge_fragments)
|
||||||
$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
|
$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
|
||||||
|
|
||||||
PHONY += tinyconfig
|
PHONY += tinyconfig
|
||||||
|
@ -1464,8 +1464,8 @@ void ConfigMainWindow::loadConfig(void)
|
|||||||
{
|
{
|
||||||
QString str;
|
QString str;
|
||||||
|
|
||||||
str = QFileDialog::getOpenFileName(this, "", configname);
|
str = QFileDialog::getOpenFileName(this, QString(), configname);
|
||||||
if (str.isNull())
|
if (str.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conf_read(str.toLocal8Bit().constData()))
|
if (conf_read(str.toLocal8Bit().constData()))
|
||||||
@ -1491,8 +1491,8 @@ void ConfigMainWindow::saveConfigAs(void)
|
|||||||
{
|
{
|
||||||
QString str;
|
QString str;
|
||||||
|
|
||||||
str = QFileDialog::getSaveFileName(this, "", configname);
|
str = QFileDialog::getSaveFileName(this, QString(), configname);
|
||||||
if (str.isNull())
|
if (str.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conf_write(str.toLocal8Bit().constData())) {
|
if (conf_write(str.toLocal8Bit().constData())) {
|
||||||
|
@ -33,6 +33,10 @@ static bool module_enabled;
|
|||||||
static bool modversions;
|
static bool modversions;
|
||||||
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
|
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
|
||||||
static bool all_versions;
|
static bool all_versions;
|
||||||
|
/* Is CONFIG_BASIC_MODVERSIONS set? */
|
||||||
|
static bool basic_modversions;
|
||||||
|
/* Is CONFIG_EXTENDED_MODVERSIONS set? */
|
||||||
|
static bool extended_modversions;
|
||||||
/* If we are modposting external module set to 1 */
|
/* If we are modposting external module set to 1 */
|
||||||
static bool external_module;
|
static bool external_module;
|
||||||
/* Only warn about unresolved symbols */
|
/* Only warn about unresolved symbols */
|
||||||
@ -1805,6 +1809,49 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record CRCs for unresolved symbols, supporting long names
|
||||||
|
*/
|
||||||
|
static void add_extended_versions(struct buffer *b, struct module *mod)
|
||||||
|
{
|
||||||
|
struct symbol *s;
|
||||||
|
|
||||||
|
if (!extended_modversions)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buf_printf(b, "\n");
|
||||||
|
buf_printf(b, "static const u32 ____version_ext_crcs[]\n");
|
||||||
|
buf_printf(b, "__used __section(\"__version_ext_crcs\") = {\n");
|
||||||
|
list_for_each_entry(s, &mod->unresolved_symbols, list) {
|
||||||
|
if (!s->module)
|
||||||
|
continue;
|
||||||
|
if (!s->crc_valid) {
|
||||||
|
warn("\"%s\" [%s.ko] has no CRC!\n",
|
||||||
|
s->name, mod->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf_printf(b, "\t0x%08x,\n", s->crc);
|
||||||
|
}
|
||||||
|
buf_printf(b, "};\n");
|
||||||
|
|
||||||
|
buf_printf(b, "static const char ____version_ext_names[]\n");
|
||||||
|
buf_printf(b, "__used __section(\"__version_ext_names\") =\n");
|
||||||
|
list_for_each_entry(s, &mod->unresolved_symbols, list) {
|
||||||
|
if (!s->module)
|
||||||
|
continue;
|
||||||
|
if (!s->crc_valid)
|
||||||
|
/*
|
||||||
|
* We already warned on this when producing the crc
|
||||||
|
* table.
|
||||||
|
* We need to skip its name too, as the indexes in
|
||||||
|
* both tables need to align.
|
||||||
|
*/
|
||||||
|
continue;
|
||||||
|
buf_printf(b, "\t\"%s\\0\"\n", s->name);
|
||||||
|
}
|
||||||
|
buf_printf(b, ";\n");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record CRCs for unresolved symbols
|
* Record CRCs for unresolved symbols
|
||||||
**/
|
**/
|
||||||
@ -1812,7 +1859,7 @@ static void add_versions(struct buffer *b, struct module *mod)
|
|||||||
{
|
{
|
||||||
struct symbol *s;
|
struct symbol *s;
|
||||||
|
|
||||||
if (!modversions)
|
if (!basic_modversions)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buf_printf(b, "\n");
|
buf_printf(b, "\n");
|
||||||
@ -1828,11 +1875,16 @@ static void add_versions(struct buffer *b, struct module *mod)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strlen(s->name) >= MODULE_NAME_LEN) {
|
if (strlen(s->name) >= MODULE_NAME_LEN) {
|
||||||
error("too long symbol \"%s\" [%s.ko]\n",
|
if (extended_modversions) {
|
||||||
s->name, mod->name);
|
/* this symbol will only be in the extended info */
|
||||||
break;
|
continue;
|
||||||
|
} else {
|
||||||
|
error("too long symbol \"%s\" [%s.ko]\n",
|
||||||
|
s->name, mod->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buf_printf(b, "\t{ %#8x, \"%s\" },\n",
|
buf_printf(b, "\t{ 0x%08x, \"%s\" },\n",
|
||||||
s->crc, s->name);
|
s->crc, s->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1961,6 +2013,7 @@ static void write_mod_c_file(struct module *mod)
|
|||||||
add_header(&buf, mod);
|
add_header(&buf, mod);
|
||||||
add_exported_symbols(&buf, mod);
|
add_exported_symbols(&buf, mod);
|
||||||
add_versions(&buf, mod);
|
add_versions(&buf, mod);
|
||||||
|
add_extended_versions(&buf, mod);
|
||||||
add_depends(&buf, mod);
|
add_depends(&buf, mod);
|
||||||
|
|
||||||
buf_printf(&buf, "\n");
|
buf_printf(&buf, "\n");
|
||||||
@ -2126,7 +2179,7 @@ int main(int argc, char **argv)
|
|||||||
LIST_HEAD(dump_lists);
|
LIST_HEAD(dump_lists);
|
||||||
struct dump_list *dl, *dl2;
|
struct dump_list *dl, *dl2;
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) {
|
while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:xb")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'e':
|
case 'e':
|
||||||
external_module = true;
|
external_module = true;
|
||||||
@ -2175,6 +2228,12 @@ int main(int argc, char **argv)
|
|||||||
case 'd':
|
case 'd':
|
||||||
missing_namespace_deps = optarg;
|
missing_namespace_deps = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
basic_modversions = true;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
extended_modversions = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ license=(GPL-2.0-only)
|
|||||||
makedepends=(
|
makedepends=(
|
||||||
bc
|
bc
|
||||||
bison
|
bison
|
||||||
cpio
|
|
||||||
flex
|
flex
|
||||||
gettext
|
gettext
|
||||||
kmod
|
kmod
|
||||||
|
@ -5,10 +5,12 @@
|
|||||||
#
|
#
|
||||||
# Simple script to generate a deb package for a Linux kernel. All the
|
# Simple script to generate a deb package for a Linux kernel. All the
|
||||||
# complexity of what to do with a kernel after it is installed or removed
|
# complexity of what to do with a kernel after it is installed or removed
|
||||||
# is left to other scripts and packages: they can install scripts in the
|
# is left to other scripts and packages. Scripts can be placed into the
|
||||||
# /etc/kernel/{pre,post}{inst,rm}.d/ directories (or an alternative location
|
# preinst, postinst, prerm and postrm directories in /etc/kernel or
|
||||||
# specified in KDEB_HOOKDIR) that will be called on package install and
|
# /usr/share/kernel. A different list of search directories can be given
|
||||||
# removal.
|
# via KDEB_HOOKDIR. Scripts in directories earlier in the list will
|
||||||
|
# override scripts of the same name in later directories. The script will
|
||||||
|
# be called on package installation and removal.
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
@ -74,10 +76,8 @@ install_maint_scripts () {
|
|||||||
# kernel packages, as well as kernel packages built using make-kpkg.
|
# kernel packages, as well as kernel packages built using make-kpkg.
|
||||||
# make-kpkg sets $INITRD to indicate whether an initramfs is wanted, and
|
# make-kpkg sets $INITRD to indicate whether an initramfs is wanted, and
|
||||||
# so do we; recent versions of dracut and initramfs-tools will obey this.
|
# so do we; recent versions of dracut and initramfs-tools will obey this.
|
||||||
debhookdir=${KDEB_HOOKDIR:-/etc/kernel}
|
debhookdir=${KDEB_HOOKDIR:-/etc/kernel /usr/share/kernel}
|
||||||
for script in postinst postrm preinst prerm; do
|
for script in postinst postrm preinst prerm; do
|
||||||
mkdir -p "${pdir}${debhookdir}/${script}.d"
|
|
||||||
|
|
||||||
mkdir -p "${pdir}/DEBIAN"
|
mkdir -p "${pdir}/DEBIAN"
|
||||||
cat <<-EOF > "${pdir}/DEBIAN/${script}"
|
cat <<-EOF > "${pdir}/DEBIAN/${script}"
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
@ -90,7 +90,15 @@ install_maint_scripts () {
|
|||||||
# Tell initramfs builder whether it's wanted
|
# Tell initramfs builder whether it's wanted
|
||||||
export INITRD=$(if_enabled_echo CONFIG_BLK_DEV_INITRD Yes No)
|
export INITRD=$(if_enabled_echo CONFIG_BLK_DEV_INITRD Yes No)
|
||||||
|
|
||||||
test -d ${debhookdir}/${script}.d && run-parts --arg="${KERNELRELEASE}" --arg="/${installed_image_path}" ${debhookdir}/${script}.d
|
# run-parts will error out if one of its directory arguments does not
|
||||||
|
# exist, so filter the list of hook directories accordingly.
|
||||||
|
hookdirs=
|
||||||
|
for dir in ${debhookdir}; do
|
||||||
|
test -d "\$dir/${script}.d" || continue
|
||||||
|
hookdirs="\$hookdirs \$dir/${script}.d"
|
||||||
|
done
|
||||||
|
hookdirs="\${hookdirs# }"
|
||||||
|
test -n "\$hookdirs" && run-parts --arg="${KERNELRELEASE}" --arg="/${installed_image_path}" \$hookdirs
|
||||||
exit 0
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
chmod 755 "${pdir}/DEBIAN/${script}"
|
chmod 755 "${pdir}/DEBIAN/${script}"
|
||||||
|
@ -49,17 +49,10 @@ mkdir -p "${destdir}"
|
|||||||
# This caters to host programs that participate in Kbuild. objtool and
|
# This caters to host programs that participate in Kbuild. objtool and
|
||||||
# resolve_btfids are out of scope.
|
# resolve_btfids are out of scope.
|
||||||
if [ "${CC}" != "${HOSTCC}" ]; then
|
if [ "${CC}" != "${HOSTCC}" ]; then
|
||||||
echo "Rebuilding host programs with ${CC}..."
|
cat "${destdir}/scripts/Makefile" - <<-'EOF' > "${destdir}/scripts/Kbuild"
|
||||||
|
subdir-y += basic
|
||||||
# This leverages external module building.
|
hostprogs-always-y += mod/modpost
|
||||||
# - Clear sub_make_done to allow the top-level Makefile to redo sub-make.
|
mod/modpost-objs := $(addprefix mod/, modpost.o file2alias.o sumversion.o symsearch.o)
|
||||||
# - Filter out --no-print-directory to print "Entering directory" logs
|
|
||||||
# when Make changes the working directory.
|
|
||||||
unset sub_make_done
|
|
||||||
MAKEFLAGS=$(echo "${MAKEFLAGS}" | sed s/--no-print-directory//)
|
|
||||||
|
|
||||||
cat <<-'EOF' > "${destdir}/Kbuild"
|
|
||||||
subdir-y := scripts
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# HOSTCXX is not overridden. The C++ compiler is used to build:
|
# HOSTCXX is not overridden. The C++ compiler is used to build:
|
||||||
@ -67,20 +60,12 @@ if [ "${CC}" != "${HOSTCC}" ]; then
|
|||||||
# - GCC plugins, which will not work on the installed system even after
|
# - GCC plugins, which will not work on the installed system even after
|
||||||
# being rebuilt.
|
# being rebuilt.
|
||||||
#
|
#
|
||||||
# Use the single-target build to avoid the modpost invocation, which
|
# Clear VPATH and srcroot because the source files reside in the output
|
||||||
# would overwrite Module.symvers.
|
# directory.
|
||||||
"${MAKE}" HOSTCC="${CC}" KBUILD_OUTPUT=. KBUILD_EXTMOD="${destdir}" scripts/
|
# shellcheck disable=SC2016 # $(MAKE), $(CC), and $(build) will be expanded by Make
|
||||||
|
"${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC=$(CC) VPATH= srcroot=. $(build)='"${destdir}"/scripts
|
||||||
|
|
||||||
cat <<-'EOF' > "${destdir}/scripts/Kbuild"
|
rm -f "${destdir}/scripts/Kbuild"
|
||||||
subdir-y := basic
|
|
||||||
hostprogs-always-y := mod/modpost
|
|
||||||
mod/modpost-objs := $(addprefix mod/, modpost.o file2alias.o sumversion.o symsearch.o)
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Run once again to rebuild scripts/basic/ and scripts/mod/modpost.
|
|
||||||
"${MAKE}" HOSTCC="${CC}" KBUILD_OUTPUT=. KBUILD_EXTMOD="${destdir}" scripts/
|
|
||||||
|
|
||||||
rm -f "${destdir}/Kbuild" "${destdir}/scripts/Kbuild"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete
|
find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete
|
||||||
|
@ -205,7 +205,7 @@ Priority: optional
|
|||||||
Maintainer: $maintainer
|
Maintainer: $maintainer
|
||||||
Rules-Requires-Root: no
|
Rules-Requires-Root: no
|
||||||
Build-Depends: debhelper-compat (= 12)
|
Build-Depends: debhelper-compat (= 12)
|
||||||
Build-Depends-Arch: bc, bison, cpio, flex,
|
Build-Depends-Arch: bc, bison, flex,
|
||||||
gcc-${host_gnu} <!pkg.${sourcename}.nokernelheaders>,
|
gcc-${host_gnu} <!pkg.${sourcename}.nokernelheaders>,
|
||||||
kmod, libelf-dev:native,
|
kmod, libelf-dev:native,
|
||||||
libssl-dev:native, libssl-dev <!pkg.${sourcename}.nokernelheaders>,
|
libssl-dev:native, libssl-dev <!pkg.${sourcename}.nokernelheaders>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user