[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v2] Support pre and post v4.19 ksymtabs for Linux kernel modules
Hello Jessica,
Jessica Yu <jeyu@kernel.org> a écrit:
> As described in commit ad8c2531fb9, the format of the Linux kernel
> ksymtab changed in v4.19 to use relative references instead of absolute
> references. This changes the type of relocations emitted for ksymtab
> sections to be place-relative 32-bit relocations instead of absolute
> relocations. One side-effect of this is that libdwfl will not relocate
> the ksymtab sections due to the PC-relative relocations. This breaks
> load_kernel_symbol_table() for kernel modules because it only reads in
> zeros from the unrelocated ksymtab section and is subsequently unable to
> determine what exported symbols it refers to. Since a vmlinux binary is
> already fully linked and relocated (and therefore we can read its
> ksymtab section just fine), this problem is only relevant to Linux
> kernel modules.
>
> To work around this, we utilize the ksymtab relocation sections to
> determine which symbols the ksymtab entries refer to. We do this by
> inspecting each relocation's r_info field for the symbol table index and
> from there we are able to read each symbol's value and subsequently add
> that to the set of exported symbols.
>
> In addition, for Linux kernel modules, we can utilize relocation types
> to implement a new heuristic to determine the ksymtab format we have.
> The presence of PC-relative relocations suggest the new v4.19 format,
> and absolute relocation types suggest the old pre v4.19 format.
OK, let me say this straight: this is just plain awesome. I am trying
very hard to avoid falling into any kind of hyperbole here. That's
super hard right now. But I digress.
Thank you for looking into this.
I just have some very minor comments about small things that I took the
liberty to amend to save you for sending a v3 patch for such
insignificant things.
[...]
> diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
[...]
> + populate_symbol_map_from_ksymtab_reloc(Elf_Scn *reloc_section, Elf_Scn *strings_section,
> + address_set_sptr exported_fns_set,
> + address_set_sptr exported_vars_set)
> + {
[...]
> + // If the symbol is from __ksymtab_strings, ignore it.
> + if (symbol->get_shndx() == strings_ndx)
> + continue;
So what we are actually saying here is that we don't want keep ELF
symbol that are actually (exported) string constants defined in the
__ksymtab_strings section, or am I missing something?
If that understanding is correct, then we don't really need/want to
expose a section index (via the get_shndx member function) in the
abigail::ir::elf_symbol type. We need to keep in mind that although
that type is named "*elf* symbol", we still want it to be high-ish
level, in terms of what it exposes, because it's part of the internal
representation of ABI artifacts of Libabigail. I mean, maybe in the
future, we'll use it (and rename it) to support symbols coming from
binary formats that are not ELF (for instance, COFF, MACH-O, etc). I
could argue that we use it to represent things coming from an 'abixml'
format today.
Long story short, I added a boolean named "is_linux_string_cst" to
abigail::ir::elf_symbol. It is meant to tell us whether if the symbol
is an exported linux string constant or not.
The resulting diff hunk exactly looks like:
- // If the symbol is from __ksymtab_strings, ignore it.
- if (symbol->get_shndx() == strings_ndx)
+ // If the symbol is a linux string constant then ignore it.
+ if (symbol->get_is_linux_string_cst())
continue;
As a result, we don't need populate_symbol_map_from_ksymtab_reloc to
take the strings_section in parameter anymore. My change to that part
thus looks like:
@@ -7821,24 +7887,23 @@ public:
///
/// @param section the ksymtab section to read from
///
- /// @param strings_section the __ksymtab_strings section
- ///
/// @param exported_fns_set the set of exported functions
///
/// @param exported_vars_set the set of exported variables
///
/// @return true upon successful completion, false otherwise.
bool
- populate_symbol_map_from_ksymtab_reloc(Elf_Scn *reloc_section, Elf_Scn *strings_section,
+ populate_symbol_map_from_ksymtab_reloc(Elf_Scn *reloc_section,
address_set_sptr exported_fns_set,
address_set_sptr exported_vars_set)
{
GElf_Shdr reloc_section_mem;
- GElf_Shdr *reloc_section_shdr = gelf_getshdr(reloc_section, &reloc_section_mem);
- size_t reloc_count = reloc_section_shdr->sh_size / reloc_section_shdr->sh_entsize;
+ GElf_Shdr *reloc_section_shdr = gelf_getshdr(reloc_section,
+ &reloc_section_mem);
+ size_t reloc_count =
+ reloc_section_shdr->sh_size / reloc_section_shdr->sh_entsize;
Elf_Data *reloc_section_data = elf_rawdata(reloc_section, 0);
- size_t strings_ndx = elf_ndxscn(strings_section);
bool is_relasec = (reloc_section_shdr->sh_type == SHT_RELA);
elf_symbol_sptr symbol;
@@ -7859,8 +7924,8 @@ public:
ABG_ASSERT(symbol);
- // If the symbol is from __ksymtab_strings, ignore it.
- if (symbol->get_shndx() == strings_ndx)
+ // If the symbol is a linux string constant then ignore it.
+ if (symbol->get_is_linux_string_cst())
continue;
I am keeping the "ELF value" property that you added to the
abigail::ir::elf_symbol type because this seems like a fairly generic
concept at that level, even if it doesn't make sense to save the actual
value of that property in, say, the abixml format. Also, I am seeing
that you are not really using it in this patch (appart for some
assert). Or do you think we should remove it as well?
I am providing the full diff of the changes I made to achieve this hunk
at the end of this email, along with the resulting new version of your
patch (with my changes).
Please note that I have also updated the ChangeLog part of the patch to
make it comply with my little changes.
I am thus applying your patch, along with my nits changes to the master
branch of the Git repository!
Thanks a lot!
diff --git a/include/abg-ir.h b/include/abg-ir.h
index febd6c8..9c01583 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -797,14 +797,14 @@ private:
size_t i,
size_t s,
uint64_t val,
- uint32_t shndx,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi);
+ visibility vi,
+ bool is_linux_string_cst = false);
elf_symbol(const elf_symbol&);
@@ -820,15 +820,15 @@ public:
create(const environment* e,
size_t i,
size_t s,
- uint64_t val,
- uint32_t shndx,
+ uint64_t val,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi);
+ visibility vi,
+ bool is_linux_string_cst = false);
const environment*
get_environment() const;
@@ -845,8 +845,8 @@ public:
uint64_t
get_value() const;
- uint32_t
- get_shndx() const;
+ bool
+ get_is_linux_string_cst() const;
const string&
get_name() const;
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index 3f53e83..df8bc49 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -1086,6 +1086,38 @@ find_section(Elf* elf_handle, const string& name, Elf64_Word section_type)
return 0;
}
+/// Test if the ELF binary denoted by a given ELF handle is a Linux
+/// Kernel Module.
+///
+/// @param elf_handle the ELF handle to consider.
+///
+/// @return true iff the binary denoted by @p elf_handle is a Linux
+/// kernel module.
+static bool
+binary_is_linux_kernel_module(Elf *elf_handle)
+{
+ return (find_section(elf_handle, ".modinfo", SHT_PROGBITS)
+ && find_section(elf_handle,
+ ".gnu.linkonce.this_module",
+ SHT_PROGBITS));
+}
+
+/// Test if the ELF binary denoted by a given ELF handle is a Linux
+/// Kernel binary (either vmlinux or a kernel module).
+///
+/// @param elf_handle the ELF handle to consider.
+///
+/// @return true iff the binary denoted by @p elf_handle is a Linux
+/// kernel binary
+static bool
+binary_is_linux_kernel(Elf *elf_handle)
+{
+ return (find_section(elf_handle,
+ "__ksymtab_strings",
+ SHT_PROGBITS)
+ || binary_is_linux_kernel_module(elf_handle));
+}
+
/// Find and return the .text section.
///
/// @param elf_handle the elf handle to use.
@@ -1131,6 +1163,20 @@ static Elf_Scn*
find_data1_section(Elf* elf_handle)
{return find_section(elf_handle, ".data1", SHT_PROGBITS);}
+/// Find the __ksymtab_strings section of a Linux kernel binary.
+///
+///
+/// @return the find_ksymtab_strings_section of the linux kernel
+/// binary denoted by @p elf_handle, or nil if such a section could
+/// not be found.
+static Elf_Scn*
+find_ksymtab_strings_section(Elf *elf_handle)
+{
+ if (binary_is_linux_kernel(elf_handle))
+ return find_section(elf_handle, "__ksymtab_strings", SHT_PROGBITS);
+ return 0;
+}
+
/// Get the address at which a given binary is loaded in memory⋅
///
/// @param elf_handle the elf handle for the binary to consider.
@@ -1725,11 +1771,14 @@ lookup_symbol_from_sysv_hash_tab(const environment* env,
const char* sym_name_str;
size_t sym_size;
uint64_t sym_value;
- uint32_t sym_shndx;
elf_symbol::type sym_type;
elf_symbol::binding sym_binding;
elf_symbol::visibility sym_visibility;
bool found = false;
+ Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
do
{
@@ -1746,7 +1795,6 @@ lookup_symbol_from_sysv_hash_tab(const environment* env,
stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(symbol.st_other));
sym_size = symbol.st_size;
sym_value = symbol.st_value;
- sym_shndx = symbol.st_shndx;
elf_symbol::version ver;
if (get_version_for_symbol(elf_handle, symbol_index,
/*get_def_version=*/true, ver))
@@ -1756,13 +1804,13 @@ lookup_symbol_from_sysv_hash_tab(const environment* env,
symbol_index,
sym_size,
sym_value,
- sym_shndx,
sym_name_str,
sym_type,
sym_binding,
symbol.st_shndx != SHN_UNDEF,
symbol.st_shndx == SHN_COMMON,
- ver, sym_visibility);
+ ver, sym_visibility,
+ symbol.st_shndx == strings_ndx);
syms_found.push_back(symbol_found);
found = true;
}
@@ -1996,6 +2044,10 @@ lookup_symbol_from_gnu_hash_tab(const environment* env,
elf_symbol::type sym_type;
elf_symbol::binding sym_binding;
elf_symbol::visibility sym_visibility;
+ Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
// Let's walk the hash table and record the versions of all the
// symbols which name equal sym_name.
@@ -2034,12 +2086,15 @@ lookup_symbol_from_gnu_hash_tab(const environment* env,
ABG_ASSERT(!ver.str().empty());
elf_symbol_sptr symbol_found =
- elf_symbol::create(env, i, symbol.st_size, symbol.st_value,
- symbol.st_shndx, sym_name_str,
+ elf_symbol::create(env, i,
+ symbol.st_size,
+ symbol.st_value,
+ sym_name_str,
sym_type, sym_binding,
symbol.st_shndx != SHN_UNDEF,
symbol.st_shndx == SHN_COMMON,
- ver, sym_visibility);
+ ver, sym_visibility,
+ symbol.st_shndx == strings_ndx);
syms_found.push_back(symbol_found);
found = true;
}
@@ -2162,6 +2217,10 @@ lookup_symbol_from_symtab(const environment* env,
char* name_str = 0;
elf_symbol::version ver;
bool found = false;
+ Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
for (size_t i = 0; i < symcount; ++i)
{
@@ -2188,10 +2247,11 @@ lookup_symbol_from_symtab(const environment* env,
ABG_ASSERT(!ver.str().empty());
elf_symbol_sptr symbol_found =
elf_symbol::create(env, i, sym->st_size,
- sym->st_value, sym->st_shndx,
+ sym->st_value,
name_str, sym_type,
sym_binding, sym_is_defined,
- sym_is_common, ver, sym_visibility);
+ sym_is_common, ver, sym_visibility,
+ sym->st_shndx == strings_ndx);
syms_found.push_back(symbol_found);
found = true;
}
@@ -6149,7 +6209,7 @@ public:
{
if (!ksymtab_strings_section_)
const_cast<read_context*>(this)->ksymtab_strings_section_ =
- find_section(elf_handle(), "__ksymtab_strings", SHT_PROGBITS);
+ dwarf_reader::find_ksymtab_strings_section(elf_handle());
return ksymtab_strings_section_;
}
@@ -6356,12 +6416,18 @@ public:
elf_symbol::visibility vis =
stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(s->st_other));
+ Elf_Scn *strings_section = find_ksymtab_strings_section();
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
+
elf_symbol_sptr sym =
elf_symbol::create(env(), symbol_index, s->st_size,
- s->st_value, s->st_shndx, name_str,
+ s->st_value, name_str,
stt_to_elf_symbol_type(GELF_ST_TYPE(s->st_info)),
stb_to_elf_symbol_binding(GELF_ST_BIND(s->st_info)),
- sym_is_defined, sym_is_common, ver, vis);
+ sym_is_defined, sym_is_common, ver, vis,
+ s->st_shndx == strings_ndx);
return sym;
}
@@ -7821,24 +7887,23 @@ public:
///
/// @param section the ksymtab section to read from
///
- /// @param strings_section the __ksymtab_strings section
- ///
/// @param exported_fns_set the set of exported functions
///
/// @param exported_vars_set the set of exported variables
///
/// @return true upon successful completion, false otherwise.
bool
- populate_symbol_map_from_ksymtab_reloc(Elf_Scn *reloc_section, Elf_Scn *strings_section,
+ populate_symbol_map_from_ksymtab_reloc(Elf_Scn *reloc_section,
address_set_sptr exported_fns_set,
address_set_sptr exported_vars_set)
{
GElf_Shdr reloc_section_mem;
- GElf_Shdr *reloc_section_shdr = gelf_getshdr(reloc_section, &reloc_section_mem);
- size_t reloc_count = reloc_section_shdr->sh_size / reloc_section_shdr->sh_entsize;
+ GElf_Shdr *reloc_section_shdr = gelf_getshdr(reloc_section,
+ &reloc_section_mem);
+ size_t reloc_count =
+ reloc_section_shdr->sh_size / reloc_section_shdr->sh_entsize;
Elf_Data *reloc_section_data = elf_rawdata(reloc_section, 0);
- size_t strings_ndx = elf_ndxscn(strings_section);
bool is_relasec = (reloc_section_shdr->sh_type == SHT_RELA);
elf_symbol_sptr symbol;
@@ -7859,8 +7924,8 @@ public:
ABG_ASSERT(symbol);
- // If the symbol is from __ksymtab_strings, ignore it.
- if (symbol->get_shndx() == strings_ndx)
+ // If the symbol is a linux string constant then ignore it.
+ if (symbol->get_is_linux_string_cst())
continue;
if (!symbol->is_function() && !symbol->is_variable())
@@ -7919,7 +7984,7 @@ public:
load_kernel_symbol_table(kernel_symbol_table_kind kind)
{
size_t nb_entries = 0;
- Elf_Scn *section = 0, *reloc_section = 0, *strings_section = 0;
+ Elf_Scn *section = 0, *reloc_section = 0;
address_set_sptr linux_exported_fns_set, linux_exported_vars_set;
switch (kind)
@@ -7942,12 +8007,9 @@ public:
break;
}
- strings_section = find_ksymtab_strings_section();
-
if (!linux_exported_vars_set
|| !linux_exported_fns_set
|| !section
- || !strings_section
|| !nb_entries)
return false;
@@ -7961,9 +8023,10 @@ public:
// determine which exported symbols are in the ksymtab.
if (!reloc_section || format == PRE_V4_19_KSYMTAB_FORMAT)
return populate_symbol_map_from_ksymtab(section, linux_exported_fns_set,
- linux_exported_vars_set, nb_entries);
+ linux_exported_vars_set,
+ nb_entries);
else
- return populate_symbol_map_from_ksymtab_reloc(reloc_section, strings_section,
+ return populate_symbol_map_from_ksymtab_reloc(reloc_section,
linux_exported_fns_set,
linux_exported_vars_set);
}
@@ -16034,14 +16097,14 @@ create_default_var_sym(const string& sym_name, const environment *env)
/*symbol index=*/ 0,
/*symbol size=*/ 0,
/*symbol value=*/ 0,
- /*symbol shndx=*/ 0,
sym_name,
/*symbol type=*/ elf_symbol::OBJECT_TYPE,
/*symbol binding=*/ elf_symbol::GLOBAL_BINDING,
/*symbol is defined=*/ true,
/*symbol is common=*/ false,
/*symbol version=*/ ver,
- /*symbol_visibility=*/vis);
+ /*symbol_visibility=*/vis,
+ /*is_linux_string_cst=*/false);
return result;
}
@@ -16475,14 +16538,14 @@ create_default_fn_sym(const string& sym_name, const environment *env)
/*symbol index=*/ 0,
/*symbol size=*/ 0,
/*symbol value=*/ 0,
- /*symbol shndx=*/ 0,
sym_name,
/*symbol type=*/ elf_symbol::FUNC_TYPE,
/*symbol binding=*/ elf_symbol::GLOBAL_BINDING,
/*symbol is defined=*/ true,
/*symbol is common=*/ false,
/*symbol version=*/ ver,
- /*symbol visibility=*/elf_symbol::DEFAULT_VISIBILITY);
+ /*symbol visibility=*/elf_symbol::DEFAULT_VISIBILITY,
+ /*symbol is linux string cst=*/false);
return result;
}
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index 67c16c9..00056c1 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -1170,7 +1170,6 @@ struct elf_symbol::priv
size_t index_;
size_t size_;
uint64_t value_;
- uint32_t shndx_;
string name_;
elf_symbol::type type_;
elf_symbol::binding binding_;
@@ -1206,6 +1205,7 @@ struct elf_symbol::priv
// STT_COMMON. If no such symbol is found, it looks for the
// STT_COMMON definition of that name that has the largest size.
bool is_common_;
+ bool is_linux_string_cst_;
elf_symbol_wptr main_symbol_;
elf_symbol_wptr next_alias_;
elf_symbol_wptr next_common_instance_;
@@ -1216,38 +1216,38 @@ struct elf_symbol::priv
index_(),
size_(),
value_(),
- shndx_(),
type_(elf_symbol::NOTYPE_TYPE),
binding_(elf_symbol::GLOBAL_BINDING),
visibility_(elf_symbol::DEFAULT_VISIBILITY),
is_defined_(false),
- is_common_(false)
+ is_common_(false),
+ is_linux_string_cst_(false)
{}
priv(const environment* e,
size_t i,
size_t s,
- uint64_t val,
- uint32_t shndx,
+ uint64_t val,
const string& n,
elf_symbol::type t,
elf_symbol::binding b,
bool d,
bool c,
const elf_symbol::version& ve,
- elf_symbol::visibility vi)
+ elf_symbol::visibility vi,
+ bool is_linux_string_cst)
: env_(e),
index_(i),
size_(s),
value_(val),
- shndx_(shndx),
name_(n),
type_(t),
binding_(b),
version_(ve),
visibility_(vi),
is_defined_(d),
- is_common_(c)
+ is_common_(c),
+ is_linux_string_cst_(is_linux_string_cst)
{
if (!is_common_)
is_common_ = type_ == COMMON_TYPE;
@@ -1279,8 +1279,6 @@ elf_symbol::elf_symbol()
///
/// @param val the value of the symbol.
///
-/// @param shndx the section header index of the symbol.
-///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
@@ -1294,19 +1292,23 @@ elf_symbol::elf_symbol()
/// @param ve the version of the symbol.
///
/// @param vi the visibility of the symbol.
+///
+/// @param is_linux_string_cst true if the symbol is a Linux Kernel
+/// string constant defined in the __ksymtab_strings section.
elf_symbol::elf_symbol(const environment* e,
size_t i,
size_t s,
- uint64_t val,
- uint32_t shndx,
+ uint64_t val,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi)
- : priv_(new priv(e, i, s, val, shndx, n, t, b, d, c, ve, vi))
+ visibility vi,
+ bool is_linux_string_cst)
+ : priv_(new priv(e, i, s, val, n, t, b, d,
+ c, ve, vi, is_linux_string_cst))
{}
/// Factory of instances of @ref elf_symbol.
@@ -1335,8 +1337,6 @@ elf_symbol::create()
///
/// @param val the value of the symbol.
///
-/// @param shndx the section header index of the symbol.
-///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
@@ -1351,6 +1351,9 @@ elf_symbol::create()
///
/// @param vi the visibility of the symbol.
///
+/// @param is_linux_string_cst if true, it means the symbol represents
+/// a string constant from a linux kernel binary.
+///
/// @return a (smart) pointer to a newly created instance of @ref
/// elf_symbol.
elf_symbol_sptr
@@ -1358,16 +1361,18 @@ elf_symbol::create(const environment* e,
size_t i,
size_t s,
uint64_t val,
- uint32_t shndx,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi)
+ visibility vi,
+ bool is_linux_string_cst)
{
- elf_symbol_sptr sym(new elf_symbol(e, i, s, val, shndx, n, t, b, d, c, ve, vi));
+ elf_symbol_sptr sym(new elf_symbol(e, i, s, val,
+ n, t, b, d, c, ve,
+ vi, is_linux_string_cst));
sym->priv_->main_symbol_ = sym;
return sym;
}
@@ -1438,12 +1443,14 @@ uint64_t
elf_symbol::get_value() const
{return priv_->value_;}
-/// Getter for the symbol section header index.
+/// Test if the ELF symbol is for a string constant of a Linux binary
+/// defined in the __ksymtab_strings symbol table.
///
-/// @return the section header index of the symbol.
-uint32_t
-elf_symbol::get_shndx() const
-{return priv_->shndx_;}
+/// @return true iff ELF symbol is for a string constant of a Linux
+/// binary defined in the __ksymtab_strings symbol table.
+bool
+elf_symbol::get_is_linux_string_cst() const
+{return priv_->is_linux_string_cst_;}
/// Getter for the name of the @ref elf_symbol.
///
diff --git a/src/abg-reader.cc b/src/abg-reader.cc
index 3c481ad..d08b800 100644
--- a/src/abg-reader.cc
+++ b/src/abg-reader.cc
@@ -2658,12 +2658,12 @@ build_elf_symbol(read_context& ctxt, const xmlNodePtr node)
elf_symbol::version version(version_string, is_default_version);
const environment* env = ctxt.get_environment();
- elf_symbol_sptr e = elf_symbol::create(env, /*index=*/0, size,
- /*value=*/0,
- /*shndx=*/0,
+ elf_symbol_sptr e = elf_symbol::create(env, /*index=*/0,
+ size, /*value=*/0,
name, type, binding,
is_defined, is_common,
- version, visibility);
+ version, visibility,
+ /*is_linux_string_cst=*/false);
return e;
}
From 79da139ec3560ff16c49a483cf141d3ae2dd09b2 Mon Sep 17 00:00:00 2001
From: Jessica Yu <jeyu@kernel.org>
Date: Tue, 3 Sep 2019 17:35:14 +0200
Subject: [PATCH] Support pre and post v4.19 ksymtabs for Linux kernel modules
As described in commit ad8c2531fb9, the format of the Linux kernel
ksymtab changed in v4.19 to use relative references instead of absolute
references. This changes the type of relocations emitted for ksymtab
sections to be place-relative 32-bit relocations instead of absolute
relocations. One side-effect of this is that libdwfl will not relocate
the ksymtab sections due to the PC-relative relocations. This breaks
load_kernel_symbol_table() for kernel modules because it only reads in
zeros from the unrelocated ksymtab section and is subsequently unable to
determine what exported symbols it refers to. Since a vmlinux binary is
already fully linked and relocated (and therefore we can read its
ksymtab section just fine), this problem is only relevant to Linux
kernel modules.
To work around this, we utilize the ksymtab relocation sections to
determine which symbols the ksymtab entries refer to. We do this by
inspecting each relocation's r_info field for the symbol table index and
from there we are able to read each symbol's value and subsequently add
that to the set of exported symbols.
In addition, for Linux kernel modules, we can utilize relocation types
to implement a new heuristic to determine the ksymtab format we have.
The presence of PC-relative relocations suggest the new v4.19 format,
and absolute relocation types suggest the old pre v4.19 format.
* include/abg-ir.h (elf_symbol::{elf_symbol, create}): Take new
symbol value and shndx parameters.
(elf_symbol::{get_value, get_shndx}): Declare new accessors.
* src/abg-ir.cc (elf_symbol::priv::{value_, shndx_}): New data
members.
(elf_symbol::priv::priv): Adjust.
(elf_symbol::elf_symbol): Take new value and is_linux_string_cst
parameters.
(elf_symbol::create): Likewise.
(elf_symbol::{get_value, get_is_linux_string_cst}): Define new
accessors.
* src/abg-reader.cc (build_elf_symbol): Adjust.
* src/abg-dwarf-reader.cc (binary_is_linux_kernel)
(binary_is_linux_kernel): New static functions.
(lookup_symbol_from_sysv_hash_tab)
(lookup_symbol_from_gnu_hash_tab)
(lookup_symbol_from_symtab): Adjust.
(read_context::{ksymtab_reloc_section_,
ksymtab_gpl_reloc_section_, ksymtab_strings_section_}): New data
members.
(read_context::read_context): Initialize ksymtab_reloc_section_,
ksymtab_gpl_reloc_section_, ksymtab_strings_section_.
(read_context::{find_ksymtab_reloc_section,
find_ksymtab_gpl_reloc_section, find_ksymtab_strings_section,
find_any_ksymtab_reloc_section, get_ksymtab_format_module,
populate_symbol_map_from_ksymtab,
populate_symbol_map_from_ksymtab_reloc, is_linux_kernel_module}):
New member functions.
(read_context::load_kernel_symbol_table): Adjust to call either
populate_symbol_map_from_ksymtab{_reloc,} depending on ksymtab
format.
(read_context::get_ksymtab_format): Adjust to call
get_ksymtab_format_module for linux kernel modules.
(read_context::lookup_elf_symbol_from_index): Adjust.
(create_default_var_sym, create_default_fn_sym): Adjust.
Signed-off-by: Jessica Yu <jeyu@kernel.org>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
include/abg-ir.h | 14 +-
src/abg-dwarf-reader.cc | 457 +++++++++++++++++++++++++++++++++++++++++++-----
src/abg-ir.cc | 55 +++++-
src/abg-reader.cc | 6 +-
4 files changed, 474 insertions(+), 58 deletions(-)
diff --git a/include/abg-ir.h b/include/abg-ir.h
index 170a844..9c01583 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -796,13 +796,15 @@ private:
elf_symbol(const environment* e,
size_t i,
size_t s,
+ uint64_t val,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi);
+ visibility vi,
+ bool is_linux_string_cst = false);
elf_symbol(const elf_symbol&);
@@ -818,13 +820,15 @@ public:
create(const environment* e,
size_t i,
size_t s,
+ uint64_t val,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi);
+ visibility vi,
+ bool is_linux_string_cst = false);
const environment*
get_environment() const;
@@ -838,6 +842,12 @@ public:
void
set_index(size_t);
+ uint64_t
+ get_value() const;
+
+ bool
+ get_is_linux_string_cst() const;
+
const string&
get_name() const;
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index ab3f8e3..df8bc49 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -1086,6 +1086,38 @@ find_section(Elf* elf_handle, const string& name, Elf64_Word section_type)
return 0;
}
+/// Test if the ELF binary denoted by a given ELF handle is a Linux
+/// Kernel Module.
+///
+/// @param elf_handle the ELF handle to consider.
+///
+/// @return true iff the binary denoted by @p elf_handle is a Linux
+/// kernel module.
+static bool
+binary_is_linux_kernel_module(Elf *elf_handle)
+{
+ return (find_section(elf_handle, ".modinfo", SHT_PROGBITS)
+ && find_section(elf_handle,
+ ".gnu.linkonce.this_module",
+ SHT_PROGBITS));
+}
+
+/// Test if the ELF binary denoted by a given ELF handle is a Linux
+/// Kernel binary (either vmlinux or a kernel module).
+///
+/// @param elf_handle the ELF handle to consider.
+///
+/// @return true iff the binary denoted by @p elf_handle is a Linux
+/// kernel binary
+static bool
+binary_is_linux_kernel(Elf *elf_handle)
+{
+ return (find_section(elf_handle,
+ "__ksymtab_strings",
+ SHT_PROGBITS)
+ || binary_is_linux_kernel_module(elf_handle));
+}
+
/// Find and return the .text section.
///
/// @param elf_handle the elf handle to use.
@@ -1131,6 +1163,20 @@ static Elf_Scn*
find_data1_section(Elf* elf_handle)
{return find_section(elf_handle, ".data1", SHT_PROGBITS);}
+/// Find the __ksymtab_strings section of a Linux kernel binary.
+///
+///
+/// @return the find_ksymtab_strings_section of the linux kernel
+/// binary denoted by @p elf_handle, or nil if such a section could
+/// not be found.
+static Elf_Scn*
+find_ksymtab_strings_section(Elf *elf_handle)
+{
+ if (binary_is_linux_kernel(elf_handle))
+ return find_section(elf_handle, "__ksymtab_strings", SHT_PROGBITS);
+ return 0;
+}
+
/// Get the address at which a given binary is loaded in memory⋅
///
/// @param elf_handle the elf handle for the binary to consider.
@@ -1724,10 +1770,15 @@ lookup_symbol_from_sysv_hash_tab(const environment* env,
GElf_Sym symbol;
const char* sym_name_str;
size_t sym_size;
+ uint64_t sym_value;
elf_symbol::type sym_type;
elf_symbol::binding sym_binding;
elf_symbol::visibility sym_visibility;
bool found = false;
+ Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
do
{
@@ -1743,6 +1794,7 @@ lookup_symbol_from_sysv_hash_tab(const environment* env,
sym_visibility =
stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(symbol.st_other));
sym_size = symbol.st_size;
+ sym_value = symbol.st_value;
elf_symbol::version ver;
if (get_version_for_symbol(elf_handle, symbol_index,
/*get_def_version=*/true, ver))
@@ -1751,12 +1803,14 @@ lookup_symbol_from_sysv_hash_tab(const environment* env,
elf_symbol::create(env,
symbol_index,
sym_size,
+ sym_value,
sym_name_str,
sym_type,
sym_binding,
symbol.st_shndx != SHN_UNDEF,
symbol.st_shndx == SHN_COMMON,
- ver, sym_visibility);
+ ver, sym_visibility,
+ symbol.st_shndx == strings_ndx);
syms_found.push_back(symbol_found);
found = true;
}
@@ -1990,6 +2044,10 @@ lookup_symbol_from_gnu_hash_tab(const environment* env,
elf_symbol::type sym_type;
elf_symbol::binding sym_binding;
elf_symbol::visibility sym_visibility;
+ Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
// Let's walk the hash table and record the versions of all the
// symbols which name equal sym_name.
@@ -2028,11 +2086,15 @@ lookup_symbol_from_gnu_hash_tab(const environment* env,
ABG_ASSERT(!ver.str().empty());
elf_symbol_sptr symbol_found =
- elf_symbol::create(env, i, symbol.st_size, sym_name_str,
+ elf_symbol::create(env, i,
+ symbol.st_size,
+ symbol.st_value,
+ sym_name_str,
sym_type, sym_binding,
symbol.st_shndx != SHN_UNDEF,
symbol.st_shndx == SHN_COMMON,
- ver, sym_visibility);
+ ver, sym_visibility,
+ symbol.st_shndx == strings_ndx);
syms_found.push_back(symbol_found);
found = true;
}
@@ -2155,6 +2217,10 @@ lookup_symbol_from_symtab(const environment* env,
char* name_str = 0;
elf_symbol::version ver;
bool found = false;
+ Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
for (size_t i = 0; i < symcount; ++i)
{
@@ -2181,9 +2247,11 @@ lookup_symbol_from_symtab(const environment* env,
ABG_ASSERT(!ver.str().empty());
elf_symbol_sptr symbol_found =
elf_symbol::create(env, i, sym->st_size,
+ sym->st_value,
name_str, sym_type,
sym_binding, sym_is_defined,
- sym_is_common, ver, sym_visibility);
+ sym_is_common, ver, sym_visibility,
+ sym->st_shndx == strings_ndx);
syms_found.push_back(symbol_found);
found = true;
}
@@ -3081,7 +3149,10 @@ public:
/// to symbols exported using the EXPORT_SYMBOL_GPL macro from the
/// linux kernel.
Elf_Scn* ksymtab_section_;
+ Elf_Scn* ksymtab_reloc_section_;
Elf_Scn* ksymtab_gpl_section_;
+ Elf_Scn* ksymtab_gpl_reloc_section_;
+ Elf_Scn* ksymtab_strings_section_;
Elf_Scn* versym_section_;
Elf_Scn* verdef_section_;
Elf_Scn* verneed_section_;
@@ -3277,7 +3348,10 @@ public:
nb_ksymtab_entries_ = 0;
nb_ksymtab_gpl_entries_ = 0;
ksymtab_section_ = 0;
+ ksymtab_reloc_section_ = 0;
ksymtab_gpl_section_ = 0;
+ ksymtab_gpl_reloc_section_ = 0;
+ ksymtab_strings_section_ = 0;
versym_section_ = 0;
verdef_section_ = 0;
verneed_section_ = 0;
@@ -6079,6 +6153,23 @@ public:
return ksymtab_section_;
}
+ /// Return the .rel{a,}__ksymtab section of a linux kernel ELF file (either
+ /// a vmlinux binary or a kernel module).
+ ///
+ /// @return the .rel{a,}__ksymtab section if found, nil otherwise.
+ Elf_Scn*
+ find_ksymtab_reloc_section() const
+ {
+ if (!ksymtab_reloc_section_)
+ {
+ Elf_Scn *sec = find_section(elf_handle(), ".rela__ksymtab", SHT_RELA);
+ if (!sec)
+ sec = find_section(elf_handle(), ".rel__ksymtab", SHT_REL);
+ const_cast<read_context*>(this)->ksymtab_reloc_section_ = sec;
+ }
+ return ksymtab_reloc_section_;
+ }
+
/// Return the __ksymtab_gpl section of a linux kernel ELF file
/// (either a vmlinux binary or a kernel module).
///
@@ -6092,6 +6183,36 @@ public:
return ksymtab_gpl_section_;
}
+ /// Return the .rel{a,}__ksymtab_gpl section of a linux kernel ELF file
+ /// (either a vmlinux binary or a kernel module).
+ ///
+ /// @return the .rel{a,}__ksymtab_gpl section if found, nil otherwise.
+ Elf_Scn*
+ find_ksymtab_gpl_reloc_section() const
+ {
+ if (!ksymtab_gpl_reloc_section_)
+ {
+ Elf_Scn *sec = find_section(elf_handle(), ".rela__ksymtab_gpl", SHT_RELA);
+ if (!sec)
+ sec = find_section(elf_handle(), ".rel__ksymtab_gpl", SHT_REL);
+ const_cast<read_context*>(this)->ksymtab_gpl_reloc_section_ = sec;
+ }
+ return ksymtab_gpl_reloc_section_;
+ }
+
+ /// Return the __ksymtab_strings section of a linux kernel ELF file
+ /// (either a vmlinux binary or a kernel module).
+ ///
+ /// @return the __ksymtab_strings section if found, nil otherwise.
+ Elf_Scn*
+ find_ksymtab_strings_section() const
+ {
+ if (!ksymtab_strings_section_)
+ const_cast<read_context*>(this)->ksymtab_strings_section_ =
+ dwarf_reader::find_ksymtab_strings_section(elf_handle());
+ return ksymtab_strings_section_;
+ }
+
/// Return either a __ksymtab or a __ksymtab_gpl section, in case
/// only the __ksymtab_gpl exists.
///
@@ -6106,6 +6227,19 @@ public:
return result;
}
+ /// Return either a .rel{a,}__ksymtab or a .rel{a,}__ksymtab_gpl section
+ ///
+ /// @return the .rel{a,}__ksymtab section if it exists, or the
+ /// .rel{a,}__ksymtab_gpl; or NULL if neither is found.
+ Elf_Scn*
+ find_any_ksymtab_reloc_section() const
+ {
+ Elf_Scn *result = find_ksymtab_reloc_section();
+ if (!result)
+ result = find_ksymtab_gpl_reloc_section();
+ return result;
+ }
+
/// Return the SHT_GNU_versym, SHT_GNU_verdef and SHT_GNU_verneed
/// sections that are involved in symbol versionning.
///
@@ -6282,11 +6416,18 @@ public:
elf_symbol::visibility vis =
stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(s->st_other));
+ Elf_Scn *strings_section = find_ksymtab_strings_section();
+ size_t strings_ndx = strings_section
+ ? elf_ndxscn(strings_section)
+ : 0;
+
elf_symbol_sptr sym =
- elf_symbol::create(env(), symbol_index, s->st_size, name_str,
+ elf_symbol::create(env(), symbol_index, s->st_size,
+ s->st_value, name_str,
stt_to_elf_symbol_type(GELF_ST_TYPE(s->st_info)),
stb_to_elf_symbol_binding(GELF_ST_BIND(s->st_info)),
- sym_is_defined, sym_is_common, ver, vis);
+ sym_is_defined, sym_is_common, ver, vis,
+ s->st_shndx == strings_ndx);
return sym;
}
@@ -7435,6 +7576,75 @@ public:
return symbol;
}
+ /// Try to determine the format of the __ksymtab and __ksymtab_gpl
+ /// sections of Linux kernel modules.
+ ///
+ /// This is important because we need to know the format of these
+ /// sections to be able to read from them.
+ ///
+ /// @return the format the __ksymtab[_gpl] sections.
+ enum ksymtab_format
+ get_ksymtab_format_module() const
+ {
+ Elf_Scn *section = find_any_ksymtab_reloc_section();
+
+ ABG_ASSERT(section);
+
+ // Libdwfl has a weird quirk where, in the process of obtaining an Elf
+ // descriptor via dwfl_module_getelf(), it will apply all relocations it
+ // knows how to and it will zero the relocation info after applying it. If
+ // the .rela__ksymtab* section contained only simple (absolute) relocations,
+ // they will have been all applied and sh_size will be 0. For arches that
+ // support relative ksymtabs, simple relocations only appear in pre-4.19
+ // kernel modules.
+ GElf_Shdr section_mem;
+ GElf_Shdr *section_shdr = gelf_getshdr(section, §ion_mem);
+ if (section_shdr->sh_size == 0)
+ return PRE_V4_19_KSYMTAB_FORMAT;
+
+ bool is_relasec = (section_shdr->sh_type == SHT_RELA);
+
+ // If we still have a normal non-zeroed relocation section, we can guess
+ // what format the ksymtab is in depending on what types of relocs it
+ // contains.
+
+ int type;
+ Elf_Data *section_data = elf_rawdata(section, 0);
+ if (is_relasec)
+ {
+ GElf_Rela rela;
+ gelf_getrela(section_data, 0, &rela);
+ type = GELF_R_TYPE(rela.r_info);
+ }
+ else
+ {
+ GElf_Rel rel;
+ gelf_getrel(section_data, 0, &rel);
+ type = GELF_R_TYPE(rel.r_info);
+ }
+
+ // Sigh, I dislike the arch-dependent code here, but this seems to be a
+ // reliable heuristic for kernel modules for now. Relative ksymtabs only
+ // supported on x86 and arm64 as of v4.19.
+ ksymtab_format format;
+ switch (type)
+ {
+ case R_X86_64_64: // Same as R_386_32, fallthrough
+ case R_AARCH64_ABS64:
+ format = PRE_V4_19_KSYMTAB_FORMAT;
+ break;
+ case R_X86_64_PC32: // Same as R_386_PC32, fallthrough
+ case R_AARCH64_PREL32:
+ format = V4_19_KSYMTAB_FORMAT;
+ break;
+ default:
+ // Fall back to other methods of determining the ksymtab format.
+ format = UNDEFINED_KSYMTAB_FORMAT;
+ break;
+ }
+ return format;
+ }
+
/// Determine the format of the __ksymtab and __ksymtab_gpl
/// sections.
///
@@ -7451,6 +7661,18 @@ public:
{
if (ksymtab_format_ == UNDEFINED_KSYMTAB_FORMAT)
{
+ // Since Linux kernel modules are relocatable, we can first try
+ // using a heuristic based on relocations to guess the ksymtab format.
+ if (is_linux_kernel_module())
+ {
+ ksymtab_format_ = get_ksymtab_format_module();
+ if (ksymtab_format_ != UNDEFINED_KSYMTAB_FORMAT)
+ return ksymtab_format_;
+ }
+
+ // If it's not a kernel module or we couldn't determine its format
+ // with relocations, fall back to the heuristics below.
+
// OK this is a dirty little heuristic to determine the
// format of the ksymtab section.
//
@@ -7553,48 +7775,24 @@ public:
return nb_ksymtab_gpl_entries_;
}
- /// Load a given kernel symbol table.
+ /// Populate the symbol map by reading exported symbols from the
+ /// ksymtab directly.
///
- /// One can thus retrieve the resulting symbols by using the
- /// accessors read_context::linux_exported_fn_syms(),
- /// read_context::linux_exported_var_syms(),
- /// read_context::linux_exported_gpl_fn_syms(), or
- /// read_context::linux_exported_gpl_var_syms().
+ /// @param section the ksymtab section to read from
///
- /// @param kind the kind of kernel symbol table to load.
+ /// @param exported_fns_set the set of exported functions
+ ///
+ /// @param exported_vars_set the set of exported variables
+ ///
+ /// @param nb_entries the number of ksymtab entries to read
///
/// @return true upon successful completion, false otherwise.
bool
- load_kernel_symbol_table(kernel_symbol_table_kind kind)
+ populate_symbol_map_from_ksymtab(Elf_Scn *section,
+ address_set_sptr exported_fns_set,
+ address_set_sptr exported_vars_set,
+ size_t nb_entries)
{
- size_t nb_entries = 0;
- Elf_Scn *section = 0;
- address_set_sptr linux_exported_fns_set, linux_exported_vars_set;
-
- switch (kind)
- {
- case KERNEL_SYMBOL_TABLE_KIND_UNDEFINED:
- break;
- case KERNEL_SYMBOL_TABLE_KIND_KSYMTAB:
- section = find_ksymtab_section();
- nb_entries = get_nb_ksymtab_entries();
- linux_exported_fns_set = create_or_get_linux_exported_fn_syms();
- linux_exported_vars_set = create_or_get_linux_exported_var_syms();
- break;
- case KERNEL_SYMBOL_TABLE_KIND_KSYMTAB_GPL:
- section = find_ksymtab_gpl_section();
- nb_entries = get_nb_ksymtab_gpl_entries();
- linux_exported_fns_set = create_or_get_linux_exported_gpl_fn_syms();
- linux_exported_vars_set = create_or_get_linux_exported_gpl_var_syms();
- break;
- }
-
- if (!linux_exported_vars_set
- || !linux_exported_fns_set
- || !section
- || !nb_entries)
- return false;
-
// The data of the section.
Elf_Data *elf_data = elf_rawdata(section, 0);
@@ -7670,12 +7868,12 @@ public:
if (symbol->is_function())
{
ABG_ASSERT(lookup_elf_fn_symbol_from_address(adjusted_symbol_address));
- set = linux_exported_fns_set;
+ set = exported_fns_set;
}
else if (symbol->is_variable())
{
ABG_ASSERT(lookup_elf_var_symbol_from_address(adjusted_symbol_address));
- set = linux_exported_vars_set;
+ set = exported_vars_set;
}
else
ABG_ASSERT_NOT_REACHED;
@@ -7684,6 +7882,155 @@ public:
return true;
}
+ /// Populate the symbol map by extracting the exported symbols from a
+ /// ksymtab rela section.
+ ///
+ /// @param section the ksymtab section to read from
+ ///
+ /// @param exported_fns_set the set of exported functions
+ ///
+ /// @param exported_vars_set the set of exported variables
+ ///
+ /// @return true upon successful completion, false otherwise.
+ bool
+ populate_symbol_map_from_ksymtab_reloc(Elf_Scn *reloc_section,
+ address_set_sptr exported_fns_set,
+ address_set_sptr exported_vars_set)
+ {
+ GElf_Shdr reloc_section_mem;
+ GElf_Shdr *reloc_section_shdr = gelf_getshdr(reloc_section,
+ &reloc_section_mem);
+ size_t reloc_count =
+ reloc_section_shdr->sh_size / reloc_section_shdr->sh_entsize;
+
+ Elf_Data *reloc_section_data = elf_rawdata(reloc_section, 0);
+
+ bool is_relasec = (reloc_section_shdr->sh_type == SHT_RELA);
+ elf_symbol_sptr symbol;
+ for (unsigned int i = 0; i < reloc_count; i++)
+ {
+ if (is_relasec)
+ {
+ GElf_Rela rela;
+ gelf_getrela(reloc_section_data, i, &rela);
+ symbol = lookup_elf_symbol_from_index(GELF_R_SYM(rela.r_info));
+ }
+ else
+ {
+ GElf_Rel rel;
+ gelf_getrel(reloc_section_data, i, &rel);
+ symbol = lookup_elf_symbol_from_index(GELF_R_SYM(rel.r_info));
+ }
+
+ ABG_ASSERT(symbol);
+
+ // If the symbol is a linux string constant then ignore it.
+ if (symbol->get_is_linux_string_cst())
+ continue;
+
+ if (!symbol->is_function() && !symbol->is_variable())
+ {
+ if (do_log())
+ {
+ if (symbol->get_type() == elf_symbol::NOTYPE_TYPE)
+ cerr << "skipping NOTYPE symbol "
+ << symbol->get_name()
+ << " shndx: "
+ << symbol->get_index()
+ << " @"
+ << elf_path()
+ << "\n";
+ else if (symbol->get_type() == elf_symbol::SECTION_TYPE)
+ cerr << "skipping SECTION symbol "
+ << "shndx: "
+ << symbol->get_index()
+ << " @"
+ << elf_path()
+ << "\n";
+ }
+ continue;
+ }
+
+ address_set_sptr set;
+ if (symbol->is_function())
+ {
+ ABG_ASSERT(lookup_elf_fn_symbol_from_address(symbol->get_value()));
+ set = exported_fns_set;
+ }
+ else if (symbol->is_variable())
+ {
+ ABG_ASSERT(lookup_elf_var_symbol_from_address(symbol->get_value()));
+ set = exported_vars_set;
+ }
+ else
+ ABG_ASSERT_NOT_REACHED;
+ set->insert(symbol->get_value());
+ }
+ return true;
+ }
+
+ /// Load a given kernel symbol table.
+ ///
+ /// One can thus retrieve the resulting symbols by using the
+ /// accessors read_context::linux_exported_fn_syms(),
+ /// read_context::linux_exported_var_syms(),
+ /// read_context::linux_exported_gpl_fn_syms(), or
+ /// read_context::linux_exported_gpl_var_syms().
+ ///
+ /// @param kind the kind of kernel symbol table to load.
+ ///
+ /// @return true upon successful completion, false otherwise.
+ bool
+ load_kernel_symbol_table(kernel_symbol_table_kind kind)
+ {
+ size_t nb_entries = 0;
+ Elf_Scn *section = 0, *reloc_section = 0;
+ address_set_sptr linux_exported_fns_set, linux_exported_vars_set;
+
+ switch (kind)
+ {
+ case KERNEL_SYMBOL_TABLE_KIND_UNDEFINED:
+ break;
+ case KERNEL_SYMBOL_TABLE_KIND_KSYMTAB:
+ section = find_ksymtab_section();
+ reloc_section = find_ksymtab_reloc_section();
+ nb_entries = get_nb_ksymtab_entries();
+ linux_exported_fns_set = create_or_get_linux_exported_fn_syms();
+ linux_exported_vars_set = create_or_get_linux_exported_var_syms();
+ break;
+ case KERNEL_SYMBOL_TABLE_KIND_KSYMTAB_GPL:
+ section = find_ksymtab_gpl_section();
+ reloc_section = find_ksymtab_gpl_reloc_section();
+ nb_entries = get_nb_ksymtab_gpl_entries();
+ linux_exported_fns_set = create_or_get_linux_exported_gpl_fn_syms();
+ linux_exported_vars_set = create_or_get_linux_exported_gpl_var_syms();
+ break;
+ }
+
+ if (!linux_exported_vars_set
+ || !linux_exported_fns_set
+ || !section
+ || !nb_entries)
+ return false;
+
+ ksymtab_format format = get_ksymtab_format();
+
+ // Although pre-v4.19 kernel modules can have a relocation section for the
+ // __ksymtab section, libdwfl zeroes the rela section after applying
+ // "simple" absolute relocations via dwfl_module_getelf(). For v4.19 and
+ // above, we get PC-relative relocations so dwfl_module_getelf() doesn't
+ // apply those relocations and we're safe to read the relocation section to
+ // determine which exported symbols are in the ksymtab.
+ if (!reloc_section || format == PRE_V4_19_KSYMTAB_FORMAT)
+ return populate_symbol_map_from_ksymtab(section, linux_exported_fns_set,
+ linux_exported_vars_set,
+ nb_entries);
+ else
+ return populate_symbol_map_from_ksymtab_reloc(reloc_section,
+ linux_exported_fns_set,
+ linux_exported_vars_set);
+ }
+
/// Load the special __ksymtab section. This is for linux kernel
/// (module) files.
///
@@ -8352,7 +8699,19 @@ public:
is_linux_kernel_binary() const
{
return find_section(elf_handle(), "__ksymtab_strings", SHT_PROGBITS)
- || find_section(elf_handle(), ".modinfo", SHT_PROGBITS);
+ || is_linux_kernel_module();
+ }
+
+ /// Guess if the current binary is a Linux Kernel module.
+ ///
+ /// To guess that, the function looks for the presence of the special
+ /// ".modinfo" and ".gnu.linkonce.this_module" sections in the binary.
+ ///
+ bool
+ is_linux_kernel_module() const
+ {
+ return find_section(elf_handle(), ".modinfo", SHT_PROGBITS)
+ && find_section(elf_handle(), ".gnu.linkonce.this_module", SHT_PROGBITS);
}
/// Getter of the "show_stats" flag.
@@ -15737,13 +16096,15 @@ create_default_var_sym(const string& sym_name, const environment *env)
elf_symbol::create(env,
/*symbol index=*/ 0,
/*symbol size=*/ 0,
+ /*symbol value=*/ 0,
sym_name,
/*symbol type=*/ elf_symbol::OBJECT_TYPE,
/*symbol binding=*/ elf_symbol::GLOBAL_BINDING,
/*symbol is defined=*/ true,
/*symbol is common=*/ false,
/*symbol version=*/ ver,
- /*symbol_visibility=*/vis);
+ /*symbol_visibility=*/vis,
+ /*is_linux_string_cst=*/false);
return result;
}
@@ -16176,13 +16537,15 @@ create_default_fn_sym(const string& sym_name, const environment *env)
elf_symbol::create(env,
/*symbol index=*/ 0,
/*symbol size=*/ 0,
+ /*symbol value=*/ 0,
sym_name,
/*symbol type=*/ elf_symbol::FUNC_TYPE,
/*symbol binding=*/ elf_symbol::GLOBAL_BINDING,
/*symbol is defined=*/ true,
/*symbol is common=*/ false,
/*symbol version=*/ ver,
- /*symbol visibility=*/elf_symbol::DEFAULT_VISIBILITY);
+ /*symbol visibility=*/elf_symbol::DEFAULT_VISIBILITY,
+ /*symbol is linux string cst=*/false);
return result;
}
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index a5243c3..00056c1 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -1169,6 +1169,7 @@ struct elf_symbol::priv
const environment* env_;
size_t index_;
size_t size_;
+ uint64_t value_;
string name_;
elf_symbol::type type_;
elf_symbol::binding binding_;
@@ -1204,6 +1205,7 @@ struct elf_symbol::priv
// STT_COMMON. If no such symbol is found, it looks for the
// STT_COMMON definition of that name that has the largest size.
bool is_common_;
+ bool is_linux_string_cst_;
elf_symbol_wptr main_symbol_;
elf_symbol_wptr next_alias_;
elf_symbol_wptr next_common_instance_;
@@ -1213,33 +1215,39 @@ struct elf_symbol::priv
: env_(),
index_(),
size_(),
+ value_(),
type_(elf_symbol::NOTYPE_TYPE),
binding_(elf_symbol::GLOBAL_BINDING),
visibility_(elf_symbol::DEFAULT_VISIBILITY),
is_defined_(false),
- is_common_(false)
+ is_common_(false),
+ is_linux_string_cst_(false)
{}
priv(const environment* e,
size_t i,
size_t s,
+ uint64_t val,
const string& n,
elf_symbol::type t,
elf_symbol::binding b,
bool d,
bool c,
const elf_symbol::version& ve,
- elf_symbol::visibility vi)
+ elf_symbol::visibility vi,
+ bool is_linux_string_cst)
: env_(e),
index_(i),
size_(s),
+ value_(val),
name_(n),
type_(t),
binding_(b),
version_(ve),
visibility_(vi),
is_defined_(d),
- is_common_(c)
+ is_common_(c),
+ is_linux_string_cst_(is_linux_string_cst)
{
if (!is_common_)
is_common_ = type_ == COMMON_TYPE;
@@ -1269,6 +1277,8 @@ elf_symbol::elf_symbol()
///
/// @param s the size of the symbol.
///
+/// @param val the value of the symbol.
+///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
@@ -1282,17 +1292,23 @@ elf_symbol::elf_symbol()
/// @param ve the version of the symbol.
///
/// @param vi the visibility of the symbol.
+///
+/// @param is_linux_string_cst true if the symbol is a Linux Kernel
+/// string constant defined in the __ksymtab_strings section.
elf_symbol::elf_symbol(const environment* e,
size_t i,
size_t s,
+ uint64_t val,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi)
- : priv_(new priv(e, i, s, n, t, b, d, c, ve, vi))
+ visibility vi,
+ bool is_linux_string_cst)
+ : priv_(new priv(e, i, s, val, n, t, b, d,
+ c, ve, vi, is_linux_string_cst))
{}
/// Factory of instances of @ref elf_symbol.
@@ -1319,6 +1335,8 @@ elf_symbol::create()
///
/// @param s the size of the symbol.
///
+/// @param val the value of the symbol.
+///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
@@ -1333,21 +1351,28 @@ elf_symbol::create()
///
/// @param vi the visibility of the symbol.
///
+/// @param is_linux_string_cst if true, it means the symbol represents
+/// a string constant from a linux kernel binary.
+///
/// @return a (smart) pointer to a newly created instance of @ref
/// elf_symbol.
elf_symbol_sptr
elf_symbol::create(const environment* e,
size_t i,
size_t s,
+ uint64_t val,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
- visibility vi)
+ visibility vi,
+ bool is_linux_string_cst)
{
- elf_symbol_sptr sym(new elf_symbol(e, i, s, n, t, b, d, c, ve, vi));
+ elf_symbol_sptr sym(new elf_symbol(e, i, s, val,
+ n, t, b, d, c, ve,
+ vi, is_linux_string_cst));
sym->priv_->main_symbol_ = sym;
return sym;
}
@@ -1411,6 +1436,22 @@ void
elf_symbol::set_index(size_t s)
{priv_->index_ = s;}
+/// Getter for the symbol value.
+///
+/// @return the value of the symbol.
+uint64_t
+elf_symbol::get_value() const
+{return priv_->value_;}
+
+/// Test if the ELF symbol is for a string constant of a Linux binary
+/// defined in the __ksymtab_strings symbol table.
+///
+/// @return true iff ELF symbol is for a string constant of a Linux
+/// binary defined in the __ksymtab_strings symbol table.
+bool
+elf_symbol::get_is_linux_string_cst() const
+{return priv_->is_linux_string_cst_;}
+
/// Getter for the name of the @ref elf_symbol.
///
/// @return a reference to the name of the @ref symbol.
diff --git a/src/abg-reader.cc b/src/abg-reader.cc
index 810a65f..d08b800 100644
--- a/src/abg-reader.cc
+++ b/src/abg-reader.cc
@@ -2658,10 +2658,12 @@ build_elf_symbol(read_context& ctxt, const xmlNodePtr node)
elf_symbol::version version(version_string, is_default_version);
const environment* env = ctxt.get_environment();
- elf_symbol_sptr e = elf_symbol::create(env, /*index=*/0, size,
+ elf_symbol_sptr e = elf_symbol::create(env, /*index=*/0,
+ size, /*value=*/0,
name, type, binding,
is_defined, is_common,
- version, visibility);
+ version, visibility,
+ /*is_linux_string_cst=*/false);
return e;
}
--
1.8.3.1
--
Dodji