Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix externalization on Userpsace Livepatching #124

Merged
merged 3 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions libcextract/ArgvParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ ArgvParser::ArgvParser(int argc, char **argv)
Ibt(false),
AllowLateExternalization(false),
PatchObject(""),
DebuginfoPath(nullptr),
Debuginfos(),
IpaclonesPath(nullptr),
SymversPath(nullptr),
DescOutputPath(nullptr),
Expand All @@ -89,6 +89,11 @@ ArgvParser::ArgvParser(int argc, char **argv)

Insert_Required_Parameters();

const char *DebuginfoPath = nullptr;
if (Debuginfos.size() > 0) {
DebuginfoPath = Debuginfos[0].c_str();
}

/* For kernel, check if the object patch is not the same as DebugInfo. If they
* are not the same, it means that the module from PatchObject is builtin, so
* assign vmlinux to PatchObject. */
Expand Down Expand Up @@ -252,7 +257,7 @@ bool ArgvParser::Handle_Clang_Extract_Arg(const char *str)
return true;
}
if (prefix("-DCE_DEBUGINFO_PATH=", str)) {
DebuginfoPath = Extract_Single_Arg_C(str);
Debuginfos = Extract_Args(str);

return true;
}
Expand Down
7 changes: 4 additions & 3 deletions libcextract/ArgvParser.hh
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ class ArgvParser
return PatchObject;
}

inline const char *Get_Debuginfo_Path(void)
inline std::vector<std::string> &Get_Debuginfo_Path(void)
{
return DebuginfoPath;
return Debuginfos;
}

inline const char *Get_Ipaclones_Path(void)
Expand Down Expand Up @@ -163,7 +163,8 @@ class ArgvParser
bool AllowLateExternalization;
std::string PatchObject;

const char *DebuginfoPath;
std::vector<std::string> Debuginfos;

const char *IpaclonesPath;
const char *SymversPath;

Expand Down
39 changes: 37 additions & 2 deletions libcextract/ElfCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,19 +335,30 @@ void ElfSymbolCache::Insert_Symbols_Into_Hash(SymbolTableHash &map, ElfSection &
}

ElfSymbolCache::ElfSymbolCache(ElfObject &eo)
: EO(eo)
: ElfSymbolCache()
{
Analyze_ELF(eo);
}

void ElfSymbolCache::Analyze_ELF(ElfObject &eo)
{
/* Look for dynsym and symtab sections. */
for (auto it = EO.section_begin(); it != EO.section_end(); ++it)
for (auto it = eo.section_begin(); it != eo.section_end(); ++it)
{
ElfSection &section = *it;
switch (section.Get_Section_Type()) {
case SHT_DYNSYM:
Insert_Symbols_Into_Hash(DynsymMap, section);

assert(ObjectPath == "" && "Multiple libraries passed as debuginfo?");
ObjectPath = eo.Get_Path();
break;

case SHT_SYMTAB:
Insert_Symbols_Into_Hash(SymtabMap, section);

/* We can also have symtab in the .so library. */
DebuginfoPath = eo.Get_Path();
break;

case SHT_PROGBITS:
Expand All @@ -365,6 +376,30 @@ ElfSymbolCache::ElfSymbolCache(ElfObject &eo)
}
}

/** Get symbol if available in either symtab. Returns in which symtab this
symbol was found. */
std::pair<unsigned char, ElfSymtabType>
ElfSymbolCache::Get_Symbol_Info(const std::string &sym)
{
unsigned char ret = 0;

/* Try the dynsym first, which means this symbol is most likely public
visible. */
ret = Get_Symbol_Info_Dynsym(sym);
if (ret != 0) {
return std::make_pair(ret, ElfSymtabType::DYNSYM);
}

/* Symbol not available on dynsym. Try symtab. */
ret = Get_Symbol_Info_Symtab(sym);
if (ret != 0) {
return std::make_pair(ret, ElfSymtabType::SYMTAB);
}

/* If symbol is not here there is nothing I can do. */
return std::make_pair(0, ElfSymtabType::TAB_NONE);
}

std::vector<std::string> ElfSymbolCache::Get_All_Symbols(void)
{
std::vector<std::string> vec;
Expand Down
41 changes: 39 additions & 2 deletions libcextract/ElfCXX.hh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class ElfObject;
class ElfSection;
class ElfSymbol;


/** The symbol table in which this symbol was found. */
enum ElfSymtabType {
TAB_NONE = 0,
SYMTAB = SHT_SYMTAB,
DYNSYM = SHT_DYNSYM,
};

/** @brief ELF symbol.
*
* An ELF executable may contain multiple symbols (variables, functions, ...).
Expand Down Expand Up @@ -338,6 +346,18 @@ class ElfSymbolCache
/** Build cache from ElfObject). */
ElfSymbolCache(ElfObject &eo);

/** Build empty cache to be filled later. */
ElfSymbolCache(void)
: DynsymMap(),
SymtabMap(),
Mod(""),
DebuginfoPath(""),
ObjectPath("")
{};

/** Analyze ELF object. */
void Analyze_ELF(ElfObject &eo);

/* Get symbol if available in the Dynsym table. Or 0 if not available. */
inline unsigned char Get_Symbol_Info_Dynsym(const std::string &sym)
{
Expand All @@ -358,13 +378,27 @@ class ElfSymbolCache
return 0;
}

/** Get symbol if available in either symtab. Returns in which symtab this
symbo was found. */
std::pair<unsigned char, ElfSymtabType> Get_Symbol_Info(const std::string &sym);

std::string Get_Symbol_Module(const std::string &)
{
return Mod;
}

std::vector<std::string> Get_All_Symbols(void);

inline const std::string &Get_Debuginfo_Path(void) const
{
return DebuginfoPath;
}

inline const std::string &Get_Object_Path(void) const
{
return ObjectPath;
}

/** Dump for debugging reasons. */
void Dump_Cache(void);

Expand All @@ -382,6 +416,9 @@ class ElfSymbolCache
/** Kernel module name, if .modinfo section is present. */
std::string Mod;

/** Reference to the ElfObject used to build this object. */
ElfObject &EO;
/** Path to the debuginfo object (contains .symtab section). */
std::string DebuginfoPath;

/** Path to the .so file object (contains .dynsym section). */
std::string ObjectPath;
};
82 changes: 52 additions & 30 deletions libcextract/InlineAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,27 @@
#include <cxxabi.h>
#include <stdexcept>

InlineAnalysis::InlineAnalysis(const char *elf_path, const char *ipaclones_path,
const char *symvers_path, bool is_kernel)
: ElfObj(nullptr),
ElfCache(nullptr),
InlineAnalysis::InlineAnalysis(const std::vector<std::string> &elfs_path,
const char *ipaclones_path,
const char *symvers_path, bool is_kernel)
: ElfCache(nullptr),
Ipa(nullptr),
Symv(nullptr),
Kernel(is_kernel)
{
try {
/* Debuginfo information is not needed for inline analysis. But is desired
for better precision. That is why whe declare those objects dynamically. */
if (elf_path) {
ElfObj = new ElfObject(elf_path);
ElfCache = new ElfSymbolCache(*ElfObj);
if (elfs_path.size() > 0) {
/* Only create an ElfSymbolCache if we received elfs from command line. */
ElfCache = new ElfSymbolCache();
}

/* Initialize the SymbolCache. */
for (auto it = elfs_path.begin(); it != elfs_path.end(); ++it) {
const std::string &path = *it;
ElfObject elf(path);
ElfCache->Analyze_ELF(elf);
}

if (ipaclones_path) {
Expand All @@ -62,8 +69,6 @@ InlineAnalysis::~InlineAnalysis(void)
{
if (ElfCache)
delete ElfCache;
if (ElfObj)
delete ElfObj;
if (Ipa)
delete Ipa;
if (Symv)
Expand Down Expand Up @@ -162,16 +167,20 @@ void InlineAnalysis::Print_Node_Colors(const std::set<IpaCloneNode *> &set, FILE

for (IpaCloneNode *node : set) {
const char *demangled = InlineAnalysis::Demangle_Symbol(node->Name.c_str());
unsigned char syminfo = Get_Symbol_Info(node->Name);
std::pair<unsigned char, ElfSymtabType> infos = Get_Symbol_Info(node->Name);
unsigned char syminfo = infos.first;
ElfSymtabType symtab = infos.second;
if (syminfo == 0) {
fprintf(fp, "\n\"%s\" [style=dotted]", demangled);
} else {
//unsigned char type = ElfSymbol::Type_Of(syminfo);
unsigned char bind = ElfSymbol::Bind_Of(syminfo);
if (bind == STB_LOCAL) {
fprintf(fp, "\n\"%s\" [color=red]", demangled);
} else if (bind == STB_GLOBAL) {
} else if (bind == STB_GLOBAL && symtab == SHT_DYNSYM) {
fprintf(fp, "\n\"%s\" [color=black]", demangled);
} else if (bind == STB_GLOBAL && symtab == SHT_SYMTAB) {
fprintf(fp, "\n\"%s\" [color=red]", demangled);
} else if (bind == STB_WEAK) {
fprintf(fp, "\n\"%s\" [color=green]", demangled);
} else {
Expand Down Expand Up @@ -247,37 +256,35 @@ void InlineAnalysis::Dump(void)
}
}

unsigned char InlineAnalysis::Get_Symbol_Info(const std::string &sym)
std::pair<unsigned char, ElfSymtabType>
InlineAnalysis::Get_Symbol_Info(const std::string &sym)
{
/* Try the dynsym first, which means this symbol is most likely publically
visible. */
unsigned char ret = 0;
if (ElfCache == nullptr) {
/* If we don't have the ElfCache there is nothing we can do. */
return ret;
return std::make_pair(0, ElfSymtabType::TAB_NONE);
}

ret = ElfCache->Get_Symbol_Info_Dynsym(sym);
if (ret != 0) {
return ret;
}

/* Symbol not available on dynsym. Try symtab. */
ret = ElfCache->Get_Symbol_Info_Symtab(sym);

/* If symbol is not here there is nothing I can do. */
return ret;
/* Forward this to ElfCXX. */
return ElfCache->Get_Symbol_Info(sym);
}

static const char *Bind(unsigned link)
static const char *Bind(unsigned link, ElfSymtabType symtab)
{
switch (link) {
case STB_LOCAL:
return "Private symbol";
break;

case STB_GLOBAL:
return "Public symbol";
if (symtab == SHT_SYMTAB) {
return "Private symbol";
} else if (symtab == SHT_DYNSYM) {
return "Public symbol";
} else {
assert(0 && "Unreachable");
}
break;

case STB_WEAK:
Expand Down Expand Up @@ -311,7 +318,9 @@ ExternalizationType InlineAnalysis::Needs_Externalization(const std::string &sym
: ExternalizationType::NONE;
}

unsigned char info = Get_Symbol_Info(sym);
auto infos = Get_Symbol_Info(sym);
unsigned char info = infos.first;
ElfSymtabType symtab = infos.second;
if (info > 0) {
unsigned bind = ElfSymbol::Bind_Of(info);
switch (bind) {
Expand All @@ -325,7 +334,17 @@ ExternalizationType InlineAnalysis::Needs_Externalization(const std::string &sym
if (Kernel) {
return ExternalizationType::STRONG;
}
return ExternalizationType::WEAK;

/* We need to be careful with the symbol table this came from. If this
is from SYMTAB we can't weakly externalize it. */
if (symtab == SHT_SYMTAB) {
return ExternalizationType::STRONG;
} else if (symtab == SHT_DYNSYM) {
return ExternalizationType::WEAK;
} else {
assert(0 && "Unreachable.");
}

break;
case STB_LOCAL:
return ExternalizationType::STRONG;
Expand Down Expand Up @@ -455,12 +474,15 @@ void InlineAnalysis::Print_Symbol_Set(const std::set<std::string> &symbol_set,

/* In case we have debuginfo, also print if symbols was completely inlined. */
if (have_debuginfo) {
unsigned char syminfo = Get_Symbol_Info(s);
auto syminfos = Get_Symbol_Info(s);
unsigned char syminfo = syminfos.first;
ElfSymtabType symtab = syminfos.second;

unsigned type = ElfSymbol::Type_Of(syminfo);
unsigned bind = ElfSymbol::Bind_Of(syminfo);

const char *type_str = ElfSymbol::Type_As_String(type);
const char *bind_str = (syminfo > 0) ? Bind(bind) : "Inlined";
const char *bind_str = (syminfo > 0) ? Bind(bind, symtab) : "Inlined";

if (csv) {
fprintf(out,"%s;%s\n", type_str, bind_str);
Expand Down
Loading
Loading